混合量子经典循环神经网络

Loading

混合量子经典循环神经网络

搭建量子RNN网络

本节内容将详细介绍混合量子经典循环神经网络(Quantum Recurrent Neural Network,简称 QRNN)的构建过程,并展示其在量子模拟器上的模型架构与实验效果[1] [2]。混合量子经典循环神经网络是传统循环神经网络(Recurrent Neural Network,简称 RNN)的量子化版本,其主要特点在于将 RNN 单元中的全连接层替换为变分量子线路(Variational Quantum Circuit,简称 VQC),同时保留了单元中原有的计算逻辑。通过本节的学习,您将深入理解混合量子经典循环神经网络的基础理论、实现方法,以及在解决实际问题时的应用案例。在探究量子RNN模型的搭建之前,首先需了解其核心组成部分——变分量子线路(Variational Quantum Circuit, VQC)。变分量子线路(VQC),也被称为参数化量子线路(Parameterized Quantum Circuit, PQC),通常包含三个关键部分:状态制备、变分量子线路和测量。下图中 U(x) 表示状态制备线路,它的作用是将经典数据 x 编码为量子态。 V(\theta) 表示具有可调节参数 \theta 的变分线路,可以通过梯度下降的方法进行优化。最后通过测量量子态,得到经典的输出值[1] [2]。

图 1 变分量子线路(VQC)的通用架构。U(x) 是将经典输入数据 x 编码为量子态的量子线路,V(θ) 是具有可调节或可学习参数 θ 的变分线路。线路最后是对部分或全部量子比特的测量

在这里,我们通过 DeepQuantum 去实现一个用于 QRNN 的 VQC 模块(图1),包括以下三个部分: 编码线路、变分线路和量子测量。

1.编码线路

编码线路将经典数据值映射为量子振幅。量子线路先进行基态初始化,然后利用 Hadamard 门来制备无偏初始态。我们使用双角编码(two-angle encoding),用两个角度编码一个值,将每个数据值分别用两个量子门( Ry 和 Rz )进行编码。两个量子门的旋转角度分别为:f(x _i) = \arctan(x _i)g(x _i) = \arctan(x _i^2) ,其中 x _i 是数据向量 x 的一个分量。编码数据的量子态为[1]:

|x\rangle=\bigotimes^N_{i=1}\cos(f(x_i)+\pi/4)|0\rangle+\exp(ig(x_i))\sin(f(x_i)+\pi/4)|1\rangle

其中 N 是向量 x 的维数,\pi /4 的角偏移是因为初始的 Hadamard 门旋转。

2.变分线路

变分量子线路由几个 CNOT 门和单量子比特旋转门组成。CNOT门以循环的方式作用于每一对位置距离1和2的量子比特,用于产生纠缠量子比特。可学习参数 \alpha\beta\gamma 控制单量子比特旋转门,它们可以基于梯度下降算法进行迭代优化更新。该变分量子线路模块可以重复多次,以增加变分参数的数量。

3.量子测量

VQC块的末端是一个量子测量层,我们通过测量来计算每个量子态的期望值。在量子模拟软件中,我们可以在经典计算机上进行数值计算,而在真实的量子计算机中,这些数值是通过重复测量进行统计估计得到的。

# 首先我们导入所有需要的包:
import torch
import torch.nn as nn
import deepquantum as dq
import matplotlib.pyplot as plt
import numpy as np
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
import pandas as pd

# 我们设计的VQC线路如下 这里我们以4个bit作为例子
cir = dq.QubitCircuit(4)
cir.hlayer()
cir.rylayer(encode=True)
cir.rzlayer(encode=True)
cir.barrier()
cir.cnot_ring()
cir.barrier()
cir.cnot_ring(step=2)
cir.barrier()
cir.u3layer()
cir.barrier()
cir.measure()
cir.draw() # 画出量子线路图
c:\Users\HP\.conda\envs\dq\lib\site-packages\qiskit\visualization\circuit\matplotlib.py:266: FutureWarning: The default matplotlib drawer scheme will be changed to "iqp" in a following release. To silence this warning, specify the current default explicitly as style="clifford", or the new default as style="iqp".
  self._style, def_font_ratio = load_style(self._style)

VQC 模块由三个主要部分构成:编码线路、变分线路以及量子测量。编码线路包含 H 门、Ry 门和 Rz 门,负责将信息编码至量子态。变分线路位于第1条和第4条虚线之间,其结构可以根据需要调整。量子比特的数量和量子测量的次数均可按实验需求进行灵活配置。此外,变分线路的层数和参数数量可以增加,以扩展模型规模,这主要取决于所使用的量子计算机或量子模拟软件的处理能力。在本节的示例中,所用的量子比特数量为4个。

量子RNN模型

在讲解量子 RNN 模型前,我们先简单回顾一下经典 RNN 模型的原理和结构(图2)。我们使用数学公式描述经典 RNN 模型的计算单元:

