konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

【DeepLearning特訓】VAE 変分自己符号化器

E資格向けの自習アウトプット
自分用メモ

VAE(変分自己符号化器)は、生成モデルに対するアプローチの一つ
生成モデルとは、「訓練データを生成する確率 p(x) を求めたい」という分野で、訓練データを元にして特徴を捉えて訓練デーアセットに似たデータを生成することができるようになる

あるアニメのキャラを訓練データとして与えたら、そのアニメに出てきそうなキャラを作ってくれるみたいなイメージかな、かな?

オートエンコーダ

教師なし学習のひとつ。なので学習には訓練用データだけで用意すればよい
入力データを Encoder 低次元に圧縮して、Decoder で復元して生成データを作る 潜在変数 𝑧(特徴量)を近似推論する
( seq2seq でも見かけた Encoder-Decoder手法 RNNでは系列データの隠れ層の状態を渡していった)

一般的なオートエンコーダの例
f:id:konchangakita:20210207003732p:plain

VAE: Variational Auto Encoder のアーキテクチャ

VAE では潜在変数 𝑧 にガウス分布(標準正規分布)𝑧 ~ 𝑁(0,1) を仮定する
潜在変数 𝑧 を確率分布に押し込める

VAEの順伝播

Encoder で平均 μ と分散 σ を出力、正規分布パラメータ(μ,σ)で潜在変数 𝑧 をランダムサンプリングする
f:id:konchangakita:20210207003759p:plain
Encoder:CNNを使うことが多い
潜在変数: 𝑧=𝜇+𝜎∗𝜀 (𝜀 は平均0、分散1正規分布の乱数)
Reparameterization Trick という手法で、モデルはepsilon(𝜀)を通じることで確率を維持しながら、エンコーダーの勾配をそれぞれ 平均 μ と分散 σ を介して逆伝播できる 。
Decoder:潜在変数 𝑧 から 生成データを出力、CNNで戻す

VAE の損失関数

