感知机只是一个神经元,若有多个神经元共同作用,则构成神经网络。目前,最常见的量子神经网络模型为基于参数化量子线路的量子神经网络,该模型用参数化量子线路代替神经网络结构,使用经典优化算法更新参数化量子线路的参数。
经典神经网络
用一个空心圆圈表示一个神经元,每层神经元通过权重系数与下一层神经元进行全互连,神经元之间不存在同层连接,也不存在跨层连接,这种神经网络模型称为多层前馈神经网络。输入层神经元仅用于接受外界的信号,组成隐含层与输出层的神经元称为功能神经元,也就是可以接受信号并对其处理的神经元,每个功能神经元都有自己的阈值。神经网络的学习过程就是根据训练数据来调整神经元之间的权重系数以及每个功能神经元的网络。
参数化量子线路
参数化量子线路的一般形式,参数化量子线路通常由单量子比特旋转门和双量子比特纠缠门(受控非门、受控旋转门)按照一定的规律排列而成。其中参数化是指单量子比特旋转门或双量子比特纠缠门是含有可训练的参数的。例如、和含有参数。
旋转层由旋转门U0(θ0), U1(θ1), …, Um−1(θm−1)组成,纠缠层(记为Um)由CNOT门组成
纠缠层的连接方法
参数化量子线路不单独运行,需要与经典的方法相结合。
参数化量子线路所表示的函数
参数化量子线路所表示的函数与测量算子有关,此处用Z算子。对只包含一个旋转层和一个纠缠层的单层参数化量子线路,记
经过酉变换U(θ)的量子态为
用测量算子Z测量 ,测量的平均值为,由于
因此,可以通过测量得到0的概率算出
目标函数与优化
测量得到0的概率为
定义样本预测标签的计算公式为
记样本x的真实标签为y(x),量子神经网络的目标是找到合适的参数θ和偏置b使得预测标签和真实标签尽量一致
因此优化目标为最小化损失函数
带动小批量的随机梯度下降法
#量子神经网络
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile
from qiskit_aer import Aer
import numpy as np
from qiskit.visualization import plot_histogram
from qiskit.circuit.library import UnitaryGate
from math import pi
import numpy as np
#将一个数据的四维归一化特征转化为三个特征角度
def convertDataToAngles(data):
prob1 = data[2] ** 2 + data[3] ** 2
prob0 = 1 - prob1
angle1 = 2 * np.arcsin(np.sqrt(prob1))
prob1 = data[3] ** 2 / prob1
angle2 = 2 * np.arcsin(np.sqrt(prob1))
prob1 = data[1] ** 2 / prob0
angle3 = 2 * np.arcsin(np.sqrt(prob1))
return np.array([angle1, angle2, angle3])
#使用三个特征角度在两个量子比特上编码
def encodeData(qc, qreg, angles):
qc.ry(angles[0], qreg[1])
qc.cry(angles[1], qreg[1], qreg[0])
qc.x(qreg[1])
qc.cry(angles[2], qreg[1], qreg[0])
qc.x(qreg[1])
#定义G门
def RyGate(qc, qreg, params):
"""
[cos(α), sin(α)]
[-sin(α), cos(α)]
"""
u00 = np.cos(params[0]/2)
u01 = np.sin(params[0]/2)
gateLabel = "Ry({})".format(
params[0]
)
RyGate = UnitaryGate(np.array(
[[u00, -u01], [u01, u00]]
), label=gateLabel)
return RyGate
#由G门组成的训练层
def GLayer(qc, qreg, params):
for i in range(2):
qc.append(RyGate(qc, qreg, params[i]), [qreg[i]])
#由CG门组成的训练层
def CGLayer(qc, qreg, anc, params):
for i in range(2):
qc.append(RyGate(
qc, qreg, params[i]
).control(1), [anc[0], qreg[i]])
#由CX门组成的训练层
def CXLayer(qc, qreg, order):
if order:
qc.cx(qreg[0], qreg[1])
else:
qc.cx(qreg[1], qreg[0])
#由CCX门组成的训练层
def CCXLayer(qc, qreg, anc, order):
if order:
qc.ccx(anc[0], qreg[0], qreg[1])
else:
qc.ccx(anc[0], qreg[1], qreg[0])
#由G层和CX层组成的量子神经网络
def generateU(qc, qreg, params):
for i in range(params.shape[0]):
GLayer(qc, qreg, params[i])
CXLayer(qc, qreg, i % 2)
#量子神经网络训练线路
def generateCU(qc, qreg, anc, params):
for i in range(params.shape[0]):
CGLayer(qc, qreg, anc, params[i])
CCXLayer(qc, qreg, anc, i % 2)
#测量预测结果的概率
def getPrediction(qc, qreg, creg, backend):
qc.measure(qreg[0], creg[0])
transpiled_qc = transpile(qc, backend)
# Run the transpiled circuit
job = backend.run(transpiled_qc, shots=1000)
results = job.result().get_counts(qc)
# Get and plot the results
if '1' in results.keys():
return results['1'] / 1000
else:
return 0
#根据预测结果的概率对其进行分类
def convertToClass(predictions):
return (predictions >= 0.5) * 1
#计算损失函数
def cost(labels, predictions):
loss = 0
for label, pred in zip(labels, predictions):
loss += (pred - label) ** 2
return loss / 2
#求准确率
def accuracy(labels, predictions):
acc = 0
for label, pred in zip(labels, predictions):
if label == pred:
acc += 1
return acc / labels.shape[0]
#计算神经网络向前传播的结果
def forwardPass(params, bias, angles, backend):
qreg = QuantumRegister(2)
anc = QuantumRegister(1)
creg = ClassicalRegister(1)
qc = QuantumCircuit(qreg, anc, creg)
encodeData(qc, qreg, angles)
generateU(qc, qreg, params)
qc.z(qreg[0])
pred = getPrediction(qc, qreg, creg, backend) + bias
return pred
#求解梯度的量子线路
def computeRealExpectation(params1, params2, angles, backend):
qreg = QuantumRegister(2)
anc = QuantumRegister(1)
creg = ClassicalRegister(1)
qc = QuantumCircuit(qreg, anc, creg)
encodeData(qc, qreg, angles)
qc.h(anc[0])
generateCU(qc, qreg, anc, params1)
qc.cz(anc[0], qreg[0])
qc.x(anc[0])
generateCU(qc, qreg, anc, params2)
qc.x(anc[0])
qc.h(anc[0])
prob = getPrediction(qc, anc, creg, backend)
return 2 * (prob - 0.5)
#求解量子线路参数的梯度
def computeGradient(params, angles, label, bias, backend):
prob = forwardPass(params, bias, angles, backend)
gradients = np.zeros_like(params)
for i in range(params.shape[0]):
for j in range(params.shape[1]):
newParams = np.copy(params)
newParams[i, j, 0] += np.pi / 2
gradients[i, j, 0] = computeRealExpectation(
params, newParams, angles, backend
)
newParams[i, j, 0] -= np.pi / 2
biasGrad = (prob + bias - label)
return gradients * biasGrad, biasGrad
#更新量子线路的参数
def updateParams(params, prevParams, grads, learningRate, momentum,v1):
v1=momentum*v1-learningRate*grads
paramsNew = np.copy(params)
paramsNew = params +v1
return paramsNew, params,v1
#训练网络的过程
def trainNetwork(data, labels, backend):
np.random.seed(1)
numSamples = labels.shape[0]
#取前75个作训练数据集,后25个作验证数据集
numTrain = int(numSamples * 0.75)
#将100个原始数据打乱
ordering = np.random.permutation(range(numSamples))
#训练数据集
trainingData = data[ordering[:numTrain]]
trainingData = trainingData.astype(np.float32)
#验证数据集
validationData = data[ordering[numTrain:]]
validationData = validationData.astype(np.float64)
#训练集数据标签
trainingLabels = labels[ordering[:numTrain]]
#验证集数据标签
validationLabels = labels[ordering[numTrain:]]
#确定神经网络参数个数,从而确定神经网络层数,这里为5层
params = 2*np.random.sample((5, 2, 1)) #两个权重一个偏置
#偏移量
bias = 0.01
prevParams = np.copy(params)
prevBias = bias
#每一次训练的数据量
batchSize = 5
#定义动量
momentum = 0.9
v1=0
v2=0
#学习率
learningRate = 0.02
#进行15次迭代
for iteration in range(20):
# 计算批次的起始位置
samplePos = iteration * batchSize
# 检查是否还有足够的样本来形成一个完整的批次
if samplePos + batchSize <= numTrain:
# 每一次迭代训练数据集(5个)
batchTrainingData = trainingData[samplePos:samplePos + batchSize]
# 每一次迭代训练数据集标签(5个)
batchLabels = trainingLabels[samplePos:samplePos + batchSize]
# ... 其余代码保持不变
#记录此批次的梯度集
batchGrads = np.zeros_like(params)
#记录此批次偏移量
batchBiasGrad = 0
else:
# 如果没有足够的样本,就退出循环
break
for i in range(batchSize):
#求解梯度与偏移量
grads, biasGrad = computeGradient(
params, batchTrainingData[i], batchLabels[i], bias, backend
)
#求五次的平均梯度与平均偏移量
batchGrads += grads / batchSize
batchBiasGrad += biasGrad / batchSize
#更新网络参数
params, prevParams,v1= updateParams(
params, prevParams, batchGrads, learningRate, momentum,v1
)
#更新偏移量
temp = bias
v2=v2*momentum-learningRate * batchBiasGrad
bias += v2
prevBias = temp
trainingPreds = np.array([forwardPass(
params, bias, angles, backend
) for angles in trainingData])
#打印此批次训练的损失值
print('Iteration {} | Loss: {}'.format(
iteration + 1, cost(trainingLabels, trainingPreds)
))
#使用验证数据集进行验证,保留量子测量结果
validationProbs = np.array(
[forwardPass(
params, bias, angles, backend
) for angles in validationData]
)
#根据测量结果进行分类
validationClasses = convertToClass(validationProbs)
#求解平均准确率
validationAcc = accuracy(validationLabels, validationClasses)
prevalidationData=X[ordering[:numTrain]]
intValidationLabels=[0 for i in range(len(validationLabels))]
for i in range(len(validationLabels)):
intValidationLabels[i]=int(validationLabels[i])
print('Validation accuracy:', validationAcc)
for x, y, p in zip(prevalidationData, intValidationLabels, validationClasses):
print('Data:', x, ' Label:', y, ' Prediction:', p)
return params,data[0]
#从附件中取数据集
data = np.genfromtxt("data3.csv", delimiter=",")
#从数据集中取特征
X = data[:, 0:4]
#对特征进行角度转换
features = np.array([convertDataToAngles(i) for i in X])
#取标签
Y = data[:, -1]
backend = Aer.get_backend('qasm_simulator')
#开始训练
params,angles=trainNetwork(features, Y, backend)
图是参数化量子线路,对应代码的118到128行,这部分的代码由两部分组成:第一encodeData(qc.qreg.angles)将样本数据制备到量子态中,在代码的124行的定义在第23到29行。第二步用generateU,实施参数化量子线路,这里共有五层,generate的定义在80到83行。注意第二步使用的Ry不是系统自带的Ry而是在31到42行自定义的Ry。而求解梯度 就是求解和内积的实部,在代码的153到165行。