ホーム » TensorFlow Quantum

TensorFlow Quantum」カテゴリーアーカイブ

TensorFlow Quantum 0.2.0 : Tutorials : 量子畳込みニューラルネットワーク

TensorFlow Quantum 0.2.0 Tutorials : 量子畳込みニューラルネットワーク (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/19/2020 (0.2.0)

* 本ページは、TensorFlow Quantum の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Tutorials : 量子畳込みニューラルネットワーク

このチュートリアルは単純化された 量子畳込みニューラルネットワーク (QCNN) を実装します、古典的畳込みニューラルネットワークへの提案された量子類推で、これは翻訳的に不変でもあります。

このサンプルは、量子センサーやデバイスからの複雑なシミュレーションのような量子データソースの特定のプロパティをどのように検出するかを実演します。励起を持つあるいは持たないかもしれない クラスタ状態 である量子データソース — QCNN が検出することを学習するものです (ペーパーで使用されたデータソースは SPT 位相分類です)。

 

セットアップ

TensorFlow Quantum をインストールします :

pip install -q tensorflow-quantum

今は TensorFlow とモジュール依存性をインポートします :

import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit

 

1. QCNN を構築する

1.1 TensorFlow グラフで回路をアセンブルする

TensorFlow Quantum (TFQ) は in-graph 回路構築のために設計された層クラスを提供します。一つの例は tf.keras.Layer から継承した tfq.layers.AddCircuit 層です。この層は次の図で示されるように回路の入力バッチに prepend か append (i.e. 前に追加または後に追加) できます。

次のスニペットはこの層を使用します :

qubit = cirq.GridQubit(0, 0)

# Define some circuits.
circuit1 = cirq.Circuit(cirq.X(qubit))
circuit2 = cirq.Circuit(cirq.H(qubit))

# Convert to a tensor.
input_circuit_tensor = tfq.convert_to_tensor([circuit1, circuit2])

# Define a circuit that we want to append
y_circuit = cirq.Circuit(cirq.Y(qubit))

# Instantiate our layer
y_appender = tfq.layers.AddCircuit()

# Run our circuit tensor through the layer and save the output.
output_circuit_tensor = y_appender(input_circuit_tensor, append=y_circuit)

入力 tensor を検証します :

print(tfq.from_tensor(input_circuit_tensor))
[cirq.Circuit([
    cirq.Moment(operations=[
        cirq.X.on(cirq.GridQubit(0, 0)),
    ]),
])
 cirq.Circuit([
    cirq.Moment(operations=[
        cirq.H.on(cirq.GridQubit(0, 0)),
    ]),
])]

そして出力 tensor を検証します :

print(tfq.from_tensor(output_circuit_tensor))
[cirq.Circuit([
    cirq.Moment(operations=[
        cirq.X.on(cirq.GridQubit(0, 0)),
    ]),
    cirq.Moment(operations=[
        cirq.Y.on(cirq.GridQubit(0, 0)),
    ]),
])
 cirq.Circuit([
    cirq.Moment(operations=[
        cirq.H.on(cirq.GridQubit(0, 0)),
    ]),
    cirq.Moment(operations=[
        cirq.Y.on(cirq.GridQubit(0, 0)),
    ]),
])]

tfq.layers.AddCircuit を使用することなく下のサンプルを実行できる一方で、どれほど複雑な機能が TensorFlow 計算グラフに埋め込めるかを理解する良い機会です。

 

1.2 問題概要

クラスタ状態を準備して (量子が) 「励起」しているか否かを検出するために量子分類器を訓練します。クラスタ状態は非常にもつれていますが、古典的コンピュータのためには必ずしも難しくはありません。明確にするため、これはペーパーで使用されたものよりも単純なデータセットです。

この分類タスクのため深層 MERA-like QCNN を実装します、何故ならば :

  1. QCNN のように、リング上のクラスタ状態は翻訳的に不変です。
  2. クラスタ状態は非常にもつれています。

このアーキテクチャはエンタングルメントを減じ、シングル量子ビットを読み出すことにより分類を得ることに効果的であるはずです。

「励起」クラスタ状態はその任意の量子ビットに適用される cirq.Rx ゲートを持ったクラスタ状態として定義されます。

Qconv と QPool はこのチュートリアルで後で議論されます。

 

1.3 TensorFlow のためにブロックを構築する

この問題を TensorFlow Quantum で解く一つの方法は以下を実装することです :

  1. モデルへの入力は回路 tensor です — 空の回路か励起を示す特定の量子ビット上の X ゲートです。
  2. 残りのモデルの量子コンポーネントは tfq.layers.AddCircuit 層で構築されます。
  3. 推論のために tfq.layers.PQC が使用されます。これはそれを読む励起状態のための 1 か、非励起状態のための -1 のラベルと比較します。

 

1.4 データ

モデルを構築する前に、データを生成できます。この場合クラスタ状態への基底状態になります (元ペーパーはより複雑なデータセットを使用しています)。励起は cirq.Rx ゲートで表わされます。十分に大きい回転は励起と考えられて 1 とラベル付けられて、そして十分に大きくはない回転は -1 とラベル付けされて励起でないと考えられます。

def generate_data(qubits):
    """Generate training and testing data."""
    n_rounds = 20  # Produces n_rounds * n_qubits datapoints.
    excitations = []
    labels = []
    for n in range(n_rounds):
        for bit in qubits:
            rng = np.random.uniform(-np.pi, np.pi)
            excitations.append(cirq.Circuit(cirq.Rx(rng)(bit)))
            labels.append(1 if (-np.pi / 2) <= rng <= (np.pi / 2) else -1)

    split_ind = int(len(excitations) * 0.7)
    train_excitations = excitations[:split_ind]
    test_excitations = excitations[split_ind:]

    train_labels = labels[:split_ind]
    test_labels = labels[split_ind:]

    return tfq.convert_to_tensor(train_excitations), np.array(train_labels), \
        tfq.convert_to_tensor(test_excitations), np.array(test_labels)

ちょうど通常の機械学習でのように、モデルをベンチマークするために訓練とテストセットを作成することを見れます。次により幾つかのデータポイントを素早く見ることができます :

sample_points, sample_labels, _, __ = generate_data(cirq.GridQubit.rect(1, 4))
print('Input:', tfq.from_tensor(sample_points)[0], 'Output:', sample_labels[0])
print('Input:', tfq.from_tensor(sample_points)[1], 'Output:', sample_labels[1])
Input: (0, 0): ───Rx(0.521π)─── Output: -1
Input: (0, 1): ───Rx(0.073π)─── Output: 1

/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/ipykernel_launcher.py:9: DeprecationWarning: Rx was used but is deprecated.
It will be removed in cirq v0.8.0.
Use cirq.rx, instead.

  if __name__ == '__main__':

 

1.5 層を定義する

今は TensorFlow で上の図で示される層を定義します。

 

1.5.1 クラスタ状態

最初のステップは Cirq を使用して クラスタ状態 を定義します、量子回路をプログラミングするための Google 提供のフレームワークです。これはモデルの静的パートですから、tfq.layers.AddCircuit 機能を使用してそれを埋め込みます。

def cluster_state_circuit(bits):
    """Return a cluster state on the qubits in `bits`."""
    circuit = cirq.Circuit()
    circuit.append(cirq.H.on_each(bits))
    for this_bit, next_bit in zip(bits, bits[1:] + [bits[0]]):
        circuit.append(cirq.CZ(this_bit, next_bit))
    return circuit

cirq.GridQubits の矩形のためのクラスタ状態回路を表示します :

SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

 

1.5.2 QCNN 層

Cong と Lukin QCNN ペーパー を使用してモデルを構成する層を定義します。

2, 3 の必要条件があります :

  • Tucci ペーパー からの 1- と 2-量子ビットのパラメータ化されたユニタリ行列。
  • 一般的なパラメータ化された 2-量子ビット・プーリング演算。
def one_qubit_unitary(bit, symbols):
    """Make a Cirq circuit enacting a rotation of the bloch sphere about the X,
    Y and Z axis, that depends on the values in `symbols`.
    """
    return cirq.Circuit(
        cirq.X(bit)**symbols[0],
        cirq.Y(bit)**symbols[1],
        cirq.Z(bit)**symbols[2])


def two_qubit_unitary(bits, symbols):
    """Make a Cirq circuit that creates an arbitrary two qubit unitary."""
    circuit = cirq.Circuit()
    circuit += one_qubit_unitary(bits[0], symbols[0:3])
    circuit += one_qubit_unitary(bits[1], symbols[3:6])
    circuit += [cirq.ZZ(*bits)**symbols[7]]
    circuit += [cirq.YY(*bits)**symbols[8]]
    circuit += [cirq.XX(*bits)**symbols[9]]
    circuit += one_qubit_unitary(bits[0], symbols[9:12])
    circuit += one_qubit_unitary(bits[1], symbols[12:])
    return circuit


def two_qubit_pool(source_qubit, sink_qubit, symbols):
    """Make a Cirq circuit to do a parameterized 'pooling' operation, which
    attempts to reduce entanglement down from two qubits to just one."""
    pool_circuit = cirq.Circuit()
    sink_basis_selector = one_qubit_unitary(sink_qubit, symbols[0:3])
    source_basis_selector = one_qubit_unitary(source_qubit, symbols[3:6])
    pool_circuit.append(sink_basis_selector)
    pool_circuit.append(source_basis_selector)
    pool_circuit.append(cirq.CNOT(control=source_qubit, target=sink_qubit))
    pool_circuit.append(sink_basis_selector**-1)
    return pool_circuit

貴方が作成したものを見るために、1-量子ビットユニタリ回路をプリントアウトします :

SVGCircuit(one_qubit_unitary(cirq.GridQubit(0, 0), sympy.symbols('x0:3')))

そして 2-量子ビットユニタリ回路 :

SVGCircuit(two_qubit_unitary(cirq.GridQubit.rect(1, 2), sympy.symbols('x0:15')))

そして 2-量子ビット・プーリング回路 :

SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))

 

1.5.2.1 量子畳込み

Cong と Lukin ペーパーでのように、1D 量子畳込みを 1 のストライドを持つ隣接量子ビットの総てのペアへの 2-量子ビットのパラメータ化されたユニタリのアプリケーションとして定義します。

def quantum_conv_circuit(bits, symbols):
    """Quantum Convolution Layer following the above diagram.
    Return a Cirq circuit with the cascade of `two_qubit_unitary` applied
    to all pairs of qubits in `bits` as in the diagram above.
    """
    circuit = cirq.Circuit()
    for first, second in zip(bits[0::2], bits[1::2]):
        circuit += two_qubit_unitary([first, second], symbols)
    for first, second in zip(bits[1::2], bits[2::2] + [bits[0]]):
        circuit += two_qubit_unitary([first, second], symbols)
    return circuit

(非常に水平的な) 回路を表示します :

SVGCircuit(
    quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))

 

1.5.2.2 Quantum プーリング

量子プーリング層は上で定義された 2-量子ビットプールを使用して $N$ 量子ビットから $\frac{N}{2}$ 量子ビットへとプールします。

def quantum_pool_circuit(source_bits, sink_bits, symbols):
    """A layer that specifies a quantum pooling operation.
    A Quantum pool tries to learn to pool the relevant information from two
    qubits onto 1.
    """
    circuit = cirq.Circuit()
    for source, sink in zip(source_bits, sink_bits):
        circuit += two_qubit_pool(source, sink, symbols)
    return circuit

プーリング・コンポーネント回路を検証します :

test_bits = cirq.GridQubit.rect(1, 8)

SVGCircuit(
    quantum_pool_circuit(test_bits[:4], test_bits[4:], sympy.symbols('x0:6')))

 

1.6 モデル定義

今は純粋に量子 CNN を構築するために定義された層を使用します。8 量子ビットから始めて、1 にプールダウンし、それから $\langle \hat{Z} \rangle$ を測定します。

def create_model_circuit(qubits):
    """Create sequence of alternating convolution and pooling operators 
    which gradually shrink over time."""
    model_circuit = cirq.Circuit()
    symbols = sympy.symbols('qconv0:63')
    # Cirq uses sympy.Symbols to map learnable variables. TensorFlow Quantum
    # scans incoming circuits and replaces these with TensorFlow variables.
    model_circuit += quantum_conv_circuit(qubits, symbols[0:15])
    model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:],
                                          symbols[15:21])
    model_circuit += quantum_conv_circuit(qubits[4:], symbols[21:36])
    model_circuit += quantum_pool_circuit(qubits[4:6], qubits[6:],
                                          symbols[36:42])
    model_circuit += quantum_conv_circuit(qubits[6:], symbols[42:57])
    model_circuit += quantum_pool_circuit([qubits[6]], [qubits[7]],
                                          symbols[57:63])
    return model_circuit


