【技术】谷歌Tensorflow量子版的分类与回归范例浅释

TFQ 技术栈

3月9日谷歌强势开源了Tensorflow的量子计算版 TFQ(Tensorflow Quantum)@GitHub
笔者第一时间取官方MNIST范例做了尝鲜(+个人KM笔记),但因项目在身,等一个月后又浏览了一份二次方程回归训练的范例、才有了这份略微迟到的体验分享(包含对官方范例的2处纠错)。
本文并不涉及“如何基于任务类型设计量子门模型”的量子回路原理,仅作TFQ总工作流的简介。

概述&准备

  • 先说结论,Tensorflow Quantum版目前尚不能达成比传统框架/模型更高的准确率——事实上其acc反而可能低很多,而是强在其“斩破时空”级别的计算速度——谷歌去年10月已用53个qubit(量子位)、验证仅用200秒就能完成百万核CPU超级计算机需用10,000年完成的计算任务(=量子制霸or量子优势)[1]
    注:验证基于[薛定谔-费曼算法(SFA)](https://arxiv.org/abs/1807.10749),不过也受到IBM质疑“传统超算只需_2.5天_”
  • 谷歌已改变之前选用D-Wave时的退火方式(Annealing),而采用与IBM一样的量子门方式,因此上述的成绩已更具通用性。
  • 相关的云计算业务可能会成为新增长点——谷歌预计明年就能进入NISQ时代(提供10-100位的中规模含噪音量子计算设备,噪音=退相干等造成的容错问题),相关产业链的业者可从熟悉其开发流程起步。
  • (准备)量子计算背景知识自检
    • 量子比特远比传统比特高效的根本原因:量子叠加态,参考“开关组游戏”
    • 量子位的可视化表征(布洛赫球面)、量子位门(X门,Z门,CNOT等):参考相关关键字
  • (准备)深度学习背景知识自检
    • 从MNIST数据集(一个最简单的手写字图片集)加载、到tf.Keras模型的compile, fit, evaluate, predict流程:TFQ范例中将沿用该工作流,有一篇入门教程可供体验传统版的MNIST分类任务解决范例

MNIST手写字分类任务量子版(官方)

请先按官方文档安装好本地环境(pip方式,TF2.1),对已有TF经验者来说简单无坑;
也可直接云端体验ipynb版

MNIST分类任务尽管是TFQ官方的第二个范例,但适合直接用来入门,完整代码请自行拼合至本地运行,以下仅对量子版相关代码做择要说明。

0.加载依赖库 & 增添SVG保存

import tensorflow_quantum as tfq
import cirq
import sympy
from cirq.contrib.svg import SVGCircuit, circuit_to_svg

tfq就是量子版深度学习的核心库了,但它所做的主要是对更为核心的专用于量子计算的cirqAPI丰富易用)作深度学习相关的封装。sympy则是解符号方程的常用库,使得能以符号方式命名变量,供cirq使用。

cirq..SVGCircuit用于将cirq的量子回路可视化成SVG对象,本地调试建议多import一个circuit_to_svg将SVG dump成文件查看(注:matplot需再添加第三方库才能支持SVG显示),相关自制代码:

def save_text(src: str, dest_path=None, file_ext="svg"):
    if dest_path is None:
        import uuid
        dest_path = str(uuid.uuid4()) + ".{}".format(file_ext)
    with open(dest_path, 'w', encoding='utf-8') as f: 
        f.write(src)

def dump_circuit(circuit: 'cirq.Circuit', dest_path=None):
    save_text(circuit_to_svg(circuit), dest_path=dest_path)

1.将传统数据转为量子回路

50000行MNIST数据集的加载一如寻常,量子版范例出于目前的处理能力多做了4步简化:

  • 仅筛选出‘3’和‘6’的手写数字图片,将十元分类任务简化成只需做二元分类
  • 将图片进一步从28x28缩小到4x4,减少所需的量子比特数量(毕竟53qubit已是领先工艺)
  • 清理掉同时被标注为‘3’和‘6’的“冲突样本”(不过后续的二值化将会导入更多标签“冲突”)
  • 二值化(注:非常影响准确率的一个步骤,但限于量子计算的处理能力而做),大于0.5的像素才视作白色

注意,这里可以添加一次冲突清理,你会发现,4x4的图片再经过二值化处理其实只剩下193个无冲突标注的有效样本用于训练…
本修改已PR给了官方:

