trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ

PRML 5.5.4~5.5.6

5.5.4 接線伝播法

やること

誤差関数に正則化項を加えて入力の変換に対する不変性をモデルに持たせる。正則化関数として、入力ベクトルを変換をした場合出力ベクトルに及ぼす影響を利用する。

詳細

変換が1つのパラメータxiで支配される場合、 入力ベクトルx_{n}を変換を作用させて得られる新たな入力ベクトルをs(x_{n},\xi)と表す。変換しない場合(\xi=0)はs(x,0)=x)。変換の方向のベクトルは(5.125)。

入力ベクトルx\xiで変換したとき出力ベクトルkに及ぶ影響は(5.125)。

データ点の近傍において局所的に不変性を持つように これを正則化関数(5.128)として、もとの誤差関数に加える(5.127)。

\lambdaで訓練データに対するフィッティングと不変性の学習のバランスを決定する

図5.16について

(b)=s(a,\xi)
(c)=(a)+(b)/\xi*15度
(d)=s(a,15度)

5.5.5 変換されたデータを用いた訓練

もとの入力パターンを変換して訓練集合を拡大する方法が、接線伝播法と関連があることを示す。

詳細

もとの入力パターンを変換して訓練集合を拡大する方法の誤差関数が 接線伝播法の誤差関数と等しくなることを示す

  1. 入力パターンを変換していない誤差関数は(5.129)。
  2. 入力パターンを変換して訓練集合を拡大した場合の誤差関数は(5.130)。
  3. y(s(x,\xi))を展開して(5.130)に代入すると(5.131)、(5.132)を得る。
  4. (5.131)より(5.133)であるため正則化項は(5.134)となる。

これは接線伝播法における正則化項(5.128)と等価である。

y(s(x,\xi))テイラー展開について

