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が性能が良い」という説明されていたので、意味もわからずに組み込んで、学習が進まなかったり、期待する出力が得られずにがっくりしていたのです。
活性化関数には、実は今回のような落とし穴があったりします。
まずは性能よりも、「出力値がどうなるか」が重要です。
コンブ入りチョコレート [ロシア文化・日本文化]
少し前になりますが、チョコレートをいただきました。
「с морской капустой тёмный шоколад」とありますが…
「昆布入りチョコレート」です。
さすがに、粉末にされているのか、ペーストにされたのか、食べてみても食感としては「昆布」は判りませんでした。
味の方は、「あれ? 昆布っぽいかな?」という後味が微かにするぐらいで、何も言われないと気がつかないかもしれません。
日本のちょっと失敗した地元産品のお土産のような感じではなく、上品な味にまとめられています。
このチョコレート、他にも「с морской солью」塩入り(シーソルト入り)もあります(右から3番目)。
どこかで見かけたときには、お土産に良いかもしれません!
で、なんというか、ロシアにあるお菓子が、探してみると、日本のご当地お土産のノリに近いものもあって、結構好きです。
ポテトチップス「行者ニンニク味(ネギ味)」とか…
ポテトチップス「イクラ味」とか…
ポテトチップス「カニ味」とか…
で、それがちゃんと一定のレベルを超えているのが面白いです。
torch7で小さくディープラーニング(4)複数データからある数値をピックアップ [人工知能(ディープラーニング)]
今回は、5つの数のうちから-1になっているものを見つけるようにします。
==================================ここから
require 'nn'
dataset={}
function dataset:size() return 200 end
-- データセットを200個作ります
for i=1,dataset:size() do
input=torch.Tensor(5)
input[1]=torch.random(1,10)
input[2]=torch.random(1,10)
input[3]=torch.random(1,10)
input[4]=torch.random(1,10)
input[5]=torch.random(1,10)
-- 乱数でどこかを-1にして、同じ位置の出力は1にします。他は0
x=torch.random(1,5)
input[x]=-1
output=torch.Tensor(5):zero()
output[x]=1
dataset[i]={input,output}
end
-- 人工知能モデルの設定
model=nn.Sequential();
model:add(nn.Linear(5,6))
model:add(nn.Tanh())
model:add(nn.Linear(6,5))
model:add(nn.Tanh())
-- 学習用パラメーターの設定
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=100
trainer:train(dataset)
-- 学習済みデータをセーブしておきます。(今回はなくてもかまいません)
torch.save('TEST.t7',model)
-- 学習結果の確認
a=torch.Tensor(5)
for i=1,10 do
a[1]=torch.random(1,10)
a[2]=torch.random(1,10)
a[3]=torch.random(1,10)
a[4]=torch.random(1,10)
a[5]=torch.random(1,10)
x=torch.random(1,5)
a[x]=-1
b=model:forward(a)
-- 見やすいように、出力結局を十倍にして小数点以下きりすてます
b=torch.round(b*10)
print("INPUT :"..a[1]..","..a[2]..","..a[3]..","..a[4]..","..a[5])
print("ANSWER:"..b[1]..","..b[2]..","..b[3]..","..b[4]..","..b[5])
print("------")
end
==============================================
これで実行すると、ちゃんと-1の値と同じ位置の出力結局が9(限りなく10に近いのですが、小数点以下切り捨てのため9になってます)
ちなみに、今回は5つの入力と同じ、出力も5つにしました。
これがもっとも簡単なパターンです。
もしこれを、出力のデータを1個にして、-1が1番目だったら1、5番目だったら5、というように出力させようとすると…かなり難しくなります。
(人工知能関係を解説しているサイトや、書籍でも説明が面倒なのか、当たり前すぎる事なのか、意外と説明はありません)
それを説明しますので、次回はちょっとだけ難しくなります…
==================================ここから
require 'nn'
dataset={}
function dataset:size() return 200 end
-- データセットを200個作ります
for i=1,dataset:size() do
input=torch.Tensor(5)
input[1]=torch.random(1,10)
input[2]=torch.random(1,10)
input[3]=torch.random(1,10)
input[4]=torch.random(1,10)
input[5]=torch.random(1,10)
-- 乱数でどこかを-1にして、同じ位置の出力は1にします。他は0
x=torch.random(1,5)
input[x]=-1
output=torch.Tensor(5):zero()
output[x]=1
dataset[i]={input,output}
end
-- 人工知能モデルの設定
model=nn.Sequential();
model:add(nn.Linear(5,6))
model:add(nn.Tanh())
model:add(nn.Linear(6,5))
model:add(nn.Tanh())
-- 学習用パラメーターの設定
criterion = nn.MSECriterion()
trainer=nn.StochasticGradient(model,criterion)
trainer.learningRate=0.01
trainer.maxIteration=100
trainer:train(dataset)
-- 学習済みデータをセーブしておきます。(今回はなくてもかまいません)
torch.save('TEST.t7',model)
-- 学習結果の確認
a=torch.Tensor(5)
for i=1,10 do
a[1]=torch.random(1,10)
a[2]=torch.random(1,10)
a[3]=torch.random(1,10)
a[4]=torch.random(1,10)
a[5]=torch.random(1,10)
x=torch.random(1,5)
a[x]=-1
b=model:forward(a)
-- 見やすいように、出力結局を十倍にして小数点以下きりすてます
b=torch.round(b*10)
print("INPUT :"..a[1]..","..a[2]..","..a[3]..","..a[4]..","..a[5])
print("ANSWER:"..b[1]..","..b[2]..","..b[3]..","..b[4]..","..b[5])
print("------")
end
==============================================
これで実行すると、ちゃんと-1の値と同じ位置の出力結局が9(限りなく10に近いのですが、小数点以下切り捨てのため9になってます)
ちなみに、今回は5つの入力と同じ、出力も5つにしました。
これがもっとも簡単なパターンです。
もしこれを、出力のデータを1個にして、-1が1番目だったら1、5番目だったら5、というように出力させようとすると…かなり難しくなります。
(人工知能関係を解説しているサイトや、書籍でも説明が面倒なのか、当たり前すぎる事なのか、意外と説明はありません)
それを説明しますので、次回はちょっとだけ難しくなります…