科学しよう

量子計算のプログラミングの解説をメインに、データサイエンス・機械学習について勉強したことをご紹介します

MENU

PennyLaneを用いた変分分類器(VariationalClassifier)

前回は実用的でなかったので、今回は分類器を作る例をPennyLaneのチュートリアル2つを通して解説します。

今回対象のチュートリアル

pennylane.ai

こちらのチュートリアルではirisデータセットを使って分類アルゴリズムを実装しています。
なお、多クラス分類のチュートリアル

pennylane.ai

にありますので、機会があればやってみてください。

どのような問題をときたいときに、どのような量子回路を定義すればいいのかなど細かい解説は今後に譲りますが、 全体の流れとしては量子回路の部分は通常の機械学習のネットワークのようなものだと感じてもらえればいいなと思います。

対象とする読者

  • 量子計算の応用に興味のある方
  • 量子機械学習に興味がある機械学習エンジニア・データサイエンティスト

前提とする知識

バージョン情報

  • Python 3.9.5
  • PennyLane 0.15.1

目次

2クラス変分分類器

変分分類器とは

そもそも「変分分類器(Variational Classifier)」とはなにか?というのが気になると思います。
変分分類器とは、変分アルゴリズムによって最適化した量子回路によって作成された分類器です。

変分アルゴリズムと量子機械学習、量子回路学習

「変分」というのは「変分法」のことです。
機械学習の文脈で「変分法」というと「変分推論」「変分ベイズ学習」などが当てはまると思います。
適用例としては変分オートエンコーダ(Variational Auto Encoder; VAE)がそうです。
求めたい潜在変数の確率分布を何らか(VAEの場合は正規分布)を仮定して、学習で得られる分布との誤差(変分)をなるべく減らして近づける、というものだと思います。

量子計算での変分も似たようなものです。
真に知りたい量子状態 | \psi \rangleに対してある時得られた量子状態 | \psi ' \rangleとの誤差(変分)を誤差関数で評価し、その誤差関数が最小なものが真に知りたかった状態に近いだろう、という考えのもとその量子状態を作り出す量子回路をデータから求めます。
(厳密には物理学の「変分法」は解析力学由来の言葉なので、機械学習文脈での「変分法」とは若干ニュアンスが違いますが、結局の使われどころは同じです。)

回路のパラメータを機械学習で求めることになるので「量子回路学習」とも呼ばれます。
またここで用いられる量子回路は「変分量子回路」と呼ばれます。 この辺りの詳細はまた別に解説することにして、変分アルゴリズムという難しい言い方をしましたが、機械学習と同じようにデータから量子回路のパラメータを最適化してその量子回路を使って分類問題を解きましょうということです。

下記で物理学会誌の記事で京都大学の藤井先生と大阪大学の御手洗先生が解説されています。 https://www.jps.or.jp/books/gakkaishi/2019/09/74-09seriesAIphys1.pdf

その中で示されている量子回路学習の手順の図が機械学習の手順と似ていてわかりやすいと思いました。

f:id:sakumadaisuke32:20210704102716p:plain
図1. 量子回路学習の概念図(こちらより転載)

 V(\mathbf{x})で特徴量を量子ビット上にエンコーディングし、 U(\mathbf{\theta})が計算を行う操作で機械学習でいうところのニューラルネットワークなどの機械学習アルゴリズムに相当します。
これらの「量子コンピュータ」と書かれた枠の中だけが機械学習の手順と異なっており、そこを差し替えれば機械学習とやることは同じなのだと思います。

また、この辺りの説明は嶋田さんの本でも解説されています。

量子コンピューティング 基本アルゴリズムから量子機械学習まで [ 情報処理学会出版委員会 ]

2クラス変分分類器の量子回路

分類器の箇所のコードはこちらです。

import pennylane as qml
from pennylane import numpy as np
from pennylane.optimize import NesterovMomentumOptimizer


dev = qml.device("default.qubit", wires=2)


def layer(W):
    qml.Rot(W[0, 0], W[0, 1], W[0, 2], wires=0)
    qml.Rot(W[1, 0], W[1, 1], W[1, 2], wires=1)
    qml.CNOT(wires=[0, 1])


@qml.qnode(dev)
def circuit(weights, angles):
    statepreparation(angles)

    for W in weights:
        layer(W)

    return qml.expval(qml.PauliZ(0))

それを図にするとこちらです。
今回のレイヤー数は6になっています。
(statepreparationはデータセットの特徴量を量子ビットエンコーディングする箇所( V(\mathbf{x})のこと)なので一旦説明を省きます。)

f:id:sakumadaisuke32:20210704110604p:plain
図2. 2クラス分類の変分量子回路

最後の第一量子ビットを測定することで、2クラスのどちらに分類するかの確率が求まります。
ソースコードではqml.expval(qml.PauliZ(0))となっているので求めているのは \langle \hat{\sigma}_{z} \rangleです。
その結果が {1, -1}どちらであるかで2クラス分類しています。
PennyLaneではどの基底で測定するかを選ぶことができ、今回は Z基底( {| 0 \rangle, | 1 \rangle}の2状態)で測定することが2クラス分類に対応しています。

