とらりもんHOME  Index  Search  Changes  Login

機械学習入門: 自前データでニューラルネット

テキストのニューラルネットのプログラム(ch05/train_neuralnet.py)に, 生物資源学類生の作った手書き文字データを入れて走らせてみよう。そのためには, まず, 資源生データを, ch05/train_neuralnet.pyの入力データ(訓練データ・テストデータ)とできるだけ同じ形に加工しよう(テキストP72~74も参考に!)。

入力データ形式の調査

課題1-01: ch05/train_neuralnet.pyは, 入力データ(訓練データ・テストデータ)をどのような名前のオブジェクト(インスタンス)に入れているか?

課題1-02: それらのオブジェクト(インスタンス)は, どの行でどのようなコマンドで読み込んでいるか?

課題1-03: そのコマンドのソースコードを探しだせ。それを含むファイルのパスと, そのファイルの中の何行目にあるかを述べよ。

課題1-04: そのソースコードを読み, 入力データはどのようなファイルに入っているかを調べ, そのファイルを実際にlsコマンド(Linux)で探し出せ。

以後, datasetディレクトリに入ってpythonシェルを立ち上げて, その中で作業せよ。

課題1-05: pythonシェルで, そのファイルをdatasetという名前で読み込め。読み込み方は, 君が既に調べた上記のソースコードに書いてあるだろう。

課題1-06: そのdatasetというオブジェクトは, どのようなクラスのインスタンスか, 調べよ。ヒント: helpコマンド

課題1-07: そのdatasetというオブジェクトは, どのような構造をしているか, 調べよ。

課題1-08: そのdatasetというオブジェクトには, 訓練データ, そのラベル, テストデータ, そのラベルがそれぞれどのように入っているか, 調べよ。

課題1-09: そのdatasetというオブジェクトの訓練データの最初の手書き数字を数値として表示してみよ。

課題1-10: そのdatasetというオブジェクトの訓練データの最初の手書き数字を画像として表示してみよ。

課題1-11: それが訓練データの最初のラベルに正しく対応しているかどうか調べよ。

課題1-12: そのdatasetというオブジェクトのテストデータの最初の手書き数字を画像として表示してみよ。

課題1-13: それがテストデータの最初のラベルに正しく対応しているかどうか調べよ。

その結果, 入力データは, 

  • 'test_img', 'test_label', 'train_img', 'train_label'という4つのキーを持つディクショナリクラスのインスタンスが,
  • mnist.pklというファイル名でファイルになったものであり,
  • 'test_label', 'train_label'は0~9の手書き数字にそれぞれ0~9のラベルがつけられたもの(one_hot表現ではない)であり,
  • 'test_img', 'train_img'は手書き数字の画像群を表現した(データ数 x 784)のnumpy.ndarrayクラスのインスタンスであり,
  • 個々の手書き数字の画像は784(=28 x 28)個の数値で表され, その数値は0以上255以下の整数型(uint8)である。

ということがわかった。資源生データもそのようにしよう。

オリジナルデータの読み込み・加工

いったんLinuxシェルに戻って, 以下を行え。

課題2-01: 資源生の手書き文字データ(shigen_tegaki.tar.gz)を, manabaからダウンロードし, datasetディレクトリに入れよ。不具合データを取り除いた改良版。

課題2-02: それを解凍せよ(datasetディレクトリに行ってから, tar zxvf shigen_tegaki.tar.gz)。

課題2-03: 資源生の手書き数字を切り出し, 28x28サイズのjpegファイルに変換せよ。cd shigen_tegaki; ./README.shでOK。resize/ディレクトリに5800枚のJPEG画像ができているはず。

では, 再びpythonシェルに戻って, 以下をやってみよう。pythonシェルはdatasetディレクトリから起動すること。

課題2-04: 資源生データ(手書き数字ファイル)から任意の1つを選んで, それらのファイル名(パス)をjpgfilenameという文字列型変数に格納せよ。もしもshigen_tegaki/resize/2_4hn1-08.jpgを選んだなら,

In[]: jpgfilename='shigen_tegaki/resize/2_4hn1-08.jpg'

課題2-05: そのファイルをimという名前のオブジェクトとして読み込んで, 表示してみよ。

from PIL import Image
import matplotlib.pyplot as plt
im=Image.open(jpgfilename)
plt.imshow(im)
plt.show()

課題2-06: それを, 784個の要素からなる数ベクトル(np.ndarrayクラスのインスタンス)に変換せよ。

import numpy as np
im=np.array(im).flatten
print(im)

課題2-07: そのラベルは, ファイル名の先頭文字で紐付けられている。それを数値として取り出せ:

import os
label=int(os.path.basename(jpgfilename)[0])
print(label)

課題2-08: 同様のことを, 3つのファイルについてやってみよう。資源生データ(手書き数字ファイル)から任意の3つを選んで, それらのファイル名(パス)をjpgfilename_listというリストにせよ。ヒント: jpgfilename_list=['shigen_tegaki/resize/2_4hn1-08.jpg', 'shigen_tegaki/resize/4_1hn1-29.jpg', 'shigen_tegaki/resize/7_0hn2-30.jpg']みたいにするだけ。