h_t = \tanh(x_t W_{ih}^T + b_{ih} + h_{t-1}W_{hh}^T + b_{hh})

其中 W_{ih} 是作用在 x_t 上的隐藏层权重参数, b_{ih} 是对应的隐藏层偏差参数, W_{hh} 是作用在 h_{t-1} 上的隐藏层权重参数, b_{hh} 是对应的隐藏层偏差参数,这里使用 tanh 函数作为激活函数。

图 2 经典RNN模型示意图

量子 RNN 模型是经典 RNN 模型的量子版本。主要区别在于经典神经网络被 VQC 取代,我们使用数学公式描述 QRNN 模型的计算单元 [1]:

h_t = \tanh(VQC(v_t))

y_t = NN(h_t)

其中输入 v_t 是前一个时间步长的隐藏状态 h_{t−1} 与当前输入向量 x_t 的级联, NN 是经典神经网络层。

首先我们先用DeepQuantum实现一个VQC模块:

## 定义VQC模块
class QuLinear(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(QuLinear, self).__init__()
        n_qubits = input_size  #定义比特个数
        self.cir = dq.QubitCircuit(n_qubits, reupload=True)
        self.cir.hlayer()
        self.cir.rylayer(encode=True)
        self.cir.rzlayer(encode=True)
        self.cir.barrier()
        self.cir.cnot_ring()
        self.cir.barrier()
        self.cir.cnot_ring(step=2)
        self.cir.barrier()
        self.cir.u3layer()

        self.cir.barrier()     #两层变分线路模块[1]
        self.cir.cnot_ring()
        self.cir.barrier()
        self.cir.cnot_ring(step=2)
        self.cir.barrier()
        self.cir.u3layer()


        for i in range(hidden_size): # 以for循环
            self.cir.observable(wires=i, basis='z')

    def forward(self, x):
        ry_parameter = torch.arctan(x)   #将经典数据转化为对应ry的角度
        rz_parameter = torch.arctan(x*x) #将经典数据转化为对应rz的角度
        r_parameter=torch.cat([ry_parameter,rz_parameter],dim=1)
        self.cir(r_parameter)
        return self.cir.expectation()

图 3 量子RNN模型示意图

图 3 展示了混合量子经典 RNN 的单元结构,结合前面章节的内容,不难发现,相比于经典结构,其中的线性全连接层被 VQC 线路替换,我们可以利用上面我们实现的 QuLinear,结合经典 RNN 的计算逻辑,实现量子混合经典循环神经网络。代码如下:

class QRNNCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(QRNNCell, self).__init__()

        self.input_size = input_size
        self.hidden_size = hidden_size
        self.linear = QuLinear(input_size+hidden_size, hidden_size)
        self.tanh = nn.Tanh()
    def forward(self, x, h):
        v = torch.cat((x, h), dim=-1)
        h = self.tanh(self.linear(v))
        return h


class QRNN_hybrid(nn.Module):
    def __init__(self, input_size, hidden_size, target_size, device):
        super(QRNN_hybrid, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.h0_linear = nn.Linear(hidden_size, hidden_size)
        self.gru = QRNNCell(input_size, hidden_size)
        self.tanh = nn.Tanh()
        self.device = device
        self.predictor = nn.Linear(hidden_size, target_size)

    def forward(self, x, hidden_state=None):
        # 初始化隐藏状态
        if hidden_state is None:
            batch_size, sequence_length, _ = x.size()
            h0 = self.tanh(self.h0_linear(torch.zeros(batch_size, self.hidden_size).to(self.device))).to(self.device)
            # 存储所有时间步的隐藏状态
            hidden_states = []
            # 遍历所有时间步
            for t in range(sequence_length):
                xt = x[:, t, :]
                ht = self.gru(xt, h0)
                hidden_states.append(ht.unsqueeze(1))
                h0 = ht
            output = self.predictor(torch.cat(hidden_states, dim=1))
            return output, hidden_states[-1]
        else:
            ht = self.gru(x[:, -1].unsqueeze(1), hidden_state)
            output = self.predictor(ht)
            return output, ht
# 测试模型代码能否成功运行
# 定义输入、隐藏状态和输出的维度
input_size = 2   #输入
hidden_size = 3
output_size = 1  #输出一个数值
# 创建模型实例
device = torch.device("cpu")
model = QRNN_hybrid(input_size, hidden_size, output_size, device)
# 随机生成输入数据
batch_size = 3
sequence_length = 4
input_data = torch.randn(batch_size, sequence_length, input_size)
# 前向传播
output, _ = model(input_data)
# 输出结果
print("输出的形状:", output.shape)
输出的形状: torch.Size([3, 4, 1])

量子RNN模型训练

本节将介绍量子 RNN 模型训练过程,包括训练的数据介绍,训练过程的代码实现。

数据任务

在这里,我们使用余弦函数作为模型训练的数据集,首先定义数据集构建函数,代码如下:

#构建时间序列数据集
class TimeSeriesDataset(Dataset):
    def __init__(self, data, sequence_length):
        self.data = data
        self.sequence_length = sequence_length   #定义数据长度
        self.xdata = []
        self.ydata = []
        for i in range(len(data)):
            if i + self.sequence_length + 1 > len(data):
                break
            self.xdata.append(self.data[i:i+self.sequence_length])      #xdata为输入数据
            self.ydata.append(self.data[i+1:i+self.sequence_length+1])  #ydata为需要预测的后一位输出数据

    def __len__(self):
        return len(self.xdata)

    def __getitem__(self, index):
        x = self.xdata[index]
        y = self.ydata[index]  # 目标序列错开一位
        return x, y

我们给出范围在 [0, 8\pi] 的余弦函数,振幅为0.5,将其分为200个数据点,取前100个数据点作为模型的训练数据,代码如下:

start = 0
end = 8
data_points = 200
amplitude = 0.5
num_train_points = 100

time_steps = np.linspace(start, end, data_points) # 在区间[0, 8*pi]之间线性均匀的选择200个数据点
data = np.cos(np.pi*time_steps)*amplitude # 生成区间[0, 8*pi]上的余弦信号时间序列数据
train_data = data[:num_train_points] # 选择前100个数据点作为训练集
test_data = data[num_train_points:]
# 创建数据集对象
# 由于第一个数据点不需要预测 而序列的最后一个数据点不能出现在训练的输入特征中 这里特征序列和标签序列的长度都需要设为总长度-1
sequence_length = num_train_points - 1
dataset = TimeSeriesDataset(torch.tensor(train_data), sequence_length)
# 创建数据加载器
# 这里我们不做数据的窗口切分,因此每个batch中只有一条完整数据,并且只有一个batch
batch_size = 1
dataloader = DataLoader(dataset, batch_size=batch_size)

#画出我们的数据集
plt.plot(time_steps, data,'o')
plt.show()

全参数训练

在接下来的实验中,我们将利用上一小节中准备的数据集,以及基于上一节构建的 QRNN 模型来进行模型训练。实验中采用均方差损失函数(Mean Squared Error Loss,简称 MSELoss)作为训练过程中的损失函数。选用的优化器为 RMSprop[1],这是一种基于梯度下降法的变体,它具备自适应学习率调整的特性。优化器的超参数配置如下:平滑常数alpha设为0.99,eps设为1e−8,动量(momentum)设为0.5。下面是定义模型训练函数的代码:

def trainer(model, epoch, learning_rate, device):
    criterion = nn.MSELoss()
    optimizer = torch.optim.RMSprop(model.parameters(),learning_rate,alpha=0.99,eps=1e-08,weight_decay=0,momentum=0.5,centered=False)

    model = model.to(device)
    flag = False
    train_loss_list=[]

    for e in range(epoch):
        if flag:
            break
        for x, y in dataloader:
            x = x.unsqueeze(-1).float()
            y = y.unsqueeze(-1).float()
            x = x.to(device)
            y = y.to(device)
            y_pred, _ = model(x)
            loss = criterion(y_pred, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        train_loss_list.append(loss.detach().numpy())
        print("Iteration: {} loss {}".format(e, loss.item()))

    metrics = {'epoch': list(range(1, len(train_loss_list)+1)),
                'train_loss': train_loss_list}
    return model, metrics

我们设置优化器的学习率 lr 为0.01,迭代次数为100次,模型中 VQC 的输入量子比特个数为1,隐藏层量子比特个数为3,输出量子比特个数为1。分别训练 QRNN,QLSTM 和 QGRU 模型,代码如下:

# 训练混合量子经典RNN
lr = 0.01   #定义学习率为0.01
epoch = 100 #定义迭代次数为100次
device = torch.device("cpu")
model_QRNN = QRNN_hybrid(1, 3, 1, device)

optim_model, metrics=trainer(model_QRNN, epoch, lr, device)
Iteration: 0 loss 0.2551746368408203
Iteration: 50 loss 0.002133062342181802
Iteration: 99 loss 0.0015379682881757617
# 绘制QRNN模型的loss曲线
epoch = metrics['epoch']
loss = metrics['train_loss']

# 创建图和Axes对象
# 绘制训练损失曲线
plt.plot(epoch, loss, label='Loss')
plt.title('Loss Curve')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

参考文献

[1] Chen, S. Y. C., Fry, D., Deshmukh, A., Rastunkov, V., & Stefanski, C. (2022). Reservoir computing via quantum recurrent neural networks. arXiv preprint arXiv:2211.02612.

[2] Chen, S. Y. C., Yoo, S., & Fang, Y. L. L. (2022, May). Quantum long short-term memory. In ICASSP 2022-2022 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (pp. 8622-8626). IEEE.

文档链接

案例代码

图灵算法

图灵算法

图灵算法组是一个神秘的算法组织。他们的工作是研究神秘的量子算法。感谢杨杰诚提供的30个算法案例;感谢李瑞琦提供的1个算法案例。