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

# 線形代数

## マンハッタン距離
```math
d(x,y) = \sum_{i=1}^n |x_i - y_i|
```

## ユークリッド距離
```math
d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}
```

## マハラノビス距離
共分散行列$\Sigma$が既知
```math
D(x,y) = \sqrt{(x-y)^T \Sigma^{-1} (x - y)}
```

# 確率・統計

## 連続型
期待値
```math
E(X)=\int_{-\infty}^{\infty}xf(x)dx
```
分散
```math
V(X)=E(X^2)-\{E(X)\}^2
```

## ベイズの定理
```math
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**
尤度関数：
```math
\prod_{i=1}^n p^{x_i}(1-p)^{1-x_i}
```
負の対数尤度：
```math
-\log L_D(p) = -\sum_{i=1}^n (x_i \log p + (1 - x_i) \log (1-p))
```
pの最尤推定量：
```math
\hat{p} = \frac{1}{n} \sum_{i=1}^n x_i
```

## 正規分布
平均$\mu$、分散$\sigma^2$
```math
\frac{1}{\sqrt{2 \pi \sigma^2}} \exp (- \frac{(x-\mu)^2}{2 \sigma^2})
```
平均$\mu$、分散1の尤度関数：
```math
\prod_{i=1}^n \frac{1}{\sqrt{2\pi}}  \exp(-\frac{1}{2}(x_i - \mu)^2)
```
$\mu$の最小化問題：
```math
\frac{1}{2} \sum_{i=1}^n (x_i - \mu)^2
```
最尤推定量：
```math
\hat{\mu} = \frac{1}{n} \sum_{i=1}^n x_i
```

# 情報理論

## 自己情報量
```math
I(A)=-\log P(A)
```

## エントロピー
ある事象Xが起こったと分かった時に得られる情報量の期待値
```math
H(X) = -\sum_x p(x) \log_2 p(x)
```

## 交差エントロピー
```math
H(p, q) = -\sum_x p(x) \log_2 q(x)
```

## KL-ダイバージェンス
二つの確率分布の擬距離を定量化する指標
```math
D(p||q) = \sum_x p(x) \log_2 \frac{p(x)}{q(x)}
```
連続型
```math
D(p||q) = -\int_X p(x) \log \frac{q(x)}{p(x)} dx
```

## JS-ダイバージェンス
```math
D_{JS} = \frac{1}{2}(D_{KL}(p||r) + D_{KL}(q||r))
```

## カテゴリカルクロスエントロピー
```math
-\sum_{i=1}^n \sum_{j=1}^m t_{i,j} \log p_{i,j}
```

## 相互情報量
```math
I(X_1, Y) = H(X_1) - H(X_1|Y)
```

# 機械学習

## 条件付き最尤推定量
```math
\theta_{ML} = \arg \max_{\theta} P(Y|X; \theta)
```

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

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

## 標準化
```python
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
```

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

## 性能指標

### 混同行列
| 混同行列  | 真の結果<br>陽性(Positive)  | 真の結果<br>陰性(Negative)  |
|:-:|:-:|:-:|
| <B>予測結果<br>陽性(Positive)  | TP  | FP  |
| <B>予測結果<br>陰性(Negative)  | FN  | TN  |

正解率
```math
Accuracy = \frac{TP+TN}{TP+TN+FN+FP}
```
適合率
```math
Precision = \frac{TP}{TP+FP}
```
再現率
```math
Recall = \frac{TP}{TP+FN}
```
F値
```math
F_{measure} = \frac{2*Recall*Precision}{Recall+Precision}
```

### 平均絶対誤差(Mean Absolute Error, MAE)
```math
MAE = \frac{1}{n} \sum_{i=1}^n |f_i - y_i|
```

### 平均二乗誤差(Mean Squared Error, MSE)
```math
MSE = \frac{1}{n} \sum_{i=1}^n (f_i - y_1)^2
```

### 平均二乗誤差平方根(Root Mean Squared Error, RMSE)
```math
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$を学習
パラメータの解釈には、よくオッズが用いられる
```math
オッズ \frac{\hat{y}}{1-\hat{y}} = \exp (w^T x + b)
```

