数据编码
在这一页中,我们将介绍量子机器学习的数据编码问题,然后描述和实现各种数据编码方法。
介绍
数据表示对于机器学习模型的成功至关重要。对于经典机器学习来说,问题是如何用数字表示数据,以便经典机器学习算法对数据进行最好的处理。
对于量子机器学习来说,这个问题是类似的,但更基本:如何将数据表示并有效地输入到量子系统中,从而可以通过量子机器学习算法进行处理。这通常称为数据编码,但也称为数据嵌入或加载。
这个过程是量子机器学习算法的关键部分,直接影响它们的计算能力。
方法
考虑一个经典的数据集包含了 M M M个样本,每个样本包含了 N N N个特征:
X = { x ( 1 ) , ⋯ , x ( i ) , ⋯ , x ( M ) } \mathscr X = \{x^{(1)},\cdots,x^{(i)},\cdots,x^{(M)}\} X={x(1),⋯,x(i),⋯,x(M)}
其中 x ( i ) ( i = 1 , ⋯ , M ) x^{(i)}(i=1,\cdots,M) x(i)(i=1,⋯,M)是 N N N维向量。为了在量子比特系统中表示此数据集,我们可以使用各种嵌入技术,其中一些技术将在下面简要解释和实现,参见参考文献1和2
基编码(Basis encoding)
基编码将经典
N
N
N比特字符串与一个
N
N
N量子比特系统计算基状态相关联。例如,如果
x
=
5
x=5
x=5,这可以表示为一个4比特字符串
0101
0101
0101,并通过4量子位系统表示为量子态。更一般地说,对于
N
N
N比特字符串
x
=
(
b
1
,
b
2
,
⋯
,
b
N
)
x=(b_1,b_2,\cdots,b_N)
x=(b1,b2,⋯,bN),其对应的
N
N
N量子比特态为:
∣
x
>
=
∣
b
1
,
b
2
,
⋯
,
b
N
>
,
b
n
∈
{
0
,
1
}
,
n
=
1
,
⋯
,
N
\left|x\right>=\left|b_1,b_2,\cdots,b_N\right>,b_n\in\{0,1\},n=1,\cdots,N
∣x⟩=∣b1,b2,⋯,bN⟩,bn∈{0,1},n=1,⋯,N
对于上面描述的经典数据集
X
\mathscr X
X,使用基编码,每个数据点都必须是一个N比特字符串
x
(
m
)
=
(
b
1
,
b
2
,
⋯
,
b
N
)
x^{(m)}=(b_1,b_2,\cdots,b_N)
x(m)=(b1,b2,⋯,bN)。它会直接映射到量子态
∣
x
m
>
=
∣
b
1
,
b
2
,
⋯
,
b
N
>
\left|x^m\right>=\left|b_1,b_2,\cdots,b_N\right>
∣xm⟩=∣b1,b2,⋯,bN⟩.于是我们可以把数据集
X
\mathscr X
X写成计算基量子态的叠加:
∣
X
>
=
1
M
∑
m
=
1
M
∣
x
m
>
\left|\mathscr X\right>=\dfrac{1}{\sqrt M}\sum^M_{m=1}\left|x^m \right>
∣X⟩=M1m=1∑M∣xm⟩
在Qiskit中,一旦我们计算可以编码数据集的量子态,我们可以使用initialize函数来制备量子态。例如,数据集 X = { x ( 1 ) = 101 , x ( 2 ) = 111 } \mathscr X = \{x^{(1)}=101,x^{(2)}=111\} X={x(1)=101,x(2)=111}就可以编码为量子态 ∣ X > = 1 2 ( ∣ 101 > + ∣ 111 > ) \left|\mathscr X\right>=\dfrac{1}{\sqrt{2}}(\left|101\right>+\left|111\right>) ∣X⟩=21(∣101⟩+∣111⟩):
import math
from qiskit import QuantumCircuit
desired_state = [
0,
0,
0,
0,
0,
1 / math.sqrt(2),
0,
1 / math.sqrt(2)]
qc = QuantumCircuit(3)
qc.initialize(desired_state, [0,1,2])
qc.decompose().decompose().decompose().decompose().decompose().draw()
这个例子说明了基编码的几个缺点。虽然它很容易理解,但状态向量可能变得非常稀疏,实现它的方案通常效率不高。
振幅编码(Amplitude encoding)
幅度编码将数据编码成量子态的幅度。它表示一个归一化的经典
N
N
N维数据点,作为一个
n
n
n量子比特量子态
∣
ψ
x
>
\left|\psi_x\right>
∣ψx⟩的振幅:
∣
ψ
x
>
=
∑
i
=
1
N
x
i
∣
i
>
,
N
=
2
n
\left|\psi_x\right>=\sum^N_{i=1}x_i\left|i\right>,N=2^n
∣ψx⟩=i=1∑Nxi∣i⟩,N=2n
其中
x
i
x_i
xi是
x
x
x的第
i
i
i个元素,
∣
i
>
\left|i\right>
∣i⟩是第
i
i
i个计算基。
为了编码上述经典数据集 X \mathscr X X,我们合并所有 M M M个 N N N为数据点到振幅向量上,它的长度因此就是 N × M N\times M N×M。
α = A n o r m ( x 1 ( 1 ) , ⋯ , x N ( 1 ) , ⋯ , x 1 ( m ) , ⋯ , x N ( m ) , ⋯ , x 1 ( M ) , ⋯ , x N ( M ) ) \alpha=A_{norm}(x^{(1)}_1,\cdots,x^{(1)}_N,\cdots,x^{(m)}_1,\cdots,x^{(m)}_N,\cdots,x^{(M)}_1,\cdots,x^{(M)}_N) α=Anorm(x1(1),⋯,xN(1),⋯,x1(m),⋯,xN(m),⋯,x1(M),⋯,xN(M))
式中
A
(
n
o
r
m
)
A_{(norm)}
A(norm)是归一化常数,使得
∣
α
∣
2
=
1
|\alpha|^2=1
∣α∣2=1。数据集
X
\mathscr X
X现在就可以在计算基下表示成
∣
X
>
=
∑
i
=
1
N
α
i
∣
i
>
\left|\mathscr X\right>=\sum^N_{i=1}\alpha_i\left|i\right>
∣X⟩=i=1∑Nαi∣i⟩
其中
α
i
\alpha_i
αi就是振幅向量的元素。振幅个数被编码为
N
×
M
N\times M
N×M个。由于一个
n
n
n量子比特系统可以提供
2
n
2^n
2n种振幅,振幅的嵌入就需要
n
≥
log
2
(
N
M
)
n\geq \log_2(NM)
n≥log2(NM)个量子比特。
例如,我们试图使用振幅编码来编码
X
=
{
x
(
1
)
=
(
1.5
,
0
)
,
x
(
2
)
=
(
−
2
,
3
)
}
\mathscr X=\{x^{(1)}=(1.5,0),x^{(2)}=(-2,3)\}
X={x(1)=(1.5,0),x(2)=(−2,3)}。把两组数据点合并后归一化得到
α
=
1
15.25
(
1.5
,
0
,
−
2
,
3
)
\alpha=\dfrac{1}{\sqrt{15.25}}(1.5,0,-2,3)
α=15.251(1.5,0,−2,3)
于是双量子比特态即
∣
X
>
=
1
15.25
(
1.5
∣
00
>
−
2
∣
10
>
+
3
∣
11
>
)
\left|\mathscr X\right>=\dfrac{1}{\sqrt{15.25}}(1.5\left|00\right>-2\left|10\right>+3\left|11\right>)
∣X⟩=15.251(1.5∣00⟩−2∣10⟩+3∣11⟩)
在上例中,振幅向量的元素个数,
N
×
M
N\times M
N×M,是2的指数。当
N
×
M
N\times M
N×M不是2的指数时,我们可以任意选择一个值作为
n
n
n,只要
2
n
≥
N
M
2^n\geq NM
2n≥NM,并且用无信息常数填充振幅矢量。
就像在基编码中一样,一旦我们计算了哪种状态会编码我们的数据集,在Qiskit中我们可以使用initialize函数来制备它:
desired_state = [
1 / math.sqrt(15.25) * 1.5,
0,
1 / math.sqrt(15.25) * -2,
1 / math.sqrt(15.25) * 3]
qc = QuantumCircuit(2)
qc.initialize(desired_state, [0,1])
qc.decompose().decompose().decompose().decompose().decompose().draw()
幅度编码的优点是它只需要 log 2 ( N M ) \log_2(NM) log2(NM)个要编码的量子比特。然而,后续算法必须在量子态的振幅上运行,而制备和测量量子态的方法往往效率不高。
角度编码(Angle encoding)
角度编码将
N
N
N个特征编码为
n
n
n个量子比特的旋转角度,其中
N
≤
n
N\leq n
N≤n。例如,数据点
x
=
(
x
1
,
⋯
,
x
N
)
x=(x_1,\cdots,x_N)
x=(x1,⋯,xN)可以编码为:
∣
x
>
=
⨂
i
=
1
N
cos
(
x
i
)
∣
0
>
+
sin
(
x
i
)
∣
1
>
\left|x\right>=\bigotimes^N_{i=1}\cos(x_i)\left|0\right>+\sin(x_i)\left|1\right>
∣x⟩=i=1⨂Ncos(xi)∣0⟩+sin(xi)∣1⟩
这与前两种编码方法不同,因为它一次只编码一个数据点,而不是整个数据集。然而,它只需要N个或更少的量子比特和一个恒定深度的量子电路,使其适合当前的量子硬件。
我们可以指定角度编码为一种幺正变换:
S
x
j
=
⨂
i
=
1
N
U
(
x
j
(
i
)
)
S_{x_j}=\bigotimes^N_{i=1}U(x^{(i)}_j)
Sxj=i=1⨂NU(xj(i))
其中
U
(
x
j
(
i
)
)
=
[
cos
(
x
i
)
−
sin
(
x
i
)
sin
(
x
i
)
cos
(
x
i
)
]
U(x^{(i)}_j)=\bigg[\begin{matrix}\cos(x_i)&&-\sin(x_i)\\\sin(x_i)&&\cos(x_i)\end{matrix}\bigg]
U(xj(i))=[cos(xi)sin(xi)−sin(xi)cos(xi)]
注意到单比特的
Y
Y
Y轴旋转门是
R
Y
(
θ
)
=
exp
(
−
i
θ
2
Y
)
=
(
cos
(
θ
2
)
−
sin
(
θ
2
)
sin
(
θ
2
)
cos
(
θ
2
)
)
RY(\theta)=\exp(-i\dfrac{\theta}{2}Y)=\bigg(\begin{matrix}\cos(\dfrac{\theta}{2})&&-\sin(\dfrac{\theta}{2})\\\sin(\dfrac{\theta}{2})&&\cos(\dfrac{\theta}{2})\end{matrix}\bigg)
RY(θ)=exp(−i2θY)=(cos(2θ)sin(2θ)−sin(2θ)cos(2θ))
因此我们有 U ( x j ( i ) = R Y ( 2 x j ( i ) ) U(x^{(i)}_j=RY(2x^{(i)}_j) U(xj(i)=RY(2xj(i))。例如,我们打算在Qiskit中编码数据点 x = ( 0 , π / 4 , π / 2 ) x=(0,\pi/4,\pi/2) x=(0,π/4,π/2):
qc = QuantumCircuit(3)
qc.ry(0, 0)
qc.ry(2*math.pi/4, 1)
qc.ry(2*math.pi/2, 2)
qc.draw()
稠密角度编码(dense angle encoding)是角度编码的一个简单推广,它使用相对相位(其中数据点为
x
=
(
x
1
,
⋯
,
x
N
)
x=(x_1,\cdots,x_N)
x=(x1,⋯,xN))编码每个量子位的两个特征
为:
∣
x
>
=
⨂
i
=
1
N
/
2
cos
(
x
2
i
−
1
)
∣
0
>
+
e
i
x
2
i
sin
(
x
2
i
−
1
)
∣
1
>
\left|x\right>=\bigotimes^{N/2}_{i=1}\cos(x_{2i-1})\left|0\right>+e^{ix_{2i}}\sin(x_{2i-1})\left|1\right>
∣x⟩=i=1⨂N/2cos(x2i−1)∣0⟩+eix2isin(x2i−1)∣1⟩
虽然角度和稠密角度编码使用正弦波和指数,但这些函数没有什么特别之处。我们可以很容易地将这些抽象为使用任意函数的一般量子比特编码,或者将编码定义为任意一元,实现为参数化量子电路。
任意编码(Arbitrary encoding)
任意编码将 N N N个特征编码为 n n n个量子比特上带有 N N N个参数的门上的旋转,其中 n ≤ N n\leq N n≤N。与角度度编码一样,它一次只编码一个数据点,而不是整个数据集。它还使用恒定深度量子电路和 n ≤ N n\leq N n≤N个量子位,这意味着它可以在当前的量子硬件上运行。
from qiskit.circuit.library import EfficientSU2
circuit = EfficientSU2(num_qubits=3, reps=1, insert_barriers=True)
circuit.decompose().draw()
在这里,我们用12个特征编码数据点 x = [ 0.1 , 0.2 , 0.3 , 0.4 , 0.5 , 0.6 , 0.7 , 0.8 , 0.9 , 1.0 , 1.1 , 1.2 ] x=[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2] x=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2],使用每个参数化门来编码不同的特征。
x = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2]
encode = circuit.bind_parameters(x)
encode.decompose().draw()
Qiskit ZZFeatureMap电路具有3个量子位,尽管具有6个参数化门,但仅编码3个特征的数据点:
from qiskit.circuit.library import ZZFeatureMap
circuit = ZZFeatureMap(3, reps=1, insert_barriers=True)
circuit.decompose().draw()
x = [0.1, 0.2, 0.3]
encode = circuit.bind_parameters(x)
encode.decompose().draw()
不同参数化量子电路在不同类型数据上的性能是一个活跃的研究领域。