無限グミ

メモとか備忘録とか日記

Chainerのモデルのセーブとロード

Chainerのモデルのセーブ・ロードについて

Chainerは作成したモデルを書き出すためのserializersを含んでいます。しかし、セーブの方法は多くのページに書いてありますが、ロードの方法がよくわからなかったため自分の環境でうまく行った方法をまとめます。

モデルのセーブ

Chainerでモデルをセーブするにはchainer.serialisersを使います。この時にGPU用に変換したものはCPU用に変えておいたほうが後で混乱しないで済むので統一しておくといいと思います。

from chainer import serializers

gpu = 0

model = myDNN(100, 500, 10)
chainer.cuda.get_device(gpu).use()
model.to_gpu(gpu) # GPUを使うための処理

# -----------------------------
#         学習とかの処理
# -----------------------------

model.to_cpu() # CPUで計算できるようにしておく
serializers.save_npz("mymodel.npz", model) # npz形式で書き出し

(必要なところだけを書いたので自分のコードに合わせてください。)

serializersでサポートしている形式はnpzとhdf5フォーマットです。普段はnpzしか使っていないのでhdf5形式で書きだしたことはないですが、基本は同じはずです。hdf5フォーマットで書き出すときは次のようにします。

serializers.save_hdf5("mymodel.h5", model)

モデルのロード

モデルをロードするときもchainer.serializersを使います。

ここで注意してほしいことは、モデルの枠組みを用意してから重みなどの情報をロードしてあげないといけないことです。コードでは以下のように記述します。

from chainer import serializers

model = myDNN(100, 500, 10) # saveした時と同じ構成
serializers.load_npz("mymodel.npz", model) # "mymodel.npz"の情報をmodelに読み込む

hdf5形式の場合も同じように書きます。

serializers.load_hdf5("mymodel.h5", model)

よくある間違いとそのエラーコード

L.Classifierを使っている場合

例えば、分類問題などの場合では、chainer.linksのClassifierを使うことが多いと思います(サンプルもそうなっている)。その場合、学習を行うプログラムでは以下のように書きます。

import chainer.links as L
from chainer import serializers

model = L.Classifier(myDNN(100, 500, 10))

# ----------------------------------------
#            学習
# ----------------------------------------

model.to_cpu()
serializers.save_npz("mymodel.npz", model)

このようにして書きだしたモデルをロードしようとして以下のコードを実行します。

from chainer import serializers

model = myDNN(100, 500, 10)
serializers.load_npz("mymodel.npz", model)

すると以下のようなエラーコードが帰ってきます。

KeyError: 'l2/W is not a file in the archive'

これはモデルをセーブするときにL.Classifierを通したモデルを指定しているため、ロード時のモデルと異なってしまうことによるエラーです。正しくロードするためにはロード時にもL.Classifierを使うことでうまく読み込むことができます。

import chainer.links as L
from chainer import serializers

model = L.Classifier(myDNN(100, 500, 10))
serializers.load_npz("mymodel.npz", model)

セーブ時とロード時のモデルの形状が違う場合

例えば、セーブ時には隠れ層のノード数を500で保存したのに、ロードするときに1000ノードの枠組みを用意してしまった、という場合。

import chainer.links as L
from chainer import serializers

model = myDNN(100, 500, 10)

# ----------------------------------------
#            学習
# ----------------------------------------

model.to_cpu()
serializers.save_npz("mymodel.npz", model)
from chainer import serializers

model = myDNN(100, 1000, 10) # 隠れ層のノード数が違う
serializers.load_npz("mymodel.npz", model) 

このような場合は以下のエラーが表示されます。

ValueError: could not broadcast input array from shape (100,500) into shape (100,1000)

解決方法はロード時のモデルをセーブした時と同じノード数に指定することです。

まとめ

Chainerは非常に直感的で書きやすく、ちょっと複雑なモデルも簡単につくれてしまうので大変重宝しています。ただ、モデルのセーブなどで少し躓いてしまったので、今回まとめてみました。まだまだ、触り始めて日が浅いので、もっと理解できるようようになりたいと思います。