Np-Urのデータ分析教室

オーブンソースデータなどWeb上から入手できるデータを用いて、RとPython両方使って分析した結果を書いていきます

「CNNって何よ」って聞かれたら、とりあえずこう説明してみたら?という話

タイトルの通り、「CNN(畳み込みニューラルネットワーク)って何よ」とざっくりと質問された時に、自分だったらざっくりとこう説明してあげる、というのをまとめます。

この記事で説明している内容は、以下を元にしているので、よろしければ是非。

Pythonと実データで遊んで学ぶ データ分析講座

Pythonと実データで遊んで学ぶ データ分析講座

なお、スマホのAMPだと、数式がうまく表示されない可能性がありますので、こちらのリンクPCから読んでいただけると。

まずニューラルネットって?

畳み込みニューラルネットワークについて説明する前に、ニューラルネットワークも軽くおさらいしてあげましょう。

脳は入力を受け取ると、以下画像のように各神経細胞が反応しながら処理が次々と行われます。

ニューラルネットワークのイメージ

ニューラルネットワークは、このような脳の神経伝達の働きを数理モデルとして落とし込んだものです。

具体例を使って

いま、部屋の広さと家賃に関するデータがあるとします。
以下のようなイメージです。

部屋の広さ(平方メートル) 家賃(万円)
22 5
30 10
28 12
36 13
25 6

可視化するとこんな感じの。

部屋の広さと家賃の関係を可視化

そこで、部屋がどれだけ広いかを元に、家賃をニューラルネットで予測してみるとしましょう。

まず先に、用語の整理を行います。
ニューラルネットは、以下の図のように、入力層中間層出力層という層構造を持っています。

ニューラルネットワーク 入力層中間層出力層

入力データを受け取る層が入力層、予測値を返す層が出力層、入力層と出力層に挟まれたその他の層が中間層です。
後述しますが、中間層と出力層は、それぞれ活性化関数という特殊な変換をする関数を持っています。

また、各層には、まるで神経細胞のように、前の層から受け取った情報を処理して次の層へ流すユニット(丸印の箇所)があります。
ユニット同士を結んでいるのがエッジで、こちらも後述しますが、それぞれ重みという情報を持っています。

まずは、簡単のため、入力層と出力層のみからなるニューラルネットを試してみます。
入力層と出力層のみのニューラルネットワーク
入力層のユニットの数は、説明変数の数にプラス1をした値です。今回は、「部屋の広さ」という変数しかないため、入力層のユニット数は2つです。

どうしてプラス1をするかというと、バイアスという線形回帰時の切片項のような働きをする変数を追加する必要があるからです。
このバイアスの値は、常に1とします。
なお、バイアスがないモデルも存在しますが、ここではあるものととして説明を進めます。

出力層のユニットの数は、予測したい変数の数です。今回は、「家賃」という変数しかないため、出力層のユニット数は1つです。

重みとエッジ

エッジは、隣合わせの層に属する全ユニットを繋ぎます。
今回は、入力層ユニットが2つ、出力層ユニットが1つしかないため、上記の図のようにエッジは2つのみです。

なお、それぞれエッジは、何らかの w_1 w_0という値の重みを持っているとします。
ニューラルネットワークの目的は、最も予測精度が高くなる(予測値と実際の値が近くなる)ように、この重みを求めることです。

また、出力層は何らかの活性化関数 f(\cdot)を持っているとします。
このような条件下で、仮に x_iという値が入力された時に、どのように予測結果 y_iが計算されるのか、説明します。

  • 入力層の各ユニットが持つ値と、関係するエッジに対する重みをそれぞれかけた値を足し合わせ、出力層のユニットに渡す
    • つまり、 w_1x_i + w_0が出力層のユニットに渡る
  • 出力層のユニットでは、入力層から受け取った値、 w_1x_i + w_0を活性化関数に渡し、出力とする
    • つまり、 \hat y_i = f(w_1x_i + w_0) を予測結果とする

様々な入力値 x_iに対して、それぞれ予測結果 \hat y_i = f(w_1x_i + w_0)が計算できます。

「活性化関数とは具体的にどのような関数か、また活性化関数をかます目的はなんなのか」という点については、次節で解説します。

活性化関数

活性化関数は任意の関数を設定して問題ないのですが、一般的によく使われる関数を紹介します。

恒等関数

$$f(x) = x$$

