Visual Studio 2010 C# を使って簡単ニューラルネットワーク

森下功啓製作所 ONLINE

Microsoft社が無償で配布しているVisual Studio 2010 ExpressのVisual C#を使って実行できるニューラルネットワーククラスライブラリについての備忘録です。
[2012/4/4]更新予定をアップ


1. はじめに

ニューラルネットワークのソースコードは結構ネット上にありますが、オブジェクト指向っぽいコードで日本語解説されたものは見当たりません。 そこでニューラルネットのクラスライブラリをC#で作ってみました。 なぜC#かというと、所属している研究室の実質的な標準言語がC#だったというだけです…。 まぁ、Visual Studioはデバッグ機能が充実しているのでコーディングが楽というメリットはあります。 (データのグラフ化は面倒というデメリットもありますが。) ライセンスは修正BSDとします。 どうぞ使ってみて下さい。 なお、本クラスはニューラルネットワークだけでなく、利便性を高めるために学習器と識別器のクラスも実装しています。


2. プログラムパッケージのダウンロード

プログラムを下記よりダウンロードしてください。 なお、実行形式ファイルの実行には.NET Framework 4が必要です。 開発にはVisual Studio 2010 Proを使っていますが、無償版のExpressでも利用可能です。

開発環境:Windows 7 Pro x86/x64, Visual Studio 2010 Pro
リリース日 プログラムパッケージ 更新内容 開発言語
2012/6/25 PatternRecognition_20120625.7z [更新内容]
  • 予告していた通り、Teacherクラスに学習の順序をランダムにする処理を実装しました。
  • Teacherクラスで交差確認ができるようになりました。
  • テスト用の静的クラスPatternRecognitionTestを整備しました。

[いつになるかも分からない更新予告]
  • WEKAフォーマットのモデルデータへも対応させたいなぁ?
Microsoft VC# 2010
2012/4/4 NA [更新予告]
更新時期未定の更新予告です。

次期リリースバージョンでは、学習器による学習をランダムに進めるようにするつもりです。 現時点ではユーザが用意した教師データを順番に学習していますので、学習順の影響が出てしまっています。 また、同じモデル内でも学習順が少なからず影響しているようですのでランダムに学習する様に変更予定です。 なお、本処理導入の影響評価が可能なように従来の学習方法も選択できるようにしたいと考えています。

と言っても、今後はかなり本業が忙しくなりそうなので更新時期は不明です。 ちなみに、学習器のクラスととニューラルネットのクラスを切り分けたのは他の識別器(例えばサポートベクタマシン)を搭載可能なようにするためです。 でもそっちまで手は回りません。 誰か改造して頂けませんか?
NA
2012/3/13 SampleOfNeuralNetwork_20120313.zip 更新したニューラルネットワーククラスライブラリとサンプルプログラムです。
ライブラリは、コンソールアプリでも利用可能なように、メッセージボックスで表示させていたエラー処理を例外を再スローする形に改めました。

サンプルプログラムはコンソールアプリに変更して余計なコードを省きました。 また、付属の学習データを統計で有名なIrisに差し替えています。
Microsoft VC# 2010
2012/3/12 NeuralNetwork_20120312.zip ニューラルネットワーククラスライブラリを更新しました。
ニューラルネットワークの出力が非値であった場合にDiscriminatorクラスで演算エラーとなっていたバグを修正しました。
Microsoft VC# 2010
2012/1/7 NeuralNetwork_20120107.zip ニューラルネットワーククラスライブラリです。
ニューラルネットサンプルコード

C#によって組んだ4つ目のプログラムだけあって大分慣れてきました(^o^)
[2012/3/13追記]諸事情により1/7リリースしたサンプルコードのダウンロードを停止しました。 以降は2012/3/13アップのサンプルコードを参照してください。
Microsoft VC# 2010

3. ニューラルネットワークを利用した識別とは

ニューラルネットワークは一般的に関数近似やクラスの識別を行うのに利用されるアルゴリズムです。 ここでは関数近似の方は置いておいて、クラスの識別について説明したいと思います。

「識別」とは、例えば成体のガマガエルとアマガエルを一つの箱に入れてそこから一匹ずつ取り出したときに機械的に分類する作業を指します。 この例において、クラスは「ガマガエル」と「アマガエル」になります。 なお、識別には特徴データが必要です。 カエルの例では体長が有効な特徴となり得ます。 つまり、システムに計測した体長として35mmを入力すると「アマガエル」と出力されるといった具合です。

