量子SVMの基本 - IBM Qiskit SVM(QSVM)を実装し、線形・非線形の基本的な2クラス分類機能を確認する

## :rocket: この記事の目的

機械学習の理論を量子コンピューティングに応用した技術の一つである**量子SVM(量子サポートベクターマシン)**について、基本的な動作確認を行います。
今回は、量子コンピューティング技術の最先端をリードするIBM社がリリースする、量子コンピューティングSDKの**Qiskit**(キスキット)を使用します。

:paperclip: IBM Qiskitチュートリアル
https://qiskit.org/documentation/locale/ja_JP/index.html

## :rocket: 古典機械学習の線形・非線形の分類について

ここでは、量子コンピューティングを応用した機械学習を**量子機械学習**と呼び、従来の機械学習を**古典機械学習**と呼びます。

サポートベクターマシンは古典機械学習分野の理論及び手法の一つで、教師あり学習を基盤とし、2クラス分類(または二値分類)などに使用されます。
その仕組みとして、基本理論では様々なデータが分散・存在する特徴量空間において超平面を求め、超平面で仕切ることでデータを2分割し、2クラス分類を実現します。このような分類方法を**線形分離**と呼びます。線形分離は以下の線形識別関数で表されます。

```math
f(\mathbf{x})=\mathbf{w}^T\mathbf{x} + b\\
ただし、\mathbf{x}は入力ベクトル、\\
\mathbf{w}は係数ベクトル、\\
bはバイアス項とする。
```

さらに、$t_i$をi番目のデータが値+1を取るクラスに分類されるか、あるいは値-1を取るクラスに分類されるかを決める正解ラベルとすると、クラス分類方程式は以下のように書き表すことができます。

```math
t_i(\mathbf{w}^T\mathbf{x_i}+b)≧1
```

下図(Wikipediaより)は、超平面により白丸と黒丸の2クラスに分類を行った様子です。
2次元空間では分離面が直線になります(赤線)。3次元以上の空間では平面となるため、超平面と呼びます。また、破線と赤線との距離を**マージン**と呼びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/5057e49e-d656-9cc8-71d4-7bd052f62485.png)

サポートベクターマシンでは、上図のマージンを以下の式を用いて最大化することを目的とします。すなわち、マージンを最大化するには、ラグランジュ未定乗数を$\alpha_i$とし、以下の評価関数$L$を最大化する相対問題に置き換えることができます。

```math
L(\alpha)=\alpha^T\mathbf{1}-\frac{1}{2}\alpha^TH\alpha\\
制約条件：\quad0≦\alpha_i≦C,\quad\alpha^T\mathbf{t}=0\\
Cはソフトマージン識別器におけるパラメータ
```

さらに、応用理論として、**カーネル関数**を用いた特徴量の高次元への写像を行うことで、線形分離では分離ができなかったデータを分離できるようにする方法を**非線形分離**と言います。
詳しくは触れませんが、カーネル関数は以下のように書き、その下の羅列のようなカーネル関数が考案されています。

```math
K(\mathbf{x_i}, \mathbf{x_j})\quad...\:式(1)
```

　・線形カーネル
　・シグモイドカーネル
　・多項式カーネル
　・ガウスカーネル
　・動径基底関数(RBF)カーネル

カーネル関数による写像と非線形分離についてのイメージ図を以下に示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/8c75527a-9548-5421-6875-38791f2e08e9.png)

非線形分類問題においては、式(1)のカーネル関数を用いて、以下の双対問題を解く問題に帰着できます。

```math
L(\alpha)=\sum_{i=1}^{N}\alpha_i-\frac{1}{2}\sum_{i=1}^{N}\sum_{j=1}^{N}\alpha_i\alpha_jt_it_jK(x_i, x_j)\\
制約条件：\quad0≦\alpha_i≦C,\quad\alpha^T\mathbf{t}=0\\
評価関数Lを最大化する。
```

以上を応用することで、サポートベクターマシンは線形・非線形問わずクラス分類を行うことができます。


## :rocket: 量子SVMの理論と線形・非線形の分類について


量子SVMは、基本的には量子コンピュータ上で従来の機械学習の理論を用いて、同様にクラス分類を行うための理論です。
量子SVMでも、古典SVMと同じように非線形分離の場合に、カーネル関数を使用する理論が提唱されています。これについての論文は、以下からご参照下さい。

:paperclip: Supervised learning with quantum enhanced feature spaces
https://arxiv.org/pdf/1804.11326.pdf

量子SVMではカーネル関数を以下のように定義します。

```math
K(\mathbf{x_i}, \mathbf{x_j})=|〈\phi(x_i)|\phi(x_j)〉|^2\quad...\:式(2)
```

