1
5

More than 1 year has passed since last update.

「ディープラーニングE資格」学習メモ

Last updated at Posted at 2023-05-13

線形代数

マンハッタン距離

d(x,y) = \sum_{i=1}^n |x_i - y_i|

ユークリッド距離

d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}

マハラノビス距離

共分散行列$\Sigma$が既知

D(x,y) = \sqrt{(x-y)^T \Sigma^{-1} (x - y)}

確率・統計

連続型

期待値

E(X)=\int_{-\infty}^{\infty}xf(x)dx

分散

V(X)=E(X^2)-\{E(X)\}^2

ベイズの定理

p(A|B) = \frac{p(B|A)p(A)}{P(B)} \\
p(A|B) = \frac{p(A,B)}{p(B)} \\
p(B|A) = \frac{p(A,B)}{p(A)}

ベルヌーイ分布

平均:np、分散:npq
尤度関数:

\prod_{i=1}^n p^{x_i}(1-p)^{1-x_i}

負の対数尤度:

-\log L_D(p) = -\sum_{i=1}^n (x_i \log p + (1 - x_i) \log (1-p))

pの最尤推定量:

\hat{p} = \frac{1}{n} \sum_{i=1}^n x_i

正規分布

平均$\mu$、分散$\sigma^2$

\frac{1}{\sqrt{2 \pi \sigma^2}} \exp (- \frac{(x-\mu)^2}{2 \sigma^2})

平均$\mu$、分散1の尤度関数:

\prod_{i=1}^n \frac{1}{\sqrt{2\pi}}  \exp(-\frac{1}{2}(x_i - \mu)^2)

$\mu$の最小化問題:

\frac{1}{2} \sum_{i=1}^n (x_i - \mu)^2

最尤推定量:

\hat{\mu} = \frac{1}{n} \sum_{i=1}^n x_i

情報理論

自己情報量

I(A)=-\log P(A)

エントロピー

ある事象Xが起こったと分かった時に得られる情報量の期待値

H(X) = -\sum_x p(x) \log_2 p(x)

交差エントロピー

H(p, q) = -\sum_x p(x) \log_2 q(x)

KL-ダイバージェンス

二つの確率分布の擬距離を定量化する指標

D(p||q) = \sum_x p(x) \log_2 \frac{p(x)}{q(x)}

連続型

D(p||q) = -\int_X p(x) \log \frac{q(x)}{p(x)} dx

JS-ダイバージェンス

D_{JS} = \frac{1}{2}(D_{KL}(p||r) + D_{KL}(q||r))

カテゴリカルクロスエントロピー

-\sum_{i=1}^n \sum_{j=1}^m t_{i,j} \log p_{i,j}

相互情報量

I(X_1, Y) = H(X_1) - H(X_1|Y)

機械学習

条件付き最尤推定量

\theta_{ML} = \arg \max_{\theta} P(Y|X; \theta)

バイアスとバリアンスのトレードオフ

単純なモデル:高バイアス、低バリアンス
複雑なモデル:低バイアス、高バリアンス
バイアス:モデルの表現力が不足していることによって生じる誤差
バリアンス:訓練データの選び方によって生じる誤差
ノイズ:データの測定誤差などによって生じる誤差

前処理

ダミー変数化
バッグオブワーズ:文書における単語の出現頻度を数えて頻度ベクトルで表現する方法
不均衡データ:アンダーサンプリングとオーバーサンプリング
正規化:最小値0、最大値1;平均0、標準偏差1(標準化)
欠損処理:時系列データの場合、前後の値を用いて線形補間を行うことが多い
外れ値除去
フィルタリング:時系列データでノイズ成分が確認できる場合に行う処理。ローバスフィルタやハイバスフィルタなどで波形を整形する
リサンプリング:時系列データのサンプリング周期が目的のタスクに対して細かすぎる場合などに行う処理
部分時系列化:スライディングウィンドウ(sliding window)
周波数変換:高速フーリエ変換

標準化

class Standardization:
    # train data
    def fit_transform(self, X):
        """
        X.shape = (データ数, 次元数)
        """

        self.mean = X.mean(axis=0)
        self.stad = X.std(axis=0)
        Xsd = (X - self.mean) / self.std
        return Xsd

    # test data
    def transform(self, X):
        """
        X.shape = (データ数, 次元数)
        """

        Xsd = (X - self.mean) / self.std
        return Xsd

    def inverse_transform(self, Xsd):
        """
        Xsd.shape = (データ数, 次元数)
        """

        X = (Xsd * self.std) + self.mean
        return X

特徴選択

フィルタ法:特徴量の重要度を測定して、有効な特徴量を選択する手法
ラッパー法:学習と変数選択を何度も繰り返すことで最適な特徴量の組み合わせを探すアプローチ
埋め込み法:ラッソなどの手法を用いて学習と同時に最適な特徴量の組み合わせを見つけるアプローチ

性能指標

混同行列

混同行列 真の結果
陽性(Positive)
真の結果
陰性(Negative)
予測結果
陽性(Positive)
TP FP
予測結果
陰性(Negative)
FN TN

正解率

Accuracy = \frac{TP+TN}{TP+TN+FN+FP}

適合率

Precision = \frac{TP}{TP+FP}

再現率

Recall = \frac{TP}{TP+FN}

F値

F_{measure} = \frac{2*Recall*Precision}{Recall+Precision}

平均絶対誤差(Mean Absolute Error, MAE)

MAE = \frac{1}{n} \sum_{i=1}^n |f_i - y_i|

平均二乗誤差(Mean Squared Error, MSE)

MSE = \frac{1}{n} \sum_{i=1}^n (f_i - y_1)^2

平均二乗誤差平方根(Root Mean Squared Error, RMSE)

RMSE = \sqrt{\frac{1}{n} \sum_{i=1}^n (f_i - y_i)^2}

ハイパーパラメータ探索

グリッドサーチ:ハイパーパラメータごとに候補点を設定し、そのすべての組み合わせを探索する
ランダムサーチ:指定された各ハイパーパラメータの範囲で、探索点をランダムに選択しながら探索を進める方法
ベイズ最適化:探索を始めてから現時点までの探索結果を用いて評価指標の値を予測するモデルを構築し、その予測モデルと獲得関数を用いて次の探索点を選択する方法。予測モデルには、ガウス過程回帰がよく用いられる

線形回帰

教師あり学習の回帰手法
回帰係数を学習する際に、二乗誤差の最小化を行う
恒等写像関数を用いる

ロジスティック回帰

教師あり学習の分類手法、2クラス分類に用いられることが多い、一般化線形モデルの一種
多クラスの分類ではソフトマックス関数を利用する
学習の際に、尤度関数の最大化を行う
シグモイド関数を用いる
モデルの出力$\hat{y}$を確率値$p(y=1|x)$として扱えて、信頼度として解釈できる
全データにおける負の対数尤度関数$-\sum_n(y_n \log \hat{y_n} + (1-y_n) \log (1-\hat{y_n}))$を最小にするようなパラメータ$w, b$を学習
パラメータの解釈には、よくオッズが用いられる

オッズ \frac{\hat{y}}{1-\hat{y}} = \exp (w^T x + b)

主成分分析

教師なし学習、データから重要な成分を見つける手法
共分散行列を対角化する固有値問題に帰結される
分散共分散行列の固有ベクトルを基に新たな座標軸を複数作成し、新たな座標軸から構成される低次元空間へデータを写像する
固有値の大きい固有ベクトルの方から第一主成分、第二主成分、…と呼ばれる
各主成分の固有値を固有値の総和で割った値が寄与率と呼ばれる
主成分:分散が大きい成分
各主成分は互いに直交するように選ばれる

def pca(X, n_components=2):
    # データから平均を引く
    X = X - X.mean(axis=0)
    
    # 共分散行列作成
    cov = np.cov(X, rowvar=False)

    # 固有値や主成分方向を計算
    l, v = np.linalg.eig(cov)

    # 固有値の大きい順に並べ替え
    l_index = np.argsort(l)[::-1]
    v_ = v[:, l_index]

    # n_components分、主成分方向を取得
    components = v_[:, :n_components]

    # データを低次元空間へ射影
    T = np.dot(X, components)
    return T

