いろいろあって、不慣れながらTensorFlowを使ってみたのですがいまいちわかってなくていろいろはまったので、記録に残しておこうと思います。
画像認識タスク
チュートリアルには手書き数字のデータセットThe MNIST Dataを機械学習で認識できるモデルを作る、というものがあります。画像認識の世界では非常にポピュラーなものだそうです。
これ自体はチュートリアルの通りに実行すればごく普通に動きます。ナイーブな実装でも90%程度の認識率のモデルができます。これを、任意の別の画像に応用させようと考えました。対象はカラー画像で、単純に1種類の物体の区別をさせようとしました。
TensorFlowには画像を読み込む機能が用意されています。tf.image.decode_jpegでjpeg形式を読み込んで、横x縦x3(RGB)のint32型3次元行列を返します。さらに、これをリサイズする関数tf.image.resize_imagesもあります。指定した縦横のサイズにリサイズされ、値もfloat型に正規化されます。
ではこれを単純にロジスティック回帰にいれようと、tf.matmulで行列の積を求めようとしたらtf.matmulでエラーになりました。コードは以下のような感じです。
import tensorflow as tf mat_size = 32 # 32x32ピクセル mat_ch = 3 # RGB input_x = tf.placeholder("float", [None, mat_size, mat_size, mat_ch]) weight = tf.Variable(tf.zeros([mat_size, mat_size, mat_ch])) bias = tf.Variable(tf.zeros([1]) >y = tf.nn.softmax(tf.matmul(input_x, weight) + bias)
いろいろ試した挙句、この関数は入力が2次元行列以外を受け付けないことがわかりました。よくよく見るとドキュメントにもそう書かれていました。
他の人はどうしているか探してみると、画像をOpenCVで読み込ませてなおかつ単純に平坦化(flatten)した1次元行列として扱っていました。結局自分もそれに習って実装しなおしました。その結果、動くコードは以下のように修正されました。
import tensorflow as tf mat_size = 32 # 32x32ピクセル mat_ch = 3 # RGB input_x = tf.placeholder("float", [None, mat_size * mat_size * mat_ch]) weight = tf.Variable(tf.zeros([mat_size * mat_size * mat_ch])) bias = tf.Variable(tf.zeros([1]) y = tf.nn.softmax(tf.matmul(input_x, weight) + bias)
扱うデータが1次元なのにinput_xが2次元なのは、input_xが受け付けるデータは訓練データの配列だからです。ミニバッチ処理のためにそのような作りになっているようです。訓練データが1つだけの配列にすればもちろん1つのデータに対しても動きます。
一応、tf.reshape(vec, [-1])とすれば平坦化できるようなのですが、今のところ確かめていません。
セッションと値の評価
とりあえず自分で用意したデータを使って訓練をさせてみたのですが、思うような結果が得られません。というか全然学習している気配がありません。
結論から言えば、単純に訓練データが圧倒的に不足していたようなのですが、そもそも学習できていないということを把握するのにちょっと苦労しました。
個々の重みはweightに入っているはずなのですが、この値の中をどうやって参照するかではまりました。単純にprint(weight)などとしても__str__によって変換された結果(オブジェクトのクラス名等)しか出てきません。
正しく値を取得するには、TensorFlowのセッション上での値の評価が必要です。
sess = tf.Session() print(sess.run(weight)) # もしくは # sess.as_default() # print(weight.eval())
この結果、すべての値が0.0のままだったので「これ、学習できてない!」とようやく理解できました。TensorFlowとの格闘はまだまだつづきそうです。