版权所有 (c) 2021 百度量子计算研究所,保留所有权利。
在这一节中,我们将介绍量子分类器(Quantum Classifier)。顾名思义,量子分类器指的是使用量子算法来完成机器学习中的数据分类任务。分类任务是机器学习领域中一个十分基础,同时也有着重要实际应用的问题。因此,量子分类器也经常在量子机器学习(Quantum Machine Learning, QML)的框架下进行讨论。需要指出的是,在一些量子机器学习的早期文献中,人们提出过一系列基于量子相位估计(Quantum Phase Estimation, QPE)以及量子随机存储器(Quantum Random Access Memory, QRAM)的量子分类器。不太严谨地说,这一系列算法是通过量子计算机对传统机器学习中的部分线性运算过程进行加速实现的。但是对于近期的量子机器来说,上文中提到的两种技术的实现存在较大的挑战,因此将不在本章节“近期量子算法“的框架内进行讨论,感兴趣的读者可以阅读相关的综述文章进行了解。本章节主要将介绍下面两种量子分类器,并给出量桨上的实现方式:
这两种方法分别对应着经典机器学习中的神经网络方法与核方法。
本节主要将针对二分类问题进行讨论。下面给出其数学上的定义:
二分类问题:给定一组训练数据 ,其中 为数据空间 中的一个数据点, 为每个数据点的标签。定义一个参数化的分类器函数 ,并且通过最小化其在训练数据 上的损失函数 定义其最优参数 ,
其中损失函数 可以具有不同的形式,一般来说需要满足当 时, 的条件。对于由最优参数定义的最优分类器 而言,我们希望它在整个数据空间 上都具有良好的分类性能,这也被称为分类器的泛化能力。该能力是评价一个分类器效果的重要指标。
在本节中,我们会给出两种量子分类器对应的量桨实现代码。为此,首先需要生成二分类问题的数据集,并将其划分为训练以及测试数据。通过测试数据,可以计算训练后的分类器的测试误差,从而对整个数据空间上的泛化误差进行估计。如何决定训练数据与测试数据的划分是一个比较经验性的问题,这里我们简单地采取 50%-50% 的划分方式。此外,本节中考虑的例子是一个圆形决策边界的二分类问题,且训练数据和测试数据都是从相同的数据分布中采样而来的,这样的数据可以通过如下代码生成(本节需要使用 sklearn
中的相关函数来实现支持向量机的功能。需要的读者可以运行下面的代码块来安装相关模块:):
from IPython.display import clear_output
!pip install scikit-learn
clear_output()
from sklearn.datasets import make_circles
X_train, y_train = make_circles(10, noise=0.05, factor=0.2)
X_test, y_test = make_circles(10, noise=0.05, factor=0.2)
对其进行可视化:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(1, 2, figsize=[10, 4])
ax[0].scatter(X_train[:,0], X_train[:,1], marker='o',
c = plt.cm.coolwarm(np.array(y_train, dtype=np.float32)))
ax[0].set_title('Train')
ax[1].set_title('Test')
ax[1].scatter(X_test[:,0], X_test[:,1], marker='v', c = plt.cm.coolwarm(np.array(y_test, dtype=np.float32)))
plt.show()
数据输出
变分量子分类器,指的是通过参数化量子电路 (parameterized quantum circuits, PQC) 来学习分类函数的一种方法。这类方法早期工作的主要代表是于 2018 年发表的 Kosuke Mitarai 等人的 Quantum Circuit Learning, Edward Farhi 与 Hartmut Neven,以及 Maria Schuld 等人的 Circuit-centric quantum classifiers。
如图 1 所示,训练变分量子分类器来学习分类函数 的流程可以分为如下几个部分:
步骤 1: 通过与数据有关的参数化编码电路 得到关于数据的量子态
步骤 2: 通过参数化量子电路 ,即拟设 (Ansatz),得到末态
步骤 3: 采样计算基于可观测量的 的期望值 作为输出
步骤 4: 根据该输出计算损失函数 ,并根据梯度信息 更新参数化量子电路 中的参数
不难发现,该学习方案和机器学习中的神经网络方法有着极大的相似性:在神经网络方法中,我们通过梯度更新神经元之间的链接权重来学习分类函数;变分量子分类器则通过梯度来更新量子电路中的旋转门的角度 来进行类似的训练。因为这种形式上的相似性,该方法以及其中的参数化量子电路也往往被称为量子神经网络(Quantum Neural Network, QNN)。
在这个类比的基础上,一个量子神经网络的有效性可以从两个角度来进行分析:表达能力(Expressibility)和可训练性(Trainability)。粗略来说,表达能力指的是量子神经网络学习数据中复杂规律的能力——表达能力越强,该分类器就可以在越复杂的数据集上取得更小的误差。可训练性则指的是通过梯度优化等最优化方法在参数空间中搜索到最优解的能力。值得一提的是,和经典神经网络类似,量子神经网络也面临着梯度消失的问题——该现象被称为贫瘠高原(Barren Plateau),指的是梯度的方差随着量子比特数的增加呈指数衰减。如何通过设计不同的拟设或者不同的损失函数来提高变分量子分类器的表达能力或者是可训练性,是目前一个比较值得探索的方向。
图 1:变分量子分类器示意图。
让我们回到实际的例子上,以上文提到的二维圆形数据的二分类问题为例来展示变分量子分类器的工作流程以及代码实现。如图 2 所示,我们使用两个量子比特来对每个数据点进行编码,即通过一个编码电路 ,使得
其中 为经典数据 对应的量子态。之后通过一个拟设 使得
最后再对第一个量子比特上进行 测量得到 之间的输出,即
最后测量得到的期望就作为预测的标签,即 。注意到这里我们并没有给出具体的编码电路和拟设电路,这是因为在这个框架下我们并不需要固定某一种编码电路以及拟设电路中的量子门的具体排列方式,它们只需要满足上文中提到的形式即可。在实际应用中,不同的编码电路以及不同的拟设电路都具有不同的表达能力,因此也会影响分类器的学习效果。不过,编码电路以及拟设电路对表达能力的影响本身是一个比较复杂的话题,因此在本节中不会进行更深入的展开,感兴趣的读者可以阅读量桨官网中有关数据编码和表达能力的教程。我们也鼓励读者在我们下文中给出的代码实现的基础上,通过尝试自己搭建不同的编码和拟设电路,又或者利用量桨中提供的众多模版来进行更多样的尝试。
图 2:变分量子分类器电路图。
下面,我们给出量桨中实现变分量子分类器的代码。需要注意的是,我们利用了内置的 iqp_encoding()
函数和 real_entanglement_layer()
函数作为模版来分别构建了编码电路和拟设电路。
import paddle
from paddle_quantum.circuit import UAnsatz
# 搭建整个优化流程图
class ClassifierNet(paddle.nn.Layer):
def __init__(self, depth, dtype='float64'):
super(ClassifierNet, self).__init__()
self.depth = depth
# 初始化参数列表 theta,并用 [0, 2*pi] 的均匀分布来填充初始值
self.theta = self.create_parameter(
shape=[depth, 2, 1],
default_initializer=paddle.nn.initializer.Uniform(low=0.0, high=2*np.pi),
dtype=dtype,
is_bias=False)
# 定义前向传播机制,即根据数据点预测器标签的决策函数
def forward(self, x):
# 创建电路
cir = UAnsatz(2)
# 根据数据添加编码电路
cir.iqp_encoding(paddle.to_tensor(x))
# 添加参数化量子电路,即拟设
cir.real_entangled_layer(self.theta, self.depth)
cir.run_state_vector()
# 测量末态的第一个量子比特
pred = cir.expecval([[1, 'Z0']])
return pred
ITR = 120 # 训练迭代的次数
LR = 0.1 # 基于梯度下降的优化方法的学习率
SEED = 2048 #设置全局随机数种子
paddle.seed(SEED)
loss_record = []
accuracy_train_record = []
accuracy_test_record = []
net = ClassifierNet(depth=2)
# 使用 Adam 优化器
opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())
# 梯度下降循环
for itr in range(1, ITR + 1):
# 运行上面定义的网络并计算整个训练集上的损失函数
loss = 0
for x, y in zip(X_train, y_train):
pred = net(x)
loss += (y - pred) ** 2
# 计算梯度并优化
loss.backward()
opt.minimize(loss)
opt.clear_grad()
loss_record.append(loss.numpy())
# 每 20 次循环计算其在训练和数据集上的准确率(预测正确的比例)
if itr % 20 == 0 or itr == 1:
pred_train = []
for x in X_train:
pred_train.append(net(x)[0].numpy())
pred_train = (np.sign(np.array(pred_train).reshape(len(pred_train)) - 0.5)/2 + 0.5)
accuracy_train = 1 - sum(abs(pred_train - y_train))/len(y_train)
accuracy_train_record.append(accuracy_train)
pred_test = []
for x in X_test:
pred_test.append(net(x)[0].numpy())
pred_test = (np.sign(np.array(pred_test).reshape(len(pred_test)) - 0.5)/2 + 0.5)
accuracy_test = 1 - sum(abs(pred_test - y_test))/len(y_test)
accuracy_test_record.append(accuracy_test)
print('iter: {}, loss: {:.15f}, accuracy train: {:.2%} test: {:.2%}.'\
.format(itr, loss.numpy()[0], accuracy_train, accuracy_test))
运行结果如下:
iter: 1, loss: 4.611088153678840, accuracy train: 50.00% test: 50.00%.
iter: 20, loss: 1.633640630557960, accuracy train: 80.00% test: 80.00%.
iter: 40, loss: 1.367107079146196, accuracy train: 90.00% test: 90.00%.
iter: 60, loss: 1.176641838367413, accuracy train: 90.00% test: 100.00%.
iter: 80, loss: 1.145952780102315, accuracy train: 100.00% test: 100.00%.
iter: 100, loss: 1.146063312512371, accuracy train: 100.00% test: 100.00%.
iter: 120, loss: 1.145638848302851, accuracy train: 100.00% test: 100.00%.
可以看到,在迭代过程中,分类器对训练集和测试集的分类效果都越来越好。下面我们给出此分类器得到的决策边界,并作图:
# 生成数据空间中的均匀网格
x = np.linspace(-1.2, 1.2, 10)
y = np.linspace(-1.2, 1.2, 10)
x_mesh, y_mesh = np.meshgrid(x, y)
# 计算网格上的决策函数值
pred_mesh = np.zeros_like(x_mesh)
for i in range(10):
for j in range(10):
pred_mesh[i, j] = net(np.array([x_mesh[i, j], y_mesh[i, j]]))
# 可视化决策函数以及决策边界
plt.contourf(x_mesh, y_mesh, pred_mesh, levels=50, cmap='coolwarm', vmin=0, vmax=1)
plt.contour(x_mesh, y_mesh, pred_mesh, levels=[0.5], vmin=0, vmax=1, colors='black')
plt.scatter(X_train[:,0], X_train[:,1],
marker='o', c = plt.cm.coolwarm(np.array(y_train, dtype=np.float32)))
plt.scatter(X_test[:,0], X_test[:,1],
marker='^', c = plt.cm.coolwarm(np.array(y_test, dtype=np.float32)))
变分分类输出1
在变分量子分类器的基础上,人们意识到了虽然该方法的训练流程和机器学习中的神经网络方法有着极大的相似性,但是其数学原理却更加类似于统计机器学习中的另外一种方法:核方法(Kernel Methods)。
在更进一步地介绍该方法的思想之前,我们需要先引入两个概念:量子特征映射与量子核函数。简单地来说,该方法借用了机器学习中常见的特征空间与特征映射的思想——通过将在原始数据空间中线性不可分的数据映射到一个合适的特征空间中,再通过一些线性的操作对其进行分类或计算。
图 3:通过特征映射来进行线性分类的示意图。
对于量子分类器而言,这种特征映射可以被定义为:
量子特征映射 (Quantum Feature Map):给定一个 量子比特的量子系统,定义 为一个 的复线性空间并定义 为 中的内积。那么如下映射就可以被称之为量子特征映射:
其中 为数据空间。并且,在本节讨论的范围内,我们一般认为这样的特征映射是通过一个编码电路 作用在 态上完成的。
注意到式(5)中定义的量子特征映射只是一个数学上的概念,在真实的量子计算机中,我们并不能直接以矩阵的形式得到映射后的特征向量 。但是相反,两个量子态之间的内积,即希尔伯特-施密特距离,是可以在量子计算机上相对高效地进行估计的。这其实和经典核方法的思路非常类似——即只通过计算特征空间中内积的方法来隐式地引入特征映射。所以在这里我们可以进一步引入量子核函数的概念:
量子核函数(Quantum Kernel Function):给定一个量子特征映射 ,量子核函数 是一个定义了两个数据点 在特征空间 中的内积的正定函数,即
在量子计算机上,我们可以利用如图 4 中所示的量子核估计(Quantum Kernel Estimation)电路来估计输出为 的概率来计算该核函数。
图 4:量子核估计电路图。
在介绍完了量子核方法中的特征映射和核函数的定义之后,让我们回到量子核分类器的概念上来——这种观点认为,在上文中提到的变分量子分类器方案中,由于无论是拟设 还是测量 都是在特征空间中的线性操作,因此量子分类器的表达能力应该只取决于量子编码电路的形式 。具体的来说,考虑式(4)中所定义的变分量子分类器输出,即决策函数
若令 ,那么上式可以被重新写为
其中用到 。在先前与神经网络的对比中,编码电路、拟设以及测量共同构成了一个可训练的非线性决策函数,而式(8)提供了另外一种理解变分量子分类器的视角——分类函数实际上是在计算特征空间中两个特征向量的内积。换言之,变分量子分类器“学习”的实际上是一个由特征空间中的超平面定义的线性分类函数。在这个基础上,Schuld 证明了一个结论:对于一般的量子机器学习任务而言,通过变分量子线路可能学习到的最优拟设以及参数所代表的函数总可以被核函数所表示。利用本节中的例子来说明的话,考虑给定损失函数 、测量方案 、编码电路 以及拟设 下的最优参数为 ,即
那么总是存在一组参数 使得 (7) 式中的最优超平面 可以被核函数表示为
其中 。以上便构成了量子核方法的理论支撑,下面我们来给出一个实际上的量子核分类器的例子。
在经典机器学习中,核方法最具代表性的应用就是支持向量机(Support Vector Machine, SVM)。支持向量机实际上就是在特征空间 中的线性分类器——通过一个超平面 可以定义它的决策函数
其中 和 为特征空间中的向量, 为特征空间中的内积。这里我们仅考虑简单的硬间隔(Hard-Margin)支持向量机模型,给定数据集 ,最优分割超平面由如下约束优化问题定义:
在这个基础上,我们引入拉格朗日乘子写出原问题式(12)的拉格朗日量
这一步的目的是为了将约束优化问题转化为一个非约束优化问题,此时原问题中的最优解对应着拉格朗日量 的一个鞍点
在这个基础上,引入 Karush-Kuhn-Tucker(KKT)条件可以得到 以及 ,此时寻找拉格朗日量鞍点的问题就可以被写为
式(15)中的优化问题也被称为原问题的对偶问题。该对偶问题是一个二次规划问题,可以被高效地求解。并且,此时的决策函数也可以由 KKT 条件重新写为
此时,若将特征空间 选定为上文中定义的量子特征空间,并利用量子线路来计算式(16)中的内积作为量子核函数,即 ,这就构成了量子核-支持向量机分类器。式(12)到(16)中给出的支持向量机也可以作为上文中式(10)提到的利用核方法来表示最优线性模型在分类任务中的一个特例。
下面,我们给出在量桨中实现量子核-支持向量机(QKE-SVM)的代码示例。首先,我们利用量桨搭建量子核估计电路,注意这里我们使用了内置的 iqp_encoding()
函数作为编码电路(特征映射)的模版:
def q_kernel_estimator(x1, x2):
"""量子核估计电路,返回 |<x1|x2>|^2
"""
# 将数据转换为 Tensor 类型
x1 = paddle.to_tensor(x1)
x2 = paddle.to_tensor(x2)
# 创建电路
cir = UAnsatz(2)
# 添加对于第一个输入数据的编码电路
cir.iqp_encoding(x1)
# 添加对于第二个输入数据的逆编码电路
cir.iqp_encoding(x2, invert=True)
# 运行电路,并测量输出
cir.run_state_vector()
measure_res = cir.measure()
# 统计观测到 '0...0' 态的概率,注意默认的 shots 数为 1024
return measure_res['00'] / 1024
def q_kernel_matrix(X1, X2):
"""利用量子核估计电路计算两个数组之间的核函数
"""
return np.array([[q_kernel_estimator(x1, x2) for x2 in X2] for x1 in X1])
然后利用 SVC
在量子核函数的基础上训练支持向量机:
from sklearn import svm
# 创建一个核函数为量子核估计方法的支持向量机分类器
svm_qke_clf = svm.SVC(kernel=q_kernel_matrix)
# 利用训练数据训练分类器
svm_qke_clf.fit(X_train, y_train)
训练完成后,可以计算其在训练集和测试集上的分类表现:
# 输出在数据集上的预测标签
predict_svm_qke_train = svm_qke_clf.predict(X_train)
predict_svm_qke_test = svm_qke_clf.predict(X_test)
# 计算准确率
accuracy_train = np.array(predict_svm_qke_train == y_train, dtype=int).sum() / len(y_train)
accuracy_test = np.array(predict_svm_qke_test == y_test, dtype=int).sum() / len(y_test)
# 可视化结果
fig, ax = plt.subplots(1, 2, figsize=[10, 4])
ax[0].scatter(X_train[:,0], X_train[:,1], marker='o',
c = plt.cm.coolwarm(np.array(predict_svm_qke_train, dtype=np.float32)))
ax[0].set_title('Prediction on training set, accuracy={:.2f}'.format(accuracy_train))
ax[1].scatter(X_test[:,0], X_test[:,1], marker='v',
c = plt.cm.coolwarm(np.array(predict_svm_qke_test, dtype=np.float32)))
ax[1].set_title('Prediction on testing set, accuracy={:.2f}'.format(accuracy_test))
plt.show()
print("QKE-SVM 在数据集上的分类效果为:")
QKE-SVM 在数据集上的分类效果为:
核分类器输出1
更进一步地,我们还可以绘制出其在整个数据空间上的决策函数与决策边界:
x = np.linspace(-1.2, 1.2, 10)
y = np.linspace(-1.2, 1.2, 10)
x_mesh, y_mesh = np.meshgrid(x, y)
pred_mesh = svm_qke_clf.decision_function(np.c_[x_mesh.ravel(), y_mesh.ravel()])
pred_mesh = pred_mesh.reshape(x_mesh.shape)
plt.contourf(x_mesh, y_mesh, pred_mesh, levels=50, cmap='coolwarm', vmin=-1, vmax=1)
plt.contour(x_mesh, y_mesh, pred_mesh, levels=[0], vmin=-1, vmax=1, colors='black')
plt.scatter(X_train[:,0], X_train[:,1],
marker='o', c = plt.cm.coolwarm(np.array(y_train, dtype=np.float32)))
plt.scatter(X_test[:,0], X_test[:,1],
marker='^', c = plt.cm.coolwarm(np.array(y_test, dtype=np.float32)))
plt.show()
核分类器输出2
本节中介绍了两种量子分类器:变分量子分类器和量子核分类器。一方面,变分量子分类器和神经网络方法类似,通过参数化量子电路构建了高维非线性函数来学习数据中的规律,从而进行分类。这种方法主要认为由于态空间的维度随着量子比特数呈指数级增加,“量子神经网络”会比经典神经网络具有更强的表达能力。不过,指数增大的态空间同时也带来了梯度消失的问题——贫瘠高原现象,严重影响了参数化量子电路的可训练性。另一方面,随着对这一类量子算法研究的推进,人们意识到它们的数学原理更类似于核方法。在这个观点下,将经典数据编码到量子态的特征映射决定了参数化量子电路的表达能力,并且我们可以利用核方法来绕过可训练性的问题。但是,量子核分类器本身也有着其局限性——和传统的核方法类似,因为这一类方法需要计算每两个数据点之间的内积,因此在大规模的数据集上难以取得应用。
此外需要指出的是,变分量子分类器也可以通过修改拟设的形式,或者是使用不简单基于可观测量的损失函数来引入额外的非线性 ,而在这种情况下它们即便在特征空间中也不再是线性模型,此时上文的分析不再适用。对于量子核方法而言,也可以设计不同于式(6)中定义的核函数。
总而言之,分类任务作为监督机器学习下的一个基本问题,相关研究对于人们理解量子机器学习这一尚且十分年轻的方向有着重要的作用。无论是分析量子机器学习中的可训练性、表达能力或者是更深层次的数学联系,量子分类器都是一个很好的测试平台。
[1] Biamonte, Jacob, et al. "Quantum machine learning." Nature 549.7671 (2017): 195-202.
[2] Mitarai, Kosuke, et al. "Quantum circuit learning." Physical Review A 98.3 (2018): 032309.
[3] Farhi, Edward, and Hartmut Neven. "Classification with quantum neural networks on near term processors." arXiv preprint arXiv:1802.06002 (2018).
[4] Schuld, Maria, et al. "Circuit-centric quantum classifiers." Physical Review A 101.3 (2020): 032308.
[5] Havlíček, Vojtěch, et al. "Supervised learning with quantum-enhanced feature spaces." Nature 567.7747 (2019): 209-212.
[6] Schuld, Maria. "Supervised quantum machine learning models are kernel methods." arXiv preprint arXiv:2101.11020 (2021).
[7] Li, Guangxi, Zhixin Song, and Xin Wang. "VSQL: Variational Shadow Quantum Learning for Classification." Proceedings of the AAAI Conference on Artificial Intelligence. Vol. 35. No. 9. 2021.
[8] Huang, Hsin-Yuan, et al. "Power of data in quantum machine learning." Nature Communications 12.1 (2021): 1-9.
最后更新时间:2021年12月20日