print("----- remove_contradicting again on x_train_bin -----")
x_train_bin, y_train_nocon = remove_contradicting(x_train_bin, y_train_nocon)
# ----- remove_contradicting again on x_train_bin -----
# Number of unique images: 193
# Number of 3s:  124
# Number of 6s:  113
# Number of contradictory images:  44
# 
# Initial number of examples:  11520
# Remaining non-contradictory examples:  3731

最关键的步骤,将经过清理、简化的传统数据转化为量子回路

def convert_to_circuit(image):
    """Encode truncated classical image into quantum datapoint."""
    values = np.ndarray.flatten(image)
    qubits = cirq.GridQubit.rect(4, 4)
    circuit = cirq.Circuit()
    for i, value in enumerate(values):
        if value:
            circuit.append(cirq.X(qubits[i]))
    return circuit

x_train_circ = [convert_to_circuit(x) for x in x_train_bin]
x_test_circ = [convert_to_circuit(x) for x in x_test_bin]

每个cirq.GridQubit.rect()=>文档会声明一组与单张图片尺寸相符的网格用量子比特,相当于一个2D的tf.Tensor(shape=(h,w))
逐一扫描传统的像素数据,当为白色,就对相应坐标位置的量子比特配置一个X门量子操作——其作用是将初始值为|0>基态的量子比特激活到|1>激活态,并将这些操作依次添加到量子回路circuit中——这种cirq.Circuit对象=>文档本质上就是一个管理着多组对特定位置qubit操作的数组,也是cirq的核心类。
x_train_bin中的每张图片都进行转化后,即可获得量子回路版的数据集x_train_circ

扩展阅读#1:Cirq API文档,系统理解cirq包中的量子回路概念。

本地运行的话,可用之前自制的dump_circuit()观察第一张图片样本的量子回路版本,可见数据量子回路的(2,2)(3,1)坐标位上已被安置了激活用的X门 (注:(2,1)上的像素因小于0.5已被二值化去除)

dump_circuit(x_train_circ[0])

记得,cirq域的量子回路还需被转入tfq域、供tf.keras使用:

x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)

2.构建模型的量子回路

分类任务的QNN(量子神经网络)应如何设计需扩展阅读相关论文 Farhi et al.,概要地说,需用到2个量子位门(对每个像素XX和ZZ),且都带一个readout比特把结果提供给外部。

  • 范例中声明了一个能创建满足该需求的工具类:
class CircuitLayerBuilder():
    def __init__(self, data_qubits, readout):
        self.data_qubits = data_qubits
        self.readout = readout
    
    def add_layer(self, circuit, gate, prefix):
        for i, qubit in enumerate(self.data_qubits):
            symbol = sympy.Symbol(prefix + '-' + str(i))
            circuit.append(gate(qubit, self.readout)**symbol)

这一Builder,引用/依赖于一组数据量子比特、以及一个readout量子比特,提供一个add_layer方法用于遍历每个数据量子比特中、给它配置一个gate所指定的门操作(命名为prefix加自增序号),并将所有操作作为添加到指定的量子回路circuit中——相当于添加了一个(借用ML概念,而非cirq概念)。

  • 简单测试理解该Builder类的作用:
demo_builder = CircuitLayerBuilder(data_qubits = cirq.GridQubit.rect(4,1),
                                   readout=cirq.GridQubit(-1,-1))

circuit = cirq.Circuit()
demo_builder.add_layer(circuit, gate = cirq.XX, prefix='xx')
dump_circuit(circuit)

这里的XX门,是两个X门的张量乘积=>cirq文档,=>张量乘积解释。4个量子比特中的每个、都被同readout比特(位于(-1,-1))、关联配置了XX门操作。

XX门

这一cirq.Circuit对象的内部结构(深入调查请系统参考Cirq文档),注意其中的exponent={Symbol}xx-0,门操作GateOperationexponent参数意味着旋转的角度、值为1.时相当于180°,TFQ会收集这些Symbol作为模型训练的参数(即PQC中的P:Parameterized)作运行时调整。

  • MNIST任务中对Builder类的实际使用:
def create_quantum_model():
    data_qubits = cirq.GridQubit.rect(4, 4)
    ......
    builder = CircuitLayerBuilder(
        data_qubits = data_qubits,
        readout=readout)

    # Then add layers (experiment by adding more).
    builder.add_layer(circuit, cirq.XX, "xx1")
    builder.add_layer(circuit, cirq.ZZ, "zz1")
    ......
    return circuit, cirq.Z(readout)