次元の呪い

次元数の増加に対して指数関数的に増加する
高次元空間ではデータのほとんどが超球面上に分布してしまう球面集中現象が起こる

k-NN(k近傍法)

教師あり学習であり、分類や回帰手法である
訓練データの中でそのデータ点との距離が近い順データ点をk個取り出し、それらのラベルの最頻値を割り当てるという手法

k-means

教師なしクラスタリング手法
各データへのクラスの割り振りとクラスの中心の更新を繰り返す
手順:
1.各クラスタ中心の初期値を設定する
2.各データ点に,最も距離が近いクラスタを割り当てる
3.各クラスタの平均ベクトル(中心)を計算する
4.収束するまで 2, 3 の処理を繰り返す
欠点:初期値のセントロイドの位置が近い場合、うまく分類できないケースがある

k = 3
n = 100
data = np.random.randn(n, 2)
centroids = data[np.random.choice(np.arange(n), size=(k,))]

for l in range(10):
    indexes = np.zeros(data.shape[0])
    for centroid in centroids:
        for i, x in enumerate(data):
            indexes[i] = np.argmin(np.sum((x - centroids) ** 2, axis=1))

    for i in range(k):
        centroids[i] = data[indexes==i].mean(axis=0)

k-means++

まず、はじめにデータ点をランダムに選び1つ目の代表ベクトルとする。次に、全てのデータとそこから最も近くにある代表ベクトルの距離を求め、その距離の二乗に比例した確率でまだ選ばれていないデータ点の中から新たな代表ベクトルをランダムに選ぶ。この初期の代表ベクトルの選択方法はルーレット選択と呼ばれる

probabilities = np.repeat(1/n, n)
centroids = np.zeros((k, 2))
distances = np.zeros((n, k))

for i in range(k):
    centroids[i] = data[np.random.choice(np.arange(n), p=probabilities, size(1))]
    distances[:, i] = np.sum((data - centroids[i] ** 2, axis=1))
    probabilities = np.sum(distances, axis=1) / np.sum(distances)

サポートベクトルマシン(SVM)

教師あり学習であり、分類にも回帰にも使われるが2値分類に使われることが多い
分類では、正負によって分類する
マージンが最大となるように学習する
マージン上にあるデータ点をサポートベクトルと呼ぶ
サポートベクトル以外の(マージンの外側の)訓練データが境界面に影響しないため、外れ値に対してロバストなモデルである
スラック変数を導入することで、誤りをある程度許容しつつ、マージンを大きくとった識別面を得ることができる
スラック変数を導入したSVMはソフトマージンSVMと呼ばれる
カーネルトリック:線形分離可能にするために高次元空間に写像する際の式変形のテクニック(データを高次元に写像する際に、計算が複雑にならないように式変形するテクニック)

ガウス動径基底関数カーネル

k(x^{(i)}, x^{(j)}) = \exp(- \frac{||x^{(i)}-x^{(j)}||^2}{2\sigma^2})

正規化線形カーネル

k(x, x') = \frac{x^Tx'}{||x||||x'||}

特徴ベクトル

\phi(x) = \frac{x}{||x||}

Activation Function

Sigmoid Function(シグモイド関数)

f(x) = \frac{1}{1 + \exp(-x)} \\
f'(x) = \left( 1 - f(x) \right) * f(x)
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

Step Function(ステップ関数)

f(x) = \left\{
\begin{array}{ll}
1 & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.
def step_function(x):
    if x > 0:
        return 1
    else:
        return 0

ReLU Function

f(x) = \left\{
\begin{array}{ll}
x & (x \gt 0) \\
0 & (x \leq 0)
\end{array}
\right.
def relu(x):
    return np.maximum(0, x)

Softplus Function

f(x) = \log (1 + e^x) \\
f'(x) = \frac{1}{1 + e^{-x}}

Identity Function(恒等関数)

f(x) = x

Softmax Function

多次元分類を目的とする
入力ベクトルの要素間の差にのみ依存する

y_{k} = \frac{\exp(a_{k})}{\sum_{i=1}^n \exp(a_{i})}
softmax(z) = softmax(z - \max_i z_i)
def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y
class Softmax:
    def __init__(self):
        self.params, self.grads = [], []
        self.out = None

    def forrward(self, x):
        self.out = softmax(x)
        return self.out

    def backward(self, dout):
        dx = self.out * dout
        sumdx = np.sum(dx, axis=1, keepdims=True)
        dx -= self.out * sumdx
        return dx
def softmax(x):
    if x.ndim == 2:
        x = x - x.max(axis=1, keepdims=True)
        x = np.exp(x)
        x /= x.sum(axis=1, keepdims=True)
    elif x.ndim == 1:
        x = x - np.max(x)
        x = np.exp(x) / np.sum(np.exp(x))
    return x

Loss Function

Sum Squared Error

E = \frac{1}{2} \sum_{k}(y_{k} - t_{k})^2
def sum_squared_error:
    return 0.5 * np.sum((y - t) ** 2)

Mean Squared Error

E = \frac{1}{n} \sum_{k}^n(y_{k} - t_{k})^2
def mean_squared_error:
    return np.mean(np.square(y - t))

Cross Entropy Error

E = -\sum_{k}t_{k}\log y_{k}
def cross_entropy_error(y, t):
    delta = le-7
    return -np.sum(t * np.log(y + delta))

Mini-batch Cross Entropy Error

E = -\sum_{n}\sum_{k}t_{nk}\log y_{nk}

Batch Cross Entropy Error

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)

    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + le-7)) / batch_size

Gradient(勾配)

def numerical_gradient(f, x):
    h = le-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]

        x[idx] = tmp_val + h
        fxh1 = f(x)

        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        x[idx] = tmp_val

    return grad

Gradient Descent Method(勾配降下法)

重みの初期値設定
①Xavier:重みの要素を、前の層のノード数の平方根で除算した値($\sqrt{\frac{1}{n}}$;平均0、標準偏差$\sqrt{\frac{2}{n_1 + n_2}}$、n1が前の層、n2が後ろの層のノード数)、sigmoidやtanh関数と相性が良い
②He:重みの要素を、前の層のノード数の平方根で除算した値に対し$\sqrt{2}$をかけ合わせた値($\sqrt{\frac{2}{n}}$)、ReLU関数と相性が良い
※重みの初期値に0を設定すると、誤差逆伝播法で全ての重みの値が均一に更新されてしまう

W^{t+1} = W^{t} - \eta\frac{∂L}{∂W}
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x

    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad

    return x

バッチ学習

学習用データを一度に全て入力して重みを更新する
パラメータの初期値を決定的若しくは確率的に定めた後、同じデータを繰り返しモデルに投入し学習させる
目的関数は変わらない

ミニバッチ学習

学習用データを複数のグループに分割し、グループごとに重みを更新していく
エポックごとにミニバッチの中身を変更するため、毎回のイテレーションで目的関数が異なる
ミニバッチ学習のほうが局所的最小解にトラップされるリスクを低減できる

Layer

MulLayer

class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

    def forward(self, x, y):
        self.x = x
        self.y = y
        out = x * y
        return out

    def backward(self, dout):
        dx = dout * self.y
        dy = dout * self.x
        return dx, dy

AddLayer

class AddLayer:
    def __init__(self):
        pass

    def forward(self, x, y):
        out = x + y
        return out

    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1
        return dx, dy

ReLU Layer

class ReLu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0
        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
        return dx

Sigmoid Layer

class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out
        return dx

Affine Layer

class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.x = x
        out = np.dot(x, self.W) + self.b
        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)
        return dx

Softmax Layer

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None
        self.t = None

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        return dx

Optimization

Stochastic Gradient Descent(SGD: 確率的勾配降下法)

W \leftarrow W - \eta\frac{∂L}{∂W}
class SGD:
    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]

バッチ勾配降下法

\theta \leftarrow \theta - \frac{\eta}{m}\nabla_{\theta}\sum_iL(f(x_i, \theta), y_i)

モメンタムありの確率的勾配降下法更新式