図の通り、活性化関数として恒等関数を選んだ場合は、何も変換していないのと同等です。

恒等関数

ReLU

$$
f(x) = \max(0, x)
$$

活性化関数としてReLUを選択した場合、受け取った値が0以下であれば0、0より大きければその値を返します。

ReLU

シグモイド

$$f(x) = \frac{1}{1+\exp(-x)}$$

活性化関数としてシグモイドを選択した場合、下図のよう変換されます。

シグモイド


通常、恒等関数や、定数をかけるだけのような線形変換する関数を活性化関数としては用いません。例えば、ただ値を3倍する関数に通すのと、重みの値を3倍にするのは結局同じです。そのため、そのような活性化関数を用いる意味があまりありません。

活性化関数を用いるメリットとして、非線形な変換を内部で行うことができる点があります。
非線形な変換を行った中間層を重ねることで、人間が定義するのが到底難しいような複雑な回帰や分類をするモデルを構築することができます。

このように、ユニットの数と活性化関数を任意に指定したら、あとはもっとも精度が高くなるようなエッジの重みを探してあげれば計算終了です。
もっとも精度の良い重みは、ランダムに探していてはあまりに効率が悪いので、誤差逆伝播法という方法を用いて計算します。(本記事では誤差逆伝播法については説明は省略します)

じゃあ、畳み込みニューラルって?

ニューラルネットワークについてスーパーざっくり理解ができたところで、畳み込みニューラルネットワークの説明に進みます。

畳み込みニューラルネットワークは、画像データに対してよく使われます。
どうして画像データに対して有効なのか、また通常のニューラルネットワークを画像データに対して使うとどのあたりが良くないか、説明していきます。

通常のネットワークで画像分類

通常のニューラルネットを使って、画像分類問題を解こうとしてみます。

以下のように、手書きで書かれた「1」や「0」を画像のみから分類したいとします。
1と0の画像を分類

ニューラルネットを使うためには、画像データを数値化する必要があります。
このようなグレースケールの画像では、各ピクセルごとに輝度値を計算することで数値化することが多いです。

例えば、画像を縦横10個に分け、それぞれで輝度値を計算したとします。
画像の左上から、1つ目の変数・2つ目の変数…とそれぞれ番号付けをすると、この画像は100個の変数を使って数値化できたと考えられます。

輝度値を計算

こちらを元にニューラルネットを適当に作ってみましょう。
変数が100個ということは、前節で学習したように入力層のユニット数は101個(変数 + バイアス)となります。
中間層の構成は任意に設定し、最終的には画像が「1」なのか「0」なのかを表す確率を出力層では出力します。

そして、色々な画像をニューラルネットにかけながら、最も精度が良くなるように、重みを調整することができれば学習終了です。

画像を分類するニューラルネットワーク

通常のニューラルネットでは、変数同士の空間的な情報を利用することができません。

変数が100個ありますが、例えば

  • 変数16は、変数15と変数17に挟まれている
  • 変数16の下には、変数26がある
  • 変数16の左下には、変数25がある

などの位置関係は一切考慮せず、それぞれ独立した変数として扱っています。
以下のように、何も考えずにただ一列に並べた状態で、入力として受け取っています。

画像の情報をベクトル化

画像のように、変数の位置が重要な意味をもつ場合、その空間的な情報はそのまま利用した方が精度は上がりそうです。
この考えを元に、次節以降で紹介する畳み込み層・プーリング層を使った手法が畳み込みニューラルネットです。

畳み込み層とプーリング層

畳み込み層では、例えば犬の画像を元に、耳の形、足、目、鼻といった個々のパーツの特徴を学習することができます。

畳み込み層では、フィルタと呼ばれる小さな特徴抽出器を通して、画像のどこに特徴が存在するか表す特徴マップを出力します。
例えば、以下のように縦横3個の輝度値情報を持つ画像があるとしましょう。

9個しか変数がないニューラルネット
前節では、この画像を以下のように通常のニューラルネットで学習させると、空間的な情報を保持できずに非効率だということを学びました。

できれば、空間的な情報、つまり上下左右がどんな値かを考慮したいです。
それを踏まえて畳み込み層では、以下のように、画像を各領域に分け、それぞれを一つのセットにて演算を行います。
畳み込み層のイメージ