## 主成分分析
**教師なし**学習、データから重要な成分を見つける手法
**共分散行列**を対角化する固有値問題に帰結される
**分散共分散行列の固有ベクトル**を基に新たな座標軸を複数作成し、新たな座標軸から構成される低次元空間へデータを写像する
**固有値の大きい固有ベクトル**の方から第一主成分、第二主成分、…と呼ばれる
各主成分の固有値を固有値の総和で割った値が**寄与率**と呼ばれる
主成分：**分散が大きい**成分
各主成分は**互いに直交する**ように選ばれる
```python
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 の処理を繰り返す
欠点：初期値のセントロイドの位置が近い場合、うまく分類できないケースがある
```python
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つ目の代表ベクトルとする。次に、全てのデータとそこから最も近くにある代表ベクトルの距離を求め、その距離の二乗に比例した確率でまだ選ばれていないデータ点の中から新たな代表ベクトルをランダムに選ぶ。この初期の代表ベクトルの選択方法は**ルーレット選択**と呼ばれる
```python
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**と呼ばれる
**カーネルトリック**：線形分離可能にするために高次元空間に写像する際の式変形のテクニック(データを高次元に写像する際に、計算が複雑にならないように式変形するテクニック)

### ガウス動径基底関数カーネル
```math
k(x^{(i)}, x^{(j)}) = \exp(- \frac{||x^{(i)}-x^{(j)}||^2}{2\sigma^2})
```

### 正規化線形カーネル
```math
k(x, x') = \frac{x^Tx'}{||x||||x'||}
```

特徴ベクトル
```math
\phi(x) = \frac{x}{||x||}
```

# Activation Function

## Sigmoid Function(シグモイド関数)
```math
f(x) = \frac{1}{1 + \exp(-x)} \\
f'(x) = \left( 1 - f(x) \right) * f(x)
```
```python
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
```

## Step Function(ステップ関数)
```math
f(x) = \left\{
\begin{array}{ll}
1 & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.
```
```python
def step_function(x):
    if x > 0:
        return 1
    else:
        return 0
```

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

## Softplus Function
```math
f(x) = \log (1 + e^x) \\
f'(x) = \frac{1}{1 + e^{-x}}
```

## Identity Function(恒等関数)
```math
f(x) = x
```

## Softmax Function
**多次元分類**を目的とする
**入力ベクトルの要素間の差**にのみ依存する
```math
y_{k} = \frac{\exp(a_{k})}{\sum_{i=1}^n \exp(a_{i})}
```
```math
softmax(z) = softmax(z - \max_i z_i)
```
```python
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
```
```python
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
```
```python
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
```math
E = \frac{1}{2} \sum_{k}(y_{k} - t_{k})^2
```
```python
def sum_squared_error:
    return 0.5 * np.sum((y - t) ** 2)
```

## Mean Squared Error
```math
E = \frac{1}{n} \sum_{k}^n(y_{k} - t_{k})^2
```
```python
def mean_squared_error:
    return np.mean(np.square(y - t))
```

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

## Mini-batch Cross Entropy Error
```math
E = -\sum_{n}\sum_{k}t_{nk}\log y_{nk}
```

## Batch Cross Entropy Error
```python
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(勾配)
```python
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を設定すると、誤差逆伝播法で全ての**重みの値が均一**に更新されてしまう
```math
W^{t+1} = W^{t} - \eta\frac{∂L}{∂W}
```
```python
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
```python
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
```python
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
```python
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
```python
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
```python
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
```python
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: 確率的勾配降下法)
```math
W \leftarrow W - \eta\frac{∂L}{∂W}
```
```python
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]
```

バッチ勾配降下法

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

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

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

## Momentum
最適化に向けた移動に「慣性」をつける。同じ方向に移動していく場合は加速し、逆方向に進もうとすると「慣性」により減速する
```math
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
```
```python
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
```math
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}
```
```python
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
最初に勾配が大きいときは学習率も大きくなり、次第に勾配が小さくになるにつれて学習率も小さくなる
利点：手動で学習率を調整しなくても、自動調整できる
欠点：学習率低下の性質が単調的であり、学習初期の段階で学習率が過度に小さくなると収束するまで時間がかかり、局所的最適解から抜け出せなくなることがある
```math
h \leftarrow h + \frac{∂L}{∂W} \odot \frac{∂L}{∂W} \\
W \leftarrow W - \eta \frac{1}{\sqrt{h}} \frac{∂L}{∂W}
```
```python
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$を優先して反映させるという効果を狙っている
過去の全て勾配の平均ではなく、指数移動平均を採用している
```math
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}
```
```python
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
```math
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)
```

```python
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$は**訓練時の移動平均を用いる**
バッチ正規化の特長：**重みの初期値**の設定に神経質にならなくてよいこと、**学習率**を大きくできること、**ドロップアウト**の使用を減らせることなど
バッチ正規化には、**オンライン学習**に適用しても意味をなさない、**ミニバッチごとのデータ数が極めて少ない**場合に収束性が悪くなる
```math
\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を含むことが多くなり、**スパース**になりやすい
```math
||x||_1 = |x_1| + |x_2| + ... + |x_n|
```

## L2正則化(Ridge回帰)
重みの大きさが小さくなり汎化性能が高くなる
```math
||x||_2 = \sqrt{x_1^2 + x_2^2 + ... + x_n^2}
```

## 荷重減衰(Weight Decay)
```math
C'(W) = C(W) + \lambda (|w_1| + |w_2| + ... + |w_n|)
```

```python
weight_decay_lambda = 0.1
loss = loss_function(t, y) + weight_decay_lambda * np.sum(np.abs(W))
```

```math
C'(W) = C(W) + \frac{1}{2} \lambda \sqrt{(w_1^2 + w_2^2 + ... + w_n^2)}
```

```python
for idx in range(1, self.hidden_layer_num + 2):
    weight_decay_gradient = self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