\theta_{t+1} \leftarrow \theta_t - \eta \nabla_{\theta_t}L(\theta_t) + \alpha(\theta_t - \theta_{t-1})

Momentum

最適化に向けた移動に「慣性」をつける。同じ方向に移動していく場合は加速し、逆方向に進もうとすると「慣性」により減速する

v \leftarrow \alpha v - \eta\frac{∂L}{∂W} \\
(v \leftarrow \alpha v - \frac{\eta}{m} \nabla_{\theta} \sum_i L(f(x_i, \theta), y_i)) \\
W \leftarrow W + v
class Momentum:
    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
            params[key] += self.v[key]

Nesterov

v_{t+1} = av_t - \eta \frac{∂L}{∂\theta_t} \\
\theta_{t+1} = \theta_t + a^2 v_t - (1+a) \eta \frac{∂L}{∂\theta_t}
class Nesterov:
    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            params[key] += self.momentum * self.momentum * self.v[key]
            params[key] -= (1 + self.momentum) * self.lr * grads[key]
            self.v[key] *= self.momentum
            self.v[key] -= self.lr * grads[key]

AdaGrad

最初に勾配が大きいときは学習率も大きくなり、次第に勾配が小さくになるにつれて学習率も小さくなる
利点:手動で学習率を調整しなくても、自動調整できる
欠点:学習率低下の性質が単調的であり、学習初期の段階で学習率が過度に小さくなると収束するまで時間がかかり、局所的最適解から抜け出せなくなることがある

h \leftarrow h + \frac{∂L}{∂W} \odot \frac{∂L}{∂W} \\
W \leftarrow W - \eta \frac{1}{\sqrt{h}} \frac{∂L}{∂W}
class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + le-7)

RMSProp

AdaGradの欠点だった単調的な学習率低下が改善された
$\alpha$によって過去の勾配の影響を抑えるとともに、$h_t$を優先して反映させるという効果を狙っている
過去の全て勾配の平均ではなく、指数移動平均を採用している

h_t = \alpha h_{t-1} + (1 - \alpha) \frac{∂L}{∂W} \odot \frac{∂L}{∂W} \\
W^{t+1} = W^t - \eta \frac{1}{\sqrt{h} + \epsilon} \frac{∂L}{∂W}
class RMSprop:
    def __init__(self, learning_rate=0.01, decay_rate = 0.99):
        self.learning_rate = learning_rate
        self.decay_rate = decay_rate
        self.h = None
        
    def update(self, params, grad):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grad[key] * grad[key]
            params[key] -= self.learning_rate * grad[key] / (np.sqrt(self.h[key]) + 1e-7)

Adam

t \leftarrow t+1 \\
g_t \leftarrow \nabla_{\theta} f_{t} (\theta_{t-1}) \\
m_t \leftarrow \beta_1 \cdot m_{t-1} + (1-\beta_1) \cdot g_t \\
v_t \leftarrow \beta_2 \cdot v_{t-1} + (1-\beta_2) \cdot g_t^2 \\
\hat{m_t} \leftarrow m_t / (1-\beta_1^t) \\
\hat{v_t} \leftarrow v_t / (1-\beta_2^t) \\
\theta_t \leftarrow \theta_{t-1} - \alpha \cdot \hat{m_t} / (\sqrt{\hat{v_t}} + \epsilon)
class Adam:
    def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999):
        self.learning_rate = learning_rate
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None
        
    def update(self, params, grad):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        lr_t  = self.learning_rate * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)         
        
        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grad[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grad[key] ** 2 - self.v[key])
            
            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

Normalization

Batch Normalization

平均0かつ分散1の分布
テスト時には、$\mu$と$\sigma$は訓練時の移動平均を用いる
バッチ正規化の特長:重みの初期値の設定に神経質にならなくてよいこと、学習率を大きくできること、ドロップアウトの使用を減らせることなど
バッチ正規化には、オンライン学習に適用しても意味をなさない、ミニバッチごとのデータ数が極めて少ない場合に収束性が悪くなる

\mu_B \leftarrow \frac{1}{m} \sum_{i=1}^m x_i \\
\sigma_B^2 \leftarrow \frac{1}{m} \sum_{i=1}^m (x_i - \mu_B)^2 \\
\hat{x_i} \leftarrow \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \\
y_i \leftarrow \gamma \hat{x_i} + \beta

Layer Normalization

レイヤー正規化では、平均$\mu_i$、標準偏差$\sigma_i$を同じ層内の全てのユニットを対象として計算するため、ミニバッチ内の各のデータごとに、正規化に用いる平均と標準偏差が異なる

Instance Normalization

インスタンス正規化では、特徴マップの高さ(H)及び幅(W)の軸に対して平均と標準偏差を計算して正規化を行う。そのため、ミニバッチ(N)、チャンネル(C)ごとに正規化に用いる平均と標準偏差が異なる
スタイル転送にインスタンス正規化を導入した理由:スタイル転送の出力を観察してみると、合成画像のコントラストはコンテント画像のコントラストに依存していないように見られたため

Regularization

L1正則化を適用した線形回帰をラッソ
L2正則化を適用した線形回帰をリッジ
L1正則化とL2正則化を組み合わせた線形回帰をエラスティックネット
正則化係数を大きく設定し過ぎると、過剰適合ではなく過少適合の傾向が現れる

L1正則化(Lasso回帰)

要素に0を含むことが多くなり、スパースになりやすい

||x||_1 = |x_1| + |x_2| + ... + |x_n|

L2正則化(Ridge回帰)

重みの大きさが小さくなり汎化性能が高くなる

||x||_2 = \sqrt{x_1^2 + x_2^2 + ... + x_n^2}

荷重減衰(Weight Decay)

C'(W) = C(W) + \lambda (|w_1| + |w_2| + ... + |w_n|)
weight_decay_lambda = 0.1
loss = loss_function(t, y) + weight_decay_lambda * np.sum(np.abs(W))
C'(W) = C(W) + \frac{1}{2} \lambda \sqrt{(w_1^2 + w_2^2 + ... + w_n^2)}
for idx in range(1, self.hidden_layer_num + 2):
    weight_decay_gradient = self.weight_decay_lambda * self.layers['Affine' + str(idx)].W

Dropout

class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask

いろいろな

いろいろなモデル

VGG:2014年 フィルタサイズ3x3の畳み込み層を複数積み重ねる
GoogLeNet:2014年 ILSVRC優勝 並列に配置するインセプションモジュール
ResNet:2015年 ILSVRC優勝 スキップ接続を採用(恒等写像を介して、特徴マップ同士を足し合わせるショートカット結合)
DenseNet:2017年 スキップ接続を採用したCNN(ショートカットされた特徴マップを足し合わせるのではなく、特徴マップをチャンネル方向を結合)
R-CNN:2014年 選択的探索、候補領域ごとにCNNの順伝播計算を行う必要があり、検出速度が低下
Fast R-CNN:2015年 画像全体を複数回畳み込んで特徴マップを生成し、得られた特徴マップから各候補領域に該当する部分を抜き出す
Faster R-CNN:2015年 領域候補の提案をニューラルネットワークによって実現し、選択的探索を排除した
YOLO:2016年 一枚画像を特定サイズのグリッドに分割し、その分割されたグリッドごとにクラス推定とバウンディングボックスの回帰を行う
SSD:2016年 大きさの異なる複数の特徴マップを使ってクラス分類やバウンディングボックス回帰を行う
U-Net:2015年 エンコーダ側が持つ入力の位置情報をデコーダに渡す時に、DenseNetのようにエンコーダ側の特徴マップとデコーダの特徴マップをチャンネル方向に結合する(2017年pix2pixにも利用されている)
MobileNet:Depthwise convolution(チャンネルごとに空間方向へ畳み込みを行う)、Pointwise convolution(チャンネル方向のみの相関を取るフィルタサイズ1x1の畳み込み)
FCN:2015年 全結合層を用いずに、畳み込み層のみを利用
SegNet:2017年 エンコーダ側の最大値プーリングで取得した値の場所を記録し、その情報をデコーダ側のアップサンプリングで利用

