梯度和训练¶
PennyLane提供经典计算与量子计算之间的无缝集成。在PennyLane中编写量子电路,计算量子电路的梯度,并将它们轻松连接到顶尖的科学计算和机器学习库。
训练和接口¶
量子和经典世界之间的桥梁是通过PennyLane与自动微分库的接口提供的。目前,支持四个库:NumPy、PyTorch、JAX和TensorFlow。PennyLane使这些库具有量子意识,允许量子电路像其他操作一样被处理。可以选择任何自动微分框架与任何设备。
在PennyLane中,自动微分框架是在创建QNode时使用interface参数声明的,例如,
@qml.qnode(dev, interface="tf")
def my_quantum_circuit(...):
...
注意
如果未指定接口,PennyLane 将根据提供的参数和关键字参数自动确定接口。请参阅 qml.math.SUPPORTED_INTERFACE_NAMES 以获取所有接受的接口字符串列表。
警告
ComplexWarning 消息可能会在运行涉及复数和浮点数类型的可微分工作流时出现,尤其是在某些接口中。这些警告在反向传播中很常见,因复数转换的特性而产生,并不表示计算错误。如果需要,可以通过添加以下代码来 suppress这些警告:
import warnings
warnings.filterwarnings("ignore", category=np.ComplexWarning)
这将允许指定库的原生数值对象(NumPy 数组、JAX 数组、Torch 张量或 TensorFlow 张量)作为参数传递给量子电路。它还使得量子电路的梯度能够被经典库访问,从而通过利用该库的原生优化器来优化任意混合电路。
当指定接口时,所选择框架的对象通常会转换为NumPy对象,并传递给设备。例外情况包括设备支持框架中的端到端计算。这类设备可能被称为反向传播或直通设备。
请参阅以下链接以了解每个特定接口的操作步骤:
除了上述讨论的核心自动微分框架,PennyLane 还提供了更高级的类,用于将 QNodes 转换为 Keras 和 torch.nn 层:
|
|
|
将一个 |
注意
允许自动微分的QNodes在评估时总会产生小的开销。如果您不需要计算QNode的量子梯度,指定 interface=None 将会消除这一开销,从而使评估速度稍快。然而,梯度将不再可用。
优化器¶
优化器是可以用于自动更新量子或混合机器学习模型参数的对象。你应该使用的优化器取决于你选择的经典自动微分库,并且可以从不同的访问点获取。
NumPy¶
当使用标准的NumPy框架时,PennyLane提供了一些内置的优化器。其中一些是特定于量子优化的,例如QNGOptimizer,RiemannianGradientOptimizer,RotosolveOptimizer,RotoselectOptimizer,ShotAdaptiveOptimizer,以及QNSPSAOptimizer。
每个维度中依赖过去梯度的学习率的梯度下降优化器。 |
|
具有自适应学习率的一阶和二阶动量的梯度下降优化器。 |
|
用于通过自适应添加门来构建完全训练的量子电路的优化器。 |
|
基本的梯度下降优化器。 |
|
具有动量的梯度下降优化器。 |
|
具有Nesterov动量的梯度下降优化器。 |
|
具有自适应学习率的优化器,通过计算Fubini-Study度量张量的对角线或块对角线近似。 |
|
黎曼梯度优化器。 |
|
均方根传播优化器。 |
|
Rotosolve 无需梯度的优化器。 |
|
Rotoselect 无梯度优化器。 |
|
优化器,其中射击率是根据参数-移位梯度的方差自适应计算的。 |
|
同时扰动随机逼近方法(SPSA)是一种随机逼近算法,用于优化可能涉及噪声的成本函数的评估。 |
|
量子自然SPSA(QNSPSA)优化器。 |
PyTorch¶
如果你正在使用PennyLane PyTorch框架,你应该导入其中一个本地PyTorch优化器(可以在torch.optim中找到)。
TensorFlow¶
使用PennyLane TensorFlow框架时,您需要利用其中一个TensorFlow优化器(位于tf.keras.optimizers中)。
JAX¶
查看JAXopt和Optax包,以找到适用于PennyLane JAX框架的优化器。
梯度¶
PennyLane与自动微分库之间的接口依赖于PennyLane计算或估计量子电路梯度的能力。实现这一点有不同的策略,这些策略可能依赖于所使用的设备。
在创建 QNode 时,您可以像这样指定 微分方法:
@qml.qnode(dev, diff_method="parameter-shift")
def circuit(x):
qml.RX(x, wires=0)
return qml.probs(wires=0)
PennyLane 目前为 QNodes 提供以下微分方法:
基于模拟的微分¶
以下方法使用 反向累积 来计算梯度;这种方法的一个著名例子是反向传播。这些方法 不 兼容硬件;它们仅在 状态向量 模拟器设备上支持,例如 default.qubit。
然而,对于在模拟器上进行快速原型设计,这些方法通常比正向模式累计器(如参数偏移规则和有限差分)表现更好。有关更多详细信息,请参见量子反向传播演示。
"backprop": 使用标准反向传播。这种微分方法仅允许在经典的端到端可微分的模拟器设备上使用,例如
default.qubit。此方法对使用有限次数拍摄估计测量统计的设备不适用;请改用parameter-shift规则。"adjoint": 使用一种利用量子计算的单位或可逆特性的反向传播形式。该adjoint method 在正向传播后通过迭代应用逆(伴随)门来反向通过电路。此方法类似于
"backprop",但具有显著更低的内存使用量和相似的运行时间。
硬件兼容的微分¶
以下方法支持量子硬件和模拟器,并且是正向积累的例子。然而,当使用模拟器时,您可能会注意到,使用这些方法计算梯度所需的电路执行次数与可训练电路参数的数量呈线性关系。
"parameter-shift":对所有支持的量子操作参数使用解析的 parameter-shift rule,以有限差分作为后备。"finite-diff": 对所有量子操作参数使用数值有限差分。"hadamard": 对所有兼容的量子操作参数在生成器上使用hadamard测试。qml.gradients.stoch_pulse_grad: 使用脉冲程序的参数移位规则的随机变体。qml.gradients.pulse_odegen: 将经典处理与参数偏移规则结合,用于多变量门来区分脉冲程序。
设备梯度¶
"device": 直接查询设备以获取梯度。仅允许在提供自己梯度计算的设备上使用。
注意
如果未指定,默认的微分方法是 diff_method="best"。 PennyLane 将尝试根据设备和接口确定 最佳 微分方法。通常,PennyLane 将优先使用设备提供的梯度、反向传播、参数偏移规则,最后才是有限差分,顺序如下。
梯度变换¶
除了注册用于自动微分框架的QNodes的微分方法外,PennyLane还通过qml.gradients模块提供了一系列梯度变换库。
量子梯度变换是计算量子电路梯度的策略,它通过转化量子电路为一个或多个梯度电路来工作。它们为这些电路配备了一个后处理其输出的函数。这些梯度电路在执行和后处理后,返回原始电路的梯度。
量子梯度变换的示例包括有限差分法则和参数变换法则;这些可以直接应用于QNodes:
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def circuit(weights):
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
qml.RX(weights[2], wires=1)
return qml.probs(wires=1)
>>> weights = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> circuit(weights)
tensor([0.9658079, 0.0341921], requires_grad=True)
>>> qml.gradients.param_shift(circuit)(weights)
(tensor([-0.04673668, 0.04673668], requires_grad=True),
tensor([-0.09442394, 0.09442394], requires_grad=True),
tensor([-0.14409127, 0.14409127], requires_grad=True))
请注意,尽管梯度变换允许量子梯度规则直接应用于QNodes,但这并不是替代品——不应该替代——标准训练工作流程(例如,如果使用Autograd,则使用qml.grad(),对于PyTorch使用loss.backward(),或者对于TensorFlow使用tape.gradient())。这是因为梯度变换不考虑经典计算节点,仅支持QNodes的梯度。有关可用梯度变换的更多详细信息,以及如何定义您自己的梯度变换,请参见qml.gradients文档。
区分梯度变换和高阶导数¶
梯度变换本身是可微的,允许计算高阶梯度:
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def circuit(weights):
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
qml.RX(weights[2], wires=1)
return qml.expval(qml.PauliZ(1))
>>> weights = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> circuit(weights)
tensor(0.9316158, requires_grad=True)
>>> qml.gradients.param_shift(circuit)(weights) # gradient
(tensor(-0.09347337, requires_grad=True),
tensor(-0.18884787, requires_grad=True),
tensor(-0.28818254, requires_grad=True))
>>> def f(weights):
... return np.stack(qml.gradients.param_shift(circuit)(weights))
>>> qml.jacobian(f)(weights) # hessian
array([[[-0.9316158 , 0.01894799, 0.0289147 ],
[ 0.01894799, -0.9316158 , 0.05841749],
[ 0.0289147 , 0.05841749, -0.9316158 ]]])
计算高阶导数的另一种方法是将 max_diff 和 diff_method 参数传递给 QNode,并通过连续求导来实现:
@qml.qnode(dev, diff_method="parameter-shift", max_diff=2)
def circuit(weights):
qml.RX(weights[0], wires=0)
qml.RY(weights[1], wires=1)
qml.CNOT(wires=[0, 1])
qml.RX(weights[2], wires=1)
return qml.expval(qml.PauliZ(1))
>>> weights = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> qml.jacobian(qml.jacobian(circuit))(weights) # hessian
array([[-0.9316158 , 0.01894799, 0.0289147 ],
[ 0.01894799, -0.9316158 , 0.05841749],
[ 0.0289147 , 0.05841749, -0.9316158 ]])
请注意,max_diff 参数仅适用于梯度变换,其默认值为 1;未正确设置其值可能会导致高阶导数的结果不正确。此外,传递 diff_method="parameter-shift" 等同于传递 diff_method=qml.gradients.param_shift。
支持的配置¶
下表显示了当前支持的所有功能,用于 "default.qubit" 设备。
目前,该设备考虑以下参数:
接口,例如
"jax"微分方法,例如
"parameter-shift"QNode的返回值,例如
qml.expval()或qml.probs()发射的数量,可以是 None 或大于 0 的整数
返回类型 |
|||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
接口 |
微分方法 |
状态 |
密度矩阵 |
概率 |
样本 |
期望值 (obs) |
期望值 (herm) |
期望值 (proj) |
方差 |
vn 熵 |
互信息 |
||||||||
|
|
1 |
1 |
1 |
9 |
1 |
1 |
1 |
1 |
1 |
1 |
||||||||
|
1 |
1 |
1 |
9 |
1 |
1 |
1 |
1 |
1 |
1 |
|||||||||
|
2 |
2 |
2 |
9 |
2 |
2 |
2 |
2 |
2 |
2 |
|||||||||
|
2 |
2 |
2 |
9 |
2 |
2 |
2 |
2 |
2 |
2 |
|||||||||
|
2 |
2 |
2 |
9 |
2 |
2 |
2 |
2 |
2 |
2 |
|||||||||
|
2 |
2 |
2 |
9 |
2 |
2 |
2 |
2 |
2 |
2 |
|||||||||
|
2 |
2 |
2 |
9 |
2 |
2 |
2 |
2 |
2 |
2 |
|||||||||
|
|
3 |
3 |
3 |
9 |
3 |
3 |
3 |
3 |
3 |
3 |
||||||||
|
4 |
4 |
5 |
9 |
5 |
5 |
5 |
5 |
5 |
5 |
|||||||||
|
7 |
7 |
7 |
9 |
7 |
7 |
7 |
7 |
7 |
7 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
10 |
10 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
11 |
10 |
10 |
|||||||||
|
|
3 |
3 |
3 |
9 |
3 |
3 |
3 |
3 |
3 |
3 |
||||||||
|
5 |
5 |
5 |
9 |
5 |
5 |
5 |
5 |
5 |
5 |
|||||||||
|
7 |
7 |
7 |
9 |
7 |
7 |
7 |
7 |
7 |
7 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
10 |
10 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
11 |
10 |
10 |
|||||||||
|
|
3 |
3 |
3 |
9 |
3 |
3 |
3 |
3 |
3 |
3 |
||||||||
|
5 |
5 |
5 |
9 |
5 |
5 |
5 |
5 |
5 |
5 |
|||||||||
|
7 |
7 |
7 |
9 |
7 |
7 |
7 |
7 |
7 |
7 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
10 |
10 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
11 |
10 |
10 |
|||||||||
|
|
3 |
3 |
3 |
9 |
3 |
3 |
3 |
3 |
3 |
3 |
||||||||
|
5 |
5 |
5 |
9 |
5 |
5 |
5 |
5 |
5 |
5 |
|||||||||
|
7 |
7 |
7 |
9 |
7 |
7 |
7 |
7 |
7 |
7 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
10 |
10 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
8 |
8 |
8 |
|||||||||
|
10 |
10 |
8 |
9 |
8 |
8 |
8 |
11 |
10 |
10 |
|||||||||
不支持。尽管提供了
diff_method,但仍未计算梯度。失败并出现错误。不支持。即使提供了
diff_method,仍然不会计算梯度。警告没有使用自动微分框架,但不会失败。 前向传播仍然受到支持。不支持。
default.qubit设备没有提供计算梯度的原生方式。详情请参见 设备雅可比矩阵。支持,但仅在
shots=None时。有关详细信息,请参见 反向传播。如果电路返回一个状态,则电路本身是不可直接微分的。然而,对电路输出进行的任何实数标量值后处理将是可微分的。有关详细信息,请参见 状态梯度。
支持,但仅在
shots=None时可用。详情见 反向传播。不支持。伴随微分算法仅在解析模拟中实现。详见 伴随微分。
支持。当
shots>0时会引发错误,因为梯度总是通过解析方式计算。详情请参阅 伴随微分。支持。
不支持。由波函数坍缩引起的输出离散化是不可微分的。前向传递仍然支持。有关详细信息,请参见 示例梯度。
不支持。“我们只是还没有理论。”
未实现。