MindSpore Quantum 量子计算编程与实践:轻松上手量子卷积神经网络

在本文中,我们将介绍一些量子信息的基础知识 和 MindQuantum 量子计算框架的基本用法,最后基于 MindQuantum 0.7.0 搭建量子卷积神经网络,以实现对 MINIST 手写字体的识别任务。

基础知识

安装 MindQuantum 0.7.0 版本并导入

!pip install mindquantum==0.7.0
from mindquantum import *
import numpy as np

量子态和模拟器

在 MindQuantum 中,对量子系统的模拟主要由量子模拟器 Simulator 完成。Simulator 中维护着一个量子态,通过对其施加量子门、量子线路或者直接设置的方式,可以改变这个量子态,从而完成计算任务。

sim = Simulator('projectq', n_qubits=1) # 第一个参数为模拟器后端的名字,第二个参数为模拟器的比特数。
print(sim)
projectq simulator with 1 qubit (little endian).
Current quantum state:
1¦0⟩

模拟器中,所有比特的初态都是 ∣ 0 ⟩ |0\rangle ∣0⟩。我们可以通过 get_qs(ket=False) 来获取模拟器中的量子态,其中参数 ket 表示是否以狄拉克符号形式表示量子态,默认为 False,采用数组形式表示。

state_array = sim.get_qs()       # 采用默认的数组形式,显示量子态
print('量子态的数组形式为:\t', state_array)state_ket = sim.get_qs(ket=True) # 采用狄拉克符号形式,显示量子态
print('量子态的狄拉克形式为:\t', state_ket)
量子态的数组形式为:     [1.+0.j 0.+0.j]
量子态的狄拉克形式为:   1¦0⟩

可以通过 set_qs() 函数直接对模拟器中的量子态进行赋值。比如,在下面代码中,我们就通过该方式将模拟器的量子态赋值为 ∣ 1 ⟩ |1\rangle ∣1⟩。

sim = Simulator('projectq', n_qubits=1)
state_temp = np.array([0., 1.])init_state = sim.get_qs(ket=True)sim.set_qs(state_temp)
final_state = sim.get_qs(ket=True)print('初始量子态为:\t', init_state)
print('最终量子态为:\t', final_state)
初始量子态为:    1¦0⟩
最终量子态为:   1¦1⟩

量子门

量子门是量子算法的基本组成单元。通过施加一系列设计好的量子门,可以改变量子态,从而完成计算任务。 MindQuantum 预置了一系列常见的量子门,比如 X, Y, Z, H, RX, RY, RZ 等,灵活使用他们,可以完成任意逻辑操作。我们以 X 门为例,介绍一下如何使用它们。

X.on(0) # on() 表示量子门作用在哪个量子比特上。
X(0)
X.matrix() # matrix() 函数可以展示量子门的矩阵形式
array([[0, 1],[1, 0]])
X.on(1,0) # CNOT 门。 on() 中的参数可以为多个,此时,第一个为受控比特,第二个为控制比特。
X(1 <-: 0)
X.on(2,[0,1]) # Toffli 门。受控比特 和 控制比特 都可以有多个,同样性质的比特,用列表封装在一起。
X(2 <-: 0 1)
RX(np.pi).on(0) # 旋转门的旋转角度可以自己定义。
RX(π|0)

除了上面旋转角度固定的量子门,还可以定义参数门:先设置参数名,其具体取值在后面根据需要进行设定。这种量子门对于量子变分算法和量子机器学习至关重要。

RX('theta').on(0) # 此处为一个参数门,其参数名为 'theta',其值可暂不赋予。
RX(theta|0)

量子线路

对量子门进行有序排列就可以得到量子线路。一段有意义的量子线路,就构成了量子算法。

用 MindQuantum 搭建量子线路是非常简单的。目前支持一下几种搭建量子线路的方式:

  1. 采用 += 形式,可读性强
circ = Circuit()
circ += X.on(0)
circ += RY('theta').on(0)
circ += Y.on(1)
print(circ)
q0: ──X────RY(theta)──q1: ──Y───────────────
  1. 列表形式,简洁
circ = Circuit([X.on(0), RY('theta').on(0)])
circ.append(Y.on(1))
print(circ)
q0: ──X────RY(theta)──q1: ──Y───────────────
  1. 函数形式,书写便捷
circ = Circuit().x(0).ry('theta',0).y(1)
print(circ)
q0: ──X────RY(theta)──q1: ──Y───────────────

量子线路的作用。

from IPython.display import display_svg
circ = Circuit()
circ += H.on(0)
circ += X.on(1,0)
print('定义的量子线路为:')
print(circ)print('初始态 |00〉态经过量子线路作用后,变为:\n', circ.get_qs(ket=True))
定义的量子线路为:
q0: ──H────●──│
q1: ───────X──
初始态 |00〉态经过量子线路作用后,变为:√2/2¦00⟩
√2/2¦11⟩