これが非常にむつかしい。。。。

 ・入力データと生成データの負の対数尤度(クロスエントロピー
f:id:konchangakita:20210206215816p:plain:w330
 ・潜在変数 𝑧 の分布と符号化された潜在変数 𝑧 の分布のカルバックダイバージェンス
f:id:konchangakita:20210206215843p:plain:w330
難しくてちょっと何言ってるわからんけど、この2つの和が総損失ということらしい
更に、確率分布をもとめるのは変分下限(わからん)の最大化に帰着するという
更に更に、変分下限の最大化はKLダイバージェンスの最小化と等しい(らしい)

対数尤度とかカルバックダイバージェンスとかから導出の式は、ちょっとややこしいので、ひとまずそこから導きだされるこの式だけは覚えておくことにする
f:id:konchangakita:20210206220350p:plain
第一項:元データと生成データの一致度、生成の精度、大きくしたい
第二項:潜在変数 𝑧 の分布(平均 0, 分散 1)と符号化された潜在変数 𝑧 の分布(平均 μ , 分散 σ )の差異、正則化項、小さくしたい


TensorFlow で実装しながら頭を整理

TensorFlow公式にチュートリアルがあるので、これに沿いながら
Convolutional Variational Autoencoder  |  TensorFlow Core

モジュールインポート

import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import mnist
import matplotlib.pyplot as plt


データセットロード
いつもの MNIST を使う

train_size = 60000
batch_size = 32
test_size = 10000

(x_train, t_train), (x_test, t_test) = mnist.load_data()

x_train = x_train.astype('float32') / 255.      #各ピクセルの値を[0,1]に制限
x_train = x_train.reshape(x_train.shape + (1,)) #(データ数, 28, 28)→(データ数, 28, 28, 1)へ変換(チャネル数を追加)
x_train = np.where(x_train > .5, 1.0, 0.0).astype('float32')
x_test = x_test.astype('float32') / 255.        #各ピクセルの値を[0,1]に制限
x_test = x_test.reshape(x_test.shape + (1,))
x_test = np.where(x_test > .5, 1.0, 0.0).astype('float32')

train_dataset = tf.data.Dataset.from_tensor_slices(x_train).shuffle(train_size).batch(batch_size)
test_dataset = tf.data.Dataset.from_tensor_slices(x_test).shuffle(train_size).batch(batch_size)
ネットワーク部分の定義クラス

Encoderネットワーク:畳み込みを2層分+1層
Decoderネットワーク:Encoderと同様に

class CVAE(tf.keras.Model):
    def __init__(self, latent_dim):
        super(CVAE, self).__init__()
        self.latent_dim = latent_dim
        self.encoder = tf.keras.Sequential([
            tf.keras.layers.InputLayer(input_shape=(28,28,1)),
            tf.keras.layers.Conv2D(filters=32, kernel_size=3, strides=(2,2), activation='relu'),    # [13,13,32]
            tf.keras.layers.Conv2D(64, 3, strides=(2,2), activation=tf.nn.relu),                    # [6,6,64]
            tf.keras.layers.Flatten(),                                                              # [2304]
            tf.keras.layers.Dense(latent_dim + latent_dim)                                          # [4]
        ])
        self.decoder = tf.keras.Sequential([
            tf.keras.layers.InputLayer(input_shape=(latent_dim)),
            tf.keras.layers.Dense(units=7*7*32, activation=tf.nn.relu),
            tf.keras.layers.Reshape(target_shape=(7,7,32)),
            tf.keras.layers.Conv2DTranspose(64, 3, strides=2, padding='same', activation='relu'),
            tf.keras.layers.Conv2DTranspose(32, 3, strides=2, padding='same', activation='relu'),
            tf.keras.layers.Conv2DTranspose(1, 3, strides=1, padding='same')
        ])

    # 学習後のデータ生成用
    @tf.function
    def sample(self, eps=None):
        if eps is None:
            eps = tf.random.normal(shape=(100, self.latent_dim))
        return self.decode(eps, apply_sigmoid=True)

    # q(z|x)
    def encode(self, x):
        mean, logvar = tf.split(self.encoder(x), num_or_size_splits=2, axis=1)
        return mean, logvar
    
    # 潜在変数の算出
    def reparameterize(self, mean, logvar):
        eps = tf.random.normal(shape=mean.shape)
        return eps * tf.exp(logvar * .5) + mean

    # p(x|z)
    def decode(self, z, apply_sigmoid=False):
        logits = self.decoder(z)
        if apply_sigmoid:
            probs = tf.sigmoid(logits)
            return probs
        return logits
定義したネットワークを確認
latent_dim = 2  # 潜在空間の次元
model = CVAE(latent_dim)
model.encoder.summary(), model.decoder.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 13, 13, 32)        320       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 6, 6, 64)          18496     
_________________________________________________________________
flatten (Flatten)            (None, 2304)              0         
_________________________________________________________________
dense (Dense)                (None, 4)                 9220      
=================================================================
Total params: 28,036
Trainable params: 28,036
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 1568)              4704      
_________________________________________________________________
reshape (Reshape)            (None, 7, 7, 32)          0         
_________________________________________________________________
conv2d_transpose (Conv2DTran (None, 14, 14, 64)        18496     
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 28, 28, 32)        18464     
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 28, 28, 1)         289       
=================================================================
Total params: 41,953
Trainable params: 41,953
Non-trainable params: 0
_________________________________________________________________
(None, None)
オプティマイズと損失関数を定義

このチュートリアル損失関数をアレンジしている模様
f:id:konchangakita:20210206230619p:plain
これをそのまま採用してみる

optimizer = tf.keras.optimizers.Adam(1e-4)

def log_normal_pdf(sample, mean, logvar, raxis=1):
    log2pi = tf.math.log(2. * np.pi)
    return tf.reduce_sum(-.5 * ((sample - mean)**2. * tf.exp(-logvar) + logvar + log2pi), axis=raxis)

