math.jsを用いたニューラルネットワーク(NN)の実装例として三層のNNを構築した。
NNはn次元のデータを入力とし、m次元の結果として出力する処理機構である。 その内容は行列演算による線形変換と、活性化関数による非線形変換によって構成される。 NNのある一層における演算は次のような式で書ける。
\[ L^a_b: X^j\mapsto Z^i=f(W^i_j X^j + B^i) \]
(写像$L$は抽象添字記法を用いてテンソル記法で表した。)
ここで$W$は$X$にかかる行列で$m\times n$の成分を持ち、
$B$はバイアスベクトルで$m$成分を持つ。
$f$は活性化関数と呼ばれる任意の実関数であり、
シグモイド関数のような非線形なものを用いる。
このようにNNの一層はアフィン変換に活性化関数を噛ませたものとなっている。
活性化関数を除けば、1レイヤーにつき$n\times m + m$の実数パラメータ自由度がある。
NN全体はこの操作を繰り返すことで構成され、その回数は隠れ層の数と呼ばれる。 入力ベクトルを$X$とすれば上で定義した写像$L$から全体のNNは次のように書くことができる。
\[ (\text{NN})=L^{a_N}_{a_{N-1}}\cdots L^{a_2}_{a_1}L^{a_1}_{a_0} : X^j \mapsto Z^i \]よってニューラルネットワークは$\reals^n\rightarrow\reals^m$の関数として機能する。 また、各レイヤーのパラメータを調整(学習)することによって任意の関数を表現できると期待される。
基本的に行列演算なのでmath.matrix()
を用いて計算を行う。
Pythonと違いJS(ES)では二項演算子はNumber型にしか使えない。
ただし、math.jsではほぼすべてのオブジェクトにchainメソッドが使えるので、これを活用するとスッキリ書くことができる。
例えば上のアフィン変換の操作については
let A1 = math.chain(x).multiply(W1).add(B1).done();
とワンライナーに書くことができる。
通常のmatrixオブジェクトに戻すには最後にdone()
をする必要がある点に注意。
また、math.jsのmatrixオブジェクトからJSのArrayオブジェクトに戻すにはvalueOf()
メソッドを用いる。
ベクトルの各成分に活性化関数を適用する部分についてはmap()
メソッドを用いる。
forEach()
メソッドは返り値がないのでここではmap()
のほうがいい。
可視化と言っても表にしただけだが、plotly.jsを使うと比較的楽にdocumentに結果を出力できる。