- 投稿日:2020-09-15T22:10:51+09:00
TensorFlowのOptimizerごとのスケール許容範囲の比較
入力値や損失関数のスケールを変えると、学習推移はどのように変わるかを調べました。
スケールを変えても学習推移が変わらないOptimizerと、大きく変わってしまい学習率のパラメータを調整しないといけないOptimizerがあります。
さきに結論
○: 学習率のパラメータはスケールに依存しない、またはわずか
✗: スケールが変わると学習速度が大きく変わり、学習率を調整することが必須
Optimizer 損失関数のスケール 入力値のスケール シンプルな勾配降下法 ✗ ✗ モーメンタム ✗ ✗ Adagrad △ ✗ RMSprop ○ ✗ Adadelta ? ? Adam ○ △ 自作アルゴリズム ○ ○ Adagradの△は、数式を見てもスケールに依存しなさそうですが、一度でも勾配が極端に大きいところに行ってしまうとその後の学習が遅くなってしまう性質があるため、スケールにやや依存します。
Adadeltaは挙動が理解できません。
Adamの△は、数式を見るとスケールに依存しそうですが、挙動を見るとうまいいくようです。
○や✗は数式からスケール依存性がわかる箇所です。
自作アルゴリズムは、スケールに依存しないように作ったアルゴリズムです。
Optimizerの数式は以前の記事にまとめました。
以下、実際に動かしてみて、この性質を確認します。
損失関数の設定
損失関数は前回の記事と同じく $ (x^2+y^2-1)^2 + \frac{1}{8}(x + 1)^2 $ です。グラフにするとこんな感じです。
スケールを表すパラメータ $ k_x, k_y, k_l $ を導入して損失関数を $ k_l \left( ((k_x x)^2+(k_y y)^2-1)^2 + \frac{1}{8}(k_x x + 1)^2 \right) $ とします。
$ k_x=1, k_y=1, k_l=1 $ のときの学習推移は次のグラフです。
この $ k_{\bullet} $ を1から10000や0.0001などにしてみてグラフがどうなるかを調べます。
この記事のグラフでは色を統一しています。
青: シンプルな勾配降下法
橙: モーメンタム
緑: Adagrad
赤: RMSprop
紫: Adadelta
茶: Adam
桃: 自作アルゴリズム各Optimizerでの学習率は $ k_x=1, k_y=1, k_l=1 $ のときにもっとも速く収束する値を適当に探しました。前回の記事と同じです。
損失関数のスケールによる違い
$ k_l $ を大きい方に変えたときの様子。
上から $ k_l=1, 10, 100, 1000, 10000 $ です。
$ k_l $ を小さい方に変えたときの様子。
上から $ k_l=1, 0.1, 0.01, 0.001, 0.0001 $ です。
シンプルな勾配降下法とモーメンタムは、$ k_l $ を大きくすると発散してしまって、グラフから消えています。$ k_l $ を小さくすると学習の速度が極端に遅くなってしまいます。損失関数のスケールが変わると学習率の調整が必須です。
Adagradは、シンプルな勾配降下法やモーメンタムほどではありませんが、学習推移が変わっています。一度でも勾配が極端に大きいところに行ってしまうとその後の学習が遅くなってしまうのが原因だと想像しています。
RMSpropは、学習推移が変わりません。損失関数のスケールには依存しません。
Adadeltaは、学習推移が変わっています。なぜ変わるのかは理解できていないです。そもそも収束させる方法がわかりません。
Adamは、学習推移が少し変わりますが、傾向や学習速度はあまり変わりません。損失関数のスケールへの依存は少ないです。
自作アルゴリズムもAdamと同じく、損失関数のスケールへの依存は少ないです。
入力値のスケールによる違い
$ k_x, k_y $ を大きい方に変えたときの様子。
上から $ k_x=k_y=1, 10, 100, 1000, 10000 $ です。
$ k_x, k_y $ を小さい方に変えたときの様子。
上から $ k_x=k_y=1, 0.1, 0.01, 0.001, 0.0001 $ です。
シンプルな勾配降下法とモーメンタムは、$ k_x, k_y $ を大きくすると発散してしまって、グラフから消えています。$ k_x, k_y $ を小さくすると学習の速度が極端に遅くなってしまいます。入力値のスケールが変わると学習率の調整が必須です。
Adagradは、シンプルな勾配降下法やモーメンタムほどではありませんが、学習推移が変わっています。入力値のスケールに依存しますので、学習率の調整が必要です。
RMSpropもAdagradと同じく、入力値のスケールに依存しますので、学習率の調整が必要です。
Adadeltaは、そもそも収束させる方法がわかりません。
Adamは、0.1〜1000ぐらいの範囲では、傾向や学習速度はあまり変わりません。入力値のスケールへの依存は少ないです。大きくスケールが変わると対応できないようです。
自作アルゴリズムは、Adamよりもう少し広く 0.0001〜1000ぐらいの範囲では、傾向や学習速度はあまり変わりません。入力値のスケールへの依存は少ないです。
Pythonソースコード
今回の調査に使ったソースコードです。Google Colaboratoryで実行しました。
opts = [ (tf.optimizers.SGD(learning_rate=0.1), "sgd"), (tf.optimizers.SGD(learning_rate=0.1, momentum=0.5), "momentum"), (tf.optimizers.Adagrad(learning_rate=2.0), "adagrad"), (tf.optimizers.RMSprop(learning_rate=0.005), "rmsprop"), (tf.optimizers.Adadelta(learning_rate=100), "adadelta"), (tf.optimizers.Adam(learning_rate=0.2), "adam"), (CustomOptimizer(learning_rate=0.1), "custom"), ] def calculate(k1x, k1y, k2, maxLoopCount, plotXY): k1x2 = k1x * k1x k1y2 = k1y * k1y # 目的となる損失関数 def loss(i): x2 = k1x2 * x[i] * x[i] y2 = k1y2 * y[i] * y[i] r2 = (x2 + y2 - 1.0) x1 = k1x * x[i] + 1.0 ret = r2 * r2 + 0.125 * x1 * x1 return k2 * ret thres = k2 * 0.0001 # 最適化する変数 x = [] y = [] # グラフにするための配列 xHistory = [] yHistory = [] lossHistory = [] calculationTime = [] convergenceCounter1 = [] convergenceCounter2 = [] maxLoopCountAnimation = maxLoopCount x_ini = 0.1 / k1x y_ini = 2.0 / k1y for i in range(len(opts)): x.append(tf.Variable(x_ini)) y.append(tf.Variable(y_ini)) xHistory.append([]) yHistory.append([]) lossHistory.append([]) convergenceCounter1.append(maxLoopCount) convergenceCounter2.append(0) start = time.time() for loopCount in range(maxLoopCount): l = float(loss(i)) # グラフにするために記録 xHistory[i].append(float(x[i])) yHistory[i].append(float(y[i])) lossHistory[i].append(l) if (math.isfinite(l) and l < thres and convergenceCounter1[i] >= maxLoopCount): convergenceCounter1[i] = loopCount if (not math.isfinite(l) or l >= thres): convergenceCounter2[i] = loopCount + 1 # 最適化 opts[i][0].minimize(lambda: loss(i), var_list = [x[i], y[i]]) calculationTime.append(time.time() - start) colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'] print((k1x, k1y, k2)) # グラフ化1つ目 plt.rcParams['figure.figsize'] = (6.4, 4.8) #plt.ylim(k2 * -1.1, k2 * 3.5) plt.ylim(-10.0, +2.0) for i in range(len(opts)): plt.plot(range(maxLoopCount), np.log10(lossHistory[i]) - np.log10(k2), color=colors[i % len(colors)]) plt.show() ths = np.linspace(-math.pi, math.pi, 100) thsx = np.cos(ths) / k1x thsy = np.sin(ths) / k1y if plotXY: # グラフ化2つ目以降 plt.rcParams['figure.figsize'] = (16.0, 6.0) for i in range(len(opts)): print(opts[i][1]) print("time: " + str(calculationTime[i])) print("counter1: " + str(convergenceCounter1[i])) print("counter2: " + str(convergenceCounter2[i])) print("loss: " + str(lossHistory[i][-1])) fig, (ax1, ax2) = plt.subplots(ncols=2) ax1.set_xlim(-2 / k1x, +2 / k1x) ax1.set_ylim(-1.5 / k1y, +1.5 / k1y) ax1.plot(thsx, thsy, color="#aaaaaa") ax1.add_patch(patches.Ellipse(xy=(-1.0, 0.0), width=0.2 / k1x, height=0.2 / k1y, fc="#cccccc")) ax1.plot(xHistory[i], yHistory[i], color=colors[i % len(colors)]) ax2.set_ylim(-10.0, +2.0) ax2.plot(range(maxLoopCount), np.log10(lossHistory[i]) - np.log10(k2), color=colors[i % len(colors)]) plt.show() calculate(1.0, 1.0, 1.0, 1000, False) calculate(0.1, 0.1, 1.0, 1000, False) calculate(0.01, 0.01, 1.0, 1000, False) calculate(0.001, 0.001, 1.0, 1000, False) calculate(0.0001, 0.0001, 1.0, 1000, False)2020/09/22追記: CustomOptimizerのソースコード → TensorFlowでOptimizerを自作する
まとめ
ディープラーニングでは値のスケールがだいたい決まっているケースが多いので、幅広いスケールへの対応は重要ではありませんが、値範囲が予想しづらい最適化問題では、スケールの許容範囲が広いほうがよいです。私の自作アルゴリズムはもともとスケールに対する耐性を意識したものですが、Adamも強いことがわかりました。
リンク
関連する私の記事
- 投稿日:2020-09-15T19:07:38+09:00
DeepLearning環境の構築 (Ubuntu 20.04 LTS)
Deep Learning環境の構築 Ubuntu 20.04 LTS
Ubuntu 20.04で安定的にDeep Learningできるようになったのでインストール方法を記録
動作確認環境
- Ubuntu 20.04 LTS
- NVIDIA RTX 2080Ti 1台構成
インストール時のフリーズ問題
Ubuntu 20.04 LTS では、Ubuntu 18.04 と同様に、Nvidia の GPUと相性が悪いため
以下のような画面になる回避方法としては、インストール時やGRUBの画面で
nouveau
という デフォルトのドライバを無効化することでフリーズしなくなる。この画面で Install Ubuntu にカーソルを合わせて e を押し
quiet splash ---
となっているところを
quiet splash nomodeset ---
と書き換えることでnouveau
を無効化できる.書き換えたら、
Ctrl-x
でオプションを更新した状態で起動が走る指示に従ってインストール。
最小構成ではなく、追加のドライバーなどをインストールすると、
nvidia-driver が入るためおすすめ。初回ログイン時のフリーズ問題
インストール後にも同様の問題が発生するので、 nomodeset 対応が必要なケースがある(特に自動ログインを有効化した場合にログインループになる場合)
ログイン画面で
ctrl + alt + F2
を押下で、CLIのログイン画面に入れるため、
username, password を入力し、nomodeset
に対応する処理を実行sudo vi /etc/default/grub GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" となっているところを GRUB_CMDLINE_LINUX_DEFAULT="quiet" とする # 変更を適用 sudo update-grab # 再起動 sudo reboot再起動後
この時点でドライバーが入っています。
$ nvidia-smi Tue Sep 15 18:56:00 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 440.100 Driver Version: 440.100 CUDA Version: 10.2 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce RTX 208... Off | 00000000:01:00.0 Off | N/A | | 41% 31C P8 13W / 250W | 311MiB / 10997MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 973 G /usr/lib/xorg/Xorg 175MiB | | 0 1339 G /usr/bin/gnome-shell 120MiB | | 0 3263 G /usr/lib/firefox/firefox 6MiB | | 0 3787 G gnome-control-center 6MiB | +-----------------------------------------------------------------------------+主要なライブラリをインストール (必要な方のみ)
sudo apt-get update sudo apt-get upgrade sudo apt-get install -y vim csh flex gfortran git g++ cmake xorg-dev patch zlib1g-dev libbz2-dev libboost-all-dev openssh-server libcairo2 libcairo2-dev libeigen3-dev lsb-core lsb-base net-tools network-manager xclip gdebi-core libffi-dev make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-opensslpyenv のインストール (必要な方のみ)
git clone https://github.com/yyuu/pyenv.git ~/.pyenvパスを通します
$ vim ~/.bashrc # 末尾に以下を追記 export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" $ source ~/.bashrc好きなPythonバージョンのインストール
$ pyenv install 3.6.9 $ pyenv global 3.6.9 $ pyenv rehash $ python -V Python 3.6.9テスト
$ mkdir -p ~/workspace/test $ cd ~/workspace/test $ python -m venv venv36tf $ source venv36tf/bin/activate $ pip install --upgrade pip $ pip install tensorflow-gpu==2.2mnist の実験
以下をtest_mnist.py
として保存test_mnist.pyimport tensorflow as tf mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(), tf.keras.layers.Dense(512, activation=tf.nn.relu), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10, activation=tf.nn.softmax) ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=5) model.evaluate(x_test, y_test)実行
以下のように出力されれば成功$ python test_mnist.py Epoch 1/5 1875/1875 [==============================] - 1s 768us/step - loss: 0.2215 - accuracy: 0.9337 Epoch 2/5 1875/1875 [==============================] - 1s 733us/step - loss: 0.0980 - accuracy: 0.9700 Epoch 3/5 1875/1875 [==============================] - 1s 739us/step - loss: 0.0711 - accuracy: 0.9780 Epoch 4/5 1875/1875 [==============================] - 1s 738us/step - loss: 0.0562 - accuracy: 0.9821 Epoch 5/5 1875/1875 [==============================] - 1s 736us/step - loss: 0.0437 - accuracy: 0.9861 313/313 [==============================] - 0s 608us/step - loss: 0.0613 - accuracy: 0.9816
- 投稿日:2020-09-15T18:39:39+09:00
Tensorflow-gpuを使う際にはまったこと
tensorflowを用いて機械学習をしようとした際にはまったことがあったので記述していく
環境
環境 version OS Windows tensorflow 2.3.0 CUDA 11.0 問題
import tensorflow as tf mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(input_shape=(28, 28)), tf.keras.layers.Dense(512, activation=tf.nn.relu), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10, activation=tf.nn.softmax) ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=5) model.evaluate(x_test, y_test)上のようなコードを実行しようとした際に
F .\tensorflow/core/kernels/random_op_gpu.h:232] Non-OK-status: GpuLaunchKernel(FillPhiloxRandomKernelLaunch<Distribution>, num_blocks, block_size, 0, d.stream(), gen, data, size, dist) status: Internal: invalid configuration argumentというエラーが発生し実行ができなくなった。
このエラーは
import os os.environ["CUDA_VISIBLE_DEVICES"] = "-1"としてGPUを使わない設定にすることで回避できた。
CUDAが原因かと思いダウンロードしなおしてみたりパスの設定を見直してみたが結果は変わらなかった・・・解決策
pip install tf-nightly-gpuとすることで解決できた。
どうやらtf-nightly-gpu
ってなんだと思ったがtensorflowの最新版で2.3.0よりも新しいもののようだった。pip listで調べてみると
tf-estimator-nightly 2.4.0.dev2020091501 tf-nightly-gpu 2.4.0.dev20200912となっており開発段階のものらしかった。
どうしてこれでエラーを解決できたのかはわからなかった...
参考
https://itips.krsw.biz/tensorflow-keras-gpu-deactivate/
https://github.com/tensorflow/tensorflow/issues/30665
- 投稿日:2020-09-15T16:56:06+09:00
Google ColabでOxford_iiit_petを使用した画像セグメン
tensorflowを使って画像のセグメンテーションを勉強中のかわたくです。
今回はoxfordのデータセットを使ってみました。
実行環境
macos
Google Colab詰まったところ
vscodeで一旦コードは書き終えたのですが、やはりGPUを使えないため処理が遅いという悩みが残ったまま。
そこで、Google先生が公表しているGoogle Colabを使うことにしました。
こちらはGPUを無料で使うことができます。まず、自分のコードを貼ったのですが、Oxfordのデータセットをダウンロードできていないとの表示。
あれ〜、おかしいな。VScodeでは普通にできたんだけどな〜って思いながら、色々調べてみたところ、
"u-netによる画像セグメンテーション"
こちらの記事が見つかりましたので、参考にさせてもらったところ、!pip install -q git+https://github.com/tensorflow/examples.git !pip install -q -U tfds-nightlyこれらのコードを最初に書く必要があったみたいでした。
まとめ
どうやらpcのterminalで一旦ダウンロードしていたとしても、google colabで使う時は、colab上でまたダウンロードする必要がありそうですね。
そしてやはりGPUはCPUよりも高速な処理が可能でした!
- 投稿日:2020-09-15T01:16:16+09:00
Nvidia-Driver インストール 完全マニュアル(2020年9月11日時点 Ubuntu 18.04対応)
目次
1. Nvidia Driver関連を安全に導入するための3つのコツ
2. 導入前の準備
3. Nvidia-Driverのインストール
4. Cudaのインストール
5. CuDNNのインストール
6. 想定されるエラー集
この投稿は個人ブログとのクロス投稿になります。
1. Nvidia Driverを安全に導入するための3つのコツ
- Nvidia-Driverをインストール後に、必ず再起動をすること
- cudaをインストールする際には,cudaのバージョンを指定してインストールすること
sudo apt-get -y install cuda-**-*
- TensorflowまたはKerasを使用される方はTensorflow GPUの公式ドキュメントを参照し、cuDNNとcuda, Tensorflow-gpuのバージョンを合わせること
2. 導入前の準備
まず、はじめにnvidia, cuda関連のライブラリがインストールされているかを確認します。
$ dpkg -l | grep nvidia $ dpkg -l | grep cudaそして、それらをまず削除し、依存関係のないクリーンな状態にします。
$ sudo apt remove --purge "nvidia-*" -y && sudo apt autoremove $ sudo apt remove --purge "cuda-*" -y && sudo apt autoremove $ sudo apt remove --purge "libcudnn*" -y && sudo apt autoremove $ sudo apt remove --purge "libnvidia-*" -y && sudo apt autoremove3. Nvidia-Driverのインストール
現在お使いのUbuntu OSに最適なNvidia-Driverを以下のコマンドで見つけます。
$ sudo ubuntu-drivers devices $ sudo apt install nvidia-driver-***上記のコマンドで推奨されていたのは440(2020年9月15日時点)でしたが, 同時期に同じVersionのUbuntu OSでGoogle Colab で450が採用されていたため、450を採用しました。
(以下のrebootがコツの1つ目です。)
$ reboot $ nvidia-smi注意 :
nvidia-smi
におけるDriver Version
とCUDA Version
は全く当てになりません。信用できるのは、nvidia-smiのバージョンのみです。
4. Cudaのインスール
- バージョンをチェックします
ここからcudaのバイナリファイルをインストールします。
注意1 : Linux, X86_64, Ubuntu, 18.04, deb(network)を選択
注意2 : 指示の最終行のsudo apt-get install cuda
は誤りです。
注意3 : 最新のTensorflowでさえcuda10.2はまだサポートしていないため、10.1をインストールすることを推薦します (参照元
間違いsudo apt-get -y install cuda正解
sudo apt-get -y install cuda-**-*このコマンド
sudo apt-get -y install cuda
は自動的に最新のcudaをダウンロードするように設定されているので要注意です。5. CuDNNのインストール
ここからCuDNNをダウンロード可能です
ダウンロード前に必ずNvidia開発者アカウントへの登録が必須です。
注意 : これら3つのファイルが必要です。(licudnn7_7**.deb
,libcudnn7-dev_**.deb
,libcudnn7-doc_**.deb
)$ sudo dpkg -i libcudnn7_**.deb $ sudo dpkg -i libcudnn7-dev_**.deb $ sudo dpkg -i libcudnn7-doc_**.deb6. 想定されるエラー集
-
$ nvidia-smi
のコマンドが存在しないと言われるPCを再起動してください。(nvidia-driverをインストール後)
$ sudo reboot-
$ sudo apt install --fix-broken
の際にエラーのループが生じるvidia-driver-440をインストール後に cuda10.2をインストールしようとしたところ、cuda10.2がnvidia-driver-450と依存関係があり、ループが生じた
$ sudo apt install --fix-broken Reading package lists... Done Building dependency tree Reading state information... Done You might want to run 'apt --fix-broken install' to correct these. The following packages have unmet dependencies: cuda-drivers : Depends: cuda-drivers-450 (= 450.36.06-1) but it is not going to be installed libnvidia-decode-450 : Depends: libnvidia-compute-450 (= 450.36.06-0ubuntu1) but it is not going to be installed nvidia-compute-utils-450 : Depends: libnvidia-compute-450 but it is not going to be installed nvidia-driver-450 : Depends: libnvidia-compute-450 (= 450.36.06-0ubuntu1) but it is not going to be installed Recommends: libnvidia-compute-450:i386 (= 450.36.06-0ubuntu1) but it is not installable Recommends: libnvidia-decode-450:i386 (= 450.36.06-0ubuntu1) but it is not installable Recommends: libnvidia-encode-450:i386 (= 450.36.06-0ubuntu1) but it is not installable Recommends: libnvidia-ifr1-450:i386 (= 450.36.06-0ubuntu1) but it is not installable Recommends: libnvidia-fbc1-450:i386 (= 450.36.06-0ubuntu1) but it is not installable Recommends: libnvidia-gl-450:i386 (= 450.36.06-0ubuntu1) but it is not installable nvidia-utils-450 : Depends: libnvidia-compute-450 but it is not going to be installed E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution).sudo apt-get -o Dpkg::Options::="--force-overwrite" install --fix-broken -ynvidia-driverのバージョン
- 使用しているPC
> GeForce RTX 2080-Ti > > OS : Ubuntu 18.04.4 LTS (Bionic Beaver) > Nvidia-Driver : 450 > Cuda : 10.1 > CuDNN : 7.6.3.30-1+cuda10.1 > Python : 3.7.5 > PyTorch : 1.5.1+cu1 > Torchvision : 0.6.1+cu1 > Tensorflow : 2.2.0- Google Colab
> Version of nvidia-driver in pre-installed Google-Colab is 450 on 2020/09/11 > >OS : Ubuntu 18.04.3 LTS (Bionic Beaver) > Nvidia-Driver : 450 > Cuda : 10.1 > CuDNN : 7.65.32-1+cuda10.1 > Python : 3.6.9 > PyTorch : 1.5.1+cu101 > Torchvision : 0.6.1+cu101 > Tensorflow : 2.2.0- 上記の確認方法
$ cat /etc/os-release ## Only works on Ubuntu OS $ dpkg -l | grep nvidia $ dpkg -l | grep cuda $ dpkg -l | grep libcudnn $ print(torch.__version__) $ print(torchvision.__version__) $ print(tensorflow.__version__) $ ! python --versionまとめ
Nvidia-Driverのメモ書きを少し丁寧にまとめました。環境を再現することは研究を再実験する上でのスタートとなるので、
Google Colabでも推奨されているように、Tensorflow, Cuda, CuDNNのバージョンを必ずJupter NotebookやColab Notebookに記載することが後からコードを見た人が再現するために重要だと再認識させられました。
皆様もぜひ参考にしていただければと思います。
- 投稿日:2020-09-15T00:06:11+09:00
追い詰められたWEBエンジニアのAIことはじめ。colabでとりあえず動かす文章分類問題(BERT)(2020/09/14現在)
AIをやれと言われて困ったエンジニアの奮闘記録です。とりあえず文章分類をcolabでやるよ!
書いた人
Python:わからん。虚無
AI:わからん。虚無 kerasとか知らんし。。
colab:わからん、虚無
情報源:web、stack over flow、github1. これをやる
https://tech-blog.cloud-config.jp/2020-02-06-category-classification-using-bert/
本記事は↑をとりあえず動かそうとして動かなかったのでわからんまま無理くり動かした補完記事です。入力
とりあえず動かしたいので最小行数のデータ
train
feature ちなみに弓道のリーグ戦は四人一組のグループが交互に引く形である 番号変えたいと思う人間もいるのではないのでしょうか?label スポーツ 携帯電話test
feature ちなみにグループが交互に引く形である 番号がほしいlabel スポーツ 携帯電話colabでの実行をバチバチ全部書いていくよ
# ドライブのマウント from google.colab import drive drive.mount('/content/drive')# 必要ライブラリのインストール !pip install sentencepiece !pip install keras_bert # これ必要だったよ?しらんけど。 !pip install np_utils # GPUとかTPUとか使いたい...わからない... from __future__ import absolute_import, division, print_function, unicode_literals try: %tensorflow_version 2.x except Exception: pass import tensorflow as tf import os import tensorflow_datasets as tfds print(tf.__version__)# BERTの設定ファイル、モデルのロード # max値を得るプログラム import pandas as pd import sentencepiece as spm # feature.csvは上記で用意したファイルのパスを指定してください train_features_df = pd.read_csv('/content/drive/My Drive/bert/data/trains/features.csv') def _get_indice(feature): tokens = [] tokens.append('[CLS]') tokens.extend(sp.encode_as_pieces(feature)) tokens.append('[SEP]') number = len(tokens) return number sp = spm.SentencePieceProcessor() # ダウンロードした事前学習モデルのパスを指定してください sp.Load('/content/drive/My Drive/bert/bert-wiki-ja/wiki-ja.model') numbers = [] for feature in train_features_df['feature']: features_number = _get_indice(feature) numbers.append(features_number) # 最大トークン数 max_token_num = max(numbers) print("max_token_number: " + str(max_token_num))ここで書き出された値を次の箱にかくのです。 18が出力された
# 学習データのロード関数 import sys sys.path.append('modules') from keras_bert import load_trained_model_from_checkpoint from keras import utils # BERTのロード config_path = '/content/drive/My Drive/bert/bert-wiki-ja/bert_finetuning_config_v1.json' # 拡張子まで記載しない checkpoint_path = '/content/drive/My Drive/bert/bert-wiki-ja/model.ckpt-1400000' # 最大のトークン数 SEQ_LEN = 18 BATCH_SIZE = 16 BERT_DIM = 768 LR = 1e-4 # 学習回数 EPOCH = 1 # 20 bert = load_trained_model_from_checkpoint(config_path, checkpoint_path, training=True, trainable=True, seq_len=SEQ_LEN) bert.summary()学習回数は1回(時間かけたくない)
# 学習データのロード関数 from keras import utils import numpy as np # これ必要だったよ? maxlen = SEQ_LEN # 参照サイトではmaxlenが宣言されずに使われようとしていた... sp = spm.SentencePieceProcessor() sp.Load('/content/drive/My Drive/bert/bert-wiki-ja/wiki-ja.model') def _get_indice(feature): indices = np.zeros((maxlen), dtype = np.int32) tokens = [] tokens.append('[CLS]') tokens.extend(sp.encode_as_pieces(feature)) tokens.append('[SEP]') for t, token in enumerate(tokens): if t >= maxlen: break try: indices[t] = sp.piece_to_id(token) except: logging.warn(f'{token} is unknown.') indices[t] = sp.piece_to_id('<unk>') return indices def _load_labeldata(train_dir, test_dir): train_features_df = pd.read_csv(f'{train_dir}/features.csv') train_labels_df = pd.read_csv(f'{train_dir}/labels.csv') test_features_df = pd.read_csv(f'{test_dir}/features.csv') test_labels_df = pd.read_csv(f'{test_dir}/labels.csv') label2index = {k: i for i, k in enumerate(train_labels_df['label'].unique())} index2label = {i: k for i, k in enumerate(train_labels_df['label'].unique())} class_count = len(label2index) train_labels = utils.np_utils.to_categorical([label2index[label] for label in train_labels_df['label']], num_classes=class_count) test_label_indices = [label2index[label] for label in test_labels_df['label']] test_labels = utils.np_utils.to_categorical(test_label_indices, num_classes=class_count) train_features = [] test_features = [] for feature in train_features_df['feature']: train_features.append(_get_indice(feature)) train_segments = np.zeros((len(train_features), maxlen), dtype = np.float32) print("maxlen") print(maxlen) for feature in test_features_df['feature']: test_features.append(_get_indice(feature)) test_segments = np.zeros((len(test_features), maxlen), dtype = np.float32) print(f'Trainデータ数: {len(train_features_df)}, Testデータ数: {len(test_features_df)}, ラベル数: {class_count}') return { 'class_count': class_count, 'label2index': label2index, 'index2label': index2label, 'train_labels': train_labels, 'test_labels': test_labels, 'test_label_indices': test_label_indices, 'train_features': np.array(train_features), 'train_segments': np.array(train_segments), 'test_features': np.array(test_features), 'test_segments': np.array(test_segments), 'input_len': maxlen }# モデル作成関数 from keras.layers import Dense, Dropout, LSTM, Bidirectional, Flatten, GlobalMaxPooling1D from keras_bert.layers import MaskedGlobalMaxPool1D from keras import Input, Model from keras_bert import AdamWarmup, calc_train_steps def _create_model(input_shape, class_count): decay_steps, warmup_steps = calc_train_steps( input_shape[0], batch_size=BATCH_SIZE, epochs=EPOCH, ) bert_last = bert.get_layer(name='NSP-Dense').output x1 = bert_last output_tensor = Dense(class_count, activation='softmax')(x1) # Trainableの場合は、Input Masked Layerが3番目の入力なりますが、 # FineTuning時には必要無いので1, 2番目の入力だけ使用します。 # Trainableでなければkeras-bertのModel.inputそのままで問題ありません。 model = Model([bert.input[0], bert.input[1]], output_tensor) model.compile(loss='categorical_crossentropy', optimizer=AdamWarmup(decay_steps=decay_steps, warmup_steps=warmup_steps, lr=LR), #optimizer='nadam', metrics=['mae', 'mse', 'acc']) return model# 学習データのロードとモデルの準備 from keras.callbacks import EarlyStopping, ModelCheckpoint, TensorBoard from keras.utils.np_utils import to_categorical import numpy as np from keras import utils trains_dir = '/content/drive/My Drive/bert/data/trains' tests_dir = '/content/drive/My Drive/bert/data/tests' data = _load_labeldata(trains_dir, tests_dir) model_filename = '/content/drive/My Drive/bert/models/knbc_finetuning.model' model = _create_model(data['train_features'].shape, data['class_count']) model.summary()# 学習の実行 history = model.fit([data['train_features'], data['train_segments']], data['train_labels'], epochs = EPOCH, batch_size = BATCH_SIZE, validation_data=([data['test_features'], data['test_segments']], data['test_labels']), shuffle=False, verbose = 1, callbacks = [ # ModelCheckpoint(monitor='val_acc', mode='max', filepath=model_filename, save_best_only=True) ])1分ぐらいかかる
# モデルの評価 from sklearn.metrics import classification_report, confusion_matrix from keras.models import load_model from keras_bert import get_custom_objects model = load_model(model_filename, custom_objects=get_custom_objects()) predicted_test_labels = model.predict([data['test_features'], data['test_segments']]).argmax(axis=1) numeric_test_labels = np.array(data['test_labels']).argmax(axis=1) report = classification_report( numeric_test_labels, predicted_test_labels, target_names=['携帯電話', 'スポーツ'], output_dict=True) display(pd.DataFrame(report).T)結果は↓
precision recall f1-score support 携帯電話 0.50 1.0 0.666667 1.0 スポーツ 0.00 0.0 0.000000 1.0 accuracy 0.50 0.5 0.500000 0.5 macro avg 0.25 0.5 0.333333 2.0 weighted avg 0.25 0.5 0.333333 2.0入力は訓練2行、テスト2行、学習1回なので学習結果としてはだめだめですが、とりあえず動くっぽい
# 予測 import sys import pandas as pd import sentencepiece as spm import logging import numpy as np from keras import utils from keras.models import load_model from keras.preprocessing.sequence import pad_sequences from keras_bert import load_trained_model_from_checkpoint from keras_bert import get_custom_objects from sklearn.metrics import classification_report, confusion_matrix sys.path.append('modules') # SentencePieceProccerモデルの読込 spp = spm.SentencePieceProcessor() spp.Load('/content/drive/My Drive/bert/bert-wiki-ja/wiki-ja.model') # BERTの学習したモデルの読込 model_filename = '/content/drive/My Drive/bert/models/knbc_finetuning.model' model = load_model(model_filename, custom_objects=get_custom_objects()) SEQ_LEN = 18 maxlen = SEQ_LEN def _get_indice(feature): indices = np.zeros((maxlen), dtype=np.int32) tokens = [] tokens.append('[CLS]') tokens.extend(spp.encode_as_pieces(feature)) tokens.append('[SEP]') for t, token in enumerate(tokens): if t >= maxlen: break try: indices[t] = spp.piece_to_id(token) except: logging.warn('unknown') indices[t] = spp.piece_to_id('<unk>') return indices# 予測実行 feature = "運動するのは楽しい" test_features = [] test_features.append(_get_indice(feature)) test_segments = np.zeros( (len(test_features), maxlen), dtype=np.float32) #np.arrayでlistに変換しないとだめだった。 predicted_test_labels = model.predict( [np.array(test_features), test_segments]).argmax(axis=1) label_data = pd.read_csv('/content/drive/My Drive/bert/label_id/id_category.csv') label = label_data.query(f'id == {predicted_test_labels[0]}') label = label.iloc[0] label_name = label['label'] print(label_name)スポーツ感想
- データ増やせばいけそう
- 試行錯誤していたらcolabのメモリ限界にすぐ到達するので辛い
- 業務でやるなら金でスペックぶっ叩いた環境が必要そう
- EC2なら1.5$/1Hのマシンがある
- tpu使いたい
追い詰められたWEBエンジニアですが、インターネッツの恩恵にあずかりAI活動を始めることができそうです。高速道路はできている感じはあり、フレームワークも優秀(というかデータ用意できたら何もすることない)です。
ただ、調べた感じではAIって作るのに金かかるから経営陣はかんたんにAIとか言わないほうが良いかもしれません。のるかそるか全くわからない状態で数百万の投資+人件費の覚悟はありますか?という問題となりそうです。