2×2のサイズのフィルタを用意し、各領域(網掛け部分)と積和を順にとります。
以下では、(1, 2, 3, 4)を成分にとるフィルタを用意しています。
畳み込み層の計算イメージ
一番上の図では、左上部分から2×2の領域(網掛け部分)を対象に、 1×1+2×2+4×3+5×4=37という計算が行われています。
その下の図では、一個右にスライドさせて、同様に、 2×1+3×2+5×3+6×4=47と計算を行います。

このように、畳み込み層では、画像の左上から右下までフィルタをスライドさせながら順番に計算していく処理が行われます。

どのようなフィルタを用いるかで、どのような結果が得られるかは当然変わってきます。通常のニューラルネットワーク同様に、学習時は、精度が最も良くなるフィルタの値を求めていきます。

さて、これまでグレースケール画像という、各ピクセルがもつ情報は1つのみ(輝度値のみ)、という場合を例にしてきました。これに対し、通常のカラー画像は、各ピクセルはRGBという3つの情報持っています。
なお、このような各ピクセルが持つ情報の数を、チャンネルと呼びます。

チャンネルが複数の場合に畳み込み際は、チャンネルの数だけフィルタの数も増やします。例えばRGB画像の場合は、チャンネルが3なので、フィルタもそれにあわせて3つ用意してあげます。

RGB画像の畳み込み層のイメージ

畳み込み演算は先程説明した通りですが、チャンネルごとに通すフィルタが違う点には意してください。
上記例では、それぞれのチャンネルと対応するフィルタで演算を行い、それぞれの演算結果を足し合わせたものを出力します。

ストライドとパディング

ここで、畳込みに関する用語として、ストライドとパディングを説明しておきます。
ストライドは、どれくらいの幅でスライドさせてフィルタ演算させるかを意味します。

これまでは、ストライドが1の例を扱ってきました。
例えばストライドを2とすると、以下のように計算が行われます。

ストライドが2

出力させる特徴マップのサイズが、ストライドによって変化することを確認してください。

また、ストライドによらず畳み込みを行う、入力よりも出力の方がサイズは小さくなります。
一度の畳み込み層だけであれば問題ないですが、畳み込み層を重ねていくと、いずれ出力サイズが1になり計算ができないという問題が起きてしまいます。

この問題を解決するのが、パディングです。
簡単に言うと、元の入力画像のサイズを事前に大きくさせておくことで、出力サイズを同じにしてあげる処理です。

例えば、4×4の入力に3×3のフィルタを通すと、出力される特徴マップは2×2のサイズになります。

3×3のフィルタ

そこで、事前に入力データの周りの各ピクセルに、0で与え、6×6のサイズにしてあげます。0で埋めるため、ゼロパディングと呼びます。

6×6のインプットにストライド1、3×3のフィルタを通してあげると、出力される特徴マップのサイズがインプットサイズと同じ4×4になることががわかると思います。

パディングの例

プーリング層

本節では、畳み込み層とセットで使われるプーリング層について解説します。
プーリング層は、先程の畳み込み層を通して出力した特徴マップの解像度を下げるために使用されます。

例を下図に挙げます。 左側が畳み込み層によって出力されたものだとします。その時に、プーリング層によって演算された結果が右側です。
プーリングをしている例
隣接する2×2のピクセルの最大値を出力していることがわかります。
(左上の2×2ピクセルは、値がそれぞれ1,2,5,8なので、この中の最大値である8を出力)

このように、各領域の最大値を出力するものを、Maxプーリングと呼びます。
なおプーリングには、Maxプーリングの他にAverageプーリングと呼ばれる、領域の平均値を出力するものもあります。

領域を集約することによって、位置のズレに対して頑健 な結果を返すことができます。
また、画像の空間サイズを小さくすることで、学習するパラメータの数を減らすことができます。 畳み込み層を使う場合は、このプーリング層もセットで使われることが多いので覚えておきましょう。

まとめ

はい、今回は「畳み込みニューラルネットワークで何よ?」と聞かれたときに、ざっくりとこんな感じで説明したら納得してくれるのでは?というのをまとめました。
畳み込み層の説明をする前に、どうしてそれをやらないといけないのか、という点を説明してあげると、スッと理解が進むのではと個人的には思います。

もしよかったら参考にしてみてくださいー。

あ、こちらもいい本なので是非。

Pythonと実データで遊んで学ぶ データ分析講座

Pythonと実データで遊んで学ぶ データ分析講座