分类问题的量子机器学习技巧#

一些常见设置和技巧的演示

概述#

我们使用 fashion-MNIST 数据集来建立二进制分类任务,我们将尝试不同的输入编码方案,并对量子输出应用可能的经典后处理以提高分类精度。 在本教程中,我们使用 TensorFlow 后端,并尝试始终如一地使用 TensorCircuit 为量子函数提供的 Keras 接口,在这里我们可以神奇地将量子函数转换为 Keras 层。

[1]:
from matplotlib import pyplot as plt
from sklearn.decomposition import PCA
import tensorflow as tf
import tensorcircuit as tc

K = tc.set_backend("tensorflow")

数据集和预处理#

我们首先加载 fashion-mnist 数据集并区分 T 恤 (0) 和裤子 (1)。

[2]:
(x_train, y_train), (x_test, y_test) = tc.templates.dataset.mnist_pair_data(
    0, 1, loader=tf.keras.datasets.fashion_mnist
)
[3]:
x_train.shape, y_train.shape, x_test.shape, y_test.shape
[3]:
((12000, 28, 28, 1), (12000,), (2000, 28, 28, 1), (2000,))
[4]:
plt.imshow(x_train[0])
[4]:
<matplotlib.image.AxesImage at 0x7f9147029610>
../_images/tutorials_qml_scenarios_cn_6_1.png
[5]:
plt.imshow(x_train[1])
[5]:
<matplotlib.image.AxesImage at 0x7f91473b6c10>
../_images/tutorials_qml_scenarios_cn_7_1.png

幅度编码#

[6]:
x_train = tf.image.pad_to_bounding_box(x_train, 2, 2, 32, 32)
x_test = tf.image.pad_to_bounding_box(x_test, 2, 2, 32, 32)
[7]:
batched_ae = K.vmap(tc.templates.dataset.amplitude_encoding, vectorized_argnums=0)
[8]:
x_train_q = batched_ae(x_train, 10)
x_test_q = batched_ae(x_test, 10)
[9]:
n = 10
blocks = 3


def qml(x, weights):
    c = tc.Circuit(n, inputs=x)
    for j in range(blocks):
        for i in range(n):
            c.rx(i, theta=weights[j, i, 0])
            c.rz(i, theta=weights[j, i, 1])
        for i in range(n - 1):
            c.exp1(i, i + 1, theta=weights[j, i, 2], unitary=tc.gates._zz_matrix)
    outputs = K.stack(
        [K.real(c.expectation([tc.gates.z(), [i]])) for i in range(n)]
        + [K.real(c.expectation([tc.gates.x(), [i]])) for i in range(n)]
    )
    outputs = K.reshape(outputs, [-1])
    return K.sigmoid(K.sum(outputs))


qml_layer = tc.keras.QuantumLayer(qml, weights_shape=[blocks, n, 3])
[10]:
model = tf.keras.Sequential([qml_layer])
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(),
    optimizer=tf.keras.optimizers.Adam(0.01),
    metrics=[tf.keras.metrics.BinaryAccuracy()],
)

model.fit(x_train_q, y_train, batch_size=32, epochs=3, validation_split=0.8)
Epoch 1/3
75/75 [==============================] - 85s 559ms/step - loss: 0.6217 - binary_accuracy: 0.7667 - val_loss: 0.3990 - val_binary_accuracy: 0.9620
Epoch 2/3
75/75 [==============================] - 14s 185ms/step - loss: 0.3701 - binary_accuracy: 0.9571 - val_loss: 0.3421 - val_binary_accuracy: 0.9507
Epoch 3/3
75/75 [==============================] - 14s 185ms/step - loss: 0.3252 - binary_accuracy: 0.9542 - val_loss: 0.3030 - val_binary_accuracy: 0.9540
[10]:
<keras.callbacks.History at 0x7f9147416e50>

经典后处理#

我们在量子输出之后附加了一个线性层,以增强机器学习模型作为量子-神经混合机器学习方法的能力。

[11]:
def qml(x, weights):
    c = tc.Circuit(n, inputs=x)
    for j in range(blocks):
        for i in range(n):
            c.rx(i, theta=weights[j, i, 0])
            c.rz(i, theta=weights[j, i, 1])
        for i in range(n - 1):
            c.exp1(i, i + 1, theta=weights[j, i, 2], unitary=tc.gates._zz_matrix)
    outputs = K.stack(
        [K.real(c.expectation([tc.gates.z(), [i]])) for i in range(n)]
        + [K.real(c.expectation([tc.gates.x(), [i]])) for i in range(n)]
    )
    outputs = K.reshape(outputs, [-1])
    return outputs


qml_layer = tc.keras.QuantumLayer(qml, weights_shape=[blocks, n, 3])

inputs = tf.keras.Input(shape=(2**n), dtype=tf.complex64)
measurements = qml_layer(inputs)
output = tf.keras.layers.Dense(1, activation="sigmoid")(measurements)
model = tf.keras.Model(inputs=inputs, outputs=output)
[12]:
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(),
    optimizer=tf.keras.optimizers.Adam(0.01),
    metrics=[tf.keras.metrics.BinaryAccuracy()],
)