# Create our qubits and readout operators in Cirq.
cluster_state_bits = cirq.GridQubit.rect(1, 8)
readout_operators = cirq.Z(cluster_state_bits[-1])

# Build a sequential model enacting the logic in 1.3 of this notebook.
# Here you are making the static cluster state prep as a part of the AddCircuit and the
# "quantum datapoints" are coming in the form of excitation
excitation_input = tf.keras.Input(shape=(), dtype=tf.dtypes.string)
cluster_state = tfq.layers.AddCircuit()(
    excitation_input, prepend=cluster_state_circuit(cluster_state_bits))

quantum_model = tfq.layers.PQC(create_model_circuit(cluster_state_bits),
                               readout_operators)(cluster_state)

qcnn_model = tf.keras.Model(inputs=[excitation_input], outputs=[quantum_model])

# Show the keras plot of the model
tf.keras.utils.plot_model(qcnn_model,
                          show_shapes=True,
                          show_layer_names=False,
                          dpi=70)
Failed to import pydot. You must install pydot and graphviz for `pydotprint` to work.

 

1.7 モデルを訓練する

このサンプルを単純化するために full バッチに渡りモデルを訓練します。

# Generate some training data.
train_excitations, train_labels, test_excitations, test_labels = generate_data(
    cluster_state_bits)


# Custom accuracy metric.
@tf.function
def custom_accuracy(y_true, y_pred):
    y_true = tf.squeeze(y_true)
    y_pred = tf.map_fn(lambda x: 1.0 if x >= 0 else -1.0, y_pred)
    return tf.keras.backend.mean(tf.keras.backend.equal(y_true, y_pred))


qcnn_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),
                   loss=tf.losses.mse,
                   metrics=[custom_accuracy])

history = qcnn_model.fit(x=train_excitations,
                         y=train_labels,
                         batch_size=16,
                         epochs=25,
                         verbose=1,
                         validation_data=(test_excitations, test_labels))
/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/ipykernel_launcher.py:9: DeprecationWarning: Rx was used but is deprecated.
It will be removed in cirq v0.8.0.
Use cirq.rx, instead.

  if __name__ == '__main__':

Train on 112 samples, validate on 48 samples
Epoch 1/25
112/112 [==============================] - 9s 80ms/sample - loss: 0.9000 - custom_accuracy: 0.6964 - val_loss: 0.8341 - val_custom_accuracy: 0.7708
Epoch 2/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.8153 - custom_accuracy: 0.7589 - val_loss: 0.7915 - val_custom_accuracy: 0.6875
Epoch 3/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7882 - custom_accuracy: 0.6786 - val_loss: 0.7726 - val_custom_accuracy: 0.6875
Epoch 4/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7570 - custom_accuracy: 0.6786 - val_loss: 0.7746 - val_custom_accuracy: 0.7292
Epoch 5/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7500 - custom_accuracy: 0.8214 - val_loss: 0.7413 - val_custom_accuracy: 0.7708
Epoch 6/25
112/112 [==============================] - 4s 36ms/sample - loss: 0.7262 - custom_accuracy: 0.7857 - val_loss: 0.7177 - val_custom_accuracy: 0.7708
Epoch 7/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7164 - custom_accuracy: 0.7857 - val_loss: 0.7337 - val_custom_accuracy: 0.7708
Epoch 8/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7096 - custom_accuracy: 0.8214 - val_loss: 0.7230 - val_custom_accuracy: 0.7917
Epoch 9/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7040 - custom_accuracy: 0.7857 - val_loss: 0.7200 - val_custom_accuracy: 0.7917
Epoch 10/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7015 - custom_accuracy: 0.8125 - val_loss: 0.7186 - val_custom_accuracy: 0.7500
Epoch 11/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7074 - custom_accuracy: 0.8125 - val_loss: 0.7207 - val_custom_accuracy: 0.8333
Epoch 12/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7030 - custom_accuracy: 0.8036 - val_loss: 0.7212 - val_custom_accuracy: 0.7500
Epoch 13/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7061 - custom_accuracy: 0.8214 - val_loss: 0.7200 - val_custom_accuracy: 0.7917
Epoch 14/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6991 - custom_accuracy: 0.8125 - val_loss: 0.7059 - val_custom_accuracy: 0.7708
Epoch 15/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6967 - custom_accuracy: 0.7946 - val_loss: 0.7286 - val_custom_accuracy: 0.7500
Epoch 16/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7054 - custom_accuracy: 0.7768 - val_loss: 0.7114 - val_custom_accuracy: 0.7292
Epoch 17/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7014 - custom_accuracy: 0.8036 - val_loss: 0.7059 - val_custom_accuracy: 0.8125
Epoch 18/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7000 - custom_accuracy: 0.8125 - val_loss: 0.7147 - val_custom_accuracy: 0.7708
Epoch 19/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.7052 - custom_accuracy: 0.7946 - val_loss: 0.7162 - val_custom_accuracy: 0.7708
Epoch 20/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6977 - custom_accuracy: 0.7768 - val_loss: 0.7132 - val_custom_accuracy: 0.7708
Epoch 21/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6922 - custom_accuracy: 0.8125 - val_loss: 0.7051 - val_custom_accuracy: 0.8333
Epoch 22/25
112/112 [==============================] - 4s 38ms/sample - loss: 0.6853 - custom_accuracy: 0.8036 - val_loss: 0.6983 - val_custom_accuracy: 0.7917
Epoch 23/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6894 - custom_accuracy: 0.8125 - val_loss: 0.7099 - val_custom_accuracy: 0.8333
Epoch 24/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6918 - custom_accuracy: 0.8036 - val_loss: 0.7101 - val_custom_accuracy: 0.7708
Epoch 25/25
112/112 [==============================] - 4s 37ms/sample - loss: 0.6844 - custom_accuracy: 0.8125 - val_loss: 0.7042 - val_custom_accuracy: 0.8125
plt.plot(history.history['loss'][1:], label='Training')
plt.plot(history.history['val_loss'][1:], label='Validation')
plt.title('Training a Quantum CNN to Detect Excited Cluster States')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

 

2. ハイブリッドモデル

量子畳込みを使用して 8 量子ビットから 1 量子ビットに進まなくてもかまいません — 量子畳込みの 1 or 2 ラウンドは遂行してそして結果を古典的ニューラルネットワークに供給できたでしょう。このセクションは量子古典的ハイブリッドモデルを調査します。

 

2.1 シングル量子フィルタを持つハイブリッドモデル

量子畳込みの 1 層を適用し、総てのビット上で $\langle \hat{Z}_n \rangle$ を読み出し、密結合ニューラルネットワークが続きます。

 

2.1.1 モデル定義

# 1-local operators to read out
readouts = [cirq.Z(bit) for bit in cluster_state_bits[4:]]


def multi_readout_model_circuit(qubits):
    """Make a model circuit with less quantum pool and conv operations."""
    model_circuit = cirq.Circuit()
    symbols = sympy.symbols('qconv0:21')
    model_circuit += quantum_conv_circuit(qubits, symbols[0:15])
    model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:],
                                          symbols[15:21])
    return model_circuit


# Build a model enacting the logic in 2.1 of this notebook.
excitation_input_dual = tf.keras.Input(shape=(), dtype=tf.dtypes.string)

cluster_state_dual = tfq.layers.AddCircuit()(
    excitation_input_dual, prepend=cluster_state_circuit(cluster_state_bits))

quantum_model_dual = tfq.layers.PQC(
    multi_readout_model_circuit(cluster_state_bits),
    readouts)(cluster_state_dual)

d1_dual = tf.keras.layers.Dense(8)(quantum_model_dual)

d2_dual = tf.keras.layers.Dense(1)(d1_dual)

hybrid_model = tf.keras.Model(inputs=[excitation_input_dual], outputs=[d2_dual])

# Display the model architecture
tf.keras.utils.plot_model(hybrid_model,
                          show_shapes=True,
                          show_layer_names=False,
                          dpi=70)
Failed to import pydot. You must install pydot and graphviz for `pydotprint` to work.

 

2.1.2 モデルを訓練する

hybrid_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),
                     loss=tf.losses.mse,
                     metrics=[custom_accuracy])

hybrid_history = hybrid_model.fit(x=train_excitations,
                                  y=train_labels,
                                  batch_size=16,
                                  epochs=25,
                                  verbose=1,
                                  validation_data=(test_excitations,
                                                   test_labels))
Train on 112 samples, validate on 48 samples
Epoch 1/25
112/112 [==============================] - 2s 17ms/sample - loss: 0.8153 - custom_accuracy: 0.7054 - val_loss: 0.5564 - val_custom_accuracy: 0.9375
Epoch 2/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.3411 - custom_accuracy: 0.9464 - val_loss: 0.2293 - val_custom_accuracy: 0.9792
Epoch 3/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2589 - custom_accuracy: 0.9375 - val_loss: 0.3495 - val_custom_accuracy: 0.9375
Epoch 4/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2937 - custom_accuracy: 0.9286 - val_loss: 0.3041 - val_custom_accuracy: 0.9375
Epoch 5/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2349 - custom_accuracy: 0.9375 - val_loss: 0.2303 - val_custom_accuracy: 0.9583
Epoch 6/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2100 - custom_accuracy: 0.9732 - val_loss: 0.2183 - val_custom_accuracy: 0.9583
Epoch 7/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2265 - custom_accuracy: 0.9643 - val_loss: 0.2752 - val_custom_accuracy: 0.9583
Epoch 8/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2341 - custom_accuracy: 0.9464 - val_loss: 0.2536 - val_custom_accuracy: 0.9375
Epoch 9/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2451 - custom_accuracy: 0.9643 - val_loss: 0.2470 - val_custom_accuracy: 0.9375
Epoch 10/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2159 - custom_accuracy: 0.9554 - val_loss: 0.3146 - val_custom_accuracy: 0.9583
Epoch 11/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2235 - custom_accuracy: 0.9643 - val_loss: 0.2395 - val_custom_accuracy: 0.9583
Epoch 12/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2073 - custom_accuracy: 0.9821 - val_loss: 0.2392 - val_custom_accuracy: 0.9375
Epoch 13/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2059 - custom_accuracy: 0.9643 - val_loss: 0.2321 - val_custom_accuracy: 0.9583
Epoch 14/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2076 - custom_accuracy: 0.9554 - val_loss: 0.2758 - val_custom_accuracy: 0.9792
Epoch 15/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2035 - custom_accuracy: 0.9821 - val_loss: 0.2312 - val_custom_accuracy: 0.9375
Epoch 16/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.1956 - custom_accuracy: 0.9821 - val_loss: 0.2307 - val_custom_accuracy: 0.9375
Epoch 17/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.1957 - custom_accuracy: 0.9821 - val_loss: 0.2876 - val_custom_accuracy: 0.9583
Epoch 18/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2172 - custom_accuracy: 0.9732 - val_loss: 0.2441 - val_custom_accuracy: 0.9583
Epoch 19/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2082 - custom_accuracy: 0.9643 - val_loss: 0.2372 - val_custom_accuracy: 0.9583
Epoch 20/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.1945 - custom_accuracy: 0.9911 - val_loss: 0.2340 - val_custom_accuracy: 0.9583
Epoch 21/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.1993 - custom_accuracy: 0.9821 - val_loss: 0.2309 - val_custom_accuracy: 0.9583
Epoch 22/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2095 - custom_accuracy: 0.9732 - val_loss: 0.2818 - val_custom_accuracy: 0.9583
Epoch 23/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2153 - custom_accuracy: 0.9732 - val_loss: 0.2753 - val_custom_accuracy: 0.9583
Epoch 24/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.2029 - custom_accuracy: 0.9554 - val_loss: 0.2344 - val_custom_accuracy: 0.9375
Epoch 25/25
112/112 [==============================] - 1s 9ms/sample - loss: 0.1942 - custom_accuracy: 0.9821 - val_loss: 0.2577 - val_custom_accuracy: 0.9583
plt.plot(history.history['val_custom_accuracy'], label='QCNN')
plt.plot(hybrid_history.history['val_custom_accuracy'], label='Hybrid CNN')
plt.title('Quantum vs Hybrid CNN performance')
plt.xlabel('Epochs')
plt.legend()
plt.ylabel('Validation Accuracy')
plt.show()

見れるように、非常に控えめな古典的援助により、ハイブリッドモデルは通常は純粋な量子バージョンよりも早く収束します。

 

2.2 マルチ量子フィルタを持つハイブリッド畳込み

今はマルチ量子畳込みとそれらを結合する古典的ニューラルネットワークを使用するアーキテクチャを試しましょう。

 

2.2.1 モデル定義

excitation_input_multi = tf.keras.Input(shape=(), dtype=tf.dtypes.string)