再帰型ニューラルネットワーク(RNN)

リカレント層から出力された中間状態を次の時刻のリカレント層に伝えるという計算を行う

LSTM

メモリセルとゲートという工夫を組み込んだことで、過去の情報を長期間保持することが可能

隠れマルコフモデル(HMM)

観測できない状態を加味したマルコフ過程のモデル
音声認識や形態素解析など、時系列データからの特徴抽出に応用されている

変分自己符号化器(VAE)

入力データを再現するように学習
通常の自己符号化器と異なり、VAEは入力データの生成メカニズムを捉える

制限ボルツマンマシン(RBM)

可視層間及び隠れ層間の結合をなくすという制約を設けて、学習に必要な計算コストを下げたモデル

プーリング層

移動に対する不変性に対して効果がある

パディング

画像の端の特徴を捉えやすくなる

im2col

畳み込み演算を効率的に行うためのアルゴリズム

万能近似定理

隠れ層が少なくとも1つ含まれるネットワーク
活性化関数が線形の場合においては成り立たない
ネットワークの大きさについては述べていない

Convolutional Neural Network(畳み込みニューラルネットワーク)

ストライド

OH = \frac{H + 2P - FH}{S} + 1 \\
OW = \frac{W + 2P - FW}{S} + 1

im2col

col. OH, OW = im2col(x, (N, C, H, W), (KH, KW), (SH, SW), (PH, PW))
col.shape = (ミニバッチサイズ,出力縦幅,出力横幅,チャンネル数,フィルタ縦幅,フィルタ横幅)

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    N, C, H, W = input_data.shape
    out_h = (H + 2 * pad - filter_h) // stride + 1
    out_w = (W + 2 * pad - filter_w) // stride + 1

    # [(0,0), (0,0), (PH, PH), (PW, PW)]
    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N * out_h * out_w, -1)
    return col

def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    N, C, H, W = input_shape
    out_h = (H + 2 * pad - filter_h) // stride + 1
    out_w = (W + 2 * pad - filter_w) // stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)

    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]

Convolution Layer

class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        self.x = None   
        self.col = None
        self.col_W = None
        
        self.dW = None
        self.db = None

    def forward(self, x):
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)

        col = functions.im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        dx = functions.col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx

Pooling Layer

class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad
        
        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = functions.im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h * self.pool_w)

        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)
        
        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,)) 
        
        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = functions.col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)
        
        return dx

VGG

フィルタサイズ3x3の層を複数積み重ねることで、大きなフィルタで畳み込むのと同等な結果を、少ないパラメータで実現する

GoogLeNet

インセプションモジュール:
1x1の畳み込みフィルタの追加によって、次元を削減しているため非零パラメータの数が減ることによって、計算効率が向上
大きな畳み込みフィルタを小さなフィルタのグループで近似することにより、モデルの表現力とパラメータ数のトレードオフを改善している
小さなネットワークを1つのモジュールとして定義し、モジュールの積み重ねでネットワークを構築している
Auxiliary classifiers:
勾配を計算する過程でクラス分類を行っている
アンサンブル学習:
複数の学習器を用意して、それらの予測結果を統合して汎化性能を高める手法
Global Average Poolingというプーリングの手法が採用されている。各チャンネルの画素平均値を求め、各チャンネルの平均値を要素とするベクトルに変換する

ResNet

恒等写像を介して特徴マップ同士を足し合わせるショートカット結合を導入することで、100層を超える非常に深いニューラルネットワークにおいても学習を可能にした
Residual Block(残差ブロック)が導入されることで、ブロックへの入力にこれ以上の変換が必要ない場合は重みが0となり、小さな変換が求めれる場合は対応する小さな変動をより見つけやすくなることが期待される
Wide ResNet:オリジナルのResNetより層数が少ない、各Residual blockでのフィルタ数が多い、高速に学習が行える

EfficientNet

ユニット数、レイヤー数、入力画像の大きさのバランスを取ってスケーリングする、効率的な手法を提案した

DenseNet

あるブロック内の畳み込み層は、そのブロック内で既に作られた特徴マップ全てをチャンネル方向に結合したものを入力とすることで、ショートカット結合の効果に加えて、少ないパラメータでの性能向上を実現した

R-CNN

領域候補を取得するために選択的探索法を用いる
CNNから出力された特徴マップSVMに入力しカテゴリ識別を行い、回帰によって正確な領域の推定を行う
CNN入力層のサイズが227x227pixelの固定サイズ
カテゴリ推定のSVMなど各学習の目的ごとに別々に学習させる必要があるため、End-to-End(一気通貫)学習ができない
欠点:
①候補領域ごとに畳み込みによる特徴抽出が必要になる
②選択的探索による候補領域の決定に時間を要する

Fast R-CNN

RoI全結合層に与えるだけで良いため、R-CNNと比べ高速化を実現している
入力画像からRoIを抽出する際に選択的探索法(Selective Search)という手法を用いており、その処理に時間がかかるという問題が残されていた
画像全体を複数回畳み込んで特徴マップを生成し、得られた特徴マップから各候補領域に該当する部分を特定することにより、R-CNN欠点①を解決した

Faster R-CNN

高速化の制約要因となっていたSelective Searchを排除し、入力画像からCNNで特徴マップを得て、特徴マップからRoIを抽出することにより、さらなる高速化を実現した
Fast R-CNNと違い、Fster R-CNNの矩形領域回帰の誤差関数には、Anchorが取り入れられている
入力画像からCNNで特徴マップを得て、Rol Poolingを用いて固定サイズの特徴マップを得る
領域候補の提案を行う手法として、Region Proposal Networkが用いられる
End-to-Endで学習させることが可能
領域提案ネットワークと呼ばれるサブネットワークで候補領域を予測することにより、R-CNN欠点②を解決した

L(\{p_i\},\{t_i\}) = \frac{1}{N_{cls}} \sum_i L_{cls} (p_i, p_i^*) + \lambda \frac{1}{N_{reg}} \sum_i p_i^* L_{reg} (t_i, t_i^*)

Mask R-CNN

Faster R-CNNの出力部にMask機構を追加
背景と各物体をピクセル単位で分類するインスタンスセグメンテーションが可能
候補領域座標の小数点以下の数値の切り捨てを回避することにより、領域のずれを軽減し、Faster R-CNNのRoI Poolingを改善することで、物体検出精度を向上

YOLO

高速な物体検出モデルとしては、入力画像を複数の小領域に分割し、各小領域ごとにクラス分類とバウンディングボックスの位置や大きさなどの回帰を行う
画像をある固定長で分割したセルごとに固定でN個の候補領域が推定される

SSD

高速な物体検出モデルとしては、大きさの異なる複数の特徴マップを使ってクラス分類やバウンディングボックス回帰を行う

FCOS

特徴マップ上の(i,j)ごとにGroud Truthとの上下左右の距離を回帰する
ポジティブサンプル数とネガティブサンプル数(検出物と背景)の不均衡(アンカーボックス欠点)を改善している

centerness* = \sqrt{\frac{\min(l^*, r^*)}{\max(l^*, r^*)} \frac{\min(t^*, b^*)}{\max(t^*, b^*)}}

FCN(Fully Convolutional Network)

CNNの内部の全結合層を無くすことで、入力画像のサイズを固定する制約がなくなった。入力画像サイズが異なっていても単一のモデルで予測できる
FCNの逆畳み込みネットワークでは、逆畳み込みを用いてアップサンプリングを施す
ダウンサンプリングされた特徴マップに対して単純に逆畳み込みを適用するだけでは粗くなってしまい、十分な出力結果が得られないため、スキップ接続を用いて情報ロスが発生する前の情報をアップサンプリング処理に入力する
大きさの違う複数の特徴マップを転置畳み込みで拡大し、大きさを揃えた上で各画素ごとに足し合わせることで、物体の詳細な情報を捉えることができる

SegNet(セグメンテーション)

スキップ接続として符号化器(encoder)の特徴マップの代わりにMax poolingインデックスが用いられる。これによりSegNetはFCNに比べて省メモリである
セマンティックセグメンテーションでモデルの性能は、予測された領域とラベルとして与えられた領域の重複度によって評価されることが多い
Dice係数:IoUの欠点を緩和し共通部分の面積をより重要視した指標