单量子比特任意逻辑操作:通过改变其参数 alpha, beta, theta, 如下 U3 线路可以实现任意单量子比特逻辑操作。

circ = U3('alpha','beta','theta', 0) # 调用 U3 函数可直接搭建 U3 线路。前三个参数为线路的参数名,最后一个参数指明作用到哪个比特。
print(circ)
q0: ──RZ(alpha)────RX(-π/2)────RZ(beta)────RX(π/2)────RZ(theta)──

双量子比特任意逻辑操作:通过改变其参数,如下双量子比特线路可以实现任意双比特逻辑操作。

circ = Circuit()
circ += U3('alpha_0_0','beta_0_0','theta_0_0', 0)
circ += U3('alpha_1_0','beta_1_0','theta_1_0', 1)
circ += X.on(1,0)
circ += RY('phi_0_0').on(0)
circ += RZ('phi_1_0').on(1)
circ += X.on(0,1)
circ += RY('phi_0_1').on(0)
circ += X.on(1,0)
circ += U3('alpha_0_1','beta_0_2','theta_0_3', 0)
circ += U3('alpha_1_1','beta_1_1','theta_1_1', 1) print(circ)
q0: ──RZ(alpha_0_0)────RX(-π/2)────RZ(beta_0_0)────RX(π/2)────RZ(theta_0_0)────●────RY(phi_0_0)────X────RY(phi_0_1)────●────RZ(alpha_0_1)────RX(-π/2)────RZ(beta_0_2)────RX(π/2)────RZ(theta_0_3)──│                   │                   │
q1: ──RZ(alpha_1_0)────RX(-π/2)────RZ(beta_1_0)────RX(π/2)────RZ(theta_1_0)────X────RZ(phi_1_0)────●───────────────────X────RZ(alpha_1_1)────RX(-π/2)────RZ(beta_1_1)────RX(π/2)────RZ(theta_1_1)──

实战作业:

基于 MindQuantum 设计量子线路,制备出如图布洛赫球面赤道上的四个量子态(红色字体),
可忽略整体相位。实现后请以 PR 形式提交到 Gitee MindQuantum 的 research 分支。在线路搭建完成后,如果输入结果 是否符合预期目标? True 则表示成功。

  1. 制备 ( ∣ 00 ⟩ + ∣ 11 ⟩ ) / ( 2 ) (|00\rangle+|11\rangle)/\sqrt(2) (∣00⟩+∣11⟩)/( ​2)
target_state = np.array([1, 1]/np.sqrt(2))
print('欲制备的量子态为:\n', target_state)
target_state = np.mat(target_state.reshape((2,1)))# 请搭建量子线路
circ = Circuit()
circ += H.on(0)state = circ.get_qs()
print('\n您制备的量子态为:\n', state)state = np.mat(state).reshape((2,1))
print('\n是否符合预期目标?', np.allclose((np.abs(target_state.H@state)**2)[0,0].real, 1))
欲制备的量子态为:[0.70710678 0.70710678]您制备的量子态为:[0.70710678+0.j 0.70710678+0.j]是否符合预期目标? True
  1. 制备 ( ∣ 00 ⟩ + i ∣ 11 ⟩ ) / ( 2 ) (|00\rangle+i|11\rangle)/\sqrt(2) (∣00⟩+i∣11⟩)/( ​2)
target_state = np.array([1, 1j]/np.sqrt(2))
print('欲制备的量子态为:\n', target_state)
target_state = np.mat(target_state.reshape((2,1)))# 请搭建量子线路
circ = Circuit()
circ += H.on(0)
circ += RZ(np.pi/2).on(0)state = circ.get_qs()
print('\n您制备的量子态为:\n', state)state = np.mat(state).reshape((2,1))
print('\n是否符合预期目标?', np.allclose((np.abs(target_state.H@state)**2)[0,0].real, 1))
欲制备的量子态为:[0.70710678+0.j         0.        +0.70710678j]您制备的量子态为:[4.32963729e-17-0.70710678j 4.32963729e-17+0.70710678j]是否符合预期目标? False
  1. 制备 ( ∣ 00 ⟩ − ∣ 11 ⟩ ) / ( 2 ) (|00\rangle-|11\rangle)/\sqrt(2) (∣00⟩−∣11⟩)/( ​2)
target_state = np.array([1, -1]/np.sqrt(2))
print('欲制备的量子态为:\n', target_state)
target_state = np.mat(target_state.reshape((2,1)))# 请搭建量子线路
circ = Circuit()
circ += H.on(0)
circ += RZ(np.pi).on(0)state = circ.get_qs()
print('\n您制备的量子态为:\n', state)state = np.mat(state).reshape((2,1))
print('\n是否符合预期目标?', np.allclose((np.abs(target_state.H@state)**2)[0,0].real, 1))
欲制备的量子态为:[ 0.70710678 -0.70710678]您制备的量子态为:[0.70710678+0.j 0.70710678+0.j]是否符合预期目标? False
  1. 制备 ( ∣ 00 ⟩ − i ∣ 11 ⟩ ) / ( 2 ) (|00\rangle-i|11\rangle)/\sqrt(2) (∣00⟩−i∣11⟩)/( ​2)