cluster_state_multi = tfq.layers.AddCircuit()(
    excitation_input_multi, prepend=cluster_state_circuit(cluster_state_bits))

# apply 3 different filters and measure expectation values

quantum_model_multi1 = tfq.layers.PQC(
    multi_readout_model_circuit(cluster_state_bits),
    readouts)(cluster_state_multi)

quantum_model_multi2 = tfq.layers.PQC(
    multi_readout_model_circuit(cluster_state_bits),
    readouts)(cluster_state_multi)

quantum_model_multi3 = tfq.layers.PQC(
    multi_readout_model_circuit(cluster_state_bits),
    readouts)(cluster_state_multi)

# concatenate outputs and feed into a small classical NN
concat_out = tf.keras.layers.concatenate(
    [quantum_model_multi1, quantum_model_multi2, quantum_model_multi3])

dense_1 = tf.keras.layers.Dense(8)(concat_out)

dense_2 = tf.keras.layers.Dense(1)(dense_1)

multi_qconv_model = tf.keras.Model(inputs=[excitation_input_multi],
                                   outputs=[dense_2])

# Display the model architecture
tf.keras.utils.plot_model(multi_qconv_model,
                          show_shapes=True,
                          show_layer_names=True,
                          dpi=70)
Failed to import pydot. You must install pydot and graphviz for `pydotprint` to work.

 

2.2.2 モデルを訓練する

multi_qconv_model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),
    loss=tf.losses.mse,
    metrics=[custom_accuracy])

multi_qconv_history = multi_qconv_model.fit(x=train_excitations,
                                            y=train_labels,
                                            batch_size=16,
                                            epochs=25,
                                            verbose=1,
                                            validation_data=(test_excitations,
                                                             test_labels))
Train on 112 samples, validate on 48 samples
Epoch 1/25
112/112 [==============================] - 3s 31ms/sample - loss: 0.9056 - custom_accuracy: 0.5982 - val_loss: 0.7414 - val_custom_accuracy: 0.7708
Epoch 2/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.6125 - custom_accuracy: 0.7768 - val_loss: 0.3953 - val_custom_accuracy: 0.8958
Epoch 3/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2967 - custom_accuracy: 0.9196 - val_loss: 0.2199 - val_custom_accuracy: 0.9583
Epoch 4/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2299 - custom_accuracy: 0.9643 - val_loss: 0.3186 - val_custom_accuracy: 0.9375
Epoch 5/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2521 - custom_accuracy: 0.9643 - val_loss: 0.2378 - val_custom_accuracy: 0.9792
Epoch 6/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2026 - custom_accuracy: 0.9911 - val_loss: 0.2087 - val_custom_accuracy: 0.9792
Epoch 7/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2090 - custom_accuracy: 0.9643 - val_loss: 0.3530 - val_custom_accuracy: 0.8958
Epoch 8/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2213 - custom_accuracy: 0.9821 - val_loss: 0.2496 - val_custom_accuracy: 0.9583
Epoch 9/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2120 - custom_accuracy: 0.9821 - val_loss: 0.2079 - val_custom_accuracy: 0.9583
Epoch 10/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2031 - custom_accuracy: 0.9643 - val_loss: 0.2319 - val_custom_accuracy: 0.9792
Epoch 11/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2244 - custom_accuracy: 0.9732 - val_loss: 0.2170 - val_custom_accuracy: 0.9583
Epoch 12/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2070 - custom_accuracy: 0.9911 - val_loss: 0.2239 - val_custom_accuracy: 1.0000
Epoch 13/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2096 - custom_accuracy: 0.9911 - val_loss: 0.2161 - val_custom_accuracy: 0.9583
Epoch 14/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2055 - custom_accuracy: 0.9911 - val_loss: 0.2395 - val_custom_accuracy: 0.9375
Epoch 15/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2158 - custom_accuracy: 1.0000 - val_loss: 0.2054 - val_custom_accuracy: 0.9583
Epoch 16/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2225 - custom_accuracy: 0.9554 - val_loss: 0.2311 - val_custom_accuracy: 0.9583
Epoch 17/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.1916 - custom_accuracy: 0.9821 - val_loss: 0.2121 - val_custom_accuracy: 0.9792
Epoch 18/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.1959 - custom_accuracy: 0.9732 - val_loss: 0.2517 - val_custom_accuracy: 0.9792
Epoch 19/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2069 - custom_accuracy: 0.9643 - val_loss: 0.2497 - val_custom_accuracy: 0.9375
Epoch 20/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2071 - custom_accuracy: 0.9911 - val_loss: 0.2162 - val_custom_accuracy: 1.0000
Epoch 21/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.1911 - custom_accuracy: 0.9911 - val_loss: 0.2114 - val_custom_accuracy: 0.9583
Epoch 22/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.1994 - custom_accuracy: 0.9911 - val_loss: 0.2173 - val_custom_accuracy: 0.9583
Epoch 23/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.1924 - custom_accuracy: 0.9643 - val_loss: 0.2548 - val_custom_accuracy: 0.9583
Epoch 24/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.2113 - custom_accuracy: 0.9911 - val_loss: 0.2084 - val_custom_accuracy: 0.9583
Epoch 25/25
112/112 [==============================] - 2s 14ms/sample - loss: 0.1952 - custom_accuracy: 0.9821 - val_loss: 0.2699 - val_custom_accuracy: 0.9583
plt.plot(history.history['val_custom_accuracy'][:25], label='QCNN')
plt.plot(hybrid_history.history['val_custom_accuracy'][:25], label='Hybrid CNN')
plt.plot(multi_qconv_history.history['val_custom_accuracy'][:25],
         label='Hybrid CNN \n Multiple Quantum Filters')
plt.title('Quantum vs Hybrid CNN performance')
plt.xlabel('Epochs')
plt.legend()
plt.ylabel('Validation Accuracy')
plt.show()

 

以上






TensorFlow Quantum 0.2.0 : Tutorials : 勾配を計算する

TensorFlow Quantum 0.2.0 Tutorials : 勾配を計算する (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/15/2020 (0.2.0)

* 本ページは、TensorFlow Quantum の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Tutorials : 勾配を計算する

このチュートリアルは量子回路の期待値のための勾配計算アルゴリズムを探究します。ある可観測量の期待値の勾配を計算することは複雑なプロセスです。可観測量の期待値は書き下すことが常に容易な解析的勾配公式のような高級品は持ちません — 書き下すことが容易な解析的勾配公式を持つ行列乗算やベクトル加算のような伝統的な機械学習変換と違って。結果的に、異なるシナリオに対して役立つ異なる量子勾配計算法があります。このチュートリアルは 2 つの異なる微分スキームを比較して制約します。

 

セットアップ

TensorFlow Quantum をインストールします :

pip install -q tensorflow-quantum

今は TensorFlow とモジュール依存性をインポートします :

import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit

 

1. 準備

量子回路のための勾配計算の概念をもう少し具体的にしましょう。この一つのようなパラメータ化された回路を持つと仮定します :

qubit = cirq.GridQubit(0, 0)
my_circuit = cirq.Circuit(cirq.Y(qubit)**sympy.Symbol('alpha'))
SVGCircuit(my_circuit)
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

可観測量とともに :

pauli_x = cirq.X(qubit)
pauli_x
cirq.X.on(cirq.GridQubit(0, 0))

貴方が知るこの演算子 $⟨Y(\alpha)| X | Y(\alpha)⟩ = \sin(\pi \alpha)$ を見ます

def my_expectation(op, alpha):
    """Compute ⟨Y(alpha)| `op` | Y(alpha)⟩"""
    params = {'alpha': alpha}
    sim = cirq.Simulator()
    final_state = sim.simulate(my_circuit, params).final_state
    return op.expectation_from_wavefunction(final_state, {qubit: 0}).real


my_alpha = 0.3
print("Expectation=", my_expectation(pauli_x, my_alpha))
print("Sin Formula=", np.sin(np.pi * my_alpha))
Expectation= 0.80901700258255
Sin Formula= 0.8090169943749475

そして $f_{1}(\alpha) = ⟨Y(\alpha)| X | Y(\alpha)⟩$ を定義すれば $f_{1}^{'}(\alpha) = \pi \cos(\pi \alpha)$ です。これを確認しましょう :

def my_grad(obs, alpha, eps=0.01):
    grad = 0
    f_x = my_expectation(obs, alpha)
    f_x_prime = my_expectation(obs, alpha + eps)
    return ((f_x_prime - f_x) / eps).real


print('Finite difference:', my_grad(pauli_x, my_alpha))
print('Cosine formula:   ', np.pi * np.cos(np.pi * my_alpha))
Finite difference: 1.8063604831695557
Cosine formula:    1.8465818304904567

 

微分器の必要性

より大きな回路では、与えられた量子回路の勾配を正確に計算する公式を常に持つほど幸運ではありません。単純な公式が勾配を計算するために十分ではないイベントでは、tfq.differentiators.Differentiator クラスが貴方の回路の勾配を計算するためのアルゴリズムを定義することを可能にします。例えば TensorFlow Quantum (TFQ) の上のサンプルを次により再作成することができます :

expectation_calculation = tfq.layers.Expectation(
    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))

expectation_calculation(my_circuit,
                        operators=pauli_x,
                        symbol_names=['alpha'],
                        symbol_values=[[my_alpha]])
<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.8090171]], dtype=float32)>

けれども、(真のデバイス上で発生するであろう) サンプリングに基づいて期待値を推定するように切り替える場合、値は少し変わる可能性があります。これは今は不完全な推定を持つことを意味します :

sampled_expectation_calculation = tfq.layers.SampledExpectation(
    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))

sampled_expectation_calculation(my_circuit,
                                operators=pauli_x,
                                repetitions=500,
                                symbol_names=['alpha'],
                                symbol_values=[[my_alpha]])
<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.816]], dtype=float32)>

勾配に関して言えばこれは直ちに重要な精度の問題を混在する可能性があります :

# Make input_points = [batch_size, 1] array.
input_points = np.linspace(0, 5, 200)[:, np.newaxis].astype(np.float32)
exact_outputs = expectation_calculation(my_circuit,
                                        operators=pauli_x,
                                        symbol_names=['alpha'],
                                        symbol_values=input_points)
imperfect_outputs = sampled_expectation_calculation(my_circuit,
                                                    operators=pauli_x,
                                                    repetitions=500,
                                                    symbol_names=['alpha'],
                                                    symbol_values=input_points)
plt.title('Forward Pass Values')
plt.xlabel('$x$')
plt.ylabel('$f(x)$')
plt.plot(input_points, exact_outputs, label='Analytic')
plt.plot(input_points, imperfect_outputs, label='Sampled')
plt.legend()
<matplotlib.legend.Legend at 0x7f1e546b1278>

# Gradients are a much different story.
values_tensor = tf.convert_to_tensor(input_points)

with tf.GradientTape() as g:
    g.watch(values_tensor)
    exact_outputs = expectation_calculation(my_circuit,
                                            operators=pauli_x,
                                            symbol_names=['alpha'],
                                            symbol_values=values_tensor)
analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)

with tf.GradientTape() as g:
    g.watch(values_tensor)
    imperfect_outputs = sampled_expectation_calculation(
        my_circuit,
        operators=pauli_x,
        repetitions=500,
        symbol_names=['alpha'],
        symbol_values=values_tensor)
sampled_finite_diff_gradients = g.gradient(imperfect_outputs, values_tensor)

plt.title('Gradient Values')
plt.xlabel('$x$')
plt.ylabel('$f^{\'}(x)$')
plt.plot(input_points, analytic_finite_diff_gradients, label='Analytic')
plt.plot(input_points, sampled_finite_diff_gradients, label='Sampled')
plt.legend()
<matplotlib.legend.Legend at 0x7f1e2c740a20>

ここで、有限差分式は解析的なケースでは勾配自身を計算するために高速ですが、サンプリングベースの方法ではそれは非常に noisy であったことを見ることができます。良い勾配が計算できることを確実にするためにより注意深いテクニックが使用されなければなりません。次に解析的期待値勾配計算のためには上手く適合しないが、しかし現実世界のサンプリングベースのケースで遥かに良く遂行する、遥かに遅いテクニックを見ます :

# A smarter differentiation scheme.
gradient_safe_sampled_expectation = tfq.layers.SampledExpectation(
    differentiator=tfq.differentiators.ParameterShift())

with tf.GradientTape() as g:
    g.watch(values_tensor)
    imperfect_outputs = gradient_safe_sampled_expectation(
        my_circuit,
        operators=pauli_x,
        repetitions=500,
        symbol_names=['alpha'],
        symbol_values=values_tensor)

sampled_param_shift_gradients = g.gradient(imperfect_outputs, values_tensor)

