Caffeを使ったCNNによる手書き文字分類 [3] - prototxtの作成

blog.li.nu >
この記事が役に立ったら
 前回は、CNNの構造、およびCNNを作る際に設定しなければならない項目(これらをハイパーパラメータと呼びます)についてひと通り説明しました。 今回は、実際に手書き文字認識を行うCNNを、Caffeが読み込める形(prototxt)で作成します。

prototxtとは

 プロトテキスト(prototxt)とは、ニューラルネットの構造を記述するためのCaffe独自のテキスト形式です。 また、prototxt自体、Googleが開発したデータ構造を記述するためのテキスト形式の一種であるProtobufの文法に基づいたものです。 実はPyCaffeには、このprototxtをPython上から自動生成する機能が備わっていますが、少し面倒なので今回は手打ちします。

prototxtの基本文法

 prototxtは「key: value」という型にしたがってデータの構造を記します。 key の部分は項目名に当たる場所、value はそれに対する実際の値です。 数字の場合は直に数字を打てますが、文字列の場合は "" で囲む必要があります。 例えば、人を表すデータを作りたいとき、年齢(age)と名前(name)を書くとしましょう。この場合は
name: "山田太郎"
age: 99
という風に書けます。もし同じ value に当たる部分に単なる値ではなく複数の key: value の構造を記したい場合は 「key { }」 のように、 {} で囲った中にさらに key: value を書き込んで表現します。
例えば、上の name と age を持つものを person という項目名にしたとします。この場合
person {
    name: "山田太郎"
    age: 99
}
と表現できます。同時に、この person を2つ並列したい場合は
person {
    name: "山田太郎"
    age: 99
}

person {
    name: "田中花子"
    age: 18
}
のように書くことができます。さらに、もし子供がいる場合、同じ name と age を持つ child というものを各 person が自由に持っていいことにします。 その場合、上の "田中花子" だけに子供がいるとすると
person {
    name: "山田太郎"
    age: 99
}

person {
    name: "田中花子"
    age: 8
    child {
        name: "田中太郎"
        age: 1
    }
}
と書くことができます。基本的にprototxtの文法はこれだけです。実際CNNの構造を書く場合は、上のように、Caffeによってあらかじめ用意されている設定項目を自分で選んで書き足していく形になります。

4つのprototext

 1つのニューラルネットを学習し、一般画像(データセットに含まれない画像)に適用するまでには、4つのprototxt (必ずしも4つのファイルである必要はありませんが、役割としては4種類) を作成する必要があります。 正式に名前が決まっているわけではありませんが、Caffeの各サンプルでの名付けられ方にならい、ここでは以下の4つの名前でそれらを呼ぶことにします。 まずは、この4つのprototxtが何をするものなのかを説明します。
 solver.prototxtは、ニューラルネットを訓練する時に読み込む大元のテキストファイルだと思ってください。ここに書いてある命令に従い、Caffeは各ネットワークの訓練およびテストを行います。 あとで説明しますが、solver.prototxtにはテスト時、訓練時にどのCNNを使うのか、また学習率といった学習そのものの方針を示すパラメータはいくらにするのかを記述します。
 train.prototxtは、訓練時に用いるCNNの構造が記されたテキストファイルで、test.prototxtは、訓練の途中、CNNがどのくらいうまく学習できているのかを調べるため、train.prototxtとは別のデータセットを指定した、同じ構造のCNNを表すテキストファイルです。 CNNの構造自体は一緒なので、節約のため、これらをまとめて1つのprototxtに含めることもできます。今回もそのように、この2つのprototxtに相当するものをまとめてtrain.prototxtに書くという方法を取ります。
 deploy.prototxtは、訓練が終わった後、未知のデータに対して分類を行うためのネットワークの構造を示したもの、要するに本番用のprototxtです。 そのへんのインターネットから拾ってきた画像でも識別できることを友達に自慢したいときに使います。deploy.prototxtはtrain.prototxtからコピペして作ります。 なので、訓練段階では別に作る必要はありません。