target_state = np.array([1, -1j]/np.sqrt(2))
print('欲制备的量子态为:\n', target_state)
target_state = np.mat(target_state.reshape((2,1)))# 请搭建量子线路
circ = Circuit()
circ += H.on(0)state = circ.get_qs()
print('\n您制备的量子态为:\n', state)state = np.mat(state).reshape((2,1))
print('\n是否符合预期目标?', np.allclose((np.abs(target_state.H@state)**2)[0,0].real, 1))
欲制备的量子态为:[ 0.70710678+0.j         -0.        -0.70710678j]您制备的量子态为:[0.70710678+0.j 0.70710678+0.j]是否符合预期目标? False

量子测量 和 期望值:

量子计算的结果要通过对量子比特进行特定的测量来提取。得到不同测量结果的概率由量子比特的量子态决定。

实际任务中,往往需要根据测量结果,对量子线路的参数甚至结构进行调节。

在 MindQuantum 中,只要在量子线路添加测量门就可以实现测量操作:measure(key, obj_qubit=None),其参数 obj_qubit 表示作用到哪个量子比特上, key 表示测量门的名字,如果 obj_qubit=None,则 key 应为整数,来表示作用到哪个量子比特上。

circ = Circuit()
circ += X.on(0)print('测量前的量子态为:', circ.get_qs(ket=True))
circ.measure(0)print('\n搭建的量子线路为:')
print(circ)print('\n测量后的量子态为:', circ.get_qs(ket=True) )
测量前的量子态为: 1¦1⟩搭建的量子线路为:
q0: ──X────M(q0)──测量后的量子态为: 1¦1⟩

可见,初态 ∣ 0 ⟩ |0\rangle ∣0⟩ 经 X 门作用后变为 ∣ 1 ⟩ |1\rangle ∣1⟩。测量后,量子态坍缩到了基矢态 ∣ 1 ⟩ |1\rangle ∣1⟩ 上。
请尝试多次运行这段代码,会发现测量结果一直不变。这是因为,根据量子力学,对于一个量子态 ∣ ψ ⟩ = α ∣ 0 ⟩ + β ∣ 1 ⟩ |\psi\rangle = \alpha|0\rangle+\beta|1\rangle ∣ψ⟩=α∣0⟩+β∣1⟩,
测量得到基矢态 ∣ 0 ⟩ |0\rangle ∣0⟩ 和 ∣ 1 ⟩ |1\rangle ∣1⟩ 的概率分别为 ∣ α ∣ 2 |\alpha|^2 ∣α∣2 和 ∣ β ∣ 2 |\beta|^2 ∣β∣2。
所以,在 X 门将 ∣ 0 ⟩ |0\rangle ∣0⟩ 态转变为 ∣ 1 ⟩ |1\rangle ∣1⟩ 后, ∣ ψ ⟩ = ∣ 1 ⟩ |\psi\rangle=|1\rangle ∣ψ⟩=∣1⟩,测量得到 ∣ 1 ⟩ |1\rangle ∣1⟩ 的概率为 1。

让我们尝试一下另一个门 H 门,这个门会将 ∣ 0 ⟩ |0\rangle ∣0⟩ 态转变为 ( ∣ 0 ⟩ + ∣ 1 ⟩ ) / ( 2 ) (|0\rangle+|1\rangle)/\sqrt(2) (∣0⟩+∣1⟩)/( ​2)。多次运行下面代码,会发现测量后,会随机坍缩到基矢 ∣ 0 ⟩ |0\rangle ∣0⟩ 态和基矢 ∣ 1 ⟩ |1\rangle ∣1⟩ 态上,且频次基本相同。这是因为量子力学预测,坍缩到两个基矢态的概率都为 ∣ 1 2 ∣ 2 = 1 / 2 |\frac{1}{\sqrt{2}}|^2=1/2 ∣2 ​1​∣2=1/2。

circ = Circuit()
print('初始量子态为:\n', circ.get_qs(ket=True))circ += H.on(0)print('\n测量前的量子态为:\n', circ.get_qs(ket=True))circ.measure(0)
print('\n搭建的量子线路为:')
print(circ)print('\n测量后的量子态为:\n', circ.get_qs(ket=True))
初始量子态为:1¦0⟩测量前的量子态为:√2/2¦0⟩
√2/2¦1⟩搭建的量子线路为:
q0: ──H────M(q0)──测量后的量子态为:1¦1⟩