def compute_loss(model, x):
    mean, logvar = model.encode(x)
    z = model.reparameterize(mean, logvar)
    x_logit = model.decode(z)
    cross_loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=x)
    logpx_z = -tf.reduce_sum(cross_loss, axis=[1,2,3])
    logpz = log_normal_pdf(z, 0., 0.)
    logqz_x = log_normal_pdf(z, mean, logvar)
    return -tf.reduce_mean(logpx_z + logpz - logqz_x)

@tf.function
def train_step(model, x, optimizer):
    with tf.GradientTape() as tape:
        loss = compute_loss(model, x)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    return loss
生成データの画像保存関数
def generate_and_save_images(model, epoch, test_sample):
    mean, logvar = model.encode(test_sample)
    z = model.reparameterize(mean, logvar)
    predictions = model.sample(z)
    fig = plt.figure(figsize=(4,4))
    
    for i in range(predictions.shape[0]):
        plt.subplot(4, 4, i+1)
        plt.imshow(predictions[i, :, :, 0], cmap='gray')
        plt.axis('off')

    plt.savefig('output/image_at_epoch_epoch{:04d}.png'.format(epoch))
    plt.show()

学習前の生成画像を一枚保存
シャッフルされたデータセットから 16枚抜き出している

num_examples_to_generate = 16

assert batch_size >= num_examples_to_generate
for test_batch in test_dataset.take(1):
    test_sample = test_batch[0:num_examples_to_generate, :,:,:]

generate_and_save_images(model, 0, test_sample)

学習前前なので、当然わけわからん画像が生成される
f:id:konchangakita:20210206232400p:plain

10 エポック分トレーニング回してみる

epochs = 10

for epoch in range(1, epochs +1):
    for x_train in train_dataset:
        train_step(model, x_train, optimizer)

    loss = tf.keras.metrics.Mean()

    for x_test in test_dataset:
        loss(compute_loss(model, x_test))

    elbo = -loss.result()
    print('Epoch: {}, Test set ELBO: {}, time elapse for current epoch: '.format(epoch, elbo))
    generate_and_save_images(model, epoch, test_sample)

エポック 1回目から何かそれっぽい画像が生成され始める
f:id:konchangakita:20210206232829p:plain

エポック 10回目で生成されたデータ
f:id:konchangakita:20210206233241p:plain


さいごに

こんな感じで、与えた訓練データに近い画像が生成されてきました
試験が終わったら、もっといろいろいじってみたい
とにかく損失関数の理解がむずかしく、実装にも時間がかかった。。
TensorFlow 自体もこれでちょっと慣れたかな
次は GAN

【DeepLearning特訓】RNN応用 seq2seq編

E資格向けの自習アウトプット
自分用メモ

seq2seq は Encoder-Decoderモデルとも呼ばれている
RNNを使って、系列データを固定次元ベクトルへ変換(エンコード)、逆に固定次元ベクトルから系列を生成できる(デコード)

エンコーダ:系列データをある規則に基づいて変換する(固定長ベクトルへ)
デコーダ:系列データへ変換(可変ベクトルへ)
f:id:konchangakita:20210131152837p:plain

日本語から英語への翻訳で考えてみると
f:id:konchangakita:20210131153008p:plain


seq2seq の構造

エンコーダでは、各タイムステップの出力は使わず隠れ層の出力を伝播させる、最終単語の隠れ層の出力をデコーダに渡す
区切り文字 で開始/終了を示す

f:id:konchangakita:20210131153049p:plain
エンコーダとデコーダの構造が違っても良い

f:id:konchangakita:20210131153109p:plain

seq2seq の問題点

エンコードの出力は固定長ベクトル
 ➔入力系列が長すぎるとうまくいかない

Attention Mechanism

エンコーダの各ステップの出力を利用する
「入力と出力でどの単語が関連しているか」という対応関係を seq2seq に学習させる

