강화 학습을위한 매개 변수화 된 양자 회로

TensorFlow.org에서보기 Google Colab에서 실행 GitHub에서 소스보기 노트북 다운로드

양자 컴퓨터는 특정 문제 영역에서 계산상의 이점을 제공하는 것으로 나타났습니다. 양자 강화 학습 (QRL) 분야는 양자 계산 모델에 의존하는 RL 에이전트를 설계하여 이러한 향상을 활용하는 것을 목표로합니다.

이 튜토리얼에서는 매개 변수화 / 변량 양자 회로 (PQC 또는 VQC)를 기반으로하는 두 가지 강화 학습 알고리즘, 즉 정책 그라데이션 및 심층 Q 학습 구현을 구현합니다. 이러한 알고리즘은 [1] Jerbi et al.[2] Skolik et al. , 각각.

TFQ에서 데이터를 다시 업로드하는 PQC를 구현하고 다음과 같이 사용합니다.

  1. 정책 그라데이션 방법으로 훈련 된 RL 정책
  2. 딥 Q- 러닝으로 훈련 된 Q- 함수 근사,

각각 OpenAI Gym의 벤치마킹 작업 인 CartPole-v1을 해결합니다. [1][2] 에 표시된대로 이러한 에이전트는 FrozenLake-v0 , MountainCar-v0 또는 Acrobot-v1 과 같은 OpenAI Gym의 다른 작업 환경을 해결하는데도 사용할 수 있습니다.

이 구현의 특징 :

  • tfq.layers.ControlledPQC 를 사용하여 QML의 많은 애플리케이션에 나타나는 데이터 재 업로드로 PQC를 구현하는 방법을 배우게됩니다. 이 구현은 자연스럽게 PQC의 입력에서 훈련 가능한 스케일링 매개 변수를 사용하여 표현성을 높이고,
  • 출력 값의 유연한 범위를 허용하기 위해 PQC의 출력에서 ​​학습 가능한 가중치로 관찰 가능 항목을 구현하는 방법을 배우게됩니다.
  • 어떻게 배울 tf.keras.Model 호환되지 않습니다 즉, 비 사소한 ML 손실 기능, 훈련 할 수 model.compilemodel.fit 사용tf.GradientTape .

설정

TensorFlow를 설치합니다.

pip install tensorflow==2.4.1

TensorFlow Quantum 설치 :

pip install tensorflow-quantum

체육관 설치 :

pip install gym==0.18.0

이제 TensorFlow 및 모듈 종속성을 가져옵니다.

import tensorflow as tf
import tensorflow_quantum as tfq

import gym, cirq, sympy
import numpy as np
from functools import reduce
from collections import deque, defaultdict
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
tf.get_logger().setLevel('ERROR')

1. 데이터 재 업로드로 PQC 구축

구현중인 두 RL 알고리즘의 핵심에는 환경 (즉, numpy 배열)에서 에이전트의 상태 $ s $를 입력으로 취하고 기대 값의 벡터를 출력하는 PQC가 있습니다. 그런 다음 이러한 기대 값은 에이전트의 정책 $ \ pi (a | s) $ 또는 대략적인 Q- 값 $ Q (s, a) $를 생성하기 위해 후 처리됩니다. 이러한 방식으로 PQC는 최신 딥 RL 알고리즘에서 딥 신경망의 역할과 아날로그 역할을합니다.

PQC에서 입력 벡터를 인코딩하는 일반적인 방법은 단일 큐 비트 회전을 사용하는 것입니다. 여기서 회전 각도는이 입력 벡터의 구성 요소에 의해 제어됩니다. 표현력뛰어난 모델 을 얻기 위해 이러한 단일 큐 비트 인코딩은 PQC에서 한 번만 수행되는 것이 아니라 변형 게이트로 인터 레이 된 여러 " 재 업로드 "에서 수행됩니다. 이러한 PQC의 레이아웃은 다음과 같습니다.

[1][2] 에서 논의 된 바와 같이, 데이터 재 업로드 PQC의 표현 성과 학습 성을 더욱 향상시키는 방법은 PQC의 각 인코딩 게이트에 대해 학습 가능한 입력 크기 조정 매개 변수 $ \ boldsymbol {\ lambda} $를 사용하는 것입니다. 출력에서 훈련 가능한 관찰 가능한 가중치 $ \ boldsymbol {w} $.

1.1 ControlledPQC 용 Cirq 회로

첫 번째 단계는 PQC로 사용될 양자 회로를 Cirq에서 구현하는 것입니다. 이를 위해 회로에 적용 할 기본 단위, 즉 임의의 단일 큐 비트 회전과 CZ 게이트의 엉킴 레이어를 정의하는 것으로 시작합니다.

def one_qubit_rotation(qubit, symbols):
    """
    Returns Cirq gates that apply a rotation of the bloch sphere about the X,
    Y and Z axis, specified by the values in `symbols`.
    """
    return [cirq.rx(symbols[0])(qubit),
            cirq.ry(symbols[1])(qubit),
            cirq.rz(symbols[2])(qubit)]

