Skip to content

Instantly share code, notes, and snippets.

@mohzeki222
Last active May 22, 2024 02:17
Show Gist options
  • Save mohzeki222/19aff66598ac9d23576cdae694437160 to your computer and use it in GitHub Desktop.
Save mohzeki222/19aff66598ac9d23576cdae694437160 to your computer and use it in GitHub Desktop.
QC4U2_day3_QML.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"private_outputs": true,
"provenance": [],
"authorship_tag": "ABX9TyOTEXvpsn3nTjqn5oofy1i9",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/mohzeki222/19aff66598ac9d23576cdae694437160/qc4u2_day3_qml.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"# 量子回路をニューラルネットワークに組み込もう\n",
"\n",
"さてそれではPyTorchで簡単な機械学習を実践することができるということがわかりました。\n",
"私たちの今回の目標は量子コンピュータを利用した機械学習を実践することです。\n",
"素朴には量子コンピュータで作られた量子回路をニューラルネットワークの中に組み込むということが考えられます。\n",
"まず前回と同様にpennylaneを導入しましょう。"
],
"metadata": {
"id": "uGmLZB6KFinL"
}
},
{
"cell_type": "code",
"source": [
"!pip install pennylane"
],
"metadata": {
"id": "1RNzd-fWFgHp"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"!pip install pennylane-lightning pennylane-lightning[gpu] pennylane-qiskit"
],
"metadata": {
"id": "bGPCd7N_FjHo"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"結果の表示のために利用するqutipも同様にインストールしておきましょう。"
],
"metadata": {
"id": "Qr6dFDadOkJ1"
}
},
{
"cell_type": "code",
"source": [
"!pip install qutip"
],
"metadata": {
"id": "D97a0mamOnbg"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import pennylane as qml\n",
"import numpy as np\n",
"import qutip"
],
"metadata": {
"id": "ARi4NZIVGBXn"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"それでは前回の復習でもありますが、量子回路を作ってみましょう。"
],
"metadata": {
"id": "9ynyTw0YF0U4"
}
},
{
"cell_type": "markdown",
"source": [
"まず量子ビットの特徴として、回転をさせることができて、その角度によって0と1の出る確率が決まります。\n",
"0と1が確率的にそれぞれ登場するような状態は、「重ね合わせの状態」と呼ばれ、その登場確率は0と1の確率振幅により決まります。\n",
"その確率振幅を半々の確率に対応するように揃えるには、アダマール回路というものを利用します。"
],
"metadata": {
"id": "mvoNF3b-MnLU"
}
},
{
"cell_type": "code",
"source": [
"n_qubits = 1\n",
"dev = qml.device('lightning.qubit', wires=n_qubits)\n",
"@qml.qnode(dev)\n",
"def circuitH():\n",
" #qml.RX(np.pi/2,wires=0)\n",
" qml.Hadamard(wires=0)\n",
" return qml.state()"
],
"metadata": {
"id": "DAFYYmyBND_b"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"ここにあるアダマール回路は、初期状態である0という状態から重ね合わせの状態、0と1が半々に重ね合わせられた状態になります。\n",
"実際に出来上がった量子状態を見てみましょう。"
],
"metadata": {
"id": "8nLCfuewOLzk"
}
},
{
"cell_type": "code",
"source": [
"fig, ax = qml.draw_mpl(circuitH)()\n",
"fig.show()"
],
"metadata": {
"id": "1c9ppa_BOUZ2"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Hというのが量子ビットにかかっていることがわかるでしょう。\n",
"この状態がどんな状態にあるのかをみてみましょう。"
],
"metadata": {
"id": "c_KJrlghOauz"
}
},
{
"cell_type": "markdown",
"source": [
"そのためには初回に登場した以下の関数を利用します。"
],
"metadata": {
"id": "oHfjDAeWOzBl"
}
},
{
"cell_type": "code",
"source": [
"def bloch_fig(state):\n",
" qstate = state[0]*qutip.basis(2,0) + state[1]*qutip.basis(2,1)\n",
" b = qutip.Bloch()\n",
" b.make_sphere()\n",
" b.add_states(qstate)\n",
" return b"
],
"metadata": {
"id": "OoSa5frfOugx"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"この関数を利用して以下のようにアダマール回路を経由した量子状態の様子を見てみましょう。\n"
],
"metadata": {
"id": "n8B252cMO3f-"
}
},
{
"cell_type": "code",
"source": [
"state = circuitH().numpy()"
],
"metadata": {
"id": "krzKS-uzPRGw"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"ちょっとアダマール回路は例外的にtensor形式で答えが出てしまうので、それを直す必要があります(仕様)。\n",
"circuitH().numpy()でnumpyのarray形式に直してくれます。"
],
"metadata": {
"id": "nXId9fEdXnnx"
}
},
{
"cell_type": "code",
"source": [
"bloch_fig(state).show()"
],
"metadata": {
"id": "u-WxcYJmO7x4"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"横にパタンと倒れた量子状態となり、0と1のどちらでもない。\n",
"この場合には0と1が確率的に50パーセントずつ結果が得られます。\n",
"これを重ね合わせの状態といいます。"
],
"metadata": {
"id": "qw2z35dLEKAY"
}
},
{
"cell_type": "markdown",
"source": [
"さて今回は1量子ビットではなく複数の量子ビットを使ってみます。\n",
"n_qubits=2などと、量子ビット数を指定して、以下のような回路を作ってみましょう。"
],
"metadata": {
"id": "oG0BVeUNNPf_"
}
},
{
"cell_type": "code",
"source": [
"n_qubits = 2\n",
"dev = qml.device('lightning.qubit', wires=n_qubits)\n",
"@qml.qnode(dev)\n",
"def circuitCZ():\n",
" qml.PauliX(0)\n",
" qml.PauliX(1)\n",
" qml.CZ([0,1])\n",
" return qml.state()"
],
"metadata": {
"id": "RpV7aqVIEc8C"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"2つの量子ビットをPauliXゲートで反転させたのちにCZ(制御Zゲート)を適用します。\n",
"制御回路というのは、最初の引数に示した量子ビット(制御量子ビット)が0の場合には何もせず、1の場合にもう片方の量子ビット(標的量子ビット)に作用をするという条件分岐を持った回路です。\n",
"制御Zゲートの場合には、標的量子ビットについて、PauliZゲートを適用します。\n",
"PauliZゲートは、0のときには係数を+1かけて、1のときには確率振幅の係数を-1にします。\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が0で、2番目の量子ビット(標的量子ビット)が0の場合は\n",
"2番目の量子ビットに何もしないので、0となる。\n",
"\n",
"$|0,0 \\rangle$ -> $|0,0 \\rangle$\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が0で、2番目の量子ビット(標的量子ビット)が1の場合は\n",
"2番目の量子ビットに何もしないので、1となる。\n",
"\n",
"$|0,1 \\rangle$ -> $|0,1 \\rangle$\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が1で、2番目の量子ビット(標的量子ビット)が0の場合は\n",
"2番目の量子ビットにZゲートを適用するので、係数は+1がかかる。\n",
"\n",
"$|1,0 \\rangle$ -> $|1,0 \\rangle$\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が1で、2番目の量子ビット(標的量子ビット)が0の場合は\n",
"2番目の量子ビットにZゲートを適用するので、係数は-1がかかる。\n",
"\n",
"$|1,1 \\rangle$ -> -$|1,1 \\rangle$\n",
"\n",
"これらを確認してみましょう。\n",
"\n"
],
"metadata": {
"id": "gYDDQNMwFDko"
}
},
{
"cell_type": "code",
"source": [
"state = circuitCZ()\n",
"state"
],
"metadata": {
"id": "DF3qX_JRGNBx"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"これを重ね合わせの状態について実行してみるとどうでしょうか。\n",
"アダマール回路を最初に経由して実行してみてください。\n"
],
"metadata": {
"id": "onOIvdnFJQt-"
}
},
{
"cell_type": "code",
"source": [
"n_qubits = 2\n",
"dev = qml.device('lightning.qubit', wires=n_qubits)\n",
"@qml.qnode(dev)\n",
"def circuitHCZ():\n",
" qml.Hadamard(0)\n",
" qml.Hadamard(1)\n",
" qml.CZ([0,1])\n",
" return qml.state()"
],
"metadata": {
"id": "vssji-yrJV-r"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"state = circuitHCZ()\n",
"state"
],
"metadata": {
"id": "BPjxWmysJb4F"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"すると$|00\\rangle$から$|11\\rangle$まで全ての状態について、その係数がそれぞれ計算された量子状態が得られます。\n",
"重ね合わせの状態を利用して、それぞれの係数を並列計算したことになります。\n",
"1つ1つの計算を試すことなく、量子回路を一度通すだけで係数について並列的に計算を行うことができます。\n",
"これは量子回路を用いた計算が持つ有利な性質といえます。"
],
"metadata": {
"id": "luclKYXBJj9T"
}
},
{
"cell_type": "markdown",
"source": [
"同様にCNOTゲート(制御Xゲート)を考えます。\n",
"制御Xゲートの場合には、標的量子ビットについて、PauliXゲートを適用します。\n",
"PauliXゲートは、0のときには1、1のときには0にします。\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が0で、2番目の量子ビット(標的量子ビット)が0の場合は\n",
"2番目の量子ビットに何もしないので、0のまま。\n",
"\n",
"$|0,0 \\rangle$ -> $|0,0 \\rangle$\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が0で、2番目の量子ビット(標的量子ビット)が1の場合は\n",
"2番目の量子ビットに何もしないので、1のまま。\n",
"\n",
"$|0,1 \\rangle$ -> $|0,1 \\rangle$\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が1で、2番目の量子ビット(標的量子ビット)が0の場合は\n",
"2番目の量子ビットにXゲートを適用するので、1に反転。\n",
"\n",
"$|1,0 \\rangle$ -> $|1,1 \\rangle$\n",
"\n",
"例: 1番目の量子ビット(制御量子ビット)が1で、2番目の量子ビット(標的量子ビット)が0の場合は\n",
"2番目の量子ビットにXゲートを適用するので、0に反転。\n",
"\n",
"$|1,1 \\rangle$ -> -$|1,0 \\rangle$\n",
"\n",
"これらを確認してみましょう。"
],
"metadata": {
"id": "iw_WJfcHLgcO"
}
},
{
"cell_type": "code",
"source": [
"n_qubits = 2\n",
"dev = qml.device('lightning.qubit', wires=n_qubits)\n",
"@qml.qnode(dev)\n",
"def circuitCNOT():\n",
" qml.PauliX(0)\n",
" qml.PauliX(1)\n",
" qml.CNOT([0,1])\n",
" return qml.state()"
],
"metadata": {
"id": "DHLk2CBAL4HY"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"state = circuitCNOT()\n",
"state"
],
"metadata": {
"id": "TJ4Vh5g0L737"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"所望の状態が得られることを確認できたでしょうか?"
],
"metadata": {
"id": "TDExJWd2MDJO"
}
},
{
"cell_type": "markdown",
"source": [
"# ベル状態の生成\n",
"\n",
"制御量子ビットについて重ね合わせの状態を作り、制御Zゲートを適用してみましょう。\n",
"制御回路の面倒なところは、制御量子ビットが0の場合、1の場合と条件分岐させた場合に両者のことを数え上げなければならないことでした。\n",
"しかし重ね合わせの状態を利用すれば、その制御量子ビットがどちらの場合についても同時に計算することができます。\n",
"これを利用して制御Xゲートを適用してみます。"
],
"metadata": {
"id": "gNCDQH9QKaBa"
}
},
{
"cell_type": "code",
"source": [
"n_qubits = 2\n",
"dev = qml.device('lightning.qubit', wires=n_qubits)\n",
"@qml.qnode(dev)\n",
"def circuitBell():\n",
" qml.Hadamard(0)\n",
" qml.CNOT([0,1])\n",
" return qml.state()"
],
"metadata": {
"id": "ws5uCpI4KuPV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"state = circuitBell()\n",
"state"
],
"metadata": {
"id": "LkVPbRX5KyAd"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"その結果得られるのが、$(|00\\rangle + |11\\rangle)/\\sqrt{2}$という状態です。\n",
"制御量子ビットが0だったらそのまま、制御量子ビットが1だったら0を1に反転させるので、こうした量子状態が得られるのは理にかなっています。\n",
"\n",
"得られたこの量子状態は、Bell状態と呼ばれる特別な量子状態です。\n",
"そもそも量子ビットというのは、実体のあるものです。超伝導量子ビットやイオントラップ、中性原子などさまざまな実現方法が今日では存在します。その量子ビットのペアが00,11と揃った状態にあります。\n",
"この性質のおかげで、離れたところに存在する量子ビットであっても、片方で0という状態であると観測されると、確実にもう片方は同じ0という結果となり、さらに片方で1という結果であれば、もう片方も1となることになります。\n",
"こうした確率的事象にも関わらず離れて結果が相関している状態をもつれあいの状態、エンタングルした状態といいます。\n"
],
"metadata": {
"id": "ULTVKcWfK3CI"
}
},
{
"cell_type": "markdown",
"source": [
"# 量子機械学習に便利な回路\n",
"\n",
"さてそうした量子状態を利用しながら、機械学習に量子回路を利用してみましょう。\n",
"まずPennylaneでは量子機械学習を進める上で非常に便利な機能が用意されています。\n",
"そのうちの1つがtemplate機能です。\n",
"様々な量子回路が事前に準備されているというものです。\n",
"まずはデータを量子ビットにエンコードするための量子回路として便利なAngleEmbeddingです。\n",
"次のように利用します。\n"
],
"metadata": {
"id": "3kVuhld6ONFw"
}
},
{
"cell_type": "code",
"source": [
"n_qubits = 2\n",
"dev = qml.device('lightning.qubit', wires=n_qubits)\n",
"@qml.qnode(dev)\n",
"def circuit(inputs):\n",
" qml.AngleEmbedding(inputs, rotation = \"X\", wires=range(n_qubits))\n",
" return qml.state()"
],
"metadata": {
"id": "TMnf6fhHFmoc"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"この回路はAngle Embeddingとありますように、角度(Angle)を埋め込む(embedする)役割があります。\n",
"指定された角度を量子ビットに1つずつ入力することによって、量子機械学習では、与えられたデータを量子回路に入力することができます。\n"
],
"metadata": {
"id": "a_vihTpXHzux"
}
},
{
"cell_type": "markdown",
"source": [
"結果として出来上がったのがどんな回路なのか、その姿形を見てみましょう。\n",
"第一回に利用した関数を参考に以下のようにコードを実行してみましょう。\n",
"今回の回路は量子ビットの数だけ、パラメータとして数値を入れておく必要があります。"
],
"metadata": {
"id": "iXrRC9nqMdRX"
}
},
{
"cell_type": "code",
"source": [
"fig, ax = qml.draw_mpl(circuit)((0.1,0.2))\n",
"fig.show()"
],
"metadata": {
"id": "OAc0hfteM1nU"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Angle Embeddingの部分は量子ビット全体にかかっているように表示されます。\n",
"中身は1つ1つの量子ビットに指定された軸周りで回転するものとなっています。"
],
"metadata": {
"id": "J7MLiK73s4Nt"
}
},
{
"cell_type": "markdown",
"source": [
"次に利用することが多いのが、Basic Entangle Layersです。\n",
"量子状態として独特なエンタングルされた状態を複数の量子ビット間で生じさせるというものです。\n",
"この量子回路は、そのエンタングルされた状態にする前に、指定されたパラメータ(weights)に従い、\n",
"各量子ビットを回転させます。ここでも先ほどのAngle Embeddingと同様にどの軸周りで回転させるのか、指定することができます。"
],
"metadata": {
"id": "nYKtgCZctU4R"
}
},
{
"cell_type": "code",
"source": [
"@qml.qnode(dev)\n",
"def circuit(weights):\n",
" qml.BasicEntanglerLayers(weights, rotation = qml.RX, wires=range(n_qubits))\n",
" return qml.state()"
],
"metadata": {
"id": "2Lhe_e_st7rQ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"この回路には回転角度を決めるパラメータを指定する必要があります。その形を調べてみましょう。"
],
"metadata": {
"id": "2QZzqSlky2wU"
}
},
{
"cell_type": "code",
"source": [
"shape = qml.BasicEntanglerLayers.shape(n_layers=1, n_wires=2)"
],
"metadata": {
"id": "v5K9wiMKy-me"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"print(shape)"
],
"metadata": {
"id": "VjAZQxhKzC7y"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"n_layersで層、n_wiresで並べてある量子ビット数を指定します。\n",
"(n_layers,n_wires)という形を持つパラメータのテンソル(ここでは行列)を用意する必要があります。\n",
"簡単に用意する場合にはnp.random.random(shape)としましょう。\n",
"一様連続分布からのサンプリングされた乱数を指定されたshape通り出力します。"
],
"metadata": {
"id": "s45kLNopzFti"
}
},
{
"cell_type": "code",
"source": [
"weights = np.random.random(size=shape)"
],
"metadata": {
"id": "WiNv6MUTzWJK"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"weights"
],
"metadata": {
"id": "EEs7t_pwzlv_"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"同様に出来上がった量子回路をみてみましょう。"
],
"metadata": {
"id": "M6bhtNtsx_LM"
}
},
{
"cell_type": "code",
"source": [
"fig, ax = qml.draw_mpl(circuit)(weights)\n",
"fig.show()"
],
"metadata": {
"id": "kyNWhEVTyFKK"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"得られた図からはどれくらいの角度を回しているのかは伺えませんが、回転させた後にエンタングルされた状態を作り出してくれます。\n",
"\n",
"それが本当に実行されているのかエンタングルメントを作る部分だけの効果を調べたいので回転角度を示すweightsはゼロにしましょう。"
],
"metadata": {
"id": "GK_x5k3pz9hN"
}
},
{
"cell_type": "code",
"source": [
"weights = np.zeros(shape)"
],
"metadata": {
"id": "V_Gfz8YF1HmB"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"weights"
],
"metadata": {
"id": "i4-hFaMJ1OtD"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"さらにアダマール回路を事前に経由することでエンタングルされた状態を作り出します。"
],
"metadata": {
"id": "DR4TRjM7PrUF"
}
},
{
"cell_type": "code",
"source": [
"@qml.qnode(dev)\n",
"def circuitE(weights):\n",
" qml.Hadamard(0)\n",
" qml.BasicEntanglerLayers(weights, rotation = qml.RX, wires=range(n_qubits))\n",
" return qml.state()"
],
"metadata": {
"id": "atOPMBb7PvRb"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"state = circuitE(weights)"
],
"metadata": {
"id": "-cpzgi6i1KbQ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"state"
],
"metadata": {
"id": "NkdPsVBt1yQw"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"狙い通りエンタングルされた状態(Bell状態)を作り出すことに成功しました。\n",
"2量子ビットであれば制御Xゲートを利用することで実現できることは先ほど確認しました。\n",
"ただ複数の量子ビットになればそれを繰り返すことが必要となります。\n",
"それを記述するのは面倒ですので、こうしたテンプレートを利用すると簡単に量子回路を作ることができて、便利であるというわけです。"
],
"metadata": {
"id": "g5m6RFUyP02K"
}
},
{
"cell_type": "markdown",
"source": [
"# 量子回路をニューラルネットワークに組み込む\n",
"\n",
"それでは前回に実行したニューラルネットワークによる学習の一部分に量子回路を埋め込んでみましょう。\n",
"まずはニューラルネットワークに入力するデータを準備しましょう。\n",
"\n",
"まずはニューラルネットワークに利用するPyTorchを準備しておきます。"
],
"metadata": {
"id": "R8s0PS_HQje1"
}
},
{
"cell_type": "code",
"source": [
"import torch\n",
"import torch.nn as nn\n",
"import torch.optim as optim"
],
"metadata": {
"id": "8yn_cjwTSk0i"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"前回と同様のデータセットを作り出して準備しましょう。"
],
"metadata": {
"id": "kf3rWwHiS-t2"
}
},
{
"cell_type": "code",
"source": [
"x = - 2.5 + 5.0*np.random.rand(100)\n",
"t = np.sin(x) + 0.1*np.random.randn(100)"
],
"metadata": {
"id": "_au-KHsVSWNV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"どんなデータなのかグラフに描画しておきます。"
],
"metadata": {
"id": "0lb_c091TCSB"
}
},
{
"cell_type": "code",
"source": [
"import matplotlib.pyplot as plt\n",
"plt.scatter(x,t)\n",
"plt.show()"
],
"metadata": {
"id": "deANDd65SeIL"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"これをdatasetとして保持しておきましょう。\n",
"ここら辺の手続きは全く前回と同様です。"
],
"metadata": {
"id": "YX93tMM2TFeZ"
}
},
{
"cell_type": "code",
"source": [
"xdata = x.reshape(100,1)\n",
"tdata = t.reshape(100,1)\n",
"xdata = torch.tensor(xdata, dtype=torch.float32)\n",
"tdata = torch.tensor(tdata, dtype=torch.float32)\n",
"dataset = torch.utils.data.TensorDataset(xdata, tdata)"
],
"metadata": {
"id": "050FlM9dSjGs"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"訓練データとテストデータで分割しておきましょう。"
],
"metadata": {
"id": "gdlb5s-_T-3C"
}
},
{
"cell_type": "code",
"source": [
"n_train = int(len(dataset) * 0.8)\n",
"n_test = len(dataset) - n_train"
],
"metadata": {
"id": "MdJCXR8DSuv7"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"random_splitでどのデータが利用されるのかわからないようにしておきます。"
],
"metadata": {
"id": "KEzTp-roUCbM"
}
},
{
"cell_type": "code",
"source": [
"train_set, test_set = torch.utils.data.random_split(dataset, [n_train, n_test])"
],
"metadata": {
"id": "TB4IqfzUSvrn"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"バッチサイズを指定して、train_loaderおよびtest_loaderを準備します。"
],
"metadata": {
"id": "ZK7kuWXnUHAk"
}
},
{
"cell_type": "code",
"source": [
"batch_size = 10\n",
"train_loader = torch.utils.data.DataLoader(train_set, batch_size = batch_size, shuffle = True)\n",
"test_loader = torch.utils.data.DataLoader(test_set, batch_size = batch_size, shuffle = False)"
],
"metadata": {
"id": "I5rSX12wSxlN"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"さてデータの準備が整いました。\n",
"量子回路をニューラルネットワークの中に組み込んでみましょう。"
],
"metadata": {
"id": "_S_2l7nnULjV"
}
},
{
"cell_type": "code",
"source": [
"n_layers = 1\n",
"n_qubits = 2\n",
"weight_shapes = {\"weights\": (n_layers, n_qubits)}\n",
"\n",
"@qml.qnode(dev)\n",
"def circuitNN(inputs,weights):\n",
" qml.AngleEmbedding(inputs, rotation = \"X\", wires=range(n_qubits))\n",
" qml.BasicEntanglerLayers(weights, rotation = qml.RX, wires=range(n_qubits))\n",
" res = []\n",
" for k in range(n_qubits):\n",
" res.append(qml.expval(qml.PauliZ(k)))\n",
" return res"
],
"metadata": {
"id": "-65LXR3WSozk"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"ここで用意した量子回路は、Angle EmbeddingとBasic Entangler Layerです。\n",
"最後の部分が少し変わっています。\n",
"expvalというのは期待値を計算するというものです。\n",
"その中身でqml.PauliZ(k)というのはk番目の量子ビットについてPauliZの期待値を計算するという意味です。\n",
"0なら+1、1なら-1という数値を取り、その登場頻度から期待値を調べるというものです。"
],
"metadata": {
"id": "TblOojdvS6Aj"
}
},
{
"cell_type": "markdown",
"source": [
"これを組み込んだニューラルネットワークを前回のものを参考に以下のように作ります。"
],
"metadata": {
"id": "yZQhXPBJVjPG"
}
},
{
"cell_type": "code",
"source": [
"class QNN(nn.Module):\n",
" def __init__(self, n_hidden):\n",
" super().__init__()\n",
" self.qnn = qml.qnn.TorchLayer(circuitNN, weight_shapes=weight_shapes)\n",
" self.fc1 = nn.Linear(1, n_hidden)\n",
" self.fc2 = nn.Linear(n_hidden, 1)\n",
"\n",
" def forward(self, x):\n",
" h = self.fc1(x)\n",
" h = self.qnn(h)\n",
" y = self.fc2(h)\n",
" return y"
],
"metadata": {
"id": "lbA5s_-LSvah"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"qml.qnn.TorchLayerという関数で、指定された量子回路をニューラルネットワークのように関数にしてくれます。\n",
"その際にweight_shapesで、内部に組み込まれるパラメータの形を指定してあげましょう。\n",
"これによりPyTorchの微分等の機能を利用できるようにします。\n",
"またinputsの形は、入力される値の形に合わせます。\n",
"\n",
"量子回路を組み込んだニューラルネットワークですら以下のようにPyTorchでニューラルネットワークの学習をするときと同じように準備ができます。"
],
"metadata": {
"id": "3ewN8KKDV4wx"
}
},
{
"cell_type": "code",
"source": [
"net = QNN(2)"
],
"metadata": {
"id": "8Ov0haxfJaQZ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"ここから先は全く前回までと同様です。\n",
"最適化手法を指定し、学習の良し悪しを決める基準を決めます。"
],
"metadata": {
"id": "PNJJJEMwWwxh"
}
},
{
"cell_type": "code",
"source": [
"optimizer = optim.SGD(net.parameters(), lr=0.1)\n",
"criterion = nn.MSELoss()"
],
"metadata": {
"id": "2oSb0FKuW3s2"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"訓練の途中経過を調べるために以下のリストを準備します。"
],
"metadata": {
"id": "Qysz03BWW4KS"
}
},
{
"cell_type": "code",
"source": [
"train_loss_value=[]\n",
"test_loss_value=[]"
],
"metadata": {
"id": "MuKiHyY2JkPC"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"学習回数をTallで指定しましょう。"
],
"metadata": {
"id": "XwaqT2kHW8Qn"
}
},
{
"cell_type": "code",
"source": [
"Tall = 50"
],
"metadata": {
"id": "yOMYNYz8WRAK"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"学習をさせるループ部分は全く一緒です。\n",
"タブの有無や場所には注意しましょう。"
],
"metadata": {
"id": "rW4SRkYfW_GJ"
}
},
{
"cell_type": "code",
"source": [
"#gpu利用の場合\n",
"#net = net.to(device)\n",
"\n",
"for epoch in range(Tall):\n",
" sum_loss = 0\n",
" for batch in train_loader:\n",
" xtrain,ttrain = batch\n",
" #gpu利用の場合\n",
" #xtrain, ttrain = xtrain.to(device), ttrain.to(device)\n",
" optimizer.zero_grad()\n",
" ytrain = net(xtrain)\n",
" loss = criterion(ytrain, ttrain)\n",
" loss.backward()\n",
" optimizer.step()\n",
" sum_loss += loss.item()\n",
" train_loss_value.append(sum_loss/len(train_loader))\n",
"\n",
" sum_loss = 0\n",
" for batch in test_loader:\n",
" xtest, ttest = batch\n",
" #gpu利用の場合\n",
" #xtest, ttest = xtest.to(device), ttest.to(device)\n",
" ytest = net(xtest)\n",
" loss = criterion(ytest, ttest)\n",
"\n",
" sum_loss += loss.item()\n",
" test_loss_value.append(sum_loss/len(test_loader))\n"
],
"metadata": {
"id": "ha2dPu24Jmw6"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"学習を終えたら、学習の途中の成績を見てみることにしましょう。"
],
"metadata": {
"id": "98fKgf5IXEUQ"
}
},
{
"cell_type": "code",
"source": [
"plt.figure(figsize=(6,6))\n",
"plt.plot(range(len(train_loss_value)), train_loss_value)\n",
"plt.plot(range(len(test_loss_value)), test_loss_value)"
],
"metadata": {
"id": "HcoCn_CdJoZ9"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"最終的に得られた関数の形を見てみることにしましょう。"
],
"metadata": {
"id": "ztFUNsecXKmF"
}
},
{
"cell_type": "code",
"source": [
"xplot = np.linspace(-2.5,2.5,100).reshape(100,1)\n",
"xplot = torch.tensor(xplot, dtype=torch.float32)\n",
"net = net.cpu()\n",
"yplot = net.forward(xplot)\n",
"plt.plot(xplot.detach().numpy().copy(), yplot.detach().numpy().copy())\n",
"plt.scatter(x,t)\n",
"plt.show()"
],
"metadata": {
"id": "zQ1dB9XSJr1O"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"非常に滑らかな形が得られていることがわかります。\n",
"これはそのままの意味で驚いてはいけません。\n",
"量子回路は中身は三角関数が関わる回転により記述されています。\n",
"そのため三角関数のような周期的な関数は得意です。\n",
"ただどんな回路にすると学習がうまくいくのか、どんな回路であればパラメータ数が少なくても効率的な結果を生み出すのか、それは多くの試行錯誤をしてみる価値があるかと思います。"
],
"metadata": {
"id": "O86lJKBsXPbC"
}
},
{
"cell_type": "markdown",
"source": [
"せっかくなので次第に学習の結果、精度が向上していく様子を見てみましょう。"
],
"metadata": {
"id": "K3ulsIBecyQP"
}
},
{
"cell_type": "markdown",
"source": [
"動画を作成するために前回も利用したライブラリをインストールします。"
],
"metadata": {
"id": "NEJGFxMIdWau"
}
},
{
"cell_type": "code",
"source": [
"!pip install APNG"
],
"metadata": {
"id": "6qfT1I35dZtQ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"保存するためのフォルダを前回同様作成しておきます。"
],
"metadata": {
"id": "E60OUaPKfHSh"
}
},
{
"cell_type": "code",
"source": [
"!mkdir figdata"
],
"metadata": {
"id": "ZqD9l1lIfKkM"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"先ほどの学習用のコードに以下のように追記したものを用意します。"
],
"metadata": {
"id": "bLinoOOOfLoG"
}
},
{
"cell_type": "code",
"source": [
"from IPython.display import Image\n",
"from apng import APNG\n",
"\n",
"filenames = []\n",
"for epoch in range(Tall):\n",
" sum_loss = 0\n",
" for batch in train_loader:\n",
" xtrain,ttrain = batch\n",
" #gpu利用の場合\n",
" #xtrain, ttrain = xtrain.to(device), ttrain.to(device)\n",
" optimizer.zero_grad()\n",
" ytrain = net(xtrain)\n",
" loss = criterion(ytrain, ttrain)\n",
" loss.backward()\n",
" optimizer.step()\n",
" sum_loss += loss.item()\n",
" train_loss_value.append(sum_loss/len(train_loader))\n",
"\n",
" sum_loss = 0\n",
" for batch in test_loader:\n",
" xtest, ttest = batch\n",
" #gpu利用の場合\n",
" #xtest, ttest = xtest.to(device), ttest.to(device)\n",
" ytest = net(xtest)\n",
" loss = criterion(ytest, ttest)\n",
"\n",
" sum_loss += loss.item()\n",
" test_loss_value.append(sum_loss/len(test_loader))\n",
"\n",
" filename = \"figdata/NN{:04}.png\".format(epoch)\n",
" filenames.append(filename)\n",
"\n",
" xplot = np.linspace(-2.5,2.5,100).reshape(100,1)\n",
" xplot = torch.tensor(xplot, dtype=torch.float32)\n",
" net = net.cpu()\n",
" yplot = net.forward(xplot)\n",
" plt.plot(xplot.detach().numpy().copy(), yplot.detach().numpy().copy())\n",
" plt.scatter(x,t)\n",
" plt.savefig(filename)\n",
" plt.clf()\n",
"\n",
"APNG.from_files(filenames, delay=100).save(\"animation.png\")\n",
"Image(\"animation.png\")"
],
"metadata": {
"id": "PHdWzvjLdaGB"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "g78s6rOzeIAt"
},
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment