hellkite 日記と雑記とメモ。

Shiki Kazamaの駄文と音楽と、時々技術な感じ

ChainerをGPUで動かそうとしてハマった話(Windows環境)

CPU上でChainerを動かしたのでそろそろGPUで動かそうとしたところ、環境構築で思いっきりハマりました。まぁ、結論からいうと、

  • Python 3.5の場合は、VisualStudio 2015を入れましょう
  • Pythonは、64bitを使いましょう
  • 各ライブラリのバージョンはちゃんと合わせましょう

この3点に尽きます。

基本的には、このサイトを参考にしました。
qiita.com


ここで書いてあることと違ったのは、VisualStudioのバージョン。
上記のサイトでは、VisualStudio 2013となっていましたが、Python 3.5の場合は2015が必要でした。

また、Pythonは32bit版を入れていたのですが、32bitだとビルドが通りません。Pythonは64bitが必要です。仕方がないので32bit Pythonを消して新たにインストールしなおしました。

CUDAは、7.5のダウンロードページが分かりにくかったので、8.0を入れてしまいました・・・。現在ChainerがサポートしているCUDAのバージョンは、7.5です。8.0を入れた後に気が付いて、7.5を後追いで入れました。

http://docs.chainer.org/en/stable/install.html#install-chainer

始めに公式サイトをちゃんと確認すれば、すんなり入ったのかもしれません。適当に調べてやりだしてハマるパターン・・・。環境構築ってハマると抜けるのに時間がかかりますよね。半日かかりましたが、ちゃんと動いているようです。

ただ、今回入れたのはモバイル版のGPUなので、サンプルでは5~7倍の速度アップにとどまりました。まぁ、丸一日かかっていた学習が1時間足らずで終わると思えば、いい線ですかね。

MacにPythonとChainerの環境を構築した

まっさらなMacにChainerの環境を入れる必要があったので、その時のメモ*1。でも、Macの場合はWindowsのような落とし穴はなかった。

Homebrewを使ってPythonOpenCVを入れる

こちらを参考に、Homebrewを使ってPythonOpenCVを入れる。
blog.ymyzk.com


最近は仮想環境を入れるのが主流のようだが、Chainer用と割り切って、そのまま利用することにした。後で困るかもしれない。。まぁ、その時はその時考えればいい。

OpenCVでハマりそうなのは、インストール後、以下の一行が必要なところか。

brew link opencv3 --force

Chainerのインストール

Chainerは単純にpipで入れるだけ。Macの場合、一部のモデルを除きGPU*2が利用できないから失敗することはほぼない。
機械学習に必要な各種ライブラリをWindowsのときと同様にpipで入れておしまい。

pip install chainer
pip install pandas
pip install matplotlib
pip install scikit-learn

などなど必要なものをいれるだけ。楽。

ただ、Xcodeすら入っていない場合は、Xcodeコマンドラインツールのインストールが必要。これが結構時間かかった。

*1:メモにすらなっていない気がするが。

*2:というかCuda

Chainerで画像カテゴリ分類(学習したモデルで任意画像を分類する)

モデルの保存処理等に問題があったため、修正しました。2016/10/25

前回CIFAR-10で学習したモデルデータを使って、任意の画像ファイルをカテゴリ分類してみます。ネットでよく見かけるのは学習して終わり、というも内容ですが、ここでは学習後のモデルデータを使った分類の方法です。
ひとまず、CIFAR-10のテストデータを使ってカテゴリ分類する方法を整理します。事前に必要なライブラリはインポートしておいてください。基本的に前回と同様にインポートすれば問題と思います。

前回記事はこちらです。
hellkite.hatenablog.com

モデルデータのロード

Chainerの学習済みモデルをロードします。pickleを使います。pickleは事前に定義が必要なので、以下のようにロードします。

# 保存したモデル定義
class Model(Chain):
    def __init__(self):
        super(Model, self).__init__(
            conv1=F.Convolution2D(3, 32, 3, pad=1),
            conv2=F.Convolution2D(32, 32, 3, pad=1),
            conv3=F.Convolution2D(32, 32, 3, pad=1),
            conv4=F.Convolution2D(32, 32, 3, pad=1),
            conv5=F.Convolution2D(32, 32, 3, pad=1),
            conv6=F.Convolution2D(32, 32, 3, pad=1),
            l1=F.Linear(512, 512),
            l2=F.Linear(512, 10)
        )
    def __call__(self, x):
        h1 = F.relu(self.conv1(x))
        h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), 2)
        h3 = F.relu(self.conv3(h2))
        h4 = F.max_pooling_2d(F.relu(self.conv4(h3)), 2)
        h5 = F.relu(self.conv5(h4))
        h6 = F.max_pooling_2d(F.relu(self.conv6(h5)), 2)
        h7 = F.dropout(F.relu(self.l1(h6)),  train=False)
        y = self.l2(h7)
        return y

model = L.Classifier(Model())
optimizer = optimizers.Adam()
optimizer.setup(model)

# 保存したモデルを読み込み(2016/10/26更新!)
serializers.load_npz('model_cifar10.model', model)
serializers.load_npz('model_cifar10.state', optimizer)

pickleの使い方が良くわらからず、ハマりました・・・。ちゃんと調べればすぐ情報があるのに・・・。
serializersを使うとCPU/GPUを意識せずにセーブ/ロードできるそうです。便利。

CIFAR-10のテストデータを準備

モデルに入力するためのテストデータを準備します。画像ファイルを入れる前にCIFAR-10のテストデータを入れてみます。前回のコードを流用します。

import sys

def unpickle(file):
    fp = open(file, 'rb')
    data = pickle.load(fp, encoding='latin-1')
    fp.close()
    
    return data

# テスト画像を準備
test_data_dic = unpickle(os.getcwd()+"\\cifar-10\\test_batch")
X_test = test_data_dic['data']
X_test = X_test.reshape(len(X_test), 3, 32, 32)

# 正解データを準備
y_test = np.array(test_data_dic['labels'])

# 結果表示用のラベルを準備
batch_meta = unpickle(os.getcwd()+"\\cifar-10\\batches.meta")
label_names = batch_meta['label_names']

CIFAR-10のテストデータでの予測

predictorメソッドを使って結果を取得できます。X_testから1枚選んで予測します。

# 選ぶ画像のインデックス
index = 1

# 予測
test_data = test_data_dic['data'][index]
test_data = test_data.reshape(3, 32, 32)
test_data = test_data.astype(np.float32)
test_data /= 255

result = model.predictor(Variable(np.array([test_data])))

# 予測出力
for i in range(10):
    print (label_names[i] + " : " + str(result.data[0,i]))

出力結果がこんな感じ。とても船。

airplane : 4.58905
automobile : -2.67672
bird : -8.51865
cat : -9.40089
deer : -14.1304
dog : -9.87916
frog : -5.56871
horse : -9.97257
ship : 14.8575
truck : -12.6762

前回のコードを使って表示させてみると、確かに船。

f:id:deis:20161005101405p:plain

渡し方が分かったので任意の画像を渡してみる

ようやく本題です。model.predictor()に渡す形式が分かったので読み込んだ画像ファイルを使って評価してみます。

続きを読む

Chainerで画像カテゴリ分類(CIFAR-10を使った学習)

ミスがあったので更新しました。 2016/10/25

すでにやりつくされている感がありますが、意外と学習から任意画像での評価までやっているサイトがなかったのでまとめておきます。学習させて用意されているテストデータを食べさせて何%の精度だったかより、ソリューション開発側としては、アプリに組み込んで動くかどうかの方が重要なので。

CIFAR-10データの準備と学習

というわけで、まずは、ChainerでCIFAR-10を学習してみます。基本は以下のサイトを参考にしました。
ai-programming.hatenablog.jp

ほぼそのままですが、少し修正して試してみます。

データの準備

