TensorFlow QuantumのMNIST量子機械学習をPyTorch + Blueqatに移植してみた

## このスライドはBlueqat Summit 2020夏のために作成しました。

---
## About Me
![gyu-don](https://qiita-user-profile-images.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F88256%2Fprofile-images%2F1473704361?ixlib=rb-1.2.2&auto=compress%2Cformat&lossless=0&w=75&s=6c89dc2b2d392b741ba1a046c0666beb)gyu-don (Blueqat inc.)

- 量子計算用ライブラリBlueqatをメインで開発しています
- 最近、未踏ターゲット事業2020での開発もやっています
- 圏論とか定理証明とかにも入門しはじめました

---

# TensorFlow Quantum
https://github.com/tensorflow/quantum

> TensorFlow Quantum (TFQ) is a Python framework for hybrid quantum-classical machine learning that is primarily focused on modeling quantum data. TFQ is an application framework developed to allow quantum algorithms researchers and machine learning applications researchers to explore computing workflows that leverage Google’s quantum computing offerings, all from within TensorFlow.

---

# 量子機械学習の時代、ついに？

---

## Blueqat開発者にとって、めちゃくちゃ気になる

ということで、とりあえず[ホワイトペーパー](https://arxiv.org/abs/2003.02989)[^1]読んで、必要に応じてソース[^2]を眺めつつ、[チュートリアルのMNIST](https://www.tensorflow.org/quantum/tutorials/mnist)[^3]をPyTorch + Blueqatに移植してみました。



---

## 前提
- ここで出てくるのはすべてゲート方式の量子コンピュータの話です
- 実際の量子コンピュータではなく、シミュレータを使っています
  - 実機に比べてシミュレータは、ノイズの影響がないため精度がよく、また、量子コンピュータの内部状態を直接取得できるため、小さな回路での期待値計算は実機より高速です

---

## 量子コンピュータの理想と現実

- 理想
  - 量子コンピュータがあれば、どんな計算でも一瞬で解ける
  - 量子的超越により量子コンピュータは既に従来のコンピュータを凌駕した
  - 量子機械学習 = シンギュラリティー
- 現実
  - そもそも「どんな計算でも一瞬で解ける」は大嘘(既知の物理法則を凌駕する必要がある)
  - 「量子的超越」を既に達成したかは微妙[^4]で、また、「量子的超越」は実用的なアプリケーションの有無とは直接関係がない
  - 今はハードウェアが未熟で、できることはほとんど何もない
  - 量子機械学習は想像を絶するほどショボい!!
     - シミュレータは非力だが、実機は(一部用途を除き)シミュレータよりも非力。なのでシミュレータを使ってやっているが、やっぱりショボい
     - 実用面でシミュレータを超える実機が出たら状況が変わる? (けれど、それはいつ?)


---
## TensorFlow Quantum (TFQ)は一体何をしてくれるのか?

- 機械学習部分→TensorFlowを使う
- 量子回路部分→Cirqを使う

TFQはそれらを糊付けするライブラリで、

- 量子回路をTFのテンソルに乗せる
- 量子回路テンソルに回路を付け加えるTFのレイヤーを作る
- 量子回路テンソルを、量子回路のシミュレータや実機で動かし、値を取り出すTFのレイヤーを作る
- 値を取り出すレイヤーについて、値を数値微分、あるいは微分を与える別回路によって微分できるようにする

を主にしている。

---
## 量子回路テンソル?

- 具体的には、Cirqの量子回路オブジェクトをprotobuf形式にバイナリエンコーディングしている
    - PickleとかJSONとかそんなんだと思ってもらえばいい
- それをバイト列としてTFのテンソルに載せている
- とりあえずテンソルに載せられるのはメリット
    - けれど、テンソルを(デコード、再エンコードすることなく)回路を直接いじるとかは難しそう
- Blueqatでやる上で、これも真似する必要があるのか、未だに迷っている
    - 今回は回路のテンソル化はやらず、floatを入力として、入力値から回路を作ることにした

---

## ということで、MNISTやります

---

## ダウンスケールの衝撃
チュートリアルによると、まず、MNISTのうち3と6だけを取り出して、3か6かを判定する問題にする。(多分、量子ビット数とか計算時間の都合上)
**衝撃ポイント1: MNISTはフルではできない**


よく見るやつ[^3]を
![Screenshot_20200702_171627.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/d8096907-d10a-fcf4-5203-a5b085d569eb.png)
こうやって[^3]、
![Screenshot_20200702_172058.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/4a1a3d6f-1914-cfc5-47dc-37edcdd8871b.png)
こうじゃ！[^3]
![Screenshot_20200702_172127.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/7c48e29f-ad42-b44b-7e45-13e9268f2234.png)

**衝撃ポイント2: MNISTの画像(28x28 pixel)は大きすぎるので、4x4 pixelに画像縮小**
もはや、元の画像の面影がないです。逆にこれで分かるのすごい。

その結果、同じデータなのにラベルが違う画像が少し出てきたので、学習用データからそれを削るらしく。
さらに、ピクセルの値が0.5より大きいかどうかで、bool値にしている。
そうすると、だいたいの画像が同じになってしまい、11520サンプルのうち、193枚しか画像が残らず、うち44枚が、3のラベルも6のラベルも両方ついている状況になってしまい、けれどそれを削ると学習には少なすぎるので、このままやるとのこと。

---
## PyTorchでやる

いろいろ思うところはあるけど、やっていきましょう。
PyTorchでも、MNISTデータをリサイズしたけれど、
![Screenshot_20200702_172913.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/cc3f4da0-3752-abf3-bb8a-e3a1da8da546.png)

TensorFlowとはだいぶ違う雰囲気になった。(同じ「3」の画像を使った)
![Screenshot_20200702_172945.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/a14094c8-032e-b450-5ab0-8e4fa92202fc.png)

リサイズアルゴリズムの違いだと思うが、詳しくは調べていない。

閾値を0.5でbool値にすると、ほとんどの画像が重複した(全部falseになった?)ので、閾値を0.2に下げた。
そうすると、315枚の画像が残って、重複は54枚だった。
TensorFlowに倣って、これでそのまま学習させる。

---
## モデル回路
TFQの実装に倣った。

- まず、画像(4x4 = 16ビット)サイズに合わせて、16 qubit用意する。各ピクセルのtrueに対応するビットをXゲートで|1>にする
- 測定用量子ビットを1つ付け足す
- 測定用量子ビットと、各ピクセル量子ビットの間にRXXゲートを噛ませる。回転角はパラメータにする(ピクセル数が16なので、全16パラメータ)
- 測定用量子ビットと、各ピクセル量子ビットの間にRZZゲートを噛ませる。回転角はパラメータにする(ピクセル数が16なので、全16パラメータ、RXXと合わせて32パラメータ)

Blueqat + PyTorchでは、入力テンソルとパラメータテンソルから量子回路を作って返す関数を用意し、その関数を「モデル回路」と考えることにした。

---
## 数値微分

前進差分法での数値微分をPyTorchに実装する。[以前書いた](https://qiita.com/gyu-don/items/ace6f69c07feb92d49b1)。

## Loss
TFQはHingeLossでやっていたが、PyTorchに見つからなかったので作った。

```py
class HingeLoss(torch.nn.Module):
    def __init__(self):
        super(HingeLoss, self).__init__()
        self.loss = torch.nn.MarginRankingLoss(margin=1.)

    def forward(self, x, y):
        zeros = torch.zeros_like(x)
        return self.loss(x, zeros, y)
```

---

## 学習

バッチサイズ16、エポック数3
各バッチごとのloss(赤線)と正解率(青線)。
グラフ用のデータ取りミスって、各エポックごとに変な線が入っているのは気にしない。

![Screenshot_20200702_175226.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/83f77856-f885-63a6-8f0c-82cb2ae2a4fe.png)
数時間かかった。

## 正解率
test用データで正解率を取ってみると1522/1968 (77.33739837398375 %)の正解。
TFQのチュートリアルでは8割を越えているので、悔しいが、結果が出るのに数時間かかるので、ハイパーパラメータを調整してやり直す気にはなれない。

---

## 古典との比較

TFQのチュートリアルで、

- CNNを利用して普通の大きさのMNIST画像で3と6とを識別
- フェアな比較になるように、4x4に縮小したMNIST画像で、パラメータ数も量子の32パラメータと近くなるように37パラメータのモデルを使って3と6とを識別

を試していた。

私の方でも、CNNは面倒で書かなかったが、37パラメータモデルは書いて試した。
学習はすぐに終わり、結果は1814/1968 (92.17479674796748 %)であった。

**衝撃ポイント3: 速度でも精度でも、圧倒的に古典のほうがいい**

---

## まとめ
量子機械学習をやった。

- 現状は何もメリットがない
- 想像を絶するほどショボい
- それでも、今後も量子機械学習はホットトピックだと思うので、PyTorch + Blueqatの方向性で今後も進めていきます

ソースコード(Jupyter Notebook): [Gist](https://gist.github.com/gyu-don/f7af13e32f9b18010c75ffdb825f97f0)に置きました。

[^1]: https://arxiv.org/abs/2003.02989
[^2]: https://github.com/tensorflow/quantum
[^3]: https://www.tensorflow.org/quantum/tutorials/mnist
[^4]: http://www2.yukawa.kyoto-u.ac.jp/~tomoyuki.morimae/supremacy202006.pdf