記録は作業の証

鉄道とコンピュータ

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

ソースコード

ステップ14 同じ変数を繰り返し使う

微分を累算して、同じ変数を使っても結果がおかしくならないようにする。 オリジナルのコードにある次のコード片をCommon Lispにどう翻訳するか少し悩んだ。そのままだと(@gradient x)という式があちこちに出てくるし、aops:vectorizeマクロで書けないのである。

if x.grad is None:
    x.grad = gx
else:
    x.grad = x.grad + gx

解決策として、UIOPというライブラリで定義されたuiop:if-letというマクロを使うことにした。uiop:if-letは判定に使う値に名前を付けて、フォームの中で使えるようにしてくれるマクロである。 ここで、UIOPはよくある処理に対するユーティリティを多数提供するライブラリである。Common Lisp処理系には、CのMakeにあたるASDFというビルドツールが付属しており、UIOPはASDFの部品として同梱されているので、準標準ライブラリとして使える。

(setf (@gradient x)
      (uiop:if-let ((agx (@gradient x)))
        (aops:vectorize (agx gx)
          (+ agx gx))
        gx))

見直してみて気が付いたが、fletを使ってローカル関数を定義する、with-accessorマクロを使うことでも解決できそう。

微分をリセットするためのclear-gradientメソッドも用意した。

(defmethod clear-gradient ((var <variable>))
  (setf (@gradient var) nil))

gist.github.com

ステップ15 複雑な計算グラフ(理論編)

説明だけなので省略。

ステップ16 複雑な計算グラフ(実装編)

関数や変数に世代を覚えさせることで、逆伝播を正しく計算できるようにする。 そのためにgenerationというスロットを用意し、世代順にソートを行う。 Common Lispsort関数は実践 Common Lispの著者がいうところのリサイクルな関数なので、必ず返り値を受け取ってsetfする。

gist.github.com

実践 Common Lispは、載せられている事例が古びてきているものの、Common Lispを学ぶための最初の一冊として素晴らしいと思う。原書は無料公開されているので、読める人はぜひ。