式(2)の絶対値の中身は$|\phi(x)〉$の内積となっています。
ここで、$|\phi(x)〉$は何らかの量子状態を意味し、以下のようにユニタリ発展を用いてこの状態を生成します。

```math
U(x)|0〉=|\phi(x)〉
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/02582e54-e703-2b9f-7c26-b3a331897acf.png)

上記の仕様はQiskitにも実装されています。
Qiskitのチュートリアルで、量子SVMについては以下からご参照下さい。

:paperclip: 量子サポート・ベクター・マシン (QSVM) [Qiskitチュートリアル]
https://qiskit.org/documentation/locale/ja_JP/tutorials/machine_learning/01_qsvm_classification.html?highlight=qsvm


## :rocket: 実行環境

今回の実行環境は以下です。なお、当記事執筆時点(2021/03/03)のバージョンであり、Qiskitの機能変更やバージョンアップにより環境が動作しなくなる可能性もありますのでご了承お願い致します。

- Ubuntu Server 20.04 LTS 64bit
- Qiskit
 - qiskit               0.23.6
 - qiskit-aer           0.7.5
 - qiskit-aqua          0.8.2
 - qiskit-ibmq-provider 0.11.1
 - qiskit-ignis         0.5.2
 - qiskit-terra         0.16.4
- Python 3.9.2

Qiskitについては、以下のコマンドでインストールを実行して下さい。

```ruby
pip install qiskit
pip install qiskit-aqua[cvx]
```


## :rocket: 線形の場合の2クラス分類の実装と動作確認(AND回路)

最も簡単な線形2クラス分類の例を見てみましょう。
下図(図1.線形分離の場合)をご覧下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/fc990b92-f579-dea6-f727-e72d19b715e9.png)

x-y平面上に青色と赤色でクラス分けされた◆(ダイヤ)マークがあります。
ここで青色をAクラス、赤色をBクラスとします。
青色◆は座標が(0,0)で、残りの3つの赤色◆は座標がそれぞれ(0,1),(1,0),(1,1)です。

これはAND回路そのものと言えます。
真理表で表すと以下のようになります。

| x | y |真理(T/F)|
|:-:|:-:|:----:|
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 1 | 1 |

真理値0をAクラス、真理値1をBクラスに割り当てれば、AND回路を2クラスに分類することができます。

サポートベクターマシンを使用してこのAND回路のクラス分類を行うにはどうすれば良いでしょうか。
サポートベクターマシン理論によれば、クラス分類を行うには超平面を定義することでした。そこで、上図の破線のように超平面(2次元の場合は分離線)を求めることで分類が可能であることがわかります。**AND回路は線形分離が可能**な例となります。

では、Qiskitを用いて分類器を実装してみます。

####1. 教師データ(訓練データ)の作成

教師データの変数名をtrain_input、正解ラベル変数名をtrain_labelとし、教師データと正解ラベルが含まれる訓練データセットを用意します。
訓練データセットはPythonの辞書型で作成します。「図1.線形分離の場合」に従い、青色をAクラス、赤色をBクラスとします。それぞれの座標から以下のように作成します。

```ruby
# 教師データ
train_input = {
    'A':[
        [0, 0],
    ],
    'B':[
        [1, 0],
        [0, 1],
        [1, 1],
    ]
}

# 正解ラベル
train_label = {
    'A':[
        [0, 0],
    ],
    'B':[
        [1, 0],
        [0, 1],
        [1, 1],
    ]
}
```

今回はデータ数が少ないことと、辞書型で作成する必要があるため、train_input及びtrain_labelは同じ内容となっています。

####2. テストデータの作成

テストデータは配列型を用意します。
今回はx-y平面上の4点の教師データを学習した上で、改めてこれら4点のテストを行うため、テストデータは4つです。
具体的には以下とします。

```ruby
# テストデータ
test_input = [
        [0, 0],
        [1, 0],
        [0, 1],
        [1, 1],
]
```

####3. QSVMのプログラミングと実行

QSVMの主計算部分のプログラミングは以下となります。

```ruby
from qiskit.circuit.library import ZZFeatureMap
from qiskit import BasicAer
from qiskit.aqua.algorithms import QSVM
from qiskit.aqua import QuantumInstance

random_seed = 10598
shots = 1024

backend = BasicAer.get_backend('qasm_simulator')                    # s1
feature_map = ZZFeatureMap(feature_dimension=2, reps=2)
qsvm = QSVM(feature_map, train_input, train_label, test_input)      # s2
qsvm.random_seed = random_seed
quantum_instance = QuantumInstance(backend, shots=shots, seed_simulator=random_seed, seed_transpiler=random_seed)