$$ y(s(x,\xi))=y(s(x,0))+(s(x,\xi)-s(x,0))^{T} \left. \frac{\partial y}{\partial s} \right|_{\xi=0}\\ \hspace{40pt}+ \frac{1}{2}(s(x,\xi)-s(x,0))^{T}\left. \frac{\partial^{2} y}{\partial^{2} s} \right|_{\xi=0}(s(x,\xi)-s(x,0)) $$ ここで
\displaystyle{
\hspace{90pt} s(x,\xi)-s(x,0)=\xi \tau +\frac{1}{2}\xi^{2} \tau'+O(\xi^{3}),\,\,\,
\left. \frac{\partial y}{\partial s}\right|_{\xi=0}=\nabla_{x}y\\
\hspace{90pt}(s(x,\xi)-s(x,0))^{T} (s(x,\xi)-s(x,0)) =
\xi^{2} \tau^{T} \tau + O(\xi^{3})
}
より
\displaystyle
\hspace{20pt}y(s(x,\xi))=y(x)+\xi\tau^{T} \nabla y(x)+\frac{\xi^{2}}{2}
\left[
(\tau')^{T}\nabla y+\tau^{T}\nabla\nabla y(x)\tau
\right]+O(\xi^{3})

(5.130)から(5.132)

y(s(x,\xi))を展開した(5.130)の
第二項:\int \xi p(\xi) d\xi=E[\xi]
第三項:\int \xi^{2} p(\xi) d\xi=E[\xi^{2}] 、\int p(t|x) dt=1\int tp(t|x) dt=E[t|x]

5.5.6 たたき込みニューラルネットワーク

ニューラルネットワークの構造そのものに不変性を構築する

画像の性質を利用する

近くにある画素同士は遠く離れた画素同士よりも強い相関を持っ。現代的なアプローチの多くは次のようにしてこの性質を利用している。

  1. 画像の小さな部分領域だけに依存する局所的な特徴を抽出
  2. 抽出した特徴を統合して高次の特徴を検出
  3. 画像全体に関する情報をもたらす

また、画像のある領域において有用な局所的な特徴は、画像の別の領域においても有用である可能性が高い(検出の対象が平行している場合など)

たたみ込みニューラルネットワークでは次の3つの機構により上記の性質を利用している
(i) 局所的受容野
(ii) 重み共有
(iii) 部分サンプリング

特徴マップ

たたみ込み層ではユニットが並んだ平面が複数あり、平面それぞれを特徴マップと呼ぶ。各ユニットは画像の小さな部分領域だけから入力を受ける。同じ特徴マップに属するユニットは同一の重みの値を共有するように制約される。

f:id:trsing:20190217221023j:plain 入力画像と特徴マップ(ずれは一マスじゃなくてもよい)

入力画像が平行移動した場合、特徴マップの活性も同じだけ平行移動する。これを利用すればネットワークに入力画像の平行移動に対して不変性を持たせることができる。

複数の特徴を検出する場合はそれぞれが独自の重みとバイアスを持つ複数の特徴マップを使う。

部分サンプリング

入力はたたみ込みユニットの出力。 それぞれの特徴マップに対して、ユニットが並んだ平面が一つある。それぞれのユニットは、対応する特徴マップにある小さな受容野から入力を受ける。

次図のように対応する特徴マップの2x2のユニット領域から入力を受けると、たたみ込み層の半分のサイズになり、少々の平行移動に対しては比較的鈍感になる。 f:id:trsing:20190217221032j:plain

※完全結合(fully connected):自分より小さい番号が振られたすべてのノードから向かってくるリンクを持つ(8.1参照)

PRML 5.5~5.5.3

5.5 ニューラルネットワーク正則化

この節でやること

ニューラルネットワークの複雑さを制御する方法について。

理由

過学習を避けるため。

詳細

入出力ニュニットの数はデータ集合の次元で定まる。 隠れユニットの数Mは調整可能な自由パラメータで、Mによりパラメータ(重みとバイアス)の数を制御する。適合不足と過学習のバランスを最適にして汎化性能を最良にしたい。

汎化性能を最良にする方法
  • Mを選ぶアプローチ。図5.10に示すようなグラフをプロットした上で、検証用集合に対して誤差を最小とする解を選ぶ
  • ニューラルネットワークモデルの複雑さを制御するアプローチ。 比較的大きなMを選んでおいて正則化項を誤差関数に追加する。最も単純な正則化項は2次式(荷重減衰)

5.5.1 無矛盾なガウス事前分布

正則化項は無矛盾性を持つべきである

理由

入力あるいは目標変数を線形変換しても、本質的には等価である。しかし、正則化項が無矛盾でないなら等価であるはずの線形変換に対して重みの評価が変わり、異なる解を選んでしまうかもしれないため。

無矛盾:入力変数、目標変数を線形変換したデータで訓練する場合、変換分だけ重みが異なる等価なネットワークが得られること

詳細

以下、2層の重みと線形出力ユニットを持つ多層パーセプトロンについて話を進める。

入力データの線形変換(5.115)に対して、重みとバイアスを(5.116)、(5.117)と変換すれば変形前後で等価なネットワークになる。

出力変数の線形変換(5.118)に対して、重みとバイアスを(5.119)、(5.120)と変換すれば変形前後で等価なネットワークになる。

荷重減衰(5.112)ではこの性質を持たない。例えば出力変数の線形変換の場合(バイアスは除く)、 $$ \lambda \hat{w}^{T} \hat{w}= \lambda (\frac{1}{a^{2}} \sum w_{ji}^{2}+\sum w_{kj}^{2}) $$ となるため\lambdaを変更しても変換前と等価にできない。

線形変換のもとで不変な正則化項は(5.121)となる(ただし、バイアス項は和から除く)。この場合
\lambda_{1}\rightarrow a^{2}\lambda,\,\,
\lambda_{2}\rightarrow c^{-2}\lambda
とすれば変換前後で等価になる。

変則事前分布、エビデンスについては「2.4.3無情報事前分布」、「3.4ベイズモデル比較」を参照。

バイパスパラメータが制約されていないと不都合なため、バイアスにも固有の超パラメータを持つ別の事前分布(一般的に(5.123))を導入する。ただし、移動への不変性((5.115),(5.118)の+b,d?)は失われる。

(5.115)、(5.116)、(5.117)について

x_{i}\rightarrow ax_{i}+bとなった場合、同じになる\hat{w}_{ji}
$$ \sum w_{ji}x_{i}+w_{j0}=\sum \hat{w}_{ji}(ax_{i}+b)+\hat{w}_{j0}\\ \sum w_{ji}x_{i}=a\sum \hat{w}_{ji}x_{i},\,\, w_{j0}=b\sum \hat{w}_{ji}+\hat{w}_{j0}\\ \hat{w}_{ji}=\frac{1}{a}w_{ji},\,\, \hat{w}_{j0}=w_{j0}-\frac{b}{a\sum}w_{ji} $$

(5.118)、(5.119)、(5.120)について

y_{k}\rightarrow cy_{k}+dとなった場合、同じになる\hat{w}_{kj}
$$ c(\sum w_{kj}z_{z}+\hat{w}_{k0})+d=\sum \hat{w}_{kj}z_{j}+\hat{w}_{k0}\\ \sum \hat{w}_{kj}z_{j}=c\sum w_{kj}z_{j}\\ \hat{w}_{kj}=c w_{kj},\,\, \hat{w}_{k0}=cw_{k0}+d $$

5.5.2 早期終了

早期終了の手順によりネットワークの有効な複雑さを制御する

早期終了の意味

最適化アルゴリズムの多くでは、訓練誤差は反復回数の非増加関数。一方、検証用データに対する誤差は、最初は減少するがネットワークが過学習を始めると増加する。つまり、訓練誤差を最小にしても良い汎化性能とならない。良い汎化性能を持つメットワークを得るため、検証用誤差が最小になったところで訓練を終了したい。

上記のネットワークの挙動を有効自由度で説明

訓練の反復回数が少ないと有効自由度も小さい。 反復回数を増やしていくにつれてモデルの複雑さが増加し、有効自由度も大きくなる。そのため、訓練誤差が最小値になる前に訓練を止めることは複雑さを制限する方法になる。

二次誤差関数の場合の説明

図5.13を参照。 荷重減衰がない場合、重みベクトルは原点を初期値とし、勾配に沿って\tilde{w}を通り誤差関数の最小値w_{ML}に収束する。 荷重減衰がある場合、距離w^{T}wの重みにより道中で止まる(図3.15参照)。したがって、 \tilde{w}の近くの点で終了することは、荷重減衰がある場合と似たようなもの。

有効パラメータの数は訓練にしたがって増えていく(勾配の低いものも有効な大きさになっていく)

5.5.3 不変性

パターン認識において、入力が多少の変換を受けても変化しない(不変)であることが求められる。 変換の例としては画像内での位置、サイズ、輝度など。

さまざまな変換の影響を受けた十分多くの訓練パターンが利用可能ならば(少なくとも近似的には)不変性を学習できる。 しかし、訓練パターンが限られていたり必要な不変性がいくつもある場合には実用的ではない。 そのため別のアプローチが必要となる。これには大きく分けて4つある。

アプローチ1:実装が比較的用意で、複雑な不変性の場合にも利用できる。しかし、計算量が増大するというデメリットがある。

アプローチ2:データ集合はそのままに、正則化項の追加で誤差関数を変化させる(接線伝播法、5.5.4節で扱う)。また、5.5.5節でアプローチ1との関係を扱う。

アプローチ3:訓練集合に含まれる変換の範囲を超えて、正しく外挿することができる。 しかし、都合の良い特徴を見つけることは困難な場合もある。

アプローチ4:5.5.6節で扱う。

pythonで特定の文字列を含むファイル名の抽出

Pythonでファイル名に特定の文字列が含まれるものをフォルダ名とセットで抽出。

import os
 
#dir以下のフォルダと特定の文字列(Move,.cs)を含むファイルをディクショナリにして返す
#key:フォルダ名、value:ファイル名のリスト
def dirfiles(dir):
    dfdict={}
    for d,s,f in os.walk(dir):
        #フォルダdにあるファイルでファイル名に「Move」と「.cs」を含むファイル
        flst=['\t'+fname+'\n' for fname in f if 'Move' in fname and '.cs' in fname]
        if 0<len(flst):
            dfdict['{}'.format(d+'\n')]=flst
    return dfdict
 
#dicの中身をfileに書き出す
def writefile(file,dic):
    with open(file, mode='w') as wf:
        for k in dic:
            wf.write(k)
            for fn in dic[k]:
                wf.write(fn)
 
dfdict={}
dnames = [fname for fname in os.listdir() if os.path.isdir(fname)]#フォルダ名
for dname in dnames:
    #.values()でlistのlistが返ってくる。sumでリストを連結
    dfdict[dname+'\n'] = sum(dirfiles(dname).values(),[])
    if 0==len(dfdict[dname+'\n']):
        dfdict.pop(dname+'\n')
 
writefile('move1.txt', dfdict)#直下のディレクトリとそのディレクトリ下にあるファイル名
writefile('move2.txt', dirfiles('./'))#各ディレクトリとそのディレクトリ下にあるファイル名

引き継いだソースコードで動作が記述されたファイル(名称に"Move"を含む)を一覧にしたかったので。2000個以上出てきた。私にどうしろと言うのですか…。

※sumはリストの連結にも使える doc.code161.com

Windows10にてフォルダ名で検索

方法1

エクスプローラの検索窓に「kind:」と入力すると検索種類のドロップダウンが出てくるのでフォルダーを選択する。 f:id:trsing:20190211110730p:plain

方法2

エクスプローラの検索窓にフォーカスすると検索タブが出てくるので、「分類」→「フォルダー」
f:id:trsing:20190211111117p:plain

方法3

検索ツールバーに入力、「その他」→「フォルダー」
f:id:trsing:20190211111231p:plain:w300

参考にしたの

フォルダのみを検索する方法 - マイクロソフト コミュニティ

機能性大幅UP! Windows 10で強化された検索機能を使いこなす:Windows 10 Tips - Engadget 日本版

PRML 5章演習問題 5.21~5.23

5.21

出力ユニットがK>1のとき、演習5.16から
\displaystyle
H=\sum_{n=1}^{N}\sum_{k=1}^{K}\nabla y_{nk}(\nabla y_{nk})^{T}
ここで、
J=NK,c_{j}=\nabla y_{n(j)k(j)},n(j)=(j-1)/K+1,k(j)=(j-1)\mod K +1
とおけば
\displaystyle
H_{J}=\sum_{j=1}^{J}c_{j}(c_{j})^{T}

5.22

(5.55),(5.56)に注意して計算する f:id:trsing:20190203155551j:plain

(5.93)を導く f:id:trsing:20190203155603j:plain

(5.94)を導く f:id:trsing:20190203155612j:plain

(5.95)を導く f:id:trsing:20190203155624j:plain

5.23

層を飛び越えた結合の重みをu_{ki}とする。

両方の重みが層を飛び越えた結合の場合 f:id:trsing:20190210143331j:plain

2層と層を飛び越えた結合の場合 f:id:trsing:20190210143343j:plain

1層と層を飛び越えた欠乏の場合 f:id:trsing:20190210143400j:plain

PRML 5章演習問題 5.16~5.20

5.16

複数の出力を持つ場合の誤差関数は
\displaystyle
E=\frac{1}{2} \sum_{n} \sum_{k }(y_{nk}-t_{nk})^{2}
勾配は
\displaystyle
\nabla E=\sum_{n}\sum_{k}(y_{nk}-t_{nk})\nabla y_{nk}
ヘッセ行列は
\displaystyle
H=\nabla \nabla E=\sum_{n}\sum_{k}\nabla y_{nk}(\nabla y_{nk})^{T}+\sum_{n}\sum_{k}(y_{nk}-t_{nk})\nabla \nabla y_{nk}
近似すると
\displaystyle
H=\sum_{n}\sum_{k}\nabla y_{nk}(\nabla y_{nk})^{T}=\sum_{n} B_{n}B_{n}^{T}\\
(B_{n})_{(l,m)}=\frac{\partial y_{nm}}{\partial w_{l}}
f:id:trsing:20190209115733j:plain

5.17

\displaystyle
\frac{\partial E}{\partial w_{s}}=\int \int (y(x,w)-t)\frac{\partial y(x,w)}{\partial w_{s}}p(x,t) dx dt\\
\frac{\partial }{\partial w_{r}}\frac{\partial E}{\partial w_{s}}=
\int \int \frac{\partial y(x,w)}{\partial w_{r}}\frac{\partial y(x,w)}{\partial w_{s}}p(x,t) dx dt
+\int \int (y(x,w)-t)\frac{\partial^{2} y(x,w)}{\partial w_{r} \partial w_{s}}p(x,t) dx dt\\ \hspace{30pt}=
\int \frac{\partial y(x,w)}{\partial w_{r}}\frac{\partial y(x,w)}{\partial w_{s}}\int p(x,t) dt dx
+\int \frac{\partial^{2} y(x,w)}{\partial w_{r} \partial w_{s}}y(x,w)\int p(x,t) dt dx
-\int \frac{\partial^{2} y(x,w)}{\partial w_{r} \partial w_{s}}\int tp(x,t) dt dx\\ \hspace{30pt}=
\int \frac{\partial y(x,w)}{\partial w_{r}}\frac{\partial y(x,w)}{\partial w_{s}}p(x) dx
+\int \frac{\partial^{2} y(x,w)}{\partial w_{r} \partial w_{s}}y(x,w) p(x) dx
-\int \frac{\partial^{2} y(x,w)}{\partial w_{r} \partial w_{s}}y(x,w) p(x) dx\\ \hspace{30pt}=
\int \frac{\partial y(x,w)}{\partial w_{r}}\frac{\partial y(x,w)}{\partial w_{s}}p(x) dx\\
※(1.89)からy(x)p(x)=\int tp(x,t)dt

5.18

入力iから出力kへ直接つながる結合に相当するパラメータをu_{ki}と置くと(5.64)より
\displaystyle
y_{k}=a_{k}=\sum_{i} u_{ki}x_{i}+\sum_{j} w_{kj}^{(2)}z_{j}
誤差関数の追加されたパラメータに関する微分の方程式は
\displaystyle
\frac{\partial E}{\partial u_{ki}}=\frac{\partial E}{\partial a_{k}}\frac{\partial a_{k}}{\partial u_{ki}}=(y_{k}-t_{k})x_{i}

5.19

あるデータx_{n}に対してy_{n}=\sigma(a_{n})
ある重みw_{i}が変更されるとすべてのデータに対しての変更となるので \frac{\partial E}{\partial w_{i}}=\sum_{n}\frac{\partial E}{\partial a_{n}}\frac{\partial a_{n}}{\partial w_{i}}
よって
\displaystyle
\nabla_{w} E=\sum_{n} \frac{\partial E}{\partial a_{n}} \nabla_{w} a_{n}=
\sum_{n}\left(-\sum_{i} \frac{\partial}{\partial a_{n}} \{t_{i} \ln y_{i}+(1-t_{i}) \ln(1-y_{i}) \} \right)  \nabla_{w} a_{n}\\ \hspace{30pt}=
\sum_{n}\left(y_{n}-t_{n} \right)  \nabla_{w} a_{n}
i \neq nのとき\frac{\partial y_{i}}{\partial a_{n}}=0
f:id:trsing:20190209145111j:plain

5.20

f:id:trsing:20190209152002j:plain f:id:trsing:20190209152042j:plain

「機械学習のエッセンス」に出てくるpython関係のメモ

読み進めるうちに順次増やしていく(予定)

numpy

numpy.array(object,dtype=None,copy=True,order='K',subok=False,ndmin=0)
numpy.array([0,1,2])#1次元配列
numpy.array([[0,1,2],[3,4,5]])#2次元配列

dtypeの配列を作成する。1次元の場合objectに[要素]。2次元の場合objectに[ [要素]]。


numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)