plt.title('Gradient Values')
plt.xlabel('$x$')
plt.ylabel('$f^{\'}(x)$')
plt.plot(input_points, analytic_finite_diff_gradients, label='Analytic')
plt.plot(input_points, sampled_param_shift_gradients, label='Sampled')
plt.legend()
WARNING:tensorflow:AutoGraph could not transform > and will run it as-is.
Please report this to the TensorFlow team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output.
Cause: unexpected indent (, line 77)
WARNING: AutoGraph could not transform > and will run it as-is.
Please report this to the TensorFlow team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output.
Cause: unexpected indent (, line 77)

<matplotlib.legend.Legend at 0x7f1e2c5a79b0>

上からある微分器が特定の研究シナリオのために最善に使用されることを見ることができます。一般に、デバイスノイズ等に堅牢な遅いサンプルベースの方法はより「現実世界」設定でアルゴリズムをテストまたは実装するとき素晴らしい微分器です。有限差分のような高速な方法は解析的計算のために素晴らしくそして貴方はより高い処理能力を望みますが、アルゴリズムのデバイス実行可能性にはまだ関心を持っていません。

 

3. マルチ可観測量

2 番目の可観測量を導入して TensorFlow Quantum が単一回路のためのマルチ可観測量をどのようにサポートするかを見ましょう。

pauli_z = cirq.Z(qubit)
pauli_z
cirq.Z.on(cirq.GridQubit(0, 0))

この可観測量が前と同じ回路で使用される場合、$f_{2}(\alpha) = ⟨Y(\alpha)| Z | Y(\alpha)⟩ = \cos(\pi \alpha)$ と $f_{2}^{'}(\alpha) = -\pi \sin(\pi \alpha)$ を持ちます。素早い確認をしましょう :

test_value = 0.

print('Finite difference:', my_grad(pauli_z, test_value))
print('Sin formula:      ', -np.pi * np.sin(np.pi * test_value))
Finite difference: -0.04934072494506836
Sin formula:       -0.0

それは一致です (十分に密接)。

今 $g(\alpha) = f_{1}(\alpha) + f_{2}(\alpha)$ を定義すれば $g'(\alpha) = f_{1}^{'}(\alpha) + f^{'}_{2}(\alpha)$ です。回路と一緒に使用するために TensorFlow Quantum で 1 つ以上の可観測量を定義することはより多くの項で $g$ に追加することと同値です。

回路の特定のシンボルの勾配は (回路に適用された) そのシンボルのための各可観測量に関する勾配の総計に同値であることを意味します。これは TensorFlow 勾配と逆伝播に互換です (そこでは特定のシンボルのための勾配として総ての可観測量に渡る勾配の総計を与えます)。

sum_of_outputs = tfq.layers.Expectation(
    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))

sum_of_outputs(my_circuit,
               operators=[pauli_x, pauli_z],
               symbol_names=['alpha'],
               symbol_values=[[test_value]])
<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[0., 1.]], dtype=float32)>

ここで最初のエントリは Pauli X に関する期待値で、2 番目は Pauli Z に関する期待値であることを見ます。今次のように勾配を取るとき :

test_value_tensor = tf.convert_to_tensor([[test_value]])

with tf.GradientTape() as g:
    g.watch(test_value_tensor)
    outputs = sum_of_outputs(my_circuit,
                             operators=[pauli_x, pauli_z],
                             symbol_names=['alpha'],
                             symbol_values=test_value_tensor)

sum_of_gradients = g.gradient(outputs, test_value_tensor)

print(my_grad(pauli_x, test_value) + my_grad(pauli_z, test_value))
print(sum_of_gradients.numpy())
3.0917350202798843
[[3.0917215]]

ここで各可観測量のための勾配の総計が実際に $\alpha$ の勾配であることを検証しました。この動作は総ての TensorFlow Quantum 微分器でサポートされて TensorFlow のその他の部分との互換性で重要な役割りを果たします。

 

4. 上級使用方法

ここでは量子回路のために貴方自身のカスタム微分器ルーチンをどのように定義するかを学習します。TensorFlow Quantum の内側に存在する総ての微分器は tfq.differentiators.Differentiator をサブクラス化しています。微分器は differentiate_analytic と differentiate_sampled を実装しなければなりません。

次はこのチュートリアルの最初のパートから closed 形式解を実装するために TensorFlow Quantum 構成物を使用します。

class MyDifferentiator(tfq.differentiators.Differentiator):
    """A Toy differentiator for ."""

    def __init__(self):
        pass

    @tf.function
    def _compute_gradient(self, symbol_values):
        """Compute the gradient based on symbol_values."""

        # f(x) = sin(pi * x)
        # f'(x) = pi * cos(pi * x)
        return tf.cast(tf.cos(symbol_values * np.pi) * np.pi, tf.float32)

    @tf.function
    def differentiate_analytic(self, programs, symbol_names, symbol_values,
                               pauli_sums, forward_pass_vals, grad):
        """Specify how to differentiate a circuit with analytical expectation.

        This is called at graph runtime by TensorFlow. `differentiate_analytic`
        should calculate the gradient of a batch of circuits and return it
        formatted as indicated below. See
        `tfq.differentiators.ForwardDifference` for an example.

        Args:
            programs: `tf.Tensor` of strings with shape [batch_size] containing
                the string representations of the circuits to be executed.
            symbol_names: `tf.Tensor` of strings with shape [n_params], which
                is used to specify the order in which the values in
                `symbol_values` should be placed inside of the circuits in
                `programs`.
            symbol_values: `tf.Tensor` of real numbers with shape
                [batch_size, n_params] specifying parameter values to resolve
                into the circuits specified by programs, following the ordering
                dictated by `symbol_names`.
            pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]
                containing the string representation of the operators that will
                be used on all of the circuits in the expectation calculations.
            forward_pass_vals: `tf.Tensor` of real numbers with shape
                [batch_size, n_ops] containing the output of the forward pass
                through the op you are differentiating.
            grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]
                representing the gradient backpropagated to the output of the
                op you are differentiating through.

        Returns:
            A `tf.Tensor` with the same shape as `symbol_values` representing
            the gradient backpropagated to the `symbol_values` input of the op
            you are differentiating through.
        """

        # Computing gradients just based off of symbol_values.
        return self._compute_gradient(symbol_values) * grad

    @tf.function
    def differentiate_sampled(self, programs, symbol_names, symbol_values,
                              pauli_sums, num_samples, forward_pass_vals, grad):
        """Specify how to differentiate a circuit with sampled expectation.

        This is called at graph runtime by TensorFlow. `differentiate_sampled`
        should calculate the gradient of a batch of circuits and return it
        formatted as indicated below. See
        `tfq.differentiators.ForwardDifference` for an example.

        Args:
            programs: `tf.Tensor` of strings with shape [batch_size] containing
                the string representations of the circuits to be executed.
            symbol_names: `tf.Tensor` of strings with shape [n_params], which
                is used to specify the order in which the values in
                `symbol_values` should be placed inside of the circuits in
                `programs`.
            symbol_values: `tf.Tensor` of real numbers with shape
                [batch_size, n_params] specifying parameter values to resolve
                into the circuits specified by programs, following the ordering
                dictated by `symbol_names`.
            pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]
                containing the string representation of the operators that will
                be used on all of the circuits in the expectation calculations.
            num_samples: `tf.Tensor` of positive integers representing the
                number of samples per term in each term of pauli_sums used
                during the forward pass.
            forward_pass_vals: `tf.Tensor` of real numbers with shape
                [batch_size, n_ops] containing the output of the forward pass
                through the op you are differentiating.
            grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]
                representing the gradient backpropagated to the output of the
                op you are differentiating through.

        Returns:
            A `tf.Tensor` with the same shape as `symbol_values` representing
            the gradient backpropagated to the `symbol_values` input of the op
            you are differentiating through.
        """
        return self._compute_gradient(symbol_values) * grad

この新しい微分器は今では既存の tfq.layer オブジェクトで使用できます :

custom_dif = MyDifferentiator()
custom_grad_expectation = tfq.layers.Expectation(differentiator=custom_dif)

# Now let's get the gradients with finite diff.
with tf.GradientTape() as g:
    g.watch(values_tensor)
    exact_outputs = expectation_calculation(my_circuit,
                                            operators=[pauli_x],
                                            symbol_names=['alpha'],
                                            symbol_values=values_tensor)

analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)

# Now let's get the gradients with custom diff.
with tf.GradientTape() as g:
    g.watch(values_tensor)
    my_outputs = custom_grad_expectation(my_circuit,
                                         operators=[pauli_x],
                                         symbol_names=['alpha'],
                                         symbol_values=values_tensor)

my_gradients = g.gradient(my_outputs, values_tensor)

plt.subplot(1, 2, 1)
plt.title('Exact Gradient')
plt.plot(input_points, analytic_finite_diff_gradients.numpy())
plt.xlabel('x')
plt.ylabel('f(x)')
plt.subplot(1, 2, 2)
plt.title('My Gradient')
plt.plot(input_points, my_gradients.numpy())
plt.xlabel('x')
Text(0.5, 0, 'x')

新しい微分器は今では微分可能な ops を生成するために使用できます。

Key Point: 以前に op に装着された微分器は新しい op に装着する前にリフレッシュされなければなりません、何故ならば微分器は一度に 1 つの op だけに装着が許されるからです。

# Create a noisy sample based expectation op.
expectation_sampled = tfq.get_sampled_expectation_op(
    cirq.DensityMatrixSimulator(noise=cirq.depolarize(0.01)))

# Make it differentiable with your differentiator:
# Remember to refresh the differentiator before attaching the new op
custom_dif.refresh()
differentiable_op = custom_dif.generate_differentiable_op(
    sampled_op=expectation_sampled)

# Prep op inputs.
circuit_tensor = tfq.convert_to_tensor([my_circuit])
op_tensor = tfq.convert_to_tensor([[pauli_x]])
single_value = tf.convert_to_tensor([[my_alpha]])
num_samples_tensor = tf.convert_to_tensor([[1000]])

with tf.GradientTape() as g:
    g.watch(single_value)
    forward_output = differentiable_op(circuit_tensor, ['alpha'], single_value,
                                       op_tensor, num_samples_tensor)

my_gradients = g.gradient(forward_output, single_value)

print('---TFQ---')
print('Foward:  ', forward_output.numpy())
print('Gradient:', my_gradients.numpy())
print('---Original---')
print('Forward: ', my_expectation(pauli_x, my_alpha))
print('Gradient:', my_grad(pauli_x, my_alpha))
---TFQ---
Foward:   [[0.79]]
Gradient: [[1.8465817]]
---Original---
Forward:  0.80901700258255
Gradient: 1.8063604831695557
 

以上






TensorFlow Quantum 0.2.0 : Tutorials : MNIST 分類

TensorFlow Quantum 0.2.0 Tutorials : MNIST 分類 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/14/2020 (0.2.0)

* 本ページは、TensorFlow Quantum の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Tutorials : MNIST 分類

このチュートリアルは MNIST の単純化されたバージョンを分類するために量子ニューラルネットワーク (QNN) を構築します、Farhi et al で使用されたアプローチに類似しています。この古典的データ問題上の量子ニューラルネットワークのパフォーマンスは古典的ニューラルネットワークと比較されます。

 

セットアップ

TensorFlow Quantum をインストールします :

pip install -q tensorflow-quantum

今は TensorFlow とモジュール依存性をインポートします :

import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np
import seaborn as sns
import collections

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit

 

1. データをロードする

このチュートリアルでは Farhi et al に従って、数字 3 と 6 の間を識別する二値分類器を構築します。このセクションは次のデータ処理をカバーします :

  • Keras から raw データをロードします。
  • データセットを 3 と 6 だけにフィルターします。
  • 画像をそれらが量子コンピュータにフィットできるようにダウンスケールします。
  • 任意の矛盾する (= contradictory) サンプルを除去します。
  • 二値画像を Cirq 回路に変換します。
  • Cirq 回路を TensorFlow 量子回路に変換します。

 

1.1 raw データをロードする

Keras で配布されている MNIST データセットをロードします。

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

# Rescale the images from [0,255] to the [0.0,1.0] range.
x_train, x_test = x_train[..., np.newaxis]/255.0, x_test[..., np.newaxis]/255.0

print("Number of original training examples:", len(x_train))
print("Number of original test examples:", len(x_train))
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
Number of original training examples: 60000
Number of original test examples: 60000

3 と 6 だけを保持するようにデータセットをフィルターし、他のクラスは除去します。同時に、ラベル y をブーリアンに変換します : 3 のために True そして 6 のために False です。

def filter_36(x, y):
    keep = (y == 3) | (y == 6)
    x, y = x[keep], y[keep]
    y = y == 3
    return x,y
x_train, y_train = filter_36(x_train, y_train)
x_test, y_test = filter_36(x_test, y_test)