model_circuit, model_readout = create_quantum_model()
分类任务用QNN模型(量子回路)的一部分
  • create_quantum_model()中省略的...代码,是对readout的置位初始化以及H门操作(变为叠加态,如下图)。
  • 扩展阅读#2:IBM量子体验教程,动图可视化理解各量子门作用等。

3.封装量子模型(到tf.keras)

模型的输入将是"数据量子回路"(x_train_circ),适配为tf.string类型,并需用tfq.layers.PQC(Parametrized Quantum Circuit)来处理、训练模型。
readout比特的期望值将被传给PQC层,而因为该预测值将介于[-1,1],适合将标签值(y_train)映射至该区间、并用hinge loss作损失函数;而如果将标签保持在[0,1]区间(即做是否为'3'的二分判断),则需用tf.losses.BinaryCrossentropy。范例采用前者。

# Build the Keras model.
model = tf.keras.Sequential([
    # The input is the data-circuit, encoded as a tf.string
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    # The PQC layer returns the expected value of the readout gate, range [-1,1].
    tfq.layers.PQC(model_circuit, model_readout),
])

model.compile(
    loss=tf.keras.losses.Hinge(),
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[hinge_accuracy])

获得的模型有32个参数:

print(model.summary())
# Model: "sequential"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# pqc (PQC)                    (None, 1)                 32        
# =================================================================
# Total params: 32
# Trainable params: 32
# Non-trainable params: 0
# _________________________________________________________________
# None

注意,这里官方源码有一处错误,训练时用到的y_train_hinge竟然是从去冲突前的y_train转换而来,这会造成输入集与标签集的索引不匹配,使得训练实际上是基于随机的匹配关系在进行…
本修改已PR给了官方:

# y_train_hinge = 2.0*y_train-1.0
y_train_hinge = 2.0*y_train_nocon-1.0

4.训练模型(如tf.keras常规流程)

NUM_EXAMPLES = 500  # len(x_train_tfcirc)完整测试的话、需45分钟
x_train_tfcirc_sub = x_train_tfcirc[:NUM_EXAMPLES]
y_train_hinge_sub = y_train_hinge[:NUM_EXAMPLES]

qnn_history = model.fit(
      x_train_tfcirc_sub, y_train_hinge_sub,
      batch_size=32,
      epochs=3,
      verbose=1,
      validation_data=(x_test_tfcirc, y_test_hinge))
qnn_results = model.evaluate(x_test_tfcirc, y_test)

完整训练能获得约59.2%的准确率。尽管有图片样本仅4x4且经二值化信息损耗等原因,但使用同样数据、并将模型简化到参数也是32个左右的情况下,传统CNN模型都能达到约91.97%的准确率[2]

  • 由MNIST案例可见,目前量子计算深度学习还处于迎接NISQ(中规模含噪音量子计算)时代的热身阶段(注:预计明年能上100位、且增强容错校验),使得广大ML研发者能熟悉起运用(谷歌家的)量子计算能力所需的开发方式
  • 相关的云计算业务可能成为新的增长点(注:官方预见的运用以科研为主,但包括了飞机/汽车电池的轻量化设计),推动产业良性升级

二次方程回归任务[3]

在浅尝过MNIST分类任务的基础上,再快速用一个角度相近的回归任务(regression)增加认识。

人类自身的学习/认知过程(Human Learning)其实不就包含着“拟合”、“梯度”、“权重(形成)”么 :)

完整代码保存至本地,原文[3]来自日文Qitta网站。

假设读者已读过之前对MNIST范例的介绍,仅对核心部分作说明:

0.以随机数方式准备训练&测试数据

num_x_train = 200
x_train = x_min + (x_max - x_min) * np.random.rand(num_x_train)
y_train = [x**2-0.5 for x in x_train]
mag_noise = 0.025
y_train = y_train + mag_noise * np.random.randn(num_x_train)
plt.plot(x_train, y_train, "o"); plt.show()

以一元二次方程的自变量X作输入,因变量Y作标签,产生200个随机数据,其中50个留作测试集。

1.将传统数据转为量子回路

nqubit = 5
def convert_to_circuit(x):
    y = np.arcsin(x)
    z = np.arccos(x**2)
    qubits = cirq.GridQubit.rect(nqubit, 1)
    circuit = cirq.Circuit()
    for i in range(nqubit):
        circuit.append(cirq.ry(y).on(qubits[i]))
        circuit.append(cirq.rz(z).on(qubits[i]))
    return circuit

x_train_circ = [convert_to_circuit(x) for x in x_train]
x_test_circ = [convert_to_circuit(x) for x in x_test]