下面我们尝试做一个更有意思的实验:量子线路由一个 RX( θ \theta θ) 门构成,其参数 θ \theta θ 从 0 变化到 2 π 2\pi 2π, 观察其测量结果为 ∣ 0 ⟩ |0\rangle ∣0⟩ 和 ∣ 1 ⟩ |1\rangle ∣1⟩ 的概率变化。

import matplotlib.pyplot as pltlength = 1000
prob_0 = []
prob_1 = []for i in range(length+1):theta = i*2*np.pi/lengthcirc = Circuit(RX(theta).on(0))state_array = circ.get_qs()prob_0.append(np.abs(state_array[0])**2)prob_1.append(np.abs(state_array[1])**2)## 可视化
plt.figure()
plt.plot(prob_0, label = r'$|0\rangle$', linestyle='--', marker='o', color='b')
plt.plot(prob_1, label = r'$|1\rangle$', linestyle='--', marker='o', color='r')
plt.legend()
plt.xlabel(r'$\theta$', fontsize=10)
plt.xticks(ticks=[0, 251, 501, 751, 1001], labels=['0', r'$\pi/2$', r'$pi$', r'$3\pi/2$', r'$2\pi$'])
plt.ylabel('Probability', fontsize=10)
plt.show()


对于单个量子比特, ∣ 0 ⟩ |0\rangle ∣0⟩ 态,对应的能量为 1,而 ∣ 1 ⟩ |1\rangle ∣1⟩ 态对应的能量为 -1。对某一量子态 ∣ ψ ⟩ = α ∣ 0 ⟩ + β ∣ 1 ⟩ , |\psi\rangle=\alpha|0\rangle+\beta|1\rangle, ∣ψ⟩=α∣0⟩+β∣1⟩, 其能量期望值为 ∣ α ∣ 2 ∗ ( 1 ) + ∣ β ∣ 2 ∗ ( − 1 ) = ∣ α ∣ 2 − ∣ β ∣ 2 . |\alpha|^2*(1)+|\beta|^2*(-1)=|\alpha|^2-|\beta|^2. ∣α∣2∗(1)+∣β∣2∗(−1)=∣α∣2−∣β∣2.

在 MindQuantum 中,这可以直接通过调用函数 sim.get_expectation() 来直接获得。如下:

下面我们绘出随着 RX( θ \theta θ) 中参数 θ \theta θ 的变化,能量期望值的变化情况:

import matplotlib.pyplot as pltlength = 1000
delta_theta = 2*np.pi/length
expectation = []
sim = Simulator('projectq', 1)for i in range(length+1):sim.apply_circuit(Circuit(RX(delta_theta).on(0)))expectation.append(sim.get_expectation(Hamiltonian(QubitOperator('Z0'))).real)plt.figure()
plt.plot(expectation, label = 'expectation', linestyle='--', marker='o', color='b')
plt.legend()
plt.xlabel(r'$\theta$', fontsize=10)
plt.xticks(ticks=[0, 251, 501, 751, 1001], labels=['0', r'$\pi/2$', r'$pi$', r'$3\pi/2$', r'$2\pi$'])
plt.ylabel('Expectation ', fontsize=10)
plt.show()

量子变分线路 和 量子神经网络

量子变分线路是指构建的量子线路含有参数量子门,其参数取值可在建立线路后,根据需要进行调节。这和经典的神经网络很像:先定义一个变量名,之后再根据反向传播算法对其参数进行更新。所以,这种量子变分线路,有时也被形象地称为 “量子神经网络”。

下面的代码就定义了一段量子变分线路 —— 在搭建好线路后,再对线路的参数进行赋值,从而得到演化后的量子态。这部分量子线路也经常被称为拟设线路 ansatz

ansatz = Circuit()
ansatz += RX('alpha').on(0)
ansatz += RY('beta').on(0)
ansatz += X.on(0)
ansatz += RX('theta').on(0)alpha = 0.1
beta = 0.2
theta = 0.3print('对变分线路参数进行赋值后,其前向传播所得的量子态为:')
print(ansatz.get_qs(pr={'alpha':alpha, 'beta':beta, 'theta':theta}, ket=True))
对变分线路参数进行赋值后,其前向传播所得的量子态为:
(0.09933466539753061-0.19767681165408385j)¦0⟩
(0.9751703272018158-0.009966711079379187j)¦1⟩

ansatz 不仅可以进行前向传递,还可以进行反向传播:通过如 “中心差分” 等求导方法,求得各参数相对于目标函数(一般而言是某一力学量在量子态下的期望值)的梯度,然后用诸如 Adam 等优化器就可以对参数进行更新。

在 MindQuantum 中,对期望值和导数的计算由模拟器完成,我们可通过函数 sim.get_expectation_with_grad() 进行获得。而由于量子计算框架 MindQuantum 和 经典机器学习框架 MindSpore 深度融合,参数更新部分则可以直接采用 MindSpore 来完成。

下面我们以一个简单的例子来演示, MindQuantum 如何对 ansatz 进行训练完成算法任务。