train.prototext / test.prototxt の作成

 0から手打ちすることもできますが、余程のことがない限りは適当なサンプルから必要に応じて組み替えていくのがよいでしょう。 以下は、手書き文字を識別するため、最小限の構成で作られたtrain.prototxtの例です。このファイルを、第一回で作った mycnn のフォルダ、つまり lmdb があるところと同じフォルダに保存してください。 この train.prototxt は、上で紹介した train と test の役割をひとつの prototxt で同時に担っています。
train.prototxt
長くて難しく見えるかもしれませんが、図にすると以下のような単純な構造です。
これは、前回まで見てきた、ごく単純なCNNに過ぎないことが理解できます。畳み込み層とプーリング層による特徴抽出は2回行われ、全結合層は2層からなります。 2つめの全結合層は出力層であり、手書き文字の「0」から「9」まで、合計10個を分類したいので10個にする必要があります。
 この構造そのものや、書いてあるパラメータの意味が分からない場合、前の記事で全部紹介しているのでそちらを見てください。 なお、例えば畳み込み層の20枚や50枚といった数字は、電気回路などとは違って、何らかの理論により求めた値ではありません。そのため、なぜこの数なのかを探るのは、実用的な意味においてはほとんど無意味と言えます。 今後訓練できるようになったら、自分で数字を変えて、色々試してみるべきです。
 ただし、数字を変える上で2つ注意点があります。1つは何度も書いているように、メモリは無制限ではないので、これを2000とか50000にすることはできないということです。 実際に大きな値にすれば分かりますが、たいがいメモリ不足でエラーが起きます。 また、本当に10個の数字を識別するのにそのような何万個種類の特徴量が必要なのかについても考える必要があります。要するに、無駄に多い、冗長な数字に設定すると、あらゆる面で損をします。 特に全結合層に関しては、数が多すぎると過学習を起こしやすいことが知られているので、無駄に10000や50000といった数にするべきではありません。 これも別にルールとして決まっているわけではありませんが、全結合層に入る前の出力の数と似たような数を設定しておくとスタートポイントとして無難です。 例えば上のCNNの場合、入力は28x28で、pool2 を通るときには 4x4 になっています。それが50枚あることになるので、800個の数値が全結合層ip1に渡されます。なので800に近い値にしておくことがよいスタート地点になるでしょう。 電卓を叩いても800は出せますが、Pythonでこの数を一瞬で調べられる方法があるので、それについては後で紹介します。
 もう1つは、出力サイズです。前回紹介したように、畳み込み層ではサイズ $n$ に対して周囲が $(n-1)/2$ 、だけ縮み、プーリング層ではストライド $s$ のとき約 $1/s$ 倍画像が縮みます。 つまり、小さい画像に畳み込みやプーリングを使いすぎると途中の知らない間に画像が $1 \times 1$ まで縮んでしまい、本当にそれ以上畳み込みやプーリングをやる必要があるのあか考える必要が出てくるような状態に陥ります。 確かにサイズ1の畳み込みといったテクニックは存在しますが、意図せず事実上そのような処理になってしまったことと、意図的にそうなるよう設定したのでは事情が違います。
 以下、構造について確認したところで、各層のprototxtの記述方法について紹介します。

各層の基本的な記述方法

まず、train.prototxt または text.prototxt では、以下のようにCNNの構造が記されます。
name: ""
layer{
    name: ""
    type: ""
    bottom: ""
    top: ""
}

layer{
    name: ""
    type: ""
    bottom: ""
    top: ""
}

