Chainerチュートリアル の和訳【Chainerの紹介と多層パーセプトロン】
バージョン1.5からFunctionSetがdeprecated(非推奨) となり、Chain, Linksなどが追加されました。バージョン1.11からTrainerが追加されました。これらの勉強ついでに【Introducion to Chainer】を和訳したので公開します。なにか問題があったら教えてください。バージョン1.11以前のMNIST認識のサンプルも残してあるので、昔の方法を知りたい方はそちらを参照ください。
- Introduction to Chainer
- Core Concept
- Forward/Backward Computation
- Links
- Write a model as a chain
- Optimizer
- Trainer
- Serializer
- Example: Multi-layer Perceptron on MNIST
- Example: Multi-layer Perceptron on MNIST (バージョン1.11以前)
Introduction to Chainer
この章はチュートリアルの初めの節です。この節では以下のことを学びます:
- 既存のフレームワークの賛否両論と、我々はなぜChainerを開発しているのか
- 単純なforward/backward計算のサンプル
- Linksの使い方と勾配計算
- つながり(多くのフレームワークでのモデル(model)に対応する)の構築
- パラメータの最適化
- LinksとOptimizersの直列化について
この節を読めば、あなたは以下のことができるようになります:
- 算術式の勾配の計算
- 多層パーセプトロンの記述
Core Concept
前面のページで述べたように、Chainerはニューラルネットワークのための柔軟性のあるフレームワークです。1つの重要な目標は柔軟性であり、我々に複雑な構造をシンプルで直感的に記述することを可能にしなければなりません。
既存のほとんどのフレームワークは"Define-and-Run"の枠組みに従っています。これは、初めにネットワークが定義、固定され、ユーザーが周期的にミニバッチ化されたデータを与えるというものです。forward/backward計算がなされる前に統計的にネットワークが定義されるため、すべてのロジックはネットワークの構造をデータとして組み込んでおく必要があります。その結果、そのようなシステム(Caffeなど)においてネットワークを定義することは宣言的なアプローチにつながってしまいます。命令型プログラミングを使って静的なネットワークを定義できるフレームワークがあることも述べておきます(Torch7やTheanoベースのものがそうです)。
それらと対照的にChainerは"Define-by-Run"の枠組みに適応しています。つまり、ネットワークが実行中に実際のforward計算によって定義されるということです。より具体的には、Chainerはプログラミングロジックの分まで計算の履歴を保持しています。この戦略によってPythonのもつプログラミングロジックの力をフルに引き出すことが可能になりました。例えば、Chaineは状態とループをネットワークの定義に持ち込むためのいかなる魔法も必要としません。"Define-by-Run"の枠組みはChainerの中心的な概念です。このチュートリアルを通じて、いかにして動的にネットワークを定義するかをお見せします。
またこの戦略は、ロジックがネットワークの操作と近いため、マルチGPUでの並列化を記述することを簡単にします。 この節のあとのほうで、このような仕組みを検討します。
! 注意 これからお見せするサンプルコードにおいて、以下のimport文が記述されていることを想定しています: import numpy as np import chainer from chainer import cuda, Function, gradient_check, report, training, utils, Variable from chainer import datasets, iterators, optimizers, serializers from chainer import Link, Chain, ChainList import chainer.functions as F import chainer.links as L from chainer.training import extensions これらのimport文はChainerのコードやサンプルにおいて広く利用されるため、このチュートリアルでは簡潔さのためにこれらのイディオムを省略しています。
Forward/Backward Computation
上記で説明したようにChainerは"Define-by-Run"の枠組みを利用しているので、forward計算それ自身がネットワークの定義となります。forward計算を始めるために、入力としてVariableオブジェクトを設定します。ここでは一個の要素を持ったシンプルなn次元配列から始めましょう。
>>> x_data = np.array([5], dtype=np.float32)
>>> x = Variable(x_data)
! 警告 Chainerはほとんどの計算において現在32ビットの浮動小数点のみをサポートします
Variableオブジェクトは基本となる算術演算を持っています。y=x2−2x+1y=x2−2x+1を計算するには以下のように記述します:
>>> y = x**2 - 2 * x + 1
結果値のyもまたVariableオブジェクトとなり、これらの値はdataフィールドにアクセスすることで得られます:
>>> y.data
array([ 16.], dtype=float32)
yが保有しているものは結果の値だけではありません。yは計算の履歴(計算グラフ)も同様に保持しています。この仕組みはyの微分を計算することを可能にします。微分演算はbackward()メソッドを呼び出すことで行われます:
>>> y.backward()
backwardは誤差逆伝播法(バックプロパゲーション)を走らせます。そしてその勾配が計算され、変数xのgradフィールドに保存されます:
>>> x.grad
array([ 8.], dtype=float32)
また中間変数の勾配についても計算することができます。注意しておきたいこととして、Chainerはデフォルトの設定ではメモリの効率性のために勾配配列の中間変数は開放されます。勾配の情報を保持するためには、backwardメソッドの引数のretain_gradにTrueを与えてください:
>>> z = 2*x >>> y = x**2 - z + 1 >>> y.backward(retain_grad=True) >>> z.grad array([-1.], dtype=float32)
これらの計算は容易に複数要素の配列に一般化できます。ただし複数要素の配列を保持する変数でbackward計算を行う場合、事前に初期エラー値を設定する必要があります。これには、シンプルに出力変数のgradフィールドに初期値を代入します:
>>> x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)) >>> y = x**2 - 2*x + 1 >>> y.grad = np.ones((2, 3), dtype=np.float32) >>> y.backward() >>> x.grad array([[ 0., 2., 4.], [ 6., 8., 10.]], dtype=float32)
! Note 多くの関数はVariableオブジェクトを取るようにfunctionsモジュールで定義されています。複雑な関数を実現するためにこれらを組み合わせて自動backward計算をすることができます。
Links
ニューラルネットを記述するためにはパラメータをもった関数を組み合わせて、そのパラメータを最適化する必要があります。これにはLinkを使います。Linkはパラメータを保持するオブジェクトとなっています(最適化の対象です)。
もっとも基礎となるものは通常の関数のように振る舞いつつ、パラメータによって引数を入れ替えるようなLinkです。もっと高次のLinkについても紹介しますが、ここでは単にパラメータを持った関数として考えてください。
! Note 実は、これらはv1.4以前の"parameterized functions"に対応しています。
最も頻繁に使われるLinkの1つは、Linear Link(総結合層、アフィン変換としても知られる)です。Linear Linkは数学関数f(x)=Wx+bを表現します、ここで行列Wとベクトルbはパラメータです。このLinkは片割れであるlinear()に対応し、これはx, W, bを引数として受け取ります。三次元空間から二次元空間へのLinear Linkは以下のように定義できます:
>>> l = L.Linear(3, 2)
! Note 多くの関数とLinkはミニバッチの入力のみ受け付けます。ここで、入力配列の初めの次元がバッチサイズとして考えられます。上記のLinear Linkのケースでは入力の形状は(N, 3)次元配列の必要があり、ここでのNがミニバッチサイズとなっています。
Linkのパラメータはフィールドに保持されます。それぞれのパラメータは1つのVariableインスタンスです。Linear Linkのケースでは、Wとbの2つのパラメータが保持されます。デフォルトで、行列Wはランダムに初期化され、ベクトルbはゼロで初期化されます。
>>> l.W.data array([[ 1.01847613, 0.23103087, 0.56507462], [ 1.29378033, 1.07823515, -0.56423163]], dtype=float32) >>> l.b.data array([ 0., 0.], dtype=float32)
Linear linkのインスタンスは通常の関数のように振る舞います:
>>> x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)) >>> y = l(x) >>> y.data array([[ 3.1757617 , 1.75755572], [ 8.61950684, 7.18090773]], dtype=float32)
パラメータの勾配はbackward()メソッドによって計算されます。それらの勾配は上書きよりもむしろ蓄積されます。そのため、計算を更新する時は勾配をゼロに初期化しなければなりません。これはzerogradsメソッドを呼び出すことで行われます。
>>> l.zerograds()
これでbackwardメソッドの呼び出しによってパラメータの勾配を計算できます。
>>> y.grad = np.ones((2, 2), dtype=np.float32) >>> y.backward() >>> l.W.grad array([[ 5., 7., 9.], [ 5., 7., 9.]], dtype=float32) >>> l.b.grad array([ 2., 2.], dtype=float32)
Write a model as a chain
多くのニューラルネットの構造は複数のLinkを含みます。例えば、多層パーセプトロンは複数の線形層から構成されます。複数のLinkを組み合わせることで、この複雑な手順をパラメータによって記述できます。
>>> l1 = L.Linear(4, 3) >>> l2 = L.Linear(3, 2) >>> def my_forward(x): ... h = l1(x) ... return l2(h)
ここで、Lはchainer.linksモジュールのことです。この方法で処理手順をパラメータによって定義すると再利用が難しいです。よりPythonらしい方法はLinkと手順をクラスにまとめることです:
>>> class MyProc(object): ... def __init__(self): ... self.l1 = L.Linear(4, 3) ... self.l2 = L.Linear(3, 2) ... ... def forward(self, x): ... h = self.l1(x) ... return self.l2(h)
もっと再利用しやすいようにするには、パラメータ管理、CPU/GPUの移行サポート、頑強で柔軟性のある保存/読み込み、などの機能がほしいです。これらの特徴はすべて、ChainerのChainクラスがサポートします。Chainクラスの特徴を利用するためには、単純に上記のクラスをChainクラスのサブクラスとして定義するだけです:
>>> class MyChain(Chain): ... def __init__(self): ... super(MyChain, self).__init__( ... l1=L.Linear(4, 3), ... l2=L.Linear(3, 2), ... ) ... ... def __call__(self, x): ... h = self.l1(x) ... return self.l2(h)
Note 我々はよくLinkの1つのforwardメソッドを__call__オペレータによって定義します。このようなLinkとChainは変数の通常の関数のように振る舞います。
より複雑なつながりがより単純なLinkによって構築されるかを示します。l1とl2のようなLinkはMyChainの子供Linkとして呼び出されます。Chainそれ自身はLinkを継承しています。それは、MyChainオブジェクトを子供Linkとして、より複雑なつながりを定義することができることを意味します。
Linkのリストのように振る舞うChainListクラスを利用することによってもつながりを定義できます:
>>> class MyChain2(ChainList): ... def __init__(self): ... super(MyChain2, self).__init__( ... L.Linear(4, 3), ... L.Linear(3, 2), ... ) ... ... def __call__(self, x): ... h = self[0](x) ... return self[1](h)
ChainListは任意の数のLinkを使う場合に便利です。上記のようにLinkの数が固定されている場合、基底クラスとしてはChainクラスをお薦めします。
Optimizer
よいパラメータの値を得るためにOptimizerクラスによって最適化がなされます。OptimizerはLinkに与えられた数値最適化アルゴリズムを走らせます。多くのアルゴリズムがoptimizersモジュールに実装されています。ここでは最も単純なもの、確率的勾配降下法をつかいましょう:
>>> model = MyChain() >>> optimizer = optimizers.SGD() >>> optimizer.setup(model)
setup()メソッドは与えられたLinkに対する最適化の準備をします。
最適化を走らせるには2つの方法があります。1つ目の方法は手動で勾配を計算し、updateメソッドを引数なしで呼び出します。勾配をリセットするのを忘れないようにしましょう!
>>> model.zerograds()
>>> # compute gradient here...
>>> optimizer.update()
もうひとつの方法は、損失関数をupdateメソッドに引き渡します。この場合、zerogradsメソッドはupdateメソッドによって自動的に呼び出されます。
>>> def lossfun(args...): ... ... ... return loss >>> optimizer.update(lossfun, args...)
いくつかのパラメータ/勾配の操作(荷重減衰や勾配切落しなど)はOptimizerにフック関数を設定することでなされる。フック関数はupdateメソッドによって実際の更新の前に呼び出される。例えば、次の行を事前に呼び出すことで、荷重減衰による正則化を設定できる:
>>> optimizer.add_hook(chainer.optimizer.WeightDecay(0.0005))
もちろん独自のフック関数を記述できます。それにはOptimizerを引数とするような関数やcallableオブジェクトでなければなりません。
Trainer
ニューラルネットワークを訓練させたいとき、パラメータを更新するために数多くの訓練ループを回す必要があります。たとえば典型的な訓練ループは以下の手順になっています:
1. 訓練データセットのイテレーション
2. ミニバッチに対する前処理
3. ニューラルネットワークのForward/backward計算
4. パラメータの更新
5. 評価データセットにおける現在のパラメータの評価
6. 中間結果をログに残す
Chainerではシンプルだが強力な方法でこれらの訓練プロセスを簡単に記述できます。訓練ループを抽象化すると主に2つに分けられます:
- Dataest
これは上記のリストの1と2に対応します。重要な部品はdatasetモジュールで定義されています。datasetsモジュールとiteratorsモジュールではそれぞれデータセットとイテレータに関する多くの実装があります。
- Trainer
これは上記のリストの3,4,5,6に対応します。全体の処理はTrainerに実装されています。パラメータを更新する方法(3と4)はUpdaterで定義されていて、これは自由にカスタマイズすることができます。5と6はExtensionのインスタンスに実装されます。これは訓練ループに追加の処理を記述できる。ユーザはExtensionを追加することで、訓練の手順を自由にカスタマイズすることができます。また、独自のExtentionを定義することも可能です。
この後のExample節でTrainerの使い方をお見せします。
Serializer
ここで最後に紹介する中心的な特徴はSerializerと呼ばれる、オブジェクトを直列化、非直列化するためのインターフェースです。LinkとOptimizerはSerializerによって直列化をサポートします。
実際のSerializerはserializersモジュールで定義されています。そして、Numpy, NPZ, HDF5フォーマットをサポートします。
例えば、serializers.save_npz() 関数によって、Linkを直列化し、NPZファイルに保存できます:
>>> serializers.save_npz('my.model', model)
このコードはモデルのパラメータを'my.model'ファイルに保存します。保存されたモデルはserializers.load_npz()関数によって読み込みすることができます:
>>> serializers.load_npz('my.model', model)
! Note パラメータと永続的な値のみがこれらのコードで直列化されます。他のフィールドについては自動的に保存されません。配列、スカラー、その他の直列化可能なオブジェクトを永続的な値として登録するには、Link.add_persistent()メソッドを使います。登録されれた値はadd_persistent()メソッドに渡したそのフィールド名でアクセスできます。
Optimizerの状態も同様に同じ関数によって保存できます:
>>> serializers.save_npz('my.state', optimizer) >>> serializers.load_npz('my.state', optimizer)
! Note Optimizerの直列化は繰り返し回数、MomentumSGDのモメンタムベクトルなどの、内部状態のみを直列化します。対象とするLinkのパラメータや永続的な値は保存しません。保存された状態から最適化を再開するには対象とするLinkとOptimizerを明示的に保存する必要があります。
HDF5フォーマットを使うにはhdpyパッケージがインストールされている必要があります。HDF5フォーマットの直列化および非直列化はほとんどNPZフォーマットと同じです。ただsave_nptz(), load_npz()をsave_hdf5(), load_hdf5()に置き換えるだけです。
Example: Multi-layer Perceptron on MNIST
いままでの概念を使って多層パーセプトロンを使って多クラス分類問題をとけるようになりました。ここではMNISTと呼ばれる手書き数字データセット(機械学習の"hello world"の定番)を使いましょう。このMNISTサンプルは公式レポジトリのexamples/mnistディレクトリにもあります。この説ではいかにしてTrainerを使って訓練ループを構築するのかを紹介します。
初めにMNISTデータセットを準備する必要があります。MNISTデータセットはサイズ28x28(784)ピクセルでグレースケールの70,000枚の画像から構成され、それぞれに数字のラベルが対応します。データセットは60,000枚の訓練画像と10,000枚のテスト画像にデフォルトで分割されています。dataset.get_mnist()によってベクトル化されたバージョン(784次元のベクトル)のデータセットを得ることができます。
>>> train, test = datasets.get_mnist()
このコードは自動的にMNISTデータセットをダウンロードし、NumPy配列を $(HOME)/.chainer ディレクトリに保存します。返り値のtrainとtestはimageとlabelのペアとして見ることができます(厳密にいえば、これらはTupleDatasetのインスタンスです)。
また、いかにしてこれらのデータセットを繰り返すのかを定義する必要があります。ここでは、訓練データセットを毎ループでシャッフルしたいです。この場合、iterators.SerialIteratorが使えます。
>>> train_iter = iterators.SerialIterator(train, batch_size=100)
一方、テストデータセットはシャッフルする必要がありません。この場合、shuffle=Falseを引数に与えることで、シャッフルを無効化できます。これにより、データセットが高速なスライスに対応している場合、データセットの繰り返しが早くなります。
>>> test_iter = iterators.SerialIterator(test, batch_size=100, repeat=False, shuffle=False)
ここで、repeat=Falseとしたが、これはすべての要素を見た時に繰り返しが終了することを意味します。テスト/評価データセットにはこのオプションが必要です。もし、このオプションを与えなかった場合、無限ループに陥ります。
次に、ネットワークの構造を定義します。ここでは、例として各層が100個のユニットを持つシンプルな3層のネットワーク(MLP:Multi Layer Perceptron)を使います。
>>> class MLP(Chain): ... def __init__(self): ... super(MLP, self).__init__( ... l1=L.Linear(784, 100), ... l2=L.Linear(100, 100), ... l3=L.Linear(100, 10), ... ) ... ... def __call__(self, x): ... h1 = F.relu(self.l1(x)) ... h2 = F.relu(self.l2(h1)) ... y = self.l3(h2) ... return y
このLinkはrelu()を活性化関数として利用しています。ここで、'l3'リンクは最後の層となっており、その出力は10個の数字のスコアに対応します。
損失関数を計算したり、予測の精度を評価するために、Classifier Chainを定義します:
>>> class Classifier(Chain): ... def __init__(self, predictor): ... super(Classifier, self).__init__(predictor=predictor) ... ... def __call__(self, x, t): ... y = self.predictor(x) ... loss = F.softmax_cross_entropy(y, t) ... accuracy = F.accuracy(y, t) ... report({'loss': loss, 'accuracy': accuracy}, self) ... return loss
このClassifierクラスは精度と損失を計算し、損失値を返します。xとtのペアはデータセットのそれぞれの例に対応します。 softmax_cross_entropy() は与えられた正解ラベルと予測との損失値を計算します。accuracy()予測精度を計算します。 Classifierインスタンスには任意の予測用Linkを設定できます。
report()関数は損失と精度の値をtrainerに報告します。詳しい訓練での統計情報の仕組みは、Reporterをみてください。似た方法で他のタイプの観測情報、例えば活性化に関する情報を集めることができます。
同様のクラスがchainer.links.Classifierとして実装されています。 なので、上記のサンプルではなく、すでに定義されているClassifierを使うこととします。
>>> model = L.Classifier(MLP()) >>> optimizer = optimizers.SGD() >>> optimizer.setup(model)
これで、trainerオブジェクトを構築できます。
>>> updater = training.StandardUpdater(train_iter, optimizer) >>> trainer = training.Trainer(updater, (20, 'epoch'), out='result')
二番目の引数(20, 'epoch')は訓練の期間を表現します。epochもしくはiterationをユニットとして使えます。この場合、訓練集合を20回繰り返すことにより、多層パーセプトロンを訓練します。
訓練ループを実行するためは、単にrun()メソッドを呼びだします。
>>> trainer.run()
このメソッドによってすべての訓練処理が実行されます。
上記のコードは単にパラメータを最適化します。多くの場合、訓練プロセスがどうなっているのかを見たいです。その場合、runメソッドを呼ぶ前に拡張(extension)コードを入れることで実現できます。
>>> 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()
これらの拡張は以下のタスクを実行します:
- Evaluator
毎ループで、テストデータセットに対する現在のモデルを評価します
- LogReport
報告された値を蓄積し、ログファイルを出力ディレクトリに書き出します
- PrintReport
LogReportの中の選択された値をプリントします
- ProgressBar
進捗状況を可視化します
chainer.training.extensionsモジュールに多くのエクステンションが実装されています。上記のコードに含まれていない最も重要な拡張は、snapshot()です。これは訓練プロセスのスナップショット(つまり、Trainerオブジェクト)を出力ディレクトリに保存します。
examples/mnistにあるサンプルコードはこのチュートリアルと同程度に重要なGPUへの対応を含んでいます。後の節でどうやってGPUを使うかを検討します。
Example: Multi-layer Perceptron on MNIST (バージョン1.11以前)
いままでの概念を使って多層パーセプトロンを使って多クラス分類問題をとけるようになりました。ここではMNISTと呼ばれる手書き数字データセット(機械学習の"hello world"の定番)を使います。このMNISTサンプルは公式レポジトリのexamples/mnistディレクトリにもあります。
MNISTを使うためには、examples/mnist/data.pyにある関数を使ってmnistのデータを読み込みましょう:
>>> import data
>>> mnist = data.load_mnist_data()
MNISTデータセットはサイズ28x28(784)ピクセルでグレースケールの70,000枚の画像から構成され、それぞれに数字のラベルが対応します。まずはじめに、ピクセルの値を[0, 1]にスケールし、データセットを60,000枚の訓練サンプルと10,000枚のテストサンプルに分けます。
>>> x_all = mnist['data'].astype(np.float32) / 255 >>> y_all = mnist['target'].astype(np.int32) >>> x_train, x_test = np.split(x_all, [60000]) >>> y_train, y_test = np.split(y_all, [60000])
次に、構造を定義したいです。ここでは、サンプルとして各層が100個のユニットを持つ三層のネットワーク(MLP:Multi Layer Perceptron)を使います。
>>> class MLP(Chain): ... def __init__(self): ... super(MLP, self).__init__( ... l1=L.Linear(784, 100), ... l2=L.Linear(100, 100), ... l3=L.Linear(100, 10), ... ) ... ... def __call__(self, x): ... h1 = F.relu(self.l1(x)) ... h2 = F.relu(self.l2(h1)) ... y = self.l3(h2) ... return y
このLinkはrelu()を活性化関数として利用しています。ここで、'l3'リンクは最後の層となっており、その出力は10個の数字のスコアに対応します。
損失関数を計算したり、予測の精度を評価するために、Classifier Chainを定義します:
>>> class Classifier(Chain): ... def __init__(self, predictor): ... super(Classifier, self).__init__(predictor=predictor) ... ... def __call__(self, x, t): ... y = self.predictor(x) ... self.loss = F.softmax_cross_entropy(y, t) ... self.accuracy = F.accuracy(y, t) ... return self.loss
このClassifierクラスは精度と損失を計算し、損失値を返します。 softmax_cross_entropy() は与えられた正解ラベルと予測との損失値を計算します。Classifierインスタンスには任意の予測用Linkを設定できます。
同様のクラスがchainer.links.Classifierとして実装されています。 なので、上記のサンプルではなく、すでに定義されているClassifierを使うこととします。
>>> model = L.Classifier(MLP()) >>> optimizer = optimizers.SGD() >>> optimizer.setup(model)
最後に、学習ループを以下のように記述できます:
>>> batchsize = 100 >>> datasize = 60000 >>> for epoch in range(20): ... print('epoch %d' % epoch) ... indexes = np.random.permutation(datasize) ... for i in range(0, datasize, batchsize): ... x = Variable(x_train[indexes[i : i + batchsize]]) ... t = Variable(y_train[indexes[i : i + batchsize]]) ... optimizer.update(model, x, t) epoch 0...
最後の3行のみがすでに説明したChainerのコードに関連します。最後の行ではmodelを損失関数としてoptimizerに渡しています。
これらの3行は明示的に勾配を計算する以下のコードに書き直すことができます:
>>> batchsize = 100 >>> datasize = 60000 >>> for epoch in range(20): ... print('epoch %d' % epoch) ... indexes = np.random.permutation(datasize) ... for i in range(0, datasize, batchsize): ... x = Variable(x_train[indexes[i : i + batchsize]]) ... t = Variable(y_train[indexes[i : i + batchsize]]) ... model.zerograds() ... loss = model(x, t) ... loss.backward() ... optimizer.update() epoch 0...
毎回ネットワークがforward計算によって定義され、誤差逆伝播法に使われ、破棄されることが見て取れます。この“Define-by-Run”の枠組みでリカレントネットで、様々な長さの入力が、単純に毎回違う長さの入力を入れることで扱えることが想像できるでしょう。
最適化の後、もしくは最中modelの精度をテスト集合によって計測したいです。これは単純にforward関数を呼び出すことで実現できます。
>>> sum_loss, sum_accuracy = 0, 0 >>> for i in range(0, 10000, batchsize): ... x = Variable(x_test[i : i + batchsize]) ... t = Variable(y_test[i : i + batchsize]) ... loss = model(x, t) ... sum_loss += loss.data * batchsize ... sum_accuracy += model.accuracy.data * batchsize ... >>> mean_loss = sum_loss / 10000 >>> mean_accuracy = sum_accuracy / 10000
examples/mnistにあるサンプルコードはこのチュートリアルと同程度に重要なGPUへの対応を含んでいます。後の節でどうやってGPUを使うかを検討します。