Dice(S_{true}, S_{pred}) = \frac{2|S_{true} \cap S_{pred}|}{|S_{true}| + |S_{pred}|} \\
IoU \leq Dice

U-Net

ネットワークがU字を描いているからであり、EncoderとDecoderで構成されている
画像内の物体の位置情報を抽出する効果がある
スキップ接続、物体の位置情報伝達、物体の位置が抽出される、ピクセル単位の分類が可能、入力画像と出力画像でサイズは一致
エンコーダで作成した特徴マップをデコーダに伝達する
デコーダはエンコーダから渡された特徴マップと前層から渡された特徴マップチャンネル方向に結合し、転置畳み込みで拡大する

自己符号化器(autoencoder)

出力が入力と同じになるように学習させる、次元削減可能なニューラルネットワークのモデル
入力層サイズよりも中間層サイズのほうが小さい自己符号化器を不完備な自己符号化器、1層目の変換器を符号化器(encoder)、2層目の変換器を復号化器(decoder)と呼ぶ
出力層の活性化関数は恒等写像関数が採用される
潜在変数自体の構造を把握することが難しいため、変分自己符号化器では、潜在変数が正規分布(ガウス分布)に従っていると仮定して学習を進める
変分自己符号化器では、再構成誤差に加え$D_{KL}(N(\mu, \sigma) | N(0, 1))$を正則化項として加える
損失関数にペナルティ項$\Omega$を加えることがあり、$\lambda \sum_i ||\nabla_x h_i||^2$を用いたものは縮小自己符号化器と呼ばれる

Reparameterization Trick(再パラメータ化トリック)

エンコーダとデコーダの間には潜在変数のサンプリング処理が入るため、エンコーダ側まで誤差を逆伝播することが困難となる
そこで、エンコーダからのサンプリングを、多変量標準正規分布からのサンプル$\varepsilon$を使って$z = \mu(x) + \sum(x)^\frac{1}{2} \varepsilon$として行うことで、エンコーダ側まで勾配を伝播することができる
VAEでは、サンプリングした$z$をデコードしたものが入力と近くなるように、かつ平均$\mu$、標準偏差$\sigma$のガウス分布が平均0、標準偏差1のガウス分布に近くなるように学習する

z = \mu + \varepsilon \sigma \\
(\varepsilon \sim N(0,1))

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

RNN

y_2 = x_2W_x + (x_1W_x + h_0W_h)W_h \\
\frac{∂L}{∂W_x} = x_1^T(\frac{∂L}{∂y_2}W_h^T + \frac{∂L}{∂y_1}) + x_2^T \frac{∂L}{∂y_2}
class RNN:
    def __init__(self, Wx, Wh, h0):
        self.params = [Wx, Wh]
        self.h_prev = h0

    def forward(self, x):
        Wx, Wh = self.params
        h_prev = self.h_prev
        t = np.dot(h_prev, Wh) + np.dot(x, Wx)
        # 活性化関数は恒等写像関数とする
        h_next = t
        self.h_prev = h_next
        return h_next

LSTM(Long short-term memory)

入力ゲート:記憶セルに追加されるgの各要素を弱める度合いを決めるゲート
忘却ゲート:記憶セルの各要素を弱める(忘れる)度合いを決めるゲート
出力ゲート:取り出した記憶について、各要素を弱める度合いを決めるゲート
記憶セル:過去の情報を記憶する部分

i = \sigma(W_ih_{t-1} + U_ix_t) \\
f = \sigma(W_fh_{t-1} + U_fx_t) \\
o = \sigma(W_oh_{t-1} + U_ox_t) \\
g = tanh(W_gh_{t-1} + U_gx_t) \\
c_t = (c_{t-1} \otimes f) \oplus (g \otimes i) \\
h_t = o_t \otimes g(c_t)
n_input = 100
n_hidden = 256
w = np.random.randn(n_input + n_hidden, n_hidden * 4)
b = np.zeros(n_hidden * 4)

def lstm(x, h, c):
    inputs = np.concatenate((x, h), axis=1)
    inputs = np.matmul(inputs, w) + b
    z, i, f, o = np.hsplit(inputs, 4)

    def sigmiod(u):
        return 1.0 / (1.0 + np.exp(-u))

    input_gate = sigmoid(i)
    forget_gate = sigmoid(f)
    output_gate = sigmoid(o)

    # c_next = (c_prev * forget_gate) + (g * input_gate)
    # h_next = np.tanh(c_next) * output_gate
    c_next = c * forget_gate + np.tanh(z) * input_gate
    h_next = c_next * output_gate

    return c_next, h_next

覗き穴(peep hole)

o_t = \sigma(x_tW_o + h_{t-1}U_o + c_t \odot V_o + b_o)

GRU

リセットゲート:過去の隠れ状態を弱める程度を決めるゲート
更新ゲート:過去の隠れ状態と仮の隠れ状態の混合割合を決めるゲート

def gru(x, h, W_r, U_r, W_z, U_z, W, U):
    """
    x: inputs, (batch_size, input_size)
    h: outputs at the previous time step, (batch_size, state_size)
    W_r, U_r: weights for reset gate
    W_z, U_z: weights for update gate
    U, W: weights for new state
    """
    r = _sigmoid(x.dot(W_r.T) + h.dot(U_r.T))
    z = _sigmoid(x.dot(W_z.T) + h.dot(U_z.T))
    h_bar = np.tanh(x.dot(W.T) + (r * h).dot(U.T))
    h_new = (1-z) * h + z * h_bar
    return h_new

系列変換モデル

RNNエンコーダ・デコーダや、seq2seqなど
エンコーダの最後の時刻の隠れ状態をデコーダの1時刻目の隠れ層に伝える
教師強制:学習時もこのように計算すると学習が不安定になりやすいため、前の時刻の出力ではなく、前の時刻の正解を入力にすることがある

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

入力の重みUと隠れ層の重みVをランダムな値で固定し、出力の重みのを学習
シンプルな線形回帰の問題になるので、勾配消失や勾配爆発が発生しなくなり、また、学習が早くなる

スキップ接続

長期依存性の課題への対策の一つとして、隠れ層を1時刻以上スキップさせた接続を追加する方法(2つ以上先の時刻の隠れ層にも接続すること)

リーキーユニット

隠れ層に線形結合を導入して移動平均の結果を得る

双方向RNN

過去から未来だけでなく、未来から過去への方向も考慮した再帰型ニューラルネットワーク
通訳や音声認識タスクなどで用いられる

アテンション

ソフト・アテンション:ソフトマックス関数などで確率分布を求め、その確率分布を用いて重み付き平均を得る方法
ハード・アテンション:ソフトマックス関数などで確率分布を求め、その確率分布に従って抽出された1点だけを得る方法。ただし、確率分布を考えずに1点を決める場合もある
グローバル・アテンション:通常の系列変換モデルでは、長い系列のデータを学習することが難しく、時間の初期においてエンコーダに入力された情報がデコーダまで伝わりづらくなるという問題がよく生じる。デコーダの対象時刻の隠れ層の出力とエンコーダの各時刻の隠れ層の出力から重みベクトルを算出し、その重みベクトルとエンコーダの各時刻の隠れ層の出力の重み付き和をとり、コンテクストベクトルを得る。コンテクストベクトルはデコーダの計算で利用される

def forward(self, hs, h):
    """
    hs:エンコーダでの全ての隠れ状態(データ数,時刻数,隠れ層のノード数)
    h:デコーダでのある時刻の隠れ状態(データ数,隠れ層のノード数)
    """
    N, T, H = hs.shape

    # デコーダのある場所の隠れ状態を3次元配列に変形する
    hr = h.reshape(N, 1, H).repeat(T, axix=1)

    # エンコーダの隠れ状態とコードの隠れ状態を掛けて足し合わせることで内積をとる
    t = hs * hr
    s = np.sum(t, axis=2)

    a = softmax(s)
    return a

自然言語処理

言語モデル:単語や文書が生成される確率をモデル化
N-gramモデル:連続した単語の並び