区間[start,stop]でnum個の等間隔な値を要素に持つ1次元配列を返す。


numpy.zeros(shape,dtype=float,order='C')
numpy.ones(shape,dtype=float,order='C')
numpy.empty(shape,dtype=float,order='C')
numpy.zeros(5)      #要素数5の1次元配列
numpy.zeros((2,3))  #形状が(2,3)の2次元配列

要素が0/1/未初期化のshape,typeの配列を返す。shapeはintかintのタプル。


numpy.r_[a,b]

a,bが2次元配列:a,bを縦方向に連結する(2次元配列)
a,bが1次元配列:a,bを連結(1次元配列)

numpy.c_[a,b]

a,bが2次元配列:a,bを横方向に連結する(2次元配列)
a,bが1次元配列:a,bそれぞれを列として横方向に連結(2次元配列)


numpy.dot(a,b)
a.dot(b)
a@b

行列積を返す。1次元配列の場合自動的に解釈(左側にあれば横ベクトル、右側にあれば縦ベクトルと解釈)

numpy.random

numpy.random.rand(d1,d2,...,dn)
numpy.random.rand(2,3)   #(2,3)の2次元配列を返す
numpy.random.rand(3)     #要素数3の1次元配列を返す
numpy.random.rand()      #浮動小数点を返す