Encoderの改良

最後の隠れ状態だけを Decoder に渡していたのを、入力される文章のすべてステップの出力の集合を作る
「ひとつの固定長ベクトル」という制約から開放
f:id:konchangakita:20210131170742p:plain

Decoder の改良

各ステップでRNNの出力結果を用いて重要度を算出する
f:id:konchangakita:20210131170805p:plain


終わりに

seq2seq からの、Attention Mechanism は非常に強力らしい
ただ、numpy で書いていくにはちょっと面倒なので、また TensorFlow Keras などでサクッと試してみようかな

【DeepLearning特訓】RNN応用 GRU編

E資格向けの自習アウトプット
自分用メモ

GRU(Gated Recurrent Unit)は、LSTM を単純化したモデル
LSTMはパラメータが多く学習に時間がかかので、パラメータを減らして計算量を減らす工夫
ゲートを2つに減らし、内部状態をなくした
 ・reset ゲート (r):過去の隠れ状態をどれだけ無視するか
 ・update ゲート (z):過去の隠れ状態を更新する役割

f:id:konchangakita:20210130193220p:plain:w300

GRUの構造

行列の内積を計算するところと、要素積(アダマール積)が混ざっているの要注意
f:id:konchangakita:20210130193333p:plain
f:id:konchangakita:20210202110524p:plain:w350

Python に落とし込むと理解しやすい(順伝播まで)
コピペで試せます

# シグモイド
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# GRUモデル
# N:バッチサイズ、D:入力単語数、H:中間層の出力次元数
class GRU:
    def __init__(self, wx, wh, b):
        self.params = wx, wh, b     # # wx[D,3H], wh[H,3H], b[3H]
        
    def forward(self, x, h_prev):
        wx, wh, b = self.params
        H = wh.shape[0]

        wxz, wxr, wxh = wx[:, :H], wx[:, H:2*H], wx[:, 2*H:]    # 入力用重み
        whz, whr, whh = wh[:, :H], wh[:, H:2*H], wh[:, 2*H:]    # 前の時刻出力用重み
        bz, br, bh = b[:H], b[H:2*H], b[2*H:]                   # バイアス

        z = sigmoid(np.dot(h_prev, whz) + np.dot(x, wxz) + bz)  # updateゲート
        r = sigmoid(np.dot(h_prev, whr) + np.dot(x, wxr) + br)  # resetゲート
        h_hat = sigmoid(np.dot(r*h_prev, whh) + np.dot(x, wxh) + bh )
        h_next = (1-z) * h_prev + z * h_hat

        return h_next



適当な入力とパラメータで実行

import numpy as np

# 入力を適当に定義
x = np.arange(25).reshape(5,5)
h_prev = np.ones((5,10))

# 重みを初期化
wx = np.random.randn(5, 30)
wh = np.random.randn(10, 30)
b = np.zeros(30)

# モデルインスタンス
gru = GRU(wx, wh, b)

# 順伝播
gru.forward(x, h_prev)
array([[7.96586166e-01, 9.79622537e-03, 9.99877107e-01, 9.76598775e-01,
        9.98707164e-01, 3.56091373e-01, 9.99988035e-01, 2.55284290e-01,
        9.89543388e-01, 9.96706424e-01],
       [2.47451855e-03, 5.64910120e-03, 9.99999999e-01, 9.99999983e-01,
        1.00000000e+00, 4.40713164e-03, 1.00000000e+00, 9.99995693e-01,
        9.82514705e-01, 1.00000000e+00],
       [5.47933744e-04, 3.69982598e-03, 1.00000000e+00, 1.00000000e+00,
        1.00000000e+00, 3.66723137e-05, 1.00000000e+00, 1.00000000e+00,
        9.70604673e-01, 1.00000000e+00],
       [1.66534473e-04, 2.42152843e-03, 1.00000000e+00, 1.00000000e+00,
        1.00000000e+00, 3.04634406e-07, 1.00000000e+00, 1.00000000e+00,
        9.50828177e-01, 1.00000000e+00],
       [5.06729678e-05, 1.58418307e-03, 1.00000000e+00, 1.00000000e+00,
        1.00000000e+00, 2.53141448e-09, 1.00000000e+00, 1.00000000e+00,
        9.19010082e-01, 1.00000000e+00]])