BLEU

機械翻訳の精度指標
cは機械翻訳の長さ、rは参照翻訳の長さ

BP = \left\{
\begin{array}{ll}
1 & (c \geq r) \\
exp(1 - \frac{r}{c}) & (c \lt r)
\end{array}
\right.

word2vec

単語の分散表現を学習する方法として、Continuous Bag-of-WordsとSkip-gramの2つのニューラルネットワークが実装されている

CBOW:周辺単語から対象となる1単語を予測する
Skip-gram:対象となる1単語から周辺単語を予測する
いずれも入力層側の重み$W_{in}$と出力層側の重み$W_{out}$をパラメータとして持つが、一般に$W_{in}$を単語埋め込み行列として利用する

巨大なコーパスを扱う場合、通常の多クラス分類を行うと計算速度が問題になる。そのため、負例サンプリングという手法を用いて、損失関数の計算時間の短縮を図る

負例サンプリングを適用した場合の出力層では、1個の正例とk個n負例だけを対象として、シグモイド関数交差エントロピー誤差関数の計算を行う

k個の負例は、コーパスの中から単語の出現頻度に基づいてサンプリングする

Google's Neural Machine Translation(GNMT)

8層のエンコーダ層と8層のデコーダ層からなる深層LSTMネットワークであり、残差接続(residual connection)及びアテンション機構を用いている

8層のエンコーダ、及び8層のデコーダに対するGPUの割り当ては、エンコーダ及びデコーダのそれぞれ1層ごとに、1個のGPUを割り当てる(合計16個)

並列効率を高めるため、デコーダ側の**最下層(第1層)の出力とエンコーダ側の最上層(第8層)**の出力を用いてアテンションを行っている

デコーダ側の時間ステップ$i$についてアテンションの計算を行ったら、その結果を時間ステップ$i+1$のデコーダの全層に送る

LSTMレイヤの多層化
双方向LSTM層(エンコーダの1層目のみ)
スキップコネクション
複数GPUを利用したモデル並列処理の分散学習

WaveNet

同時確率分布の数式は、各音声サンプル$x_t$が$t$より前の全ての時刻ステップにおける音声サンプルによって条件付けられることを意味
Wavenetでは、ダイレイト・コーザル畳み込み(dilated causal convolution)を採用することによって、少ないパラメータで長い時間の特徴を考慮することを可能としている

コーザル畳み込みは、過去の時刻ステップだけを用いて畳み込みを行う方法
特徴:モデルが入力データの時間順序を破らないことを保証できる。再帰結合を持たないため、RNNに比べて計算が高速である。画像処理におけるマスク畳み込みと似通った手法である

ダイレイト畳み込みは、少ないパラメータで広い範囲を畳み込むことを意図しており、フィルタを適用する入力データの場所を数ステップずつスキップし、疑似的にフィルタ長より大きな受容野を持たせる
層が深くなるにつれて入力の間隔を開けることによって、データが長くなっても効率的に計算できる

Transformer

seq2seqと同じようにエンコーダとデコーダからなる
エンコードとデコードはそれぞれN回繰り返される
内部には、いくつものアテンション機構が配置されている
それらアテンション機構の基本モジュールは、スケール・ドットプロダクト・アテンション(scaled dot-product attention)と呼ばれている

Attention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}} V)

マルチヘッド・アテンションの入力順序:V、K、Q

エンコーダにおけるマルチヘッド・アテンションでは、Query・Key・Valueは全て直前のエンコーダの出力から伝わってくる

デコーダにおけるマルチヘッド・アテンションは2種類がある
1つ目のマルチヘッド・アテンションは、セルフ・アテンションであるが、予測すべき単語の情報が予測時に利用されないようにするためにマスクがかけられる。マスクがかけられた場所の値は、ソフトマックス関数に入力される前に$-\infty$に置き換えられる
2つ目のマルチヘッド・アテンションでは、keyとvalueはエンコーダ、queryはデコーダから伝わってくる

ポジショナル・エンコーディングは、posを単語の位置、$i$を埋め込み後の次元、$d_model$をマルチヘッド・アテンションの入力(出力)の次元数として、偶数番目の次元、奇数番目の次元がそれぞれ以下のように与えられる。

PE_{(pos, 2i)} = sin(\omega_{pos, i}) \\
PE_{(pos, 2i+1)} = cos(\omega_{pos, i}) \\
\omega_{pos, i} = \frac{pos}{10000^{2i/d_{model}}}

BERT(Bidirectional Encoder Representations from Transformers)

BERTの学習は、事前学習と再学習の2段階で行われる
Attention機構を用いて、文章を一度にまとめて読み込むため、学習が困難
事前学習では、入力系列の中の欠落した単語を予測するタスク2つの入力文に対する一方の文が他方の文の次に位置するかどうかを判別するタスクを同時に解くことによって、それぞれ、対象言語における局所的な特徴大域的な特徴を捉える
そして、そこで得られたパラメータを初期値として、タスクに合わせてパラメータを訓練し直すファインチューニングを行うことで、質疑応答やセンチメント分析などの11種類の自然言語処理タスクでSoTAを達成した
マスクされた単語を予測する問題は多クラス分類問題
次の文かどうかを予測する問題は2クラス分類問題
事前学習はコーパスデータによる教師なし学習によって行われる
再学習は教師あり学習によって行われる
トークン埋め込み:単語の違いを表す情報
セグメント埋め込み:文の違いを表す情報
ポジジョン埋め込み:単語の順序を表す情報

GPT(Generative Pre-Training)

GPT-3のパラメータ数は1750億個にもなり、約45TBのコーパスで事前学習を行う
GPTの構造はTransformerを基本とし、「ある単語の次に来る単語」を予測し、自動的に文章を完成できるように、教師なし学習を行う

GPT-2の変更点:
Layer Normの位置を前にずらしたこと
最後のself-attentionブロックの後にもLayer Norm層を入れたこと
バッチサイズやデータセットを大きくした

GPT‐3の変更点:
埋め込みサイズ、層、Multi-Head Attentionの数、コンテキストウィンドウの数を増やした

GPT-3では、fine-tuningをしない、改めて勾配を更新し直すことをしない。GPT-3の推論は、zero-shot、one-shot、few-shotに分類できる

BERT:
Transformerのエンコーダー部分を使っている
新しいタスクに対してファインチューニングが必要
双方向Transformer
GPT:
Transformerのデコーダー部分を使っている
ファインチューニングをしない
単一方向Transformer

生成ネットワーク

GAN(Generative Adversarial Networks)

GANの活性化関数として、生成器の出力層にtanh、出力層以外の各層にReLU、識別器の全層にLeaky ReLuが適用される

\min_G \max_D V(D, G) = E_{x, p_{data}(x)}[\log D(X)] + E_{z, p_{z}(z)}[\log(1-D(G(z)))]

Discriminatorにより最大化された価値関数

