Sansan Builders Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

歴史をたどってディープラーニングを学ぶ第十三回 コードをGitHubで公開してみる。パーセプトロン編

こんにちは、ニューラルネット老人こと糟谷勇児です。
今回はニューラルネット老人というよりGitHub老人というところでしょうか。

というわけで今回はGitHubでニューラルネットのコードを公開していきます。C#でディープラーニングをしているプロジェクトってほかにあるのかなと調べたら、
github.com
KelpNetというプロジェクトがありました。偉大な先人です。
じゃあもう公開しなくていいかな。でもたぶんやりたいことや目的が違うから参考になる人はいるのかなと思って公開していくことにします。


私は機械学習アルゴリズムを自分で実装することを結構重視していて、学生当時はもちろんライブラリがなかったのでSVMやHMMなど一通り自作していたわけですが、仕事でも自作のライブラリを使った機械学習を本番環境で活用しています。
学ぶという目的では、やっぱり時間があるならゼロから作るのが一番理解できるのと、多くの人はそこまでやらないので自分ならではの発見があります。これが研究につながる可能性を秘めていると思っています。
もう一つは痒い所に手が届くので、実際の利用で一般的なライブラリより有利なことがあります。ただ、大規模なデータに適用する場合は世の中のライブラリのほうがスピードも速くバグも少ないので、小規模なデータに使いたいけど特殊な運用が必要になるような用途に限定ですが、その場合は活躍する可能性もあるかなと思っています。

Pythonでやったほうが楽なのではという話もあるのですが、そうすると速度面でどうしてもnumpyなどのライブラリに頼らざるを得ず、for文でがりがり書くことができなくなってしまい、中まで作った気がしないのでC#を使っています。
C#はunsafeにするとポインターが使えたり、Vectorクラスに詰めるとSIMDが使えたりして、頑張ればそれなりに速度が出るのと、C言語ほどセグメンテーションフォルトを起こさないのがいいところです。

パーセプトロンのコードの公開

公開に際して考えないといけないこと

一度に全公開したいなという思いもあるんですが、結構公開に際して考えることが多いので、ちょっとずつやっていこうと思います。本命のコンボリューショナルネットについては次回に公開していこうと思います。

考えることの一番大きいこととして、ライセンスに関することがあります。
これまで、MNISTやCifar10などのデータセットを使用していましたが、こちら再配布OKともNGとも書かれていないライセンスのようです。
そう考えると、勝手にzipに固めてプロジェクトにおいてしまうのはよくないですよね。
とはいえ、せっかく公開するならGitHubからcloneしてきたらすぐにデータセットで試せるようにしたいとも思います。
自分でデータをダウンロードしてきたらファイル名がバージョンアップで違っていて、これでいいのかなんて思って書き直して、
なんか動いている状況にするみたいなことって結構あるかもしれません。でも、そこからよくわからないとソース読んでみようとか
使ってみようという気にもならないと思います。

とはいえ、この辺りはちょっと妥協して、ファイルをダウンロードしてきてもらうような形でまずは公開して、後々直していこうかと思います。

次に考えないといけないこととしては、このプロジェクトのライセンスです。
私としては自由に活用してほしいのですが、再配布/商用利用OKともNGとも書かないと逆に活用されないかなと思ったので一応BSD2条項ライセンスにしてみました。この辺り何がいいのか私もわからないのでアドバイスありましたらお願いいたします。

あとはプロジェクト名ですが、HomeMadeNeuralとしてみました。手作り感を大事にしたいと思って。ステラおばさんのクッキー的なホームメイド感を出していきたい。

パーセプトロンのコードの特徴

こちらは出来上がったものをライブラリとして提供するという目的ではなく、あくまで作る過程を体験することで理解を深めるということを目的にしようと思います。なので、一旦モデルの保存機能など実用には必要だけど機械学習の様子を観察するだけならいらない機能はバッサリ落としました。
まあ、どうせあとで必要になると思うのでその時にまたつけようと思います。