おわりに

LSTM と GRU どちらを使うべきか?ですが、文献によると、タスクやハイパーパラメータの調整によりケースバイケースの模様
最近では LSTM の方(+LSTM改良版)が多く使われているようですが、GRUは計算量が少なくて済むので、小さいデータセットやモデルの設計を繰り返し修正しながら作っていくには向いているらしい
LSTM のその応用はここでおしまい
他の自然言語処理モデルへつづく

LSTM応用編はおわり

【DeepLearning特訓】RNN応用 LSTM編

E資格向けの自習アウトプット
自分用メモ

LSTM:Long Short-Term Memory(長短期記憶)は、RNNで系列が長くなっていった時におきてしまう「長期依存性の課題」への解決アプローチの一つです

長期依存性の課題

RNNの弱点として多くの時間ステップでわたって伝播されて勾配が消失してしまう「勾配消失問題」があります。

【課題解決アプローチ方法】
スキップ接続:時刻をスキップして直接続、粗い時間スケールを得る
Leakyユニット:前の時刻から接続をα倍、現時刻の入力(1-α)倍する
接続の削除
LSTM:ゲート付きRNN
GRU

LSTM の手法

内部状態という考え方を新しく付与
f:id:konchangakita:20210130113536p:plain:w300

内部状態に影響を与えるj3つ のゲートを導入して、勾配消失問題に対応
忘却ゲート(f):前の状態の影響率
入力ゲート(i):現時刻の入力データの影響率
出力ゲート(o):現在の状態の影響率



LSTM 順伝播の構造

各ゲートを追加してイメージはこんな感じ
かなりややこしくなってきた
f:id:konchangakita:20210130124147p:plain
f:id:konchangakita:20210130001813p:plain:w400


整理のためにも順伝播までを Pythonコードの実装

# シグモイド
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# LSTMモデル
# N:バッチサイズ、D:入力単語数、H:中間層の出力次元数
class LSTM:
    def __init__(self, wx, wh, b):
        self.params = [wx, wh, b]   # wx[D,4H], wh[H,4H], b[4H]
        self.grads = [np.zeros_like(wx), np.zeros_like(wh), np.zeros_like(b)]
        self.cache = None
    
    def forward(self, x, h_prev, c_prev):
        wx, wh, b = self.params 
        N, H = h_prev.shape

        A = np.dot(h_prev, wh) + np.dot(x, wx) + b  # [N,4H]

        # slice
        f = sigmoid(A[:, :H])       # 忘却ゲート [N,H]
        i = sigmoid(A[:, 2*H:3*H])  # 入力ゲート [N,H]
        g = np.tanh(A[:, H:2*H])  
        o = sigmoid(A[:, 3*H:])     # 出力ゲート [N,H]

        c_next = f * c_prev + i * g # 現時刻の状態 [N,H]
        h_next = o * np.tanh(c_next) # 現時刻の出力 [N,H]

        self.cache = (x, h_prev, c_prev, f, i, g, o, c_next)
        return h_next, c_next



適当な入力値で実行してみる

import numpy as np

# 入力を適当に定義
x = np.arange(25).reshape(5,5)
h_prev = np.ones((5,10))
c_prev = np.zeros((5,10))

# 重みを初期化
wx = np.random.randn(5, 40)
wh = np.random.randn(10, 40)
b = np.zeros(40)

# モデルインスタンス
lstm = LSTM(wx, wh, b)

# 順伝播
lstm.forward(x, h_prev, c_prev)


Peephole Connections