...
 最初に name でニューラルネット全体に名前を付けます。 出力層まわりの構造やパラメータなどが違いますが、上の図のような構造で、同じMNISTの文字分類を行う目的を持ったニューラルネットはY.LeCunらによる "LeNet" という名前のネットワークによってはじめて提案されたため、今回はその名前を持ってきています。
 あとは、入力データ、畳み込み層、プーリング層、全結合層、誤差(後で説明)など、全て上にあるように layer で囲まれた構造を並べて構成します。
 layer の構成方法ですが、これは自分が使いたい層によって微妙に違います。しかしながら、以下に記すことはどのような層であっても同様です。 まず最初に name により層の名前を付けます。これも決まりはありませんが、慣例(一般的な論文や、Caffeのサンプル)では以下のように付けられています。 畳み込み層は英語で convolutional layer なので conv, プーリング層は pooling layer なので pool 、全結合層は fully connected layer なので fc です。 普通は全結合層と呼びますが、この部分は数学でいうと前回書いたように「内積」(inner product)という計算に相当するため、Caffeでは inner product layer と呼ばれており、ip と書かれることもあります。 ここではCaffeの各サンプルにならって ip の規則で名付けることにします。
 type は重要で、Caffeによってあらかじめ定義された値の種類の中から、必要な層の名前を選んでここに書きます。Caffeの Layer Catalogue というページに全種類載っていますが、ここでも今回使うものを掲載します。 また、make する前のソースコードを自分でいじると、新しい層を作ることもできます。 なお、一般的なCNNの構造を説明する上では「層」ではないものも、Caffeでは便宜上 layer として定義する必要があるので、混乱のないようにしましょう。  結局どれも重要ですが、一番注意したいのは top と bottom についてです。これは、上のCNNの構造を示した図でいう各層を結んだ「→」に相当する関係を規定しています。 要するに、Caffeではたくさんの layer を作っておいて、それぞれ bottom と top によってそれらを連結することで CNN を作ります。
 top は、この層の出力をどのような名前で伝達するかを指定します。これは name とは別で、実際プログラミングをする上で使うのはこちらの方の名前です。 例えば top に "input" という名前をつけると、その層の出力データは "input" という名前であとで Python からアクセスできるようになることを意味します。 基本的には conv1 や pool2 といった、 name と同じものにしておけばよいでしょう。
 bottom は、そうして top により生成されたデータのうち、この層はどれを受け取るのかを指定するものです。 上の例でいう "input" を別のある層で受け取りたい場合には、 bottom: "input" と書けばよいですね。
 最後に、train.prototxt と test.prototxt を1つにまとめたい場合の方法についてです。通常これらは別々に書いても問題ありません。 なぜ同じ構造のはずなのに別々に書く必要があるかですが、それぞれデータセットが違っているからです。 train では訓練のためのデータセット、test では、訓練と同じものを使うと訓練用データにしか適応できない(過学習)おそれがあるのでそれとは別のデータセットを通常指定します。 なので、同じ構造であっても、訓練用データのLMDBを指定したCNNと、テストデータのLMDBを指定したCNNで別々に用意する必要があります。
 1つにまとめたい場合は、上でダウンロードした train.prototxt にもあるように、以下のように書きます。
name: "LeNet"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "mnist_train_lmdb"
    batch_size: 64
    backend: LMDB
  }
}
説明していないものもいくつかありますが、それは気にしないでください。この時の「include」の部分が重要です。 訓練時だけ存在するようにしたい層は、上のように phase: TRAIN を含んだ include を書くと実現できます。 同様に、訓練時ではなく、現時点での訓練データではないデータ、つまりテストデータに対する正答率を見ている時にだけ存在させたい層は phase: TEST を含んだ include を書けばよいのです。 この記述を駆使することで、基本的な構造は流用しながら、訓練時、テスト時だけ一部の層が異なっているニューラルネットワークを作ることができます。
 solver.prototxt だけは上で書いたルールとはまた違うので別途説明しますが、以下、各層ごとに基本的な書き方を見ていきます。