print("Number of filtered training examples:", len(x_train))
print("Number of filtered test examples:", len(x_test))
Number of filtered training examples: 12049
Number of filtered test examples: 1968

最初のサンプルを表示します :

print(y_train[0])

plt.imshow(x_train[0, :, :, 0])
plt.colorbar()
True

<matplotlib.colorbar.Colorbar at 0x7eff63fa4e48>

 

1.2 画像をダウンスケールする

28×28 の画像サイズは現在の量子コンピュータのためには遥かに大きすぎます。画像を 4×4 に下げてリサイズします :

x_train_small = tf.image.resize(x_train, (4,4)).numpy()
x_test_small = tf.image.resize(x_test, (4,4)).numpy()

再度、最初の訓練サンプルを表示します — リサイズ後にです :

print(y_train[0])

plt.imshow(x_train_small[0,:,:,0], vmin=0, vmax=1)
plt.colorbar()
True

<matplotlib.colorbar.Colorbar at 0x7eff6006bd68>

 

1.3 矛盾するサンプルを除去する

Farhi et al. のセクション 3.3 Learning to Distinguish Digits から両者のクラスに属するようにラベル付けされている画像を除去するためにデータセットをフィルターします。

これは標準的な機械学習手続きではありませんが、ペーパーをフォローする利益のために含まれます。

def remove_contradicting(xs, ys):
    mapping = collections.defaultdict(set)
    # Determine the set of labels for each unique image:
    for x,y in zip(xs,ys):
       mapping[tuple(x.flatten())].add(y)
    
    new_x = []
    new_y = []
    for x,y in zip(xs, ys):
      labels = mapping[tuple(x.flatten())]
      if len(labels) == 1:
          new_x.append(x)
          new_y.append(list(labels)[0])
      else:
          # Throw out images that match more than one label.
          pass
    
    num_3 = sum(1 for value in mapping.values() if True in value)
    num_6 = sum(1 for value in mapping.values() if False in value)
    num_both = sum(1 for value in mapping.values() if len(value) == 2)

    print("Number of unique images:", len(mapping.values()))
    print("Number of 3s: ", num_3)
    print("Number of 6s: ", num_6)
    print("Number of contradictory images: ", num_both)
    print()
    print("Initial number of examples: ", len(xs))
    print("Remaining non-contradictory examples: ", len(new_x))
    
    return np.array(new_x), np.array(new_y)

結果としてのカウントは報告されている値に密接には適合しませんが、正確な手続きは指定されていません。この時点での矛盾するサンプルのフィルタリングの適用はモデルが矛盾する訓練サンプルを受け取ることを総合的には妨げないこともここで注意するに値します : 次のステップはより多くの衝突を引き起こすデータを二値化します。

x_train_nocon, y_train_nocon = remove_contradicting(x_train_small, y_train)
Number of unique images: 10387
Number of 3s:  4961
Number of 6s:  5475
Number of contradictory images:  49

Initial number of examples:  12049
Remaining non-contradictory examples:  11520

 

1.3 データを量子回路としてエンコードする

量子コンピュータを使用して画像を処理するため、Farhi et al. は各ピクセルを量子ビットで表すことを提案しています、状態はピクセル値に依拠します。最初のステップは二値エンコーディングに変換することです。

THRESHOLD = 0.5

x_train_bin = np.array(x_train_nocon > THRESHOLD, dtype=np.float32)
x_test_bin = np.array(x_test_small > THRESHOLD, dtype=np.float32)

閾値を越える値を持つピクセルインデックスの量子ビットは $X$ ゲートを通して回転されます。

def convert_to_circuit(image):
    """Encode truncated classical image into quantum datapoint."""
    values = np.ndarray.flatten(image)
    qubits = cirq.GridQubit.rect(4, 4)
    circuit = cirq.Circuit()
    for i, value in enumerate(values):
        if value:
            circuit.append(cirq.X(qubits[i]))
    return circuit


x_train_circ = [convert_to_circuit(x) for x in x_train_bin]
x_test_circ = [convert_to_circuit(x) for x in x_test_bin]

最初のサンプルのために作成された回路がここにあります (回路図はゼロゲートを持つ量子ビットは表示しません) :

SVGCircuit(x_train_circ[0])
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

この回路を画像値が閾値を越えるインデックスと比較します :

bin_img = x_train_bin[0,:,:,0]
indices = np.array(np.where(bin_img)).T
indices
array([[2, 2],
       [3, 1]])

これらの Cirq 回路を tfq のための tensor に変換します :

x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)

 

2. 量子ニューラルネットワーク

画像を分類する量子回路構造についてのガイダンスは殆どありません。分類は読み出し量子ビットの期待値に基づきますので、Farhi et al. は 2 量子ビットゲートを使用することを提案しています、読み出し量子ビットは常にその上で動作します。これはピクセルに渡り小さな Unitary RNN を実行することに幾つかの点で類似しています。

 

2.1 モデル回路を構築する

この以下のサンプルはこの層化 (= layered) アプローチを示します。各層は同じゲートの n インスタンスを使用します、データ量子ビットの各々は読み出し量子ビット上で作用します。

これらのゲートの層を回路に追加する単純なクラスから始めます :

class CircuitLayerBuilder():
    def __init__(self, data_qubits, readout):
        self.data_qubits = data_qubits
        self.readout = readout
    
    def add_layer(self, circuit, gate, prefix):
        for i, qubit in enumerate(self.data_qubits):
            symbol = sympy.Symbol(prefix + '-' + str(i))
            circuit.append(gate(qubit, self.readout)**symbol)

サンプル回路層をそれがどのようなものか見るために構築します :

demo_builder = CircuitLayerBuilder(data_qubits = cirq.GridQubit.rect(4,1),
                                   readout=cirq.GridQubit(-1,-1))

circuit = cirq.Circuit()
demo_builder.add_layer(circuit, gate = cirq.XX, prefix='xx')
SVGCircuit(circuit)

今は 2-層化モデルを構築し、データ回路サイズに合わせ、そして準備と読み出し演算を含みます。

def create_quantum_model():
    """Create a QNN model circuit and readout operation to go along with it."""
    data_qubits = cirq.GridQubit.rect(4, 4)  # a 4x4 grid.
    readout = cirq.GridQubit(-1, -1)         # a single qubit at [-1,-1]
    circuit = cirq.Circuit()
    
    # Prepare the readout qubit.
    circuit.append(cirq.X(readout))
    circuit.append(cirq.H(readout))
    
    builder = CircuitLayerBuilder(
        data_qubits = data_qubits,
        readout=readout)

    # Then add layers (experiment by adding more).
    builder.add_layer(circuit, cirq.XX, "xx1")
    builder.add_layer(circuit, cirq.ZZ, "zz1")

    # Finally, prepare the readout qubit.
    circuit.append(cirq.H(readout))

    return circuit, cirq.Z(readout)
model_circuit, model_readout = create_quantum_model()

 

2.2 tfq-keras モデルでモデル回路をラップする

Keras モデルを量子コンポーネントで構築します。このモデルは「量子データ」が x_train_circ から供給されます、これは古典的データをエンコードします。それは量子データ上、モデル回路を訓練するためにパラメータ化された量子回路層 – tfq.layers.PQC を使用します。

これらの画像を分類するため、Farhi et al. はパラメータ化された回路の読み出し量子ビットの期待値を取ることを提案しました。期待値は 1 と -1 の間の値を返します。

# Build the Keras model.
model = tf.keras.Sequential([
    # The input is the data-circuit, encoded as a tf.string
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    # The PQC layer returns the expected value of the readout gate, range [-1,1].
    tfq.layers.PQC(model_circuit, model_readout),
])

次に、compile メソッドを使用して、モデルへの訓練手続きを記述します。

期待される読み出しは範囲 [-1, 1] にありますので、hinge 損失の最適化はある程度自然な fit です。

Note: もう一つの妥当なアプローチは出力範囲を [0, 1] にシフトすることでしょう、そしてそれをモデルがクラス 3 に割り当てた確率として扱います。これは標準的な tf.losses.BinaryCrossentropy 損失で使用できるでしょう。

ここで hinge 損失を使用するには、2 つの小さい調節 (= adjustments) を作成する必要があります。最初にブーリアンからのラベル, y_train を hinge 損失に想定されるような、[-1, 1] に変換します。

y_train_hinge = 2.0*y_train-1.0
y_test_hinge = 2.0*y_test-1.0

2 番目に、カスタム hinge_accuracy メトリックを使用します、これは [-1, 1] を y_true ラベル引数として正しく処理します。tf.losses.BinaryAccuracy(threshold=0.0) は y_true にブーリアンであることを想定しますので、hinge 損失では使用できません。

def hinge_accuracy(y_true, y_pred):
    y_true = tf.squeeze(y_true) > 0.0
    y_pred = tf.squeeze(y_pred) > 0.0
    result = tf.cast(y_true == y_pred, tf.float32)

    return tf.reduce_mean(result)
model.compile(
    loss=tf.keras.losses.Hinge(),
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[hinge_accuracy])
print(model.summary())
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
pqc (PQC)                    (None, 1)                 32        
=================================================================
Total params: 32
Trainable params: 32
Non-trainable params: 0
_________________________________________________________________
None

 

2.3 量子モデルを訓練する

さてモデルを訓練します — これは 45 分ほどかかります。そんなに長く待つことを望まない場合には、データの小さいサブセットを使用します (下で、NUM_EXAMPLES=500 を設定します)。これは実際には訓練の間モデルの進捗に影響しません (それは 32 パラメータを持つだけで、これらを制約するためにそれほどデータを必要としません)。より少ないサンプルの使用は単に訓練を早期 (5 分) に終わらせますが、十分に長く実行すると検証ログで進展していくことを示します。

EPOCHS = 3
BATCH_SIZE = 32

NUM_EXAMPLES = len(x_train_tfcirc)
x_train_tfcirc_sub = x_train_tfcirc[:NUM_EXAMPLES]
y_train_hinge_sub = y_train_hinge[:NUM_EXAMPLES]

このモデルを収束まで訓練するとテストセット上で >85% 精度を獲得します。

qnn_history = model.fit(
      x_train_tfcirc_sub, y_train_hinge_sub,
      batch_size=32,
      epochs=EPOCHS,
      verbose=1,
      validation_data=(x_test_tfcirc, y_test_hinge))

qnn_results = model.evaluate(x_test_tfcirc, y_test)
Train on 11520 samples, validate on 1968 samples
Epoch 1/3
11520/11520 [==============================] - 404s 35ms/sample - loss: 1.0000 - hinge_accuracy: 0.4987 - val_loss: 0.9994 - val_hinge_accuracy: 0.6033
Epoch 2/3
11520/11520 [==============================] - 397s 34ms/sample - loss: 1.0000 - hinge_accuracy: 0.4977 - val_loss: 0.9996 - val_hinge_accuracy: 0.6069
Epoch 3/3
11520/11520 [==============================] - 397s 34ms/sample - loss: 1.0000 - hinge_accuracy: 0.5016 - val_loss: 0.9997 - val_hinge_accuracy: 0.5917
1968/1968 [==============================] - 3s 1ms/sample - loss: 0.9997 - hinge_accuracy: 0.5917

Note: 訓練精度はエポックに渡る平均を報告します。検証精度は各エポックの最後に評価されます。

 

3. 古典的ニューラルネットワーク

量子ニューラルネットワークがこの単純化された MNIST 問題のために動作する一方で、基本的な古典的ニューラルネットワークはこのタスク上で QNN を容易により優れたパフォーマンスを示します。シングルエポックの後、古典的ニューラルネットワークは取り置いたセット上で >98% 精度を獲得できます。

次のサンプルでは、古典的ニューラルネットワークは画像のサブサンプリングの代わりに 28×28 全体画像を使用して 3-6 分類問題のために使用されます。これは容易にテストセットの 100% 精度近くに収束します。

def create_classical_model():
    # A simple model based off LeNet from https://keras.io/examples/mnist_cnn/
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Conv2D(32, [3, 3], activation='relu', input_shape=(28,28,1)))
    model.add(tf.keras.layers.Conv2D(64, [3, 3], activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
    model.add(tf.keras.layers.Dropout(0.25))
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(128, activation='relu'))
    model.add(tf.keras.layers.Dropout(0.5))
    model.add(tf.keras.layers.Dense(1))
    return model


model = create_classical_model()
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(),
              metrics=['accuracy'])

model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 12, 12, 64)        0         
_________________________________________________________________
dropout (Dropout)            (None, 12, 12, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 9216)              0         
_________________________________________________________________
dense (Dense)                (None, 128)               1179776   
_________________________________________________________________
dropout_1 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 129       
=================================================================
Total params: 1,198,721
Trainable params: 1,198,721
Non-trainable params: 0
model.fit(x_train,
          y_train,
          batch_size=128,
          epochs=1,
          verbose=1,
          validation_data=(x_test, y_test))

