![]() | ![]() | ![]() | ![]() |
Dieses Tutorial implementiert ein vereinfachtes Quantenfaltungs-Neuronales Netzwerk (QCNN), ein vorgeschlagenes Quantenanalogon zu einem klassischen Faltungs-Neuronalen Netzwerk, das auch translatorisch invariant ist .
Dieses Beispiel zeigt, wie bestimmte Eigenschaften einer Quantendatenquelle wie ein Quantensensor oder eine komplexe Simulation von einem Gerät aus erkannt werden. Die Quantendatenquelle ist ein Clusterzustand , der möglicherweise eine Anregung aufweist oder nicht - was der QCNN zu erkennen lernt (Der in diesem Artikel verwendete Datensatz war die SPT-Phasenklassifizierung).
Installieren
pip install -q tensorflow==2.3.1
Installieren Sie TensorFlow Quantum:
pip install -q tensorflow-quantum
Importieren Sie nun TensorFlow und die Modulabhängigkeiten:
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. Erstellen Sie eine QCNN
1.1 Schaltkreise in einem TensorFlow-Diagramm zusammenbauen
TensorFlow Quantum (TFQ) bietet Schichtklassen für den Aufbau von In-Graph-Schaltungen. Ein Beispiel ist die tfq.layers.AddCircuit
, die von tf.keras.Layer
erbt. Diese Ebene kann dem Eingabestapel von Schaltkreisen entweder vorangestellt oder angehängt werden, wie in der folgenden Abbildung gezeigt.
Das folgende Snippet verwendet diese Ebene:
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)
Untersuchen Sie den Eingangstensor:
print(tfq.from_tensor(input_circuit_tensor))
[cirq.Circuit([ cirq.Moment( cirq.X(cirq.GridQubit(0, 0)), ), ]) cirq.Circuit([ cirq.Moment( cirq.H(cirq.GridQubit(0, 0)), ), ])]
Und untersuchen Sie den Ausgangstensor:
print(tfq.from_tensor(output_circuit_tensor))
[cirq.Circuit([ cirq.Moment( cirq.X(cirq.GridQubit(0, 0)), ), cirq.Moment( cirq.Y(cirq.GridQubit(0, 0)), ), ]) cirq.Circuit([ cirq.Moment( cirq.H(cirq.GridQubit(0, 0)), ), cirq.Moment( cirq.Y(cirq.GridQubit(0, 0)), ), ])]
Die folgenden Beispiele können zwar ohne Verwendung von tfq.layers.AddCircuit
, es ist jedoch eine gute Gelegenheit zu verstehen, wie komplexe Funktionen in TensorFlow-Berechnungsdiagramme eingebettet werden können.
1.2 Problemübersicht
Sie bereiten einen Clusterzustand vor und trainieren einen Quantenklassifikator, um festzustellen, ob er "angeregt" ist oder nicht. Der Clusterzustand ist stark verwickelt, aber für einen klassischen Computer nicht unbedingt schwierig. Aus Gründen der Übersichtlichkeit ist dies ein einfacherer Datensatz als der im Papier verwendete.
Für diese Klassifizierungsaufgabe implementieren Sie eine tiefe MERA- ähnliche QCNN-Architektur, da:
- Wie beim QCNN ist der Clusterstatus auf einem Ring translatorisch invariant.
- Der Clusterstatus ist stark verwickelt.
Diese Architektur sollte die Verschränkung wirksam reduzieren und die Klassifizierung durch Auslesen eines einzelnen Qubits erhalten.
Ein "angeregter" Clusterzustand ist definiert als ein Clusterzustand, bei dem ein cirq.rx
Gatter auf eines seiner Qubits angewendet wurde. Qconv und QPool werden später in diesem Tutorial erläutert.
1.3 Bausteine für TensorFlow
Eine Möglichkeit, dieses Problem mit TensorFlow Quantum zu lösen, besteht darin, Folgendes zu implementieren:
- Die Eingabe in das Modell ist ein Schaltungstensor - entweder eine leere Schaltung oder ein X-Gatter an einem bestimmten Qubit, das eine Anregung anzeigt.
- Der Rest der Quantenkomponenten des Modells besteht aus
tfq.layers.AddCircuit
Schichten. - Zur Inferenz wird eine
tfq.layers.PQC
Schicht verwendet. Dies liest $ \ langle \ hat {Z} \ rangle $ und vergleicht es mit einer Bezeichnung von 1 für einen angeregten Zustand oder -1 für einen nicht angeregten Zustand.
1.4 Daten
Bevor Sie Ihr Modell erstellen, können Sie Ihre Daten generieren. In diesem Fall handelt es sich um Anregungen für den Clusterstatus (das Originalpapier verwendet einen komplizierteren Datensatz). Anregungen werden mit cirq.rx
Toren dargestellt. Eine ausreichend große Drehung wird als Anregung betrachtet und mit 1
und eine Drehung, die nicht groß genug ist, wird mit -1
und als keine Anregung angesehen.
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)
Sie können sehen, dass Sie genau wie beim regulären maschinellen Lernen ein Trainings- und Test-Set erstellen, mit dem Sie das Modell vergleichen können. Sie können einige Datenpunkte schnell anzeigen mit:
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.477π)─── Output: 1 Input: (0, 1): ───Rx(-0.39π)─── Output: 1
1.5 Ebenen definieren
Definieren Sie nun die in der obigen Abbildung gezeigten Ebenen in TensorFlow.
1.5.1 Clusterstatus
Der erste Schritt besteht darin, den Clusterstatus mithilfe von Cirq zu definieren, einem von Google bereitgestellten Framework zum Programmieren von Quantenschaltungen. Da dies ein statischer Teil des Modells ist, tfq.layers.AddCircuit
es mit der Funktion 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.GridQubit
eine Cluster- cirq.GridQubit
für ein Rechteck von cirq.GridQubit
:
SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.
1.5.2 QCNN-Schichten
Definieren Sie die Ebenen, aus denen das Modell besteht, mit dem QCNN-Papier von Cong und Lukin . Es gibt einige Voraussetzungen:
- Die Ein- und Zwei-Qubit-parametrisierten Einheitsmatrizen aus dem Tucci-Papier .
- Eine allgemeine parametrisierte Zwei-Qubit-Pooling-Operation.
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[6]]
circuit += [cirq.YY(*bits)**symbols[7]]
circuit += [cirq.XX(*bits)**symbols[8]]
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
Um zu sehen, was Sie erstellt haben, drucken Sie die Ein-Qubit-Einheitsschaltung aus:
SVGCircuit(one_qubit_unitary(cirq.GridQubit(0, 0), sympy.symbols('x0:3')))
Und die Zwei-Qubit-Einheitsschaltung:
SVGCircuit(two_qubit_unitary(cirq.GridQubit.rect(1, 2), sympy.symbols('x0:15')))
Und die Zwei-Qubit-Pooling-Schaltung:
SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))
1.5.2.1 Quantenfaltung
Definieren Sie wie in der Arbeit von Cong und Lukin die 1D-Quantenfaltung als die Anwendung einer parametrisierten Zwei-Qubit-Einheit auf jedes Paar benachbarter Qubits mit einem Schritt von eins.
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
Zeigen Sie die (sehr horizontale) Schaltung an:
SVGCircuit(
quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))
1.5.2.2 Quantenpooling
Eine Quantenpooling-Schicht bündelt von $ N $ Qubits zu $ \ frac {N} {2} $ Qubits unter Verwendung des oben definierten Zwei-Qubit-Pools.
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
Untersuchen Sie einen Pooling-Komponentenkreis:
test_bits = cirq.GridQubit.rect(1, 8)
SVGCircuit(
quantum_pool_circuit(test_bits[:4], test_bits[4:], sympy.symbols('x0:6')))
1.6 Modelldefinition
Verwenden Sie nun die definierten Schichten, um ein reines Quanten-CNN zu konstruieren. Beginnen Sie mit acht Qubits, bündeln Sie sie auf eins und messen Sie dann $ \ 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)
1.7 Trainieren Sie das Modell
Trainieren Sie das Modell über den gesamten Stapel, um dieses Beispiel zu vereinfachen.
# 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))
Epoch 1/25 7/7 [==============================] - 1s 105ms/step - loss: 0.9105 - custom_accuracy: 0.6875 - val_loss: 0.8674 - val_custom_accuracy: 0.7292 Epoch 2/25 7/7 [==============================] - 1s 83ms/step - loss: 0.8002 - custom_accuracy: 0.7679 - val_loss: 0.8001 - val_custom_accuracy: 0.7917 Epoch 3/25 7/7 [==============================] - 1s 79ms/step - loss: 0.7684 - custom_accuracy: 0.7679 - val_loss: 0.7767 - val_custom_accuracy: 0.8125 Epoch 4/25 7/7 [==============================] - 1s 79ms/step - loss: 0.7079 - custom_accuracy: 0.8482 - val_loss: 0.7055 - val_custom_accuracy: 0.8542 Epoch 5/25 7/7 [==============================] - 1s 78ms/step - loss: 0.6118 - custom_accuracy: 0.8393 - val_loss: 0.6706 - val_custom_accuracy: 0.8125 Epoch 6/25 7/7 [==============================] - 1s 77ms/step - loss: 0.6449 - custom_accuracy: 0.8571 - val_loss: 0.7594 - val_custom_accuracy: 0.7917 Epoch 7/25 7/7 [==============================] - 1s 76ms/step - loss: 0.6338 - custom_accuracy: 0.8214 - val_loss: 0.6836 - val_custom_accuracy: 0.8333 Epoch 8/25 7/7 [==============================] - 1s 79ms/step - loss: 0.6074 - custom_accuracy: 0.8125 - val_loss: 0.6933 - val_custom_accuracy: 0.7917 Epoch 9/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5805 - custom_accuracy: 0.8661 - val_loss: 0.6830 - val_custom_accuracy: 0.8333 Epoch 10/25 7/7 [==============================] - 1s 76ms/step - loss: 0.5927 - custom_accuracy: 0.8571 - val_loss: 0.6539 - val_custom_accuracy: 0.8125 Epoch 11/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5728 - custom_accuracy: 0.8661 - val_loss: 0.6819 - val_custom_accuracy: 0.8125 Epoch 12/25 7/7 [==============================] - 1s 79ms/step - loss: 0.5892 - custom_accuracy: 0.8571 - val_loss: 0.6453 - val_custom_accuracy: 0.8333 Epoch 13/25 7/7 [==============================] - 1s 78ms/step - loss: 0.6058 - custom_accuracy: 0.8482 - val_loss: 0.7092 - val_custom_accuracy: 0.7292 Epoch 14/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5642 - custom_accuracy: 0.8750 - val_loss: 0.6312 - val_custom_accuracy: 0.8333 Epoch 15/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5828 - custom_accuracy: 0.8661 - val_loss: 0.6869 - val_custom_accuracy: 0.7708 Epoch 16/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5769 - custom_accuracy: 0.8571 - val_loss: 0.6485 - val_custom_accuracy: 0.8125 Epoch 17/25 7/7 [==============================] - 1s 77ms/step - loss: 0.5581 - custom_accuracy: 0.8750 - val_loss: 0.6631 - val_custom_accuracy: 0.7708 Epoch 18/25 7/7 [==============================] - 1s 79ms/step - loss: 0.5426 - custom_accuracy: 0.8571 - val_loss: 0.6215 - val_custom_accuracy: 0.7917 Epoch 19/25 7/7 [==============================] - 1s 77ms/step - loss: 0.5535 - custom_accuracy: 0.8750 - val_loss: 0.6431 - val_custom_accuracy: 0.8333 Epoch 20/25 7/7 [==============================] - 1s 79ms/step - loss: 0.5351 - custom_accuracy: 0.8750 - val_loss: 0.6179 - val_custom_accuracy: 0.8333 Epoch 21/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5343 - custom_accuracy: 0.8571 - val_loss: 0.6229 - val_custom_accuracy: 0.8333 Epoch 22/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5501 - custom_accuracy: 0.8571 - val_loss: 0.6865 - val_custom_accuracy: 0.7917 Epoch 23/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5815 - custom_accuracy: 0.8571 - val_loss: 0.6117 - val_custom_accuracy: 0.8542 Epoch 24/25 7/7 [==============================] - 1s 78ms/step - loss: 0.5502 - custom_accuracy: 0.8750 - val_loss: 0.6104 - val_custom_accuracy: 0.8333 Epoch 25/25 7/7 [==============================] - 1s 77ms/step - loss: 0.5321 - custom_accuracy: 0.8750 - val_loss: 0.6244 - val_custom_accuracy: 0.8333
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. Hybridmodelle
Sie müssen mit der Quantenfaltung nicht von acht Qubits auf ein Qubit wechseln - Sie hätten eine oder zwei Runden der Quantenfaltung durchführen und die Ergebnisse in ein klassisches neuronales Netzwerk einspeisen können. In diesem Abschnitt werden quantenklassische Hybridmodelle untersucht.
2.1 Hybridmodell mit einem einzelnen Quantenfilter
Wenden Sie eine Schicht der Quantenfaltung an und lesen Sie $ \ langle \ hat {Z} _n \ rangle $ auf alle Bits aus, gefolgt von einem dicht verbundenen neuronalen Netzwerk.
2.1.1 Modelldefinition
# 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)
2.1.2 Trainieren Sie das Modell
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))
Epoch 1/25 7/7 [==============================] - 0s 66ms/step - loss: 0.9796 - custom_accuracy: 0.5446 - val_loss: 0.8089 - val_custom_accuracy: 0.7500 Epoch 2/25 7/7 [==============================] - 0s 50ms/step - loss: 0.6154 - custom_accuracy: 0.7589 - val_loss: 0.3587 - val_custom_accuracy: 0.9375 Epoch 3/25 7/7 [==============================] - 0s 51ms/step - loss: 0.2987 - custom_accuracy: 0.9286 - val_loss: 0.2861 - val_custom_accuracy: 1.0000 Epoch 4/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2366 - custom_accuracy: 0.9286 - val_loss: 0.2483 - val_custom_accuracy: 0.9792 Epoch 5/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2445 - custom_accuracy: 0.9196 - val_loss: 0.2535 - val_custom_accuracy: 0.9583 Epoch 6/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2204 - custom_accuracy: 0.9554 - val_loss: 0.2450 - val_custom_accuracy: 0.9583 Epoch 7/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2254 - custom_accuracy: 0.9643 - val_loss: 0.2546 - val_custom_accuracy: 0.9583 Epoch 8/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2203 - custom_accuracy: 0.9464 - val_loss: 0.2662 - val_custom_accuracy: 0.9167 Epoch 9/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2249 - custom_accuracy: 0.9464 - val_loss: 0.2395 - val_custom_accuracy: 1.0000 Epoch 10/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2403 - custom_accuracy: 0.9554 - val_loss: 0.2485 - val_custom_accuracy: 1.0000 Epoch 11/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2231 - custom_accuracy: 0.9554 - val_loss: 0.2453 - val_custom_accuracy: 0.9167 Epoch 12/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2199 - custom_accuracy: 0.9464 - val_loss: 0.2580 - val_custom_accuracy: 0.9375 Epoch 13/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2174 - custom_accuracy: 0.9732 - val_loss: 0.2308 - val_custom_accuracy: 0.9792 Epoch 14/25 7/7 [==============================] - 0s 48ms/step - loss: 0.2138 - custom_accuracy: 0.9643 - val_loss: 0.2826 - val_custom_accuracy: 0.9792 Epoch 15/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2168 - custom_accuracy: 0.9821 - val_loss: 0.2282 - val_custom_accuracy: 0.9792 Epoch 16/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2158 - custom_accuracy: 0.9821 - val_loss: 0.2524 - val_custom_accuracy: 0.9583 Epoch 17/25 7/7 [==============================] - 0s 50ms/step - loss: 0.2122 - custom_accuracy: 0.9643 - val_loss: 0.2415 - val_custom_accuracy: 0.9583 Epoch 18/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2058 - custom_accuracy: 0.9464 - val_loss: 0.2396 - val_custom_accuracy: 0.9583 Epoch 19/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2132 - custom_accuracy: 0.9375 - val_loss: 0.2615 - val_custom_accuracy: 0.9792 Epoch 20/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2144 - custom_accuracy: 0.9821 - val_loss: 0.2276 - val_custom_accuracy: 1.0000 Epoch 21/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2049 - custom_accuracy: 0.9911 - val_loss: 0.2463 - val_custom_accuracy: 0.9583 Epoch 22/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2122 - custom_accuracy: 0.9464 - val_loss: 0.2538 - val_custom_accuracy: 0.9583 Epoch 23/25 7/7 [==============================] - 0s 48ms/step - loss: 0.2200 - custom_accuracy: 0.9732 - val_loss: 0.2516 - val_custom_accuracy: 0.9375 Epoch 24/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2161 - custom_accuracy: 0.9554 - val_loss: 0.2506 - val_custom_accuracy: 0.9583 Epoch 25/25 7/7 [==============================] - 0s 49ms/step - loss: 0.2169 - custom_accuracy: 0.9554 - val_loss: 0.2297 - val_custom_accuracy: 1.0000
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()
Wie Sie sehen können, konvergiert das Hybridmodell mit sehr bescheidener klassischer Unterstützung normalerweise schneller als die reine Quantenversion.
2.2 Hybridfaltung mit mehreren Quantenfiltern
Versuchen wir nun eine Architektur, die mehrere Quantenfaltungen und ein klassisches neuronales Netzwerk verwendet, um sie zu kombinieren.
2.2.1 Modelldefinition
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)
2.2.2 Modell trainieren
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))
Epoch 1/25 7/7 [==============================] - 1s 80ms/step - loss: 0.9445 - custom_accuracy: 0.6250 - val_loss: 0.7592 - val_custom_accuracy: 0.6875 Epoch 2/25 7/7 [==============================] - 0s 59ms/step - loss: 0.5999 - custom_accuracy: 0.8571 - val_loss: 0.3112 - val_custom_accuracy: 1.0000 Epoch 3/25 7/7 [==============================] - 0s 62ms/step - loss: 0.2962 - custom_accuracy: 0.9196 - val_loss: 0.2053 - val_custom_accuracy: 0.9583 Epoch 4/25 7/7 [==============================] - 0s 56ms/step - loss: 0.2971 - custom_accuracy: 0.8929 - val_loss: 0.2274 - val_custom_accuracy: 0.9583 Epoch 5/25 7/7 [==============================] - 0s 56ms/step - loss: 0.2219 - custom_accuracy: 0.9643 - val_loss: 0.2527 - val_custom_accuracy: 0.9583 Epoch 6/25 7/7 [==============================] - 0s 61ms/step - loss: 0.2159 - custom_accuracy: 0.9464 - val_loss: 0.2751 - val_custom_accuracy: 0.8750 Epoch 7/25 7/7 [==============================] - 0s 59ms/step - loss: 0.2123 - custom_accuracy: 0.9554 - val_loss: 0.2428 - val_custom_accuracy: 0.9583 Epoch 8/25 7/7 [==============================] - 0s 58ms/step - loss: 0.2084 - custom_accuracy: 0.9821 - val_loss: 0.2297 - val_custom_accuracy: 0.9583 Epoch 9/25 7/7 [==============================] - 0s 56ms/step - loss: 0.2093 - custom_accuracy: 0.9821 - val_loss: 0.2674 - val_custom_accuracy: 0.9583 Epoch 10/25 7/7 [==============================] - 0s 55ms/step - loss: 0.2035 - custom_accuracy: 0.9643 - val_loss: 0.2452 - val_custom_accuracy: 0.9583 Epoch 11/25 7/7 [==============================] - 0s 55ms/step - loss: 0.2120 - custom_accuracy: 0.9643 - val_loss: 0.2744 - val_custom_accuracy: 0.9167 Epoch 12/25 7/7 [==============================] - 0s 61ms/step - loss: 0.2139 - custom_accuracy: 0.9554 - val_loss: 0.2394 - val_custom_accuracy: 0.9583 Epoch 13/25 7/7 [==============================] - 0s 58ms/step - loss: 0.2086 - custom_accuracy: 0.9732 - val_loss: 0.2416 - val_custom_accuracy: 0.9583 Epoch 14/25 7/7 [==============================] - 0s 55ms/step - loss: 0.2005 - custom_accuracy: 0.9911 - val_loss: 0.2945 - val_custom_accuracy: 0.9375 Epoch 15/25 7/7 [==============================] - 0s 55ms/step - loss: 0.2163 - custom_accuracy: 0.9286 - val_loss: 0.2349 - val_custom_accuracy: 0.9583 Epoch 16/25 7/7 [==============================] - 0s 54ms/step - loss: 0.2096 - custom_accuracy: 0.9464 - val_loss: 0.2474 - val_custom_accuracy: 0.9375 Epoch 17/25 7/7 [==============================] - 0s 58ms/step - loss: 0.2204 - custom_accuracy: 0.9643 - val_loss: 0.2637 - val_custom_accuracy: 0.9583 Epoch 18/25 7/7 [==============================] - 0s 57ms/step - loss: 0.2112 - custom_accuracy: 0.9643 - val_loss: 0.2433 - val_custom_accuracy: 0.9375 Epoch 19/25 7/7 [==============================] - 0s 61ms/step - loss: 0.2006 - custom_accuracy: 0.9554 - val_loss: 0.2515 - val_custom_accuracy: 0.9375 Epoch 20/25 7/7 [==============================] - 0s 60ms/step - loss: 0.2058 - custom_accuracy: 0.9643 - val_loss: 0.2786 - val_custom_accuracy: 0.9375 Epoch 21/25 7/7 [==============================] - 0s 57ms/step - loss: 0.1959 - custom_accuracy: 0.9821 - val_loss: 0.2590 - val_custom_accuracy: 0.9375 Epoch 22/25 7/7 [==============================] - 0s 54ms/step - loss: 0.2111 - custom_accuracy: 0.9554 - val_loss: 0.2456 - val_custom_accuracy: 0.9583 Epoch 23/25 7/7 [==============================] - 0s 56ms/step - loss: 0.1979 - custom_accuracy: 0.9911 - val_loss: 0.2502 - val_custom_accuracy: 0.9583 Epoch 24/25 7/7 [==============================] - 0s 57ms/step - loss: 0.1976 - custom_accuracy: 0.9732 - val_loss: 0.2427 - val_custom_accuracy: 0.9583 Epoch 25/25 7/7 [==============================] - 0s 53ms/step - loss: 0.2110 - custom_accuracy: 0.9464 - val_loss: 0.2504 - val_custom_accuracy: 0.9375
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()