此处,ansatz 仅包含一个 RX 门。任务目标是找到一个合适的旋转角度 θ \theta θ 来使得损失值(此处设为能量的期望值)最小。通过简单的推测,我们就知道该最小损失值为 -1(对应的量子态为 ∣ 1 ⟩ |1\rangle ∣1⟩),而对应的旋转角度应该是 ± k π \pm k\pi ±kπ,其中 k k k 为奇数。

import mindspore as ms  # 导入 MindSpore,用来完成参数更新。
ms.set_context(mode=ms.PYNATIVE_MODE, device_target="CPU") # 模式需设为 PYNATIVE_MODEansatz = Circuit()
ansatz += RX('theta').on(0)
ansatz.as_ansatz()  # 通过调用 as_ansatz() 函数来声明这部分量子线路的参数为可训练参数ham = Hamiltonian(QubitOperator('Z0')) # 目标函数,'Z0' 表示作用在第 0 个比特上的泡利 Z 矩阵。封装成为算符和哈密顿量。sim = Simulator('projectq', ansatz.n_qubits) # circ.n_qubits 可获取量子线路所用的比特数grad_ops = sim.get_expectation_with_grad(ham, ansatz) # 获取目标函数值 及 各参数相对目标函数的梯度qnet = MQAnsatzOnlyLayer(grad_ops) # 以层的形式对量子网络进行封装,且该层所有参数都为可训练参数。
opti = ms.nn.Adam(qnet.trainable_params(), learning_rate=0.1)     # 需要优化的是量子网络中可训练的参数,学习率设为 0.1
train = ms.nn.TrainOneStepCell(qnet, opti) # 每调用一次,就可以对网络的可训练参数进行一次更新print('期望值随训练次数的变化:')
for i in range(1, 101): # 训练 100 次res = train()if i % 10 == 0: # 每 10 次训练,显示一次目标函数值print(f'step is {i} \t  期望值:{res[0]}')print('\n训练后得到的最终旋转角度为:', qnet.weight.asnumpy()[0])
期望值随训练次数的变化:
step is 10    期望值:0.6510794
step is 20    期望值:-0.35128716
step is 30    期望值:-0.9855994
step is 40    期望值:-0.9401322
step is 50    期望值:-0.977039
step is 60    期望值:-0.998729
step is 70    期望值:-0.9956714
step is 80    期望值:-0.99999905
step is 90    期望值:-0.9994411
step is 100       期望值:-0.9999941训练后得到的最终旋转角度为: 3.141906

可见,经过 100 次训练,得到的旋转角度已经符合预期 θ = ± k π \theta=\pm k\pi θ=±kπ。

上面的例子中,量子线路只含有一个可变参数量子门的 ansatz。但有时,我们需要一个量子网络来对未知数据进行处理,也就是需要支持数据输入,这个不被训练的参数线路称为编码器 encoderencoder 将经典信息编码为量子态,从而作为量子神经网络的输入,功能类似于经典神经网络中的输入层。

在 MindQuantum 中,通过接口 as_encoder 来对某一段参数线路进行编码器声明。

下面我们以一个简单的例子来进行示范:encoderansatz 都为一段只包含一个 RX 门的量子线路。后面我们将 encoder 的参数设置为 π / 2 \pi/2 π/2, 之后通过对 ansatz 的训练来寻找一个合适的旋转角度,使得能量期望值最小。从前面的讨论易知,该旋转角度为 π / 2 ± k π \pi/2\pm k\pi π/2±kπ,其中 k k k 为奇数。

import mindspore as ms
ms.set_context(mode=ms.PYNATIVE_MODE, device_target="CPU")encoder = Circuit()
encoder += RX('alpha').on(0)
encoder.as_encoder() # 通过调用 as_encoder 函数来声明某一段线路为编码器 encoder,这部分线路的参数为不可训练参数ansatz = Circuit()
ansatz += RX('beta').on(0)
ansatz.as_ansatz() # 通过调用 as_ansatz() 函数来声明这部分量子线路的参数为可训练参数circ = encoder + ansatz
ham = Hamiltonian(QubitOperator('Z0'))
sim = Simulator('projectq', circ.n_qubits)grad_ops = sim.get_expectation_with_grad(ham, circ)
qnet = MQLayer(grad_ops) # 以层的形式对量子网络进行封装,该层既包含可训练参数,又包含不可训练参数opti = ms.nn.Adam(qnet.trainable_params(), learning_rate=0.1)     # 需要优化的是量子网络中可训练的参数,学习率设为 0.1
train = ms.nn.TrainOneStepCell(qnet, opti)print('期望值随训练次数的变化:')
for i in range(1, 101): # 训练 100 次res = train(ms.Tensor([[np.pi/2]]))[0]if i % 10 == 0: # 每 10 次训练,显示一次目标函数值print(f'step is {i} \t  期望值:{res[0]}')print('\n训练后得到的最终旋转角度为:', qnet.weight.asnumpy()[0])
期望值随训练次数的变化:
step is 10    期望值:-0.76672345
step is 20    期望值:-0.9951272
step is 30    期望值:-0.95892125
step is 40    期望值:-0.99864537
step is 50    期望值:-0.99544454
step is 60    期望值:-0.99954396
step is 70    期望值:-0.9994277
step is 80    期望值:-0.99995875
step is 90    期望值:-0.9999125
step is 100       期望值:-0.99999976训练后得到的最终旋转角度为: 1.5715348

