いまさらですが、2017年のサハリンでの出来事を報告します。 [サハリン・バイクツーリング]
すごく今更なんですが、2017年にサハリンに行ったときの報告をしていこうと思います。
そもそもなんで2年も経った今頃かというと、なんというかたまたまちょっとだけお仕事でロシアやら人工知能やらに関わることがあって、プライベートと仕事を分けてブログに書き込むことが面倒になったためです。
今は、ロシア関係と人工知能関係のどちらも落ち着きましたので、やっと再開できたという感じです。
●2017年のサハリン行きのきっかけ
そもそもは、バイクでサハリンに渡った2度目の年、2015年に始まります。
私が友人とバイクで渡った2013年、途中でホンダのアフリカツインに乗った現地のロシア人の方(名前はセルゲイさん)から話しかけられたのがきっかけです。
200キロ位一緒にバイクで走ったりして、それはそれで面白かったんですが、結局その年はそれで終わりました。
で、2回目の2015年にサハリンにバイクで渡ったときにも、これまた白鳥湖のあたりでこれまた偶然にセルゲイさんと再開し、ひょっとしてまた会えるんじゃないかなと思って用意していたおみやげを渡したわけです。で、セルゲイさんからは持っていたランチをごちそうしてもらったりして、じゃあせっかくだから一緒にご飯でも食べようよ、と話をしたんですがお互いにスケジュールが合わず、とにかく電話番号とメールアドレスの交換をして別れたんです。
で、本当に時々ショートメッセージで新年のお祝いくらいをやり取りしていたんですけど、2017年に「今年はサハリンに来ないの?」というメッセージをもらったことをきっかけに…
友人と「じゃあ行ってみるか?」ということで、「友達と昨日電話して相談した。サハリンに行くことに決めたよ」という連絡をしてサハリンに行くことになったのが2017年だったんです。
稚内とサハリンのフェリーも運行が終了し旅客船だけの運行に切り替わっていましたし、もうバイクでは行けなかったので飛行機で行くことにしました。
友人と二人して成田からヤクーツク航空でサハリン入りしたわけですが、もう私は仕事も含めると5回目、友人も旅行だけで3回目(しかも友人の場合には初めての飛行機でのサハリン入り)だったので、なんというか慣れた感じでした。
どうせツアーについているホテルまでの送迎車もついてましたし。
そんなわけで、2017年のサハリン旅行が始まったのです。
そもそもなんで2年も経った今頃かというと、なんというかたまたまちょっとだけお仕事でロシアやら人工知能やらに関わることがあって、プライベートと仕事を分けてブログに書き込むことが面倒になったためです。
今は、ロシア関係と人工知能関係のどちらも落ち着きましたので、やっと再開できたという感じです。
●2017年のサハリン行きのきっかけ
そもそもは、バイクでサハリンに渡った2度目の年、2015年に始まります。
私が友人とバイクで渡った2013年、途中でホンダのアフリカツインに乗った現地のロシア人の方(名前はセルゲイさん)から話しかけられたのがきっかけです。
200キロ位一緒にバイクで走ったりして、それはそれで面白かったんですが、結局その年はそれで終わりました。
で、2回目の2015年にサハリンにバイクで渡ったときにも、これまた白鳥湖のあたりでこれまた偶然にセルゲイさんと再開し、ひょっとしてまた会えるんじゃないかなと思って用意していたおみやげを渡したわけです。で、セルゲイさんからは持っていたランチをごちそうしてもらったりして、じゃあせっかくだから一緒にご飯でも食べようよ、と話をしたんですがお互いにスケジュールが合わず、とにかく電話番号とメールアドレスの交換をして別れたんです。
で、本当に時々ショートメッセージで新年のお祝いくらいをやり取りしていたんですけど、2017年に「今年はサハリンに来ないの?」というメッセージをもらったことをきっかけに…
友人と「じゃあ行ってみるか?」ということで、「友達と昨日電話して相談した。サハリンに行くことに決めたよ」という連絡をしてサハリンに行くことになったのが2017年だったんです。
稚内とサハリンのフェリーも運行が終了し旅客船だけの運行に切り替わっていましたし、もうバイクでは行けなかったので飛行機で行くことにしました。
友人と二人して成田からヤクーツク航空でサハリン入りしたわけですが、もう私は仕事も含めると5回目、友人も旅行だけで3回目(しかも友人の場合には初めての飛行機でのサハリン入り)だったので、なんというか慣れた感じでした。
どうせツアーについているホテルまでの送迎車もついてましたし。
そんなわけで、2017年のサハリン旅行が始まったのです。
JETSON NANOにPYTORCHをインストール [人工知能(ディープラーニング)]
NVIDIAからワンボードの人工知能開発用ボード「JETSON NANO」が発売されました。
お値段13000円くらいで、販売が終了したTK1よりも高性能です。しかも安い!!
TK1だとセッティングのためにはUBUNTUが動作する母艦PCが必要だったんですが、NANOには必要ありません。単純なワンボードPCとしても性能は悪くないですし、すごいヤツです。
おかげで、これまでGPU演算ライブラリを使って人工知能を勉強しようと思ってもそれなりに敷居は高かったのですが、ずいぶん低くなりました。
で、私が使っていた人工知能フレームワークtorchもメンテナンス状態ということで新しい機能の追加はストップしていますので、Pytorchに移行することにしました。
※7月時点での補足があります!!
7月に追加した情報も読んでからインストールしてください!!!
◆Pytorchのインストール前の準備
最初にPIP3とgitを使えるようにする
sudo apt install python3-pip git
※注意:この他にもPytorchのインストールに必要なライブラリが抜けているかもしれません。
適宜必要なライブラリをインストールしてください。
◆Pytorchのインストール手順
各種ファイルのアーカイブファイルのダウンロードをする
wget https://nvidia.box.com/shared/static/veo87trfaawj5pfwuqvhl6mzc5b55fbj.whl -O torch-1.1.0a0+b457266-cp36-cp36m-linux_aarch64.whl
Python3で使えるようにインストールする
pip3 install numpy torch-1.1.0a0+b457266-cp36-cp36m-linux_aarch64.whl
これで、python3からPytorchが使えるようになるはずです。
以下のテストプログラムを実行して問題なければPytorchはインスールされています。
import torch
print(torch.__version__)
print('CUDA available: ' + str(torch.cuda.is_available()))
a = torch.cuda.FloatTensor(2).zero_()
print('Tensor a = ' + str(a))
b = torch.randn(2).cuda()
print('Tensor b = ' + str(b))
c = a + b
print('Tensor c = ' + str(c))
画像では自分が使いやすいようにIpyhon3をインストールして、そこからPython3を使っています。
人工知能フレームワークの勉強で数値データをメインに扱うだけならここまででも大丈夫ですが、やはりそれでは物足りなくなります。データセットの入手や画像処理ができるtorchvisionもインストールしましょう。
torchvisionをインストールしないとJPEG画像を読み込んで、そこからtensorに一気に変換したりということが出来ません。いちいちnumpyやらを経由させる必要が出てきて面倒になりますので、画像処理をやってみたい人はtorchvisionのインストールは必須です。
●torchvisionのインストール
インストール用ファイルのダウンロード
git clone https://github.com/pytorch/vision
ダウンロードしたディレクトリに移動
cd vision
python3へインストール実行
sudo python3 setup.py install
これで、エラーが出なければ良いのですが私の場合にはエラーが出たんです。
torchvisionは、Pillowに入っているPILを使います。Pillowを使えるようにしないと、画像処理の便利なコマンドが使えません。しかもpip3 install pillow --userとかやってもインストールできなかったりします。
そんなときには
sudo apt-get install libjpeg-dev
として、jpeg関係のライブラリをインストールしてください。
pillowは外部の画像処理ライブラリを呼び出しているので、それがないと動かないのです。
これでpython3を起動して、import torchvisionとしてエラーが出なければ大丈夫です。
参考URL
NVIDIAの公式サイト
https://devtalk.nvidia.com/default/topic/1049071/pytorch-for-jetson-nano/
======================
●2019/07/14 補足
nVIDIAの公式サイトの情報は、結構な頻度で変わっています。
私がインストールしたときには、 libjpeg-devの情報は記載されていませんでしたが、現在は記載されています。
抜粋すると、以下のようになります。
●python3.6にpytorch1.1をインストールする
WGETとPIP3でインストールします。
wget https://nvidia.box.com/shared/static/j2dn48btaxosqp0zremqqm8pjelriyvs.whl -O torch-1.1.0-cp36-cp36m-linux_aarch64.whl
pip3 install numpy torch-1.1.0-cp36-cp36m-linux_aarch64.whl
●torch visionをインストール
(1)まず画像等の変換作業に必要な他の関連するライブラリーをインストール
sudo apt-get install libjpeg-dev zlib1g-dev
(2)必要なファイルを、torchvisonというフォルダーを作成してgitでダウンロードする
git clone -b v0.3.0 https://github.com/pytorch/vision torchvision
(3)torchvisionのフォルダーに移動
cd torchvision
(4)Python3で設定ファイルを読み込ませて実行(実質的なインストール作業)する
sudo python3 setup.py install
(※公式サイトでは、「python3」ではなく、ver2.7である「python」と記載されていますが、それだとpython3を使う場合には不整合が起こると思います)
お値段13000円くらいで、販売が終了したTK1よりも高性能です。しかも安い!!
TK1だとセッティングのためにはUBUNTUが動作する母艦PCが必要だったんですが、NANOには必要ありません。単純なワンボードPCとしても性能は悪くないですし、すごいヤツです。
おかげで、これまでGPU演算ライブラリを使って人工知能を勉強しようと思ってもそれなりに敷居は高かったのですが、ずいぶん低くなりました。
で、私が使っていた人工知能フレームワークtorchもメンテナンス状態ということで新しい機能の追加はストップしていますので、Pytorchに移行することにしました。
※7月時点での補足があります!!
7月に追加した情報も読んでからインストールしてください!!!
◆Pytorchのインストール前の準備
最初にPIP3とgitを使えるようにする
sudo apt install python3-pip git
※注意:この他にもPytorchのインストールに必要なライブラリが抜けているかもしれません。
適宜必要なライブラリをインストールしてください。
◆Pytorchのインストール手順
各種ファイルのアーカイブファイルのダウンロードをする
wget https://nvidia.box.com/shared/static/veo87trfaawj5pfwuqvhl6mzc5b55fbj.whl -O torch-1.1.0a0+b457266-cp36-cp36m-linux_aarch64.whl
Python3で使えるようにインストールする
pip3 install numpy torch-1.1.0a0+b457266-cp36-cp36m-linux_aarch64.whl
これで、python3からPytorchが使えるようになるはずです。
以下のテストプログラムを実行して問題なければPytorchはインスールされています。
import torch
print(torch.__version__)
print('CUDA available: ' + str(torch.cuda.is_available()))
a = torch.cuda.FloatTensor(2).zero_()
print('Tensor a = ' + str(a))
b = torch.randn(2).cuda()
print('Tensor b = ' + str(b))
c = a + b
print('Tensor c = ' + str(c))
画像では自分が使いやすいようにIpyhon3をインストールして、そこからPython3を使っています。
人工知能フレームワークの勉強で数値データをメインに扱うだけならここまででも大丈夫ですが、やはりそれでは物足りなくなります。データセットの入手や画像処理ができるtorchvisionもインストールしましょう。
torchvisionをインストールしないとJPEG画像を読み込んで、そこからtensorに一気に変換したりということが出来ません。いちいちnumpyやらを経由させる必要が出てきて面倒になりますので、画像処理をやってみたい人はtorchvisionのインストールは必須です。
●torchvisionのインストール
インストール用ファイルのダウンロード
git clone https://github.com/pytorch/vision
ダウンロードしたディレクトリに移動
cd vision
python3へインストール実行
sudo python3 setup.py install
これで、エラーが出なければ良いのですが私の場合にはエラーが出たんです。
torchvisionは、Pillowに入っているPILを使います。Pillowを使えるようにしないと、画像処理の便利なコマンドが使えません。しかもpip3 install pillow --userとかやってもインストールできなかったりします。
そんなときには
sudo apt-get install libjpeg-dev
として、jpeg関係のライブラリをインストールしてください。
pillowは外部の画像処理ライブラリを呼び出しているので、それがないと動かないのです。
これでpython3を起動して、import torchvisionとしてエラーが出なければ大丈夫です。
参考URL
NVIDIAの公式サイト
https://devtalk.nvidia.com/default/topic/1049071/pytorch-for-jetson-nano/
======================
●2019/07/14 補足
nVIDIAの公式サイトの情報は、結構な頻度で変わっています。
私がインストールしたときには、 libjpeg-devの情報は記載されていませんでしたが、現在は記載されています。
抜粋すると、以下のようになります。
●python3.6にpytorch1.1をインストールする
WGETとPIP3でインストールします。
wget https://nvidia.box.com/shared/static/j2dn48btaxosqp0zremqqm8pjelriyvs.whl -O torch-1.1.0-cp36-cp36m-linux_aarch64.whl
pip3 install numpy torch-1.1.0-cp36-cp36m-linux_aarch64.whl
●torch visionをインストール
(1)まず画像等の変換作業に必要な他の関連するライブラリーをインストール
sudo apt-get install libjpeg-dev zlib1g-dev
(2)必要なファイルを、torchvisonというフォルダーを作成してgitでダウンロードする
git clone -b v0.3.0 https://github.com/pytorch/vision torchvision
(3)torchvisionのフォルダーに移動
cd torchvision
(4)Python3で設定ファイルを読み込ませて実行(実質的なインストール作業)する
sudo python3 setup.py install
(※公式サイトでは、「python3」ではなく、ver2.7である「python」と記載されていますが、それだとpython3を使う場合には不整合が起こると思います)
サハリン・バイクツーリング2017(予告) [サハリン・バイクツーリング]
ウラジオストクに行ってきました。 [ロシアについて思う]
年度末のいろいろな大変さを抜けて、ウラジオストクに行ってきました。
(金閣湾にかかる黄金橋「золотой мост」)
もう冬ではなくて、春のイメージでした。
ウラジオストクは一言で言えば「坂が多くて歩くとたいへん」でした。
まぁ、もともとは軍港のある閉鎖された町でしたから、それでどうこう言ってもしょうがないんですが…
ウラジオストクは、サハリンのユジノサハリンスクとはまた違った表情を見せる街でした。
なんか…「おしゃれ」なイメージがあるんです。
(ユジノサハリンスクは、私の個人的なイメージは「ほのぼの」した感じなんですが)
うまく言えないですが、なんかそんな気がしました。
なんで今の時期に行ったのかというと、これまたバイクでのツーリングを考えていたりするからです…
(今じゃないですよ! 夏以降です)
(金閣湾にかかる黄金橋「золотой мост」)
もう冬ではなくて、春のイメージでした。
ウラジオストクは一言で言えば「坂が多くて歩くとたいへん」でした。
まぁ、もともとは軍港のある閉鎖された町でしたから、それでどうこう言ってもしょうがないんですが…
ウラジオストクは、サハリンのユジノサハリンスクとはまた違った表情を見せる街でした。
なんか…「おしゃれ」なイメージがあるんです。
(ユジノサハリンスクは、私の個人的なイメージは「ほのぼの」した感じなんですが)
うまく言えないですが、なんかそんな気がしました。
なんで今の時期に行ったのかというと、これまたバイクでのツーリングを考えていたりするからです…
(今じゃないですよ! 夏以降です)
torch7:CSVでファイル保存 [人工知能(ディープラーニング)]
データをファイルとして保存するときにいろいろと方法はあるのですが、CSVファイルとして保存するのは意外と面倒だったりします。
例えば、以下のような単純な10x10の二次元配列をファイルとして保存するだけでも結構面倒です。
--------------------------------------
1 6 8 2 1 7 4 4 9 7
6 9 7 3 8 9 0 9 8 0
8 2 4 6 2 7 8 4 7 7
7 6 9 1 7 2 3 8 1 9
2 4 6 7 5 5 1 5 6 6
8 9 2 3 6 1 7 8 5 0
0 3 4 5 7 4 2 0 8 6
9 6 5 1 3 8 5 9 3 8
0 9 8 9 5 4 5 8 4 2
1 3 5 3 2 1 7 7 4 2
[torch.DoubleTensor of size 10x10]
--------------------------------------
こんな時は、以前の「パターン認識」でも紹介したcsvigoを使えば簡単です。
(今回はコマンドラインで直接動かしていますが、プログラムとしてファイルに保存しても動作はできます)
基本的にはこれだけです。
require 'csvigo'
a=torch.Tensor(10,10):random(0,9)
b=torch.totable(a)
csvigo.save{data=b,path='test.csv'}
解説すると…
-- csvigoライブラリーを読み込む
require 'csvigo'
-- 10x10の数値(Tensor)を乱数で作る
a=torch.Tensor(10,10):random(0,9)
-- torch.Tensor形式のaを、テーブル形式でbにセットする
b=torch.totable(a)
-- csvigo.saveを使って、ファイルとしてむ保存
-- 「data=」に続いてテーブルの変数、「path='ファイル名'」をつけます
-- カッコ()ではなく、{ }を使ってください。
csvigo.save{data=b,path='test.csv'}
たったこれだけです。
これでCSVファイルとして保存できます。
ただし、ここで注意するポイントがあります。
単純に
1
8
5
6
7
4
1
3
6
8
のようにファイルを出力したい場合、Tensorは
th> a
1
8
5
6
7
4
1
3
6
8
[torch.DoubleTensor of size 10]と単純な10個の配列を元にして、保存用テーブルのデータを作ってcsvigoライブラリで保存しようとしてもエラーになってしまいます。
具体的にはテーブルの変数 b
{
1 : 1
2 : 8
3 : 5
4 : 6
5 : 7
6 : 4
7 : 1
8 : 3
9 : 6
10 : 8
}
*このデータをcsvigoで保存しようするとエラーになります。
このような場合には、
-- まずは10個の配列を 1x10に直して
a=a:repeatTensor(a,1,1)
-- それを10x1に「t()」で行と列を入れ替えた配列に直して
a=a:t()
-- テーブル形式に変換します
a=torch.totable(a)
という感じで、10x1の配列(テンソル)としてデータに直して、それをテーブルに変換する必要があります。(もっとスマートな方法があるかもしれません)
th> a
9
6
9
8
7
9
1
4
1
8
[torch.DoubleTensor of size 10x1]
これが最終的には…
th> b
{
1 :
{
1 : 9
}
2 :
{
1 : 6
}
3 :
{
1 : 9
}
4 :
{
1 : 8
}
5 :
{
1 : 7
}
6 :
{
1 : 9
}
7 :
{
1 : 1
}
8 :
{
1 : 4
}
9 :
{
1 : 1
}
10 :
{
1 : 8
}
}
こんな形になります。
これであれば縦に数字が並んだCSVファイルが作成できます。
th> csvigo.save{path='ttt.csv',data=b}
<csv> writing to file: ttt.csv
<csv> writing done
これでもうCSVファイルでの保存ができるのでいろいろと利用範囲が広がります。
(CSVでの保存例)
例えば、以下のような単純な10x10の二次元配列をファイルとして保存するだけでも結構面倒です。
--------------------------------------
1 6 8 2 1 7 4 4 9 7
6 9 7 3 8 9 0 9 8 0
8 2 4 6 2 7 8 4 7 7
7 6 9 1 7 2 3 8 1 9
2 4 6 7 5 5 1 5 6 6
8 9 2 3 6 1 7 8 5 0
0 3 4 5 7 4 2 0 8 6
9 6 5 1 3 8 5 9 3 8
0 9 8 9 5 4 5 8 4 2
1 3 5 3 2 1 7 7 4 2
[torch.DoubleTensor of size 10x10]
--------------------------------------
こんな時は、以前の「パターン認識」でも紹介したcsvigoを使えば簡単です。
(今回はコマンドラインで直接動かしていますが、プログラムとしてファイルに保存しても動作はできます)
基本的にはこれだけです。
require 'csvigo'
a=torch.Tensor(10,10):random(0,9)
b=torch.totable(a)
csvigo.save{data=b,path='test.csv'}
解説すると…
-- csvigoライブラリーを読み込む
require 'csvigo'
-- 10x10の数値(Tensor)を乱数で作る
a=torch.Tensor(10,10):random(0,9)
-- torch.Tensor形式のaを、テーブル形式でbにセットする
b=torch.totable(a)
-- csvigo.saveを使って、ファイルとしてむ保存
-- 「data=」に続いてテーブルの変数、「path='ファイル名'」をつけます
-- カッコ()ではなく、{ }を使ってください。
csvigo.save{data=b,path='test.csv'}
たったこれだけです。
これでCSVファイルとして保存できます。
ただし、ここで注意するポイントがあります。
単純に
1
8
5
6
7
4
1
3
6
8
のようにファイルを出力したい場合、Tensorは
th> a
1
8
5
6
7
4
1
3
6
8
[torch.DoubleTensor of size 10]と単純な10個の配列を元にして、保存用テーブルのデータを作ってcsvigoライブラリで保存しようとしてもエラーになってしまいます。
具体的にはテーブルの変数 b
{
1 : 1
2 : 8
3 : 5
4 : 6
5 : 7
6 : 4
7 : 1
8 : 3
9 : 6
10 : 8
}
*このデータをcsvigoで保存しようするとエラーになります。
このような場合には、
-- まずは10個の配列を 1x10に直して
a=a:repeatTensor(a,1,1)
-- それを10x1に「t()」で行と列を入れ替えた配列に直して
a=a:t()
-- テーブル形式に変換します
a=torch.totable(a)
という感じで、10x1の配列(テンソル)としてデータに直して、それをテーブルに変換する必要があります。(もっとスマートな方法があるかもしれません)
th> a
9
6
9
8
7
9
1
4
1
8
[torch.DoubleTensor of size 10x1]
これが最終的には…
th> b
{
1 :
{
1 : 9
}
2 :
{
1 : 6
}
3 :
{
1 : 9
}
4 :
{
1 : 8
}
5 :
{
1 : 7
}
6 :
{
1 : 9
}
7 :
{
1 : 1
}
8 :
{
1 : 4
}
9 :
{
1 : 1
}
10 :
{
1 : 8
}
}
こんな形になります。
これであれば縦に数字が並んだCSVファイルが作成できます。
th> csvigo.save{path='ttt.csv',data=b}
<csv> writing to file: ttt.csv
<csv> writing done
これでもうCSVファイルでの保存ができるのでいろいろと利用範囲が広がります。
(CSVでの保存例)
torch7のバリエーションcl-torchはおススメできません [人工知能(ディープラーニング)]
torch7のバリエーションには、open-clで処理をGPUに割り当てるものもあります。
cl-torchとか、distro-clとか呼ばれているものですが…オススメできません。
(プログラムのライブラリにclnnやcltorchが含まれているcl-torch動作版のプログラム)
導入してみたのですが…すごく遅いのです。
MacBook Pro (15インチ, 2016)、GPUにRadeon Pro 460を搭載しているモデルでも、下手にOpen-CLなど使わずにCPUで処理した方が早かったほどです。
最初はCPUのパワーを使わずに動いていて、「お、これはいけるか?」と喜んだのですが…
JETSON TK1の5倍もの時間がかかります。(JETSON TK1(CUDA動作)で40秒で終わる処理が、cl-Torchでは200秒以上かかります)
まさかと思って、CPUで動作させると、同じ処理に40秒…しかかかりませんでした。
しかも、別のLINUXのTorch7で作成した人工知能データを読み込ませると、エラーで動作しないというおまけつきです。
発想は良かったのですが…中身が付いてきてませんでした。
CUDAが使えないパソコンの救世主に見えて、実は見掛け倒しという悲しい運命を背負ってしまったようです。
さっそく、cl-Torchは、コマンドラインからさっくり「rm -rf cl-torch」で削除して、通常版のtorch7をインストールしました。
それにしても、2016年のMACBOOK PROはCPU動作でもJETSON TK1のCUDA動作に負けない性能になっていたんですね。
2012年モデルのMAC MINIはTK1にかなり大差で負けてしまうのですが。
(3万円のJETSON TK1が大健闘していると言えるかもしれません)
結論としては、cl-torchはお勧めできません。
なにせ、正式なtorch7で作成した人工知能モデルがエラーで動かないのは致命的です。
cl-torchとか、distro-clとか呼ばれているものですが…オススメできません。
(プログラムのライブラリにclnnやcltorchが含まれているcl-torch動作版のプログラム)
導入してみたのですが…すごく遅いのです。
MacBook Pro (15インチ, 2016)、GPUにRadeon Pro 460を搭載しているモデルでも、下手にOpen-CLなど使わずにCPUで処理した方が早かったほどです。
最初はCPUのパワーを使わずに動いていて、「お、これはいけるか?」と喜んだのですが…
JETSON TK1の5倍もの時間がかかります。(JETSON TK1(CUDA動作)で40秒で終わる処理が、cl-Torchでは200秒以上かかります)
まさかと思って、CPUで動作させると、同じ処理に40秒…しかかかりませんでした。
しかも、別のLINUXのTorch7で作成した人工知能データを読み込ませると、エラーで動作しないというおまけつきです。
発想は良かったのですが…中身が付いてきてませんでした。
CUDAが使えないパソコンの救世主に見えて、実は見掛け倒しという悲しい運命を背負ってしまったようです。
さっそく、cl-Torchは、コマンドラインからさっくり「rm -rf cl-torch」で削除して、通常版のtorch7をインストールしました。
それにしても、2016年のMACBOOK PROはCPU動作でもJETSON TK1のCUDA動作に負けない性能になっていたんですね。
2012年モデルのMAC MINIはTK1にかなり大差で負けてしまうのですが。
(3万円のJETSON TK1が大健闘していると言えるかもしれません)
結論としては、cl-torchはお勧めできません。
なにせ、正式なtorch7で作成した人工知能モデルがエラーで動かないのは致命的です。
torch7で小さくディープラーニング(6)パターン認識 [人工知能(ディープラーニング)]
今回はパターン認識をしてみます。
さらには、これまではプログラムの中で学習用データを作成していましたが、外部のデータを読み込んで処理をします。
まずは、以下のテキストデータをNUMBERS.CSVとしてセーブします。
========================= CSV FILEここの下から
1,1,1,1,0,1,1,0,1,1,0,1,1,1,1,0
0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,1
1,1,1,0,0,1,1,1,1,1,0,0,1,1,1,2
1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,3
1,0,1,1,0,1,1,1,1,0,0,1,0,0,1,4
1,1,1,1,0,0,1,1,1,0,0,1,1,1,1,5
1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,6
1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,7
1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,8
1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,9
========================= CSV FILEここの上まで
実はこのデータ、縦5、横3のピクセルで数字の0〜9を表しています。
例えば数字の9ならば以下のような形です。
===9===
1 1 1
1 0 1
1 1 1
0 0 1
0 0 1
=======
(数字の1が9の形に並んでいます)
これを横一列に並べれば、数字の9は…
1,1,1,1,0,1,1,1,1,0,0,1,0,0,1
というデータになります。
さらに、データを入れたCSVファイルの各行のいちばん右側の数字は、データに対する答えです。
こんな形で0〜9に対応するデータを用意し、それをニューラル・ネットワークで認識できるようにします。
※なお、torch7でCSVファイルを扱えるように、csvigoライブラリーをインストールしておく必要があります。
「luarocks install csvigo」でインストールしてください。
CSVファイルから読み込んだデータは、そのままでは行列データのtorch.Tensorではなく、連想配列といわれるテーブル形式なので、「1」という数字も実際にはテキストの「1」扱いになっているので、それを数値に変えてあげる作業が必要です。
で、パターン認識のプログラムは以下のようになります。
=====================================ここから
require 'nn'
require 'csvigo'
-- ####”luarocks install csvigo” でインストールしておいてください
-- CSVファイルのデータをロード
m=csvigo.load{path='NUMBERS.CSV',mode='large'}
print ("####### DATA LOAD! ######")
dataset={}
function dataset:size() return 10 end
-- 読み込んだデータをtorch.Tensorに直して学習出来るようにする。
for i=1,dataset:size() do
input =torch.Tensor(15)
output=torch.Tensor(10):zero()
input[1]=m[i][1]
input[2]=m[i][2]
input[3]=m[i][3]
input[4]=m[i][4]
input[5]=m[i][5]
input[6]=m[i][6]
input[7]=m[i][7]
input[8]=m[i][8]
input[9]=m[i][9]
input[10]=m[i][10]
input[11]=m[i][11]
input[12]=m[i][12]
input[13]=m[i][13]
input[14]=m[i][14]
input[15]=m[i][15]
a=m[i][16]
-- 10チャンネルの出力のうち、答えになるところを1に設定する
-- Torchの配列などは0スタートではなく、1がスタートになるので注意!
output[a+1]=1
dataset[i]={input,output}
print("###########",m[i][16])
print(input[1],input[2],input[3])
print(input[4],input[5],input[6])
print(input[7],input[8],input[9])
print(input[10],input[11],input[12])
print(input[13],input[14],input[15])
end
print ("####### DATASET! ######")
model=nn.Sequential();
model:add(nn.Linear(15,15))
model:add(nn.Tanh())
model:add(nn.Linear(15,30))
model:add(nn.Tanh())
model:add(nn.Linear(30,10))
-- 今回はソフトマックスも入れてみますが、これがなくともそれぞれの合計が1にならないだけで、順序そのものが入れ替わるなどの変化はありません
model:add(nn.SoftMax())
print ("####### MODEL SET OK! ######")
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=10000
print ("####### TARINER SET! ######")
trainer:train(dataset)
print ("####### LEARNING...DONE! ######")
-- 学習結果の検証
for i=1,dataset:size() do
input =torch.Tensor(15)
input[1]=m[i][1]
input[2]=m[i][2]
input[3]=m[i][3]
input[4]=m[i][4]
input[5]=m[i][5]
input[6]=m[i][6]
input[7]=m[i][7]
input[8]=m[i][8]
input[9]=m[i][9]
input[10]=m[i][10]
input[11]=m[i][11]
input[12]=m[i][12]
input[13]=m[i][13]
input[14]=m[i][14]
input[15]=m[i][15]
a=model:forward(input)
print ("#############CHECK!",i-1)
print (input:reshape(input,5,3),a)
end
=====================================ここまで
これをファイルに保存し、dofile 'ファイル名'で実行をすると…
それぞれのデータ(行列データ)を認識すると、それに応じて、0〜9の位置の数値が1に近い数値が返ってきます。
※今回のプログラムでは、答えになる部分を数字にしたため、9などの数字としてますが、これを
0,0,0,0,0,0,0,0,1というデータ形式に変えるのも面白いと思います。
15個の数字の配置状況から、それに応じた答えを返す人工知能の完成です。
ここまでくると、なんか面白い(不気味?)ですよね。
IF文が全く存在しないのに、期待する答えが出てきてしまいます。
プログラム主体ではなく、データ主体で動作が決まるので、これまでのプログラム作成になれている人には特に違和感を感じるかもしれません。
(ちなみに…このプログラムにも落とし穴があって、データが1つがズレると、正しい答えが得られなくなってしまいます。それを解決する方法はまた次回以降でお話しします)
さらには、これまではプログラムの中で学習用データを作成していましたが、外部のデータを読み込んで処理をします。
まずは、以下のテキストデータをNUMBERS.CSVとしてセーブします。
========================= CSV FILEここの下から
1,1,1,1,0,1,1,0,1,1,0,1,1,1,1,0
0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,1
1,1,1,0,0,1,1,1,1,1,0,0,1,1,1,2
1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,3
1,0,1,1,0,1,1,1,1,0,0,1,0,0,1,4
1,1,1,1,0,0,1,1,1,0,0,1,1,1,1,5
1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,6
1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,7
1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,8
1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,9
========================= CSV FILEここの上まで
実はこのデータ、縦5、横3のピクセルで数字の0〜9を表しています。
例えば数字の9ならば以下のような形です。
===9===
1 1 1
1 0 1
1 1 1
0 0 1
0 0 1
=======
(数字の1が9の形に並んでいます)
これを横一列に並べれば、数字の9は…
1,1,1,1,0,1,1,1,1,0,0,1,0,0,1
というデータになります。
さらに、データを入れたCSVファイルの各行のいちばん右側の数字は、データに対する答えです。
こんな形で0〜9に対応するデータを用意し、それをニューラル・ネットワークで認識できるようにします。
※なお、torch7でCSVファイルを扱えるように、csvigoライブラリーをインストールしておく必要があります。
「luarocks install csvigo」でインストールしてください。
CSVファイルから読み込んだデータは、そのままでは行列データのtorch.Tensorではなく、連想配列といわれるテーブル形式なので、「1」という数字も実際にはテキストの「1」扱いになっているので、それを数値に変えてあげる作業が必要です。
で、パターン認識のプログラムは以下のようになります。
=====================================ここから
require 'nn'
require 'csvigo'
-- ####”luarocks install csvigo” でインストールしておいてください
-- CSVファイルのデータをロード
m=csvigo.load{path='NUMBERS.CSV',mode='large'}
print ("####### DATA LOAD! ######")
dataset={}
function dataset:size() return 10 end
-- 読み込んだデータをtorch.Tensorに直して学習出来るようにする。
for i=1,dataset:size() do
input =torch.Tensor(15)
output=torch.Tensor(10):zero()
input[1]=m[i][1]
input[2]=m[i][2]
input[3]=m[i][3]
input[4]=m[i][4]
input[5]=m[i][5]
input[6]=m[i][6]
input[7]=m[i][7]
input[8]=m[i][8]
input[9]=m[i][9]
input[10]=m[i][10]
input[11]=m[i][11]
input[12]=m[i][12]
input[13]=m[i][13]
input[14]=m[i][14]
input[15]=m[i][15]
a=m[i][16]
-- 10チャンネルの出力のうち、答えになるところを1に設定する
-- Torchの配列などは0スタートではなく、1がスタートになるので注意!
output[a+1]=1
dataset[i]={input,output}
print("###########",m[i][16])
print(input[1],input[2],input[3])
print(input[4],input[5],input[6])
print(input[7],input[8],input[9])
print(input[10],input[11],input[12])
print(input[13],input[14],input[15])
end
print ("####### DATASET! ######")
model=nn.Sequential();
model:add(nn.Linear(15,15))
model:add(nn.Tanh())
model:add(nn.Linear(15,30))
model:add(nn.Tanh())
model:add(nn.Linear(30,10))
-- 今回はソフトマックスも入れてみますが、これがなくともそれぞれの合計が1にならないだけで、順序そのものが入れ替わるなどの変化はありません
model:add(nn.SoftMax())
print ("####### MODEL SET OK! ######")
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=10000
print ("####### TARINER SET! ######")
trainer:train(dataset)
print ("####### LEARNING...DONE! ######")
-- 学習結果の検証
for i=1,dataset:size() do
input =torch.Tensor(15)
input[1]=m[i][1]
input[2]=m[i][2]
input[3]=m[i][3]
input[4]=m[i][4]
input[5]=m[i][5]
input[6]=m[i][6]
input[7]=m[i][7]
input[8]=m[i][8]
input[9]=m[i][9]
input[10]=m[i][10]
input[11]=m[i][11]
input[12]=m[i][12]
input[13]=m[i][13]
input[14]=m[i][14]
input[15]=m[i][15]
a=model:forward(input)
print ("#############CHECK!",i-1)
print (input:reshape(input,5,3),a)
end
=====================================ここまで
これをファイルに保存し、dofile 'ファイル名'で実行をすると…
それぞれのデータ(行列データ)を認識すると、それに応じて、0〜9の位置の数値が1に近い数値が返ってきます。
※今回のプログラムでは、答えになる部分を数字にしたため、9などの数字としてますが、これを
0,0,0,0,0,0,0,0,1というデータ形式に変えるのも面白いと思います。
15個の数字の配置状況から、それに応じた答えを返す人工知能の完成です。
ここまでくると、なんか面白い(不気味?)ですよね。
IF文が全く存在しないのに、期待する答えが出てきてしまいます。
プログラム主体ではなく、データ主体で動作が決まるので、これまでのプログラム作成になれている人には特に違和感を感じるかもしれません。
(ちなみに…このプログラムにも落とし穴があって、データが1つがズレると、正しい答えが得られなくなってしまいます。それを解決する方法はまた次回以降でお話しします)
torch7で白黒画像をカラー化してみる3(過学習) [人工知能(ディープラーニング)]
今、自作しているカラー化人工知能の製作でぶつかっている問題が「過学習」です。
なんとか人工知能モデルも改良し、良い感じになってきたと思ったんですが、そう簡単にはいきませんでした。
「過学習(オーバー・フィッティング)」とは、限られた学習用サンプルに人工知能が最適化されてしまい、学習していないサンプルを与えるとダメダメな結果になることです。
地獄のネコを生み出した人工知能モデルも、学習に使ったサンプルを白黒画像にして、それをカラー化してみると思いのほか良い結果を出してきます。
この画像をカラー化すると…
こんな結果になります。
枯れ木も枝の先が赤く着色されて、まるで花が咲いたみたいで、良い感じです。
(実際には花が咲かない街路樹です)
学習サンプルには花の画像などは含まれていないので、なぜ枝の先が赤くなるのかは謎です。
この何気ない駐輪場の風景などは
びっくりする完成度です!
最初にこの結果を見たときは、間違って白黒にする前のカラー画像(元画像)を見たんじゃないかと疑ったほどです。
それくらい、学習に使った画像はうまくカラー化出来るようになっています。
(聞いたところ、もしこの白黒画像を人間がカラー化しようとすると、物凄く手間がかかり、金額もかなりになるらしいです)
全部うまくいっているわけではなく、例えばこの画像は
空のカラー化が不自然です。
こんな感じで、学習に使ったサンプルは、実際にカラー化させてみると「それなり」、もしくは「予想以上」に良い結果になります。
その一方で、学習していない画像は、地獄のネコのようになったりしますし…
この学習サンプルとして使っていない画像(同じ場所の風景)などは、
わけのわからない色に着色されてしまいます。
人工知能モデルが複雑なのに、サンプル数が200以下と少ない事がどうも原因のようです。
(この場所にはイングレスのポータル奪取が目的で行ったため、そんなに撮影をしてませんでした)
過学習…なんとかしないといけません。
なんとか人工知能モデルも改良し、良い感じになってきたと思ったんですが、そう簡単にはいきませんでした。
「過学習(オーバー・フィッティング)」とは、限られた学習用サンプルに人工知能が最適化されてしまい、学習していないサンプルを与えるとダメダメな結果になることです。
地獄のネコを生み出した人工知能モデルも、学習に使ったサンプルを白黒画像にして、それをカラー化してみると思いのほか良い結果を出してきます。
この画像をカラー化すると…
こんな結果になります。
枯れ木も枝の先が赤く着色されて、まるで花が咲いたみたいで、良い感じです。
(実際には花が咲かない街路樹です)
学習サンプルには花の画像などは含まれていないので、なぜ枝の先が赤くなるのかは謎です。
この何気ない駐輪場の風景などは
びっくりする完成度です!
最初にこの結果を見たときは、間違って白黒にする前のカラー画像(元画像)を見たんじゃないかと疑ったほどです。
それくらい、学習に使った画像はうまくカラー化出来るようになっています。
(聞いたところ、もしこの白黒画像を人間がカラー化しようとすると、物凄く手間がかかり、金額もかなりになるらしいです)
全部うまくいっているわけではなく、例えばこの画像は
空のカラー化が不自然です。
こんな感じで、学習に使ったサンプルは、実際にカラー化させてみると「それなり」、もしくは「予想以上」に良い結果になります。
その一方で、学習していない画像は、地獄のネコのようになったりしますし…
この学習サンプルとして使っていない画像(同じ場所の風景)などは、
わけのわからない色に着色されてしまいます。
人工知能モデルが複雑なのに、サンプル数が200以下と少ない事がどうも原因のようです。
(この場所にはイングレスのポータル奪取が目的で行ったため、そんなに撮影をしてませんでした)
過学習…なんとかしないといけません。
torch7で白黒画像をカラー化してみる2(途中経過報告) [人工知能(ディープラーニング)]
torch7を使って、白黒画像をカラー化出来る人工知能を作っているのは前にもお話ししましたが…
そう簡単にはいきません…
例えばこの、まだサハリンが樺太として日本領だった頃の豊原(ユジノサハリンスク)の写真ですが…
これを自作のカラー化人工知能で処理してみると…
えーと、なんか空が妖しいです…
ビルが緑です。
まだ人工知能モデルの構築がうまくいってません。
で、いろいろと画像処理をしている人に話を聞いたところ、おそらくは人工知能モデルが参考にする情報が足りていないんだろう、との事です。
コンボリューション・レイヤーは工夫しつつ組み込んでいるんですが…
白黒画像のカラー化は、いきなり買い物帰りの人からおつりを見せられて「手元に残ったおつりはこれだけなんだが、何を買ったか当ててみろ」と言われているようなもの、だそうです。
早稲田大学の人工知能モデルでは、全体イメージ、中ぐらいの大きさのイメージ、細部のイメージを補助的な情報としてカラー化するための情報として利用しているのですが…確かに今のところ、私の人工知能は細部のイメージしか利用できていません、
もっと何か、補助的な情報を組み込める事が出来れば精度はあがるはずなんですが…
そんなことを考えながらぼーっとしていたら、人工知能が過学習していまい、変な色合いに着色するようになってしまいました。
元画像はこれですが…
出来たのはこんな画像です。
前回では緑色のネコが、地獄のネコになってしまいました…
そう簡単にはいきません…
例えばこの、まだサハリンが樺太として日本領だった頃の豊原(ユジノサハリンスク)の写真ですが…
これを自作のカラー化人工知能で処理してみると…
えーと、なんか空が妖しいです…
ビルが緑です。
まだ人工知能モデルの構築がうまくいってません。
で、いろいろと画像処理をしている人に話を聞いたところ、おそらくは人工知能モデルが参考にする情報が足りていないんだろう、との事です。
コンボリューション・レイヤーは工夫しつつ組み込んでいるんですが…
白黒画像のカラー化は、いきなり買い物帰りの人からおつりを見せられて「手元に残ったおつりはこれだけなんだが、何を買ったか当ててみろ」と言われているようなもの、だそうです。
早稲田大学の人工知能モデルでは、全体イメージ、中ぐらいの大きさのイメージ、細部のイメージを補助的な情報としてカラー化するための情報として利用しているのですが…確かに今のところ、私の人工知能は細部のイメージしか利用できていません、
もっと何か、補助的な情報を組み込める事が出来れば精度はあがるはずなんですが…
そんなことを考えながらぼーっとしていたら、人工知能が過学習していまい、変な色合いに着色するようになってしまいました。
元画像はこれですが…
出来たのはこんな画像です。
前回では緑色のネコが、地獄のネコになってしまいました…
torch7で小さくディープラーニング(5)活性化関数の注意 [人工知能(ディープラーニング)]
今回は、「活性化関数」についての話です。
説明で使うのは、与えられた5つの数の中から、-1がどこにあるのかを見つける人工知能です。
前回は、入力5、出力5でしたが、今回は入力5、出力1です。
ちなみに、前回のプログラムを変更して学習させてみます。
=================================TYPE NG
require 'nn'
dataset={}
function dataset:size() return 100 end
for i=1,dataset:size() do
input=torch.Tensor(5)
for j=1,5 do
input[j]=torch.random(1,10)
end
x=torch.random(1,5)
input[x]=-1
output=torch.Tensor(1):zero()
output[1]=x
dataset[i]={input,output}
end
-- 人工知能モデルの設定
model=nn.Sequential();
model:add(nn.Linear(5,10))
model:add(nn.Tanh())
model:add(nn.Linear(10,1))
model:add(nn.Tanh())
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=1000
trainer:train(dataset)
-- torch.save('TEST.t7',model) 今回は使いませんのでコメントアウトします
-- ===================TEST
a=torch.Tensor(5)
for i=1,10 do
for j=1,5 do
a[j]=torch.random(1,10)
end
x=torch.random(1,5)
a[x]=-1
b=model:forward(a)
print("INPUT :"..a[1]..","..a[2]..","..a[3]..","..a[4]..","..a[5])
print("ANSWER:"..b[1])
print("------")
end
=================================
結果は、全く学習が進まずに、テスト結果もボロボロです。
何が問題かというと、人工知能モデルの最後(出力の直前)にある活性化関数「Tanh」にあります。
活性化関数というのは、ぶっちゃけて言うと「入力値によってオン・オフされるスイッチ」です。
ただ、プログラムを作る人が任意でオン・オフするようなものではなく、「人工知能モデルが自分自身でオン・オフするためのもの」です。なので、これをいろいろなタイプのものに変更しても、なかなかプログラムを作った本人にはその変化の反応がダイレクトに伝わってこないもどかしさを感じたりもします。
で、このTanhですが、以下のような出力を返します。
なんと、出力値が-1〜1です.
1以上の数値を与えても、1にしかなりません。
出力の直前にこれを入れてしまったために、答えが正しくならないのです。
では、もしこれを取り除いてしまったらどうなるでしょう…
=================================TYPE OK
require 'nn'
dataset={}
function dataset:size() return 100 end
for i=1,dataset:size() do
input=torch.Tensor(5)
for j=1,5 do
input[j]=torch.random(1,10)
end
x=torch.random(1,5)
input[x]=-1
output=torch.Tensor(1):zero()
output[1]=x
dataset[i]={input,output}
end
model=nn.Sequential();
model:add(nn.Linear(5,10))
model:add(nn.Tanh())
model:add(nn.Linear(10,1))
-- model:add(nn.Tanh()) コメントアウトします。
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=1000
trainer:train(dataset)
-- torch.save('TEST.t7',model)
-- ===================TEST
a=torch.Tensor(5)
for i=1,10 do
for j=1,5 do
a[j]=torch.random(1,10)
end
x=torch.random(1,5)
a[x]=-1
b=model:forward(a)
print("INPUT :"..a[1]..","..a[2]..","..a[3]..","..a[4]..","..a[5])
print("ANSWER:"..b[1])
print("------")
end
=================================
これだときちんと、その場所の数値(-1が左から1個目なら1,2個目なら2…)に近い値が返ってきます。
「いくら正しい答えが返ってきても、なんか活性化関数がないのはイヤだな」と考える人がいるかもしれません。
実は、ここには「活性化関数がない」のではなく、「恒等関数(入力値をそのまま出力する)がある」とも考えられる事も出来ます。(なんか、とんち話でごまかされた気がするかもしれませんが…)
また、ここに入力値がプラスの時には、数値をそのまま出力する「ReLU」を使っても問題ありません。
人工知能モデルをこんな感じで変更します。
====================
model=nn.Sequential();
model:add(nn.Linear(5,10))
model:add(nn.Tanh())
model:add(nn.Linear(10,1))
-- model:add(nn.Tanh())
model:add(nn.ReLU())
====================
これでも問題なくこちらの意図するように動いてくれます。
※ただし、もし0以下のマイナスの出力値が欲しい場合に、出力の直前でReLUを組み込んだりすると、0にされてしまうので注意が必要です。
実は、以前は良くWEBで見られるサンプルには「Sigmoid(出力値は0〜1)」が使われていたり、WEBでは「TanhよりもSigmoidが性能が良い」という説明されていたので、意味もわからずに組み込んで、学習が進まなかったり、期待する出力が得られずにがっくりしていたのです。
活性化関数には、実は今回のような落とし穴があったりします。
まずは性能よりも、「出力値がどうなるか」が重要です。
説明で使うのは、与えられた5つの数の中から、-1がどこにあるのかを見つける人工知能です。
前回は、入力5、出力5でしたが、今回は入力5、出力1です。
ちなみに、前回のプログラムを変更して学習させてみます。
=================================TYPE NG
require 'nn'
dataset={}
function dataset:size() return 100 end
for i=1,dataset:size() do
input=torch.Tensor(5)
for j=1,5 do
input[j]=torch.random(1,10)
end
x=torch.random(1,5)
input[x]=-1
output=torch.Tensor(1):zero()
output[1]=x
dataset[i]={input,output}
end
-- 人工知能モデルの設定
model=nn.Sequential();
model:add(nn.Linear(5,10))
model:add(nn.Tanh())
model:add(nn.Linear(10,1))
model:add(nn.Tanh())
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=1000
trainer:train(dataset)
-- torch.save('TEST.t7',model) 今回は使いませんのでコメントアウトします
-- ===================TEST
a=torch.Tensor(5)
for i=1,10 do
for j=1,5 do
a[j]=torch.random(1,10)
end
x=torch.random(1,5)
a[x]=-1
b=model:forward(a)
print("INPUT :"..a[1]..","..a[2]..","..a[3]..","..a[4]..","..a[5])
print("ANSWER:"..b[1])
print("------")
end
=================================
結果は、全く学習が進まずに、テスト結果もボロボロです。
何が問題かというと、人工知能モデルの最後(出力の直前)にある活性化関数「Tanh」にあります。
活性化関数というのは、ぶっちゃけて言うと「入力値によってオン・オフされるスイッチ」です。
ただ、プログラムを作る人が任意でオン・オフするようなものではなく、「人工知能モデルが自分自身でオン・オフするためのもの」です。なので、これをいろいろなタイプのものに変更しても、なかなかプログラムを作った本人にはその変化の反応がダイレクトに伝わってこないもどかしさを感じたりもします。
で、このTanhですが、以下のような出力を返します。
なんと、出力値が-1〜1です.
1以上の数値を与えても、1にしかなりません。
出力の直前にこれを入れてしまったために、答えが正しくならないのです。
では、もしこれを取り除いてしまったらどうなるでしょう…
=================================TYPE OK
require 'nn'
dataset={}
function dataset:size() return 100 end
for i=1,dataset:size() do
input=torch.Tensor(5)
for j=1,5 do
input[j]=torch.random(1,10)
end
x=torch.random(1,5)
input[x]=-1
output=torch.Tensor(1):zero()
output[1]=x
dataset[i]={input,output}
end
model=nn.Sequential();
model:add(nn.Linear(5,10))
model:add(nn.Tanh())
model:add(nn.Linear(10,1))
-- model:add(nn.Tanh()) コメントアウトします。
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=1000
trainer:train(dataset)
-- torch.save('TEST.t7',model)
-- ===================TEST
a=torch.Tensor(5)
for i=1,10 do
for j=1,5 do
a[j]=torch.random(1,10)
end
x=torch.random(1,5)
a[x]=-1
b=model:forward(a)
print("INPUT :"..a[1]..","..a[2]..","..a[3]..","..a[4]..","..a[5])
print("ANSWER:"..b[1])
print("------")
end
=================================
これだときちんと、その場所の数値(-1が左から1個目なら1,2個目なら2…)に近い値が返ってきます。
「いくら正しい答えが返ってきても、なんか活性化関数がないのはイヤだな」と考える人がいるかもしれません。
実は、ここには「活性化関数がない」のではなく、「恒等関数(入力値をそのまま出力する)がある」とも考えられる事も出来ます。(なんか、とんち話でごまかされた気がするかもしれませんが…)
また、ここに入力値がプラスの時には、数値をそのまま出力する「ReLU」を使っても問題ありません。
人工知能モデルをこんな感じで変更します。
====================
model=nn.Sequential();
model:add(nn.Linear(5,10))
model:add(nn.Tanh())
model:add(nn.Linear(10,1))
-- model:add(nn.Tanh())
model:add(nn.ReLU())
====================
これでも問題なくこちらの意図するように動いてくれます。
※ただし、もし0以下のマイナスの出力値が欲しい場合に、出力の直前でReLUを組み込んだりすると、0にされてしまうので注意が必要です。
実は、以前は良くWEBで見られるサンプルには「Sigmoid(出力値は0〜1)」が使われていたり、WEBでは「TanhよりもSigmoidが性能が良い」という説明されていたので、意味もわからずに組み込んで、学習が進まなかったり、期待する出力が得られずにがっくりしていたのです。
活性化関数には、実は今回のような落とし穴があったりします。
まずは性能よりも、「出力値がどうなるか」が重要です。