入力層(Data)

 実際の入力データを供給する層が Data です。 type: "Data" と書くことでこの層にできます。まずは、 train.prototxt の中にあるこの層の記述について見てみましょう。
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "mnist_train_lmdb"
    batch_size: 64
    backend: LMDB
  }
}
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "mnist_test_lmdb"
    batch_size: 100
    backend: LMDB
  }
}
先ほど説明したように、訓練時とテスト時では違うLMDBを読み込まなければならないので、includeで訓練用とテスト用の Data 層をうまく分けています。 当然この層は入力層なので、他の層から受け取るものはありません。したがって入力層に限っては bottom が必要ありません。 一方で、この層から生成されるデータは2つあります。LMDBには画像とあわせ、その画像の正解ラベルが何であるかという情報が格納されているからです。 正解ラベルは、識別したい画像の種類のうち、最初から数えて何番目のカテゴリなのかを数字で指定します。この数字はプログラミングなどではおなじみですが、1ではなく0から始まります。 今回のMNISTの手書き文字の場合、0〜9とちょうど0からはじまっているため、数字 = 正解ラベルにすれば分かりやすいでしょう。なので今回は、数字がそのまま正解ラベルに対応するようにします。 例えば犬猫人を判別したい場合は、自分で犬=0, 猫=1, 人=2 と定義した上で、LMDBを作る際も、この画像は犬なので 0、これは人なので 2 ・・・といったラベルを手動で付ける必要があります。 1つめの top では、画像データを受け取ります。2つめの top で、正解ラベルを受け取っています。これらはどういう名前でも良いですが、分かりやすいのでそれぞれ top: "data" と top: "label" と名づけました。
 transoform_param の部分は、データの値を改変したい場合に使います。 MNISTのLMDBから直接受け取った画像データ(に限らず普通の画像データ)は、0〜255までの256段階で画素値が記録されていますが、Caffeはこれを0〜1の範囲で使うため、単純に $1/256$ に相当する 0.0039 という値がここで指定されています。 この scale で指定した倍率だけ入力データの画素値が一律スケーリングされます。つまり、この transform_param を書いたことで、0〜255 までの画素値が 0〜1 の範囲に縮められるのです。 通常の画像分類の場合、とりあえずおまじない感覚でよいのでこれをコピペして流用しましょう。
 data_param 各種で、具体的な入力データの場所などを指定します。source は、現在のパスから見たLMDBフォルダの場所です。この場合、LMDBフォルダと同じ場所に train.prototext を置き、パスも同じ場所で訓練を行うという前提のもとでこのような書き方になっています。 もしフォルダが違う場所にある場合は、相対パスなり絶対パスなりで指定する必要があります。
 batch_size は、確率的勾配降下法(SGD)におけるバッチサイズを指定します。今回はとりあえず64にしてありますが、32でも128でも50でも適当な数にしておきましょう。 MNISTの場合、28x28しか画像サイズがなく、ネットワークも小規模なので、メモリをあまり消費しないという考えから64と大きめにしてあります。リッチな環境なら128でも256でも行けるかもしれません。 ただし、この数が大きすぎると、本当の最小値ではないところで解が落ち着いてしまう状況を偶発的に抜け出せるというSGDの良さが失われることになるため、バッチ数の大きさとSGDの恩恵は天秤にかけるようにしてください。
 backend は、 source で指定したデータベースが何の種類なのかを指定します。普通は LMDB なので LMDB と書いておけば問題ありません。なお面倒なことに、ここだけ "LMDB" のように "" で囲んではいけないので注意しましょう。
 説明のため色々書きましたが、結局画像分類をする時は source と batch_size くらいしかいじる場所がないので、一度動かすことができれば後はコピペ流用で問題ないかと思います。

畳み込み層(Convolution)

 畳み込み層は type: "Convolution" と書くことで設定できます。 bottom でこの層が受け取るデータを、top で処理後のデータの名前を指定します。 以下が train.prototxt の中にも書いてある、典型的な書き方です。
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  convolution_param {
    num_output: 20
    kernel_size: 5
    weight_filler {
      type: "xavier"
    }
  }
}
convolution_param の中で前回説明した畳み込み層のパラメータを指定していきます。num_output がフィルタの枚数、 kernel_size がフィルタのサイズです。
 weight_filler というのは名前の通り「重みの初期化方法」です。これは特にこだわりがなければ type: "xavier" をコピペして使いまわして下さい。 ニューラルネットにおいて、各パラメータは自動更新されるとしましたが、このような手法の場合、まずはパラメータを全部ランダムにしておくことが考えられます。 そのランダムの仕方の中でも、フィルターの枚数やユニットの個数に応じてランダムの幅を調節してくれるアルゴリズムがこの type: "xavier" です。 なので、多くの場合は weight_filler はこの type: "xavier" で放置しておいて問題ありません。 例えば、ランダムで初期化しない初期化法の1つとしては、「他のタスクで学習したニューラルネットのパラメータを流用して初期値にする」といったものがあります。

