DNN の基礎を学習する
承前
LLM アプリケーションの構築に何度か携わったのですが、その結果として LLM の面白味はプラットフォーム各社が提供する LLM サービスを単に利用するだけではわからないという結論に至りました。そのため、今更ながら DNN の基礎部分から勉強し直したいと思います。
この記事では以下の2冊を並行して読み進めます。
前者は割と有名で評判のよい本ですが、冒頭を軽く眺めたところ、もう少し理論面にもしっかり踏み込んでほしい感じがしたので、家に転がっていた後者を副読本として採用することにしました。初版発行が 1993 年と少々古いですが、逆に現代では省略されそうな事柄についてもよく書かれており、個人的には気に入っています。
なお、ゼロから作る Deep Learning では Python を使った実装が示されていますが、この記事では Haskell を使用します。理由は単に筆者が Haskell を書くのが好きだからであり、それ以上の意味はありません。
生理学的なニューロン
ニューラルネットワークは脳の構造をベースにしているので、生理学的なニューロンの特定について知っておくと今後の学習の参考になる。
生理学的なニューロンは細胞体・樹状突起・軸索の3つの部位で構成され、細胞体から多数の樹状突起と一本の軸索が枝のように伸びているという形状をしている。
機能的には、信号が樹状突起から入り、細胞体で処理され、軸索から出ていくという構成になっている。それぞれの樹状突起から入った信号は細胞体内部の電位を高め、これが一定値に達すると細胞体はインパルスと呼ばれる急激な成長と減衰を伴う電気波形を形成する。そしてこのインパルスが軸索を伝って外部に出ていく、というのが基本的な流れである。なお、形成されるインパルスの波形は細胞体の電位の高さに依らず一定で、樹状突起からの入力信号がいくら大きくなっても出力されるインパルスの波形には影響しない。
軸索の先端は多くの枝に分かれており、これが他のニューロンの樹状突起に接続することで巨大なネットワークを構成している。この軸索と樹状突起の接続部分はシナプスと呼ばれ、シナプスには軸索から出てきた信号の大きさを強めたり弱めたりするフィルターのような機能がある。
ニューロンには以下の特性がある:
非線形性
樹状突起からの入力信号の大きさが一定値を超えない限りインパルスは形成されず、またインパルスが形成された場合でも樹状突起からの入力信号の大きさによらず波形は一定である。従って、生理学的なニューロンは非線形な素子であると解釈できる。
二値性
非線形性の項で述べたのと同じ理由により、軸索からの出力はインパルスが形成されるかされないかの2通りしかない。従って、生理学的なニューロンは二値の信号を扱う素子であると解釈できる。
空間的加算性
ニューロンがインパルスを形成するのは、それぞれの樹状突起から入力された信号の大きさの和が一定値を超えた場合であるとされている。従って、軸索を伝って樹状突起へやってくる信号の大きさを
がある閾値を超えるかどうかによってインパルスが形成されるかどうかが決定される。この性質は空間的加算性と呼ばれる。なお、インパルスの形成に関わる閾値はニューロンごとに異なる。
時間的加算性
ある時間における軸索の信号の大きさはそれより後の時間における同信号の大きさに影響を与えることが観察されている。従って、軸索の信号の大きさを時間の関数と見なして、時刻
がある閾値を超えるかどうかによってインパルスが形成されるかどうかが決定される。ただし、上記の
時間的加算性についてはゼロから作る Deep Learning では言及すらされていないが、昨今注目されているエージェントなどの概念は時間的な広がりを持つため、研究してみる価値はありそうに思える。
ニューロンのモデル
生理学的なニューロンを参考に様々なモデルが考案されている。
モデル以前
先に紹介したニューロンの特性の中でも空間的加算性は特に重要な特性と見なされており、以下に紹介する全てのモデルにおいて採用されている。そのため、モデルの紹介に先んじて空間的加算性に関する共通部分について説明しておく。
空間的加算性によると、軸索から樹状突起へと入力される信号の大きさを
が一定の値を超えた場合にインパルスが形成されるのであった。従って、その閾値を
によってインパルス形成の条件を表現できるが、ここで更に
として閾値
この式変形には式の右辺からニューロンに依存する閾値
式
ここで
一方で
のように
静的デジタルモデル
ニューロンが形成するインパルスの電気波形はニューロン内部の電圧に依らず一定であることから、ニューロンにはインパルスを形成したかしなかったかの2通りしか状態が存在しないと考えられる。この点に着想を得て、活性化関数
より具体的には、集合
と表せる場合に
このモデルはニューロンの時間的加算性を無視しているという意味で静的であり、またニューロンの非線形性・二値性を 0 と 1 による非連続関数で表しているという意味でデジタルである。
上記では静的デジタルモデルの構築に 0 と 1 を用いたが、代わりに -1 と 1 を用いても同様のモデルを構築することができ、さらにこの2つのモデルは等価である(一方のモデルの関数が与えられれば、同じ振る舞いをする関数をもう一方のモデルで構築できる)ことが証明できる。証明の詳細については割愛するが、それほど難しい内容ではなく、基本的に [0, 1] と [-1, 1] の2つのスケールの相互変換について考えることで自然に証明できる。
動的デジタルモデル
上記の静的デジタルモデルにニューロンの時間的加算性を加味することすることで別のモデルが得られる。つまり、入力
によってモデルを構築する。これを動的デジタルモデルという。
動的デジタルモデルは有限オートマトンと等価であることが Kleene による こちら の論文で証明されている。この証明が提出されたのは 1956 年とのことで、先人の先進性の凄まじさが感じられる。
静的アナログモデル
我々は普段、音や光などの物理量を連続量として感じ取ることができる。生理学的な研究によると、これは単位時間あたりにニューロンがインパルスを形成した数、即ちパルス密度に対応した感覚であると考えられている。この観察に着目し、入力
この関数は S 字のような曲線を描くことからシグモイド関数と呼ばれる。シグモイドとはアルファベットの S に対応するギリシャ文字の Sigma と「〜のような形状の」という意味を持つ -oid を組み合わせて作った用語である。実際の曲線の形状については Wikipedia を参照するとよい。
こちらも静的デジタルモデルと同様に、各変数の取る範囲を [-1, 1] とし、シグモイド関数を以下のように定義することでモデルの亜種を作成することができる。
こちらについても [0, 1] と [-1, 1] の範囲で作成したモデルはそれぞれ等価であることが証明できる。基本的な証明方針は静的デジタルモデルと同じで、
生理学的なニューロンに比べると二値性が排除された形になるが、ゼロから学ぶ Deep Learning で使用されているのは [0, 1] の範囲で作成された静的アナログモデルであり、おそらくこのモデルが現代で最もベーシックなモデルであると考えられる。
動的アナログモデル
静的デジタルモデルから動的デジタルモデルを導いたのと同様に、静的アナログモデルから動的アナログモデルを導くことができる。静的アナログモデルで定めた
によってニューロンの時間的加算性を考慮した1つのモデルを得ることができる。
一方で、アナログモデルは連続なシグモイド関数を使用するため、上記のような離散的に変化する時間を用いずに連続的に変化する時間を用いて動的モデルを考えることもできる。このモデルと区別するために、上記の離散的な時間変化におけるモデルは離散時間型動的アナログモデルと呼ばれる。
では連続的に変化する時間を用いた場合、モデルはどのように定義されるかというと、時間あたりの変化量が以下の微分方程式に従うと仮定する。
従って、このモデルで各時間における
このモデルを連続時間型動的アナログモデルと呼ぶ。結果を得るのがあまりに面倒なので使いたくはない。
ニューロンと論理回路
この章では Haskell を使ったニューロンによる論理回路の実装まで行っていくが、先んじて静的デジタルモデルの雛形を用意しておく。静的デジタルモデルの定義に従って線型結合とステップ関数を定義すればニューロンのモデルの定義は容易である。
-- Linear Combination
lc :: Num a => [a] -> [a] -> a
lc xs ys = sum . map (uncurry (*)) $ zip xs ys
step :: (Num a, Ord a) => a -> a
step u
| u >= 0 = 1
| otherwise = 0
neuron :: (Num a, Ord a) => [a] -> [a] -> a
neuron ws xs = step $ lc ws (1 : xs)
AND
まずは先ほど定義したニューロンのモデルを使って AND 回路を実装してみる。
AND 回路では2つの入力
今回は
and :: (Num a, Ord a) => [a] -> a
and = neuron [-2, 1, 1]
GHCi でテストした結果は以下の通り。なお、型注釈は警告抑制のためのものである。
ghci> print (and [0, 0] :: Double)
0.0
ghci> print (and [0, 1] :: Double)
0.0
ghci> print (and [1, 0] :: Double)
0.0
ghci> print (and [1, 1] :: Double)
1.0
OR
同様に OR 回路も実装してみる。重みには
or :: (Num a, Ord a) => [a] -> a
or = neuron [-1, 1, 1]
結果は以下の通り。
ghci> print (or [0, 0] :: Double)
0.0
ghci> print (or [1, 0] :: Double)
1.0
ghci> print (or [0, 1] :: Double)
1.0
ghci> print (or [1, 1] :: Double)
1.0
NOT
NOT 回路も実装しておく。
not :: (Num a, Ord a) => [a] -> a
not = neuron [0, -1]
結果は以下。
ghci> print (not [0] :: Double)
1.0
ghci> print (not [1] :: Double)
0.0
XOR
ここまで順調に実装してきたので XOR も同様に実装できそうに思えるが、実はそうはいかない。これは XOR の重みが満たすべき条件を代数的に整理してみるとすぐにわかる。
XOR は入力
を満たすので、これから条件
を得る。他方、入力
を満たす。従って、
を得る。ここで
が得られるが、明らかにこの2式を同時に満たす重みの組
より俯瞰的な視点で見ると、論理回路の構成は入力となる
は
XOR は線形分離不可能な出力を持つ論理回路の好例で、これが単一のニューロンで実現できない様子については以下の Qiita の記事に掲載されている画像を見るとわかりやすい。
多層化
しかしながら、AND・OR・NOT が既に実装されているので、そこから XOR を構成できるという点に気がついた人も多いだろう。実際、
nand :: (Num a, Ord a) => [a] -> a
nand = neuron [1, -1, -1]
ghci> print (nand [0, 0] :: Double)
1.0
ghci> print (nand [0, 1] :: Double)
1.0
ghci> print (nand [1, 0] :: Double)
1.0
ghci> print (nand [1, 1] :: Double)
0.0
あとは NAND と OR を組み合わせて XOR を実装すればよい。
xor :: (Num a, Ord a) => [a] -> a
xor xs = and [nand xs, or xs]
ghci> print (xor [0, 0] :: Double)
0.0
ghci> print (xor [0, 1] :: Double)
1.0
ghci> print (xor [1, 0] :: Double)
1.0
ghci> print (xor [1, 1] :: Double)
0.0
このように複数のニューロンを組み合わせて多層構成をとることで XOR をはじめとする線形分離不可能な出力を持つ論理回路も実現することができる。
多層構成のニューロンがどれくらいの表現能力を持つのかが気になるかもしれないが、実はこれに答えるのは簡単である。というのは、NAND 回路には完全性があり、どんな論理回路も NAND 回路を使って組み上げることができることが知られている。従って、先ほどしれっと行なった NAND 回路の実装により、静的デジタルモデルのニューロンの多層構成で任意の論理回路が得られることがわかる。
加法標準形
先ほどは2層構成のニューロンで XOR 回路を作成したが、実は2層構成のニューロンだけでどんな論理回路も作れることが証明できる。これは任意の論理式が加法標準形という形式の同等な式に書き直すことができ、かつこの加法標準形の式は2層構成のニューロンで実現可能であるという事実に由来する。
のような加法標準形に書き直すことができる。加法標準形は
加法標準形の
また
従って、任意の論理式は2層構成のニューロンで作成可能であることがわかる。
古典パーセプトロン
現代の DNN は Rosenblatt が 1958 年に発表したパーセプトロンと呼ばれるモデルをその起源とする。パーセプトロンは視覚系のパターン認識のためのモデルとして開発されたモデルで、感覚層・連合層・反応層の3つの階層からなる。Rosenblatt が書いた 論文 にはその様子を表現した図が掲載されている。