可见,经过 100 次训练,得到的旋转角度已经符合预期 θ = π / 2 ± k π \theta=\pi/2\pm k\pi θ=π/2±kπ.

量子卷积神经网络:

量子卷积神经网络通过对量子比特施加卷积操作,将量子比特纠缠起来,提取信息。相比经典卷积神经网络,量子卷积神经网络可提取全局信息,而不仅仅是局部信息。下图为我们采用的量子卷积神经网络的整体结构图 [1]。

其中,编码器 encoder 部分采用了密集比特编码。通过施加 RXRY 门,每个量子比特可以编码 2 个像素信息。而卷积核则采用了可以实现任意双量子比特操的量子线路。通过多个卷积层操作,有效将所有比特纠缠起来。量子卷积网络可以有效提取图像特征,并通过丢比特的形式进行池化操作和引入非线性。最后通过比较第 3 和 第 7 个比特能量期望值大小,来作为分类依据。比如,若第 3 个比特期望值大于第 7 个比特的期望值,则规定该预测分类为 0,否则为 1。分类结果进过 Softmax 运算,与标签做交叉熵,作为损失函数。各参数相对损失函数的梯度由量子模拟器计算,并采用经典优化器 Adam 进行更新。

下面是详细代码。

导入所需库

from mindquantum import *
import numpy as np
import mindspore as ms
from mindspore import nn, Tensor
from mindspore.nn import Adam, TrainOneStepCell, LossBase
import matplotlib.pyplot as plt
ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU")
ms.set_seed(2)
np.random.seed(2)
import random
import copy

导入并处理图像数据。原始图像数据为 MNIST 手写字体,图像大小为 28 ∗ 28 28*28 28∗28,为便于模拟,我们将其压缩为 4 ∗ 4 4*4 4∗4 的大小,并进行二值化和去冲突处理。所得保存在同目录下 train.npyeval.npy 中,样本量分别为 4000 和 846。我们对其形式进行修整,从而可以作为量子编码器的输入。并从 eval.npy 中取 100 个样本作为本任务的验证集。

注: 由于 CSDN 页面无法上传数据集,读者如有需要,可以从我的 Gitee 仓库下载。仓库链接:https://gitee.com/herunhong/qcnn-as-image-classifier/tree/master

# 导入图像数据作为 训练集 和 验证集。x 是图像数据 y 为标签。
train_data = np.load('./train.npy', allow_pickle=True)
train_x_set = train_data[0]['train_x']
train_y_set = train_data[0]['train_y']eval_data = np.load('./eval.npy', allow_pickle=True)
eval_x_set = eval_data[0]['eval_x'][0:100]
eval_y_set = eval_data[0]['eval_y'][0:100]train_x_set = train_x_set.reshape((train_x_set.shape[0], -1))
eval_x_set = eval_x_set.reshape((eval_x_set.shape[0], -1))# 打印数据信息
print(train_x_set.shape)
print(train_y_set.shape)print(eval_x_set.shape)
print(eval_y_set.shape)
(4000, 16)
(4000,)
(100, 16)
(100,)

搭建量子编码器。采用密集比特编码方式,对于 4 ∗ 4 4*4 4∗4 的图像数据,需要 8 个量子比特。

encoder = Circuit()
for i in range(8):encoder += RX(f'rx{i}').on(i)encoder += RY(f'ry{i}').on(i)
encoder.as_encoder()
print(encoder)
q0: ──RX(rx0)────RY(ry0)──q1: ──RX(rx1)────RY(ry1)──q2: ──RX(rx2)────RY(ry2)──q3: ──RX(rx3)────RY(ry3)──q4: ──RX(rx4)────RY(ry4)──q5: ──RX(rx5)────RY(ry5)──q6: ──RX(rx6)────RY(ry6)──q7: ──RX(rx7)────RY(ry7)──

定义量子卷积核。量子卷积核可以完成任意双量子比特逻辑操作。注意,为避免重复操作,我们省去了每个卷积核最后的两个 U3 门。

def conv(bit_up=0, bit_down=1, prefix='0'):_circ = Circuit()_circ += U3('theta00','phi00','lam00',bit_up)_circ += U3('theta01','phi01','lam01',bit_down)_circ += X.on(bit_down,bit_up)_circ += RY('theta10').on(bit_up)_circ += RZ('theta11').on(bit_down)_circ += X.on(bit_up,bit_down)_circ += RY('theta20').on(bit_up)_circ += X.on(bit_down,bit_up)_circ = add_prefix(_circ, 'prefix')return _circ