def entangling_layer(qubits):
    """
    Returns a layer of CZ entangling gates on `qubits` (arranged in a circular topology).
    """
    cz_ops = [cirq.CZ(q0, q1) for q0, q1 in zip(qubits, qubits[1:])]
    cz_ops += ([cirq.CZ(qubits[0], qubits[-1])] if len(qubits) != 2 else [])
    return cz_ops

이제 다음 함수를 사용하여 Cirq 회로를 생성합니다.

def generate_circuit(qubits, n_layers):
    """Prepares a data re-uploading circuit on `qubits` with `n_layers` layers."""
    # Number of qubits
    n_qubits = len(qubits)

    # Sympy symbols for variational angles
    params = sympy.symbols(f'theta(0:{3*(n_layers+1)*n_qubits})')
    params = np.asarray(params).reshape((n_layers + 1, n_qubits, 3))

    # Sympy symbols for encoding angles
    inputs = sympy.symbols(f'x(0:{n_qubits})'+f'(0:{n_layers})')
    inputs = np.asarray(inputs).reshape((n_layers, n_qubits))

    # Define circuit
    circuit = cirq.Circuit()
    for l in range(n_layers):
        # Variational layer
        circuit += cirq.Circuit(one_qubit_rotation(q, params[l, i]) for i, q in enumerate(qubits))
        circuit += entangling_layer(qubits)
        # Encoding layer
        circuit += cirq.Circuit(cirq.rx(inputs[l, i])(q) for i, q in enumerate(qubits))

    # Last varitional layer
    circuit += cirq.Circuit(one_qubit_rotation(q, params[n_layers, i]) for i,q in enumerate(qubits))

    return circuit, list(params.flat), list(inputs.flat)

이것이 변형 레이어와 인코딩 레이어를 번갈아 가며 생성하는 회로를 생성하는지 확인합니다.

n_qubits, n_layers = 3, 1
qubits = cirq.GridQubit.rect(1, n_qubits)
circuit, _, _ = generate_circuit(qubits, n_layers)
SVGCircuit(circuit)
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

svg

1.2 ControlledPQC를 사용한 ReUploadingPQC 레이어

위 그림에서 다시 업로드하는 PQC를 구성하기 위해 사용자 지정 Keras 레이어를 만들 수 있습니다. 이 계층은 학습 가능한 매개 변수 (가변 각도 $ \ boldsymbol {\ theta} $ 및 입력 스케일링 매개 변수 $ \ boldsymbol {\ lambda} $)를 관리하고 입력 값 (입력 상태 $ s $)을 해당 기호로 해석합니다. 회로.

class ReUploadingPQC(tf.keras.layers.Layer):
    """
    Performs the transformation (s_1, ..., s_d) -> (theta_1, ..., theta_N, lmbd[1][1]s_1, ..., lmbd[1][M]s_1,
        ......., lmbd[d][1]s_d, ..., lmbd[d][M]s_d) for d=input_dim, N=theta_dim and M=n_layers.
    An activation function from tf.keras.activations, specified by `activation` ('linear' by default) is
        then applied to all lmbd[i][j]s_i.
    All angles are finally permuted to follow the alphabetical order of their symbol names, as processed
        by the ControlledPQC.
    """

    def __init__(self, qubits, n_layers, observables, activation="linear", name="re-uploading_PQC"):
        super(ReUploadingPQC, self).__init__(name=name)
        self.n_layers = n_layers
        self.n_qubits = len(qubits)

        circuit, theta_symbols, input_symbols = generate_circuit(qubits, n_layers)

        theta_init = tf.random_uniform_initializer(minval=0.0, maxval=np.pi)
        self.theta = tf.Variable(
            initial_value=theta_init(shape=(1, len(theta_symbols)), dtype="float32"),
            trainable=True, name="thetas"
        )

        lmbd_init = tf.ones(shape=(self.n_qubits * self.n_layers,))
        self.lmbd = tf.Variable(
            initial_value=lmbd_init, dtype="float32", trainable=True, name="lambdas"
        )

        # Define explicit symbol order.
        symbols = [str(symb) for symb in theta_symbols + input_symbols]
        self.indices = tf.constant([sorted(symbols).index(a) for a in symbols])

        self.activation = activation
        self.empty_circuit = tfq.convert_to_tensor([cirq.Circuit()])
        self.computation_layer = tfq.layers.ControlledPQC(circuit, observables)        

    def call(self, inputs):
        # inputs[0] = encoding data for the state.
        batch_dim = tf.gather(tf.shape(inputs[0]), 0)
        tiled_up_circuits = tf.repeat(self.empty_circuit, repeats=batch_dim)
        tiled_up_thetas = tf.tile(self.theta, multiples=[batch_dim, 1])
        tiled_up_inputs = tf.tile(inputs[0], multiples=[1, self.n_layers])
        scaled_inputs = tf.einsum("i,ji->ji", self.lmbd, tiled_up_inputs)
        squashed_inputs = tf.keras.layers.Activation(self.activation)(scaled_inputs)

        joined_vars = tf.concat([tiled_up_thetas, squashed_inputs], axis=1)
        joined_vars = tf.gather(joined_vars, self.indices, axis=1)

        return self.computation_layer([tiled_up_circuits, joined_vars])

