科学しよう

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

MENU

量子コンピュータで加算回路を作る

今回からは前回までの論理回路の知識を使って、実際に計算をしてみます。
まずはQuantumChalleng2020でも出題された加算回路についてです。

QuantumChalleng公式

加算回路のベースは古典回路の半加算器全加算器です。
加算回路ではXORANDORゲートを使って加算器を構成し、それらを最終的に量子ゲートに置き換えるということで実現が可能です。
そのため、今回の説明の大部分は古典回路の説明になります。

今回のエントリーではそのような回路を作るとどうして「1 + 1 = 2」などとコンピュータ上で計算ができるのかをご説明します。
主にこれまで論理演算や電子回路に触れてこなかった方向けの内容となります。

なお、実装はQiitaにいくつか記事が上がっています。
ここでは仕組みの理解を目指します。

対象とする読者

  • 量子プログラミングに興味を持ち始めたばかりのエンジニア
  • 量子計算に興味を持ち始めたばかりの高校生・大学生
  • 数学・論理演算・電子回路に慣れていない方
    など

対象でない読者

  • 計算機科学・電子工学関連ご出身の方
    など

※もちろん読んでいただける分には嬉しいですが、
特に前半部分は電子回路の授業で扱われていたり教科書にも載っている内容となりますので
いまさらと感じさせてしまうかもしれません。

前提とする知識

  • 簡単な論理演算(AND, OR, NOT)が分かること
  • 基本的な量子ゲート(X, CX, CCX)が分かること

バージョン情報

目次

コンピュータ・計算機で足し算をするとは

量子でも古典でも、ビット01の2つの値を取る物体・回路部品でしかありません。
それ自身に計算する機能も数値を表現する機能もありません。
数値の表現方法や計算方法を回路という形で人間が教えてあげることで、計算が可能になります。
(今ご覧なっているようにブログなどをグラフィックに表示したりアプリケーションを構築するというのはそれぞれのビットから見たらさらに高度なことです。)

実はこういった数字そのもの以外の「もの」を使った数値の表現や計算を人間は日常的にやっています。
それがこれから説明する「指・正の字・そろばん」などです。
まずはこれらで計算を行う仕組みを振り返って、コンピュータでの計算を理解しましょう。

指・正の字・そろばんなどで数を数える

「指・正の字・そろばん」のある状態と、その状態に対応する数値・数字の組み合わせは下図の通りです。

f:id:sakumadaisuke32:20210207182439p:plain
図1. 指・正の字・そろばんでの数の表現

それぞれ人間は次のようにものの状態から数値を解釈しています。

  • 指は立っているその本数で数値を示します。
  • 正の字は画数の合計で数値を示し、「正」の字が1つ出来上がれば「5」を示し、「正」の字が何文字出来上がったら「5 x 正の字の数」で5の倍数を示します。
  • そろばんは上がっているコマの数(下の段が「1」上の段が「5」)が10進法のある桁の数値を示します。

計算機上での数の表現(2進数)

コンピュータではこれらと同様に、複数のビットを1列に並べて、何番目のビットが01かで人間が数値だと認識しています。 それが2進数と呼ばれる表記です。
これもコンピュータやビットという「もの」にとっては01が並んでいるだけです。

2進数(0/1表記、古典ビット(トランジスタの電圧)、量子ビット(Bloch球))と10進数は下記のように対応させ、人間が意味づけます。

f:id:sakumadaisuke32:20210207184920p:plain
図2. 2進数と古典状態・量子状態・10進数との対応関係

計算機上で足し算をするための手順

ここまでで、「もの」の状態と人間が認識する「数値・数字」との対応を確認しました。
ではそれぞれのものの「元の状態」に「1」を足すときの手順を振り返りましょう。

f:id:sakumadaisuke32:20210207212011p:plain
図3. 元の数に「1」増えるときの「もの」の状態変化

  • 指は指をもう一本足すこと
  • 正の字はもう一画足すこと
  • そろばんはコマを1つ動かすこと

コンピュータの計算と一番近いのはそろばんです。
1つずつコマを上げていきますが、それができるのは4つまで。
5つめはなく、上段の「5」のコマを上げて、下段のコマはすべて下げます。 再び1増えるごとに下段のコマを増やしますが、「9」の次のコマがまたなくなります。
そうしたら左隣の桁のコマを1つ上げ、元々の桁のコマは全て下げ「10」を表現します。
これは「10 (左の桁の単位) x 1 + 1 (右の桁の単位) x 0」ということですね。

2進数ではそれが2ずつ起きます。
(表現するのに使える文字が01の2種類しかないため。)

計算を図にするとこちらです。

f:id:sakumadaisuke32:20210207214109p:plain
図5. 2進数の計算

この図でやっていることを手順として整理すると下記です。

  • 足し算するための入力となる2つの数(01)をビットとして2つ用意する
  • 計算結果の1桁目を次のルールで出力する
    • 入力が両方0なら0を出力:0 + 0 = 00を満たすため
    • 入力の片方が1なら1を出力:0 + 1 = 1 + 0 = 01を満たすため
    • 入力が両方1なら0を出力:1 + 1 = 10を満たすため
  • 計算結果の2桁目を次のルールで出力する
    • 入力が両方0なら0を出力:0 + 0 = 00を満たすため
    • 入力の片方が1でも0を出力:0 + 1 = 1 + 0 = 01を満たすため
    • 入力が両方1なら1を出力:1 + 1 = 10を満たすため
  • 出力は左から順に「2桁目を表現する回路の出力」「1桁目を表現する回路の出力」と並べ、人間はこの2進数表記と10進数表記を対応させて認識する。
    • 2進数の00 = 10進数の0
    • 2進数の01 = 10進数の1
    • 2進数の10 = 10進数の2
    • 2進数の11 = 10進数の3

これらを満たす回路を次節で組んでいきましょう。

古典での加算回路(半加算器・全加算器)の作り方

前節の内容で「1 + 1 = 2」を計算するための手順が分かりました。
これを「半加算器」と言います。
さらに下の桁からの桁上がりを考慮して足し算する回路を半加算器を組み褪せて作ります。
これを「全加算器」と言います。

半加算器

前節の内容を真理値表で表すと下記です。
1桁目はXORゲートで表せ、2桁目はANDゲートで表せます。 実際にそうなっているはずなので、確かめてみてください。

1桁目

入力1 入力2 出力
0 0 0
0 1 1
1 0 1
1 1 0

2桁目

入力1 入力2 出力
0 0 0
0 1 0
1 0 0
1 1 1

2つの入力ビットからそれぞれのゲートにつなぎ、それぞれのゲートからの出力を計算結果とします。 (下図) 一般に1桁目の計算結果であるXORの計算結果をSum(S)、2桁目の桁上がりをCarry(C)と呼びます。