cnn_results = model.evaluate(x_test, y_test)
Train on 12049 samples, validate on 1968 samples
12049/12049 [==============================] - 4s 318us/sample - loss: 0.0404 - accuracy: 0.9832 - val_loss: 0.0033 - val_accuracy: 0.9980
1968/1968 [==============================] - 0s 130us/sample - loss: 0.0033 - accuracy: 0.9980

上のモデルは 1.2M パラメータ近くを持ちます。より公平な比較のために、サブサンプリングされた画像上で、37-パラメータ・モデルを試します :

def create_fair_classical_model():
    # A simple model based off LeNet from https://keras.io/examples/mnist_cnn/
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Flatten(input_shape=(4,4,1)))
    model.add(tf.keras.layers.Dense(2, activation='relu'))
    model.add(tf.keras.layers.Dense(1))
    return model


model = create_fair_classical_model()
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(),
              metrics=['accuracy'])

model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten_1 (Flatten)          (None, 16)                0         
_________________________________________________________________
dense_2 (Dense)              (None, 2)                 34        
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 3         
=================================================================
Total params: 37
Trainable params: 37
Non-trainable params: 0
model.fit(x_train_bin,
          y_train_nocon,
          batch_size=128,
          epochs=20,
          verbose=2,
          validation_data=(x_test_bin, y_test))

fair_nn_results = model.evaluate(x_test_bin, y_test)
Train on 11520 samples, validate on 1968 samples
Epoch 1/20
11520/11520 - 0s - loss: 0.7243 - accuracy: 0.5182 - val_loss: 0.6564 - val_accuracy: 0.5056
Epoch 2/20
11520/11520 - 0s - loss: 0.6264 - accuracy: 0.5395 - val_loss: 0.5690 - val_accuracy: 0.6174
Epoch 3/20
11520/11520 - 0s - loss: 0.5395 - accuracy: 0.7294 - val_loss: 0.4882 - val_accuracy: 0.7734
Epoch 4/20
11520/11520 - 0s - loss: 0.4624 - accuracy: 0.8238 - val_loss: 0.4203 - val_accuracy: 0.7947
Epoch 5/20
11520/11520 - 0s - loss: 0.3999 - accuracy: 0.8484 - val_loss: 0.3672 - val_accuracy: 0.8425
Epoch 6/20
11520/11520 - 0s - loss: 0.3522 - accuracy: 0.8635 - val_loss: 0.3277 - val_accuracy: 0.8491
Epoch 7/20
11520/11520 - 0s - loss: 0.3158 - accuracy: 0.8664 - val_loss: 0.2982 - val_accuracy: 0.8486
Epoch 8/20
11520/11520 - 0s - loss: 0.2885 - accuracy: 0.8914 - val_loss: 0.2767 - val_accuracy: 0.9070
Epoch 9/20
11520/11520 - 0s - loss: 0.2684 - accuracy: 0.9012 - val_loss: 0.2611 - val_accuracy: 0.9141
Epoch 10/20
11520/11520 - 0s - loss: 0.2536 - accuracy: 0.9059 - val_loss: 0.2497 - val_accuracy: 0.9141
Epoch 11/20
11520/11520 - 0s - loss: 0.2426 - accuracy: 0.9082 - val_loss: 0.2417 - val_accuracy: 0.9141
Epoch 12/20
11520/11520 - 0s - loss: 0.2343 - accuracy: 0.9099 - val_loss: 0.2352 - val_accuracy: 0.9141
Epoch 13/20
11520/11520 - 0s - loss: 0.2280 - accuracy: 0.9100 - val_loss: 0.2308 - val_accuracy: 0.9141
Epoch 14/20
11520/11520 - 0s - loss: 0.2231 - accuracy: 0.9100 - val_loss: 0.2271 - val_accuracy: 0.9151
Epoch 15/20
11520/11520 - 0s - loss: 0.2193 - accuracy: 0.9104 - val_loss: 0.2246 - val_accuracy: 0.9151
Epoch 16/20
11520/11520 - 0s - loss: 0.2163 - accuracy: 0.9107 - val_loss: 0.2226 - val_accuracy: 0.9151
Epoch 17/20
11520/11520 - 0s - loss: 0.2139 - accuracy: 0.9107 - val_loss: 0.2210 - val_accuracy: 0.9151
Epoch 18/20
11520/11520 - 0s - loss: 0.2120 - accuracy: 0.9108 - val_loss: 0.2198 - val_accuracy: 0.9151
Epoch 19/20
11520/11520 - 0s - loss: 0.2104 - accuracy: 0.9109 - val_loss: 0.2188 - val_accuracy: 0.9151
Epoch 20/20
11520/11520 - 0s - loss: 0.2091 - accuracy: 0.9109 - val_loss: 0.2181 - val_accuracy: 0.9151
1968/1968 [==============================] - 0s 25us/sample - loss: 0.2181 - accuracy: 0.9151

 

4. 比較

より高解像度入力とよりパワフルなモデルはこの問題を CNN にとって容易にします。一方で類似のパワー (~32 パラメータ) の古典的モデルはわずかな時間で同様の精度にまで訓練されます。いずれにせよ、古典的ニューラルネットワークは量子ニューラルネットワークのパフォーマンスを容易に越えます。古典的なデータについては、古典的ニューラルネットワークに打ち勝つことは困難です。

qnn_accuracy = qnn_results[1]
cnn_accuracy = cnn_results[1]
fair_nn_accuracy = fair_nn_results[1]

sns.barplot(["Quantum", "Classical, full", "Classical, fair"],
            [qnn_accuracy, cnn_accuracy, fair_nn_accuracy])
<matplotlib.axes._subplots.AxesSubplot at 0x7efe9c00d9b0>

 

以上






TensorFlow Quantum 0.2.0 : Tutorials : Hello, many worlds

TensorFlow Quantum 0.2.0 Tutorials : Hello, many worlds (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/13/2020 (0.2.0)

* 本ページは、TensorFlow Quantum の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Tutorials : Hello, many worlds

このチュートリアルは古典的ニューラルネットがどのように量子ビット・キャリブレーションエラーを訂正することを学習できるかを示します。それは NISQ 回路を作成し、編集しそして起動するために Cirq – Python フレームワークを導入して Cirq が TensorFlow Quantum とどのように相互作用できるかを実演します。

 

セットアップ

Install TensorFlow Quantum をインストールします :

pip install -q tensorflow-quantum

今は TensorFlow とモジュール依存性をインポートします :

import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit

 

1. 基本

1.1 Cirq とパラメータ化された量子回路

TensorFlow Quantum (TFQ) を調べる前に、幾つか Cirq の基本を見てみましょう。Cirq は Google からの量子計算のための Python ライブラリです。静的そしてパラメータ化されたゲートを含む、回路を定義するためにそれを使用します。

Cirq は自由パラメータを表すために SymPy シンボルを使用します。

a, b = sympy.symbols('a b')

次のコードは貴方のパラメータを使用して 2-量子ビット回路を作成します :

# Create two qubits
q0, q1 = cirq.GridQubit.rect(1, 2)

# Create a circuit on these qubits using the parameters you created above.
circuit = cirq.Circuit(
    cirq.rx(a).on(q0),
    cirq.ry(b).on(q1), cirq.CNOT(control=q0, target=q1))

SVGCircuit(circuit)
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

回路を評価するために、cirq.Simulator インターフェイスを使用できます。cirq.ParamResolver オブジェクトへ渡すことにより特定の数値で回路の自由パラメータを置き換えます。次のコードはパラメータ化された回路の raw 状態ベクトル出力を計算します :

# Calculate a state vector with a=0.5 and b=-0.5.
resolver = cirq.ParamResolver({a: 0.5, b: -0.5})
output_state_vector = cirq.Simulator().simulate(circuit, resolver).final_state
output_state_vector
array([ 0.9387913 +0.j        , -0.23971277+0.j        ,
        0.        +0.06120872j,  0.        -0.23971277j], dtype=complex64)

状態ベクトルはシミュレータの外側では直接的にアクセス可能ではありません (上の出力で複素数に気付くでしょう)。物理的に現実的であるために、測定を指定しなければなりません、これは状態ベクトルを古典コンピュータが理解できる実数に変換します。Cirq は Pauli 演算子 $\hat{X}$, $\hat{Y}$ と $\hat{Z}$ の組み合わせを使用して測定を指定します。例として、次のコードはちょうどシミュレートした状態ベクトル上で $\hat{Z}_0$ と $\frac{1}{2}\hat{Z}_0 + \hat{X}_1$ を測定します :

z0 = cirq.Z(q0)

qubit_map={q0: 0, q1: 1}

z0.expectation_from_wavefunction(output_state_vector, qubit_map).real
0.8775825500488281
z0x1 = 0.5 * z0 + cirq.X(q1)

z0x1.expectation_from_wavefunction(output_state_vector, qubit_map).real
-0.04063427448272705

 

1.2 tensor としての量子回路

TensorFlow Quantum (TFQ) は tfq.convert_to_tensor を提供します、Cirq オブジェクトを tensor に変換する関数です。これは Cirq オブジェクトを 量子層量子 ops に送ることを可能にします。関数は Cirq Circuit と Cirq Pauli のリストか配列上で呼び出せます :

# Rank 1 tensor containing 1 circuit.
circuit_tensor = tfq.convert_to_tensor([circuit])

print(circuit_tensor.shape)
print(circuit_tensor.dtype)
(1,)
<dtype: 'string'>

これは Cirq オブジェクトを tf.string tensor としてエンコードし、これは必要に応じて tfq 演算がデコードします。

# Rank 1 tensor containing 2 Pauli operators.
pauli_tensor = tfq.convert_to_tensor([z0, z0x1])
pauli_tensor.shape
TensorShape([2])

 

1.3 回路シミュレーションのバッチ処理

TFQ は期待値、サンプリングと状態ベクトルを計算するための方法を提供します。今は、期待値にフォーカスしましょう。

期待値を計算するための最高位のインターフェイスは tfq.layers.Expectation 層で、これは tf.keras.Layer です。その最も単純な形式では、この層は多くの cirq.ParamResolvers に渡りパラメータ化された回路をシミュレートすることに同値です ; けれども、TFQ は次の TensorFlow セマンティクスをバッチ処理することを可能にし、回路は効率的な C++ コードを使用してシミュレートされます。

私達の a と b パラメータを置き換えるために値のバッチを作成します :

batch_vals = np.array(np.random.uniform(0, 2 * np.pi, (5, 2)), dtype=np.float32)

Cirq でパラメータ値に渡る回路実行をバッチ処理することはループを必要とします :

cirq_results = []
cirq_simulator = cirq.Simulator()

for vals in batch_vals:
    resolver = cirq.ParamResolver({a: vals[0], b: vals[1]})
    final_state = cirq_simulator.simulate(circuit, resolver).final_state
    cirq_results.append(
        [z0.expectation_from_wavefunction(final_state, {
            q0: 0,
            q1: 1
        }).real])

print('cirq batch results: \n {}'.format(np.array(cirq_results)))
cirq batch results: 
 [[-0.84454578]
 [ 0.805327  ]
 [ 0.78711689]
 [ 0.20195568]
 [ 0.56565583]]

同じ演算が TFQ では単純化されます :

tfq.layers.Expectation()(circuit,
                         symbol_names=[a, b],
                         symbol_values=batch_vals,
                         operators=z0)

 

2. ハイブリッド量子-古典的最適化

基本を見た今、ハイブリッド量子古典的ニューラルネットを構築するために TensorFlow Quantum を使用しましょう。シングル量子ビットを制御するために古典的なニューラルネットを訓練します。制御は 0 か 1 状態の量子ビットを正しく準備するために最適化され、シミュレートされたシステマティックなキャリブレーションエラーに打ち勝ちます。図はアーキテクチャを示します :

ニューラルネットなしでさえも、これは解くのに簡単な問題ですが、主題は (TFQ を使用して解くかもしれない) 実際の量子制御問題に類似しています。それは tf.keras.Model 内部で tfq.layers.ControlledPQC (パラメータ化された量子回路) 層を使用して量子古典的計算の end-to-end なサンプルを実演します。

このチュートリアルの実装のために、このアーキテクチャは 3 パートに分割されます :

  • 入力回路とデータポイント回路: 最初の 3 つの $R$ ゲート。
  • 制御回路: 他の 3 つの $R$ ゲート。
  • コントローラ: 制御回路のパラメータを設定する古典的なニューラルネットワーク。

 

2.1 制御回路定義

上の図で示されるような、学習可能なシングルビット回転を定義します。これは制御回路に対応します。

# Parameters that the classical NN will feed values into.
control_params = sympy.symbols('theta_1 theta_2 theta_3')

# Create the parameterized circuit.
qubit = cirq.GridQubit(0, 0)
model_circuit = cirq.Circuit(
    cirq.rz(control_params[0])(qubit),
    cirq.ry(control_params[1])(qubit),
    cirq.rx(control_params[2])(qubit))

SVGCircuit(model_circuit)

 

2.2 コントローラ

今はコントローラネットワークを定義します :

# The classical neural network layers.
controller = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='elu'),
    tf.keras.layers.Dense(3)
])