まず、CIFAR-10を読み込みます。こちらは上のサイトと同じコードになりますが、読み込んだデータを画像として取り出してみました。画像として取り出すための処理は以下を参考にしました。
qiita.com

コードは以下の通りです。

import sklearn.datasets
import matplotlib
import matplotlib.pyplot as plt

import sys
import pickle
import os.path

def unpickle(file):
    fp = open(file, 'rb')
    data = pickle.load(fp, encoding='latin-1')
    fp.close()
    
    return data

X_train = None
y_train = []

for i in range(1,6):
    data_dic = unpickle(os.getcwd()+"\\data\\cifar-10\\data_batch_{0}".format(i))
    if i == 1:
        X_train = data_dic['data']
    else:
        X_train = np.vstack((X_train, data_dic['data']))
    y_train += data_dic['labels']
    
test_data_dic = unpickle(os.getcwd()+"\\data\\cifar-10\\test_batch")

# 評価用データ
X_test = test_data_dic['data']
X_test = X_test.reshape(len(X_test), 3, 32, 32)
y_test = np.array(test_data_dic['labels'])
X_train = X_train.reshape(len(X_train), 3, 32, 32)
y_train = np.array(y_train)

# ラベル名
batch_meta = unpickle(os.getcwd()+"\\data\\cifar-10\\batches.meta")
label_names = batch_meta['label_names']
label_names

# indexに入っている画像を描画(3×32×32のデータを32×32×3に変更)
index = 220
img = np.rollaxis(test_data_dic['data'][index].reshape((3,32,32)), 0, 3)
plt.title(label_names[y_test[index]])
plt.imshow(img, interpolation='none')

画像の出力結果はこんな感じ。

f:id:deis:20160923123641p:plain

学習

モデルと学習のコードは以下の通りです。ちょっとだけ修正しています。
話が逸れますがtqdmってライブラリがあるんですね。進捗が分かるようになっていたので感動しました。

import chainer
from chainer import cuda, Function, FunctionSet,gradient_check, Variable, optimizers, serializers, utils
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
import time

plt.style.use('ggplot')

# ニューラルネットワークの順伝播を記述
class Model(Chain):
    def __init__(self):
        super(Model, self).__init__(
            conv1=F.Convolution2D(3, 32, 3, pad=1),
            conv2=F.Convolution2D(32, 32, 3, pad=1),
            conv3=F.Convolution2D(32, 32, 3, pad=1),
            conv4=F.Convolution2D(32, 32, 3, pad=1),
            conv5=F.Convolution2D(32, 32, 3, pad=1),
            conv6=F.Convolution2D(32, 32, 3, pad=1),
            l1=F.Linear(512, 512),
            l2=F.Linear(512, 10)
        )
    def __call__(self, x):
        #x = chainer.Variable(x_data)
        h1 = F.relu(self.conv1(x))
        h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), 2)
        h3 = F.relu(self.conv3(h2))
        h4 = F.max_pooling_2d(F.relu(self.conv4(h3)), 2)
        h5 = F.relu(self.conv5(h4))
        h6 = F.max_pooling_2d(F.relu(self.conv6(h5)), 2)
        h7 = F.dropout(F.relu(self.l1(h6)))
        y = self.l2(h7)
        return y

# Classifier Chain
model = L.Classifier(Model())

# Optimizer
optimizer = optimizers.Adam()
optimizer.setup(model)

## 学習
from tqdm import tqdm

train_loss = []
train_acc = []
test_loss = []
test_acc = []

batchsize = 100

N = len(X_train)
N_test = len(X_test)