```

## Dropout
```python
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(畳み込みニューラルネットワーク)            

## ストライド
```math
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 = (ミニバッチサイズ,出力縦幅,出力横幅,チャンネル数,フィルタ縦幅,フィルタ横幅)
```python
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
```python
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
```python
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欠点②を解決した
```math
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との上下左右の距離を回帰する
ポジティブサンプル数とネガティブサンプル数（検出物と背景）の不均衡（アンカーボックス欠点）を改善している
```math
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の欠点を緩和し共通部分の面積をより重要視した指標
```math
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のガウス分布に近くなるように学習する
```math
z = \mu + \varepsilon \sigma \\
(\varepsilon \sim N(0,1))
```

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

## RNN
```math
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}
```
```python
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の各要素を弱める度合いを決めるゲート
忘却ゲート：記憶セルの各要素を弱める(忘れる)度合いを決めるゲート
出力ゲート：取り出した記憶について、各要素を弱める度合いを決めるゲート
記憶セル：過去の情報を記憶する部分
```math
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)
```

```python
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)
```math
o_t = \sigma(x_tW_o + h_{t-1}U_o + c_t \odot V_o + b_o)
```

## GRU
リセットゲート：過去の隠れ状態を弱める程度を決めるゲート
更新ゲート：過去の隠れ状態と仮の隠れ状態の混合割合を決めるゲート
```python
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点を決める場合もある
**グローバル・アテンション**：通常の系列変換モデルでは、長い系列のデータを学習することが難しく、**時間の初期においてエンコーダに入力された情報**がデコーダまで伝わりづらくなるという問題がよく生じる。デコーダの対象時刻の隠れ層の出力と**エンコーダの各時刻の隠れ層の出力**から重みベクトルを算出し、その重みベクトルと**エンコーダの各時刻の隠れ層の出力**の重み付き和をとり、**コンテクストベクトル**を得る。**コンテクストベクトル**はデコーダの計算で利用される
```python
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は参照翻訳の長さ
```math
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個ｎ負例だけを対象として、**シグモイド関数**と**交差エントロピー誤差関数**の計算を行う

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)と呼ばれている
```math
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$をマルチヘッド・アテンションの入力(出力)の次元数として、偶数番目の次元、奇数番目の次元がそれぞれ以下のように与えられる。
```math
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**が適用される
```math
\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により最大化された価値関数
```math
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)}]
```
```python
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画像化、航空写真の地図画像化
例えば、エッジからカラー写真への変換タスクを学習する場合、エッジの画像を条件として生成器、識別器それぞれに与える影響という動作になる
```math
\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での事後分布：
```math
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)$はエンコーダの出力
デコーダへの入力：
```math
z_q(x) = e_k, \; where \, k=arg\min_j||z_e(x) - e_j||_2
```