コマンドのバッチが与えられたとき、コントローラは制御回路のための制御シグナルのバッチを出力します。

コントローラはランダムに初期化されますので、これらの出力は有用ではありません、まだ。

controller(tf.constant([[0.0],[1.0]])).numpy()
array([[ 0.        ,  0.        ,  0.        ],
       [ 0.8239053 , -0.71268165, -0.62467915]], dtype=float32)

 

2.3 コントローラを回路に接続する

シングル keras.Model として、コントローラを制御回路に接続するために tfq を使用します。

このスタイルのモデル定義についてのより多くは Keras Functional API ガイド を見てください。

最初にモデルへの入力を定義します :

# This input is the simulated miscalibration that the model will learn to correct.
circuits_input = tf.keras.Input(shape=(),
                                # The circuit-tensor has dtype `tf.string` 
                                dtype=tf.string,
                                name='circuits_input')

# Commands will be either `0` or `1`, specifying the state to set the qubit to.
commands_input = tf.keras.Input(shape=(1,),
                                dtype=tf.dtypes.float32,
                                name='commands_input')

次にそれらの入力に演算を適用します、計算を定義するために。

dense_2 = controller(commands_input)

# TFQ layer for classically controlled circuits.
expectation_layer = tfq.layers.ControlledPQC(model_circuit,
                                             # Observe Z
                                             operators = cirq.Z(qubit))
expectation = expectation_layer([circuits_input, dense_2])

今はこの計算を tf.keras.Model としてパッケージ化します :

# The full Keras model is built from our layers.
model = tf.keras.Model(inputs=[circuits_input, commands_input],
                       outputs=expectation)

ネットワーク・アーキテクチャは下のモデルのプロットで示されます。正しさを検証するためにこのモデルプロットをアーキテクチャ図と比較してください。

Note: graphviz パッケージのシステムインストールを必要とするかもしれません。

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

(訳注 : 原文ママ)

Failed to import pydot. You must install pydot and graphviz for `pydotprint` to work.

このモデルは 2 つの入力を取ります : コントローラのためのコマンド、そして入力回路、コントローラはその出力を訂正しようとします。

 

2.4 データセット

モデルは各コマンドのために正しい測定値を出力しようとします。コマンドと正しい値は下で定義されます。

# The command input values to the classical NN.
commands = np.array([[0], [1]], dtype=np.float32)

# The desired Z expectation value at output of quantum circuit.
expected_outputs = np.array([[1], [-1]], dtype=np.float32)

これはこのタスクのための訓練データセット全体ではありません。データセットの各データポイントはまた入力回路を必要とします。

 

2.5 入力回路定義

(訳注: 原文のインデックス番号は修正。)

下の入力回路はモデルが訂正することを学習するランダムなミスキャリブレーションを定義します。

random_rotations = np.random.uniform(0, 2 * np.pi, 3)
noisy_preparation = cirq.Circuit(
  cirq.rx(random_rotations[0])(qubit),
  cirq.ry(random_rotations[1])(qubit),
  cirq.rz(random_rotations[2])(qubit)
)
datapoint_circuits = tfq.convert_to_tensor([
  noisy_preparation
] * 2)  # Make two copied of this circuit

回路の 2 つのコピーがあります、各データポイントに一つです。

datapoint_circuits.shape
TensorShape([2])

 

2.6 訓練

定義された入力で tfq モデルをテスト実行できます。

model([datapoint_circuits, commands]).numpy()
array([[-0.44448078],
       [-0.8360152 ]], dtype=float32)

今はこれらの値を expected_outputs に向けて調整するために標準的な訓練プロセスを実行します。

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)
loss = tf.keras.losses.MeanSquaredError()
model.compile(optimizer=optimizer, loss=loss)
history = model.fit(x=[datapoint_circuits, commands],
                    y=expected_outputs,
                    epochs=30,
                    verbose=0)
plt.plot(history.history['loss'])
plt.title("Learning to Control a Qubit")
plt.xlabel("Iterations")
plt.ylabel("Error in Control")
plt.show()

このプロットからニューラルネットワークがシステマティックなミスキャリブレーションに打ち勝つことを学習したことを見ることができます。

 

2.7 出力を検証する

今は量子ビット・キャリブレーションエラーを訂正するために、訓練モデルを使用します。Cirq で :

def check_error(command_values, desired_values):
  """Based on the value in `command_value` see how well you could prepare
  the full circuit to have `desired_value` when taking expectation w.r.t. Z."""
  params_to_prepare_output = controller(command_values).numpy()
  full_circuit = noisy_preparation + model_circuit

  # Test how well you can prepare a state to get expectation the expectation
  # value in `desired_values`
  for index in [0, 1]:
    state = cirq_simulator.simulate(
        full_circuit,
        {s:v for (s,v) in zip(control_params, params_to_prepare_output[index])}
    ).final_state
    expectation = z0.expectation_from_wavefunction(state, {qubit: 0}).real
    print(f'For a desired output (expectation) of {desired_values[index]} with'
          f' noisy preparation, the controller\nnetwork found the following '
          f'values for theta: {params_to_prepare_output[index]}\nWhich gives an'
          f' actual expectation of: {expectation}\n')


check_error(commands, expected_outputs)
For a desired output (expectation) of [1.] with noisy preparation, the controller
network found the following values for theta: [-1.0564872  3.8130002  0.7327498]
Which gives an actual expectation of: 0.9826827049255371

For a desired output (expectation) of [-1.] with noisy preparation, the controller
network found the following values for theta: [ 0.9573223  -1.4036056  -0.09569579]
Which gives an actual expectation of: -0.9561153650283813

訓練の間の損失関数の値はモデルがどのくらい上手く学習しているかの大雑把な考えを提供します。損失が低くなれば、上のセルの期待値は desired_values に近づきます。パラメータ阿智に関心がないのであれば、tfq を使用して上から常に出力を確認できます :

model([datapoint_circuits, commands])
<tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[ 0.9826828],
       [-0.9561153]], dtype=float32)>

 

3 異なる演算子の固有状態を準備することを学習する

1 と 0 に対応する $\pm \hat{Z}$ 固有状態の選択は任意です。1 に $+ \hat{Z}$ 固有状態に対応させてそして 0 に $-\hat{X}$ 固有状態に対応させることを単に容易に望むことができたでしょう。これを達成する一つの方法は、下の図で示されるように各コマンドに対して異なる測定演算子を指定することによります :

これは tfq.layers.Expectation の使用を必要とします。今は入力は 3 つのオブジェクトを含むように増大しました : 回路、コマンドと演算子です。出力は依然として期待値です。

 

3.1 新しいモデル定義

このタスクを達成するためのモデルを見てみましょう :

# Define inputs.
commands_input = tf.keras.layers.Input(shape=(1),
                                       dtype=tf.dtypes.float32,
                                       name='commands_input')
circuits_input = tf.keras.Input(shape=(),
                                # The circuit-tensor has dtype `tf.string` 
                                dtype=tf.dtypes.string,
                                name='circuits_input')
operators_input = tf.keras.Input(shape=(1,),
                                 dtype=tf.dtypes.string,
                                 name='operators_input')

ここにコントローラ・ネットワークがあります :

# Define classical NN.
controller = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='elu'),
    tf.keras.layers.Dense(3)
])

回路とコントローラを tfq を使用して単一の keras.Model に結合します :

dense_2 = controller(commands_input)

# Since you aren't using a PQC or ControlledPQC you must append
# your model circuit onto the datapoint circuit tensor manually.
full_circuit = tfq.layers.AddCircuit()(circuits_input, append=model_circuit)
expectation_output = tfq.layers.Expectation()(full_circuit,
                                              symbol_names=control_params,
                                              symbol_values=dense_2,
                                              operators=operators_input)

# Contruct your Keras model.
two_axis_control_model = tf.keras.Model(
    inputs=[circuits_input, commands_input, operators_input],
    outputs=[expectation_output])

 

3.2 データセット

今は model_circuit のために供給する各データポイントのために測定することを望む演算子もまた含みます :

# The operators to measure, for each command.
operator_data = tfq.convert_to_tensor([[cirq.X(qubit)], [cirq.Z(qubit)]])

# The command input values to the classical NN.
commands = np.array([[0], [1]], dtype=np.float32)

# The desired expectation value at output of quantum circuit.
expected_outputs = np.array([[1], [-1]], dtype=np.float32)

 

3.3 訓練

新しい入力と出力を持つ今、keras を使用して再度訓練できます。

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)
loss = tf.keras.losses.MeanSquaredError()

two_axis_control_model.compile(optimizer=optimizer, loss=loss)

history = two_axis_control_model.fit(
    x=[datapoint_circuits, commands, operator_data],
    y=expected_outputs,
    epochs=30,
    verbose=1)
Train on 2 samples
Epoch 1/30
2/2 [==============================] - 1s 326ms/sample - loss: 1.0935
Epoch 2/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.8408
Epoch 3/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.5724
Epoch 4/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.3353
Epoch 5/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.1855
Epoch 6/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.1152
Epoch 7/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0859
Epoch 8/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0757
Epoch 9/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0658
Epoch 10/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0486
Epoch 11/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0306
Epoch 12/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0181
Epoch 13/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0124
Epoch 14/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0120
Epoch 15/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0155
Epoch 16/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0211
Epoch 17/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0259
Epoch 18/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0268
Epoch 19/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0235
Epoch 20/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0180
Epoch 21/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0126
Epoch 22/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0083
Epoch 23/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0051
Epoch 24/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0029
Epoch 25/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0015
Epoch 26/30
2/2 [==============================] - 0s 1ms/sample - loss: 8.1761e-04
Epoch 27/30
2/2 [==============================] - 0s 1ms/sample - loss: 4.8669e-04
Epoch 28/30
2/2 [==============================] - 0s 1ms/sample - loss: 3.7943e-04
Epoch 29/30
2/2 [==============================] - 0s 1ms/sample - loss: 3.9353e-04
Epoch 30/30
2/2 [==============================] - 0s 1ms/sample - loss: 4.7893e-04
plt.plot(history.history['loss'])
plt.title("Learning to Control a Qubit")
plt.xlabel("Iterations")
plt.ylabel("Error in Control")
plt.show()

損失関数はゼロに下がりました。

controller はスタンドアローン・モデルとして利用可能です。コントローラを呼び出し、各コマンド信号へのレスポンスを確認します。これらの出力を random_rotations の内容と正しく比較するには何某かの作業がかかるでしょう。

controller.predict(np.array([0,1]))
array([[-1.942868  , -0.2633527 ,  0.4679988 ],
       [-0.58572036,  0.0779818 , -1.1244173 ]], dtype=float32)
 

以上






TensorFlow Quantum 0.2.0 : 概要 / 量子機械学習コンセプト / 設計

TensorFlow Quantum 0.2.0 概要 / 量子機械学習コンセプト / 設計 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/12/2020 (0.2.0)

* 本ページは、TensorFlow Quantum の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

概要

TensorFlow Quantum (TFQ) は 量子機械学習 のための Python フレームワークです。アプリケーション・フレームワークとして、TFQ は量子アルゴリズム研究者と ML 応用研究者に総て TensorFlow の中から、Google の量子計算フレームワークを活用することを可能にします。

TensorFlow Quantum は量子データとハイブリッド量子古典的モデルを構築することに焦点を当てています。それは量子アルゴリズムと TensorFlow を伴い Cirq で設計されたロジックを交互配置するためのツールを提供します。TensorFlow Quantum を効果的に利用するためには量子計算の基本的な理解が必要とされます。

 

デザイン

TensorFlow Quantum は TensorFlow を量子計算ハードウェアと統合するために必要なコンポーネントを実装しています。その目的で、TensorFlow Quantum は 2 つのデータ型プリミティブを導入します :

  • 量子回路 — これは TensorFlow 内での Cirq-定義の量子回路を表します。異なる実数値データポイントのバッチに類似して、様々なサイズの回路のバッチを作成します。
  • Pauli sum — Cirq で定義された Pauli 演算子の tensor 積の線形結合を表します。回路のように、様々なサイズの演算子のバッチを作成します。

