Chainerの練習として、簡単な線形分離問題にチャレンジしてみた。
環境
- OSX 10.11
- python 2.7.11
- chainer 1.7.2
タスク
身長(cm)、体重(kg)、胸囲(cm)を入力として、肥満状態かどうかを判別する関数を学習したい。ただし、ここで肥満状態とはBMI(体重を身長の2乗で除算した数)が25以上であると定義する。したがって、肥満状態か否かを判定するには体重と身長の情報があれば十分であり、胸囲の情報は不要である。では、今回作る学習器は、はたして胸囲の情報に惑わされることなく、身長と体重だけに注目して肥満状態か否かを見極めることができるようになるのだろうか。
データ
エクセルでダミーデータを作った。スペース区切りで身長、体重、胸囲、肥満フラグを1行に並べている。身長、体重、胸囲はそれぞれ、男性の平均値に適当な分散をもつ正規乱数を加えて生成した。肥満フラグは、身長と体重から算出されるBMIが25以上であれば1を立てた。これを独立に1000個作り、うち900個を学習用、100個を評価用として分けた。
身長 体重 胸囲 肥満フラグ
152.5110992 70.64096855 76.24909648 1
176.5483602 72.54812988 79.99468908 0
171.9815877 78.13768514 80.87788608 1
180.013773 77.60660479 79.71464192 0
171.9685041 81.20240554 84.93720091 1
186.3999693 77.03393024 82.25099179 0
175.1117213 81.23388203 86.89111757 1
ご覧の通りほぼ線形だ。
学習器
Chainerの練習がてら多層パーセプトロンを組んでみた。入力3次元、隠れ素子4次元、出力2次元の3層構造にした。(線形分離タスクなので単層パーセプトロンでも可能。)その他の設定は以下のようにした。
- 活性化関数: ReLu
- 最適化アルゴリズム: Adam
- 誤差関数: ソフトマックス・クロスエントロピー
- Dropout率: 0.5
- ミニバッチ数: 5
- 反復数(epoch数): 100
class MLP(Chain):
def __init__(self):
super(MLP, self).__init__(
# 3-4-2次元のネットワーク
l1=L.Linear(3, 4),
l2=L.Linear(4, 2),
)
def forward(self, x, t, train):
h1 = F.dropout(F.relu(self.l1(x)), train=train)
y = self.l2(h1)
return F.softmax_cross_entropy(y, t), F.accuracy(y, t)
# インスタンス化
model = MLP()
# 最適化アルゴリズムにはAdamを採用
optimizer = optimizers.Adam()
optimizer.setup(model)
N = 900 # 学習データ数
N_test = 100 # 評価データ数
n_epoch = 100 # 反復数
batchsize = 5 # ミニバッチ
#以下省略
結果
- 左青: 学習データでの誤差関数
- 左緑: 学習データでの正解率
- 右青: 評価データでの誤差関数
- 右緑: 評価データでの正解率
8割弱の性能だった。微妙?
また、学習後に評価データに対してどのような出力がなされるのかを見てみた。
身長 体重 胸囲 システムが推定した肥満フラグ 正解の肥満フラグ
[ 179.30055237 69.73477936 84.73832703] 0 0
[ 176.89619446 84.05502319 85.10128021] 1 1
[ 172.04129028 77.36618805 87.89541626] 1 1
[ 168.48660278 73.91072845 84.5171814 ] 1 1
[ 166.53656006 71.42696381 83.17546844] 0 1
[ 163.44270325 77.11021423 90.57539368] 1 1
[ 180.63993835 77.33372498 85.33548737] 0 0
[ 165.73175049 71.87976837 80.57328033] 0 1
本来は肥満である下から2番目と4番目を正常と判断してしまっていた。いずれも体重だけ見ると低めではある。身長との関係を捉えきれていないのか。
詰まりどころ
-
overflow encountered in subtract
softmax_cross_entropyを使うところでoverflow encountered in subtract
とかいうエラーが出て、誤差関数がnanになることが多かった。このエラーは、cross-entropy誤差関数を計算するときにlogに0を入力してしまっていることが原因で起きるらしい。実は最初、活性化関数に線形関数を採用していたのだが、それがいけなかったもよう。 -
局所解に陥る
局所解に陥り学習が進まなくなることが何度かあった。ミニバッチ数を小さくしたり、重みの初期値を振りなおして何度も試行し、うまく学習されるまでやり直した。
今後
このタスクで性能8割弱は低い。学習率、ミニバッチサイズ、ドロップアウト率、データの正規化など、いろいろ調整して感覚をつかんでいきたい。