model.fit(x_train_q, y_train, batch_size=32, epochs=3, validation_split=0.8)
Epoch 1/3
75/75 [==============================] - 71s 508ms/step - loss: 0.5140 - binary_accuracy: 0.8841 - val_loss: 0.3617 - val_binary_accuracy: 0.9521
Epoch 2/3
75/75 [==============================] - 14s 182ms/step - loss: 0.2803 - binary_accuracy: 0.9421 - val_loss: 0.2093 - val_binary_accuracy: 0.9506
Epoch 3/3
75/75 [==============================] - 15s 200ms/step - loss: 0.2057 - binary_accuracy: 0.9437 - val_loss: 0.1795 - val_binary_accuracy: 0.9483
[12]:
<keras.callbacks.History at 0x7f913d4ee520>

PCA 嵌入#

幅度编码很难在真正的量子硬件上实现,我们在这里考虑另一种数据输入方式,其中只涉及单个量子比特旋转。为了压缩输入数据以使其适合小型电路,需要 PCA 降维。

[13]:
x_train_r = PCA(10).fit_transform(x_train.numpy().reshape([-1, 32 * 32]))
[14]:
x_train_r.shape  # 我们现在对每个图形进行 10 维向量压缩
[14]:
(12000, 10)
[15]:
def qml(x, weights):
    c = tc.Circuit(n)
    for i in range(10):
        c.rx(i, theta=x[i])  # 加载数据
    for j in range(blocks):
        for i in range(n):
            c.rx(i, theta=weights[j, i, 0])
            c.rz(i, theta=weights[j, i, 1])
        for i in range(n - 1):
            c.exp1(i, i + 1, theta=weights[j, i, 2], unitary=tc.gates._zz_matrix)
    outputs = K.stack(
        [K.real(c.expectation([tc.gates.z(), [i]])) for i in range(n)]
        + [K.real(c.expectation([tc.gates.x(), [i]])) for i in range(n)]
    )
    outputs = K.reshape(outputs, [-1])
    return K.sigmoid(K.sum(outputs))


qml_layer = tc.keras.QuantumLayer(qml, weights_shape=[blocks, n, 3])
[16]:
model = tf.keras.Sequential([qml_layer])
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(),
    optimizer=tf.keras.optimizers.Adam(0.01),
    metrics=[tf.keras.metrics.BinaryAccuracy()],
)

model.fit(x_train_r, y_train, batch_size=32, epochs=3, validation_split=0.8)
Epoch 1/3
75/75 [==============================] - 71s 447ms/step - loss: 0.7993 - binary_accuracy: 0.6996 - val_loss: 0.3026 - val_binary_accuracy: 0.8829
Epoch 2/3
75/75 [==============================] - 6s 80ms/step - loss: 0.2745 - binary_accuracy: 0.8983 - val_loss: 0.2559 - val_binary_accuracy: 0.9087
Epoch 3/3
75/75 [==============================] - 6s 83ms/step - loss: 0.2513 - binary_accuracy: 0.9167 - val_loss: 0.2385 - val_binary_accuracy: 0.9187
[16]:
<keras.callbacks.History at 0x7f91204f1430>

数据重新加载#

通过在 VQA 中多次加载 PCA 嵌入数据,我们可以进一步提高模型的准确性。

[17]:
def qml(x, weights):
    c = tc.Circuit(n)
    for j in range(blocks):
        for i in range(10):
            c.ry(i, theta=x[i])  # 重复加载数据
        for i in range(n):
            c.rx(i, theta=weights[j, i, 0])
            c.rz(i, theta=weights[j, i, 1])
        for i in range(n - 1):
            c.exp1(i, i + 1, theta=weights[j, i, 2], unitary=tc.gates._zz_matrix)
    outputs = K.stack(
        [K.real(c.expectation([tc.gates.z(), [i]])) for i in range(n)]
        + [K.real(c.expectation([tc.gates.x(), [i]])) for i in range(n)]
    )
    outputs = K.reshape(outputs, [-1])
    return K.sigmoid(K.sum(outputs))


qml_layer = tc.keras.QuantumLayer(qml, weights_shape=[blocks, n, 3])
[19]:
model = tf.keras.Sequential([qml_layer])
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(),
    optimizer=tf.keras.optimizers.Adam(0.01),
    metrics=[tf.keras.metrics.BinaryAccuracy()],
)

model.fit(x_train_r, y_train, batch_size=32, epochs=3, validation_split=0.8)
Epoch 1/3
75/75 [==============================] - 20s 146ms/step - loss: 0.2506 - binary_accuracy: 0.9070 - val_loss: 0.2271 - val_binary_accuracy: 0.9177
Epoch 2/3
75/75 [==============================] - 7s 95ms/step - loss: 0.2191 - binary_accuracy: 0.9246 - val_loss: 0.2071 - val_binary_accuracy: 0.9287
Epoch 3/3
75/75 [==============================] - 7s 95ms/step - loss: 0.2049 - binary_accuracy: 0.9287 - val_loss: 0.2016 - val_binary_accuracy: 0.9305
[19]:
<keras.callbacks.History at 0x7f90a813b460>