# 学習ループ
n_epoch = 30
for epoch in range(1, n_epoch+1):
    print("eopch", epoch)
    
    # training
    perm = np.random.permutation(N)
    sum_accuracy = 0
    sum_loss = 0
    # 0~Nまでのデータをバッチサイズごとに使って学習
    for i in tqdm(range(0, N, batchsize)):
        x = Variable(X_train[perm[i:i + batchsize]])
        t = Variable(y_train[perm[i:i + batchsize]])
        
        optimizer.update(model, x, t)
        
        sum_loss += float(model.loss.data) * len(t.data)
        sum_accuracy += float(model.accuracy.data) * len(t.data)

        train_loss.append(float(model.loss.data) * len(t.data))
        train_acc.append(float(model.accuracy.data) * len(t.data))
        
    # 訓練データの誤差と正解精度を表示
    print("train mean loss={0}, accuracy={1}".format(sum_loss / N, sum_accuracy / N))
    
    # 評価
    # テストデータの誤差と正解精度を算出し、汎化性能を確認
    sum_accuracy = 0
    sum_loss = 0
    for i in tqdm(range(0, N_test, batchsize)):
        x_batch = Variable(X_test[i:i+batchsize])
        y_batch = Variable(y_test[i:i+batchsize])

        loss = model(x, t)
        
        sum_loss += float(loss.data) * len(t.data)
        sum_accuracy += float(model.accuracy.data) * len(t.data)

        train_loss.append(float(loss.data) * len(t.data))
        train_acc.append(float(model.accuracy.data) * len(t.data))

    # テストデータでの誤差と正解精度を表示
    print("test mean loss={0}, accuracy={1}".format(sum_loss / N_test, sum_accuracy / N_test))

ちなみに、Trainerという機能を使った場合は、以下のコードになります。開発元のPFNの方が、Trainerを使ってください、とカンファレンスで言っていたので、できるだけこちらを利用した方がいいはず。ただ、残念ながら、私の環境では進捗がうまく表示されませんでしたが・・・コードのせいなのかな・・・。

from chainer import report, training, datasets, iterators
from chainer.training import extensions
from chainer.datasets import tuple_dataset

# データセットの準備
train = tuple_dataset.TupleDataset(X_train, y_train)
test = tuple_dataset.TupleDataset(X_test, y_test)

train_iter = iterators.SerialIterator(train, batch_size=100)
test_iter = iterators.SerialIterator(test, batch_size=100, repeat=False, shuffle=False)

updater = training.StandardUpdater(train_iter, optimizer)
trainer = training.Trainer(updater, (30, 'epoch'), out='result')

# trainerを設定
trainer.extend(extensions.Evaluator(test_iter, model))
trainer.extend(extensions.LogReport())
trainer.extend(extensions.PrintReport(['epoch', 'main/accuracy', 'validation/main/accuracy']))
trainer.extend(extensions.ProgressBar())

trainer.run()

学習時のエポック数は30にしました。学習結果は精度が90%程度でした。元サイトの精度グラフの30あたりを確認すると90%となっているので、妥当な値になっているようです。
ちなみに学習時間はCPU利用で6時間程度。このレベルならまだCPUでがんばれます。。

学習ファイルの保存(2016/10/25更新)

以下のコードで学習したモデルを保存します。次からは、この学習データを使って評価することができます。

# 学習データの保存

# 古いやり方らしく、うまく動作しない
# model.to_cpu()
# with open('model.pkl', 'wb') as o:
#     pickle.dump(model, o)

serializers.save_npz('model_cifar10.model', model)
serializers.save_npz('model_cifar10.state', optimizer)

その他

ちなみに学習の最中にMemoryErrorが発生することが何度かありました。そのため、Jupyterを利用して検証しながらやっていましたが、最終的にはコマンドプロンプトで実行しました。それでも発生することがありましたが、エラーの頻度は減ったようです。原因はよくわかりませんが、PCを再起動するとでなくなったりします。

次は、学習ファイルを読み込んで任意の画像ファイルが認識できるか確認してみます。
↓更新しました。
hellkite.hatenablog.com

機械学習のトレンドはマルチモーダル、応用で生活が変わる

今年もPrometech Simulation Conferenceに行ってきました。
www.prometech.co.jp
シミュレーション技術がメインのカンファレンスですが、Deep Learningのセッションも多く大学の研究内容を話してくれるので、情報を得るには有益でした。

