HELLO CYBERNETICS

深層学習、機械学習、強化学習、信号処理、制御工学、量子計算などをテーマに扱っていきます

畳み込みニューラルネットワークの基礎

 

 

follow us in feedly

事前知識

テンソルで理解しておくべきことは意外と少ない

畳み込みニューラルネットワークでは非常にたくさんの添字が現れます。

しかし決して難しいものではありません。実はとてもシンプルな処理を繰り返しているだけです。そのことを理解するためには、テンソルの概念を知っておくと便利です。

 

言葉が聞き慣れないだけで全く難しくありません。

 

テンソルのインターネットで検索すると何やら難しい話が出てきます。

以下はWikipediaの引用です(読む必要ありません。テンソルって聞くとこんな話が出てきますが、気にしないでくださいということが言いたいのです。)。

多重線型写像としての取り扱い[編集]

テンソルを多次元配列として定義するやり方では、内在的な幾何学的対象であることから期待されるべき性質である基底の取り方に依らないことが、定義から明らかでないという欠点がある。テンソルの変換法則が実際に基底の取り方に依らないことは証明できることではあるが、しばしばより内在的な定義が取り上げられる。その一つが、テンソルを多重線型写像として定義することである[1]。これによれば、(p, q)-型テンソル T は函数

T\colon \underbrace {V^{*}\times \dots \times V^{*}} _{p{\text{ copies}}}\times \underbrace {V\times \dots \times V} _{q{\text{ copies}}}\to \mathbb {R}

で、各引数に関して線型であるようなものとして定義される。ここで V有限次元ベクトル空間で、V はその双対ベクトルからなる双対空間である。

(p, q)-型の多重線型写像 TV の基底 {ej} とその V における標準的な双対基底 {εi} に対して施せば、

T_{j_{1}\dots j_{q}}^{i_{1}\dots i_{p}}\equiv T({\boldsymbol {\varepsilon }}^{i_{1}},\ldots ,{\boldsymbol {\varepsilon }}^{i_{p}},\mathbf {e} _{j_{1}},\ldots ,\mathbf {e} _{j_{q}})

によりその成分として (p + q)-次元配列が得られる。基底の取り方を変えれば異なる成分が得られるが、T は各引数に関して線型であるから、成分は多次元配列としてのテンソルの変換法則を満足する。したがって、T の成分の成す多次元配列はその意味において確かにテンソルを成していることが分かる。さらに言えば、そのような性質を持つ多次元配列は必ず多重線型写像 T の成分として実現できる。そのような事情により、多重線型写像をテンソルに基づく内在的対象を与えるものとして見ることができる。