指定された次元の配列を返す。指定がない場合単なる浮動小数点を返す。要素は範囲[0,1)のランダムな浮動小数点。

numpy.random.randint(low,high=None,size=None,dtype='l')
numpy.random.randint(5,size=(3,3))   #(3,3)の配列を返す。要素は[0,5)
numpy.random.randint(5,size=3)       #要素数3の1次元配列を返す。

範囲[low,high)のランダムな整数を返す。high=Noneの場合範囲は[0,low)。sizeを指定すると配列を返す。

numpy.linalg

numpy.linalg.inv(a)      #aの逆行列を返す
numpy.linalg.solve(a,b)  #ax=bの解(x=inv(a)b)を返す

scipy.linalg

scipy.linalg.lu_factor(a)        #aのLU分解(a=PLU)P,LUを返す
scipy.linalg.lu_solve((lu,p),b)  #PLUx=bの解(x=inv(PLU)b)を返す

numpy.ndarrayの属性

shape    #配列の次元のタプル
ndim     #配列の次元数
size     #配列の要素数
T        #配列の転置(2次元以上の場合。1次元の場合そのまま)

numpy.ndarrayのメソッド

reshape(shape)      #要素は同じで配列の形状がshapeである配列を返す
resize(new_shape)   #配列の形状をnew_shapeに変更する
ravel()             #1次元の配列を返す


sum([axis])   #合計
mean([axis])  #平均
max([axis])   #最大値
min([axis])   #最小値

axis=0で行方向、axis=1で列方向 ※ndarrayで1次元配列と行数が1or列数が1の2次元配列は別の物

ブロードキャスト

ndarrayに対して演算を行う場合に次元数や形状を自動的に調整する

numpy.exp(a)
numpy.log(a)
numpy.sqrt(a)

配列aの要素それぞれに対して演算を行う。 演算子(+、*、**、>=)などについても同様


リファレンス
NumPy Reference — NumPy v1.15 Manual