”はやぶさ”の技術ノート

Chainerと深層学習で非線形回帰モデルを生成

非線形回帰モデルが生成できれば、ブラックボックス解析・リバースエンジニアリンなどに応用でき、ビジネスにも使えるよ!

Chainerインストール

In [5]:
!apt -y install libcusparse8.0 libnvrtc8.0 libnvtoolsext1
!ln -snf /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so.8.0 /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so
!pip install 'cupy-cuda80' 'chainer'
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  libcusparse8.0 libnvrtc8.0 libnvtoolsext1
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 28.9 MB of archives.
After this operation, 71.6 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu artful/multiverse amd64 libcusparse8.0 amd64 8.0.61-1 [22.6 MB]
Get:2 http://archive.ubuntu.com/ubuntu artful/multiverse amd64 libnvrtc8.0 amd64 8.0.61-1 [6,225 kB]
Get:3 http://archive.ubuntu.com/ubuntu artful/multiverse amd64 libnvtoolsext1 amd64 8.0.61-1 [32.2 kB]
Fetched 28.9 MB in 1s (16.7 MB/s)

78Selecting previously unselected package libcusparse8.0:amd64.
(Reading database ... 18396 files and directories currently installed.)
Preparing to unpack .../libcusparse8.0_8.0.61-1_amd64.deb ...
7Progress: [  0%] [..........................................................] 87Progress: [  6%] [###.......................................................] 8Unpacking libcusparse8.0:amd64 (8.0.61-1) ...
7Progress: [ 12%] [#######...................................................] 87Progress: [ 18%] [##########................................................] 8Selecting previously unselected package libnvrtc8.0:amd64.
Preparing to unpack .../libnvrtc8.0_8.0.61-1_amd64.deb ...
7Progress: [ 25%] [##############............................................] 8Unpacking libnvrtc8.0:amd64 (8.0.61-1) ...
7Progress: [ 31%] [##################........................................] 87Progress: [ 37%] [#####################.....................................] 8Selecting previously unselected package libnvtoolsext1:amd64.
Preparing to unpack .../libnvtoolsext1_8.0.61-1_amd64.deb ...
7Progress: [ 43%] [#########################.................................] 8Unpacking libnvtoolsext1:amd64 (8.0.61-1) ...
7Progress: [ 50%] [#############################.............................] 87Progress: [ 56%] [################################..........................] 8Setting up libnvtoolsext1:amd64 (8.0.61-1) ...
7Progress: [ 62%] [####################################......................] 87Progress: [ 68%] [#######################################...................] 8Setting up libcusparse8.0:amd64 (8.0.61-1) ...
7Progress: [ 75%] [###########################################...............] 87Progress: [ 81%] [###############################################...........] 8Setting up libnvrtc8.0:amd64 (8.0.61-1) ...
7Progress: [ 87%] [##################################################........] 87Progress: [ 93%] [######################################################....] 8Processing triggers for libc-bin (2.26-0ubuntu2.1) ...

78Collecting cupy-cuda80
  Downloading https://files.pythonhosted.org/packages/30/82/9eb531d941b22021ee47e7946a551beca407f532f5834ceb5efad6c1c20d/cupy_cuda80-4.3.0-cp36-cp36m-manylinux1_x86_64.whl (200.4MB)
    11% |███▋                            | 22.7MB 32.2MB/s eta 0:00:06    100% |████████████████████████████████| 200.4MB 145kB/s 
Collecting chainer
  Downloading https://files.pythonhosted.org/packages/ad/c6/61ff9041ea7427fc1e39768f740ab8b880f8ef20960a5f791e978e8d81c0/chainer-4.3.1.tar.gz (400kB)
    100% |████████████████████████████████| 409kB 12.2MB/s 
Requirement already satisfied: numpy>=1.9.0 in /usr/local/lib/python3.6/dist-packages (from cupy-cuda80) (1.14.5)
Collecting fastrlock>=0.3 (from cupy-cuda80)
  Downloading https://files.pythonhosted.org/packages/fa/24/767ce4fe23af5a4b3dd229c0e3153a26c0a58331f8f89af324c761663c9c/fastrlock-0.3-cp36-cp36m-manylinux1_x86_64.whl (77kB)
    100% |████████████████████████████████| 81kB 18.2MB/s 
Requirement already satisfied: six>=1.9.0 in /usr/local/lib/python3.6/dist-packages (from cupy-cuda80) (1.11.0)
Collecting filelock (from chainer)
  Downloading https://files.pythonhosted.org/packages/2d/ba/db7e0717368958827fa97af0b8acafd983ac3a6ecd679f60f3ccd6e5b16e/filelock-3.0.4.tar.gz
Requirement already satisfied: protobuf>=3.0.0 in /usr/local/lib/python3.6/dist-packages (from chainer) (3.6.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.6/dist-packages (from protobuf>=3.0.0->chainer) (39.1.0)
Building wheels for collected packages: chainer, filelock
  Running setup.py bdist_wheel for chainer ... done
  Stored in directory: /content/.cache/pip/wheels/8a/ef/b0/e67e0555c4d520566d6565d9634ecb7fbb1594758236bb7b40
  Running setup.py bdist_wheel for filelock ... done
  Stored in directory: /content/.cache/pip/wheels/35/ba/67/4cc48738870c3b54f9e3b5d78bf9de130befb70c1d359faf8b
Successfully built chainer filelock
Installing collected packages: fastrlock, cupy-cuda80, filelock, chainer
Successfully installed chainer-4.3.1 cupy-cuda80-4.3.0 fastrlock-0.3 filelock-3.0.4

import

数値演算やChainer用のモジュールをimportします

In [0]:
import math
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from chainer import Chain, Variable
import chainer.functions as F
import chainer.links as L
from chainer import optimizers

実験データ(ダミー)生成

解析したいシステムの入出力値をセンサ等で取得します。
今回は、入力値x、出力値yの実験データ(ダミー)※を生成します。

※解析しやすい数値で”ドヤ顔”できないように、実験データは乱数で生成します

In [16]:
# 実験データ用の配列
x = [] 
y = []
get_values = 0

for i in range(10):
    get_values = random.random()
    x.append([i])
    y.append([get_values])

# データフレーム生成(列基準)
df = pd.DataFrame({'X': x, 
                   'Y': y})

# グラフ出力
plt.plot(x, y)
plt.title("Training Data")
plt.xlabel("input_x")
plt.ylabel("output_y")
plt.grid(True)

df
Out[16]:
X Y
0 [0] [0.6599171460511132]
1 [1] [0.31619112575785624]
2 [2] [0.9976452773473861]
3 [3] [0.8672424412003542]
4 [4] [0.6279379672004397]
5 [5] [0.7440373779177075]
6 [6] [0.1968773072894494]
7 [7] [0.5759651485486432]
8 [8] [0.8726284747856246]
9 [9] [0.2434453906431554]

エンジニアリングとシステム解析


↑が解析対象の入出力関係です。

この対象をモデルベース開発などのエンジニアリングに活用するには、↑のデータを解析して対象システムの特性を把握する必要があります。

この”対象システムの特性を把握”というのは”対象の数式モデル(運動方程式)を算出”と同義と考えて良いよー。(←私的にはね)

システム解析と制御


もう少し噛砕いて説明すると、y = f(x)を算出できたということは、入力値xに対する出力値yが既知になったということです。

具体的には、このシステムに x = 5 を入力すれば、y = f(5)で出力値が得られるということ。

このように、システムの特性(入出力関係)を把握できれば、システムを制御できるようになります。

システム解析の課題


システム解析の基本は、システムを構成している部品を調べ、その部品の物理特性から運動方程式を導くのが王道です。

しかし、この手法には以下の問題があります。

  • 構成部品を調べきれない
  • 部品の組付けが特殊で物理法則が不鮮明
  • 摩擦などの非線形成分の把握が困難

このような問題があるため、システムに適当な入力を与え、そのときの出力から実験的にシステムの特性を把握するのが有効です。

…で↑のような非線形な実験データが得られると、どーやって数式モデル(運動方程式)を算出するんだよ…という問題が発生する。

この問題解決に”深層学習”を使います!!

Chainer用に変換

実験値x,yをChainerで使えるようにnumpy配列のfloat32に変換する

In [0]:
x = Variable(np.array(x, dtype=np.float32))
y = Variable(np.array(y, dtype=np.float32))

# print(x)
# print(y)

ニューラルネットワーク設計

オリジナルのニューラルネットワーク『MyChain』を設計します!

『MyChain』の重み数は”100⇒50”とした。100と50にした理由は特になし。(無理やり理由を付けるならキリの良い数字にしました。)

また、活性化関数に悩んだら"relu"使うようにしています。

In [0]:
class MyChain(Chain):

    def __init__(self):
        super(MyChain, self).__init__(
            l1 = L.Linear(1, 100),
            l2 = L.Linear(100, 50),
            l3 = L.Linear(50, 1)
        )

    def predict(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)

ニューラルネットワーク(NN)モデルの宣言

↑で設計した『MyChaine』を使うための宣言をします。

In [19]:
# NNモデルの宣言
model = MyChain()
print(model)
<__main__.MyChain object at 0x7ff415eeb550>

学習

1万回の学習だと精度が悪かった(lossがあまり下がらなかった)ので、8万回 学習させました!

学習過程のグラフがあるので、頑張って一番下までスクロールして下さい!

In [20]:
# 損失関数の計算(二乗誤差(MSE)を採用)
def forward(x, y, model):
    t = model.predict(x)
    loss = F.mean_squared_error(t, y)
    return loss

# 最適化アルゴリズムにAdamを採用
optimizer = optimizers.Adam()
optimizer.setup(model)

# パラメータの学習を繰り返す
loss_list = []
step = []
for i in range(0, 80000): # 10000
    loss = forward(x, y, model)
    step.append(i)
    loss_list.append(loss.data)
    # print("loss: {}".format(loss.data))
    optimizer.update(forward, x, y, model)

plt.plot(step, loss_list)
plt.title("Training Data")
plt.xlabel("step")
plt.ylabel("loss")
plt.grid(True)
plt.show()

考察

6.3万回付近で収束しかけていたloss値が急速に降下し、6.7万付近で収束している。

正直に言えば、このタイミングでloss値が下がった理由は分からない。。

ただし、この事実から分かることは、諦めずに学習させ続ければ、loss値は下がる!かも(?)ということ。

補足


lossの説明は割愛するが、今回のような回帰モデル生成の場合は、lossが低ければ低いほど良い学習結果という意味です。

(”過学習”や”汎化と特化”の話しは本稿では控える。一気に説明すると混乱させそうなので…)

In [24]:
plt.plot(step, loss_list)
plt.xlim([60000,70000])
plt.ylim([0,0.04])
plt.title("Training Data")
plt.xlabel("step")
plt.ylabel("loss")
plt.grid(True)
plt.show()

推論結果確認


結果を可視化して、効果を確認します。

可視化したグラフは上から以下の順に並んでいます。

  1. 教師データ(実験データ)入力x=0~9(整数)に対する出力yの結果(本記事の冒頭のグラフと同じ)
  2. 教師データと同じ入力xに対し『MyChain』が出力ymを推論した結果
  3. 未知の入力:xtに対し『MyChain』が出力ytを推論した結果
  4. 全ての結果を重ねた

また、コードの最後に1入力1出力(SISO)の推論結果も算出してます。

考察


  • 教師データと同じ入力xに対する出力の推論結果は完璧ですね!
  • 未知の入力に対する出力の推論結果も良好だと考えています

今回、ランダムな数値を出力するシステムに対し、推論を行いましたが、
実存するシステムが非線形な場合でも、もう少し滑らかな出力をする可能性が高いと考えています。

その点も考慮すれば、未知の入力に対する出力値(赤プロット)は許容できるレベルかと…

In [29]:
# 教師データ(実験データ)
plt.plot(x.data, y.data)
plt.title("Training Data")
plt.xlabel("x axis")
plt.ylabel("y axis")
plt.grid(True)
plt.show()

# 推論結果
ym = model.predict(x)
plt.plot(x.data, ym.data)
plt.title("Predict")
plt.xlabel("input x")
plt.ylabel("output ym")
plt.grid(True)
plt.show()

# 推論結果2
xt = [[0.5], [1.8], [2.3], [3.3], [4.5], [6.8], [7.2], [7.7], [8.0], [8.2]]
xt = Variable(np.array(xt, dtype=np.float32))
yt = model.predict(xt)
plt.plot(xt.data, yt.data, "ro")
plt.title("Predict2")
plt.xlabel("input xt")
plt.ylabel("output yt")
plt.grid(True)
plt.show()

# グラフを重ねる
plt.plot(x.data, y.data)
plt.plot(x.data, ym.data)
plt.plot(xt.data, yt.data, "ro")
plt.title("comparison")
plt.xlabel("input")
plt.ylabel("output")
plt.grid(True)
plt.show()

# 1入力1出力の推論も可
n = [[5.5]]
n = Variable(np.array(n, dtype=np.float32))
yn = model.predict(n)
print(yn)
variable([[0.39144507]])

まとめ

本稿では、あらゆるエンジニアリングに必須なシステム解析を深層学習で実施しました。

本記事が、ビジネスや研究で深層学習を使ってみたい!という人のヒントになれば嬉しい。

この記事で某企業のディープラーニングお兄さんから、日本のディープラーニングお兄さんになれたかな?

以上