会为每个传统浮点数数据,配置一个5x1的量子比特网格(注:5位取决于所希望的浮点精度),并对每个量子比特配置一个Ry门操作(绕Y轴旋转指定角度)和Rz门操作(绕Z轴旋转指定角度)
参考量子回路理论[4]可知,将多量子位|00..0>置位为x需要(其中j为位数)

可再用自制的dump_circuit()观察第一个浮点数样本的量子回路版本,

dump_circuit(x_train_circ[0])

2.构建模型的量子回路

class CircuitLayerBuilder():
    ...... # 略,__init__, add_layer方法与MNIST范例相同            
    def add_layer_single(self,circuit,gate,prefix):
        symbol = sympy.Symbol(prefix + '-' + str(0))
        circuit.append(gate(symbol).on(self.readout))
    
    def add_entangler(self,circuit,len_qubit):
        circuit.append(cirq.CZ(self.readout,self.data_qubits[0]))
        for i in range(len_qubit-1):
            circuit.append(cirq.CZ(self.data_qubits[i],self.data_qubits[(i+1)%len_qubit]))
        circuit.append(cirq.CZ(self.readout,self.data_qubits[-1]))

回归任务的Builder类增添了add_layer_single(仅对readout位配置操作门)和add_entangler(纠缠关联)两种方法。

def create_quantum_model(c_depth=3):
    data_qubits = cirq.GridQubit.rect(nqubit,1)
    ......
    builder = CircuitLayerBuilder(
        data_qubits = data_qubits,
        readout = readout
    )
    for i in range(c_depth):
        builder.add_entangler(circuit,nqubit)
        builder.add_layer(circuit, gate = cirq.XX, prefix='xx'+str(i))
        builder.add_layer(circuit, gate = cirq.ZZ, prefix='zz'+str(i))
        builder.add_layer(circuit, gate = cirq.XX, prefix='xx1'+str(i))
        builder.add_layer_single(circuit, gate = cirq.rz, prefix='z1'+str(i))
        builder.add_layer_single(circuit, gate = cirq.rx, prefix='x1'+str(i))
        builder.add_layer_single(circuit, gate = cirq.rz, prefix='z2'+str(i))
    return circuit, cirq.Z(readout)

model_circuit, model_readout = create_quantum_model()
回归任务用QNN模型(量子回路)的一部分
  • 回归任务用的量子回路设计原理需参考原作者推荐文章(基于qulacs库)。

3.封装量子模型(到tf.keras)

使用tfq.layers.PQC封装到tf.keras.Sequential中,与MNIST范例一样;compile()时所用的损失函数则需用MSE均方差。

model.compile(
    loss=tf.keras.losses.mse,
    optimizer=tf.keras.optimizers.Adam(),
    metrics=['mae'])

获得的模型将有54个参数,比MNIST任务多出不少。

4.训练模型(如tf.keras常规流程)

fit, evaluate与常规流程,即MNIST范例无异,评估方差为0.0262;回归任务还对测试集做了predict、以比对观察模型的训练成果,

y_pred = model.predict(x_test_tfcirc)

plt.plot(x_test,y_pred,"o",label="pred")
plt.plot(x_test,y_test,"xr",label="test")
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

红叉所示的预测值很贴近训练样本的规律,二次曲线回归任务的准确率确实比MNIST图片分类任务高出许多!

小结

Tensorflow Quantum版目前通过将Cirq量子计算库的结果封装后与传统tf.Keras衔接的方式作为其工作流,本文通过简化版MNIST分类任务与二次曲线回归任务、在代码级别体验了衔接方法(i.e.将cirq.Circuit数据量子回路传递给经过tfq.layers.PQC封装的QNN模型量子回路),扩展到Cirq文档及量子门文档,包括纠正了官方范例发布一个多月以来的2处小错误。
QNN(或者说QML(量子机器学习))模型的量子回路设计(量子门排列)才是通向实用的重点,需针对不同深度学习任务参考相关文献。
就效果而言,二次曲线回归任务的训练成果还是相当不错的,更令人感兴趣的图片分类任务则主要因近期的量子位数量限制而表现欠佳、但已算法可行。明年可能如预期进入NISQ时代开放相关云计算服务,有志于相关产业链的业者可提前布局、从熟悉其开发流程起步。


  1. 量子优越性的验证实验等,2019/10,谷歌官方日志

  2. 官方MNIST分类任务:量子模型与传统模型的成绩对比

  3. 二次方程回归任务范例(来自日文Qitta网站)

  4. 通过Ry门Rz门旋转操作对多量子位赋值的原理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容