LoginSignup
4
1

More than 1 year has passed since last update.

[量子ニューラルネット] 勾配降下vs非勾配 どっちが速いのだ?

Last updated at Posted at 2021-06-20

量子ニューラルネット

PennyLaneを用いて量子ニューラルネットワークを学習させた際の計算時間を考えます。
学習には非勾配最適化(e.g. Nelder-Mead)と、勾配最適化(e.g. Adam)があります。
古典ニューラルネットワーク機械学習では、常識的に、勾配最適化のほうが速いとされます。
量子の場合もそうなのでしょうか?

PennyLaneは勾配降下が出来ることを売りにしていますが、本当に有用なのでしょうか?
非勾配vs勾配のフェアな比較は、ほとんど見られなかったので、やってみました。

非勾配vs勾配 の比較条件

  • 非勾配はscipy.optimize.minimize
  • 学ぶ対象はsin(x)
  • 学習対象のデータセット、ニューラルネットワークモデルは同一とする。
  • Optimizerには、勾配はAdam、非勾配はNelder-Meadとする。
    • iteration回数は揃える。ただし非勾配については、必ずしもiteration回数=パラメータupdate回数にはならないよう。
    • optimizerのハイパラは、収束コストが両者でだいたい等しくなる程度に設定する。

厳密に出来ていない箇所もありますが、ご容赦ください。

コード

勾配降下

import pennylane as qml
from pennylane import numpy as np
from matplotlib import pyplot as plt
num_of_data = 64
X =  np.random.uniform(high=2 * np.pi, size=(num_of_data,1))
Y = np.sin(X[:,0])

########  パラメータ  #############
n_qubits = 2 ## qubitの数
n_layers = 2 # q_layer
dev = qml.device("lightning.qubit", wires=n_qubits, shots=None) # define a device

# Initial circuit parameters
var_init = np.random.uniform(high=2 * np.pi, size=(n_layers, n_qubits, 3))

@qml.qnode(dev, diff_method='adjoint')
def quantum_neural_net(var, x):
    qml.templates.AngleEmbedding(x, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(var, wires=range(n_qubits))
    return qml.expval(qml.PauliZ(0))

def square_loss(desired, predictions):
    loss = 0
    for l, p in zip(desired, predictions):
        loss = loss + (l - p) ** 2
    loss = loss / len(desired)
    return loss

def cost(var, features, desired):
    preds = [quantum_neural_net(var, x) for x in features]
    return square_loss(desired, preds)

opt = qml.AdamOptimizer(0.1)
import time

hist_cost = []
var = var_init
for it in range(50):
    t1 = time.time() 
    var, _cost = opt.step_and_cost(lambda v: cost(v, X, Y), var)
    t2 = time.time() 
    elapsed_time = t2-t1
    print("Iter:"+str(it)+", cost="+str(_cost.numpy()))
    print(f"Time:{elapsed_time}")
    hist_cost.append(_cost)

plt.plot(10*np.log10(hist_cost),'o-')

Y_pred = [quantum_neural_net(var, x) for x in X]
plt.plot(X[:,0],Y_pred,'o')

Iter:0, cost=0.32403188045966985
Time:0.4249999523162842
Iter:1, cost=0.17929298669460514
Time:0.38500022888183594
Iter:2, cost=0.11539541686157953
Time:0.38000011444091797

学習時間:0.4 x 50 = 20 sec

image.png

image.png

非勾配

ほぼ同じですが、scipy.optimize.minimize は1次元配列しか変数に取れないことに注意します。
(1次元で渡して、cost関数内部でrehshapeします)


# Initial circuit parameters
var_init = np.random.uniform(high=2 * np.pi, size=(n_layers*n_qubits*3)) # one-dimensional array

@qml.qnode(dev, diff_method='adjoint')
def quantum_neural_net(var, x):
    var_3d_array = np.reshape(var,(n_layers,n_qubits,3))
    qml.templates.AngleEmbedding(x, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(var_3d_array, wires=range(n_qubits))
    return qml.expval(qml.PauliZ(0))

from scipy.optimize import minimize
hist_cost = []
var = var_init

count = 0
def cbf(Xi):
    global count
    global hist_cost
    count += 1
    cost_now = cost(Xi,X,Y)
    hist_cost.append(cost_now)
    print('iter = '+str(count)+' | cost = '+str(cost_now))

result = minimize(fun=cost, x0=var_init, args=(X,Y) ,method='Nelder-Mead', callback=cbf, options={"maxiter":50})
t2 = time.time() 
elapsed_time = t2-t1
print(f"Time:{elapsed_time}")
hist_cost.append(_cost)
print(f"Time per iteration : {elapsed_time/50}")       

plt.plot(10*np.log10(hist_cost[1:len(hist_cost)-1]),'o-') 

image.png

iter = 1 | cost = 0.3949460800587229
iter = 2 | cost = 0.3949460800587229
iter = 3 | cost = 0.3949460800587229
iter = 4 | cost = 0.3949460800587229

Time:41.019999980926514
Time per iteration : 0.8203999996185303

勾配降下の2倍の時間がかかりました。
思ったほど差がない・・と思いました。

では、パラメータ数が増えるとどうなるでしょうか。

n_layers = 2→6 にしてみます。(パラメータ数が3倍になります)

勾配では
Iter:0, cost=0.8111990194081542
Time:0.940000057220459
Iter:1, cost=0.3314598963846486
Time:0.8700001239776611
Iter:2, cost=0.1956043243597784
Time:0.8700001239776611

image.png

非勾配では

iter = 1 | cost = 1.004932862586646
iter = 2 | cost = 1.004932862586646
iter = 3 | cost = 1.004932862586646
iter = 4 | cost = 1.004932862586646
iter = 5 | cost = 1.004932862586646
iter = 6 | cost = 1.004932862586646
iter = 7 | cost = 1.004932862586646

Time:98.82499980926514
Time per iteration : 1.9764999961853027

image.png

やはり速度差は2倍程度だったのですが、非勾配は全く収束していません。
非勾配の場合、パラメータ数が増えるとパラメータupdateに何度もiterationを必要とするようです。
iteration回数を4倍にしてみますと、

image.png

収束はしています。しかし計算時間は勾配法の8倍かかっていますし、まだ収束コストで負けています。

このように、勾配降下はパラメータ数が多いとアドバンテージが大きくなることがわかりました。
直感的にそうかもしれませんが、実際みてみると大変面白い結果ではないでしょうか。

まとめ

勾配降下はやっぱり速いのだ。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1