其他
【新手指南】用 Python 实现量子机器学习(附代码)
最近,我们目睹了算法如何学会驾驶汽车、如何击败国际象棋和围棋的世界冠军。从军事到航空航天、从农业到制造业、从金融到医疗保健,几乎所有可以想象到的领域都在应用机器学习。
但是,由于这些算法由数十亿个参数组成,因此越来越难以训练。量子计算机有望解决当前计算技术难以解决的问题:它拥有同时计算多个状态的能力使其能够并行执行无限数量的叠加任务;这种能力有望改进、加速机器学习技术。
与基于顺序信息处理的经典计算机不同,量子计算利用了量子物理学的特性,例如局域性、纠缠和干涉。但量子计算并没有增加可用的计算能力,而是降低了解决问题所需的能力。
但量子计算又要求我们改变对计算机的思考方式——它需要一套全新的算法:编码和使用量子信息的算法,这其中就包括机器学习算法。
它还需要一批新的开发人员:了解机器学习和量子计算的开发人员。开发人员有能力解决以前从未解决过的实际问题。
量子机器学习有望带来颠覆性变革。尽管机器学习和量子计算这两个活跃的研究领域的合并在很大程度上还停留在概念领域,但已经有许多应用量子计算解决现实问题的例子:谷歌、亚马逊、IBM、微软和一大批高科技初创公司都在努力成为第一个构建和销售量子机器学习系统的公司。
在一项技术即将证明其优越性的时刻,研究这项技术的机会是独一无二的。
千万不要错过机会。
/目录/
一、NISQ时代的量子机器学习(QML)二、在Python上配置QML算法三、编写第一个量子电路四、搭建参数化量子电路五、运行混合量子-经典算法
机器学习的目标是为尚未被标记的事物贴上标签;主要有三种方法:分类、回归和分割。
- 在分类中,我们试图预测一个实例的离散标签。给定输入和一组可能的标签,哪个是它呢?这是一张图片;是猫还是狗?
- 回归是指找到一个函数来预测某些输入和依赖的连续输出值之间的关系。例如,如果你知道你朋友的收入和实际税率,能根据你的收入估算出税率吗?
- 分割是将人口划分为具有相似特征的群体的过程,这些群体可能会表现出相似的行为。如果您生产一种昂贵的产品(如游艇),并有一批潜在客户,您想向谁销售呢?
现在,有无数种机器学习算法。但每一种算法都有三个组成部分:
1)表示(Representation)描述了算法用于表示知识的内部架构。它可能由一组规则、实例、决策树、支持向量机、神经网络等组成。
2)评估(Evaluation)是对候选算法参数化进行评估的函数。例如,准确率、预测和召回率、平方误差、后验概率、成本、边际、熵等。
3)优化(Optimization)描述了生成候选算法参数化的方法。它被称为搜索过程,包括组合优化、凸优化和约束优化。
机器学习的第一步是开发架构(architecture)和表示。架构规定了参数,这些参数的值是知识的表征;这一步决定了解决方案如何适合解决特定问题。参数并非越多越好。如果我们的问题可以用一个线性函数来解决,那么试图用一个由数百万个参数组成的解决方案来解决这个问题很可能会失败。另一方面,参数极少的架构可能不足以解决复杂问题,例如自然语言理解。
一旦我们确定了表示知识的架构,就可以用实例来训练机器学习算法。根据参数的数量,我们需要大量的样本。算法试图预测每个样本的标签,然后,我们使用评估函数来衡量算法的表现。
最后,优化调整表示参数,以保证在测量评估中获得更好的性能;这甚至可能涉及到改变架构。
学习不是大跃进式的, 而是以微小的步骤进行。为了获得良好的性能,根据问题的复杂程度,工程师需要对这一过程进行多次迭代,直到机器能够对某一事物贴上正确的标签。
量子计算是一种不同的计算形式,可以改变解决问题的复杂性、使问题变得容易解决。但这种不同的计算形式也带来了自己的挑战。
数字计算机需要区分两种状态:0和1,因为电路需要区分高电压和低电压。这种离散化意味着误差必须比较大才会被注意到,而检测和纠正这种误差的方法就可以实现。
与数字计算机不同,量子计算机需要非常精确。它们基于对连续变化参数的精确操作。然而,在量子计算机中,错误可以是任意小的、不可能被检测到,但它们的影响仍然可以累积起来破坏计算。
这种脆弱的量子态非常容易受到来自量子比特周围环境的噪声的影响。噪声可能来自控制电子设备、热量或量子计算机材料本身的杂质,也可能导致难以纠正的严重计算错误。
但为了兑现量子计算机的承诺,我们需要容错设备——需要计算Shor因式分解算法的设备,以解决数字计算机难以解决的问题。但这样的设备需要数百万量子比特。
目前的量子计算机拥有多达433个量子比特。尽管IBM力争到2030年实现10万量子比特的计算机,但预计近期的量子处理器仍将在1000比特左右;即使超过这些数字,它们仍然相对较小且噪音较大。这些计算机只能执行较短的程序:因为程序越长,与噪声相关的输出错误就越多。
然而,超过50量子比特的设备上运行的程序已经很难在经典计算机上模拟。这些设备可以做到经典计算机无法做到的事情。
这就是我们的时代——NISQ时代。在这个时代,我们可以制造出量子计算机,虽然不具备容错能力,但却能做到经典计算机做不到的事情。
当前的NISQ设备时代需要一套不同的算法、工具和策略。例如,变分量子经典算法已经成为思考近期量子设备的量子算法的一种流行方式。在这些算法中,经典计算机通过在量子计算机上运行某些难以计算的计算而获得的信息来执行整个机器学习任务。
量子算法根据经典算法提供的一组参数产生信息。因此,它们被称为参数化量子电路(PQC)。它们相对较小、寿命较短,因此适用于NISQ设备。
众多算法工具中,Python易于学习。其简单的语法可以让人们专注于学习量子机器学习,而不必花时间去研究语言的特殊性。最重要的是,机器学习工具(如PyTorch和Tensorflow),以及量子计算工具(如Qiskit和Cirq)都可以通过Python SDK获得。
在使用量子机器学习和Python时,强烈推荐使用Ubuntu Linux环境,因为所有需要的工具都可以轻松安装和配置。
其他Linux发行版(如Debian)或MacOS(同样基于Unix)也可以。但还有一些方面需要考虑;所有代码也都应能在Windows上运行。然而,Windows工作环境的配置本身就是一个挑战。
我们使用Linux终端完成所有步骤。
1)首先,打开命令行,更新apt-get软件包管理器。
2)下一步下载并安装Python(以Python 3.8.5为例)。
如果你想把这个 Python 版本作为默认版本(default),运行
3)Python 已经准备就绪。现在让我们安装和更新 Python 包管理器 pip:
我们在虚拟环境中安装所有 Python 包。因此,我们需要安装 virtualenv:
要创建虚拟环境,请进入您的项目目录并运行 venv。以下参数(此处为env)指定环境名称:
在开始安装或使用软件包之前,您需要激活您的环境。
通过运行deactivate命令离开您的虚拟环境。如果想重新进入,只需再次调用source env/bin/activate即可。
4)安装我们需要的软件包。
现在,一切配置完成,我们可以开始了!用以下命令打开JupyterLab:
现在,我们将编写第一个量子电路。量子电路是量子比特变换的序列,也即量子程序。
量子力学的世界与众不同,量子系统可以处于叠加状态。一个流行的叠加概念是:除非观察者对其进行测量,否则系统会同时处于不同的状态。
例如,粒子的自旋不是向上或向下,而是同时向上和向下。但是当我们观察它时,又会发现它要么是向上的、要么是向下的。
叠加的另一个概念解释是,系统是真正随机的。例如,抛掷一枚(正常的)硬币似乎是随机的,因为无论何时抛掷,条件都略有不同。即使是微小的差异也会使结果从正面变为反面。
如果我们能够精确测量所有条件,我们就能知道结果。在经典力学中,不存在随机性:我们日常生活中的事物,如硬币,似乎是随机的;但它们并非随机。如果测量无限精确,随机性就会消失。相比之下,量子系统才是真正的随机。
重要的是后果。在一个经典系统中,一个对初始条件敏感的系统,问题的答案在我们提出之前就已经确定了。
与此相反,在量子系统中,问题的答案在提出之前是不确定的。由于答案尚未确定,我们仍然可以改变测量不同状态的概率。
正因如此,爱因斯坦提出了著名的“上帝不掷骰子”论断:量子态虽然是隐藏的,但却是一种定义明确的状态。这就是所谓的隐变量理论。
在统计学上,遵循隐变量理论的系统和遵循叠加原理的量子系统有截然不同的行为。然而,实验表明量子力学的预言是正确的。
我们来看看量子计算机。假设你有一个量子比特,一旦观察到它的值,将得到0或1。量子比特产生任一值的机会并不需要是50:50,它可以是25:75、67:33,甚至100:0。它可以是任何加权概率分布。
量子比特在被观测时的概率分布取决于它的状态:量子态。
在量子力学中,我们使用矢量来描述量子态;表示量子态矢量的一种流行方式是狄拉克符号,看起来像|ψ⟩的结构。在 Python 中,我们没有矢量,但是我们有数组。幸运的是,它们的结构是相似的。
从最简单的情况开始。比方说,我们有一个量子比特,观察时,它的值总是0。在被观测到之前,这个量子比特被观测到时的值为0的概率为1(=100%)。这些是观测到的结果总是0的量子比特的等价表示(矢量、数组):
这在Python中表示为[1, 0]。因此,我们也可以描述一个在观察时结果总是 1 的量子比特:
在Python中表示为[0, 1]。
好了,理论到此为止。让我们来看看这样一个量子比特的代码,测试Qiskit。
如果您收到这样的回复,说明Qiskit有效。很好!我们准备创建第一个量子比特了。
我们的电路由一个量子比特组成(第4行),我们定义[0,1]为量子比特的initial_state(第7行),并用它初始化量子电路的第一个也是唯一一个量子比特(第10行)。
现在是时候启动我们的量子计算机了;或者说,我们可以“模拟”它。
Qiskit提供了Aerpackage(我们在第1行导入)。它为模拟量子电路提供了不同的后端,最常用的后端是statevector_simulator(第4行)。
execute函数(我们也在第1行导入)在指定的后端运行量子电路(qc)。它返回一个job对象,其中有一个有用的方法job.result()。一旦我们的程序完成,它将返回结果对象。
让我们来看看我们的量子比特的运行情况。
Qiskit使用Matplotlib来提供有用的可视化。一个简单的直方图就可以了。result对象提供了get_counts方法来获取已执行电路的直方图数据(第5行)。方法plot_histogram返回一个Matplotlib图形,Jupyter可以自动绘制(第8行)。
我们看到观察到值1的概率是100%。
现在,让我们来看看更高级的情况。比方说,我们希望我们的量子比特以相同的概率(50:50)产生0或1。
在量子力学中,有一个基本的叠加原理。它表示任何两个(或更多)量子态可以相加(叠加),其结果将是另一个有效的量子态。根据矢量运算,我们的量子叠加态应该是ψ*:
振幅是我们阵列中的值,它们与概率成正比。所有概率相加应正好为1(100%)。我们需要为量子态|0⟩和|1⟩添加权重。我们用 α 给 |0⟩ 加权,用 β 给 |1⟩ 加权:
为满足上述条件,α和β的数值都是1/(√2)。在 Python 中对应的数组是:[1/sqrt(2), 1/sqrt(2)]。
量子比特是一个二维量子系统。每个维度由一个标准基向量表示。尽管对量子比特状态矢量进行归一化并不是一件难事,但反复进行数学运算却相当麻烦。
——也许还有另一种方法、一种简单的方法。
在该表示法中,两个维度都位于纵轴上,但方向相反。系统的顶部和底部分别对应标准基向量|0⟩和|1⟩。
由于量子比特状态矢量是归一化的,|ψ⟩起源于圆心,其幅值(长度)为.由于幅值相等,所有的状态矢量都以尖圆结束,|ψ⟩也是如此。
状态矢量|0⟩和|ψ⟩之间的夹角称为θ,它控制着矢量头与系统顶部和底部的距离(虚线)。这些接近度代表了:
- α^2测量 |ψ⟩ 为 0 的概率- 和β^2测量为1的概率
因此,通过控制邻近度,角度 θ 也控制了测量量子比特处于状态 0 或状态 1 的概率。
与其指定 α 和 β 之间的关系,然后再对它们的值进行归一化处理,我们可以指定角度 θ,然后使用所需的归一化处理来推导出 α 和 β。从而得到状态 |ψ⟩:
在Python中,二维数组[cos(theta/2), sin(theta/2)]表示这种状态。
但是,对于 θ ∈ R,如果 π < θ < 2π呢?或者通俗地说,如果 θ 表示指向纵轴左侧的矢量呢?在数学上,这没有问题;由于我们将 α 和 β 平方,它们的符号(+ 或 -)与得到的概率无关。
但这意味着什么呢?如图所示,α^2 或 β^2怎么会是负数呢?答案是 i。i 是复数,其平方为负数:i^2 = - 1。如果 α 和 β 是复数 (α, β∈ C),它们的平方也可以是负数。
现在,我们将纵轴左侧的所有向量解释为 β^2 为负值 (β^2 < 0)。虽然这样的值可以让我们区分纵轴两侧的量子比特状态向量,但是对于得到的概率并不重要。
上述α^2或β^2为负值的后果之一是我们的归一化(normalization)需要进行一些调整。
另外,量子比特是一个两级量子系统,它处于量子态|0⟩和|1⟩的叠加态——除非您观察到它。一旦观察到它,就会有测量到0或1的不同概率。在物理学中,这被称为“观察者效应”。
在我们所经历的现实世界中,观察的影响往往可以忽略不计。但在量子力学的亚原子世界里,这些影响却很重要、它们非常重要。仅仅观察一个量子比特,就会改变它的状态,从|0⟩和|1⟩的叠加状态变为任意一个值。
因此,我们在开发量子电路时需要考虑的是,即使简单的观测也是对系统的操纵。下面是这种状态的狄拉克符号和矢量符号:
在这个电路中,我们添加了一个“仿真后端(simulation backend)”(第 15 行)、执行电路并得到结果(第 18 行)。结果对象提供了get_counts函数,该函数提供了我们的量子比特的结果(观察到的)状态的概率。
让我们来看看我们的电路。QuantumCircuit提供了draw函数,用于渲染电路图的图像。提供output=text作为命名参数,可以得到ASCII艺术版本的图像。
我们在这里看到的是单个量子比特(q)及其初始化值(=0.707)。这些值既是我们电路的输入,也是输出。当我们执行这个电路时,我们的结果函数会评估|0⟩和|1⟩叠加状态下的量子比特。因此,我们有50:50的机会将量子比特捕捉到任何一种状态。
下面,让我们看看如果我们将量子比特作为电路的一部分来观察,会发生什么。
结果状态1的概率为100%!
无论如何频繁地运行这段代码,总是会得到100%的概率,要么是0、要么是1。事实上,如果您多次重复运行这段代码并计算结果(加入时间维度),就会看到50:50的分布。
在量子编程中,测量会对结果产生影响,这与经典编程不同,在经典编程中,我们可以随意检查、打印和显示比特的值。
如果我们不断测量量子比特来跟踪它的值,我们将使它保持在一个明确定义的状态,要么是0、要么是1。这样的量子比特与经典比特没有区别,计算可以很容易地被经典计算所取代。在量子计算中,我们必须允许量子比特探索更复杂的状态。因此,只有当我们需要输出时,才会使用测量。
这意味着我们通常将所有测量放在量子电路的末端。
PQC是一种量子电路,它将所有需要的数据作为输入参数。因此,它被称为“参数化”量子电路:它根据这些参数预测事物的标签。
事实上,该电路输出0或1、概率均为50%。接下来,假设针对经典的乘客问题,我们将这个电路封装成一个函数:
首先要注意的区别是该函数需要两个参数,而不是一个(第5行)。第一个参数是Qiskit后端,第二个参数是量子状态向量(passenger_state)。
该函数用一个量子比特创建一个量子回路(第12行),用乘客状态初始化它(第15行)、测量量子比特(第18行)、执行量子回路(第21行)并从结果中获取计数(第24行)。
加载完所需的乘客数据后,上述代码运行pqc_classifier的初始状态为0.5、概率分别为0或1(第5行)。此外,我们还创建了一个后端(第2行),并将其作为参数提供,以便重复使用(第8行)。
如果我们知道它从操作系统得到的值,或者我们自己指定一个不同的值,我们就可以重现精确的预测。这就是为什么 Python 的 random 函数产生伪随机数 (参见 Python- docs)。
相比之下,PQC产生的是真正的随机结果(在真正的量子计算机上运行时)。尽管如此,我们还没有使用任何量子方法来让我们看到经典伪随机和量子真随机之间的区别。
然而,PQC二元分类器与随机分类器相比,并没有理论上的优势:因为它并没有提高信息量。
为改变这种情况,我们需要对乘客数据进行预处理,使其能够被量子计算机计算;事实上,我们还需要对其进行后处理。
总之,我们将PQC包装成一个经典的前处理和后处理过程。这是一种算法,其外部结构在经典计算机上运行,内部组件在量子计算机上运行。这是一种变分混合量子经典算法——近期量子设备的流行方法。
数据在经典计算机上进行预处理,以确定PQC的一组参数。在我们的简单案例中,这就是量子态向量|ψ⟩。
量子硬件使用初始量子态,与之协同工作并执行测量。所有的计算都是参数化的。因此,它们是相对较小和短暂的。由于我们只测量量子态,除了|ψ⟩之外,我们不使用任何进一步的参数。最后,经典计算机对测量结果进行后处理,生成预测结果。
整个算法由经典和量子两部分组成一个闭环。
我们首先创建一个状态向量模拟器后端(第2行),这个后端可以重复用于我们所有的预判。除了输出中使用的任意名称(第5行),主要输入是我们提供的分类器(第6行)。
我们提供了一个匿名(lambda)函数(没有名称的函数)作为分类器。它接受一个参数passenger,并以passenger为参数运行(从内到外)pre_process函数。我们将后端的结果放入pqc函数,并将其结果放入post_process函数。
当我们使用初始状态运行pqc分类器时,我们可以看到它得到了与随机分类器相同的分数。
当前的PQC测量所提供的量子态向量并返回计数。如果我们提供一个向量作为输入,该向量的概率与乘客实际存活的概率相对应,我们就可以保持不变。
因此,预处理的任务是将数字转换成量子态向量,其概率与乘客的实际生存概率相对应。找到这样的概率是任何机器学习算法的内在目标。
通过上述算法概述,我们仅仅触及了量子机器学习的表面。
我们创建了一个简单的变分混合量子经典分类算法。但这个算法仅仅是个开始,它是我们所能想象的最简单的分类器之一。尽管我们使用了参数化量子电路,但我们并没有做任何经典计算机做不到的事情。
在量子机器学习领域还有很多东西值得探索。
接下来,光子盒还将探索如何创建更复杂的量子电路、如何利用纠缠和干涉来降低问题的复杂性......进而,带领读者以一种实用、易学的方式继续探索神秘的量子领域。
参考链接:[1]https://www.pyqml.com/[2]https://pyqml.medium.com/quantum-parallelism-fe1c58db53bd