図の一番左が網膜を表しており、網膜から入ってきた信号が感覚層 (Projection Area) に渡り、そこからさらに連合層 (Association Area) を経由して反応層 (Responses) に伝わる様子が描かれている。
上の図では網膜も含めた4つの層が描かれているが、情報処理の観点では網膜は重要でないので省略されるのが一般的である。また、上の図では反応層に複数のニューロンが描かれているが、元々パーセプトロンは二値分類問題のためのモデルであり、反応層には1つのニューロンしか存在しない(即ち反応層のニューロンが 0 と 1 のいずれの値をとるかで二値分類の機能を果たせる)とする定義になっている。
感覚層・連合層・反応層の間で行われる処理は、先に述べた静的デジタルモデルに従う。つまり 0 か 1 のいずれかの値のみを取る感覚層の値
によって決定される。シナプスによる作用を表す重みはニューロンごとに異なるため、重みの添字に
同様に連合層の値
となる。ここで
なお、上で述べた
とし、同様に
とすれば、パーセプトロン全体を合成関数
先述の通り、パーセプトロンは視覚系のパターン認識に関わる二値分類のために考案されたモデルではあるが、現代では DNN をはじめとするより広範な用途の基礎として用いられている。そのため、視覚系に起源を持つ感覚層・連合層・反応層という名前は廃れ、現代では入力層・隠れ層・出力層という名前で呼ばれている。この記事でも以降はこの呼称に従うことにする。
また、モデルの提案当初は静的デジタルモデルが採用されていたが、現代では静的アナログモデルの方が主流となっている。そのため、ニューロコンピューティングの数学的基礎では、静的デジタルモデルを採用したオリジナルのモデルを古典パーセプトロンと呼び、静的アナログモデルを採用したモデルを現代パーセプトロンと呼んで区別している。この記事でもこの呼称に従うことにする。
古典パーセプトロンの実装
早速実装してみる。
perceptron :: (Num a, Ord a) => [[a]] -> [a] -> [a] -> a
perceptron wss vs xs =
let ys = map (\ws -> neuron ws xs) wss
in neuron vs ys
リストを使用している影響で若干型定義の内容が分かりづらいが、要素数を明示できる依存型 Vect を使った擬似コードで表すと多少マシになる。
perceptron :: (Num a, Ord a)
=> (Vect m (Vect n a)) -- w_ij
-> (Vect m a) -- v_j
-> (Vect n a) -- x_i
-> a -- z
もちろんこの時点で実行可能ではあるが、結果が非常に分かりづらいので、後で学習アルゴリズムを実装した際にまとめて確認することにする。(ちなみにここからしばらくは理論面の記述が続く)
重みの決定に対する方程式的アプローチ
古典パーセプトロンは重みによって振る舞いが決定される
を満たすような重みを決定する方法を見出したいと考えるのは自然なことである。
すぐに思いつく方法は古典パーセプトロンの式が与える連立方程式を解くことであるが、
が満たされれば目的の二値分類が果たされる。ただし、
上記の式を満たすためには
を満たす必要があり、これが解くべき連立方程式である。これで式としてはかなり簡単になったが、実はそれでもこの連立方程式を代数的ないし解析的に解くのは困難である。[1]
古典パーセプトロンの式を直接解くのはあまりにも難しいので、ここで方針を転換して直接的な解ではなく近似解を得る方法を模索してみる。つまり、
を定義し、これを
からなる二乗誤差としておく。右辺が 2 で割られているのは後の計算を簡単にするためで、深い意味はない。
近似解を求める最もナイーブな方法は誤差関数を微分して極点を求めることである。つまり
を方程式として解きたいところだが、残念ながら
非線形関数が強すぎる ... 。
重みの決定に対する力学的アプローチ
古典パーセプトロンに関する方程式を解いて解を得るのは絶望的なので、ここで発想の転換を行い、
右辺はいかにも恣意的である。実際、これは時間
が成り立つ。これは実際に
である。この式から等号が成り立つのは全ての
この性質によれば、誤差
とはいえ、微分方程式を真っ当に解くのはやはり絶望的なので、ここで
という近似を施す。微分の定義から極限を取り去り、代わりに十分小さい値
という漸化式が得られる。ここで
であり、シグモイド関数によってステップ関数
なので、これらを
という漸化式が得られる。ただし、
整理のために
とすれば
となる。
この漸化式は、ある時点の重みに対して
として
とする。ただし
\delta 学習則
を古典パーセプトロンに入力することで重み
式
この式は古典パーセプトロンがニューロンの重みを修正する規則を表していることから
パーセプトロン学習則
として以下の新たな学習則を得る。
この式はパーセプトロン学習則と呼ばれる。このパーセプトロン学習則がステップ関数を使用した状態でも適切に機能することを確認してみる。
古典パーセプトロンが正しい結果を出力した場合、即ち
まずは
であったことを思い出すと、重みの減少はステップ関数の出力が 0 になる方向に近づくので、これも望ましい振る舞いと言える。
学習のアルゴリズム
(工事中)
-
実のところ、古典パーセプトロンでは
に関する重みf を決定するのは困難を極めるため、以降の古典パーセプトロンについての議論では専らこの簡略化したモデルを対象にw_i を決定する方法のみを扱うことになる。なお、このv_i の決定の困難さが研究対象を古典パーセプトロンから現代パーセプトロンに遷移させる直接の原因となっている。 ↩︎w_i -
ニューロコンピューティングの数学的基礎にはこのように書いてあるが、実際に解くことが不可能だと証明されたかどうかについては言及されていない。有識者がいれば教えてほしい。 ↩︎
-
筆者の不勉強を告白しておくと、ニューロコンピューティングの数学的基礎の該当箇所を初めて読んだ際、力学系という言葉の意味を勘違いしており内容を全く理解できなかった。力学系とは「一定の規則に従って時間の経過とともに状態が変化する系」という意味である。時間による展開が重要なのであって、力はあまり関係ない。(戒め ↩︎