f:id:sakumadaisuke32:20201212180842p:plain
図6. 半加算器。A, Bが入力。上の太い三日月型の部品がXORゲート。下の半楕円型の部品がANDゲート。SがSum(加算結果の一桁目)でCがCarry(加算結果の2桁目)。(図は http://www.cs.shinshu-u.ac.jp/Lecture/LogicCirc/chap9/chap9.htmlから引用。)

入力と出力の対応が下記です。
たしかに「0 + 0」から「1 + 1」までの計算が人間が行う結果と一緒になっていると思います。

半加算回路の計算結果

A B C S 計算(10進数)
0 0 0 0 0 + 0 = 0
0 1 0 1 0 + 1 = 1
1 0 0 1 1 + 0 = 1
1 1 1 0 1 + 1 = 2

全加算器

続いて全加算器です。
これは、下の桁からの桁上がりを考慮するための回路です。

例えばひっ算をするときに次の状況がよくあると思います。(2進数)

f:id:sakumadaisuke32:20210207221320p:plain
図7. 2進数のひっ算

この緑色の計算をコンピュータ上でもできるようにしたのが全加算器です。

要点としては、その桁の計算結果に下の桁からの桁上がり(Carryの値)を足して最終結果とするということです。

結論から先に、回路図と計算結果の表は下記になります。

f:id:sakumadaisuke32:20201212183311p:plain
図8. 全加算器。HA:Half Adder(半加算器)、FA:Full Adder(全加算器)の略称。Ci:下の桁からの桁上がり。Co:その桁で発生した桁上がり

A B Ci Co S 10進数での計算
0 0 0 0 0 0 + 0 + 0 = 0
0 0 1 0 1 0 + 0 + 0 = 1
0 1 0 0 1 0 + 1 + 0 = 1
0 1 1 1 0 0 + 1 + 1 = 2
1 0 0 0 1 1 + 0 + 0 = 1
1 0 1 1 0 1 + 0 + 1 = 2
1 1 0 1 0 1 + 1 + 0 = 2
1 1 1 1 1 1 + 1 + 1 = 3

ある桁の入力A, Bと下の桁からの桁上がり(Carry)Ciの3つの入力があります。 A, Bの計算を半加算器で行い、そのSumとCiも半加算器で計算します。
最終的な桁上がりは、最初のA + B の時点か、そのあとのA +B + Ciの結果のどちらかなのでそれを右下のORゲートで判断します。

実際にはこの全加算器が何個も連なることで大きな数の計算ができるようになっているのですね。
最後に出力となるビットの値(上の図だとSCo)を読んで2進数表記でならべることで下記のように人間はそのビット列を数値と認識します。

古典から量子への加算回路の置き換え

古典ゲートを量子ゲートに置き換え

前回までで古典論理ゲートと量子ゲートとの対応を確認しました。
そのままこの全加算器にも下記の要領で当てはめます。

f:id:sakumadaisuke32:20210207233739p:plain
図9. 古典ゲートを量子ゲートに置き換え

そしてこれは量子コンピュータの特徴ですが、ゲートの出力の線の本数は入力と同じになります。
よって、最終的に不要なビットが生じますが、無視して量子全加算器の回路は下図になります。

f:id:sakumadaisuke32:20210207235206p:plain
図10. 古典全加算器と量子全加算器の対応。量子加算器で文字がないビットは途中の演算で使うのみで最終的に測定しない。

計算例

例えばQuantumChallengeに提出した下記の回路を計算してみます。

f:id:sakumadaisuke32:20210208001804p:plain
図11. ひっ算の緑枠部の計算を全加算器で実現(A = 0, B = 1, Ci = 1)

この測定結果は下記です。 ひっ算で計算すると同じようにA + B + Ci = 0 + 1 + 1 = 10が成り立っていて確かに計算できてそうです

f:id:sakumadaisuke32:20210208001940p:plain
図12. 図11の測定結果

以上が量子ビットを使って足し算するための回路の組み方と計算が行われる仕組みです。
これは単純な足し算を量子回路上でどう実現するか、という話でしたが、他の様々な問題も工夫して量子回路に置き換えて何らかの答えを出せるようにします。

まとめ

  • コンピュータで計算をするにはXORANDゲートを組み合わせればよい
  • 量子コンピュータでも同様にXORANDゲートを組み合わせればよい
  • 一般に行いたい計算が実現できるように量子ゲートを組み合わせればよい

量子コンピュータで行う論理演算(3)

前回はMultiple Controlled Toffoli(MCT)を使って複数の条件のANDゲートの実装を紹介しました。
今回もCXゲート関連のテクニックの紹介です。
これで基本事項の紹介は最後にして次回からはちょっとした計算を紹介したいと思います。

今回は通常のプログラミングで

if not A: 
    処理

という場合の量子回路の作り方をご紹介します。

対象とする読者

  • 量子プログラミングに興味を持ち始めたばかりのエンジニア
  • 量子計算に興味を持ち始めたばかりの高校生・大学生 など

前提とする知識

  • 論理演算(AND, OR, NOT)が分かること
  • 基本的な量子ゲート(X, CX, CCX)が分かること

お断り
僕は基本Qiskitしか使わないので解説ソースもQiskitで書きます。
Q#やIonQなど他の言語・フレームワーク・ハードウェアに興味ある人には申し訳ないですが、考え方は共通していると思うのでご参考程度に、ということでお願いします。

バージョン情報

目次

量子回路でif not Aを実装する方法

先ほどご紹介しましたが、下記のような場合です。

if not A: 
    処理

実装方法

先に結論を述べますと、下記のようにCXゲートの制御ビットをXゲートで挟めばよいです。

qr_input = QuantumRegister(1, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(2)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.x(qr_input)
qc.cx(qr_input, qr_output)
qc.x(qr_input)

qc.barrier()

qc.measure(qr_input, cr[:1])
qc.measure(qr_output, cr[1])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210131181542p:plain
図1. if not Aを判断する量子回路

見やすさのためにqc.barrier()を追加していますが、計算上は不要です。

この測定結果は、下記のように制御ビット(ビット列の右側)が0の時に標的ビット(ビット列の左側)が1になります。
したがって、if文の条件に対応する制御ビットの値が0の時に条件が満たされてCXゲートが機能しているということです。

f:id:sakumadaisuke32:20210131181642p:plain
図2. 図1の回路の量子状態の分布

解説

「制御ビットの端子(黒丸)の直前でXゲートがかかって標的ビットが1になるんだから、CXゲートがかかって当然じゃん」
と思った方、その通りです。
それを利用しています。

この図の読み方を説明します。
この図は2つのXゲートと制御ビットの黒丸を1セットで読みます。
Xゲート - 黒丸 - Xゲートで一つの端子と思ってまとめて白丸に置き換えます。 その直前の状態がこの新しい端子への入力で、中でどんなゲートがかかるかはブラックボックスとして読んでください。

f:id:sakumadaisuke32:20210131182246p:plain
図3. 制御ビット入力端子の置き換え

そうすると、

  • 制御ビットは |0>状態で初期化
  • 新しいCXゲートの端子(制御ビットの状態が |0>の状態で標的ビットを反転する)
  • CXゲートを過ぎた後の制御ビットの状態はCXゲートに入る前の状態( |0>)のまま

という挙動になります。

ブラックボックスと書きましたが、左側のXゲートで反転させられた制御ビットの状態は右側のXゲートで元に戻すので、CXゲートはかかるけどXゲートはなかったことになります。
(実機ではXゲートを掛けている分ゲートエラーは乗りますが、数学上は影響なしです。)
※なお、この「元に戻す」というのは用語としてはUncomputation・Uncomputing(逆計算)といいます。

端子での値が1(True)の時に条件を満たすのが黒丸に対して、端子での値が0(False)の時に条件を満たすのが白丸ということで、役割が逆になっています。
この対応は量子計算のバイブルNielsen-Chuangでもちゃんと(しれっと)紹介されています。 *1 現在出回っている教科書では当たり前すぎるのか白丸については解説無いままのことが多いと思います。

量子回路でif not A and not Bのような複数の条件を実装する方法

すべてnotの場合

下記のような場合です。

if not A and not B: 
    処理

これは1つの場合のCXゲートCCXゲートに置き換えればよいです。

qr_input = QuantumRegister(2, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.x(qr_input)
qc.ccx(qr_input[0], qr_input[1], qr_output)
qc.x(qr_input)

qc.barrier()

qc.measure(qr_input, cr[:2])
qc.measure(qr_output, cr[2])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210131183414p:plain
図4. CCXゲートの制御ビットの入力が|00>状態の時に標的ビットの状態を反転する量子回路

この結果は下記で、標的ビットが反転しています。

f:id:sakumadaisuke32:20210131183544p:plain
図5. 図4の量子回路の量子状態の分布

3つ以上の場合はMCTを使います。

if not A and not B and not C: 
    処理
qr_input = QuantumRegister(3, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.x(qr_input)
qc.mct(qr_input, qr_output)
qc.x(qr_input)

qc.barrier()

qc.measure(qr_input, cr[:3])
qc.measure(qr_output, cr[3])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210131183623p:plain
図6. 制御ビットが3つ以上の場合

f:id:sakumadaisuke32:20210131183652p:plain
図7. 図6の量子回路の量子状態の分布

notが一部混ざっている場合

下記のような場合です。

if not A and B and not C: 
    処理

AとCがFalse(0)で、BがTrue(1)の時にif文の条件が満たされてif文の中の処理が実行されます。

これはソースは若干書くのが手間ですが、not xxになる端子だけXゲートを掛けます。

qr_input = QuantumRegister(3, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.x([qr_input[0], qr_input[2]])
qc.mct(qr_input, qr_output)
qc.x([qr_input[0], qr_input[2]])

qc.barrier()

qc.measure(qr_input, cr[:3])
qc.measure(qr_output, cr[3])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210131183819p:plain
図8. 制御ビットの状態が|010>の時に標的ビットの状態を反転させる量子回路

これはこのまま実行しても第2制御ビットが条件を満たさないので標的ビットは反転しません。

f:id:sakumadaisuke32:20210131183947p:plain
図9. 図8の量子回路の量子状態の分布

標的ビットを反転させるには下記のようにします。

qr_input = QuantumRegister(3, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.x(qr_input[1]) #|010>状態を入力とするということ

qc.barrier() 

qc.x([qr_input[0], qr_input[2]])
qc.mct(qr_input, qr_output)
qc.x([qr_input[0], qr_input[2]])

qc.barrier()

qc.measure(qr_input, cr[:3])
qc.measure(qr_output, cr[3])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210131184020p:plain
図10. 図8の量子回路の制御ビットの入力を|010>とした場合

見やすさのために、MCTの前にもqc.barrier()を追加しています。

ソースにコメントしていますが、この回路はMCTゲートへの入力は |010>としていて、標的ビットがちゃんと反転していることが下記の測定結果からも分かります。

f:id:sakumadaisuke32:20210131184112p:plain
図11. 図10の量子回路の量子状態の分布

まとめ

  • if not Aの量子回路での実装は、制御ビットの端子をXゲートで挟む。
  • 複数の端子がある場合は、notが対応する端子だけXゲートで挟む。

*1:Quantum Computation and Quantum Information (10th anniversary edition), 2010, M. A. Nielsen and I. L. Chuang, p185, Figure 4.11

量子コンピュータで行う論理演算(2)

前回に引き続き、論理演算を量子回路に実装する方法を解説します。

前回基本的な古典論理演算を量子回路に実装する方法の一つで、AND(論理積)演算をCCXゲートで実装する方法を紹介しました。 これは2つの入力の場合の演算ですが、一般に複数の入力に対してAND演算する方法をご紹介します。

対象とする読者

  • 量子プログラミングに興味を持ち始めたばかりのエンジニア
  • 量子計算に興味を持ち始めたばかりの高校生・大学生 など

前提とする知識

  • 論理演算(AND, OR, NOT)が分かること
  • 基本的な量子ゲート(X, CNOT, CCNOTなど)が分かること

バージョン情報

複数の入力についての古典ANDゲートとMulti Controlled Toffoli(トフォリ)ゲート

複数の入力についてANDゲート(論理積)を取るとは下記のような場合です

if A and B and C and D ....: 
    処理

通常プログラミングしていたらこのような場合に遭遇することがよくありますが、 このような条件の判定は、量子ゲートではMulti Controlled Toffoliゲート(MCTゲート)でできます。 ちょうどControlled-Xゲートの入力が複数に増えたものです。 (CCXゲートもMCTゲートの一つです。)

qr_input = QuantumRegister(3, 'input') <<  inputの数は好きに増やせる
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.mct(qr_input, qr_output)
qc.measure(qr_input, cr[:3])
qc.measure(qr_output, cr[3])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210124204946p:plain
Multi Controlled Toffoliゲートの実装例(左:制御ビット×3, 右:制御ビット×10)

Multi Controlled Toffoliゲートはqc.mct(qr_input, qr_output)で定義できますが、 この第一引数に設定されるビット数によって入力端子(制御ビット)の数を自動的に決めてくれます。

この入力端子の条件をすべて満たしたときに(上の図だとすべて |1>状態の場合に)標的ビット(output)にXゲートを掛けます。

そのようになっていることを前回同様inputの状態に対するoutputの状態を測定することで確認しましょう。

入力が |000>の場合

f:id:sakumadaisuke32:20210124211513p:plain
左:入力が000の場合の量子回路, 右:この時の測定結果

右側で測定されている状態は0001で、outputのビット(標的ビット)は一番左のビットで表現されているので、0のままですね。

入力が |100>の場合

f:id:sakumadaisuke32:20210124211736p:plain
左:入力が100の場合の量子回路, 右:この時の測定結果

こちらもoutput(一番左)は0のままです。

入力が |110>の場合

f:id:sakumadaisuke32:20210124211913p:plain
左:入力が110の場合の量子回路, 右:この時の測定結果

こちらもoutput(一番左)は0のままです。

入力が |111>の場合

f:id:sakumadaisuke32:20210124214038p:plain
左:入力が111の場合の量子回路, 右:この時の測定結果

ここでやっとoutput(一番左)が1に変化しました!

まとめ

  • 複数条件のAND演算はMulti Controlled Toffoli(MCT)ゲートで書ける
  • MCTは制御ビットがすべて条件を満たしている場合に、標的ビットにXゲートを掛ける

量子コンピュータで行う論理演算(1)

先日QuantumChallengeに参加してみていくつか感じたことの一つに
「問題設定が与えられた後、それを量子回路に対応させることが難しい」
ということがあります。
現状の量子計算はアカデミア要素が強いせいもあってか参考になる記事も少なく、自分で考えるということが強く求められるフェーズのプログラミングだと感じました。
論文や有名なアルゴリズムを実装してみたという記事はいくつか見つかりましたが、どうしてそのように量子回路を組むのかまではまだ踏み込めていない印象です。

つまり、数理的な素養やもともと量子計算・量子情報処理に関わっている・いたという人はいくらか苦労した上でプログラミングできるけど、そうでない人には敷居が高いと感じました。

というわけで、自分の勉強も兼ねて量子回路を書くためのテクニック集的なものをこれから書いていこうと思います。
量子情報処理が専門でない人でこれから量子計算始めたいという人が少しでもプログラミングできるようになる助けになれば幸いと思います。

今回はQuantumChallengのweek1Aで解説がされていた、量子ゲートと論理演算との対応についていくつか解説します。

対象とする読者

  • 量子プログラミングに興味を持ち始めたばかりのエンジニア
  • 量子計算に興味を持ち始めたばかりの高校生・大学生 など

前提とする知識(簡単に解説はします)

  • 論理演算(AND, OR, NOT)が分かること
  • 基本的な量子ゲート(X, CNOT, CCNOTなど)が分かること

お断り

僕は基本Qiskitしか使わないので解説ソースもQiskitで書きます。
Q#やIonQなど他の言語・フレームワーク・ハードウェアに興味ある人には申し訳ないですが、考え方は共通していると思うのでご参考程度に、ということでお願いします。

バージョン情報

目次

論理演算とは

2つないし複数の論理値(日本語で真/偽、Boolean型でいうところのTrue/False)を組み合わせて計算すること。
プログラミングでは主にif文の条件式で用いることで、処理の条件分岐を行います。

量子計算でも計算途中で条件分岐をすることが発生します。(今度紹介しますが加算器などの計算するにも根本的には条件分岐です。)
でも量子回路上にはif文は存在しないので、量子ゲートを組み合わせて実現します。

論理演算と量子ゲートの対応

古典NOTゲートとXゲート

NOTゲートというのは「○○でない」という元の「○○である」を否定することです。
通常のプログラミングだと、例えば性別フラグ・男性フラグis_manなどをBoolearn型で用意して、顧客データの集計などすることを想像してみてください。 あえてフラグの男女を反転させるとこういうケースです。

if not is_man: 
    cnt_woman += 1
else: 
    cnt_man += 1

古典NOTゲートは量子ゲートではXゲートが対応します。

qr = QuantumRegister(1, 'input')
cr = ClassicalRegister(2)
qc = QuantumCircuit(qr, cr)

qc.measure(qr, cr[0])
qc.x(qr)
qc.measure(qr, cr[1])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210117182807p:plain
Xゲート

この測定結果の分布は下記です。
Xゲートの性質を考えればその通りですが、入力が|0>状態に対して出力は|1>状態になっています。
測定結果のビットの並びは測定順と逆順で、左側がXゲート通過後で右側がXゲート通過前です。
(量子回路に書いてある測定の番号が若い順に文字列の右側から表示されます)

f:id:sakumadaisuke32:20210117183103p:plain
Xゲート前後の状態の分布

なお、量子ビットの状態は|0>で初期化されているので、何らかの理由で|1>状態を使いたい場合にもXゲートを使います。
以降の説明でもこの手法を多用します。

古典ANDゲートとCCNOT(CCX)ゲート

ANDゲートとは「AかつBである」と二つの条件の両方が満たされていることを示すものです。 例えば、性別ごとに職業を集計する時などでしょうか。

if is_man and is_sport_player: 
    cnt_man_sport += 1
elif is_man and is_engineer: 
    cnt_man_engineer += 1
elif is_woman and is_sport_player: 
    cnt_woman_sport += 1 
elif is_woman and is_engineer: 
    cnt_woman_engineer += 1
(以下略)

古典ANDゲートにはCCNOT(CCX)ゲート(2つのコントールビットがともに |1>状態であればターゲットビットの状態の0/1を反転する)が対応します。
CCNOTゲートの2つのコントロールビットを量子AND回路の入力、そしてCCNOTゲートのターゲットビットを量子AND回路の出力(論理演算した結果)に対応させます。
「AかつBである」のAは入力1の状態が1(BooleanでいうところのTrue)でBは入力2の状態が1に対応します。

qr_input = QuantumRegister(2, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.ccx(qr_input[0], qr_input[1], qr_output)
qc.measure(qr_input, cr[:2])
qc.measure(qr_output, cr[2])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210117184525p:plain
量子AND回路(入力が00の場合)

この結果は下記に示すように、ターゲットビットは0のままです。
コントロールビットである2つの入力がともに0だからですね。

f:id:sakumadaisuke32:20210117184739p:plain
量子AND回路の状態分布(入力が00の場合)

他のパターンは以下になります。(コードと入力が10の場合は省略します。)

f:id:sakumadaisuke32:20210117185007p:plain
量子AND回路(入力が01の場合)
f:id:sakumadaisuke32:20210117185036p:plain
量子AND回路の状態分布(入力が01の場合)

f:id:sakumadaisuke32:20210117185140p:plain
量子AND回路(入力が11の場合)
f:id:sakumadaisuke32:20210117185206p:plain
量子AND回路の状態分布(入力が11の場合)

最後の入力が11の場合のみ、ターゲットビットの状態が1になっていますね。
「入力1が1(True)でかつ入力2が1(True)」であることが示されたということです。

古典XORゲートとCNOT(CX)ゲート

XORゲートとは「AとBのどちらかが一方のみがTrueである場合」を示します。 例えば会社の部署の人数を集計する時に、兼務を除きたい場合です。

if (dept_A or dept_B) and not (dept_A and dept_B): 
    if dept_A: 
        cnt_only_A += 1
    else: 
        cnt_only_B += 1

古典XORゲートはCNOT(CX)ゲートを2つ組み合わせた量子回路が対応します。

qr_input = QuantumRegister(2, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.cx(qr_input[0], qr_output)
qc.cx(qr_input[1], qr_output)
qc.measure(qr_input, cr[:2])
qc.measure(qr_output, cr[2])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210117191232p:plain
量子XOR回路(入力が00の場合)

この状態の分布は下記で、入力どちらの状態も0なので出力も0です。

f:id:sakumadaisuke32:20210117191344p:plain
量子XORゲートの状態分布(入力が00の場合)

その他のパターンは以下になります。

f:id:sakumadaisuke32:20210117191444p:plain
量子XOR回路(入力が01の場合)
f:id:sakumadaisuke32:20210117191511p:plain
量子XOR回路の状態分布(入力が01の場合)

f:id:sakumadaisuke32:20210117191550p:plain
量子XOR回路(入力が11の場合)
f:id:sakumadaisuke32:20210117191614p:plain
量子XOR回路の状態分布(入力が11の場合)

最後の入力が11の場合は出力が0になっており、古典XORと同じ挙動になっていますね。

古典ORゲートとCNOT(CX)ゲート・CCNOT(CCX)ゲートの組み合わせ

ORゲートは「AとBのすくなくともどちらかTrueである場合」を示します。
XORと異なり、兼務を含めて集計する場合が相当します。

if (dept_A or dept_B): 
    cnt_AorB += 1

古典ORゲートはCNOT(CX)ゲート2つとCCNOT(CX)ゲート1つを組み合わせた量子回路が対応します。

qr_input = QuantumRegister(2, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.cx(qr_input[0], qr_output)
qc.cx(qr_input[1], qr_output)
qc.ccx(qr_input[0], qr_input[1], qr_output)
qc.measure(qr_input, cr[:2])
qc.measure(qr_output, cr[2])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210117192451p:plain
量子OR回路(入力が00の場合)

量子XOR回路と異なるのは、最後にCCXゲートがあることで入力が11の場合に2つのCXゲートを通ってターゲットビットの状態が0 -> 1 -> 0と変化した後、改めてターゲットビットを反転して1になることです。

上記の回路の分布は下記で、やはり入力がともに0なので出力も0です。

f:id:sakumadaisuke32:20210117192549p:plain
量子OR回路の状態分布(入力が00の場合)

その他のパターンは以下になります。

f:id:sakumadaisuke32:20210117192637p:plain
量子OR回路(入力が01の場合)
f:id:sakumadaisuke32:20210117192702p:plain
量子OR回路の状態分布(入力が01の場合)

f:id:sakumadaisuke32:20210117192731p:plain
量子OR回路(入力が11の場合)
f:id:sakumadaisuke32:20210117192750p:plain
量子OR回路の状態分布(入力が11の場合)

最後の入力が11の場合に出力が1になっており、古典ORと同じ挙動になっていますね。

古典NANDゲートとCCNOT(CCX)ゲート

NANDゲートとは「AかつB以外の場合」を示します。 例えば、男性のスポーツ選手以外の人数(男性でスポーツ選手以外の人数 + 女性)を集計する場合などです。(あくまで例なのでこういう集計をする状況があるかは分かりません)

if not (is_man and is_sport_player): 
   cnt_not_man_and_sport += 1    

古典NANDにはCCNOT(CCX)ゲートのターゲットビットにXゲートを組み合わせた量子回路が対応します。

qr_input = QuantumRegister(2, 'input')
qr_output = QuantumRegister(1, 'output')
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr_input, qr_output, cr)

qc.x(qr_output)
qc.ccx(qr_input[0], qr_input[1], qr_output)
qc.measure(qr_input, cr[:2])
qc.measure(qr_output, cr[2])
qc.reverse_bits()
qc.draw(output='mpl')

f:id:sakumadaisuke32:20210117193527p:plain
量子NAND回路(入力が00の場合)

量子ゲートそのものはそれぞれの量子ビットの状態を操作することなので、行いたい演算の結果がその通り得られるならターゲットビットにもXゲートをかけていいのです。 (かけちゃいけないというルールもない)

この結果は下記で、入力が00の場合は「AでなくBでもない」ので「AかつB以外の場合」に含まれておりNANDの結果はTrueです。
実際に出力は1になっています。

f:id:sakumadaisuke32:20210117193910p:plain
量子NAND回路の状態分布(入力が00の場合)

その他のパターンは以下になります。

f:id:sakumadaisuke32:20210117193951p:plain
量子NAND回路(入力が01の場合)
f:id:sakumadaisuke32:20210117194016p:plain
量子NAND回路の状態分布(入力が01の場合)

f:id:sakumadaisuke32:20210117194044p:plain
量子NAND回路(入力が11の場合)
f:id:sakumadaisuke32:20210117194110p:plain
量子NAND回路の状態分布(入力が11の場合)

最後の回路でNANDの特徴である、入力がともに1の時は出力が0になっていて「AかつBの場合」は「AかつB以外の場合」にふくまれていないことが示されています。

まとめ

  • 基本的な古典論理演算に対応する量子論理回路を量子ゲートを組み合わせて表現することができた
  • 複雑な論理演算はここで紹介した量子論理回路を組み合わせる

IBM Quantum Challenge 2020 体験記

こんにちは、sakumatchoです。 2020/11/9 ~ 2020/11/30の22日間実施されたIBM Quamtum Challenge 2020に参加した体験記を書きます。

対象とする読者

  • これから量子プログラミングを始めたいという方
  • すでに学んでいるけど実践する場・挑戦する場が欲しいという方
  • 惜しくも参加できず内容が気になる方

など

目次

自己紹介

  • 職業:データサイエンティスト
  • 趣味:
    • 量子計算(趣味で量子計算だなんて良い時代になったもんだ)
    • プログラミング
    • 水泳(観る・やる、両方)
  • マインド:
    • 機械学習や量子計算を現実の問題に適用させようとしています
    • 一人でも量子計算に興味を持つ人が増えてほしいと願っています

IBM Quantum Challengeとは

こちらで記載されていますが、IBMが年に一度主催している量子コンピューターの競技型オンライン・プログラミング・コンテストです。 コンテストではありますが、初心者向けの導入から課題が与えられるので、Pythonさえ書ければ量子プログラミング・量子計算が初めての方でも参加可能です。

IBM Quantum Challenge 2020 の出題内容

詳細は僕のGitHubでノートブックを公開していますのでご確認ください。 ここでは概要だけ示します。

なお、公式でも公開されています。

期間は3週間で、問題は1A, 1B, 2A, 2B, Finalの全5題でした。
1連の問題を解くとGroverの探索アルゴリズムを使いこなせるようになっており、また量子並列性や重ね合わせ状態の威力を実感できる内容となっています。

それぞれの課題では、前半で基礎知識を導入し、後半で問題が1題出題され、量子回路を構成する関数を作成して関数を提出します。
提出用に運営が作成した関数に関数を引数として渡し、チェック処理が走って構成した量子回路が必要な入出力や条件など満たしていれば正答となり次の課題に進むことができます。

なお、導入部ではIBMQ ExperienceのナビゲータであるDr. Ryoko(量子(りょうこ)ちゃん)とのアドベンチャーストーリーが展開され、課題を解くごとに先に進む話がまた面白いです(笑)

僕は今回期間内に2Bまで解くことができ、Finalはいまだ解けていません(笑) 解けた人ホントすごいです!

なお、IBMからbadgeをもらいました!

www.youracclaim.com

1A:量子全加算器

導入

  • 量子ゲートの使い方
  • 古典論理ゲート(AND, OR, NOT, XOR, NANDなど)を量子ゲートで再現
  • 古典半加算器を量子ゲートで再現
  • 量子回路のコスト計算の方法

古典半加算器

f:id:sakumadaisuke32:20201212180842p:plain
図1. 古典半加算器。A, Bが入力でSがSum(加算結果の一桁目)でCがCarry(加算結果の2桁目)。(図は http://www.cs.shinshu-u.ac.jp/Lecture/LogicCirc/chap9/chap9.htmlから引用。)

図1が通常の論理回路(量子と対比して古典回路とここで呼ぶことにします。)で作成される半加算器です。 回路の上側で左側が弧を描いている部品がXORゲート(排他的論理和)で、下側で半楕円の部品がANDゲート(論理積)です。

量子半加算器

f:id:sakumadaisuke32:20201212181146p:plain
図2. 量子半加算器。0 ~ 3の4つ量子ビットがあり、0と1が入力、2がSumで3がCarryに相当します。

図2が量子回路で作成した半加算器です。 回路を縦破線で区切って一番左でXORゲート、中央でANDゲートを作成し、最後に2と3の量子ビットを測定して計算結果の出力としています。

という感じに、集合演算は古典回路を量子で再構成することで同じように実行することができます。 「なんだ、じゃあ量子コンピュータも従来のコンピュータとできること変わらないのか、、」なんてことはありません! 後でGorverのアルゴリズムを使うと分かりますが、論理演算を並列に実行できるところが量子コンピュータのすごいところです!

課題

下記の真理値表を満たす全加算器を量子ゲートだけで実装します。 僕が提出した回路はオーソドックスなものですが、別解もあり1問目からやりこみが発生したようです。

全加算器の真理値表

A(input) B(input) X(carry input) S(sum) C(carry out)
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 0 1
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1

提出した全加算器

f:id:sakumadaisuke32:20201212183025p:plain
図3. 提出した全加算器。半加算器を2つ組み合わせた。左・中央が半加算器、右がそれぞれの半加算器のCarryのOR。

図3が提出した回路で、課題の入力に合わせて最初にXゲートを加えている以外は基本的には先の半加算器を2つ組み合わせた構造です。 図4の古典全加算器と同じ構造になっていることを確かめてみてください。

古典全加算器

f:id:sakumadaisuke32:20201212183311p:plain
図4. 全加算器。(図は http://www.cs.shinshu-u.ac.jp/Lecture/LogicCirc/chap9/chap9.htmlから引用。)

公式解答

学べる事・身につくこと

  • 量子ゲートを使った論理演算の方法

1B:Groverのアルゴリズムの反復回数

導入

Groverのアルゴリズムの実装(外部入力を用いた場合)

f:id:sakumadaisuke32:20201212190745p:plain
図4. Groverのアルゴリズム。0と1が探索したい状態を生成する量子ビットで、2が外部入力となる量子ビットです。

f:id:sakumadaisuke32:20201212190941p:plain
図5. シミュレータでの実行結果。正解状態である|11>のみが検出されている。

図4の回路を構成して実行すると、最初に0と1はアダマールゲートが実行されることによって|00>, |01>, |10>, |11>の4状態が等確率で生成されるはずですが、結果は図5のように|11>だけが残ります!

繰り返し回数と確率振幅の大きさの関係

f:id:sakumadaisuke32:20201212192104p:plain
図6. Groverのアルゴリズムを複数回繰り返す場合の量子回路

f:id:sakumadaisuke32:20201212191402p:plain
図7. Groverのアルゴリズムの繰り返し実行した場合の確率振幅の大きさの変化

4量子ビットを使って|0000>から|1111>の中から|0111>だけ増幅するという例題で回路は図6で、Groverを3回繰り返している例です。
ここでのGroverの繰り返し回数はオラクル(一番縦に長いMCT)の数を数えてください。
3本あるはずです。

その|0111>の確率振幅の大きさは図7のように繰り返し回数に対して \sinで変化します。

課題

例題と同様に使う量子ビットの数を7にして、[tex: N=27 = 128]個の状態の中から、|0000111>だけを増幅しその確率振幅の大きさが最大になる繰り返し回数を求め、回数を答えます。

f:id:sakumadaisuke32:20201212191946p:plain
図8. 実行回数と確率振幅の大きさの変化

例題と同様の回路(図6)を7量子ビット用に直して実行すると図8のような結果が得られます。 図からは8回で最大となるので、これが答えです。

公式解答

学べる事・身につくこと


2A:Groverのアルゴリズムでライツアウトパズルを解く

導入

  • ライツアウトパズルとは

ライツアウトパズル

f:id:sakumadaisuke32:20201213170249p:plain
図9. ライツアウトパズル。左上が出題される初期状態の点灯状態で、この例題では3か所のマスをpushすることで、すべてのマスを消灯状態にすることができクリアとなる。

図9に示すのがライツアウトパズルと解答例です。 初期の点灯状態がinputの配列で与えられ、1が点灯で0が消灯を示します。 対してすべてのマスを消灯状態にする正解状態をoutput配列として求め、1がpushで0がunpushを示します。

課題

例題と同じく3 x 3のライツアウトパズルをGroverのアルゴリズムだけを使って解きます。
入力は3 x 3 = 9マスの点灯状態を示す0/1の配列数9の任意の配列とし、パズルの解としてpushするボタンの0/1を示すやはり配列数9の配列を測定結果として取り出す量子回路を構成する関数を作成し、関数を提出します。 ただし制約が大きく2つあります。 - 外部のソルバなどを使ってパズルを解くことは不可でGroverアルゴリズムだけを使うこと - 出題された問題だけでなく、任意のライツアウトパズルを解ける回路であること

f:id:sakumadaisuke32:20201213171734p:plain
図10. 出題された面。

提出した回路

f:id:sakumadaisuke32:20201213171351p:plain
図11. 提出した量子回路。前半が問題の埋め込みと正解状態を検出するOracle回路で、後半がその正解状態の確率振幅の大きさを増幅するDiffusion回路です。

f:id:sakumadaisuke32:20201213171517p:plain
図12. 計測結果。1つの状態だけ検出確率が突き抜けています。これが出題された面についての正解状態です。

図11が僕が提出した量子回路(長くてデバッグがしんどい、、)で、図12がその回路の計測結果です。 1つの状態だけ確率が飛びぬけていて、それが実際正解の状態で[1, 1, 0, 0, 1, 0, 1, 0, 1]です。 僕のGitHubではこの量子回路を構成するまでの検証も含めてご紹介しています。

公式解答

学べる事・身につくこと


2B:Groverのアルゴリズムで複数のライツアウトパズルを同時に解く

導入

  • 4つのライツアウトパズルの重ね合わせ状態
  • QRAM

4つのライツアウトパズルの重ね合わせ状態

f:id:sakumadaisuke32:20201214020817p:plain
図13. 4面重ね合わせ状態の出題例です。4つの内、1回の操作でクリアできる面はどれかという例題で、ここではBoard1が正解です。

重ね合わせ状態のライツアウトってどういうことだよ、って突っ込みたいところですが、 このストーリによるとこのライツアウト自体が量子ビットでできていてこの4状態でfluctuateしているそうです。
量子の世界の話なら仕方ないですね、解きましょう。

今回はpushするマスの配列を求めて終わりではなく、それも求めた上で各面のpushするマスの数を数えます。 例題では1回で解ける面を求めよというものでした。

古典の世界なら順に解いていけば解けるのですが、量子の世界だそうで1度で4面解いて目的の面を答えないといけません。
でも大丈夫、Groverならできるそうです。

qRAM

f:id:sakumadaisuke32:20201214021615p:plain
図14. qRAMを使ったGroverのアルゴリズムの概念図。

課題

図13と同様な3 x 3のライツアウトパズルが4面同時に出題され、その中に一つだけ3箇所だけpushして解ける問題があり、それが何番目の面かをGroverのアルゴリズムで当てるというものです。 提出するのは問題を解いて2ビットで表された0~3の面番号を測定して出力するような量子回路を構成する関数です。

図で示すと長いのでノートブックを参照してください。
コンセプトとしては公式のヒントにある図15の回路です。
ラクルの中でオラクルを記述するせいで量子回路が長くなります。

f:id:sakumadaisuke32:20201214091810p:plain
図15. 課題2Bのコンセプト

公式解答

学べる事・身につくこと


Final:Groverのアルゴリズムで複数のアステロイドパズルを解く

公式からのノートブック公開が12/14現在ありませんので、解説を書けていませんが僕のノートブックをご参照ください。

導入

f:id:sakumadaisuke32:20201215005707p:plain
図16. アステロイドパズル。図では水色の3列消しています。

ステロイドパズルとは図16に示したように面に配置された星を縦横1列まとめて消せます。 面に配置された星をすべて消去できればクリアです。

課題

4 x 4のアステロイドパズルが16面同時に出題され、15面は縦横計3列消せばクリアできます。 その中に一つ含まれている3列消しでは解けない面が何番か当てることが課題です。 人間が目で見て解くことも可能ですが、やはり重ね合わせで出題されるのでGroverのアルゴリズムで解かないといけないという制約があります。 この問題を解いて面番号を出力する量子回路を構成し提出します。

まだ解けていません。 3列消去で解ける15面の解を抽出することはできましたが、それぞれの面番号と3列消去で解ける・解けないというフラグを結び付けられていません、、 3列消去で解ける問題は4列以上を消去しても解けるので、そのあたりの枝刈りもしないといけません。 これを解けた人が全体の17%くらいで200人くらいいるみたいですが、すごいです。

学べる事・身につくこと

  • これでGroverのアルゴリズムマスター
  • 枝刈りなど数理的な素養も試されている感じがあります

感想

  • Groverのアルゴリズムはデータベース探索としか知りませんでしたが、データベースだけでなくより広い問題を解くことだできる便利なアルゴリズムであることが分かった。
  • ライツアウトもアステロイドもトイプロブレムだけれども、量子計算を使って問題を解くことが出来て嬉しかった。 量子情報処理を知って10年経って「量子計算スゲー」って思えました。 (それまでは論文のイントロとかで、この結果は量子計算にも応用ができるらしいとか、量子計算で並列計算ができるらしいとか字面でしか知らなかった)
  • こういう問題を解いていった結果のノウハウが溜まって、いずれ実際の問題を解けるようにしていきたい!
  • 今回はQASMシミュレータ上での計算だったので、実機で動くのか知りたい。
  • 来年も開催されたら、最後まで解きたい!


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

kaggle体験記: Global Wheat Detection (物体検出による小麦の穂の検出)

8月4日まで開催されていたkaggleの「Global Wheat Detection」コンペの体験談と学んだことをまとめます。

このコンペは様々な地域で撮影された写真から小麦の穂を検出するという物体検出の課題でした。

なお、僕は物体検出はもちろん深層学習もほぼ初心者のため、このエントリーでは技術的に深い内容は書けていないと思います。
逆にこれからkaggle始める、深層学習を使い始める、物体検出を始める、という方に近い内容となっております。

初めてのエントリーでわかりにくいところもあるかと思います。
ご指摘いただけますと幸いです。

目次

1. コンペ概要

https://www.kaggle.com/c/global-wheat-detection

小麦は世界中の食卓に使われていて、植物学的にも農業的にも小麦の分布を知るということが重要らしい。
ただし、これまでの物体検出の取り組みでは地域によってことなる小麦を同じモデルでは検出できていなかった。
このコンペでは複数の地域の小麦の画像を混ぜたデータセットを用いて一つのモデルで小麦を検出できることを目指す。

ということから、地域によるバイアスがかからない柔軟なモデルを構築する必要があります。一昔前に問題になった人種バイアス・性別バイアスにも通じますね。

画像データサマリ

学習データ画像

  • 3422件

評価データ画像

  • 10件(公開データの件数、非公開なデータでは1000件)

評価方法

評価指標

ノートブックの規定

 このコンペはノートブックを提出する必要があります。
https://www.kaggle.com/c/global-wheat-detection/overview/code-requirements

  • CPUだけを使う場合は9時間以内に実行が終了すること
  • GPUを使う場合は6時間に実行が終了すること
  • TPUは使用不可
  • 提出するノートブックについてインターネットは使用不可
    →kaggle kernel標準でインストールされていないライブラリなどをインターネットからpipすることは不可(whlファイルなどをデータセットとしてアップロードしておくことや、gitレポジトリをあらかじめcloneしてデータセットとしてアップロードしておくことは可)
  • 学習済みモデルなど外部データを使うことは可
  • kaggle kernel内でライブラリを編集することは不可
  • 提出ファイル名は「submission.csv」であること

2. 参戦動機

scikit-learnやSQLを使った統計的機械学習はこれまでの業務で経験したり勉強してきましたが、深層学習はありませんでした。
画像処理や物体検出は将来アプリ開発をしてみたく、今のうちに経験しておこうという動機です。
具体的には下記となります。

  • PyTorchを使えるようになりたい
  • 深層学習の勉強をしたい
  • 物体検出の経験を積みたい
  • 画像処理の経験を積みたい
  • (将来的に)メダルが欲しい
    →今回上位50%にやっと入った程度なので、力不足でした、、

3. 結果として経験になったこと

  • PyTorchで学習済みモデルを使った再学習の標準的な流れ
  • FasterRCNNとYOLOv5の使い方
    →YOLOv5は使ってみたけど最終的にはライセンスの問題で使用不可となりました
  • 画像の前処理のライブラリ(albumentation, OpenCV)の使い方
  • 画像の代表的な前処理(DataAugumentation)
  • kaggle kernelの使い方
    ・datasetのアップロード方法
    ・internet使用不可の場合のライブラリ追加方法


4. EDA

小麦画像可視化

バウンディングボックスの位置・幅・高さは画像ごとに下記のようにCSVで与えられています。

f:id:sakumadaisuke32:20200817074841p:plain

小麦画像のバウンディングボックスCSV

"width", "height"列は画像自体のサイズで、1024px x 1024pxの画像ということです。
"x", "y" は各バウンディングボックスの左上の座標で、"w", "h"がその幅と高さです。
これらを基に下記のような画像を作ることでバウンディングボックスの形状や配置を理解することができます。

f:id:sakumadaisuke32:20200817072157p:plain

小麦画像とバウンディングボックス

異常な大きさのバウンディングボックス

バウンディングボックスのCSVから面積の分布を観察しました。

f:id:sakumadaisuke32:20200817075725p:plain

バウディングボックスの大きさの分布(実軸)

f:id:sakumadaisuke32:20200817075813p:plain

バウンディングボックスの大きさの分布(対数軸)

上のグラフからは面積50万平方pxということで、1024px四方にも関わらず700px x 700px程度の大きさもありうることが示唆され、また逆に下の図からは数平方pxの非常に小さいバウンディングボックスが存在することがわかります。
実際に対象の画像を可視化すると下記のようになっていました。

f:id:sakumadaisuke32:20200817080309p:plain

最大の面積のバウンディングボックスを持つ画像
(複数の小麦を含む大きなバウンディングボックス)

f:id:sakumadaisuke32:20200817080728p:plain

最小の面積のバウンディングボックスを持つ画像

最大面積のバウンディングボックスの方は見ての通りですが、最小面積の方は右下をよく見ると

f:id:sakumadaisuke32:20200817080938p:plain

最小面積のバウンディングボックス詳細

という点のようなバウンディングボックスがあります。

これらは教師データとしてはノイズとなるので、除外します。
具体的には面積をあらかじめ計算し、観察した結果から上限・下限を決め打ちし、範囲外のバウンディングボックスを除外します。

また、学習用のデータセットの画像はより大きな画像から切り出された画像であることが元の論文からわかるそうです。(僕は未着手です)
なんとかして切り貼りして元の画像を復元することで新しい画像を作り出すこともできたようです。
https://www.kaggle.com/c/global-wheat-detection/discussion/149805


5. 重要だった前処理

画像処理

用いたライブラリ

 

前処理の種類

アフィン変換

  • 回転
  • 拡大・縮小
  • 上下左右反転(マイナス倍の拡大縮小)
  • せん断(やってない)
  • 平行移動

https://imagingsolution.blog.fc2.com/blog-entry-284.html

の画像がわかりやすく、大きく4種類です。


クロッピング

画像の一部分を切り出すこと

 

HSV変換

画像データの表現方法の一つ、HSV(Hue-Sturation-Value=色相-彩度-明度)を調整すること

 

モザイク

ここでのモザイクは画像をぼかすことではなく、複数(今回は四枚)の画像を切り貼りして一つの画像とすることでした

 

cut-out

CNNの正則化手法の一つで、画像の一部を塗りつぶして欠落させること

これらを試した結果の前後が下図です。

f:id:sakumadaisuke32:20200817083002p:plain

前処理前

f:id:sakumadaisuke32:20200817083045p:plain

前処理後

画像処理後、範囲外やcut-outで画像が小麦がなくなった箇所のバウンディングボックスは削除や変形が必要です。
albumentationsを用いた場合には自動で処理してくれるメソッドもありますが、してくれないメソッドもあります。

なのでcut-outやモザイクは下記のノートブックのように自作関数で処理しています。

https://www.kaggle.com/nvnnghia/awesome-augmentation#Augmentation-functions

https://www.kaggle.com/kaushal2896/data-augmentation-tutorial-basic-cutout-mixup#Let's-define-new-augmentations

また、僕は今回適用する時間がなかったのですが下記のmix-upも有効だったようです。

mix-up

2つの画像を透過させる形で重ね合わせる手法です。
概要は下記の記事をご参照ください。
https://qiita.com/yu4u/items/70aa007346ec73b7ff05
https://www.kaggle.com/kaushal2896/data-augmentation-tutorial-basic-cutout-mixup#Let's-define-new-augmentations

Pseudo-labeling(擬似ラベリング) 

COCOなどで作成された事前学習モデルから今回の小麦データで再学習する前に、検証用のpublicの画像データにバウンディングボックスを推論し、検証用データも学習データに含めること。
データの水増しとエントロピー減少に役立つらしい。
ノートブック
https://www.kaggle.com/nvnnghia/fasterrcnn-pseudo-labeling
元論文
http://deeplearning.net/wp-content/uploads/2013/03/pseudo_label_final.pdf

 

6. よく利用されていたアルゴリズム

Faster-RCNN

https://qiita.com/mshinoda88/items/9770ee671ea27f2c81a9#2-5-faster-r-cnn

EfficientDet

https://www.kaggle.com/shonenkov/training-efficientdet

YOLOv5(ライセンスの問題で使用不可) 

https://github.com/ultralytics/yolov5

7. 苦労したこと

データ拡張のハイパーパラメータ調整

上記の前処理ではHSV・回転角・平行移動の距離などを元の画像からどれだけ変調するかというのがパラメータでした。
またここで紹介した以外にもコントラスト・ブライトネスの調整した方がいいのかなど、public scoreを見てみないとわかりませんでしたので、試したものを全て記録・比較しました。

f:id:sakumadaisuke32:20200817092652p:plain

前処理結果の比較

実行した結果がどのノートブックかわかるように「ver」で管理していました。

albumentationsでのデータ拡張とそれ以外のデータ拡張を組み合わせること

top kernelの方々を参考にしつつ上記の要領で前処理を試しては追加しました。
この時の実行順やデータ型が適切でなくてエラーになったり、バウンディングボックスの座標が崩れました。

特に順番に決まりはないと思いますが、自作関数で前処理を追加した前後ではバウンディングボックスの数・位置・サイズを確認し、削除されるべきものが削除されているかなど確認しました。

dataloderのreturnではtorch.Tensorになっているようにしました。

YOLOv5など外部レポジトリを用いてモデル作成するときのバージョン管理

特にYOLOv5が日々アップデートされていたからですが、後から追いつこうとした場合に採用しているpytorchなどのバージョンが異なっていて、あるべきメソッドがなかったり、CUDAがうまく使えなかったです。

discussionやノートブックが共有されたら早めにキャッチアップしましょう。

8. 反省・これから頑張りたいこと

  • PyTorchのお作法を学ぶ
    ・入力データのTensorのndimやshapeがどうあるべきか
    ・DatasetやDataloaderの挙動を理解する
    ・学習済みモデルの重みのセーブ・ロードの方法に何パターンかあり、状況に応じて使い分けられるようになる
  • (既存の手法を組み合わせるとしても)ゼロからスクリプトを書けるようになること
    ・今回はtop kernelのノートブックをforkしてそのまま使わせていただいたので、自分で必要と思う処理を探してきて、インターフェースに気をつけながら自分で組み合わせられるようになる
  • top kernelのノートブックを鮮度が新しいうちに真似すること
    ・時間が経つとノートブック内で参照されている外部レポジトリがアップデートされて、ノートブックだけforkしてもそのままでは実行できない状態になっている。その結果デバッグに無駄な時間を費やす(結果的に勉強にはなるのだけど、コンペ向きではない)


9. 基礎知識

kaggleカーネル

  • データセット
    外部ファイルなどをノートブックで使えるようにするには、kernelの右側で「add data」でできます。

    f:id:sakumadaisuke32:20200817085947p:plain

  • pip
    インターネットが使えるコンペであればいつものこれです
    !pip install ライブラリ名
    手元で実行している分にはこれでいいですが、submitする場合にはこれではダメです。
    これはあらかじめwhlファイルを取得してkaggle datasetにアップロードしておくことで対処できます。
    https://www.kaggle.com/c/severstal-steel-defect-detection/discussion/113195
  • GitHub連携
    データセットアップロード画面にGitHubの項目があります。

    f:id:sakumadaisuke32:20200817090657p:plain



  • 読み取り・書き込み権限
    ・inputフォルダ:予めデータセットとしてデータを格納することが可能。
    しかし、後からファイル保存や書き込みは不可。
    ・outputフォルダ:予めデータを格納することは不可。
    しかし、後からファイル保存や書き込みは可能。
    使用するレポジトリの都合でファイルが作成される場合などはこちらに出力するようにしておく必要がある。
  • ノートブックから「.py」ファイル中でCUDAを実行するための環境構築
    コンペ終了直前でPyTorchが1.6.0にアップデートされました。
    そのままYOLOv5などのスクリプトを実行しようとすると、CUDAが認識されませんでした。
    原因は不明ですが、下記のノートの通りに「torch==1.6.0+cu101」であれば実行可能でした。
    https://www.kaggle.com/okeaditya/what-s-new-in-pytorch-1-6

評価指標

  • MAP(Mean Average Precision):
    ・物体検出の評価指標の一つ
    ・検出された物体のうちラベルが正解だったものの割合がPrecision
    ・IoUの閾値を0~1で変化させた時の平均がAverage Precision
    ・全ての画像についてAverage Precisionを平均したのがMean Average Precision
  • IoU(Intersection of Union) :
    ・物体検出の評価指標の一つ
    ・教師データと推論したバウンディングボックスの重なりの正確さを示す
    ・(教師データと推論結果の領域の積集合) / (教師データと推論結果の領域の和集合)
    ・全く重なっている部分がなければ0、ぴったり重なっていれば1、一部分が重なっていたり領域が包含関係にある場合には0~1の間となる。