前の内部状態が各ゲートに影響与えるコネクションを追加
f:id:konchangakita:20210130124306p:plain
f:id:konchangakita:20210130013304p:plain:w480


さいごに

ここからが自然言語処理の本番だ
実践で使えるRNN応用技術にとりくんでいく

【DeepLearning特訓】RNNモデルの考え方

E資格向けの自習アウトプット
自分用メモ

RNN を利用したモデル構築の考慮事項について
E資格試験対策の意味合いが多し

RNN の学習方法

通時的誤差逆伝播(BPTT: Back Propagation Though Time)

Simple RNNの逆伝播は1つ先の時刻の勾配を含んで計算されていたので、並列で処理できない
つまりメモリコストも大きくなる
中間層同士の接続なので強力ではある

教師強制

訓練時に、前の時刻の正解ラベルを中間層へ入力として与える
並列処理が可能になるが、出力層が必要な情報を全てもっていることは少ないので強力ではない
前日の終値が確実に分かっている、株価とかでは使える


出力回帰(出力層から中間層へ接続)は、RNNほど強力ではない

教師強制で学習できる

有向グラフィカルモデル

求めたいものが出力値の確率であるとき、RNNのモデルは有向グラフィカルモデルといえる
こんなイメージ
f:id:konchangakita:20210127100739p:plain

RNNを使った「文脈で条件付けされた」系列モデリング

言葉としてややこしいが、文脈=「過去の真の値」と考える
直前の真の値が入力として中間層へ接続される
株価予想もそう(過去の終値が今日には確定している)

深層回帰結合型ネットワーク

RNN の層を深くする方法
 ・隠れた回帰状態 ➔ 階層別の回帰に分解
 ・より深い計算 ➔ 中間層でより深い接続
 ・経路が長くなる ➔ スキップ接続で緩和

再帰ニューラルネットワーク

RNN のもう一つの一般系と呼ばれている
木構造として扱う、深さを大幅に減らし長期依存を取り扱うのに有利になる可能性がある
最適な木構造をどのように構築するかは未解決の課題

エコーステートネットワーク

RNNで難しい一つ前の時刻からの重み𝑊_ℎ,"現時刻の入力重み" 𝑊_𝑥の学習が難しい
スペクトル半径を3などに固定し、単純な重みに固定する戦略

双方向RNN

出力が入力系列全体に依存していることがある
音声認識:後ろに続く音素によって、直前の音素が決定する
翻訳:言語によって語順が変わってくる

系列全体を参照できる仕組みが必要
逆方向に接続するRNNを追加し、結果をマージ
f:id:konchangakita:20210127101754p:plain

さいごに

このあたりの考え方は実際にコード組んでみないことには、習得するのが難しそう

【DeepLearning特訓】RNN 入門

E資格向けの自習アウトプット
自分用メモ

RNN(Recurrent Neural Network)回帰結合型ニューラルネットワークは、時系列データを処理するためのニューラルネットワークCNNが格子状のデータを処理するのに特化RNNは系列上の値 𝑥^*1,𝑥^*2,,,,,𝑥^*3 を処理するのに特化、可変長な系列データにも対応

応用例

機械翻訳Google翻訳など
音声認識スマホとかスマートスピーカー
CNNと組み合わせて画像からキャプションを生成

RNN モデル概要

前の時刻の中間層の出力を次の中間層へ渡す
時刻𝑡 の入力データ 𝑥^*4 とすると
(RNNでは縦に描いて横に並べていくことが多くなる)
f:id:konchangakita:20210125215003p:plain

RNN の中間層の構造

中間層での計算を表すと
f:id:konchangakita:20210125215216p:plain
計算グラフでそれぞれの要素を書き下してみる
f:id:konchangakita:20210125215240p:plain

RNN の順伝播

各時刻ごとにAffine変換と損失関数の計算を行って完成
一つ前の時刻の中間層の出力と、この時刻の入力を合わせて計算し
中間層での出力を次の時刻の中間層へ渡す
f:id:konchangakita:20210125215519p:plain
f:id:konchangakita:20210129004055p:plain