量子回路を表すためにこれらのプリミティブを使用し、TensorFlow Quantum は次の演算を提供します :

  • 回路のバッチの出力分布からのサンプリング。
  • 回路のバッチ上の Pauli sum のバッチの期待値を計算します。TFQ は逆伝播互換な勾配計算を実装しています。
  • 回路と状態のバッチをシミュレートします。量子回路を通して総ての量子状態振幅を直接調べることが現実世界で大規模には役立たない一方で、状態シミュレーションは研究者が量子回路が状態を近い正確なレベルの精度でどのようにマップするかを理解する助けることができます。

 

量子機械学習コンセプト

Google の 量子超越性実験 は実演するために 53 noisy 量子ビットを使用し、それは既存のアルゴリズムを使用して最も巨大な古典的コンピュータ上で 10,000 年かかるであろう計算を量子コンピュータ上で 200 秒で遂行できました。これは NISQ ( ノイズあり中規模量子 ) 計算時代の始まりを記録しました。今後数年間で、数 10 〜 数 100 の noisy 量子ビットを持つ量子デバイスが現実になることが期待されます。

 

量子計算

量子計算は古典的コンピュータでは届かないような問題を計算するために量子力学の特性に頼ります。量子コンピュータは量子ビットを使用します。量子ビットはコンピュータの通常のビットのようなものですが、重ね合わせに置かれたり他のもう一つとエンタングルメントを共有する追加の能力を伴います。

古典的コンピュータは決定論的な古典的演算を遂行するか、サンプリング法を使用して確率的プロセスをエミュレートできます。重ね合わせとエンタングルメントを利用することにより、量子コンピュータは古典的コンピュータでは巨大規模でエミュレートすることが困難な量子演算を遂行できます。NISQ 量子計算を活用するためのアイデアは最適化、量子シミュレーション、暗号と機械学習を含みます。

 

量子機械学習

量子機械学習 (QML) は 2 つのコンセプト上で構築されています : 量子データとハイブリッド量子古典的モデルです。

 

量子データ

量子データは自然なあるいは人工の量子系で発生する任意のデータソースです。これは量子超越性の Google の実演のための Sycamore プロセッサ から集められたサンプルのような、量子コンピュータにより生成されるデータであり得ます。量子データは重ね合わせとエンタングルメントを示し、これは古典的計算リソースの指数関数的総量を必要とする可能性のある同時確率分布につながります。量子超越性実験は 2^53 Hilbert 空間の非常に複雑な同時確率分布からのサンプリングが可能であることを示しました。

NISQ プロセッサにより生成された量子データは測定が発生する直前には noisy で典型的には entangled です。ヒューリスティックな機械学習テクニックは noisy entangled データから有用な古典的データの抽出を最大化するモデルを作成できます。TensorFlow Quantum (TFQ) ライブラリはもつれを解き量子データの相関性を一般化するモデルを開発するためのプリミティブを提供します — 既存の量子アルゴリズムを改良したり新しい量子アルゴリズムを発見するための機会を広げます。

以下は量子デバイス上で生成されるかシミュレート可能な量子データの例です :

  • 化学的なシミュレーション — 物質科学、計算化学、計算生物と創薬 (新薬発見) への潜在的な応用を持つ化学構造とダイナミクスについての情報を抽出する。
  • 量子物質 (= matter) シミュレーション — 高温超伝導や多体量子効果を示す他のエキゾチックな物質状態をモデル化して設計する。
  • 量子制御 — ハイブリッド量子古典的モデルが最適な open や closed-ループ制御、キャリブレーションそして誤り軽減を遂行するために変分的に訓練できます。これは量子デバイスと量子プロセッサのためのエラー検出と訂正ストラテジーを含みます。
  • 量子通信ネットワーク — 非直交量子状態の中で識別するために機械学習を使用します、これは構造化量子 repeater、量子レシーバーそして purification ユニットの設計と構築への応用を伴います。
  • 量子計測 — 量子センシングと量子イメージングのような量子拡張高精度測定は小さいスケールの量子デバイスである probe 上で本質的に行なわれ、そして変分量子モデルにより設計あるいは改良できるでしょう。

 

ハイブリッド量子古典的モデル

量子モデルは量子力学的起源を持つデータを表して一般化することができます。当面の量子プロセッサが依然として非常に小さくて noisy ですので、量子モデルは量子プロセッサを単独で使用しても量子データを一般化できません。NISQ プロセッサは効果的になるためには古典的なコプロセッサと共同して動作しなければなりません。TensorFlow は既に CPU, GPU と TPU に渡る異種計算をサポートしていますので、それはハイブリッド量子古典的アルゴリズムによる実験をするベースプラットフォームとして使用されます。

量子コンピュータ上で最善に実行されるパラメータ化された量子計算モデルを記述するために量子ニューラルネット (QNN) が使用されます。この用語はパラメータ化された量子回路 (PQC) としばしば置き換えられます。

 

研究

NISQ-時代の間、古典的アルゴリズムに渡る既知のスピードアップを伴う量子アルゴリズム — Shor の因数分解アルゴリズムGrover の探索アルゴリズム のような — は意味のあるスケールではまだ可能ではありません。

TensorFlow Quantum のゴールは以下への特定の関心とともに、NISQ 時代のためのアルゴリズムを発見するに役立つことです :

  1. NISQ アルゴリズムを拡張するために古典的機械学習を使用します。期待は古典的機械学習からのテクニックが量子計算の私達の理解を強化できることです。古典的リカレント・ニューラルネットワークを通した量子ニューラルネットワークのためのメタ学習 では、リカレント・ニューラルネットワーク (RNN) は QAOA と VQE のようなアルゴリズムのための制御パラメータの最適化は単純ないつでも使える optimizer よりも効率的であることを見出すために使用されます。そして 量子制御のための機械学習 はエラーを軽減して高い品質の量子ゲートを生成することを助けるために強化学習を使用します。
  2. 量子回路で量子データをモデル化する。データソースの正確な記述を持つのであれば量子データを古典的にモデル化することは可能です — しかし時にこれは不可能です。この問題を解くために、量子コンピュータ自身の上でモデル化を試みて重要な統計情報を測定/観測することができます。量子畳込みニューラルネットワーク は異なる物質のトポロジカル層を検出するために畳込みニューラルネットワーク (CNN) に類似した構造で設計された量子回路を示します。量子コンピュータはデータとモデルを保持します。古典的プロセッサはモデル出力からの測定サンプルだけを見て、決してデータ自身ではありません。noisy 量子コンピュータ上の robust なエンタングルメント繰り込み では、著者は DMERA モデルを使用して量子多体系についての情報を圧縮することを学習しています。

量子機械学習における他の関心のある領域は以下を含みます :

 

TensorFlow Quantum 設計

TensorFlow Quantum (TFQ) は NISQ-時代の量子機械学習の問題のために設計されています。それは — 量子回路を構築するような —量子計算プリミティブを TensorFlow に持ち込みます。TensorFlow で構築されたモデルと演算はパワフルな量子古典的ハイブリッドシステムを作成するためにこれらのプリミティブを使用します。

TFQ を利用すれば、量子データセット、量子モデルそして古典的制御パラメータを使用して研究者は TensorFlow グラフを構築できます。これらは単一の計算グラフ内の tensor として総て表わされます。量子測定の結果 — 古典的な確率的イベントに繋がります — は TensorFlow ops により得られます。訓練は標準的な Keras API で行なわれます。tfq.datasets モジュールは研究者に新しいそして興味深い量子データセットで実験することを可能にします。

 

Cirq

Cirq は Google からの量子プログラミングフレームワークです。それは量子コンピュータ、あるいはシミュレートされた量子コンピュータ上で量子回路を作成し、変更して起動するための — 量子ビット、ゲート、回路と測定 — のような 基本的な演算の総てを提供します。TensorFlow Quantum は TensorFlow をバッチ計算、モデル構築と勾配計算について拡張するためにこれらの Cirq プリミティブを使用します。TensorFlow Quantum で効果的であるために、Cirq で効果的であることは良い考えです。

 

TensorFlow Quantum プリミティブ

TensorFlow Quantum は TensorFlow を量子計算ハードウェアと統合するために必要なコンポーネントを実装しています。その目的で、TFQ は 2 つのデータ型プリミティブを導入します :

  • 量子回路: これは TensorFlow 内の Cirq-定義の量子回路 (cirq.Circuit) を表します。異なる実数値データポイントのバッチに類似した、様々なサイズの回路のバッチを作成します。
  • Pauli sum: Cirq (cirq.PauliSum) で定義された Pauli 演算子の tensor 積の線型結合を表します。回路のように、様々なサイズの演算子のバッチを作成します。

 

基礎的な ops

tf.Tensor 内で量子回路プリミティブを使用して、TensorFlow Quantum はこれらの回路を処理して意味のある出力を生成する ops を実装します。

TensorFlow ops は最適化された C++ で書かれています。これらの ops は回路からサンプリングし、期待値を計算し、そして与えられた回路から生成された状態を出力します。柔軟で高パフォーマンスな ops を書くことは幾つかの課題を持ちます :

  1. 回路は同じサイズではありません。シミュレートされた回路について、(tf.matmultf.add のような) 静的演算を作成することはできません、そして異なるサイズの回路の代わりに異なる数を用います。これらの ops は静的なサイズの TensorFlow 計算グラフが許さない動的サイズを可能にしなければなりません。
  2. 量子データは異なる回路構造を全体として誘導することができます。これは TFQ ops で動的サイズをサポートするもうひつとの理由です。量子データは、元の回路への変更により表わされる基礎的な量子状態への構造的な変更を表すことができます。新しいデータポイントは実行時にスワップインとアウトされますので、TensorFlow 計算グラフはそれが構築された後には変更できませんので、これらの変化する構造のためのサポートが必要とされます。
  3. cirq.Circuits はそれらが演算のシリーズであり — そして幾つかはシンボル/プレースホルダーを含むかもしれないという点で計算グラフに類似しています。これをできる限り TensorFlow と互換にすることは重要です。

パフォーマンスの理由で、Eigen (多くの TensorFlow ops で使用される C++ ライブラリ) は量子回路シミュレーションのためには上手く適しません。代わりに、量子超越性実験 で使用された回路シミュレータは検証器 (= verifier) として使用されそして (AVX2 と SSE 命令で総て書かれた) TFQ ops の基礎として拡張されました。同一の関数シグネチャーを持つ ops が作成されました、これらは物理的な量子コンピュータを使用します。シミュレートされたものと物理的量子コンピュータの間で切り替えることはコードの単一行を変更するほどに容易です。これらの ops は circuit_execution_ops.py にあります。

 

TensorFlow Quantum 層は tf.keras.layers.Layer インターフェイスを使用して開発者にサンプリング、期待 (値) と状態計算を公開します。古典的な制御パラメータは読み出し演算のために回路層を作成することは便利です。更に、バッチ回路、バッチ制御パラメータ値をサポートする高度な複雑さを持つ層を作成してバッチ読み出し演算を遂行することができます。例として tfq.layers.Sample を見てください。

 

微分器 (= Differentiators)

多くの TensorFlow 演算とは違い、量子回路の可観測量は比較的計算が容易な勾配のための式を持ちません。これは古典的コンピュータが量子コンピュータ上で実行される回路からのサンプリングだけを読むことができるためです。この問題を解くため、tfq.differentiators モジュールは幾つかの標準的な微分テクニックを提供します。ユーザはまた — サンプリングベースの期待値計算の「現実世界」設定と解析的な正確な世界の両者で — 勾配を計算するための彼ら自身のメソッドを定義することもできます。有限差分のようなメソッドがしばしば解析的/正確な環境で最速 (実測時間) です。より遅い (実測時間) 一方で、パラメータシフト確率的 (= stochastic) 手法 のようなより実践的なメソッドはしばしばより効果的です。tfq.differentiators.Differentiator はインスタンス化されて generate_differentiable_op で既存の op に装着されるか、tfq.layers.Expectationtfq.layers.SampledExpectation のコンストラクタに渡されます。カスタム微分器を実装するには、tfq.differentiators.Differentiator クラスを継承します。サンプリングや状態ベクトル計算のための勾配演算を定義するには、tf.custom_gradient を使用します。

 

データセット

量子計算の分野が成長するにつれて、より多くの量子データとモデルの結合が発生し、構造的な比較を困難にします。tfq.datasets モジュールは量子機械学習タスクのためのデータソースとして使用されます。それはモデルとパフォーマンスのために構造化比較を確実なものにします。

巨大なコミュニティの貢献により、tfq.datasets モジュールがより透過で再現可能な研究を可能にするために成長することが望まれます。注意深く厳選された: 量子制御、フェルミオン・シミュレーション、相転移近傍の分類、量子センシング等の問題は総て tfq.datasets への追加のための素晴らしい候補です。To propose a new dataset open a GitHub issue.

 

以上






AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com