また、出来上がった最終形が一つあるのではなく、これまで作ってきたものを別ファイルで公開していこうと思います。

  1. 3層だけのパーセプトロン
  2. N層に拡張したパーセプトロン
  3. N層パーセプトロンの各層をオブジェクト(クラス化)にしたパーセプトロン

今回は上記の3つを公開していきます。
おそらく、ニューラルネットを実際に作ってみようと考える方は、これらを順に作っていったほうが、いきなり最終形から作るより効率がいいんじゃないかなと勝手に思っています。
だいたいコピペでできるので3倍時間がかかることはないです。

※このように作ったものを順番に公開していくので、DRYなコード(同じことを二回書かないコード)にはあまりこだわっていません。

3層のみのパーセプトロン

3層のみのパーセプトロンを作るときはバックプロパゲーションを意識する必要はあまりありません。
2層目から3層目の重みの更新式と1層目から2層目の重みの更新式をそのまま書けば作れます。
なのでこれは誰でも半日ぐらいでできると思います。ちなみにこのプログラムは私が学部4年ぐらいの時にJavaで書いたものをC#に移植したものなので結構ひどいものです。

github.com


N層に拡張したパーセプトロン

何層でもOKにするには、バックプロパゲーションの特徴である、一つ上の層の微分値を使って次の層の微分値を漸化式で
導出するという実装が必要になります。
これをfor文で実装します。漸化式はプログラムに落とすのが簡単なのでこれも簡単なはずです。

github.com

クラスを用いたN層パーセプトロン

さて、DeepLearningがこれで終わりならいいですが、この先、コンボリューション層やプーリング層も増えてきますので、増えるたびにforの中にif (プーリング層)と書いていくと読みにくくなるのと、コンボリューション層を実装するときに間違ってプーリング層部分のコードを書き換えて動かなくなってしまうなど、開発が難しくなってしまうことが懸念されます。
そこで、ポリモーフィズムという、ザックリいうとクラスのインターフェースさえそろえておけば、コンボリューション層のオブジェクトはコンボリューション層として、プーリング層のオブジェクトはプーリング層として働き、意識しなくても勝手に動いてくれるような方法にしたいと思います。

今回はその前段階として、全結合層をクラス化し、パーセプトロンが全結合層クラスをリストで保持するような方法を取ります。

github.com

実験とテスト

プログラムを動かすとだんだん誤差が減っていることが分かるかと思います。
f:id:kasuya_ug:20210104000200p:plain
たぶんそれっぽく動いているのですが、本当に大丈夫でしょうか。

ここではテスト方法の一つを紹介します。
今回作ったどのパーセプトロンも数学的に同じ性質を持っているとしたら、同じ条件で動かせば「ほぼ」同じ結果になるはずです。
ただし、丸め誤差などによって計算順序で浮動小数点数の計算結果が異なってくるので、完全には一致しません。
そこで有効数字5桁が一致していればOKとしました。

github.com

f:id:kasuya_ug:20210104180045p:plain
テストによって、同じ条件で動かしたとき、ほぼ同じ結果になることが分かります。
ニューラルネットの計算において添え字が違っているとかプラスマイナスが違っているレベルの大きなミスがある場合は、
このように同じ結果にならないでしょう。
もし違う結果になったら、何をしたところで値が変わるのかデバッガで追っていけば間違いを正せます。

まとめと次回は

今回はパーセプトロンのコードを紹介しました。テスト方法など参考になれば幸いです。
ニューラルネットをプログラミングしようと思うと、今まであまり使わなかったプログラミングの機構を使わざるを得ず、勉強になります。
次回はコンボリューショナルネットのコードを公開していこうと思います。
まずはベーシックなLeNet(の一部さぼって作った)のコードを載せていこうと思います。
できたら、AlexNet, VGGなどもやっていきたいですね。




▼本シリーズのほかの記事はこちら
buildersbox.corp-sansan.com

© Sansan, Inc.