量子机器学习入门¶
1. 量子机器学习简介¶
1.1 什么是量子机器学习?¶
量子机器学习 (Quantum Machine Learning, QML) 是量子计算与机器学习的交叉领域,旨在利用量子计算的优势来加速或改进机器学习算法。
核心特点:
- 利用量子态的高维希尔伯特空间进行特征映射
- 使用变分量子电路作为神经网络层
- 结合经典优化和量子计算
- 在特定任务上可能实现指数级加速
1.2 QML 的主要方向¶
- 量子神经网络 (QNN): 使用参数化量子电路进行学习
- 量子核方法: 利用量子计算机计算核函数
- 量子增强数据: 使用量子态编码数据
- 混合量子-经典算法: 如 VQE, QAOA 等
1.3 应用场景¶
- 分类任务: 图像识别、文本分类
- 回归任务: 函数拟合、预测
- 聚类分析: 数据分组、模式识别
- 生成模型: 量子玻尔兹曼机、量子生成对抗网络
1.4 难度等级¶
⭐⭐⭐⭐ 高级 (需要量子计算和机器学习双重基础)
2. 理论基础¶
2.1 量子神经网络 (QNN)¶
量子神经网络是经典神经网络的量子泛化:
经典神经网络:
输入 -> [权重矩阵] -> [激活函数] -> 输出
量子神经网络:
输入态 -> [参数化量子门] -> [测量] -> 输出
关键组件:
数据编码: 将经典数据编码为量子态
- 振幅编码
- 基态编码
- 角度编码 (RX, RY, RZ)
变分层 (Ansatz): 参数化量子电路
- 强可展性电路
- 硬件高效电路
- 对称保持电路
测量: 提取经典信息
- 期望值测量
- 投影测量
2.2 量子残差网络 (QResNet)¶
量子残差网络受到经典 ResNet 的启发,通过引入残差连接来训练更深的量子电路。
核心思想:
- 使用跳跃连接 (skip connections)
- 学习残差函数而非直接学习目标函数
- 缓解梯度消失问题
2.3 变分量子电路¶
变分量子电路是 QML 的核心,其训练过程:
- 初始化: 随机初始化参数 θ
- 前向传播: 计算量子电路输出 U(θ)|ψ⟩
- 损失计算: 测量并计算损失函数 L(θ)
- 参数更新: 使用梯度下降更新 θ ← θ - α∇L
- 重复: 直到收敛
3. 环境准备¶
3.1 导入必要的库¶
In [ ]:
Copied!
import sys
sys.path.insert(0, 'E:/02_Projects/turingQ/deepquantum/src')
import deepquantum as dq
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles, make_moons, make_classification
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import seaborn as sns
# 设置随机种子以保证结果可复现
torch.manual_seed(42)
np.random.seed(42)
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
print("环境配置完成!")
print(f"PyTorch 版本: {torch.__version__}")
print(f"DeepQuantum 路径: {sys.path[0]}")
import sys
sys.path.insert(0, 'E:/02_Projects/turingQ/deepquantum/src')
import deepquantum as dq
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles, make_moons, make_classification
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import seaborn as sns
# 设置随机种子以保证结果可复现
torch.manual_seed(42)
np.random.seed(42)
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
print("环境配置完成!")
print(f"PyTorch 版本: {torch.__version__}")
print(f"DeepQuantum 路径: {sys.path[0]}")
3.2 DeepQuantum 基础示例¶
首先,让我们创建一个简单的量子电路来熟悉 DeepQuantum 的用法:
In [ ]:
Copied!
# 创建一个简单的量子电路
nqubit = 4
cir = dq.QubitCircuit(nqubit)
# 添加量子门
cir.hlayer() # 对所有量子比特应用 Hadamard 门
cir.rylayer(encode=True) # 编码层,后续注入参数
cir.cnot_ring() # 循环 CNOT 门
# 设置观测量
for i in range(nqubit):
cir.observable(i)
# 运行电路
params = torch.tensor([0.1, 0.2, 0.3, 0.4])
cir(params)
# 获取期望值
expectations = cir.expectation()
print(f"期望值: {expectations}")
print(f"\n电路参数数量: {len(params)}")
# 创建一个简单的量子电路
nqubit = 4
cir = dq.QubitCircuit(nqubit)
# 添加量子门
cir.hlayer() # 对所有量子比特应用 Hadamard 门
cir.rylayer(encode=True) # 编码层,后续注入参数
cir.cnot_ring() # 循环 CNOT 门
# 设置观测量
for i in range(nqubit):
cir.observable(i)
# 运行电路
params = torch.tensor([0.1, 0.2, 0.3, 0.4])
cir(params)
# 获取期望值
expectations = cir.expectation()
print(f"期望值: {expectations}")
print(f"\n电路参数数量: {len(params)}")
In [ ]:
Copied!
class SimpleQNN(nn.Module):
"""
简单的量子神经网络
参数:
nqubit: 量子比特数量
nlayer: 电路层数
"""
def __init__(self, nqubit=4, nlayer=2):
super().__init__()
self.nqubit = nqubit
self.nlayer = nlayer
# 构建量子电路
self.circuit = dq.QubitCircuit(nqubit)
# 初始化层:叠加态
self.circuit.hlayer()
# 变分层
for _ in range(nlayer):
# 数据编码
self.circuit.rylayer(encode=True)
self.circuit.rzlayer(encode=True)
# 纠缠层
self.circuit.cnot_ring()
# 观测量
for i in range(nqubit):
self.circuit.observable(i)
def forward(self, x):
"""
前向传播
参数:
x: 输入数据 [batch_size, features]
返回:
输出 [batch_size, nqubit]
"""
batch_size = x.shape[0]
outputs = []
for i in range(batch_size):
# 将数据编码到量子电路
# 这里我们使用简单的角度编码
params = x[i:i+1].expand(self.nqubit)
self.circuit(params)
# 获取期望值
exp = self.circuit.expectation()
outputs.append(exp)
return torch.stack(outputs)
# 测试 QNN
qnn = SimpleQNN(nqubit=4, nlayer=2)
x_test = torch.randn(2, 4) # 2个样本,每个4维
output = qnn(x_test)
print(f"QNN 输出形状: {output.shape}")
print(f"QNN 输出: {output}")
class SimpleQNN(nn.Module):
"""
简单的量子神经网络
参数:
nqubit: 量子比特数量
nlayer: 电路层数
"""
def __init__(self, nqubit=4, nlayer=2):
super().__init__()
self.nqubit = nqubit
self.nlayer = nlayer
# 构建量子电路
self.circuit = dq.QubitCircuit(nqubit)
# 初始化层:叠加态
self.circuit.hlayer()
# 变分层
for _ in range(nlayer):
# 数据编码
self.circuit.rylayer(encode=True)
self.circuit.rzlayer(encode=True)
# 纠缠层
self.circuit.cnot_ring()
# 观测量
for i in range(nqubit):
self.circuit.observable(i)
def forward(self, x):
"""
前向传播
参数:
x: 输入数据 [batch_size, features]
返回:
输出 [batch_size, nqubit]
"""
batch_size = x.shape[0]
outputs = []
for i in range(batch_size):
# 将数据编码到量子电路
# 这里我们使用简单的角度编码
params = x[i:i+1].expand(self.nqubit)
self.circuit(params)
# 获取期望值
exp = self.circuit.expectation()
outputs.append(exp)
return torch.stack(outputs)
# 测试 QNN
qnn = SimpleQNN(nqubit=4, nlayer=2)
x_test = torch.randn(2, 4) # 2个样本,每个4维
output = qnn(x_test)
print(f"QNN 输出形状: {output.shape}")
print(f"QNN 输出: {output}")
4.2 改进的 QNN (支持批量处理)¶
In [ ]:
Copied!
class ImprovedQNN(nn.Module):
"""
改进的量子神经网络,支持批量处理
参数:
nqubit: 量子比特数量
nlayer: 电路层数
encoding_type: 编码类型 ('angle', 'amplitude')
"""
def __init__(self, nqubit=4, nlayer=2, encoding_type='angle'):
super().__init__()
self.nqubit = nqubit
self.nlayer = nlayer
self.encoding_type = encoding_type
# 经典预处理层
self.preprocess = nn.Sequential(
nn.Linear(nqubit, nqubit),
nn.Tanh() # 将值映射到 [-1, 1]
)
# 构建量子电路
self.circuit = self._build_circuit()
# 经典后处理层
self.postprocess = nn.Sequential(
nn.Linear(nqubit, 2), # 二分类
)
def _build_circuit(self):
"""构建量子电路"""
cir = dq.QubitCircuit(self.nqubit)
# 初始化
cir.hlayer()
# 变分层
for _ in range(self.nlayer):
if self.encoding_type == 'angle':
cir.rylayer(encode=True)
cir.rzlayer(encode=True)
cir.cnot_ring()
# 观测量
for i in range(self.nqubit):
cir.observable(i)
return cir
def forward(self, x):
"""
前向传播
参数:
x: 输入数据 [batch_size, nqubit]
返回:
输出 [batch_size, 2]
"""
batch_size = x.shape[0]
# 预处理
x = self.preprocess(x)
# 量子层处理
qnn_outputs = []
for i in range(batch_size):
# 将数据映射到旋转角度
angles = x[i] * np.pi # 映射到 [-π, π]
self.circuit(angles)
exp = self.circuit.expectation()
qnn_outputs.append(exp)
qnn_out = torch.stack(qnn_outputs)
# 后处理
output = self.postprocess(qnn_out)
return output
# 测试改进的 QNN
improved_qnn = ImprovedQNN(nqubit=4, nlayer=2)
x_test = torch.randn(3, 4)
output = improved_qnn(x_test)
print(f"输入形状: {x_test.shape}")
print(f"输出形状: {output.shape}")
print(f"输出:\n{output}")
class ImprovedQNN(nn.Module):
"""
改进的量子神经网络,支持批量处理
参数:
nqubit: 量子比特数量
nlayer: 电路层数
encoding_type: 编码类型 ('angle', 'amplitude')
"""
def __init__(self, nqubit=4, nlayer=2, encoding_type='angle'):
super().__init__()
self.nqubit = nqubit
self.nlayer = nlayer
self.encoding_type = encoding_type
# 经典预处理层
self.preprocess = nn.Sequential(
nn.Linear(nqubit, nqubit),
nn.Tanh() # 将值映射到 [-1, 1]
)
# 构建量子电路
self.circuit = self._build_circuit()
# 经典后处理层
self.postprocess = nn.Sequential(
nn.Linear(nqubit, 2), # 二分类
)
def _build_circuit(self):
"""构建量子电路"""
cir = dq.QubitCircuit(self.nqubit)
# 初始化
cir.hlayer()
# 变分层
for _ in range(self.nlayer):
if self.encoding_type == 'angle':
cir.rylayer(encode=True)
cir.rzlayer(encode=True)
cir.cnot_ring()
# 观测量
for i in range(self.nqubit):
cir.observable(i)
return cir
def forward(self, x):
"""
前向传播
参数:
x: 输入数据 [batch_size, nqubit]
返回:
输出 [batch_size, 2]
"""
batch_size = x.shape[0]
# 预处理
x = self.preprocess(x)
# 量子层处理
qnn_outputs = []
for i in range(batch_size):
# 将数据映射到旋转角度
angles = x[i] * np.pi # 映射到 [-π, π]
self.circuit(angles)
exp = self.circuit.expectation()
qnn_outputs.append(exp)
qnn_out = torch.stack(qnn_outputs)
# 后处理
output = self.postprocess(qnn_out)
return output
# 测试改进的 QNN
improved_qnn = ImprovedQNN(nqubit=4, nlayer=2)
x_test = torch.randn(3, 4)
output = improved_qnn(x_test)
print(f"输入形状: {x_test.shape}")
print(f"输出形状: {output.shape}")
print(f"输出:\n{output}")
In [ ]:
Copied!
class QResidualBlock(nn.Module):
"""
量子残差块
参数:
nqubit: 量子比特数量
nlayer: 每个块的层数
"""
def __init__(self, nqubit=4, nlayer=2):
super().__init__()
self.nqubit = nqubit
self.nlayer = nlayer
# 主路径
self.main_circuit = self._build_circuit(nlayer)
# 跳跃连接 (简单恒等映射)
self.skip_weight = nn.Parameter(torch.ones(1) * 0.5)
def _build_circuit(self, nlayer):
"""构建量子电路"""
cir = dq.QubitCircuit(self.nqubit)
for _ in range(nlayer):
cir.rylayer(encode=True)
cir.rzlayer(encode=True)
cir.cnot_ring()
for i in range(self.nqubit):
cir.observable(i)
return cir
def forward(self, x):
"""
前向传播
参数:
x: 输入 [batch_size, nqubit]
返回:
输出 [batch_size, nqubit]
"""
batch_size = x.shape[0]
outputs = []
for i in range(batch_size):
# 主路径
angles = x[i] * np.pi
self.main_circuit(angles)
main_out = self.main_circuit.expectation()
# 残差连接
skip_out = x[i]
# 组合
output = main_out + self.skip_weight * skip_out
outputs.append(output)
return torch.stack(outputs)
class QResNet(nn.Module):
"""
量子残差网络
参数:
nqubit: 量子比特数量
nblocks: 残差块数量
nlayer: 每个块的层数
num_classes: 分类类别数
"""
def __init__(self, nqubit=4, nblocks=3, nlayer=2, num_classes=2):
super().__init__()
self.nqubit = nqubit
# 输入层
self.input_layer = nn.Linear(nqubit, nqubit)
# 残差块
self.blocks = nn.ModuleList([
QResidualBlock(nqubit, nlayer) for _ in range(nblocks)
])
# 输出层
self.output_layer = nn.Linear(nqubit, num_classes)
# 激活函数
self.activation = nn.Tanh()
def forward(self, x):
"""
前向传播
参数:
x: 输入 [batch_size, nqubit]
返回:
输出 [batch_size, num_classes]
"""
# 输入投影
x = self.activation(self.input_layer(x))
# 通过残差块
for block in self.blocks:
x = self.activation(block(x))
# 输出
output = self.output_layer(x)
return output
# 测试 QResNet
qresnet = QResNet(nqubit=4, nblocks=2, nlayer=2, num_classes=2)
x_test = torch.randn(3, 4)
output = qresnet(x_test)
print(f"QResNet 输出形状: {output.shape}")
print(f"QResNet 输出:\n{output}")
print(f"\n模型参数数量: {sum(p.numel() for p in qresnet.parameters())}")
class QResidualBlock(nn.Module):
"""
量子残差块
参数:
nqubit: 量子比特数量
nlayer: 每个块的层数
"""
def __init__(self, nqubit=4, nlayer=2):
super().__init__()
self.nqubit = nqubit
self.nlayer = nlayer
# 主路径
self.main_circuit = self._build_circuit(nlayer)
# 跳跃连接 (简单恒等映射)
self.skip_weight = nn.Parameter(torch.ones(1) * 0.5)
def _build_circuit(self, nlayer):
"""构建量子电路"""
cir = dq.QubitCircuit(self.nqubit)
for _ in range(nlayer):
cir.rylayer(encode=True)
cir.rzlayer(encode=True)
cir.cnot_ring()
for i in range(self.nqubit):
cir.observable(i)
return cir
def forward(self, x):
"""
前向传播
参数:
x: 输入 [batch_size, nqubit]
返回:
输出 [batch_size, nqubit]
"""
batch_size = x.shape[0]
outputs = []
for i in range(batch_size):
# 主路径
angles = x[i] * np.pi
self.main_circuit(angles)
main_out = self.main_circuit.expectation()
# 残差连接
skip_out = x[i]
# 组合
output = main_out + self.skip_weight * skip_out
outputs.append(output)
return torch.stack(outputs)
class QResNet(nn.Module):
"""
量子残差网络
参数:
nqubit: 量子比特数量
nblocks: 残差块数量
nlayer: 每个块的层数
num_classes: 分类类别数
"""
def __init__(self, nqubit=4, nblocks=3, nlayer=2, num_classes=2):
super().__init__()
self.nqubit = nqubit
# 输入层
self.input_layer = nn.Linear(nqubit, nqubit)
# 残差块
self.blocks = nn.ModuleList([
QResidualBlock(nqubit, nlayer) for _ in range(nblocks)
])
# 输出层
self.output_layer = nn.Linear(nqubit, num_classes)
# 激活函数
self.activation = nn.Tanh()
def forward(self, x):
"""
前向传播
参数:
x: 输入 [batch_size, nqubit]
返回:
输出 [batch_size, num_classes]
"""
# 输入投影
x = self.activation(self.input_layer(x))
# 通过残差块
for block in self.blocks:
x = self.activation(block(x))
# 输出
output = self.output_layer(x)
return output
# 测试 QResNet
qresnet = QResNet(nqubit=4, nblocks=2, nlayer=2, num_classes=2)
x_test = torch.randn(3, 4)
output = qresnet(x_test)
print(f"QResNet 输出形状: {output.shape}")
print(f"QResNet 输出:\n{output}")
print(f"\n模型参数数量: {sum(p.numel() for p in qresnet.parameters())}")
In [ ]:
Copied!
# 生成圆形数据集
X, y = make_circles(
n_samples=500,
noise=0.1,
factor=0.5,
random_state=42
)
# 标准化
scaler = StandardScaler()
X = scaler.fit_transform(X)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 转换为 PyTorch 张量
X_train_torch = torch.FloatTensor(X_train)
y_train_torch = torch.LongTensor(y_train)
X_test_torch = torch.FloatTensor(X_test)
y_test_torch = torch.LongTensor(y_test)
# 将 2D 特征扩展到 4D (用于 4 个量子比特)
def expand_features(X, target_dim=4):
"""将特征扩展到目标维度"""
if X.shape[1] >= target_dim:
return X[:, :target_dim]
# 使用多项式特征扩展
X_expanded = np.zeros((X.shape[0], target_dim))
X_expanded[:, :X.shape[1]] = X
# 添加交互特征
if X.shape[1] == 2 and target_dim >= 4:
X_expanded[:, 2] = X[:, 0] * X[:, 1] # 交互项
X_expanded[:, 3] = X[:, 0]**2 + X[:, 1]**2 # 径向特征
return X_expanded
X_train_expanded = expand_features(X_train, 4)
X_test_expanded = expand_features(X_test, 4)
X_train_torch = torch.FloatTensor(X_train_expanded)
X_test_torch = torch.FloatTensor(X_test_expanded)
print(f"训练集大小: {X_train_torch.shape}")
print(f"测试集大小: {X_test_torch.shape}")
print(f"类别分布: 训练集 {np.bincount(y_train)}, 测试集 {np.bincount(y_test)}")
# 生成圆形数据集
X, y = make_circles(
n_samples=500,
noise=0.1,
factor=0.5,
random_state=42
)
# 标准化
scaler = StandardScaler()
X = scaler.fit_transform(X)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 转换为 PyTorch 张量
X_train_torch = torch.FloatTensor(X_train)
y_train_torch = torch.LongTensor(y_train)
X_test_torch = torch.FloatTensor(X_test)
y_test_torch = torch.LongTensor(y_test)
# 将 2D 特征扩展到 4D (用于 4 个量子比特)
def expand_features(X, target_dim=4):
"""将特征扩展到目标维度"""
if X.shape[1] >= target_dim:
return X[:, :target_dim]
# 使用多项式特征扩展
X_expanded = np.zeros((X.shape[0], target_dim))
X_expanded[:, :X.shape[1]] = X
# 添加交互特征
if X.shape[1] == 2 and target_dim >= 4:
X_expanded[:, 2] = X[:, 0] * X[:, 1] # 交互项
X_expanded[:, 3] = X[:, 0]**2 + X[:, 1]**2 # 径向特征
return X_expanded
X_train_expanded = expand_features(X_train, 4)
X_test_expanded = expand_features(X_test, 4)
X_train_torch = torch.FloatTensor(X_train_expanded)
X_test_torch = torch.FloatTensor(X_test_expanded)
print(f"训练集大小: {X_train_torch.shape}")
print(f"测试集大小: {X_test_torch.shape}")
print(f"类别分布: 训练集 {np.bincount(y_train)}, 测试集 {np.bincount(y_test)}")
6.2 可视化数据集¶
In [ ]:
Copied!
# 可视化数据集
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 训练集
axes[0].scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='viridis', alpha=0.6)
axes[0].set_title('训练集', fontsize=14, fontweight='bold')
axes[0].set_xlabel('特征 1', fontsize=12)
axes[0].set_ylabel('特征 2', fontsize=12)
axes[0].grid(True, alpha=0.3)
# 测试集
axes[1].scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='viridis', alpha=0.6)
axes[1].set_title('测试集', fontsize=14, fontweight='bold')
axes[1].set_xlabel('特征 1', fontsize=12)
axes[1].set_ylabel('特征 2', fontsize=12)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 可视化数据集
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 训练集
axes[0].scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='viridis', alpha=0.6)
axes[0].set_title('训练集', fontsize=14, fontweight='bold')
axes[0].set_xlabel('特征 1', fontsize=12)
axes[0].set_ylabel('特征 2', fontsize=12)
axes[0].grid(True, alpha=0.3)
# 测试集
axes[1].scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='viridis', alpha=0.6)
axes[1].set_title('测试集', fontsize=14, fontweight='bold')
axes[1].set_xlabel('特征 1', fontsize=12)
axes[1].set_ylabel('特征 2', fontsize=12)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
6.3 训练函数定义¶
In [ ]:
Copied!
def train_model(model, X_train, y_train, X_test, y_test,
epochs=50, lr=0.01, batch_size=32):
"""
训练量子模型
参数:
model: 量子模型
X_train: 训练数据
y_train: 训练标签
X_test: 测试数据
y_test: 测试标签
epochs: 训练轮数
lr: 学习率
batch_size: 批量大小
返回:
训练历史
"""
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
train_losses = []
train_accs = []
test_accs = []
n_samples = X_train.shape[0]
print(f"开始训练 {epochs} 轮...")
print("=" * 60)
for epoch in range(epochs):
model.train()
epoch_loss = 0
correct = 0
total = 0
# 小批量训练
for i in range(0, n_samples, batch_size):
batch_end = min(i + batch_size, n_samples)
batch_X = X_train[i:batch_end]
batch_y = y_train[i:batch_end]
# 前向传播
optimizer.zero_grad()
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
# 反向传播
loss.backward()
optimizer.step()
# 统计
epoch_loss += loss.item()
_, predicted = torch.max(outputs.data, 1)
total += batch_y.size(0)
correct += (predicted == batch_y).sum().item()
# 计算平均损失和准确率
avg_loss = epoch_loss / (n_samples // batch_size + 1)
train_acc = 100 * correct / total
# 测试集评估
model.eval()
with torch.no_grad():
outputs = model(X_test)
_, predicted = torch.max(outputs.data, 1)
test_acc = 100 * (predicted == y_test).sum().item() / y_test.size(0)
train_losses.append(avg_loss)
train_accs.append(train_acc)
test_accs.append(test_acc)
# 打印进度
if (epoch + 1) % 10 == 0 or epoch == 0:
print(f'轮次 [{epoch+1:3d}/{epochs}], '
f'损失: {avg_loss:.4f}, '
f'训练准确率: {train_acc:.2f}%, '
f'测试准确率: {test_acc:.2f}%')
print("=" * 60)
print("训练完成!")
history = {
'train_losses': train_losses,
'train_accs': train_accs,
'test_accs': test_accs
}
return history
def train_model(model, X_train, y_train, X_test, y_test,
epochs=50, lr=0.01, batch_size=32):
"""
训练量子模型
参数:
model: 量子模型
X_train: 训练数据
y_train: 训练标签
X_test: 测试数据
y_test: 测试标签
epochs: 训练轮数
lr: 学习率
batch_size: 批量大小
返回:
训练历史
"""
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
train_losses = []
train_accs = []
test_accs = []
n_samples = X_train.shape[0]
print(f"开始训练 {epochs} 轮...")
print("=" * 60)
for epoch in range(epochs):
model.train()
epoch_loss = 0
correct = 0
total = 0
# 小批量训练
for i in range(0, n_samples, batch_size):
batch_end = min(i + batch_size, n_samples)
batch_X = X_train[i:batch_end]
batch_y = y_train[i:batch_end]
# 前向传播
optimizer.zero_grad()
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
# 反向传播
loss.backward()
optimizer.step()
# 统计
epoch_loss += loss.item()
_, predicted = torch.max(outputs.data, 1)
total += batch_y.size(0)
correct += (predicted == batch_y).sum().item()
# 计算平均损失和准确率
avg_loss = epoch_loss / (n_samples // batch_size + 1)
train_acc = 100 * correct / total
# 测试集评估
model.eval()
with torch.no_grad():
outputs = model(X_test)
_, predicted = torch.max(outputs.data, 1)
test_acc = 100 * (predicted == y_test).sum().item() / y_test.size(0)
train_losses.append(avg_loss)
train_accs.append(train_acc)
test_accs.append(test_acc)
# 打印进度
if (epoch + 1) % 10 == 0 or epoch == 0:
print(f'轮次 [{epoch+1:3d}/{epochs}], '
f'损失: {avg_loss:.4f}, '
f'训练准确率: {train_acc:.2f}%, '
f'测试准确率: {test_acc:.2f}%')
print("=" * 60)
print("训练完成!")
history = {
'train_losses': train_losses,
'train_accs': train_accs,
'test_accs': test_accs
}
return history
6.4 训练 QNN 模型¶
In [ ]:
Copied!
# 创建并训练 QNN 模型
qnn_model = ImprovedQNN(nqubit=4, nlayer=2)
print("训练量子神经网络 (QNN)")
print("-" * 60)
qnn_history = train_model(
qnn_model,
X_train_torch, y_train_torch,
X_test_torch, y_test_torch,
epochs=30,
lr=0.01,
batch_size=32
)
# 创建并训练 QNN 模型
qnn_model = ImprovedQNN(nqubit=4, nlayer=2)
print("训练量子神经网络 (QNN)")
print("-" * 60)
qnn_history = train_model(
qnn_model,
X_train_torch, y_train_torch,
X_test_torch, y_test_torch,
epochs=30,
lr=0.01,
batch_size=32
)
6.5 训练 QResNet 模型¶
In [ ]:
Copied!
# 创建并训练 QResNet 模型
qresnet_model = QResNet(nqubit=4, nblocks=2, nlayer=2, num_classes=2)
print("\n训练量子残差网络 (QResNet)")
print("-" * 60)
qresnet_history = train_model(
qresnet_model,
X_train_torch, y_train_torch,
X_test_torch, y_test_torch,
epochs=30,
lr=0.01,
batch_size=32
)
# 创建并训练 QResNet 模型
qresnet_model = QResNet(nqubit=4, nblocks=2, nlayer=2, num_classes=2)
print("\n训练量子残差网络 (QResNet)")
print("-" * 60)
qresnet_history = train_model(
qresnet_model,
X_train_torch, y_train_torch,
X_test_torch, y_test_torch,
epochs=30,
lr=0.01,
batch_size=32
)
6.6 训练可视化¶
In [ ]:
Copied!
# 可视化训练过程
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# 损失曲线
axes[0].plot(qnn_history['train_losses'], 'b-', label='QNN', linewidth=2)
axes[0].plot(qresnet_history['train_losses'], 'r-', label='QResNet', linewidth=2)
axes[0].set_xlabel('训练轮次', fontsize=12)
axes[0].set_ylabel('损失', fontsize=12)
axes[0].set_title('训练损失', fontsize=14, fontweight='bold')
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
# 训练准确率
axes[1].plot(qnn_history['train_accs'], 'b-', label='QNN', linewidth=2)
axes[1].plot(qresnet_history['train_accs'], 'r-', label='QResNet', linewidth=2)
axes[1].set_xlabel('训练轮次', fontsize=12)
axes[1].set_ylabel('准确率 (%)', fontsize=12)
axes[1].set_title('训练准确率', fontsize=14, fontweight='bold')
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
# 测试准确率
axes[2].plot(qnn_history['test_accs'], 'b-', label='QNN', linewidth=2)
axes[2].plot(qresnet_history['test_accs'], 'r-', label='QResNet', linewidth=2)
axes[2].set_xlabel('训练轮次', fontsize=12)
axes[2].set_ylabel('准确率 (%)', fontsize=12)
axes[2].set_title('测试准确率', fontsize=14, fontweight='bold')
axes[2].legend(fontsize=11)
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 打印最终结果
print("\n" + "=" * 60)
print("最终性能对比")
print("=" * 60)
print(f"QNN - 训练准确率: {qnn_history['train_accs'][-1]:.2f}%, "
f"测试准确率: {qnn_history['test_accs'][-1]:.2f}%")
print(f"QResNet - 训练准确率: {qresnet_history['train_accs'][-1]:.2f}%, "
f"测试准确率: {qresnet_history['test_accs'][-1]:.2f}%")
# 可视化训练过程
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# 损失曲线
axes[0].plot(qnn_history['train_losses'], 'b-', label='QNN', linewidth=2)
axes[0].plot(qresnet_history['train_losses'], 'r-', label='QResNet', linewidth=2)
axes[0].set_xlabel('训练轮次', fontsize=12)
axes[0].set_ylabel('损失', fontsize=12)
axes[0].set_title('训练损失', fontsize=14, fontweight='bold')
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
# 训练准确率
axes[1].plot(qnn_history['train_accs'], 'b-', label='QNN', linewidth=2)
axes[1].plot(qresnet_history['train_accs'], 'r-', label='QResNet', linewidth=2)
axes[1].set_xlabel('训练轮次', fontsize=12)
axes[1].set_ylabel('准确率 (%)', fontsize=12)
axes[1].set_title('训练准确率', fontsize=14, fontweight='bold')
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
# 测试准确率
axes[2].plot(qnn_history['test_accs'], 'b-', label='QNN', linewidth=2)
axes[2].plot(qresnet_history['test_accs'], 'r-', label='QResNet', linewidth=2)
axes[2].set_xlabel('训练轮次', fontsize=12)
axes[2].set_ylabel('准确率 (%)', fontsize=12)
axes[2].set_title('测试准确率', fontsize=14, fontweight='bold')
axes[2].legend(fontsize=11)
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 打印最终结果
print("\n" + "=" * 60)
print("最终性能对比")
print("=" * 60)
print(f"QNN - 训练准确率: {qnn_history['train_accs'][-1]:.2f}%, "
f"测试准确率: {qnn_history['test_accs'][-1]:.2f}%")
print(f"QResNet - 训练准确率: {qresnet_history['train_accs'][-1]:.2f}%, "
f"测试准确率: {qresnet_history['test_accs'][-1]:.2f}%")
In [ ]:
Copied!
class ClassicalNN(nn.Module):
"""
经典神经网络 (用于对比)
参数:
input_dim: 输入维度
hidden_dims: 隐藏层维度列表
num_classes: 分类类别数
"""
def __init__(self, input_dim=4, hidden_dims=[16, 8], num_classes=2):
super().__init__()
layers = []
prev_dim = input_dim
# 隐藏层
for hidden_dim in hidden_dims:
layers.append(nn.Linear(prev_dim, hidden_dim))
layers.append(nn.ReLU())
layers.append(nn.Dropout(0.2))
prev_dim = hidden_dim
# 输出层
layers.append(nn.Linear(prev_dim, num_classes))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
# 创建并训练经典神经网络
classical_model = ClassicalNN(input_dim=4, hidden_dims=[16, 8], num_classes=2)
print("训练经典神经网络 (CNN)")
print("-" * 60)
classical_history = train_model(
classical_model,
X_train_torch, y_train_torch,
X_test_torch, y_test_torch,
epochs=30,
lr=0.01,
batch_size=32
)
class ClassicalNN(nn.Module):
"""
经典神经网络 (用于对比)
参数:
input_dim: 输入维度
hidden_dims: 隐藏层维度列表
num_classes: 分类类别数
"""
def __init__(self, input_dim=4, hidden_dims=[16, 8], num_classes=2):
super().__init__()
layers = []
prev_dim = input_dim
# 隐藏层
for hidden_dim in hidden_dims:
layers.append(nn.Linear(prev_dim, hidden_dim))
layers.append(nn.ReLU())
layers.append(nn.Dropout(0.2))
prev_dim = hidden_dim
# 输出层
layers.append(nn.Linear(prev_dim, num_classes))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
# 创建并训练经典神经网络
classical_model = ClassicalNN(input_dim=4, hidden_dims=[16, 8], num_classes=2)
print("训练经典神经网络 (CNN)")
print("-" * 60)
classical_history = train_model(
classical_model,
X_train_torch, y_train_torch,
X_test_torch, y_test_torch,
epochs=30,
lr=0.01,
batch_size=32
)
7.2 性能对比¶
In [ ]:
Copied!
# 性能对比
models = {
'经典 NN': classical_model,
'QNN': qnn_model,
'QResNet': qresnet_model
}
histories = {
'经典 NN': classical_history,
'QNN': qnn_history,
'QResNet': qresnet_history
}
# 最终准确率对比
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 测试准确率柱状图
final_test_accs = [
histories['经典 NN']['test_accs'][-1],
histories['QNN']['test_accs'][-1],
histories['QResNet']['test_accs'][-1]
]
bars = axes[0].bar(models.keys(), final_test_accs,
color=['steelblue', 'green', 'red'], alpha=0.7)
axes[0].set_ylabel('测试准确率 (%)', fontsize=12)
axes[0].set_title('模型性能对比', fontsize=14, fontweight='bold')
axes[0].grid(axis='y', alpha=0.3)
axes[0].set_ylim([0, 100])
# 在柱状图上添加数值标签
for bar, acc in zip(bars, final_test_accs):
axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{acc:.1f}%', ha='center', va='bottom', fontsize=11, fontweight='bold')
# 训练曲线对比
axes[1].plot(histories['经典 NN']['test_accs'], 'b-', label='经典 NN', linewidth=2)
axes[1].plot(histories['QNN']['test_accs'], 'g-', label='QNN', linewidth=2)
axes[1].plot(histories['QResNet']['test_accs'], 'r-', label='QResNet', linewidth=2)
axes[1].set_xlabel('训练轮次', fontsize=12)
axes[1].set_ylabel('测试准确率 (%)', fontsize=12)
axes[1].set_title('训练过程对比', fontsize=14, fontweight='bold')
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 打印详细对比
print("\n" + "=" * 70)
print("详细性能对比")
print("=" * 70)
print(f"{'模型':<15} {'训练准确率':<15} {'测试准确率':<15} {'参数量':<15}")
print("-" * 70)
for name, model in models.items():
train_acc = histories[name]['train_accs'][-1]
test_acc = histories[name]['test_accs'][-1]
n_params = sum(p.numel() for p in model.parameters())
print(f"{name:<15} {train_acc:<15.2f} {test_acc:<15.2f} {n_params:<15}")
print("=" * 70)
# 性能对比
models = {
'经典 NN': classical_model,
'QNN': qnn_model,
'QResNet': qresnet_model
}
histories = {
'经典 NN': classical_history,
'QNN': qnn_history,
'QResNet': qresnet_history
}
# 最终准确率对比
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 测试准确率柱状图
final_test_accs = [
histories['经典 NN']['test_accs'][-1],
histories['QNN']['test_accs'][-1],
histories['QResNet']['test_accs'][-1]
]
bars = axes[0].bar(models.keys(), final_test_accs,
color=['steelblue', 'green', 'red'], alpha=0.7)
axes[0].set_ylabel('测试准确率 (%)', fontsize=12)
axes[0].set_title('模型性能对比', fontsize=14, fontweight='bold')
axes[0].grid(axis='y', alpha=0.3)
axes[0].set_ylim([0, 100])
# 在柱状图上添加数值标签
for bar, acc in zip(bars, final_test_accs):
axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{acc:.1f}%', ha='center', va='bottom', fontsize=11, fontweight='bold')
# 训练曲线对比
axes[1].plot(histories['经典 NN']['test_accs'], 'b-', label='经典 NN', linewidth=2)
axes[1].plot(histories['QNN']['test_accs'], 'g-', label='QNN', linewidth=2)
axes[1].plot(histories['QResNet']['test_accs'], 'r-', label='QResNet', linewidth=2)
axes[1].set_xlabel('训练轮次', fontsize=12)
axes[1].set_ylabel('测试准确率 (%)', fontsize=12)
axes[1].set_title('训练过程对比', fontsize=14, fontweight='bold')
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 打印详细对比
print("\n" + "=" * 70)
print("详细性能对比")
print("=" * 70)
print(f"{'模型':<15} {'训练准确率':<15} {'测试准确率':<15} {'参数量':<15}")
print("-" * 70)
for name, model in models.items():
train_acc = histories[name]['train_accs'][-1]
test_acc = histories[name]['test_accs'][-1]
n_params = sum(p.numel() for p in model.parameters())
print(f"{name:<15} {train_acc:<15.2f} {test_acc:<15.2f} {n_params:<15}")
print("=" * 70)
7.3 混淆矩阵可视化¶
In [ ]:
Copied!
# 计算混淆矩阵
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for idx, (name, model) in enumerate(models.items()):
# 预测
model.eval()
with torch.no_grad():
outputs = model(X_test_torch)
_, predicted = torch.max(outputs, 1)
# 混淆矩阵
cm = confusion_matrix(y_test_torch.numpy(), predicted.numpy())
# 绘制
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=axes[idx])
axes[idx].set_title(f'{name} 混淆矩阵', fontsize=14, fontweight='bold')
axes[idx].set_xlabel('预测标签', fontsize=12)
axes[idx].set_ylabel('真实标签', fontsize=12)
plt.tight_layout()
plt.show()
# 打印分类报告
print("\n" + "=" * 70)
print("分类报告")
print("=" * 70)
for name, model in models.items():
model.eval()
with torch.no_grad():
outputs = model(X_test_torch)
_, predicted = torch.max(outputs, 1)
print(f"\n{name}:")
print("-" * 70)
print(classification_report(y_test_torch.numpy(), predicted.numpy(),
target_names=['类别 0', '类别 1']))
# 计算混淆矩阵
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for idx, (name, model) in enumerate(models.items()):
# 预测
model.eval()
with torch.no_grad():
outputs = model(X_test_torch)
_, predicted = torch.max(outputs, 1)
# 混淆矩阵
cm = confusion_matrix(y_test_torch.numpy(), predicted.numpy())
# 绘制
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=axes[idx])
axes[idx].set_title(f'{name} 混淆矩阵', fontsize=14, fontweight='bold')
axes[idx].set_xlabel('预测标签', fontsize=12)
axes[idx].set_ylabel('真实标签', fontsize=12)
plt.tight_layout()
plt.show()
# 打印分类报告
print("\n" + "=" * 70)
print("分类报告")
print("=" * 70)
for name, model in models.items():
model.eval()
with torch.no_grad():
outputs = model(X_test_torch)
_, predicted = torch.max(outputs, 1)
print(f"\n{name}:")
print("-" * 70)
print(classification_report(y_test_torch.numpy(), predicted.numpy(),
target_names=['类别 0', '类别 1']))
In [ ]:
Copied!
# 创建量子卷积神经网络
qcnn = dq.QuantumConvolutionalNeuralNetwork(
nqubit=8,
nlayer=2,
requires_grad=True
)
print("量子卷积神经网络 (QCNN)")
print("-" * 60)
print(f"量子比特数: {qcnn.nqubit}")
print(f"层数: {2}") # QCNN 自动缩减层数
print(f"可训练参数: {sum(p.numel() for p in qcnn.parameters() if p.requires_grad)}")
# 测试 QCNN
x_test = torch.randn(1, 8)
# QCNN 期望的输入是编码角度
angles = x_test * np.pi
print(f"\n输入形状: {x_test.shape}")
print(f"\n注意: QCNN 通过卷积和池化操作自动处理特征提取")
# 创建量子卷积神经网络
qcnn = dq.QuantumConvolutionalNeuralNetwork(
nqubit=8,
nlayer=2,
requires_grad=True
)
print("量子卷积神经网络 (QCNN)")
print("-" * 60)
print(f"量子比特数: {qcnn.nqubit}")
print(f"层数: {2}") # QCNN 自动缩减层数
print(f"可训练参数: {sum(p.numel() for p in qcnn.parameters() if p.requires_grad)}")
# 测试 QCNN
x_test = torch.randn(1, 8)
# QCNN 期望的输入是编码角度
angles = x_test * np.pi
print(f"\n输入形状: {x_test.shape}")
print(f"\n注意: QCNN 通过卷积和池化操作自动处理特征提取")
8.2 矩阵乘积态 (MPS) 优化¶
DeepQuantum 支持 MPS 表示,可以显著降低内存需求:
In [ ]:
Copied!
# 比较标准表示和 MPS 表示
nqubits = [4, 6, 8, 10]
memory_standard = []
memory_mps = []
for nq in nqubits:
# 标准表示
cir_std = dq.QubitCircuit(nq)
cir_std.hlayer()
for _ in range(3):
cir_std.rylayer(encode=True)
cir_std.cnot_ring()
# MPS 表示
cir_mps = dq.QubitCircuit(nq, mps=True, chi=8)
cir_mps.hlayer()
for _ in range(3):
cir_mps.rylayer(encode=True)
cir_mps.cnot_ring()
# 估算内存使用 (状态向量维度)
mem_std = 2 ** nq
mem_mps = nq * 8 * 8 # nq * chi * chi
memory_standard.append(mem_std)
memory_mps.append(mem_mps)
# 可视化内存对比
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(nqubits, memory_standard, 'o-', label='标准表示', linewidth=2, markersize=8)
ax.plot(nqubits, memory_mps, 's-', label='MPS 表示 (χ=8)', linewidth=2, markersize=8)
ax.set_xlabel('量子比特数', fontsize=12)
ax.set_ylabel('内存使用 (复数)', fontsize=12)
ax.set_title('内存效率对比', fontsize=14, fontweight='bold')
ax.set_yscale('log')
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("\n内存使用对比:")
print("-" * 60)
for nq, mem_std, mem_mps in zip(nqubits, memory_standard, memory_mps):
reduction = mem_std / mem_mps
print(f"{nq} 量子比特: 标准={mem_std:>8}, MPS={mem_mps:>8}, 节省={reduction:>6.1f}x")
# 比较标准表示和 MPS 表示
nqubits = [4, 6, 8, 10]
memory_standard = []
memory_mps = []
for nq in nqubits:
# 标准表示
cir_std = dq.QubitCircuit(nq)
cir_std.hlayer()
for _ in range(3):
cir_std.rylayer(encode=True)
cir_std.cnot_ring()
# MPS 表示
cir_mps = dq.QubitCircuit(nq, mps=True, chi=8)
cir_mps.hlayer()
for _ in range(3):
cir_mps.rylayer(encode=True)
cir_mps.cnot_ring()
# 估算内存使用 (状态向量维度)
mem_std = 2 ** nq
mem_mps = nq * 8 * 8 # nq * chi * chi
memory_standard.append(mem_std)
memory_mps.append(mem_mps)
# 可视化内存对比
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(nqubits, memory_standard, 'o-', label='标准表示', linewidth=2, markersize=8)
ax.plot(nqubits, memory_mps, 's-', label='MPS 表示 (χ=8)', linewidth=2, markersize=8)
ax.set_xlabel('量子比特数', fontsize=12)
ax.set_ylabel('内存使用 (复数)', fontsize=12)
ax.set_title('内存效率对比', fontsize=14, fontweight='bold')
ax.set_yscale('log')
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("\n内存使用对比:")
print("-" * 60)
for nq, mem_std, mem_mps in zip(nqubits, memory_standard, memory_mps):
reduction = mem_std / mem_mps
print(f"{nq} 量子比特: 标准={mem_std:>8}, MPS={mem_mps:>8}, 节省={reduction:>6.1f}x")
In [ ]:
Copied!
# 生成半月形数据集
X_moons, y_moons = make_moons(
n_samples=500,
noise=0.15,
random_state=42
)
# 标准化
scaler_moons = StandardScaler()
X_moons = scaler_moons.fit_transform(X_moons)
# 划分数据集
X_train_m, X_test_m, y_train_m, y_test_m = train_test_split(
X_moons, y_moons, test_size=0.2, random_state=42
)
# 扩展特征
X_train_m_exp = expand_features(X_train_m, 4)
X_test_m_exp = expand_features(X_test_m, 4)
X_train_m_torch = torch.FloatTensor(X_train_m_exp)
y_train_m_torch = torch.LongTensor(y_train_m)
X_test_m_torch = torch.FloatTensor(X_test_m_exp)
y_test_m_torch = torch.LongTensor(y_test_m)
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
axes[0].scatter(X_train_m[:, 0], X_train_m[:, 1], c=y_train_m,
cmap='coolwarm', alpha=0.6)
axes[0].set_title('半月形训练集', fontsize=14, fontweight='bold')
axes[0].set_xlabel('特征 1', fontsize=12)
axes[0].set_ylabel('特征 2', fontsize=12)
axes[0].grid(True, alpha=0.3)
axes[1].scatter(X_test_m[:, 0], X_test_m[:, 1], c=y_test_m,
cmap='coolwarm', alpha=0.6)
axes[1].set_title('半月形测试集', fontsize=14, fontweight='bold')
axes[1].set_xlabel('特征 1', fontsize=12)
axes[1].set_ylabel('特征 2', fontsize=12)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"训练集: {X_train_m_torch.shape}, 测试集: {X_test_m_torch.shape}")
# 生成半月形数据集
X_moons, y_moons = make_moons(
n_samples=500,
noise=0.15,
random_state=42
)
# 标准化
scaler_moons = StandardScaler()
X_moons = scaler_moons.fit_transform(X_moons)
# 划分数据集
X_train_m, X_test_m, y_train_m, y_test_m = train_test_split(
X_moons, y_moons, test_size=0.2, random_state=42
)
# 扩展特征
X_train_m_exp = expand_features(X_train_m, 4)
X_test_m_exp = expand_features(X_test_m, 4)
X_train_m_torch = torch.FloatTensor(X_train_m_exp)
y_train_m_torch = torch.LongTensor(y_train_m)
X_test_m_torch = torch.FloatTensor(X_test_m_exp)
y_test_m_torch = torch.LongTensor(y_test_m)
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
axes[0].scatter(X_train_m[:, 0], X_train_m[:, 1], c=y_train_m,
cmap='coolwarm', alpha=0.6)
axes[0].set_title('半月形训练集', fontsize=14, fontweight='bold')
axes[0].set_xlabel('特征 1', fontsize=12)
axes[0].set_ylabel('特征 2', fontsize=12)
axes[0].grid(True, alpha=0.3)
axes[1].scatter(X_test_m[:, 0], X_test_m[:, 1], c=y_test_m,
cmap='coolwarm', alpha=0.6)
axes[1].set_title('半月形测试集', fontsize=14, fontweight='bold')
axes[1].set_xlabel('特征 1', fontsize=12)
axes[1].set_ylabel('特征 2', fontsize=12)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"训练集: {X_train_m_torch.shape}, 测试集: {X_test_m_torch.shape}")
9.2 在半月形数据上训练模型¶
In [ ]:
Copied!
# 创建模型
qnn_moons = ImprovedQNN(nqubit=4, nlayer=3)
qresnet_moons = QResNet(nqubit=4, nblocks=2, nlayer=2, num_classes=2)
classical_moons = ClassicalNN(input_dim=4, hidden_dims=[16, 8], num_classes=2)
# 训练 QNN
print("训练 QNN (半月形数据)")
print("-" * 60)
qnn_history_m = train_model(
qnn_moons,
X_train_m_torch, y_train_m_torch,
X_test_m_torch, y_test_m_torch,
epochs=40,
lr=0.01,
batch_size=32
)
# 训练 QResNet
print("\n训练 QResNet (半月形数据)")
print("-" * 60)
qresnet_history_m = train_model(
qresnet_moons,
X_train_m_torch, y_train_m_torch,
X_test_m_torch, y_test_m_torch,
epochs=40,
lr=0.01,
batch_size=32
)
# 训练经典 NN
print("\n训练经典 NN (半月形数据)")
print("-" * 60)
classical_history_m = train_model(
classical_moons,
X_train_m_torch, y_train_m_torch,
X_test_m_torch, y_test_m_torch,
epochs=40,
lr=0.01,
batch_size=32
)
# 创建模型
qnn_moons = ImprovedQNN(nqubit=4, nlayer=3)
qresnet_moons = QResNet(nqubit=4, nblocks=2, nlayer=2, num_classes=2)
classical_moons = ClassicalNN(input_dim=4, hidden_dims=[16, 8], num_classes=2)
# 训练 QNN
print("训练 QNN (半月形数据)")
print("-" * 60)
qnn_history_m = train_model(
qnn_moons,
X_train_m_torch, y_train_m_torch,
X_test_m_torch, y_test_m_torch,
epochs=40,
lr=0.01,
batch_size=32
)
# 训练 QResNet
print("\n训练 QResNet (半月形数据)")
print("-" * 60)
qresnet_history_m = train_model(
qresnet_moons,
X_train_m_torch, y_train_m_torch,
X_test_m_torch, y_test_m_torch,
epochs=40,
lr=0.01,
batch_size=32
)
# 训练经典 NN
print("\n训练经典 NN (半月形数据)")
print("-" * 60)
classical_history_m = train_model(
classical_moons,
X_train_m_torch, y_train_m_torch,
X_test_m_torch, y_test_m_torch,
epochs=40,
lr=0.01,
batch_size=32
)
9.3 决策边界可视化¶
In [ ]:
Copied!
# 定义决策边界绘制函数
def plot_decision_boundary(model, X, y, title, ax):
"""
绘制决策边界
参数:
model: 训练好的模型
X: 特征数据 (原始 2D)
y: 标签
title: 图标题
ax: matplotlib 轴对象
"""
# 创建网格
h = 0.02
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
# 扩展特征
grid_points = np.c_[xx.ravel(), yy.ravel()]
grid_expanded = expand_features(grid_points, 4)
grid_tensor = torch.FloatTensor(grid_expanded)
# 预测
model.eval()
with torch.no_grad():
Z = model(grid_tensor)
Z = torch.softmax(Z, dim=1)[:, 1].numpy()
Z = Z.reshape(xx.shape)
# 绘制决策边界
ax.contourf(xx, yy, Z, levels=50, alpha=0.6, cmap='coolwarm')
ax.contour(xx, yy, Z, levels=[0.5], colors='black', linewidths=2)
ax.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='k')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.set_xlabel('特征 1', fontsize=12)
ax.set_ylabel('特征 2', fontsize=12)
# 绘制所有模型的决策边界
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
plot_decision_boundary(qnn_moons, X_test_m, y_test_m, 'QNN 决策边界', axes[0])
plot_decision_boundary(qresnet_moons, X_test_m, y_test_m, 'QResNet 决策边界', axes[1])
plot_decision_boundary(classical_moons, X_test_m, y_test_m, '经典 NN 决策边界', axes[2])
plt.tight_layout()
plt.show()
# 定义决策边界绘制函数
def plot_decision_boundary(model, X, y, title, ax):
"""
绘制决策边界
参数:
model: 训练好的模型
X: 特征数据 (原始 2D)
y: 标签
title: 图标题
ax: matplotlib 轴对象
"""
# 创建网格
h = 0.02
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
# 扩展特征
grid_points = np.c_[xx.ravel(), yy.ravel()]
grid_expanded = expand_features(grid_points, 4)
grid_tensor = torch.FloatTensor(grid_expanded)
# 预测
model.eval()
with torch.no_grad():
Z = model(grid_tensor)
Z = torch.softmax(Z, dim=1)[:, 1].numpy()
Z = Z.reshape(xx.shape)
# 绘制决策边界
ax.contourf(xx, yy, Z, levels=50, alpha=0.6, cmap='coolwarm')
ax.contour(xx, yy, Z, levels=[0.5], colors='black', linewidths=2)
ax.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='k')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.set_xlabel('特征 1', fontsize=12)
ax.set_ylabel('特征 2', fontsize=12)
# 绘制所有模型的决策边界
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
plot_decision_boundary(qnn_moons, X_test_m, y_test_m, 'QNN 决策边界', axes[0])
plot_decision_boundary(qresnet_moons, X_test_m, y_test_m, 'QResNet 决策边界', axes[1])
plot_decision_boundary(classical_moons, X_test_m, y_test_m, '经典 NN 决策边界', axes[2])
plt.tight_layout()
plt.show()
10. 总结与展望¶
10.1 关键要点总结¶
量子机器学习的优势¶
- 高维特征空间: 量子态的高维希尔伯特空间可以编码复杂特征
- 非线性变换: 量子纠缠提供强大的非线性映射能力
- 参数效率: 通常需要更少的参数就能达到相似性能
- 量子加速: 在特定任务上可能实现指数级加速
量子机器学习的挑战¶
- 硬件限制: 当前量子设备的噪声和量子比特数限制
- 训练困难: 梯度消失、局部最优等问题
- 经典模拟开销: 经典模拟量子电路的计算成本高
- 可解释性: 量子模型的决策过程难以解释
实现技巧¶
- 数据编码: 选择合适的数据编码方式对性能至关重要
- 电路设计: 使用强可展性电路避免梯度消失
- 正则化: 适当的正则化可以防止过拟合
- 混合架构: 结合经典和量子层的混合模型通常效果更好
10.2 模型对比¶
| 模型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| QNN | 结构简单,易于实现 | 表达能力有限 | 简单分类任务 |
| QResNet | 训练稳定,可深度堆叠 | 参数较多 | 复杂模式识别 |
| QCNN | 层次特征提取 | 结构固定 | 图像类数据 |
| 经典 NN | 训练快速,工具成熟 | 参数需求大 | 大规模数据 |
10.3 实用建议¶
何时使用量子机器学习?¶
- 数据维度适中 (4-20 维)
- 样本量较小 (<10000)
- 需要模型可解释性
- 有量子计算资源或高效模拟器
何时使用经典机器学习?¶
- 大规模数据集 (>10000 样本)
- 高维数据 (>100 维)
- 对训练速度要求高
- 需要成熟的生产环境
10.4 未来发展方向¶
算法创新¶
- 自适应量子电路: 动态调整电路结构
- 量子迁移学习: 预训练量子模型
- 量子元学习: 学习如何学习
- 量子联邦学习: 分布式量子机器学习
硬件发展¶
- 错误缓解: 减少量子噪声影响
- 量子纠错: 实现可靠的量子计算
- 专用量子处理器: 针对 QML 优化的硬件
- 云量子计算: 便捷的量子计算访问
应用拓展¶
- 量子金融: 投资组合优化、风险分析
- 量子药物发现: 分子性质预测
- 量子自然语言处理: 语义理解
- 量子计算机视觉: 图像识别
10.5 学习资源¶
推荐论文¶
- Mitarai et al., "Quantum circuit learning" (2018)
- Schuld et al., "Evaluating analytic gradients on quantum hardware" (2019)
- Grant et al., "Hierarchical quantum classifiers" (2018)
- Beer et al., "Training deep quantum neural networks" (2020)
在线资源¶
- Pennylane: 量子机器学习框架
- Qiskit Machine Learning: IBM 的 QML 库
- TensorFlow Quantum: Google 的量子 ML 库
- DeepQuantum: 本教程使用的库
练习建议¶
- 从简单数据集开始 (本教程的例子)
- 尝试不同的编码方式
- 实验各种电路架构
- 对比量子与经典方法
- 探索混合量子-经典模型
11. 练习题¶
练习 1: 不同的编码方式¶
实现使用 RX 门而非 RY 门的数据编码,并比较性能差异。
练习 2: 自定义量子电路¶
设计自己的变分量子电路 (ansatz),包括:
- 不同的纠缠模式 (全连接 vs 线性)
- 不同的层数
- 不同的旋转门组合
练习 3: 多分类问题¶
修改代码以处理 3 个或更多类别的分类任务。
练习 4: 回归任务¶
实现量子神经网络解决回归问题 (预测连续值)。
练习 5: 超参数优化¶
使用网格搜索或随机优化找到最佳超参数组合:
- 学习率
- 电路层数
- 批量大小
- 优化器类型
练习 6: 真实数据集¶
使用真实世界的数据集 (如 Iris, Wine, Breast Cancer) 训练量子模型。
练习 7: 量子卷积网络¶
使用 DeepQuantum 的 QuantumConvolutionalNeuralNetwork 解决图像分类问题。
练习 8: MPS 加速¶
比较使用 MPS 和标准表示在大规模量子电路上的性能差异。
恭喜你完成了量子机器学习入门教程!
你已经学习了:
- 量子神经网络 (QNN) 的原理和实现
- 量子残差网络 (QResNet) 的设计
- 变分量子电路的训练方法
- 量子与经典机器学习的对比
- DeepQuantum 库的高级特性
继续探索量子机器学习的奇妙世界吧!如有问题,欢迎交流讨论。
教程信息
- 难度: ⭐⭐⭐⭐ (高级)
- 预计时长: 2-3 小时
- 前置知识:
- 量子计算基础 (量子比特、量子门、测量)
- 机器学习基础 (神经网络、反向传播)
- PyTorch 基础
作者: TuringQ 团队
版本: 1.0
日期: 2024