# 強化学習

## 方策勾配
```math
\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)$をかけることで、価値の期待値を算出できる
```math
V_\pi(s) = \sum_a \pi (a|s) \sum_{s'} T(s'|s,a) (R(s,s') + \gamma V_\pi(s'))
```

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

## モンテカルロ法
サンプル**収益を平均化**することに基づいて強化学習問題を解く方法
現時点の方策を用いてエピソードを生成し、その結果を用いて価値関数と方策を改善
1エピソードの実績で修正を行う方法
TD法と比較して、修正の妥当性があり、修正の速さが遅い
```math
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行動価値関数の更新式
```math
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学習行動価値関数の更新式
```math
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学習での損失の定義：
```math
L(\theta) = E[(r + \gamma \max_{a'}Q(s', a'; \theta) - Q(s, a; \theta))^2]
```
DQNでは**経験再生**を適用(状態s、行動a、報酬r、遷移先s')：
```math
L(\theta) = E_{s,a,r,s' \sim D} [(r + \gamma \max_{a'}Q(s', a'; \theta) - Q(s, a; \theta))^2]
```
「価値関数が小さく更新されただけでも選ばれる行動が大きく変わってしまう」という問題について、DQNでは**目標Qネットワークの固定**を適用：
```math
L(\theta) = E_{s,a,r,s' \sim D} [(r + \gamma \max_{a'}Q(s', a'; \theta^-) - Q(s,a;\theta))^2]
```

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

### 「選択」ステップ
```math
勝敗期待値 + 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よりも短い演算時間で高い性能が得られる

# パターン認識

## 物体検出
対象となる**物体が含まれる矩形領域**を示すバウンディングボックスとそれに対応するクラスを用意する必要がある
**非最大値抑制**：各バウンディングボックスに対応する信頼度スコアが最も高いものだけを採用する
```math
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}$を直接出力する手法である
確率に基づいた識別境界を設定し、最大値が識別境界を下回った場合は回答しないモデルとすることが可能になる
```math
C_{pred} = arg\max_C p(C|x)
```

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

## 生成モデル
特定のクラスに属する人工的なデータを作成することが可能である
予測以外にも、データ拡張、異常検知、および欠損値の補完といった応用が可能となる
```math
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:
```math
L = \frac{1}{2}(yd^2 + (1-y) \max (m-d, 0)^2)
```
類似しない2つのサンプル間の距離が全てマージンの値に収束するように学習が行われる
2つのサンプルが同じ・違うものというコンテキストを固定する必要がある

## Triplet Network
アンカーサンプルと類似サンプル間の距離を$d_p$、アンカーサンプルと非類似サンプル間の距離を$d_n$、類似サンプルと非類似サンプル間のマージンをmとした時、最小化したい目的関数triplet loss:
```math
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の予測結果であるソフトラベルを訓練データとして、より小さなモデルに学習させること
ソフトターゲット損失を算出する際、**先生の出力(予測結果)を目標分布**に用いる
ハードターゲット損失を算出する際、**正解ラベルを目標分布**に用いる
蒸留で用いる温度付きソフトマックス関数：
```math
Softmax(z)_i = \frac{\exp (z_i / T)}{\sum_j \exp (z_j / T)}
```
温度Tを高くすることで、不正解に対する確率を大きくできる
生徒を学習させる際の損失：
```math
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の設定が実行環境に依存しない