2. PQC 정책이있는 정책 그라데이션 RL

이 섹션에서는 [1]에 제시된 정책 그라데이션 알고리즘을 구현합니다. 이를 위해 방금 정의 된 PQC에서 softmax-VQC 정책 (VQC는 변형 양자 회로를 나타냄)을 구성하는 것으로 시작합니다.

$$ \pi_\theta(a|s) = \frac{e^{\beta \langle O_a \rangle_{s,\theta} } }{\sum_{a'} e^{\beta \langle O_{a'} \rangle_{s,\theta} } } $$

여기서 $ \ langle O_a \ rangle_ {s, \ theta} $는 PQC의 출력에서 ​​측정 된 옵저버 블 $ O_a $ (액션 당 하나)의 기대 값이고 $ \ beta $는 조정 가능한 역 온도 매개 변수입니다.

CartPole에 대해 [1] 에서 사용 된 것과 동일한 Observable을 채택 할 수 있습니다. 즉, 모든 큐 비트에 대해 작동하는 전역 $ Z_0Z_1Z_2Z_3 $ Pauli 제품을 각 작업에 대한 작업 별 가중치로 가중치를 적용 할 수 있습니다. Pauli 제품의 가중치를 구현하려면 작업 별 가중치를 저장하고 예상 값 $ \ langle Z_0Z_1Z_2Z_3 \ rangle_ {s, \ theta} $에 곱하여 적용하는 추가 tf.keras.layers.Layer 를 사용할 수 있습니다.

class Alternating(tf.keras.layers.Layer):
    def __init__(self, output_dim):
        super(Alternating, self).__init__()
        self.w = tf.Variable(
            initial_value=tf.constant([[(-1.)**i for i in range(output_dim)]]), dtype="float32",
            trainable=True, name="obs-weights")

    def call(self, inputs):
        return tf.matmul(inputs, self.w)

PQC의 정의를 준비하십시오.

n_qubits = 4 # Dimension of the state vectors in CartPole
n_layers = 5 # Number of layers in the PQC
n_actions = 2 # Number of actions in CartPole

qubits = cirq.GridQubit.rect(1, n_qubits)

및 그 관찰 가능 항목 :

ops = [cirq.Z(q) for q in qubits]
observables = [reduce((lambda x, y: x * y), ops)] # Z_0*Z_1*Z_2*Z_3

이를 통해 이전에 정의 된 ReUploadingPQC 레이어를 순차적으로 적용하는 tf.keras.Model 을 정의한 다음 Alternating 사용하여 가중 관찰 가능 항목을 계산 한 후 tf.keras.layers.Softmax 레이어로 공급되는 후 처리 레이어를 정의합니다. 에이전트의 softmax-VQC 정책을 출력합니다.

def generate_model_policy(qubits, n_layers, n_actions, beta, observables):
    """Generates a Keras model for a data re-uploading PQC policy."""

    input_tensor = tf.keras.Input(shape=(len(qubits), ), dtype=tf.dtypes.float32, name='input')
    re_uploading_pqc = ReUploadingPQC(qubits, n_layers, observables)([input_tensor])
    process = tf.keras.Sequential([
        Alternating(n_actions),
        tf.keras.layers.Lambda(lambda x: x * beta),
        tf.keras.layers.Softmax()
    ], name="observables-policy")
    policy = process(re_uploading_pqc)
    model = tf.keras.Model(inputs=[input_tensor], outputs=policy)

    return model

model = generate_model_policy(qubits, n_layers, n_actions, 1.0, observables)
tf.keras.utils.plot_model(model, show_shapes=True, dpi=70)

png

이제 기본 REINFORCE 알고리즘을 사용하여 CartPole-v1에서 PQC 정책을 훈련 할 수 있습니다 ( [1]의 Alg. 1 참조). 다음 사항에주의하십시오.

  1. 스케일링 매개 변수, 변형 각도 및 관찰 가능 가중치는 다른 학습률로 훈련되기 때문에 자체 학습률을 사용하여 3 개의 개별 최적화 프로그램을 정의하는 것이 편리합니다. 각 최적화 프로그램은 이러한 매개 변수 그룹 중 하나를 업데이트합니다.
  2. 정책 기울기 RL의 손실 함수는 다음과 같습니다.