より一般的には、ニューラルネットワークを利用した識別には図3.1に示すように3つの過程があります。 始めは特徴データを抽出する過程です。 次に抽出した特徴データを用いてニューラルネットを学習させる過程です。 最後に学習した結果を用いて未知データを識別する過程です。 この中でニューラルネットワークが関係するのは学習と識別の過程になります。

ニューラルネットワークを利用した識別の3つの過程
図3.1 ニューラルネットワークを利用した識別の3つの過程

特徴抽出・学習・識別の各々の過程の流れを図3.2に示します。 特徴ベクトルの生成段階では、例えば音声や画像から識別の役に立つデータを抽出します。 学習では、それぞれのクラス(例えばガマガエルやアマガエル)から抽出した特徴データを「教師データ」として学習器を通してニューラルネットワークを教育します。 そこで得られたニューラルネットの結合係数を識別器にセットすれば識別の準備は完了です。 未知の特徴データを入力すれば、そのデータがどのクラスに属するデータであるかを知ることができます。

各段階の流れ
図3.2 各段階の流れ


4. 最新版の使い方

特徴データを生成する方法は識別対象に固有の処理ですのでここでは取り扱いません。 以下の節ではダウンロードコーナから入手できるプログラムの説明します。


4.1 公開プログラムに含まれる主なクラス(ここでいうクラスはプログラミング言語のクラスです)

以降の説明の前準備として、設計したクラスを紹介します。 まずは図4.1.1にニューラルネットワークにとって基本となるクラスを示します。 中には入れ子になったクラスもありますが、気にしないでください。 使用する際に特に意識することはありません。 なお、アイコンに施錠マークがついているものは外部のクラスには非公開のメンバを表しています。

ニューラルネットワークの基本クラス群
図4.1.1 ニューラルネットワークの基本クラス群

次に、識別器と学習器を図4.1.2に示します。 プログラマはこの2つのクラスさえ意識すればOKです。

識別器と学習器
図4.1.2 識別器と学習器


4.2 学習に使用する特徴ベクトルの準備

まずはニューラルネットワークを教育するのに必要な特徴データを特定の一つのフォルダにまとめて下さい。 その際、ファイル名を「クラス名.fea」とし、ファイルの中身は1行に1つの特徴データを格納します。 特徴データは図4.2.1に示すようにタブ(\t)やカンマ(,)や半角スペース( )で区切ればOKです。 なお、ファイル名はクラス名に_アンダーバーを続けて任意の文字列を付けることもできます。 例えば、「アマガエル_熊本で収集.fea」の様になります。 ちなみに学習に使用する特徴データのことを教師データと言ったりします。

クラス名はファイル名から取得されます。 例えば、ファイル名が「アマガエル_熊本で収集.fea」であれば、クラス名は「アマガエル」となります。 将来的にはスーパークラス(例えば「カエル」)とサブクラス(例えば「アマガエル」)をファイル名から入力できるようにするかも知れません。

ニューラルネットの入力層のユニットは、.feaファイル内の特徴データの次元数と同数が自動的に用意されます。 また、出力層のユニット数はクラスの数と同じだけ用意されます。

特徴ベクトル
図4.2.1 特徴ベクトル

4.3 学習および学習成果の途中確認

では教師データが揃ったところでニューラルネットワークを学習させましょう。 学習にはNeuralNetTeacherクラスを使用します。 図4.3.1に学習器を利用した学習の例を示します。 1行目で学習器のインスタンスを確保しています。 コンストラクタの引数は最低限のパラメータである中間層数と中間層のユニット数と学習係数です。 2行目では教師データを格納したフォルダ名を渡して教師データを読み込ませています。 フォルダアドレスは相対アドレスでも絶対アドレスでも構いません。 ただし、相対アドレスはWindows XPだと上手く行かないかもしれません。 3行目では学習係数をセットし、後はforループを使用して学習を実施させています。

学習器を利用したニューラルネットワークの学習
図4.3.1 学習器を利用したニューラルネットワークの学習