E_{x,p_{data}} \log[\frac{p_{data}(x)}{p_{data}(x) + p_g(x)}] + E_{x,p_g} \log[1 - \frac{p_{data}(x)}{p_{data}(x) + p_g(x)}]
def discriminator_loss(real_output, fake_output):
    real_loss = tf.keras.losses.BinaryCrossentropy(tf.ones_like(real_output), real_output)
    fake_loss = tf.keras.losses.BinaryCrossentropy(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss

def generator_loss(fake_output):
    return tf.keras.losses.BinaryCrossentropy(tf.ones_like(fake_output), fake_output)

いろいろなGAN

LAPGAN:CNNを使ったモデルで、低解像度の画像を学習し、高解像度の画像を学習するような多段構造のネットワークを作成したことで、高解像度の画像を作成することに成功した
ACGAN:Generatorに入力ノイズとしてのベクトルと入力画像のクラス情報を同時に与え、Discriminatorが生成画像の真偽に加えてクラスの判別も行う
SNGAN:新しい正規化であるSpectral normalizationを提案したもの
DCGAN:Generatorに入力ノイズとしてのベクトルだけを与え、Discriminatorが生成画像の真偽の判別を行う。Pooling層を使用しない。全結合層を取り除く。batch normalizationをgeneratorとdiscriminatorの両方に適用する。generatorでは活性化関数に出力層だけTanhを使って、それ以外の層では全てReLUを使う。discriminatorでは全ての層でLeaky ReLUを使う

Pix2pix

線画の自動着色、グレースケール画像からRGB画像化、航空写真の地図画像化
例えば、エッジからカラー写真への変換タスクを学習する場合、エッジの画像を条件として生成器、識別器それぞれに与える影響という動作になる

\min_G \max_D V(G, D) + \lambda L(G) \\
V(G, D) = E_{c,x} [\log D(c,x)] + E_{c,x} [\log (1-D(c,G(c,z)))] \\
L(G) = E_{x,c,z} [||x - G(c,z)||_1]

pix2pixは、ConditionalGANの目的関数V(G,D)にL(G)を追加することで、画像の大域的な情報を捉えられるようになり、より違和感のない画像を生成することに成功した
識別器が画像の高周波数の構造を捉えられるように、画像の中でNxNのバッチがそれぞれ本物か偽物かを判定するようにする。ここでは、バッチサイズよりも離れた場所にあるピクセル間は独立であることを仮定している

U-Netを生成器に用いる
U-Netでは、スキップ接続を用いてエンコーダの情報をデコーダへ渡し、連結するため、白黒画像からカラー画像への変換タスクでは生成器において、入出力画像間でエッジの位置を共有するという処理を行う

VQ(ベクトル量子化)-VAE

VAEは潜在表現を学習するモデル(潜在変数が無視される課題を持つ)
VQ-VAEでは、潜在埋め込みベクトルを離散的なコードにする(VAE課題に対処)
VQ-VAEでの事後分布:

q(z=k|x) = \left\{
\begin{array}{ll}
1 & (k=arg\min_j||z_e(x) - e_j||_2) \\
0 & (otherwise)
\end{array}
\right.

k,jは離散潜在埋め込みベクトルのインデックス
$e_j$はインデックスjの潜在埋め込みベクトル
$z_e(x)$はエンコーダの出力
デコーダへの入力:

z_q(x) = e_k, \; where \, k=arg\min_j||z_e(x) - e_j||_2

強化学習

方策勾配

\bigtriangledown j(\theta) \propto E_{\pi_\theta} [\bigtriangledown \log \pi_\theta (a | s) Q^{\pi_\theta} (s, a)]

ベルマン方程式

価値$r_{t+1} + \gamma V_\pi(s_{t+1})$に、行動確率$\pi(a|s)$、そして遷移状態確率$T(s'|s,a)$をかけることで、価値の期待値を算出できる

V_\pi(s) = \sum_a \pi (a|s) \sum_{s'} T(s'|s,a) (R(s,s') + \gamma V_\pi(s'))

動的計画法

環境の完全なモデルがマルコフ決定過程として与えられている場合にのみ適用できる方法
全ての遷移の完全な確率分布が必要

モンテカルロ法

サンプル収益を平均化することに基づいて強化学習問題を解く方法
現時点の方策を用いてエピソードを生成し、その結果を用いて価値関数と方策を改善
1エピソードの実績で修正を行う方法
TD法と比較して、修正の妥当性があり、修正の速さが遅い

V(s_t) \leftarrow V(s_t) + a((r_{t+1} + \gamma r_{t+2} + \gamma^2 r_{t+3} + ... + \gamma^{T-1} r_T) - V(s_t))

TD学習

目標の価値と現在の価値のずれを修正していことによって、価値関数を推定する方法
代表的な手法として、SarsaとQ学習がある
Sarsa行動価値関数の更新式

Q(S_t,A_t) \leftarrow Q(S_t,A_t) + \alpha[R_{t+1} + \gamma Q(S_{t+1},A_{t+1}) - Q(S_t,A_t)]

Q学習行動価値関数の更新式

Q(S_t,A_t) \leftarrow Q(S_t,A_t) + \alpha[R_{t+1} + \gamma \max_{a'} Q(S_{t+1},a') - Q(S_t,A_t)]

Sarsa

行動価値関数を更新する際に、Q学習に比べ行動価値の小さい探索結果が反映されやすいという特徴がありますが、計算が不安定になりやすいという面もあります
SarsaもQ学習も、経験していない「状態と行動の組み合わせ」に対する行動価値関数は更新しない
Sarsaは方策オン型の手法であり、行動を決定する方策と行動価値関数の更新に利用される方策同じ
SarsaはTD学習の一種であるため、Sarsaでは方策勾配法は用いられない

Q学習

経験再生において、エージェントが経験した状態、行動、報酬、および遷移先は一旦メモリに保存され、損失の計算を行う際に、その保存した値からランダムサンプリングを行う
「価値関数が小さく更新されただけでも選ばれる行動が大きく変わってしまう」という問題について、DQNでは目標Qネットワークの固定を適用した
「報酬のスケールが与えられたタスクによって大きく異なる」という問題について、DQNでは報酬のクリッピングを適用した(報酬のクリッピングは報酬の値を{-1,0,1}の3値に制限することである)
Sarsaとは異なり、行動価値関数Qの更新が行動の決定方法に依存しない
Sarsaよりも行動価値関数の収束が早くなることが多い
Q学習では、行動を決定する方策と行動価値関数の更新に利用される方策異なる
Q学習では、行動価値関数を更新する際に、行動価値の小さい探索結果は反映されにくい傾向がある

「入力データが時系列データであり、入力データ間に独立性がない」という問題について、通常のQ学習での損失の定義:

L(\theta) = E[(r + \gamma \max_{a'}Q(s', a'; \theta) - Q(s, a; \theta))^2]

DQNでは経験再生を適用(状態s、行動a、報酬r、遷移先s'):

L(\theta) = E_{s,a,r,s' \sim D} [(r + \gamma \max_{a'}Q(s', a'; \theta) - Q(s, a; \theta))^2]

「価値関数が小さく更新されただけでも選ばれる行動が大きく変わってしまう」という問題について、DQNでは目標Qネットワークの固定を適用:

L(\theta) = E_{s,a,r,s' \sim D} [(r + \gamma \max_{a'}Q(s', a'; \theta^-) - Q(s,a;\theta))^2]

AlphaGoLee

モンテカルロ木探索は「選択」、「評価」、「バックアップ」、「成長」の4ステップで構成される

「選択」ステップ

勝敗期待値 + c * 着手予想確率 * \frac{\sqrt{そのノードが選択された数の合計}}{その手が選択された数 + 1}

という計算された方策によって行われる
cは勝敗期待値着手予想確率のバランスを決めるパラメータを表す
着手予想確率はAlphaGoではPolicyNetの出力が用いられた

「評価」ステップ

ValueNetでの評価を行うと同時に、その局面を開始局面として方策でRollOut対局シミュレーションを行い、両方の評価を合議したものをその着手の評価とする

「バックアップ」ステップ

「選択」ステップでの評価を親ノードに遡って更新する

「成長」ステップ

一定回数選択されたリーフノードの合法手を展開し、新たなリーフノードとする

A3C(Asynchronous Advantage Actor-Critic)

方策反復ベースの深層強化学習として代表的なアルゴリズム
サンプルの生成を並列に行い、パラメータの更新を非同期で行うことで学習の安定を図っている
Actor-Critic法は何らかの形でパラメトリックな価値関数を用いて$Q^{\pi_\theta}(s,a)-b(s)$を推定
方策をActor、価値関数をCriticという
Atari2600で学習性能を評価した時、マルチコアCPUマシンにてA3CはDQNよりも短い演算時間で高い性能が得られる

パターン認識

物体検出

対象となる物体が含まれる矩形領域を示すバウンディングボックスとそれに対応するクラスを用意する必要がある
非最大値抑制:各バウンディングボックスに対応する信頼度スコアが最も高いものだけを採用する

IoU(B_{true}, B_{pred}) = \frac{|B_{true} \cap B_{pred}|}{|B_{true} \cup B_{pred}|} = \frac{|B_{true} \cap B_{pred}|}{|B_{true}| + |B_{pred}| - |B_{true} \cap B_{pred}|}

識別関数

入力データ$x$を与えると、それが属すると予測されるクラスの番号$C_{pred}$を直接出力する手法である
確率に基づいた識別境界を設定し、最大値が識別境界を下回った場合は回答しないモデルとすることが可能になる

C_{pred} = arg\max_C p(C|x)

識別モデル

入力データ$x$を与えると、各クラスの番号ごとにそれが属する確率$p$を割り当てる手法である
確率に基づいた識別境界を設定し、最大値が識別境界を下回った場合は回答しないモデルとすることが可能になる

生成モデル

特定のクラスに属する人工的なデータを作成することが可能である
予測以外にも、データ拡張、異常検知、および欠損値の補完といった応用が可能となる

p(C|x) = \frac{p(x|C)p(C)}{p(x)} = \frac{p(x,C)}{p(x)}

転移学習

ワンショット学習

たったひとつの事例(データ)しか見ずに学習を行う(新たなデータに対して適切に分類できるようにする)ことであり、他の転移学習と比較して大量の訓練データを用意する必要がない

ドメイン適応

対象ドメインでの汎化性能を向上させるため、事前訓練済みパラメータと対象ドメインの訓練データを用いる

Data Augmentation(データ拡張)

Horizontal Flip:水平方向(左右)反転処理
Vertical Flip:垂直方向(上下)反転処理
Crop:あるサイズを画像中からランダムに切り出す処理
Contrast:コントラストをランダムに調整する処理
Brightness:輝度値δをランダムに調整する処理
Hue:色相δをランダムに調整する処理
Rotate:回転処理
Random Erasing:大きさがランダムの矩形領域で画像をマスクさせる
MixUp:2つの学習データを混合(ラベル/データ双方を線形補完)させる

距離学習

Siamese Network

2つの入力サンプル間の距離をd、入力が類似している(y=1)かどうかの識別ラベルを{0,1}、非類似サンプル間の距離のマージンをmとした時、最小化したい目的関数contrastive loss:

L = \frac{1}{2}(yd^2 + (1-y) \max (m-d, 0)^2)

類似しない2つのサンプル間の距離が全てマージンの値に収束するように学習が行われる
2つのサンプルが同じ・違うものというコンテキストを固定する必要がある

Triplet Network

アンカーサンプルと類似サンプル間の距離を$d_p$、アンカーサンプルと非類似サンプル間の距離を$d_n$、類似サンプルと非類似サンプル間のマージンをmとした時、最小化したい目的関数triplet loss:

L = \max(d_p - d_n + m, 0)

学習データセットサイズが増えるとペアの数が爆発的に増えるという課題がある
学習時のコンテキストに関する制約が緩和されている

メタ学習(MAML)

様々なタスクを解くことができる汎用的な単一のモデルを訓練することを目的とした学習手法
確率的勾配降下法を用いて学習する
2階微分を計算するため計算量が多くなる

深層学習の説明性

Grad-CAM(Class Activation Mapping)

モデルがある画像においてどのピクセルに着目してクラス分類したのかを調べる手法

LIME

SHAP

音声処理

窓区間の信号が周期的であるという有限区間フーリエ変換の仮定に基づき、窓の両端の値を滑らかに接続するため、窓関数を施す

人間の聴覚特性:線形周波数をメル尺度に変換して、低周波数域では分解能が高く、高周波数域では分解能が低くなる

GCN(Graph convolutional networks)

グラフフーリエ変換を施し、領域を変換してから畳み込み演算をするGCNをSpectral GCNという
Spectral GCNの欠点:
Lの固有値分解を含むため、最低でも$O(N^2)$の計算量がかかる
学習するされるパラメータがQに依存
異なる構造のグラフ間で同じパラメータを利用することができない

深層学習ライブラリ

特性:
define-and-run:ネットワークアーキテクチャの構成を先に行った後に順伝播処理を行う
define-by-run:順伝播処理を行いながらネットワークアーキテクチャの構成を順に行う

軽量化

ハードウェアへの組み込みなど、メモリ使用量に大きな制約がある場合、単に推論を高速化したい場合にも有用

MobileNet

Depthwise Separable Convolutionという手法により空間方向とチャネル方向を分けて畳み込む
Depthwise Convolution:空間方向に関する計算、チャネルごとに独立した畳み込み演算
Pointwise Convolution:チャネル方向に関する計算、1x1の畳み込み演算

通常の畳み込み層が持つ学習可能パラメータ数は、フィルタサイズが縦横どちらも$f$、入力チャンネル数が$c_{in}$、出力チャンネル数が$c_{out}$のときに、$c_{in}c_{out}f^2$となる
デプスワイズ畳み込みはチャンネルごとに独立のフィルタを用いて畳み込みを行うため、学習可能パラメータ数が$c_{in}f^2$になって、通常の畳み込み層と比べて$\frac{1}{c_{out}}$倍となる
ポイントワイズ畳み込みはフィルタサイズを1とした畳み込み層のことであり、学習可能パラメータ数が$c_{in}c_{out}$になって、通常の畳み込みと比べて$\frac{1}{f^2}$倍となる
少ないパラメータ数で広い領域を参照できる畳み込みとして考えられたのがダイレイト畳み込みである
生成モデルのようにアップサンプリングを行う場合に使う畳み込みとして転置畳み込みがある

蒸留

学習済みのDNNの予測結果であるソフトラベルを訓練データとして、より小さなモデルに学習させること
ソフトターゲット損失を算出する際、先生の出力(予測結果)を目標分布に用いる
ハードターゲット損失を算出する際、正解ラベルを目標分布に用いる
蒸留で用いる温度付きソフトマックス関数:

Softmax(z)_i = \frac{\exp (z_i / T)}{\sum_j \exp (z_j / T)}

温度Tを高くすることで、不正解に対する確率を大きくできる
生徒を学習させる際の損失:

L = \frac{\lambda_1 T^2 L_{soft} + \lambda_2 L_{hard}}{\lambda_1 + \lambda_2}

剪定

学習と同時もしくは学習後に行われる
精度に影響を与えないノードを削除することで、データ量と計算回数を削減できる

チャンネル剪定では、ラッソに基づいて代表的なチャンネルを選択し、冗長なチャンネルを刈り取った上で、最小二乗法で残ったチャンネルを再構成する

量子化

通常、32ビットや16ビットの浮動小数点数であるモデルのパラメータを、より小さいビット幅の固定小数点数や整数によって置き換えること

分散処理

一般に、同期型のデータ並列処理は、非同期型のものに比べて収束性が良い傾向
非同期型のデータ並列処理では、一部のワーカの実行が止まったとしても、それ以外のワーカは引き続き実行される

データ並列

入力データを分割し、複数の計算機で分担して学習・推論を行う

モデル並列

複数の計算機が1つのデータに対して共同で動作し、それぞれがモデルの異なる部分を実行することで学習・推論を行う

GPU

単一命令列・複数データ(SIMD)
ハイエンドモデルは数千個の演算コアを持ち、並列計算に適している
メモリにデータとプログラムを内蔵しメモリから命令を逐次取り出し実行するノイマン型プロセッサの一種
CPUに比べて、GPUはコア当たりの計算性能は低くなる
GPUのコアは、それぞれ独立に制御することはできない
DNNの計算をGPUで行う際に、単精度(32ビット)浮動小数点数で計算するのが一般的
行列演算のように単純な計算を並列に複数回行うといった処理が得意

TPU

数千の積和演算器が配置され、それらが互いに直接接続されている。このアーキテクチャはシストリックアレイと呼ばれる
演算の結果をメモリではなく、次の乗算器に渡すことで、メモリアクセスの必要をなくし、スループットを向上
演算器の計算精度を8や16bitのみにすることで、スループットの向上や低消費電力化を図っている

Docker

コンテナ型仮想化ソフトウェア
イメージの構成をDockerfileで管理し、複数のコンテナの構成をdocker-compose.ymlで管理
ハイパーバイザ型より優れている点:
OSのカーネルを利用するため、コンピュータリソースの使用効率が良い、アプリケーションの起動速度が速い
ハードウェアやOSの設定が実行環境に依存しない

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5