$$ \mathcal{L}(\theta) = -\frac{1}{|\mathcal{B}|}\sum_{s_0,a_0,r_1,s_1,a_1, \ldots \in \mathcal{B} } \left(\sum_{t=0}^{H-1} \log(\pi_\theta(a_t|s_t)) \sum_{t'=1}^{H-t} \gamma^{t'} r_{t+t'} \right)$$

$ \ pi_ \ theta $ 정책을 따르는 환경에서 $ \ mathcal {B} $ 에피소드 $ (s_0, a_0, r_1, s_1, a_1, \ ldots) $ 일괄 처리 이는 모델이 적합해야하는 고정 된 목표 값을 사용하는지도 학습 손실과 model.fit 과 같은 간단한 함수 호출을 사용하여 정책을 훈련하는 것이 불가능합니다. 대신tf.GradientTape 사용하면 PQC (예 : 정책 샘플링)와 관련된 계산을 추적하고 상호 작용 중에 손실에 대한 기여도를 저장할 수 있습니다. 일련의 에피소드를 실행 한 후 이러한 계산에 역 전파를 적용하여 PQC 매개 변수에 대한 손실의 기울기를 얻고 최적화 프로그램을 사용하여 정책 모델을 업데이트 할 수 있습니다.

환경과의 상호 작용 에피소드를 수집하는 함수를 정의하여 시작하십시오.

def gather_episodes(state_bounds, n_actions, model, n_episodes, env_name):
    """Interact with environment in batched fashion."""

    trajectories = [defaultdict(list) for _ in range(n_episodes)]
    envs = [gym.make(env_name) for _ in range(n_episodes)]

    done = [False for _ in range(n_episodes)]
    states = [e.reset() for e in envs]

    while not all(done):
        unfinished_ids = [i for i in range(n_episodes) if not done[i]]
        normalized_states = [s/state_bounds for i, s in enumerate(states) if not done[i]]

        for i, state in zip(unfinished_ids, normalized_states):
            trajectories[i]['states'].append(state)

        # Compute policy for all unfinished envs in parallel
        states = tf.convert_to_tensor(normalized_states)
        action_probs = model([states])

        # Store action and transition all environments to the next state
        states = [None for i in range(n_episodes)]
        for i, policy in zip(unfinished_ids, action_probs.numpy()):
            action = np.random.choice(n_actions, p=policy)
            states[i], reward, done[i], _ = envs[i].step(action)
            trajectories[i]['actions'].append(action)
            trajectories[i]['rewards'].append(reward)

    return trajectories

할인을 계산하는 함수는 에피소드에서 수집 한 보상 $ r_t $ 중 $ \ sum_ {t '= 1} ^ {Ht} \ gamma ^ {t'} r_ {t + t '} $를 반환합니다.

def compute_returns(rewards_history, gamma):
    """Compute discounted returns with discount factor `gamma`."""
    returns = []
    discounted_sum = 0
    for r in rewards_history[::-1]:
        discounted_sum = r + gamma * discounted_sum
        returns.insert(0, discounted_sum)

    # Normalize them for faster and more stable learning
    returns = np.array(returns)
    returns = (returns - np.mean(returns)) / (np.std(returns) + 1e-8)
    returns = returns.tolist()

    return returns

하이퍼 파라미터를 정의합니다.

state_bounds = np.array([2.4, 2.5, 0.21, 2.5])
gamma = 1
batch_size = 10
n_episodes = 1000

최적화 프로그램을 준비합니다.

optimizer_in = tf.keras.optimizers.Adam(learning_rate=0.1, amsgrad=True)
optimizer_var = tf.keras.optimizers.Adam(learning_rate=0.01, amsgrad=True)
optimizer_out = tf.keras.optimizers.Adam(learning_rate=0.1, amsgrad=True)

# Assign the model parameters to each optimizer
w_in, w_var, w_out = 1, 0, 2

상태, 작업 및 반환을 사용하여 정책을 업데이트하는 함수를 구현합니다.

@tf.function
def reinforce_update(states, actions, returns, model):
    states = tf.convert_to_tensor(states)
    actions = tf.convert_to_tensor(actions)
    returns = tf.convert_to_tensor(returns)

    with tf.GradientTape() as tape:
        tape.watch(model.trainable_variables)
        logits = model(states)
        p_actions = tf.gather_nd(logits, actions)
        log_probs = tf.math.log(p_actions)
        loss = tf.math.reduce_sum(-log_probs * returns) / batch_size
    grads = tape.gradient(loss, model.trainable_variables)
    for optimizer, w in zip([optimizer_in, optimizer_var, optimizer_out], [w_in, w_var, w_out]):
        optimizer.apply_gradients([(grads[w], model.trainable_variables[w])])

이제 에이전트의 기본 학습 루프를 구현하십시오.

env_name = "CartPole-v1"

# Start training the agent
episode_reward_history = []
for batch in range(n_episodes // batch_size):
    # Gather episodes
    episodes = gather_episodes(state_bounds, n_actions, model, batch_size, env_name)

    # Group states, actions and returns in numpy arrays
    states = np.concatenate([ep['states'] for ep in episodes])
    actions = np.concatenate([ep['actions'] for ep in episodes])
    rewards = [ep['rewards'] for ep in episodes]
    returns = np.concatenate([compute_returns(ep_rwds, gamma) for ep_rwds in rewards])
    returns = np.array(returns, dtype=np.float32)

    id_action_pairs = np.array([[i, a] for i, a in enumerate(actions)])

    # Update model parameters.
    reinforce_update(states, id_action_pairs, returns, model)

    # Store collected rewards
    for ep_rwds in rewards:
        episode_reward_history.append(np.sum(ep_rwds))

    avg_rewards = np.mean(episode_reward_history[-10:])

    print('Finished episode', (batch + 1) * batch_size,
          'Average rewards: ', avg_rewards)

    if avg_rewards >= 500.0:
        break
Finished episode 10 Average rewards:  22.3
Finished episode 20 Average rewards:  27.4
Finished episode 30 Average rewards:  24.7
Finished episode 40 Average rewards:  21.2
Finished episode 50 Average rewards:  33.9
Finished episode 60 Average rewards:  31.3
Finished episode 70 Average rewards:  37.3
Finished episode 80 Average rewards:  34.4
Finished episode 90 Average rewards:  58.4
Finished episode 100 Average rewards:  33.2
Finished episode 110 Average rewards:  67.9
Finished episode 120 Average rewards:  63.9
Finished episode 130 Average rewards:  83.5
Finished episode 140 Average rewards:  88.0
Finished episode 150 Average rewards:  142.9
Finished episode 160 Average rewards:  204.7
Finished episode 170 Average rewards:  138.1
Finished episode 180 Average rewards:  183.0
Finished episode 190 Average rewards:  196.0
Finished episode 200 Average rewards:  302.0
Finished episode 210 Average rewards:  374.4
Finished episode 220 Average rewards:  329.1
Finished episode 230 Average rewards:  307.8
Finished episode 240 Average rewards:  359.6
Finished episode 250 Average rewards:  400.7
Finished episode 260 Average rewards:  414.4
Finished episode 270 Average rewards:  394.9
Finished episode 280 Average rewards:  470.7
Finished episode 290 Average rewards:  459.7
Finished episode 300 Average rewards:  428.7
Finished episode 310 Average rewards:  500.0

에이전트의 학습 기록을 플로팅합니다.

plt.figure(figsize=(10,5))
plt.plot(episode_reward_history)
plt.xlabel('Epsiode')
plt.ylabel('Collected rewards')
plt.show()

png

축하합니다. Cartpole에서 양자 정책 기울기 모델을 학습했습니다! 위의 플롯은 환경과의 상호 작용을 통해 에이전트가 에피소드 당 수집 한 보상을 보여줍니다. 수백 개의 에피소드 후에 에이전트의 성능이 최적에 가까워지는 것을 볼 수 있습니다 (예 : 에피소드 당 500 개의 보상).

이제 샘플 에피소드에서 env.render() 를 사용하여 에이전트의 성능을 시각화 할 수 있습니다 (노트북에 디스플레이에 대한 액세스 권한이있는 경우에만 다음 셀의 주석 처리를 제거 / 실행).

# from PIL import Image

# env = gym.make('CartPole-v1')
# state = env.reset()
# frames = []
# for t in range(500):
#     im = Image.fromarray(env.render(mode='rgb_array'))
#     frames.append(im)
#     policy = model([tf.convert_to_tensor([state/state_bounds])])
#     action = np.random.choice(n_actions, p=policy.numpy()[0])
#     state, _, done, _ = env.step(action)
#     if done:
#         break
# env.close()
# frames[1].save('./images/gym_CartPole.gif',
#                save_all=True, append_images=frames[2:], optimize=False, duration=40, loop=0)

3. PQC Q- 함수 근사치를 사용한 심층 Q 학습

이 섹션에서는 [2]에 제시된 딥 Q 학습 알고리즘의 구현으로 이동합니다. 정책 그라데이션 접근 방식과 달리 딥 Q 학습 방법은 PQC를 사용하여 에이전트의 Q 기능을 근사합니다. 즉, PQC는 함수 근사치를 정의합니다.

$$ Q_\theta(s,a) = \langle O_a \rangle_{s,\theta} $$

여기서 $ \ langle O_a \ rangle_ {s, \ theta} $는 PQC 출력에서 ​​측정 된 옵저버 블 $ O_a $ (액션 당 하나)의 기대 값입니다.

이러한 Q- 값은 Q- 학습에서 파생 된 손실 함수를 사용하여 업데이트됩니다.

$$ \mathcal{L}(\theta) = \frac{1}{|\mathcal{B}|}\sum_{s,a,r,s' \in \mathcal{B} } \left(Q_\theta(s,a) - [r +\max_{a'} Q_{\theta'}(s',a')]\right)^2$$

$ 1 $-단계 상호 작용 $ (s, a, r, s ') $의 배치 $ \ mathcal {B} $의 경우 재생 메모리에서 샘플링 된 환경과의 $ (s, a, r, s') $ 및 대상 PQC를 지정하는 매개 변수 $ \ theta '$ ( 즉, 매개 변수가 학습 전반에 걸쳐 기본 PQC에서 산발적으로 복사되는 기본 PQC의 사본).

CartPole에 대해 [2] 에서 사용 된 것과 동일한 Observable을 채택 할 수 있습니다. 즉, $ 0 $ 작업에 $ Z_0Z_1 $ Pauli 제품을, $ 1 $ 작업에 $ Z_2Z_3 $ Pauli 제품을 사용할 수 있습니다. 두 관찰 가능 항목 모두 크기가 조정되어 예상 값이 $ [0,1] $에 있고 작업 별 가중치에 의해 가중치가 부여됩니다. Pauli 제품의 크기 조정 및 가중치를 구현하려면 작업 별 가중치를 저장하고이를 기대 값 $ \ left (1+ \ langle Z_0Z_1에 곱하여 적용하는 추가 tf.keras.layers.Layer 를 다시 정의 할 수 있습니다. \ rangle_ {s, \ theta} \ right) / 2 $ 및 $ \ left (1+ \ langle Z_2Z_3 \ rangle_ {s, \ theta} \ right) / 2 $.

class Rescaling(tf.keras.layers.Layer):
    def __init__(self, input_dim):
        super(Rescaling, self).__init__()
        self.input_dim = input_dim
        self.w = tf.Variable(
            initial_value=tf.ones(shape=(1,input_dim)), dtype="float32",
            trainable=True, name="obs-weights")

    def call(self, inputs):
        return tf.math.multiply((inputs+1)/2, tf.repeat(self.w,repeats=tf.shape(inputs)[0],axis=0))

PQC 및 관찰 가능 항목의 정의를 준비합니다.

n_qubits = 4 # Dimension of the state vectors in CartPole
n_layers = 5 # Number of layers in the PQC
n_actions = 2 # Number of actions in CartPole

qubits = cirq.GridQubit.rect(1, n_qubits)
ops = [cirq.Z(q) for q in qubits]
observables = [ops[0]*ops[1], ops[2]*ops[3]] # Z_0*Z_1 for action 0 and Z_2*Z_3 for action 1

PQC 정책 모델과 유사하게 Q- 학습 에이전트의 기본 및 대상 모델을 생성하는 데 사용되는 Q- 함수 근사치를 구성하는 tf.keras.Model 을 정의합니다.

def generate_model_Qlearning(qubits, n_layers, n_actions, observables, target):
    """Generates a Keras model for a data re-uploading PQC Q-function approximator."""

    input_tensor = tf.keras.Input(shape=(len(qubits), ), dtype=tf.dtypes.float32, name='input')
    re_uploading_pqc = ReUploadingPQC(qubits, n_layers, observables, activation='tanh')([input_tensor])
    process = tf.keras.Sequential([Rescaling(len(observables))], name=target*"Target"+"Q-values")
    Q_values = process(re_uploading_pqc)
    model = tf.keras.Model(inputs=[input_tensor], outputs=Q_values)

    return model

model = generate_model_Qlearning(qubits, n_layers, n_actions, observables, False)
model_target = generate_model_Qlearning(qubits, n_layers, n_actions, observables, True)

model_target.set_weights(model.get_weights())
tf.keras.utils.plot_model(model, show_shapes=True, dpi=70)

png

tf.keras.utils.plot_model(model_target, show_shapes=True, dpi=70)

png

이제 심층 Q- 학습 알고리즘을 구현하고 CartPole-v1 환경에서 테스트 할 수 있습니다. 에이전트의 정책에 대해 $ \ varepsilon $ -greedy 정책을 사용할 수 있습니다.

$$ \pi(a|s) = \begin{cases} \delta_{a,\text{argmax}_{a'} Q_\theta(s,a')}\quad \text{w.p.}\quad 1 - \varepsilon\\ \frac{1}{\text{num_actions} }\quad \quad \quad \quad \text{w.p.}\quad \varepsilon \end{cases} $$

여기서 $ \ varepsilon $은 상호 작용의 각 에피소드에서 곱셈으로 붕괴됩니다.

환경에서 상호 작용 단계를 수행하는 함수를 정의하여 시작합니다.

def interact_env(state, model, epsilon, n_actions, env):
    # Preprocess state
    state_array = np.array(state) 
    state = tf.convert_to_tensor([state_array])

    # Sample action
    coin = np.random.random()
    if coin > epsilon:
        q_vals = model([state])
        action = int(tf.argmax(q_vals[0]).numpy())
    else:
        action = np.random.choice(n_actions)

    # Apply sampled action in the environment, receive reward and next state
    next_state, reward, done, _ = env.step(action)

    interaction = {'state': state_array, 'action': action, 'next_state': next_state.copy(),
                   'reward': reward, 'done':float(done)}

    return interaction

일련의 상호 작용을 사용하여 Q- 함수를 업데이트하는 함수 :

@tf.function
def Q_learning_update(states, actions, rewards, next_states, done, model, gamma, n_actions):
    states = tf.convert_to_tensor(states)
    actions = tf.convert_to_tensor(actions)
    rewards = tf.convert_to_tensor(rewards)
    next_states = tf.convert_to_tensor(next_states)
    done = tf.convert_to_tensor(done)

    # Compute their target q_values and the masks on sampled actions
    future_rewards = model_target([next_states])
    target_q_values = rewards + (gamma * tf.reduce_max(future_rewards, axis=1)

                                                   * (1.0 - done))
    masks = tf.one_hot(actions, n_actions)

    # Train the model on the states and target Q-values
    with tf.GradientTape() as tape:
        tape.watch(model.trainable_variables)
        q_values = model([states])
        q_values_masked = tf.reduce_sum(tf.multiply(q_values, masks), axis=1)
        loss = tf.keras.losses.Huber()(target_q_values, q_values_masked)

    # Backpropagation
    grads = tape.gradient(loss, model.trainable_variables)
    for optimizer, w in zip([optimizer_in, optimizer_var, optimizer_out], [w_in, w_var, w_out]):
        optimizer.apply_gradients([(grads[w], model.trainable_variables[w])])

하이퍼 파라미터를 정의합니다.

gamma = 0.99
n_episodes = 2000

# Define replay memory
max_memory_length = 10000 # Maximum replay length
replay_memory = deque(maxlen=max_memory_length)

epsilon = 1.0  # Epsilon greedy parameter
epsilon_min = 0.01  # Minimum epsilon greedy parameter
decay_epsilon = 0.99 # Decay rate of epsilon greedy parameter
batch_size = 16
steps_per_update = 10 # Train the model every x steps
steps_per_target_update = 30 # Update the target model every x steps

최적화 프로그램을 준비합니다.

optimizer_in = tf.keras.optimizers.Adam(learning_rate=0.001, amsgrad=True)
optimizer_var = tf.keras.optimizers.Adam(learning_rate=0.001, amsgrad=True)
optimizer_out = tf.keras.optimizers.Adam(learning_rate=0.1, amsgrad=True)

# Assign the model parameters to each optimizer
w_in, w_var, w_out = 1, 0, 2

이제 에이전트의 기본 학습 루프를 구현하십시오.

env = gym.make("CartPole-v1")

episode_reward_history = []
step_count = 0
for episode in range(n_episodes):
    episode_reward = 0
    state = env.reset()

    while True:
        # Interact with env
        interaction = interact_env(state, model, epsilon, n_actions, env)

        # Store interaction in the replay memory
        replay_memory.append(interaction)

        state = interaction['next_state']
        episode_reward += interaction['reward']
        step_count += 1

        # Update model
        if step_count % steps_per_update == 0:
            # Sample a batch of interactions and update Q_function
            training_batch = np.random.choice(replay_memory, size=batch_size)
            Q_learning_update(np.asarray([x['state'] for x in training_batch]),
                              np.asarray([x['action'] for x in training_batch]),
                              np.asarray([x['reward'] for x in training_batch], dtype=np.float32),
                              np.asarray([x['next_state'] for x in training_batch]),
                              np.asarray([x['done'] for x in training_batch], dtype=np.float32),
                              model, gamma, n_actions)

        # Update target model
        if step_count % steps_per_target_update == 0:
            model_target.set_weights(model.get_weights())

        # Check if the episode is finished
        if interaction['done']:
            break

    # Decay epsilon
    epsilon = max(epsilon * decay_epsilon, epsilon_min)
    episode_reward_history.append(episode_reward)
    if (episode+1)%10 == 0:
        avg_rewards = np.mean(episode_reward_history[-10:])
        print("Episode {}/{}, average last 10 rewards {}".format(
            episode+1, n_episodes, avg_rewards))
        if avg_rewards >= 500.0:
            break
Episode 10/2000, average last 10 rewards 22.1
Episode 20/2000, average last 10 rewards 27.9
Episode 30/2000, average last 10 rewards 31.5
Episode 40/2000, average last 10 rewards 23.3
Episode 50/2000, average last 10 rewards 25.4
Episode 60/2000, average last 10 rewards 19.7
Episode 70/2000, average last 10 rewards 12.5
Episode 80/2000, average last 10 rewards 11.7
Episode 90/2000, average last 10 rewards 14.9
Episode 100/2000, average last 10 rewards 15.3
Episode 110/2000, average last 10 rewards 14.6
Episode 120/2000, average last 10 rewards 20.4
Episode 130/2000, average last 10 rewards 14.2
Episode 140/2000, average last 10 rewards 21.6
Episode 150/2000, average last 10 rewards 30.4
Episode 160/2000, average last 10 rewards 27.6
Episode 170/2000, average last 10 rewards 18.5
Episode 180/2000, average last 10 rewards 30.6
Episode 190/2000, average last 10 rewards 12.2
Episode 200/2000, average last 10 rewards 27.2
Episode 210/2000, average last 10 rewards 27.2
Episode 220/2000, average last 10 rewards 15.3
Episode 230/2000, average last 10 rewards 128.4
Episode 240/2000, average last 10 rewards 68.3
Episode 250/2000, average last 10 rewards 44.0
Episode 260/2000, average last 10 rewards 119.8
Episode 270/2000, average last 10 rewards 135.3
Episode 280/2000, average last 10 rewards 90.6
Episode 290/2000, average last 10 rewards 120.9
Episode 300/2000, average last 10 rewards 125.3
Episode 310/2000, average last 10 rewards 141.7
Episode 320/2000, average last 10 rewards 144.7
Episode 330/2000, average last 10 rewards 165.7
Episode 340/2000, average last 10 rewards 26.1
Episode 350/2000, average last 10 rewards 9.7
Episode 360/2000, average last 10 rewards 9.6
Episode 370/2000, average last 10 rewards 9.7
Episode 380/2000, average last 10 rewards 9.4
Episode 390/2000, average last 10 rewards 11.3
Episode 400/2000, average last 10 rewards 11.6
Episode 410/2000, average last 10 rewards 165.4
Episode 420/2000, average last 10 rewards 170.5
Episode 430/2000, average last 10 rewards 25.1
Episode 440/2000, average last 10 rewards 74.1
Episode 450/2000, average last 10 rewards 214.7
Episode 460/2000, average last 10 rewards 139.1
Episode 470/2000, average last 10 rewards 265.1
Episode 480/2000, average last 10 rewards 296.7
Episode 490/2000, average last 10 rewards 101.7
Episode 500/2000, average last 10 rewards 146.6
Episode 510/2000, average last 10 rewards 325.6
Episode 520/2000, average last 10 rewards 45.9
Episode 530/2000, average last 10 rewards 263.5
Episode 540/2000, average last 10 rewards 223.3
Episode 550/2000, average last 10 rewards 73.1
Episode 560/2000, average last 10 rewards 115.0
Episode 570/2000, average last 10 rewards 148.3
Episode 580/2000, average last 10 rewards 41.6
Episode 590/2000, average last 10 rewards 266.7
Episode 600/2000, average last 10 rewards 275.2
Episode 610/2000, average last 10 rewards 253.9
Episode 620/2000, average last 10 rewards 282.2
Episode 630/2000, average last 10 rewards 348.3
Episode 640/2000, average last 10 rewards 162.2
Episode 650/2000, average last 10 rewards 276.0
Episode 660/2000, average last 10 rewards 234.6
Episode 670/2000, average last 10 rewards 187.4
Episode 680/2000, average last 10 rewards 285.0
Episode 690/2000, average last 10 rewards 362.8
Episode 700/2000, average last 10 rewards 316.0
Episode 710/2000, average last 10 rewards 436.0
Episode 720/2000, average last 10 rewards 366.1
Episode 730/2000, average last 10 rewards 305.0
Episode 740/2000, average last 10 rewards 273.2
Episode 750/2000, average last 10 rewards 236.8
Episode 760/2000, average last 10 rewards 260.2
Episode 770/2000, average last 10 rewards 443.9
Episode 780/2000, average last 10 rewards 494.2
Episode 790/2000, average last 10 rewards 333.1
Episode 800/2000, average last 10 rewards 367.1
Episode 810/2000, average last 10 rewards 317.8
Episode 820/2000, average last 10 rewards 396.6
Episode 830/2000, average last 10 rewards 494.1
Episode 840/2000, average last 10 rewards 500.0

에이전트의 학습 기록을 플로팅합니다.

plt.figure(figsize=(10,5))
plt.plot(episode_reward_history)
plt.xlabel('Epsiode')
plt.ylabel('Collected rewards')
plt.show()

png

위의 그림과 유사하게, ~ 1000 개의 에피소드 후에 에이전트의 성능이 최적에 가까워지는 것을 볼 수 있습니다. 즉, 에피소드 당 500 개의 보상이 있습니다. Q- 기능이 정책보다 학습 할 "풍부한"기능이기 때문에 Q- 학습 에이전트의 경우 학습 시간이 더 오래 걸립니다.

4. 운동

이제 두 가지 유형의 모델을 학습 했으므로 서로 다른 환경 (및 서로 다른 큐 비트 및 레이어 수)으로 실험 해보십시오. 마지막 두 섹션의 PQC 모델을 행위자 비평가 에이전트 로 결합 할 수도 있습니다.