RSVP für Ihr lokales TensorFlow Everywhere-Event noch heute!
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Quantenfaltungs-Neuronales Netz

Ansicht auf TensorFlow.org Führen Sie in Google Colab aus Quelle auf GitHub anzeigen Notizbuch herunterladen

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:

  1. Wie beim QCNN ist der Clusterstatus auf einem Ring translatorisch invariant.
  2. 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:

  1. Die Eingabe in das Modell ist ein Schaltungstensor - entweder eine leere Schaltung oder ein X-Gatter an einem bestimmten Qubit, das eine Anregung anzeigt.
  2. Der Rest der Quantenkomponenten des Modells besteht aus tfq.layers.AddCircuit Schichten.
  3. 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.

svg

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')))

svg

Und die Zwei-Qubit-Einheitsschaltung:

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

svg

Und die Zwei-Qubit-Pooling-Schaltung:

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

svg

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')))

svg

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')))

svg

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)

png

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()

png

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)

png

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()

png

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)

png

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()

png