pennylane.readthedocs.io

ただ、すみません、どうしてこの量子回路だと分類問題が解けるのかというのはまだ理解が足りていません、、
詳細についてはまたの機会に解説解説させていただければと思います。

また、現状の量子回路学習の課題として、解きたい問題に対してどのような回路を構成すべきかという定石(機械学習でいうところの画像だったらCNN, 時系列だったらRNNなど)が無いようです。
これからいろんな問題について適用されて知見が溜まることを期待します。

なお、測定についてはNielsen-Chuangに詳しく書かれています。

【新品】量子コンピュータと量子通信 2 量子コンピュータとアルゴリズム Michael A.Nielsen/共著 Isaac L.Chuang/共著 木村達也/訳

NIIの根本先生の本も物理現象に即して解説されていてわかりやすいです。

www.saiensu.co.jp

データ読み込み〜モデル学習までの基本的な流れ

振幅エンコーディング

まずデータ(問題設定)を量子回路に置き換える方法は主に次の2通りがあります。

チュートリアルでは下記になっています。

def get_angles(x):

    beta0 = 2 * np.arcsin(np.sqrt(x[1] ** 2) / np.sqrt(x[0] ** 2 + x[1] ** 2 + 1e-12))
    beta1 = 2 * np.arcsin(np.sqrt(x[3] ** 2) / np.sqrt(x[2] ** 2 + x[3] ** 2 + 1e-12))
    beta2 = 2 * np.arcsin(
        np.sqrt(x[2] ** 2 + x[3] ** 2)
        / np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2 + x[3] ** 2)
    )
    return np.array([beta2, -beta1 / 2, beta1 / 2, -beta0 / 2, beta0 / 2])


def statepreparation(a):
    qml.RY(a[0], wires=0)

    qml.CNOT(wires=[0, 1])
    qml.RY(a[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RY(a[2], wires=1)

    qml.PauliX(wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RY(a[3], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RY(a[4], wires=1)
    qml.PauliX(wires=0)


@qml.qnode(dev)
def circuit(weights, angles):
    statepreparation(angles)
    for W in weights:
        layer(W)
    return qml.expval(qml.PauliZ(0))


def variational_classifier(var, angles):
    weights = var[0]
    bias = var[1]
    return circuit(weights, angles) + bias


def cost(weights, features, labels):
    predictions = [variational_classifier(weights, f) for f in features]
    return square_loss(labels, predictions)


# irisデータセットの読み込み
data = np.loadtxt("variational_classifier/data/iris_classes1and2_scaled.txt")
X = data[:, 0:2]

# パディング
padding = 0.3 * np.ones((len(X), 1))
X_pad = np.c_[np.c_[X, padding], np.zeros((len(X), 1))]

# 正規化(標準化)
normalization = np.sqrt(np.sum(X_pad ** 2, -1))
X_norm = (X_pad.T / normalization).T

# 角度(位相)に変換
features = np.array([get_angles(x) for x in X_norm])

今回のirisもそうですが一般に特徴量は実数です。
対して回転ゲート \hat{U}(\theta) 2\piの周期性があります。
特徴量の値が大きいと何周回ったのかわからなくなります。
なのでできるだけ1周程度に収めるために正規化(ここでは標準化)をしています。

そしてそのあと、学習処理

var = opt.step(lambda v: cost(v, feats_train_batch, Y_train_batch), var)

にて、実際に振幅エンコーディングします。
(cost関数にて量子回路を定義しています。必要な関数は並べてあるので、お手数ですが少し式を追ってください。)

誤差関数

測定結果 \langle \hat{\sigma}_z \rangleの値に対して、教師データとの精度を誤差関数で評価します。

def square_loss(labels, predictions):
    loss = 0
    for l, p in zip(labels, predictions):
        loss = loss + (l - p) ** 2

    loss = loss / len(labels)
    return loss


def cost(weights, features, labels):
    predictions = [variational_classifier(weights, f) for f in features]
    return square_loss(labels, predictions)

予測結果の正解ラベルへの当てはまり具合を平均自乗誤差で評価します。
この辺りはもう機械学習と同じ流れです。

最適化アルゴリズム

誤差関数での評価結果に基づいて、パラメータ更新をします。

opt = NesterovMomentumOptimizer(0.01)

var = opt.step(lambda v: cost(v, feats_train_batch, Y_train_batch), var)

varが変分量子回路 \hat{U}(w_1, w_2, w_3), \hat{U}(w_4, w_5, w_6) x 6層分ののパラメータ w_1, w_2, w_3, \dots w_{36}です。

分類結果

学習済みの量子回路を使って特徴量空間をクラスで色分けしたのが下図です。

f:id:sakumadaisuke32:20210704171823p:plain
図3. 変分量子回路を使ってirisデータセットを2クラス分類した結果(こちらより転載)

まとめ

  • 量子回路(変分量子回路)を用いた2クラス分類ができることを確かめた
  • 量子回路学習とは量子回路のパラメータを機械学習的に最適化することで、この時の量子回路を変分量子回路という
  • どのような問題設定の時にどのような変分量子回路にすればいいかという定石は定まってなく、個別に計算式・量子回路を考えて実装する必要がある