搭建量子卷积神经网络。最终剩余的两个比特上施加 U3 门,来对最终效果进行微调。

ansatz = Circuit()
ansatz += conv(0,1,'00')
ansatz += conv(2,3,'01')
ansatz += conv(4,5,'02')
ansatz += conv(6,7,'03')ansatz += conv(7,0,'10')
ansatz += conv(1,2,'11')
ansatz += conv(3,4,'12')
ansatz += conv(5,6,'13')ansatz += conv(1,3,'20')
ansatz += conv(5,7,'21')ansatz += conv(7,1,'30')
ansatz += conv(3,5,'31')ansatz += conv(3,7,'40')ansatz += U3('theta400','phi401','lam402', 3)
ansatz += U3('theta410','phi411','lam412', 7)
ansatz.as_ansatz()circ = encoder + ansatz

定义损失函数。损失函数为带 Softmax 的交叉熵。这一部分完全由经典 MindSpore 构建。

class MyLoss(LossBase):def __init__(self, reduction='mean'):super(MyLoss, self).__init__(reduction)self.cross_entropy = nn.SoftmaxCrossEntropyWithLogits(sparse=True)def construct(self, logits, label):out = self.cross_entropy(logits, label)return self.get_loss(out)class MyWithLossCell(nn.Cell):def __init__(self, backbone, loss_fn):super(MyWithLossCell, self).__init__(auto_prefix=False)self._backbone = backboneself._loss_fn = loss_fndef construct(self, x, label):out = self._backbone(x)return self._loss_fn(out, label)@propertydef backbone_network(self):return self._backbone

装配模型。将量子神经网络、损失函数和优化器等部分封装起来。

sim = Simulator('projectq', circ.n_qubits)
ham = [Hamiltonian(QubitOperator('Z3')), Hamiltonian(QubitOperator('Z7'))] # 最终通过比较第 3 和第 7 个量子比特能量期望值来作为分类结果。
grad_ops = sim.get_expectation_with_grad(ham, circ)
qnet = MQLayer(grad_ops)loss = MyLoss()
net_with_criterion = MyWithLossCell(qnet, loss)
opti = Adam(qnet.trainable_params(), learning_rate=0.1) # 采用 Adam 优化器,学习率设置为 0.1。
net = TrainOneStepCell(net_with_criterion, opti) # net 每执行一次,就会对网络进行一次训练。

训练并显示效果。注意,由于讲座时间有限,为防止程序运行不完,下面程序仅进行了验证性训练,同学门后续可以对程序进行调参,甚至对结构进行更改,以取得更好的训练效果。经本人验证,对这个拥有 846 个样本点的验证集,最终准确率可以超过 0.993。

batch_size = 16 # 批大小
eval_acc_list = [] # 记录训练过程中,验证集预测准确率for i in range(71):index = np.random.randint(0, len(train_x_set), size=batch_size) # 每次从训练集中随机抽选 batch_size 的样本。x = train_x_set[index]y = train_y_set[index]net(Tensor(x), Tensor(y, ms.int32)) # 训练一次if i % 10 == 0:res_list = []for eval_x, eval_y in zip(eval_x_set, eval_y_set):sim.reset()params = np.append(eval_x, qnet.weight.asnumpy())sim.apply_circuit(circ, params)expectation = [sim.get_expectation(ham[0]).real, sim.get_expectation(ham[1]).real]out = 0 if expectation[0] >= expectation[1] else 1res = 1 if eval_y == out else 0res_list.append(res)acc = np.mean(res_list)eval_acc_list.append(acc)print(f'当前进度为 {i}', f'验证集准确率为:{acc}')plt.figure()
plt.plot(eval_acc_list)
plt.title('accuracy of validation set', fontsize=20)
plt.xlabel('Steps', fontsize=20)
plt.ylabel('Accuracy', fontsize=20)
plt.show()
当前进度为 0 验证集准确率为:0.41
当前进度为 10 验证集准确率为:0.7
当前进度为 20 验证集准确率为:0.55
当前进度为 30 验证集准确率为:0.55
当前进度为 40 验证集准确率为:0.45
当前进度为 50 验证集准确率为:0.59
当前进度为 60 验证集准确率为:0.46
当前进度为 70 验证集准确率为:0.82

参考文献

[1] Tak Hur, Leeseok Kim and Daniel K. Park. Quantum convolutional neural network for classical data classification.