テンソルを多重線型写像と見る立場では、ベクトル空間 VV の双対空間上の線型汎函数全体の成す空間(つまり V二重双対英語版 V∗∗ と同一視するのが普通である。V からその二重双対への自然な線型写像が常に、V のベクトルを「V に属する線型形式を与えられたベクトルにおいて評価した値へ写す写像」と看做すことによって与えられる。V が有限次元ならばこの線型写像は同型であり、しばしば V をその二重双対と同一視することは有用である。

テンソル積に基づく定義[編集]

数学的応用に際しては、より抽象的やり方の方が有効なこともある。これは普遍性を通じて定義できるベクトル空間のテンソル積の元としてテンソルを定義することによってなされる。この文脈では、(p, q)-型テンソルはベクトル空間のテンソル積の元

T\in \underbrace {V\otimes \dots \otimes V} _{p{\text{ copies}}}\otimes \underbrace {V^{*}\otimes \dots \otimes V^{*}} _{q{\text{ copies}}}

として定義される[2]

一般に、viV の基底を成し、wjW の基底を成すとき、テンソル積空間 VW は自然な基底 {viwj} を持つ。テンソル T の成分は、V の基底 {ei} とその双対基底{εj} から得られる基底に関するテンソルの係数

T=T_{j_{1}\dots j_{q}}^{i_{1}\dots i_{p}}\;e_{i_{1}}\otimes \cdots \otimes e_{i_{p}}\otimes \varepsilon ^{j_{1}}\otimes \cdots \otimes \varepsilon ^{j_{q}}

に等しい。テンソル積の性質を用いて、この成分が (p, q)-型テンソルの変換則を満たすことが示せる。さらに言えば、テンソル積の普遍性により、今定義した意味でのテンソルと多重線型写像として定義した意味でのテンソルが一対一対応することが言える。

テンソルは極めて一般に(例えば任意の環上の加群まで含めて)定義することができる。一つの原理として「『テンソル』とは単に任意のテンソル積空間の元である」と定めることはできるが、数学の文献では「テンソル」とは上記のように一つの空間 V とその双対から得られるテンソル積(テンソル空間)の元のために用いるのが普通である。

 

それは数学的な都合によるもので、機械学習で応用する上では難しい概念は必要ありません。

単に多次元配列のことをテンソルだと思えばいいのです。

n階のテンソルとはn次元配列のことであり、例えば3階テンソルは

 

X_{ijk}

 

と表せます。i,j,kの値を変えれば、テンソルの要素にアクセスできるくらいに思っておけばいいでしょう。i,j,kがそれぞれ何のインデックスなのかは問題に応じて人間が勝手に決めればいいのです。

 

ぜひ以下の記事でテンソルというものの概念を掴んでください。

s0sem0y.hatenablog.com

 

畳み込みニューラルネットワークを扱う上ではテンソルの添字の扱いに慣れておいたほうがいいでしょう。プログラミングに慣れている人は単に多次元配列だと考えても支障はないです。

畳み込みとは

畳み込み

畳み込みという言葉はなにやら難しそうな言葉です。

一般的に畳み込み演算とは以下で表されます。

(最後に述べますが、畳み込みニューラルネットは厳密には以下で述べる畳み込み演算とは異なります。読み飛ばしても構わないです。

f(n)とg(n)があったとき、この2つの畳み込みf*g(m)とは

 

f*g(m)=\sum_n f(n)g(m-n)

 

です。g(m-n)の引数にm-nでは、実際にはnの方のみが変化をしていきます。gの引数が1つずつずれていっているのが分かるでしょうか。具体例を見てみましょう。

以下の単純な問題を考えます。

 

f(n)=(2,-1,4,-2)

 

g(n)=(1/3,1/3,1/3)

 

のような数列を準備しましょう。ここで数列の長さが異なっていますね。

g(n)においてn=1,2,3でしか値を持たないので、他のg(n)について例えばg(-1)などは0と定義しておきます。これらを畳み込んでみましょう。

 

f*g(m)=\sum_n f(n)g(m-n)

=f(0)g(m)+f(1)g(m-1)+f(2)g(m-2)+f(3)g(m-3)

 

定義通りに値を代入していっただけです。

ここで引数(数列のインデックス)は0から始めています。f(n)の各値については既に知っているのでこれを代入してしまいましょう。

 

f*g(m)=2g(m)-g(m-1)+4g(m-2)-2g(m-3)

 

これでf*g(m)mだけの関数になり、畳み込まれた結果である数列についてm番目の値を具体的に計算できることになります。今数列と数列を畳み込んで、新たな数列を作り出しました。畳み込み演算自体は離散でなくとも連続でも扱うことができます。片方を固定し、片方の引数を変化させていき積を取るのが畳み込み演算であり、以下の図が非常に分かりやすいです。

 

f:id:s0sem0y:20161222215013g:plain

(wikipedia 畳み込みより)

 

 

通常、信号処理や制御では当たり前のように扱われており、波形をシステムに畳み込むことで出力を獲得します。通常、信号処理で行われるフィルタ処理は畳み込み処理によって実現されます。

畳み込みニューラルネットでもフィルタと2次元データに畳み込みのような処理を行い、何らかの新たな2次元データを獲得します。しかし実は厳密には畳み込みニューラルネットの畳み込みと呼ばれる処理は、「畳み込み演算」ではありません。

 

 

畳み込みニューラルネット

畳み込みニューラルネットの畳み込み処理

空間フィルタ

今2次元配列データx(i,j)があるとしましょう。i,jが画像のピクセル位置を指定し、その位置における画素値がx(i,j)という具合です。実際には画像でなくてもいいですが、馴染みが深いので画像を例にしていきます。

 

画像処理では従来から、エッジ処理やぼかし処理と呼ばれる処理が使われてきました。これらを使うには、x(i,j)に対して空間フィルタh(p,q)を準備します。一般にx(i,j)よりも小さなサイズの空間フィルタh(p,q)を用いることに注意してください。

 

画像x(i,j)に対してフィルタh(p,q)を用いる場合には以下のような計算をします。

 

u(i,j)=\sum_m \sum_n x(i+m,i+n)h(m,n)

 

u(i,j)は処理が施された後の画像です。図で見てみましょう。

簡単のため極端にピクセルの少ない画像を考えます。

 

下の図のように画像とフィルタがあった場合

f:id:s0sem0y:20161222222423p:plain

 

以下のようにフィルタを掛ける。

画像とフィルタにはそれぞれのマスに値が入っており、要素同士の掛け算を行ってから和を取る(積和演算)

 

f:id:s0sem0y:20161222222443p:plain

 

1つの積和演算から1つの画素が得られるため、今回の場合3×3の処理された画像が得られることとなる。 

 

これが空間フィルタリングになりますが、それと同時にほぼ畳み込み層の全容と言えます。

 

出力画像のサイズを調整するために以下の図のようなテクニックを使う場合もあります。

f:id:s0sem0y:20161222223504p:plain

本来の画像の周囲に、適当な値を外挿することで擬似的にピクセル数を増やす。これに2×2のフィルタを用いた場合は5×5の画像が得られる。

この画像の周囲に値を外挿する処理をパディングと呼び、周囲に0を外挿することを特にゼロパディングと呼びます。

仮に4×4の画像に対してフィルタ処理をした際に画像サイズを維持したい場合は、周囲に1マス分のパディング処理を行い6×6に変更し、3×3のフィルタを準備すれば良いです。

 

また、上記ではフィルタが1マスずつ移動していくことを想定しましたが実際にはフィルタを2マスずつ移動しても構いません。このフィルタの移動の大きさをストライドと言います。通常はストライド1の処理を行いますが、密な情報が不要だと思えばストライドを大きくしても良いでしょう。

 

畳み込み層

畳み込み層の全容は上記で述べた空間フィルタに集約されています。

通常の画像処理では、どのような空間フィルタに対してどのような処理が対応するのかをしっかりと検討します。例えばぼかし処理をしたいとか、輪郭強調をしたいとか、何らかの要望があった場合にフィルタはどのような値を配置しておくべきかなどを検討します。

 

畳み込みニューラルネットにおいては、この空間フィルタが学習の対象になります。すなわち、識別をするという目的があった場合に、その識別に有利になるような空間フィルタを学習で探させるということです。従って、その空間フィルタに具体的に如何なる意味があるのかというのは、予め決めておくものではありません。

 

そして更に重要な点は、何も空間フィルタは1つとは限られないということです。ある画像からその動物を当てたいときに、輪郭が重要であったり、色の具合が重要であったりと様々です。それらすべてを考慮して動物を当てればいいのであって、輪郭しか見てはいけないなどというルールはありません。

 

したがって畳み込みニューラルネットワークでは、学習すべきフィルタを予め複数準備しておきます。畳み込みニューラルネットに入力された画像は、それぞれのフィルタ処理を並列に施されるため、例えばフィルタが4枚準備されているのであれば、画像は4枚に増幅されることになります。

 

f:id:s0sem0y:20161222225402p:plain

異なるフィルタを4枚準備し、異なる処理を施した画像を4枚獲得。

 

従って数式としては以下のようになります。 

 

u(i,j,k)=\sum_m \sum_n x(i+m,j+n)h(m,n,k)

 

添字kが追加されていますが、これがフィルタを指定するものです。畳み込み演算に関しては関与していないことに注意してください。あくまでピクセル上を動かすのが畳み込み演算です。その計算がフィルタの種類kの数だけあるということです。フィルタの数によって画像の数も決まってくるわけですが、通常これを画像のチャンネル数と表現します。つまり、上記の画像では畳み込み層を通過した際のデータのチャンネル数は4であるということです。

 

RGB画像を扱う場合

RGB画像の場合、画像は赤色に対応する画素値、緑色に対応する画素値、青色に対応する画素値をそれぞれ独立に考えることができます。すなわち1つのRGB画像は、それぞれの色に関する3枚の2次元データを使って表現するということです。

言い換えれば、RGB画像のチャンネル数は最初から3であるということです。

このような場合には、それぞれのチャンネルに対してフィルタを準備してあげないと行けません。すなわちフィルタにもチャンネルという概念を考え、入力のチャンネル数に揃える必要があります。

 

f:id:s0sem0y:20161222231534p:plain

入力がRGBの3チャンネルならば、フィルタも必ずセットで3チャンネルとする。

上の図の場合、出てくる画像は1チャンネルであることに注意。

 

上記の図は画像1枚に対してフィルタを1種類準備した例です。チャンネル数が増えたからと言って、それは入力のチャンネル数に合わせただけの話で、上記の図の場合3枚のフィルタで1種類と考えます。

 

従って畳み込みニューラルネットでは、RGBの画像に対して3チャンネルのフィルタを複数種類準備するということを行います。

 

f:id:s0sem0y:20161222232002p:plain

 

これを見ると画像が「2枚出てきた」と考えるのではなく、「2チャンネル」の1枚の画像が出てきたとも考えることができます。畳み込みニューラルネットでは実際にそのように考えます。

従って畳み込み層での処理では

 

入力画像のチャンネル数cにフィルタのチャンネル数を合わせる。

そのようなフィルタをk種類揃える。

出力はkチャンネルの画像となる。

 

という変換が行われます。つまり出力層のチャンネル数を、フィルタを何種類準備するかで調整できるということです。通常は、チャンネル数を次第に増やしていき、代わりに画像のサイズを小さくしていきます。

 

最後に数式を見ておきましょう。

 

u(i,j,k)=\sum_c \sum_m \sum_n x(i+m,j+n,c)h(m,n,c,k)

 

入力画像にチャンネルcが複数あっても和の計算を行うため、出力では1つのチャンネルに情報が集約されることになります。あくまで出力のチャンネル数を決めているのはフィルタの種類数kであることに注意しましょう。実際入力ではチャンネルに相当していたインデックスcの部分が、出力ではkに代わっています。

 

実際には右辺にバイアスb(i,j,k)が加わりますが、画素(i,j)ごとに考えるのはやめてしまい、同一のチャンネルには共通のバイアスb(k)をつかう場合が多いです。

 

u(i,j,k)=\sum_c \sum_m \sum_n x(i+m,j+n,c)h(m,n,c,k) + b(k)

 

これが畳み込み層の全容となります。

当然、u(i,j,k)を新たな画像だと思い、もう一度畳み込み層を準備する(つまり多層化する)ことも可能です(それが可能なように整備されているわけです)。

 

畳み込み層まとめ

入力のサイズ、フィルタのサイズによって出力のサイズが変わる。

パディング処理、ストライドの変更なので出力サイズを調整できる。

空間フィルタを複数種類することで出力画像のチャンネル数を調整できる。

1種類の空間フィルタのチャンネル数は入力データに合わせる。

学習のパラメータは空間フィルタの係数。

 

 

分類の方法について

分類を如何にして行うのかを考えるのは至ってシンプルです。

上記の図の場合、3×3の画像が4枚あります。つまり画素値が3×3×4=36個あるわけですね。分類を行う場合は、あたかもこれらの画像の集まりを36次元ベクトルとみなして、従来同様のニューラルネットワークに接続してしまうのです。

すなわち、画像が10種類に分類可能ならば、36次元ベクトルを10次元に変換する通常のニューラルネットを準備し、出力層にソフトマックス層を準備するという具合です。ソフトマックスは如何なる入力であろうと、出力層がソフトマックスである限り、入力があるクラスに属する確率を出力すると見なすことができます。

 

s0sem0y.hatenablog.com

 

 

そして学習時にはまずデータを通過させ、ソフトマックス層で確率を出力させます。

当然最初はデタラメの確率となっていますから、正解に近づくようにニューラルネットの結合係数や、畳み込み層における空間フィルタの係数を調整していくことになります。

ただし畳込み層は係数が2次元上に配置されており、そのようなフィルタが複数種類あり、しかも1種類のフィルタが複数チャンネルであるため、学習パラメータの数は膨大極まりないです。

GPUなしではまともに学習するのは難しいかもしれません。

 

プーリング層

畳み込みニューラルネットにはプーリング層なるものもあります。

これはとても簡単です。画像の代表値を抜き出す処理だと言えます。

f:id:s0sem0y:20161222234843p:plain

プーリングのサイズを決める。上の例は2×2。

 2×2ごとの画素を見ていき、代表値を決めて新たな画像とするのです。例えば最も有名なマックスプーリングでは、着目領域で最も大きな値の画素値を代表として抜き出します。他にも着目領域の平均値を代表とする平均プーリングなどもあります。

 

この処理は学習などは行わず決め打ちします。なぜにこの処理を挟むのかというと、例えば着目領域のどこかの部分に高い値が1個でもあればいいという場合において、どこにあるかは問題ではない場合は、返って高い画素数が学習の妨げになる可能性があります。

例えば「1」という手書き文字が、画像の真ん中にあろうが左の方にずれていようが構わないはずです。着目領域における代表値の並進移動に対してロバスト性を有することを期待してプーリング処理が行われると言えます。

 

活性化関数

活性関数は基本的にReLU関数

 

\max (0,x)

 

が用いられます。これは画素毎に行われるため、特別解説の必要はないでしょう。

単に各画素値を見て0以上ならばそのまま、負なら0にしてしまうということです。

(生画像ならば当然非負値ですが、畳み込みニューラルネットの内部では、フィルタ係数が負を取るために内部で画素値が負になることも考えられます。そのような値はReLUの活性化では0に変更されます。)

 

全体のまとめ

今回最も重要だったのは畳み込み層に関することです。

畳み込みニューラルネットの豊かさは、この層によって実現されています。

フィルタの種類によって出力データのチャンネル数を調整でき、更にフィルタのサイズによってデータのサイズも変更を受けていきます。またフィルタのサイズが着目領域の範囲を決定します。ここの部分が最も重要であることは疑いありません。

 

プーリング層はロバスト性を確保するために補助的に導入します。

学習のパラメータは含まれておらず表現力には関与しないものの、畳み込みニューラルネットの汎化性能を影で支えている立役者と言えます。

 

活性化関数は多層化に耐えうる単純な構造を持っているReLUが用いられます。

他にも様々な工夫が施された活性化関数が提案されていますが、あまり性能差は出ていないようです。本質的に多層化に意味があるというのであれば、活性化関数は誤差逆伝搬法を助けることのできる微分可能かつ単純なものが使われるのが標準になっていくかもしれません。