学習の進み具合はCSV形式の判別表を出力させることで確認できます。 図4.3.1のソースコードでは7行目に当たります。 そのファイルをExcelで開いた様子を図4.3.2に示します。 縦軸が入力モデルで、横軸が出力モデルです。 例えばキビタキ(鳥の名前)に注目すると、教師データの内2つはコジュケイと誤って判定されていることが分かります。

判別表
図4.3.2 学習結果を使用した判別表

4.4 学習結果をファイルとして出力する

学習が終わったら、その成果を保存しましょう。 学習の結果は任意のファイル名で保存可能です。 図4.3.1では9行目に当たります。 引数を指定しなければデフォルトのファイル名で保存されます。 出力されるファイルの例を図4.4.1に示します。 クラス名やニューラルネットワークの構成パラメータや結合係数をテキスト形式で保存しています。

学習結果
図4.4.1 学習結果
学習の結果は独自フォーマットのテキストデータとして出力される。

4.5 識別する

始めは図4.3.1のソースコードを利用して、学習器が保存したファイルを読み込ませる方法で識別器を構成する方法を説明します。 まず11行目で示しているように、コンストラクタの引数なしで識別器のインスタンスを生成させます。 次にSetup()メソッドを呼び出すことで保存された学習結果ファイルを読み込ませて識別の準備を行います。 デフォルト以外のファイル名を利用した場合はSetup()メソッドの引数に保存したファイル名のパスを渡せばOKです。


では次にインスタンス生成時に学習器から受け取ったニューラルネットワーククラスのインスタンスを受け渡す方法を説明します。 この方法は図4.5.1を使用して説明します。 この図では3行目で識別のテストに使用するデータを用意しています。 Featureクラスのインスタンス生成時に特徴ベクトルを入力した文字列を渡して初期化しています。 識別器のインスタンスは6行目でニューラルネットワークのインスタンスと出力のクラス名(「アマガエル」等のstring型配列)を渡す事で生成します。

識別結果はDiscriminatorクラス内に宣言しているIDandLikelihood構造体として返ります。 この構造体は答えのクラス名(「アマガエル」とか)と尤度をセットにしたものです。 7行目で答えを格納する配列を宣言しつつ答えを格納しています。 その際、引数を特徴ベクトルのみとすることで一番尤度の高い結果だけ(この場合、返値は配列ではないことに注意)を得ることもできます。

識別器を使った識別のテスト
図4.5.1 識別器を使った識別のテスト

以上で学習と識別クラスの説明は終わりです。 なお、学習器と識別機に同じニューラルネットワーククラスのインスタンスを使えば…学習させながら識別も可能です。 ・・・まぁ、途中で識別対象クラスが増えるようなケース(例えばアマガエルとガマガエルで学習した途中でさらにツチガエルが増えるようなケース)には現時点では対応できないのですが。


5. サンプルコード(ダウンロードコーナにおいているサンプルから抜粋)

学習クラスを用いずに実現できる範囲で学習の例を示したいと思います。 個人的には関数近似の方に興味があるので、将来的にはここは充実させたいと考えていますが、それもいつになるやら…。


5.1 XORの学習

ニューラルネットワークでは定番のXORを学習させるサンプルコードを図5.1.1に示します。 学習器を使用しない場合、ニューラルネットワークのパラメータをまず設定します。 入力層のユニット数や中間層のユニット数などです。 パラメータはNeuralNetworkクラス内に宣言したParameter構造体で構成します。 それを引数としてNeuralNetworkクラスのインスタンスを確保すれば、後は特徴ベクトルと教師ベクトルを入力するだけで学習できます。 学習させずに結果を得るには、Recognize()メソッドを呼び出して下さい。

学習結果である結合係数はSave()メソッドによってテキストファイルに保存できます。 逆に学習済みの結合係数を読み込ませるにはSetup()メソッドによって実施します。 任意のファイル名を引数に渡す事ができます。 省略された場合はNN.iniというファイル名で保存・読み込みが行われます。 読み出しに失敗すると例外をスローしますので安全のためにtry-catch構文を使った方が良いかもしれません。

XORの学習
図5.1.1 XORの学習

5.2 sin関数の学習

0~1の間しか学習しませんが、sin関数を学習させるサンプルコードを図5.2.1に示します。

sin関数の学習
図5.2.1 sin関数の学習

参考文献

inserted by FC2 system