課題2-09: for ループをjpgfilename_listに使って, その3つのファイルを順次読み込んで, 画像を(3 x 784)のimagesというnumpy.ndarrayインスタンスに, ラベルをlabelsというnumpy.ndarrayインスタンスに格納せよ(うまくできたか確認せよ)。

images=[]
labels=[]
for jpgfilename in jpgfilename_list:
   print(jpgfilename)
   im=np.array(Image.open(jpgfilename)).flatten()
   lb=int(os.path.basename(jpgfilename)[0])
   images += [im]
   labels += [lb]
images = np.array(images)
labels = np.array(labels)

課題2-10: それを参考にして, 資源生手書き数字を全て読み込み, 画像を(5800, 784)のimagesというnumpy.ndarrayインスタンスに, ラベルを要素数784のlabelsというnumpy.ndarrayインスタンスに格納するpythonプログラムを, "shigen_tegaki_create.py"という名前で作れ(うまくできたか確認せよ)。ヒント: glob.glob

課題2-11: "shigen_tegaki_create.py"を改良し, 以下ができるようにせよ:

  • 全データの1/6をテストデータに, 5/6を訓練データになるように分割する。
  • その分割は, 乱数を使って行う。ヒント: np.random.choice(, , replace=False)
  • 上で調べた, ch05/train_neuralnet.pyの入力データと同じ構造にする(ディクショナリを使って)。
  • それを, "shigen_tegaki.pkl"というファイルに書き出す。ヒント: pickle.dumpコマンド。
# 例
import os
import numpy as np
import sys
import pickle
import glob
from PIL import Image
picklefilename="shigen_tegaki.pkl"

jpgfilename_list=glob.glob("shigen_tegaki/resize/?_*.jpg")
jpgfilename_list.sort()

images=[]
labels=[]
for jpgfilename in jpgfilename_list:
   print(jpgfilename)
   im=np.array(Image.open(jpgfilename)).flatten()
   lb=int(os.path.basename(jpgfilename)[0])
   images += [im]
   labels += [lb]
images = np.array(images)
labels = np.array(labels)
numdata=labels.shape[0]
numtest=int(numdata/6)
index_test=np.random.choice(numdata, numtest, replace=False)
index_test.sort()
test_img=images[index_test]
test_label=labels[index_test]
labels[index_test]=-1
train_img=images[labels>-1]
train_label=labels[labels>-1]
dataset = {'train_img':train_img, 'train_label':train_label, 'test_img':test_img, 'test_label':test_label}
with open(picklefilename, 'wb') as f:
   pickle.dump(dataset, f, -1)

ニューラルネットのソースコード変更と実行

課題3-01: dataset/mnist.pyを改造して, その中にload_shigen()という関数を作れ。その関数は, load_mnist()とほぼ同じ動作をするが, 読み込むのは先ほど作ったshigen_tegaki.pklである。

例: def load_mnist(...)のあたりをコピーして, 関数名を変えて以下のようにする:

def load_shigen(normalize=True, flatten=True, one_hot_label=False):
   dataset_dir = os.path.dirname(os.path.abspath(__file__))
   shigen_file = dataset_dir + "/shigen_tegaki.pkl"
(以下略)

課題3-02: ch05/train_neuralnet.pyを改造して, mnist.pklでなくshigen_tegaki.pklによってニューラルネットを訓練できるようにせよ。ヒント: まず, ch05/train_neuralnet.pyの冒頭部で, load_shigen()関数を使えるようにしておく。次に, 「データの読み込み」の部分を変更して, load_mnistのかわりにload_shigenを使う。また, 現状ではiter_per_epochが整数値にならない場合があるので, 整数値になるようにキャストする。

課題3-03: 結果はどうか? いろいろうまくいかないだろう。最初は精度もぜんぜん上がらないだろう(10~20 %くらい)。それらをひとつずつ潰して, うまく走るようにしてみよ。最終的に精度は90%程度になるはず。ヒント: 資源生の画像をMNISTの画像と比べてみよ。様子がだいぶ違うだろう。それをMNISTの画像に寄せていくのだ。いろんなやり方があるが, dataset/mnist.pyのload_shigen()関数の中で, normalizeのあたりで処理をするというのもひとつの手だろう。ちなみに, dataset/mnist.pyの変更を反映してch05/train_neuralnet.pyを走らせるには, いちどpythonシェルを抜けてから入り直す。

課題3-04: shigen_tegaki.pklで訓練したニューラルネットを, mnist.pklのテストデータで評価してみよ。

課題3-05: mnist.pklで訓練したニューラルネットを, shigen_tegaki.pklのテストデータで評価してみよ。

訓練データ・テストデータのサンプリングが精度に及ぼす影響