result = qsvm.run(quantum_instance)                                 # s3

print('正解率', result['testing_accuracy'])
print('クラス分類結果', result['predicted_classes'])

```

要点のみ解説します。

<dl>
  <dt>s1</dt>
  <dd>量子コンピュータとして、qasmシミュレータを設定します。</dd>
  <dt>s2</dt>
  <dd>QSVMを定義します。入力データとして、train_input, train_label, test_inputを設定します。</dd>
  <dt>s3</dt>
  <dd>実際に計算を実行する部分です。</dd>
</dl>

データが少ないため、処理の実行時間は瞬時に実行されました。

####4. 結果の確認

処理結果を確認します。
処理結果はresult変数に格納されます。ここでは、正解率とテストデータのクラス分類結果を表示しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/f0d45886-69d7-56df-da44-89fed5ff852e.png)
正解率1.0、つまり100%で正解することができました。
また、クラスの分類結果についても、想定通りのクラスに分類できたことがわかります。
以上から線形の場合の2クラス分類の動作確認を取ることができました。

続いて、非線形の場合の動作確認を行います。


## :rocket: 非線形の場合の2クラス分類の実装と動作確認(XOR回路)

AND回路は線形分離が可能な例でしたが、ここでは線形分離が不可能な例を見てみます。
下図(図2.非線形分離の場合)をご覧下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/7b0201d2-2ef4-083a-d009-75faf5fc0755.png)

x-y平面のクラス分け状況を見てみると、
青色◆は2つあり、それぞれの座標は(0,0),(1,1)となっています。
また、赤色◆も2つで、それぞれの座標は(1,0),(0,1)です。

これはXOR回路となります。
真理表で表すと以下のようになります。

| x | y |真理(T/F)|
|:-:|:-:|:----:|
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 1 | 0 |

先程と同様に、真理値0をAクラス、真理値1をBクラスに割り当てます。
図を見てわかる通り、直線で2クラスに分類することはできません。図の破線(分離線)ように曲線を用いないと2クラスに分類することが不可能です。
このように、**XOR回路は線形分離が不可能**な例となります。

では、量子SVMでこのような非線形の分類が可能なのか、実装して動作確認してみます。

####1. 教師データ(訓練データ)の作成

線形の場合と同様に、教師データの変数名をtrain_input、正解ラベル変数名をtrain_labelとし、教師データと正解ラベルが含まれる訓練データセットを用意します。
訓練データセットはPythonの辞書型で作成します。「図2.非線形分離の場合」に従い、青色をAクラス、赤色をBクラスとします。それぞれの座標から以下のように作成します。

```ruby
# 教師データ
train_input = {
    'A':[
        [0, 0],
        [1, 1],
    ],
    'B':[
        [1, 0],
        [0, 1],
    ]
}

# 正解ラベル
train_label = {
    'A':[
        [0, 0],
        [1, 1],
    ],
    'B':[
        [1, 0],
        [0, 1],
    ]
}
```

同様の理由で、辞書型で作成する必要があるため、train_input及びtrain_labelは同じ内容となっています。

####2. テストデータの作成

テストデータは配列型を用意します。
こちらも同様にテストデータは4つです。具体的には以下とします。

```ruby
# テストデータ
test_input = [
        [0, 0],
        [1, 0],
        [0, 1],
        [1, 1],
]
```

####3. QSVMのプログラミングと実行

プログラムは線形の場合と同じ内容になります。
線形の場合は想定通り正解を得られましたが、非線形の場合も想定通り結果が得られるかどうかを確認します。


####4. 結果の確認

処理結果は以下となりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399135/14484a5e-4dd2-9a7c-36f7-53973c5c1298.png)

こちらも正解率1.0、つまり100%で正解することができました。
また、クラスの分類結果についても、想定通りのクラス分類ができました。

以上から線形の場合と非線形の場合のどちらにおいても、2クラス分類の動作確認を取ることができました。



## :rocket: 関連情報

■Raspberry PIで量子コンピューターをシミュレーションする(IBM Qiskit環境構築編)
https://qiita.com/ttabata/items/64f06b7cdf3a44971c77

■量子サポート・ベクター・マシン (QSVM) [Qiskitチュートリアル]
https://qiskit.org/documentation/locale/ja_JP/tutorials/machine_learning/01_qsvm_classification.html?highlight=qsvm

■IBM Qiskitチュートリアル
https://qiskit.org/documentation/locale/ja_JP/index.html



## :rocket: ご意見など

ご意見、間違い訂正などございましたらお寄せ下さい。