プーリング層(Pooling)

 プーリング層は type: "Pooling" と書くことで設定できます。 bottom でこの層が受け取るデータを、top で処理後のデータの名前を指定します。 以下が train.prototxt の中にも書いてある、典型的な書き方です。
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
pooling_param の中でパラメータを指定します。前回説明したように、プーリングにはいくらか種類があるので、 pool で指定できるようになっています。 これも "" で囲まない点に注意が必要ですが、MAXプーリングは pool: MAX で指定できます。プーリングの枠の大きさは kernel_size で、ストライドは stride で指定できます。
 枚数の指定場所がないことに注意してください。なぜならば、プーリング層は畳み込み層に一律処理をかける層であるため、畳み込み層の num_output と同じ枚数の出力が出てくるからです。

全結合層(InnerProduct)

 全結合層は type: "InnerProduct" と書くことで設定できます。 bottom でこの層が受け取るデータを、top で処理後のデータの名前を指定します。 以下が train.prototxt の中にも書いてある、典型的な書き方です。
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
  }
}
毎回名前が微妙に変わってややこしいですが、一度動けば基本コピペ改変で済むので我慢しましょう。 inner_product_param でパラメータの詳細を指定します。
 num_output でこの層のユニットの数を、weight_filler で初期化方法を指定しています。なお、最後の前結合層、つまり出力層の num_output は、分類したいカテゴリの数と同じにする必要があります。 今回の場合は 10 です。
 この層は名前の通り内積計算しかしない、つまり活性化関数の適用は含まれないので、後述の方法で活性化関数の層(Caffeの仕組み上、便宜的に層に含めることにする)と連結させる必要があります。

活性化関数(ReLU)

 本来であれば活性化関数は層には数えませんが、Caffeの構造上 layer として定義する必要があります。 要するに InnerProduct 層は文字通り内積(掛け算足し算)しかせず、その後活性化関数を通したいなら自分で定義して通しなさいということです。
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
書き方が特殊ですが、bottom と top を同じにして、 type: "ReLU" とすることで ReLU 関数を活性化関数として指定することができます。 これにより、 ip1 の出力は内積をとった後の値ではなく、 ReLU を適用した後の値に修正されます。

誤差関数(SoftmaxWithLoss)

 これも層ではありませんが、やはりCaffeの仕組み上 layer として定義する必要があります。典型的な書き方は以下のとおりです。
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip2"
  bottom: "label"
  top: "loss"
}
bottom は2つあります。1つめの bottom は、ネットワークの出力、2つめはそれと比較する正解データです。この場合は最初に Data 層から読み込んだラベルなのでその時名づけた label を指定しています。
 こう書くことで、自動で ip2 の10個の出力をソフトマックス関数により確率化し、正解ラベルとの違いを(交差エントロピー誤差関数という形で)計算して数値化してくれます。 ここで計算した正解ラベルとの違いの度合いをもとに、ニューラルネットはパラメータの更新を自分でかけます。
 前回少しだけ紹介したように、本来は勾配という微分計算を全パラメータに対して行う必要があるところ、バックプロパゲーション(誤差逆伝搬法)というテクニックにより、出力層側から入力層側に向かって単純計算だけでパラメータの更新を行います。 簡単に言うと、実はそれぞれのパラメータの微分は独立なものではなく、ある層 $l$ 手前 $(l-1)$ 層のユニットのパラメータに関する誤差関数の微分は、 $l$ 層のユニットのパラメータの微分が求まっていると、微分計算をしなくても自動的に単純計算で決まる、つまり微分を微分でないものの計算に置き換えられるということを、前回紹介したニュールネットワークの構造に照らし合わせて数学的に示したものです。 そしてその $l$ 層のパラメータは $(l+1)$ 層から、この層もまた $(l+2)$ 層から・・・という具合になるので、結果的には最終層のパラメータの微分が分かっていれば全部単純計算で済むことになり、しかもこの最終層も最終的には微分は不要(数学的に微分できて、その結果が微分を含まない形にできる)であるため、出力層側から値を決めて前方に向けて逆方向に伝搬されるということです。 以上の説明でなんとなく分かった気になれる方は、その辺の参考書で数式を勉強してください。実用上はデータの伝搬は入力→出力、パラメータの更新は出力方向→入力方向とさえ知識として知っておけば十分です。