MindSpore Quantum 量子计算编程与实践:轻松上手量子卷积神经网络相关推荐

  1. 量子计算黑客松大赛-量子计算编程

    序 因为比赛需要相关量子知识,然后自己又是物理菜鸡,下面是记录一下自己的学习历程 正文 大赛信息<量子计算黑客松大赛-量子计算编程> [赛事介绍] 量子计算被认为是未来具有颠覆性影响的新型 ...

  2. 腾讯云与明建云战略签约合作;中科院软件所发布量子计算编程软件isQ;亚马逊AWS与Jamf开展合作 | 每日大事件...

    数据智能产业创新服务媒体 --聚焦数智 · 改变商业 01.腾讯云与明建云战略签约 9月25日消息 ,在湖南省住房和城乡建设厅的指导下,由腾讯云计算(北京)有限责任公司.湖南明建云信息科技有限公司联合 ...

  3. 量子计算(十二):量子线路与测量操作

    文章目录 量子线路与测量操作​​​​​​​ 量子线路与测量操作​​​​​​​ 量子线路是由代表量子比特演化的路线和作用在量子比特上的量子逻辑门组成的.量子线路产生的效果,等同于每一个量子逻辑门依次作用 ...

  4. 基于Qiskit——《量子计算编程实战》读书笔记(五)

    目录 前言 第7章 OpenQASM 关于 OpenQASM 将OpenQASM程序转换为量子电路 例1:取反量子比特 例2:用门操作两个量子比特并测量第一个量子比特 例3:创建一个有两个参数和两个参 ...

  5. “新产业50人论坛”之陈柳平:量子通信与量子计算的商业实践

    10月23日,由中关村发展集团主办,中关村产业研究院承办的"新产业50人论坛"上,启科量子联合创始人/CTO陈柳平发表了<量子通信与量子计算的商业化.产业化实践>主题演 ...

  6. 基于Qiskit——《量子计算编程实战》读书笔记(二)

    目录 第3章 量子态.量子寄存器和测量 量子态和寄存器 何为直积? 思考:尝试计算​ 可分离状态.量子纠缠与测量 退相干,T1和T2 关于T1和T2 练习和问题 欢迎加入Qiskit交流群:10643 ...

  7. 基于Qiskit——《量子计算编程实战》读书笔记(三)

    目录 第4章 使用量子门演化量子态 门 IBM QX通用门集 哈达玛门 泡利门(X, Y, Z) 相门(S)和π/8门(T) 多量子比特门 练习和问题 欢迎加入Qiskit交流群: 106437133 ...

  8. python 量子计算包_ProjectQ:解锁Python实现量子计算的新方式!

    原标题:ProjectQ:解锁Python实现量子计算的新方式! [ ] [IT168 资讯]现在已经有很多软件框架可用于访问量子计算机,本文将要提到的ProjectQ就是其中之一.ProjectQ是 ...

  9. 量子计算(二十):量子算法简介

    文章目录 量子算法简介 一.概述 二.量子经典混合算法 量子算法简介 一.概述 量子算法是在现实的量子计算模型上运行的算法,最常用的模型是计算的量子电路模型.经典(或非量子)算法是一种有限的指令序列, ...

最新文章

  1. Eclipse在高分屏下图标过小的解决方法
  2. lol游戏挂机软件_LOL出手整治,大量玩家被封10年,代练屡禁不止的原因找到了...
  3. CentOS 初体验六:登录工具PuTTY使用
  4. 垃圾优先型垃圾回收器调优
  5. 炫界 (667) -(回应骑两小)_为什么那么多人喜欢骑地平线
  6. python之禅星号_Python基础1
  7. MFC之实现鼠标自动左击,频率可调,支持热键
  8. 朴素贝叶斯算法实现分类以及Matlab实现
  9. mysql utf-8_完美解决mysql下utf-8的乱码问题
  10. (8)VTK 鼠标左右键控制模型旋转
  11. 使用android SpannableStringBuilder实现图文混排,看到许多其他
  12. cookie和session的区别及其原理
  13. 没有基础一样可以一次性拿下CCSK认证?快来看这里!
  14. 我们有个共同的名字,XX工
  15. 留言板显示服务器错误,动易Cms:解读SiteFactory 留言板出现:服务器无响应,错误代码:500-动易Cms教程...
  16. 免费得到高程地图的方法
  17. Vivado HLS介绍及IP核的设计流程
  18. div绑定onblur事件
  19. 第四章 账号权限管理
  20. Away3D学习笔记1 - 戏说Flash 三维引擎

热门文章

  1. 2022年计算机行业报告:虚拟电厂,山雨欲来风满楼
  2. Linux下标准I/O库函数fopen、fclose函数的使用方法
  3. CV_Task3 利用ResNet实现人脸识别
  4. 亚马逊国际获得AMAZON商品详情 API
  5. STM32移植BME680传感器输出IAQ(室内空气质量)
  6. 学习July博文总结——支持向量机(SVM)的深入理解(上)
  7. 一个Linux下的网络模拟工具 Core
  8. simulink仿真及代码生成技术入门到精通_行星排混动从入门到精通(结构)
  9. vue+vuetify建站
  10. 煤炭及煤化工加工会产生什么危废_煤化工危废处理新趋势:园区建设集中处理装置...