20200915のTensorFlowに関する記事は6件です。

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 $ です。グラフにするとこんな感じです。

image.png

スケールを表すパラメータ $ 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 $ のときの学習推移は次のグラフです。

image.png

この $ 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 $ です。

image.png
image.png
image.png
image.png
image.png

$ k_l $ を小さい方に変えたときの様子。

上から $ k_l=1, 0.1, 0.01, 0.001, 0.0001 $ です。

image.png
image.png
image.png
image.png
image.png

シンプルな勾配降下法モーメンタムは、$ k_l $ を大きくすると発散してしまって、グラフから消えています。$ k_l $ を小さくすると学習の速度が極端に遅くなってしまいます。損失関数のスケールが変わると学習率の調整が必須です。

Adagradは、シンプルな勾配降下法やモーメンタムほどではありませんが、学習推移が変わっています。一度でも勾配が極端に大きいところに行ってしまうとその後の学習が遅くなってしまうのが原因だと想像しています。

RMSpropは、学習推移が変わりません。損失関数のスケールには依存しません。

Adadeltaは、学習推移が変わっています。なぜ変わるのかは理解できていないです。そもそも収束させる方法がわかりません。

Adamは、学習推移が少し変わりますが、傾向や学習速度はあまり変わりません。損失関数のスケールへの依存は少ないです。

自作アルゴリズムもAdamと同じく、損失関数のスケールへの依存は少ないです。

入力値のスケールによる違い

$ k_x, k_y $ を大きい方に変えたときの様子。

上から $ k_x=k_y=1, 10, 100, 1000, 10000 $ です。

image.png
image.png
image.png
image.png
image.png

$ k_x, k_y $ を小さい方に変えたときの様子。

上から $ k_x=k_y=1, 0.1, 0.01, 0.001, 0.0001 $ です。

image.png
image.png
image.png
image.png
image.png

シンプルな勾配降下法モーメンタムは、$ 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も強いことがわかりました。

リンク

関連する私の記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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と相性が悪いため
以下のような画面になる

image.png

回避方法としては、インストール時やGRUBの画面で nouveau という デフォルトのドライバを無効化することでフリーズしなくなる。

この画面で Install Ubuntu にカーソルを合わせて e を押し
image.png

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-openssl

pyenv のインストール (必要な方のみ)

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.2

mnist の実験
以下を test_mnist.py として保存

test_mnist.py
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(),
  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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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よりも高速な処理が可能でした!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 autoremove

3. 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 VersionCUDA 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_**.deb

6. 想定されるエラー集

- $ 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 -y

nvidia-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に記載することが後からコードを見た人が再現するために重要だと再認識させられました。
皆様もぜひ参考にしていただければと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

追い詰められたWEBエンジニアのAIことはじめ。colabでとりあえず動かす文章分類問題(BERT)(2020/09/14現在)

AIをやれと言われて困ったエンジニアの奮闘記録です。とりあえず文章分類をcolabでやるよ!

書いた人

Python:わからん。虚無
AI:わからん。虚無 kerasとか知らんし。。
colab:わからん、虚無
情報源:web、stack over flow、github

1. これをやる

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とか言わないほうが良いかもしれません。のるかそるか全くわからない状態で数百万の投資+人件費の覚悟はありますか?という問題となりそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む