最近のDeepLearningの1つのトレンドは、画像認識の分野において、CNNの多層化が顕著だということ。1つ1つの層のフィルタを小さめに、階層を深くすることで、より少ないパラメータでより複雑な非線形性を持たせることができるらしい。
一般画像認識の分野は、CNNがかなりいい成績を出しているので、今後大きな進展はないかも?正答率97%くらいらしいですし。

一方で、画像と他のデータを組み合わせた研究が盛ん。こういういくつかの異なる情報を使うことをマルチモーダルと呼ぶらしいです。今回は文章からの画像生成や画像の説明文を生成したりといった事例やロボット制御の話題などがでました。それにしても、人工知能が画像を想起できる能力を得たことはスゴイことです。
分かり易くいうと「赤い魚」といったときに、こんな形かな?と人が連想する処理と全く同じことを今の人工知能ではできてしまうということ。
これを応用すると、こんな動作をすると、次にこんな画像が得られるはず、つまり、次に起こることを連想しながら行動したり出力を操作したりすることができるようになる。

まぁ、実際にこういったものを実現するためには、色々な論文を読んだりしないといけないんでしょうけど、そういったことができる未来がすぐそこにあると感じられたのは、よいきっかけだったかと思います。逆にちょっと焦りますね。そんな世界で技術者としてやっていけるのかと。。。
人との一番確実なコミュニケーションは自然言語。この分野も面白そうだなぁ。強化学習もやってみたいけど。。


まぁ、とりあえず、いまだに動かないCIFAR-10をなんとかします^^;
↓動きました。
hellkite.hatenablog.com
hellkite.hatenablog.com

Chainerが面白い。手書き文字認識MNISTをやってみた。

触り始めた機械学習ライブラリ、Chainer。
まず、ここでNNの用語、構成要素を整理した後で、サンプルをやってみました。
パラメータやネットワークの記述の仕方に関してはなんとなくつかめた気がします。
qiita.com


やっぱり機械学習に触れてみたら画像を扱ってみたい。というわけで、画像を使ったサンプルとしては有名な、MNISTをやってみました。

f:id:deis:20160908174204p:plain

おおーって感じ。精度は98%程度ですが、パッと見、間違いが見つかりません。というより300文字くらいは見ましたが見つけられませんでした^^;

Chainerはバージョンによってネットワーク記述のやり方が変わったり*1、学習部分を担当するクラスが増えたり*2と結構バージョンによって書き方が変わるようです。ネットで探していると記述の違うサンプルがいくつか出てきて混乱しました。

Chainerのサンプルはアチコチに転がっているので、特に書きませんが、以下のサイトを参考にしました。

qiita.com

次のステップとしては、CIFAR-10という10種類のカテゴリ別に画像を分類するデータセットを試したいと思います。
というより試しているのですが、うまく動いていないのでデバッグ中です・・・。これができたら少し応用的なものを考えたいと思ってます。

*1:FunctionSetのcollect_parametersを使う書き方は古いらしく警告が出る

*2:Trainerというクラスが便利みたい。

Chainerを試してみた

急に時間が空いたので前々から興味のあった機械学習ライブラリ、Chainerをいじってみました。といっても、機械学習に関してはド素人なので、以下のページをやってみただけですが。

qiita.com


機械学習のライブラリは色々と設定が大変という印象があったので時間がないとできないと思っていましたが、

pip install chainer

の一行だけで使えるようになっているのが素敵。
昔とはえらい違いですね。。
なお、numpy scipy scikit-learn等が必要です。これらをWindowsにインストールしておく必要があります。今回はすでにやっていたのであっという間です。
hellkite.hatenablog.com


サイトの通りにやったら、ちゃんとこんな感じの結果が得られました。
f:id:deis:20160830145839p:plain
学習のところで固まってしまったので、不安でしたが30秒程度で戻ってきました。このくらいでも固まるのか・・・。ちゃんとやるならやっぱりGPUが欲しくなるかも。

色々と試したいこと*1はありますが、まずは今回やったことを調べて消化してから次のステップにいかないと分けわからない状態になりそうです。。

*1:ライントレースロボットとか倒立振子とか・・・