solver.prototxtの書き方

 これで、訓練用のデータセットを使うCNN、およびテスト用のデータセットを使う同じ構造のCNNをひとつのファイル train.prototxt の中に書くことに成功しました。 あとは、このCNNを使って実際に訓練するための方針を示したファイルである solver.prototxt を別途作る必要があります。
 これも、最小限の量に抑えた solver.prototxt を以下に用意したのでダウンロードして LMDB や先ほどの train.prototxt と同じ場所に保存してください。
solver.prototxt
これがその中身です。
net: "train.prototxt"
base_lr: 0.01
lr_policy: "fixed"
max_iter: 5000
display: 100

test_interval: 200
test_iter: 100

solver_mode: GPU
 net で訓練する時、どの prototxt にするのかを指定します。今回のように訓練用、テスト用を同じファイルに書いた場合は net: "ファイル名" で良いですが、include を使わずに別々のファイルにした場合は次のように書いてください。
train_net: "訓練用prototxt"
test_net: "テスト用prototxt"
 base_lr は、学習率を指定します。これは適当に小さい値であればいいので最初は 0.01 や 0.001 くらいにして、あまりに繰り返しても進捗が見られない場合は10倍くらい、逆に誤差が爆発(莫大な値に張り付いて減らなくなってしまった)場合は1/10にしてみるなど試行錯誤しながら決めましょう。 無駄に小さいと、例えば本来であれば30分でできる学習内容なのに、3時間、ヘタしたら3日もかけてしまうことを意味します。
 lr_policy も必須の設定項目で、学習が進むに連れて、学習率をどうするかここで方針を指定します。ずっと変えない場合は lr_policy: "fixed" にします。 今回はそのようにしてありますが、一般には学習が進むに連れて小さくし、微調整をかけるという方法が取られます。
 max_iter は、何バッチで訓練を打ち切るか設定します。今回は5,000にしてありますが、少なすぎる場合、学習の成果が出る前に打ち切ってしまう可能性があり得るため、大きな値にしておき、適度な間隔でスナップショットを撮影しておくというのでもよいでしょう。 スナップショットの作り方などは、後の回で紹介します。
 display は、何バッチおきに訓練中のネットワークの誤差を報告するのかを設定します。ここでは 100 なので、100バッチ進むごとに、ネットワークの誤差が報告されます。  
 test_interval と test_iter はテストに関する方針を記載します。 test_interval は、何バッチ分訓練を進めたらテストを実施するかを記録します。 1の場合は1バッチ進むたびテストが行われますが、テストも全く計算コストなしに行えるものではないため、普通は何十回や何百回といった間隔が設定されます。 また、SGDで学習するため、細かくテストすることにはそれほど大きな意味はありません。なぜなら、短期的に見れば、誤差が激しく上下することがあり得るからです。 そのように上下するのはSGDを使う以上わかりきったことであり、あくまで重要なのは長期的に見た場合、一貫して誤差が下がる傾向が見られることなので、処理時間を無駄に増やさないためにも、少しは間を空けておきましょう。 ここでは200バッチ毎にテストをする設定にしてあります。
 test_iter では、テスト時には何バッチ分テストを実施するのかを指定します。これは、テスト用ネットワークの prototxt に記載したバッチ数で、ここに書いた回数だけテストが行われることを意味します。 今回はバッチサイズ100、 test_iter が 100 なので、合計10,000枚のテストを実施します。これはちょうどMNISTのテスト用データセットの全枚数と一緒です。 このように、 $ {\rm batch\_size} \times {\rm test\_iter}$ がデータセットの枚数を上回らないよう注意しましょう。  
 最後に solver_mode で CUDA による計算をするのか CPU で計算するのか指定します。GeForce GTX各種を使っている場合で、GPUモードで make した場合はこのまま GPU に、何らかの事情で CPU で学習したい場合は CPU にしましょう。

今回のまとめ

 今回は、prototxtの文法および、必要になる prototxt の種類について説明しました。 訓練用とテスト用のニューラルネットは別々に定義しなければならないものの、構造が共通なので多くの部分を流用する場合はひとつの prototxt に書いてもよいことも説明しました。 その後、solver.prototxt の書き方について紹介しました。
 これで学習の準備が整ったので、次回は実際に Caffe で学習を行う方法について説明します。

その他の記事

コメント

名前 :
電子メール :
URL :
コメント