課題4-01: 上の課題3-04, 課題3-05で, 精度が上がらなかった理由は何か? 精度が10 %くらいということは, ぜんぜん当たっていないということ(10個の数字の判別なので、でたらめな予想でも1/10=10%の精度は出る)。これはひどすぎるだろう。何かがおかしいはずだ。ヒント: shigen_tegakiのデータと, mnistのデータをもういちどひとつずつ画像に表示して, 様子を比べてみよ。

課題4-02: その理由をもとに, 課題3-04で精度がせめて30%を超えるように工夫せよ。

ところで, 課題3-03では, shigen_tegakiのデータは, 資源生116人が書いた5800個の数字で訓練およびテストを行った。その際, 訓練データとテストデータは重複しないように気をつけた。しかし, それでも同一人物が書いた文字が, 訓練データとテストデータにまたがって存在しているだろう。これでは真の意味での「テスト」になっていない。

課題4-03: 訓練データを書いた人と, テストデータを書いた人が重ならないように, 訓練データとテストデータを作りなおせ。その際, 訓練データとテストデータのバランスが概ね5:1になるように工夫せよ。

課題4-04: そのデータを用いて, ニューラルネットを訓練し, 精度を評価してみよ。課題3-03の結果と比べて, 考察せよ。

畳み込みニューラルネットワーク

課題5-01: shigen_tegakiのデータを使って, 7章の畳み込みニューラルネットワークを訓練し, 精度を評価せよ。(ch07/train_convnet.pyをいじればよい)。

ライブラリを使う

課題6-01: scikit-learnライブラリを使って, shigen_tegakiの手書き数字データでサポートベクトルマシーンを訓練し、精度を評価せよ。ヒント: 「RaspberryPiではじめる機械学習」第6章。ポイントは,

  • ml-06-06-svc-traintest.pyをベースに書き換える。
  • from dataset.mnist import load_shigenを入れて, load_shigen()を使えるようにする。
  • その際, パスに注意。必要に応じて, import sys, os; sys.path.append(os.pardir)などを入れよう(意味を理解してね!!)
  • digits=からclf=の直前までの行は不要。そのかわりに, (X_train, y_train), (X_test, y_test) = load_shigen(normalize=True, one_hot_label=False) を入れればよい。これまでxだったのが大文字Xになり, tだったのがyになったことに注意。one_hot_label=Falseにすることも大事。このへんはもとのコードを走らせながら自分で気づくべし。
  • だいたい89 %くらいの精度になるでしょう!

課題6-02: scikit-learnライブラリを使って, shigen_tegakiの手書き数字データでニューラルネットワークを訓練し、精度を評価せよ。ヒント: 「RaspberryPiではじめる機械学習」第6章。ポイントは,

  • ml-06-07-nn-traintest.pyを参考にする。
  • 課題6-01で作ったプログラムをベースに書き換える。
  • といっても, すべきことは分類アルゴリズムの変更だけ。以下の行を適当にどこかの行と入れ替える(自分で考えよう): clf = MLPClassifier(hidden_layer_sizes=(100, ), max_iter=1000, tol=0.0001, random_state=None)
  • ただし, それだけではNameError: name 'MLPClassifier' is not definedみたいなエラーが出るかも。何がおかしいか, 自分で考えよう!!
  • だいたい76 %くらいの精度になるでしょう!

課題6-03(オプション): scikit-learnライブラリを使って, shigen_tegakiの手書き数字データでディープラーニングを訓練し、精度を評価せよ。ヒント: 「RaspberryPiではじめる機械学習」第10章。この課題は, 学情端末ではうまくいかないだろう。ライブラリが足りないからである。自分のパソコン等を使ってがんばろう!!

うまくいかずに気持ちが切れそうになったら...

  • うまくいくところまで戻って, そこからすこしずつ変えていく。
  • exit文を挿入して, エラーが出ている箇所を絞り込む。
  • print文を挿入して, 中間データを表示させ, それっぽい値になっているかをチェックする。
  • プログラミングはそういうものだと割りきって, 粘り強く取り組む。
  • pythonの教科書をちゃんと読む。
  • いろいろやってみる。

期末課題

以下の6種類の樹種の葉を分類する分類器を作れ:

  1. ヒノキ
  2. クスノキ
  3. ヤマボウシ
  4. ヤマモモ
  5. イチョウ
  6. イロハカエデ

注意:

  • 入力画像は, 100ピクセル x 100ピクセルのjpeg形式のカラー画像。
  • できたら奈佐原に報告すること。
  • 奈佐原が用意するテストデータを分類し, その精度(パーセント)を得点とする。
  • 奈佐原に報告した後, レポートを書いてmanabaで提出すること(各自)。
  • レポートには, 以下は必ず書くこと。
  1. メンバー(相棒)の氏名
  2. アルゴリズムの概要
  3. トレーニングデータの作成法
  4. 結果(精度)
  5. 工夫したこと・大変だったこと。
  6. 感想
Last modified:2019/06/24 07:22:49
Keyword(s):
References:[機械学習入門]