記録は作業の証

鉄道とコンピュータ

ゼロから作るDeep LearningをCommon Lispで書き直す(ステップ1~3)

はじめに

Common Lispで深層学習コンパイラを作っている人に感銘を受けたので、プログラミングの練習のためにと自分もCommon Lispで似たものを書いてみようと思い、この本を買った。

本のソースコードはここで公開されている。 github.com

先駆者の方々も何人かおられるようなので、参考にする。 msyksphinz.hatenablog.com github.com

github.com

実装の方針

行列を表現するなら、PythonであればNumPy一択だが、Common Lispでは少し考える必要がある。 最初の数ステップを書き直しながら、次のライブラリを試した。

  • numcl

    • NumPyに似せたAPIが使えるというライブラリ。Lisp力が低くてうまく動かせず。
  • mgl-mat

    • BLASやCUDAを使って高速な計算ができるというライブラリ。足し算や掛け算をするためにaxpy!gemm!を使ってみたものの、計算したい行列のほかに計算結果を格納するための行列を用意する必要があり、計算式を書くのも少し難しかったので、途中でやめてしまった。
  • Common Lisp標準の多次元配列
    • msyksphinzさんの記事ではRubyArrayをそのまま使っていたので、Common Lispでも同じように自分で書いてみようとした。多次元配列の生成や計算の書き方で自分が詰まってしまうことがあった。
  • array-operations
    • Common Lisp標準の多次元配列を生かし、機能を拡張する関数やマクロを提供するライブラリ。直感的に使える関数やマクロが多く、特にvectorizeマクロが便利で扱いやすかった。

array-operationsが自分に合っていると感じ、自作のルーチンも使いながらプログラムを書くことに決めた。 テストフレームワークFiveAMroveで迷ったが、とりあえずroveを使用することにした。

ソースコード

ステップ1

github.com

オリジナルの書き方に似せるため、規約を設けることにした。

  • クラス名は<>で囲う。
    • 今後出てくるクラスとしてexpfunctionという名前があるが、これはcl:expcl:functionと衝突するため。
  • インスタンスを作るラッパー関数を用意し、クラス名と同じ名前を付ける。
    • make-instance関数を何度も呼ぶのは少し面倒だし、map関数で複数の値をオブジェクトに包む場合は(lambda (x) (make-instance...)と書かなければならないため。
    • Pythonのようにクラス名の後にコンストラクタに渡す値が書けるとすっきりして見える、気がする。
  • アクセサは@で始まる名前を付ける。
  • クラスはファイルの先頭寄りにまとめて記述する。
    • アクセサを使う場所より後にアクセサを含むクラスを定義すると、そのような関数は定義されていないという警告が出るため。

ステップ2

github.com

functionという名前はcl:functionと衝突する。defpackage:shadowを使ってもよかったかもしれない。

ステップ3

github.com

行列の計算のためにarray-operationsを導入するが、このぐらいは自分で書いてもよかったかもしれない。