RNN の逆伝播

勾配の計算には一つ先の時刻の勾配を受け取って含めて計算し、
一つ前の時刻へ勾配計算結果を渡す
f:id:konchangakita:20210125215731p:plain
f:id:konchangakita:20210125215822p:plain

Python コードで理解してみた方が分かりやすいかも
f:id:konchangakita:20210125220249p:plain

さいごに

RNN(回帰結合型ネットワーク)からどんどんややこしくなってきました
ディープラーニングにおける時系列の考え方は自然言語処理にはかかせない要素
もう少し、RNNの基礎をつづく

*1:0

*2:1

*3:𝑡

*4:𝑡

【DeepLearning特訓】CNN 進化の歴史

E資格向けの自習アウトプット
自分用メモ

CNN発展の歴史を抑えておく
ILSVRC(ImageNet Large Scale Visual Recognition Challenge):画像認識コンペ
で 2012年の トロント大 ヒントン教授の率いるチームが CNN を採用したAlexNetで圧倒的な成績をのしてディープラーニングの火付け役となったと言われています

2012年以降のILSVRCの優勝アーキテクチャと有名どころ

AlexNet(2012) 8層
ZFNet(2013) 8層
GoogleNet(2014) 22層:Inception Moduleで並列化、Auxiliary Loss, Global Average Pooling, Pointwise Convolution
VGG(2014) :深いネットワーク構造が特徴、構造が単純なので応用しやすい
ResNet(2015) 152層:Residual Blockのショートカット構造
DenseNet(2016):Dense Blockで層の出力をショートカット伝搬する、Transition層でダウンサンプリング
SENet(2017) 152層

LeNet

1998年(!?)現Facebook AI ResearchのYann LeCun氏による CNN の元祖
畳み込み、プーリングなどのアーキテクチャ実装済
活性化関数がシグモイドであることやプーリングがMAXではなくサブサンプリングなのが違い

R-CNN(2013)

Regions with CNN(領域CNN)
Selective Searchで物体がありそうなところにあたりを付けて(Region Proposal)、SVMで分類
CNNは特徴抽出のみなので、分類部分は学習できない
問題点
 ・複数のモデル個別で学習が必要
 ・あたりをつけた候補領域の数だけCNNを回すので、実行時間がかかる
 ・何もない領域にあたりをつけた時の大きく間違ってしまう

ざっくりこんなイメージ
f:id:konchangakita:20210123133213p:plain

Fast R-CNN (2015)

R-CNN の改良版
候補領域の数の分だけ CNN 回していたのを、まず画像全体に CNN 畳み込んで特徴量マップを抽出し、候補領域で切り抜いた画像部分を特徴量マップを抽出(ROI Pooling)
CNNは一回だけになるので、速くなる

ざっくりこんなイメージ
f:id:konchangakita:20210123133244p:plain

Faster R-CNN(2015)

Fast R-CNN を更に改良
Fast R-CNN で時間がかかっていたのはほぼ Selective Search で候補領域を指定いいた部分
Selective Search に CNNの特徴マップを使用して物体検知(Regional Proposal Network)
候補領域抽出タスクと分類タスクで損失関数を共有(Multi Task Loss )し End-to-End で学習を実現

ざっくりこんなイメージ
f:id:konchangakita:20210123123421p:plain

YOLO(2016)

You Only Look Once(You Only Live Onceからもじった)
画像全体をグリッド分割し、各グリッドに対して分類・矩形指定タスクを行う
信頼度スコアを利用して、高速でリアルタイムで物体の「検出」と「識別」を行う

ざっくりこんなイメージ?
f:id:konchangakita:20210123135749p:plain


とりあえず

CNNに関連する有名どころだけをピックアップ
一旦 CNNを卒業して、自然言語処理へと勉強すすめる