はじめに
ラビットチャレンジの「深層学習後編(day3)レポート」になります。
✅ 応用数学レポート
✅ 機械学習レポート
✅ 深層学習前編(day1)レポート
✅ 深層学習前編(day2)レポート
⭐️ "深層学習後編(day3)レポート"
深層学習後編(day4)レポート
Section1: 再帰型ニューラルネットワークの概念
1-1. 要点
RNN全体像
-
RNNとは
- 時系列データに対応可能な、ニューラルネットワークである
-
時系列データ
- 時間的順序を追って一定間隔ごとに観察され、しかも相互に統計的依存関係が認められるようなデータの系列
- 例 : 音声データ、テキストデータ
BPTT(Backpropagation Through Time)
- BPTT
- RNNにおける誤差逆伝播
- 「時間方向に展開したニューラルネットワークの誤差逆伝搬法」
- 長い時系列データを学習する場合に問題発生
- 時系列データの時間サイズが大きくなるに比例して、BPTTで消費するコンピュータの計算リソースも増加する
- また、時間サイズが長くなると、逆伝播の勾配が不安定になることも問題
- BPTTにおいては、"勾配消失", "勾配爆発" が起こるには原因がある
- RNNの課題
- 時系列を遡れば遡るほど勾配が消失していく (長い時系列の学習が困難)
- 解決策 : LSTM (次の章で確認)
1-2. 確認テスト
【確認テスト1】 サイズ5x5の入力画像を、サイズ3x3のフィルタで畳み込んだ時の出力画像のサイズを答えよ。ストライドは2で、パディングは1とする
- 解答 : 3x3 (公式より計算)
【確認テスト2】 RNNのネットワークには大きく分けて3つの重みがある。1つは入力から現在の中間層を定義する際にかけられる重み、一つは中間層から出力を定義する際にかけられる重み。残り一つは?
- 解答: 前の中間層(t−1)から現在の中間層(t)を定義する際にかけられる重み
【確認テスト3】 連鎖律の原理を使い、dz/dxを求めよ。
$ dz/dx = dz/dt・dt/dx$
$ = 2t・1 = 2(x+y)$
【確認テスト4】
下図のy1を $ x, Z_{0}, Z_{1}, W_{in}, W, W_{out}$を用いて数式で表わせ。中間層の出力にシグモイド関数g(x)を作用させよ。
- $ y1 = g(W_{out} * f(W * Z_{0}+ W_{in}*X_{1}+b)+c)$
【演習チャレンジ】
- 解答 : (2) W.dot(np.concatenate([left, right]))
【コード演習問題】
- 解答 : (2) delta_t = delta_t.dot(U)
1-3. 関連学習
※ BPTTの改良モデル
- Truncated BPTT
- ネットワークのつながりを適当な長さで断ち切る。小さなネットワークを複数作る
- その切り取った小さなネットワークに対して、誤差逆伝播法を行う
- 重要な点は、ネットワークの逆伝搬だけのつながりを断ち切る。順伝搬のつながりは維持されたまま
- 参考
- ゼロから作るDeep Learning②
1-4. 実装演習
- PyTorchによるRNN実装
import numpy as np
import torch
import matplotlib.pyplot as plt
% matplotlib inline
import seaborn as sns
sns.set()
np.random.seed(1)
torch.manual_seed(1)
# sin曲線+ノイズ
ts = np.linspace(0, 10 * np.pi, 500)
ys = np.sin(ts) + np.random.normal(scale=0.1, size=len(ts))
plt.plot(ts, ys)
# 学習設定
batch_size = 32 # ミニバッチサイズ
n_steps = 50 # 入力系列の長さ
input_size = 1 # 入力の次元
hidden_size = 50 # 中間層の次元
output_size = 1 # 出力層の次元
lr = 0.005 # 学習率(SGD)
n_iter = 500 # イテレーション回数
# 訓練データとテストデータに分割
train_ratio = 0.8
data = []
for i in range(len(ys) - n_steps - 1):
data.append(ys[i: i+n_steps+1])
data = np.array(data, dtype=np.float32)
n_train = int(train_ratio * len(data))
x_train, y_train = np.split(data[:n_train], [-1], axis=1)
x_test, y_test = np.split(data[n_train:], [-1], axis=1)
x_train = np.reshape(x_train, [-1, n_steps, input_size])
x_test = np.reshape(x_test, [-1, n_steps, input_size])
import torch.nn as nn
class RNN_Net(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(RNN_Net, self).__init__()
# batch_first=True; then
# input: (batch_size, n_steps, input_size)
self.rnn = nn.RNN(input_size, hidden_size, num_layers=1, batch_first=True)
# self.rnn = nn.LSTM(input_size, hidden_size, num_layers=1, batch_first=True)
self.l = nn.Linear(hidden_size, output_size)
def forward(self, x, h_0=None):
output, h_n = self.rnn(x, h_0)
return self.l(output[:, -1])
# モデルをインスタンス化
model = RNN_Net(input_size, hidden_size, output_size)
# 最適化手法を設定
# optimizer = torch.optim.SGD(model.parameters(), lr=lr)
optimizer = torch.optim.Adam(model.parameters())
# 損失関数を設定
loss_fun = nn.MSELoss()
# 訓練データのインデックスをランダムに
perm = np.random.permutation(len(x_train))
for i in range(n_iter):
idx = (i * batch_size) % len(x_train)
batch_x, batch_y = x_train[perm[idx: idx+batch_size]], y_train[perm[idx: idx+batch_size]]
batch_x, batch_y = torch.tensor(batch_x), torch.tensor(batch_y)
y = model(batch_x)
loss = loss_fun(y, batch_y)
# パラメータ更新(勾配のリセット+誤差逆伝播+更新)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i % 50 == 0:
print("step: {}, loss {:.5f}".format(i, loss.data))
# テストデータに対する予測
x_test = torch.tensor(x_test)
prediction = model(x_test)
# 1次元配列に
prediction = prediction.detach().numpy().reshape(-1)
true_y = y_test.reshape(-1)
# テストデータに対する予測を可視化
xx = np.arange(len(prediction))
plt.plot(xx, true_y, label='true')
plt.plot(xx, prediction, label='prediction')
plt.legend()
# テストデータの最初のデータからスタートし、
# モデルの予測を利用し再帰的に予測
curr_x = x_test[0]
predicted = []
# 予測するステップ数
N = 200
for i in range(N):
# 予測
pred = model(curr_x[None])
predicted.append(pred.item())
# 入力を更新
curr_x = torch.cat([curr_x[1:], pred], dim=0)
#再帰的な予測を可視化
plt.plot(xx, true_y, label='true')
plt.plot(np.arange(N), predicted, label='prediction')
plt.legend()
考察
- フレームワーク(torch)を使うと楽に実装できる。比較的シンプルなモデルでもそれなりの結果を得ることができました。
Section2: LSTM
2-1. 要点
CEC (Constant Error Carousel)
- CEC
- 別名セル
- 誤差を内部にとどめる役割を持っており勾配消失を防ぐ
- CECの課題
- 入力データについて、時間依存度に関係なく重みが一律である
- NNの学習特性がないということ
入力ゲートと出力ゲート
- 入力・出力ゲートの役割
- それぞれのゲートへの入力値の重みを重み行列で可変可能とする
- CEC課題を解決
- それぞれのゲートへの入力値の重みを重み行列で可変可能とする
忘却ゲート
- LSTMの現状
- CECは過去の情報がすべて保管されている
- 課題
- 過去の情報が要らなくなった場合、削除することはできず保管され続ける
- 解決策
- 過去の情報が不要になったらそのタイミングで情報を忘れる必要なり→忘却ゲート
覗き穴結合
- 課題
- CECの保存されている過去の情報を、任意のタイミングで他のノードに伝播させたり、あるいは任意のタイミングで忘却させたい
- 覗き穴結合
- CEC自身の値に。重み行列を介して伝播可能にした構造
2-2. 確認テスト
【確認テスト】 シグモイド関数を微分した時、入力値が0の時に最大値をとる。その値は?
- 解答 : (2)0.25 (0.5*0.5)
【演習チャレンジ】
- 解答 : (1) gradient*rate
- 勾配のノルムがしきい値より大きい時は、勾配のノルムをしきい値に正規化するので、クリッピングした勾配は、勾配x(しきい値/勾配のノルム)と計算できる
【確認テスト】 以下の文章をLSTMに入力し空欄に当てはまる単語を予測したいとする。文中の「とても」という言葉は空欄の予測においてなくなっても影響を及ばさないと考えられている。どのゲートが作用したか?
- 解答 : 忘却ゲート
【演習チャレンジ】
- 解答: (3)input_gate * a + forget_gate * c
2-3. 関連学習
- LSTMの学習のコツ
- 設定が必要なパラメータ
- 隠れ層のLSTM Blockの個数
- 学習率
- モーメンタム
- BPTTの打ち切りステップ数(Truncated BPTT)
- 勾配の絶対値のクリッピング(Gradient Clip)
- 学習率の設定は何においても重要
- 論文中ではまず高い学習率(1程度)から始めて、性能の改善が停止するたびに10で割る大雑把な探索が推奨
- 隠れ層の数を増やせば増やすほど性能は改善しますが、そのトレードオフとして実行時間が増加
- モーメンタムの値は今回の解析では値の設定による性能の変化はなかったと報告
- 参考記事
- 設定が必要なパラメータ
Section3: GRU (Gated Recurrent Unit)
3-1. 要点
-
LSTM課題
- パラメータ数が多く、計算負荷が高くなる問題あり
- → 解決策 :GRU
-
GRU
- LSTMと比べるとパラメータを大幅に削減し、精度は同等またはそれ以上が望める様になった構造
- 計算負荷が低い
3-2. 確認テスト
【確認テスト】 LSTMとCECが抱える課題について簡潔に述べよ。
- LSTMは多くのパラメータを持つため、複雑で計算負荷が大きい
- CEC自体には学習機能がなく、周りに学習能力のあるゲート(入力ゲート、出力ゲート、忘却ゲート)が必要
【演習チャレンジ】
- 解答 : (4) (1-z)*h + z+h_bar
【確認テスト】 LSTMとGRUの違いを簡潔に述べよ
- LSTMはCEC、入力ゲート、出力ゲート、忘却ゲートを持ちパラメータが多いため計算量が多い
- GRUはリセットゲート、更新ゲートと2つのゲートを持ち、パラメータが少ないので計算コストがLSTMよりも小さい
3-3. 関連学習
- LSTMとGRUのどちらを使用すべきか?
- タスクやハイパーパラメータの調整によって、勝者は変動する
- 最近の研究だと LSTMやLSTMの亜種が多く使われる
- 一方、GRUはパラメータが少なく計算量も小さいためデータセットのサイズが小さい場合やモデル設計で繰り返しの試行が必要な場合に適している
- GRUは,省略前のLSTMほどの長期記憶性能は持ち合わせいないので,GRUだと性能が出ない場合には,LSTMのほうが予測性能が良くなる場合もある
- https://cvml-expertguide.net/terms/dl/rnn/gru/
Section4: 双方向RNN
4-1. 要点
- 双方向RNN
- 過去の情報だけでなく、未来の情報を加味することで精度を向上させるため
- 順方向と逆方向の2つのRNNを結合したニューラルネットワークで、これにより、与えられた時点の入力が、過去と未来の両方の情報にアクセスできるようになった
- (例)文章の推敲、機械翻訳 etc..
- メリット
- 前後の情報の統合: 双方向RNNは、時系列データの前後の依存関係を同時に捉えることができるため、より豊かな表現が可能
- 強力な特徴抽出: 未来の情報も考慮することで、通常のRNNよりも複雑なパターンや関係を学習できる
- 多岐にわたる応用: 自然言語処理から音声認識、株価予測など、多岐にわたる時系列データ分析タスクで使用できる
4-2. 確認テスト
【演習チャレンジ】
- 解答 : (4) np.concatenate([h_f,h_b[::-1]],axis=1)
- 双方向RNNでは、順方向と逆方向に伝播した時の中間層表現を合わせたものが特徴量
Section5: Seq2Seq
5-1. 要点
- Seq2Seqとは
- Encoder-Decoderモデルの一種
- 用途 : 機械対話、機械翻訳
Encoder RNN
- ユーザがインプットしたテキストデータを、単語等のトークンに区切って渡す構造
Decoder RNN
- システムがアウトプットデータを単語等のトークンごとに生成する構造
HRED
-
Seq2Seq課題
- 一問一答しかできない
- 問いに対して文脈も何もなく、ただ応答が行われ続ける
- → HRED
-
HRED
- 過去のn-1個の発話から次の発話を生成する
- Seq2Seqでは、会話の文脈無視で応答がなされたが、HREDでは前の単語の流れに則して応答されるのでより人間らしい文章が生成される
- HRED = Seq2Seq + Context RNN
-
課題
- 確率的な多様性が字面にしかなく、会話の流れのような多様性がない
- HREDは短く情報量に乏しい答えをしがち
VHRED
- VHRED
- HREDにVAEの潜在変数の概念を追加したもの
VAE
-
オートエンコーダー
- 教師なし学習
- 入力データから潜在空間zに変換するニューラルネットワークをEncoderとし、逆に潜在空間から元画像を復元するニューラルネットをDecoder
- メリットは次元削減できること
-
VAE
- 通常のAEの場合、何かしら潜在空間zにデータを押し込めているものの構造がわからない
- VAEはこの潜在空間zに確率分布 $ z~N(0,1)を仮定したもの $
5-2. 確認テスト
【確認テスト】 Seq2Seqについて説明しているものは?
- (2) RNNを用いたEncoder-Decoderモデルの一種であり、機械翻訳などのモデルに使われる
【演習チャレンジ】
- 解答 : (1) E.dot(w)
【確認テスト】 Seq2SeqとHRED, HREDとVHREDの違いを簡潔に述べよ
▪️ Seq2SeqとHRED
- seq2seq2は文脈を無視していたのに対して、HREDは文脈を考慮する
▪️ HREDとVHRED - HREDは文脈を字面だけで考慮していたのに対して、VHREDは同じコンテキストに対しても、字面だけではない多様な返答ができる
【確認テスト】 VAEに関する下記の説明文の空欄に当てはまる言葉は?
自己符号器の潜在空間に____を導入したもの
- 解答 : 確率分布
5-3. 関連学習
- seq2seq with attention
- 機械翻訳で初めに用いられたテキストなど系列データ同士の系列変換モデルをRNN2つの直列接続で構成される "RNN Encoder-Decoder" で学習する仕組み
- seq2seqでは入力系列が長い場合にRNNが推定していく潜在変数が初期の単語の情報を覚えておくことができず出力単語数が多いとその潜在変数の値が似通ってしまう問題があった
- そこで、この問題を解決するためにseq2seqに「アテンション機構」を挿入して拡張をおこなった seq2seq with attentionが提案された
- seq2seq with attention ではデコード中の各フレームで動的に変化する入力コンテキストを得られる。これにより毎フレームで各入力全単語への注目度重みづけ係数(=アテンション係数)を変えて文章の生成が可能になった
- https://cvml-expertguide.net/terms/dl/seq2seq-translation/seq2seq/
Section6: Word2Vec
6-1. 要点
- Word2Vec
- 2013年にGoogleの研究者トマス・ミコロフ氏によって提案された手法
- 「意味ベクトル」という単語のベクトル表現を可能にする手法
- 「同じ文脈に現れる単語は類似した意味を持つ」という分布仮説がその設計思想にある
- 2つのモデルを使用
- CBOW
- skip-gram
- メリット
- 大規模データの分散表現の学習が、現実的な計算速度とメモリ量で実現可能にした
6-2. 関連学習
- word2vecの高速化
- ① Embeddingレイヤという新しいレイヤを導入
- 入力側のMatMulレイヤをEmbeddingレイヤに切り替える
- メモリ使用量を減らし、さらに無駄な計算を省くことができる
- ② Negative Samplingという新しい損失関数を導入
- ボトルネックは、行列の積とsoftmaxレイヤの計算
- Softmaxの代わりにNegative samplingwp用いることで、語彙数がどれだけ多くなったとしても計算量を少なく一定に抑えることができる
- ① Embeddingレイヤという新しいレイヤを導入
Section7: Attention Mechanism
7-1. 要点
- 課題
- Seq2Seqの問題は長い文章への対応が難しい。Seq2Seqでは、2単語でも、100単語でも、固定次元ベクトルの中に入力しなければならない
- 解決策
- 文章が長くなるほどそのシーケンスの内部表現の次元も大きくなっていく仕組みが必要
- → "Attention Mechanism"
- 「入力と出力のどの単語が関連しているのか」の関連度を学習する仕組み
7-2. 確認テスト
【確認テスト】 RNNとword2vec、seq2seqとAttentionの違いを述べよ
▪️ RNNとWord2vec
- RNN : 時系列データを処理するのに適したニューラルネットワーク
- word2vec : 単語の分散表現ベクトルを得る手法
▪️ seq2seqとAttention
- Seq2Seq : ある時系列データから別の時系列データを得るニューラルネットワーク
- Attention : 時系列の中身に対して、関連性に重みをつける手法
7-3. 関連学習
- Attentionの応用
- TransformerはAttentionで構成
- "Self-Attention" というテクニックが利用されている
- 直訳すると 「 自分自身に対してのAttention 」
- これは一つの時系列データを対象としたAttentionであり、ひとつの時系列データ内において各要素が他の要素に対してどのような関係性があるか見ていこうというもの
8. VQ-VAE(Vector Quantised-Variational AutoEncoder)
8-1. 要点
- VQ-VAE
- VAEの派生技術にあたる生成モデル
- 「自然界の様々な事物の特徴を捉えることには離散変数の方が適している」という発想から、潜在変数が離散値になるように学習が行われる
- 従来VAEで起こりやすいとされていた "posterior collapse" 問題を回避し、高品質なデータを生成することが可能
9. [フレームワーク演習]双方向RNN/勾配クリッピング
9-1. 要点
- 勾配クリッピング
- 学習時に勾配が大きくなりすぎてしまい、学習が不安定になってしまうという問題のことを勾配爆発問題という。これを防ぐための手法として "勾配クリッピング" がある
- 勾配クリッピングは、勾配があらかじめ設定しておいた閾値以上となってしまった場合に、勾配の値を閾値にクリッピングするという手法
- 極端に大きな勾配になってしまった際に、パラメータが大きく更新されることを防ぐ効果があることがわかります
- PyTorchで勾配クリッピングを行う際には、
torch.nn.utils.clip_grad_norm_
を用いるのが便利 - 勾配クリッピングを行うタイミングは、ロスから勾配を計算するのとパラメータを更新する間、つまりloss.backward()とoptimizer.step()の間で行います。
- 参考
10. [フレームワーク演習]Seq2Seq
10-1. 要点
- seq2seq (sequence-to-sequence)
- 機械翻訳で初めに用いられた,テキストなど系列データ同士の系列変換モデルを,RNN2つの直列接続で構成されるRNN Encoder-Decoderで学習する仕組み
- seq2seqでは入力系列が長い場合に,RNNが推定していく潜在変数が,初期の単語の情報を覚えておくことができず,出力単語数が多いとその潜在変数の値が似通ってしまう問題
- seq2seq はのちにアテンション機構を加えたseq2seq with attention [Bahdanau et al., 2015] が提案されて,汎用的に様々な系列変換問題に使用されるようになる
10-2. 実装演習
Seq2Seqを用いたSin-Cos変換
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# データ準備
x = np.linspace(-3 * np.pi, 3 * np.pi, 100)
seq_in = np.sin(x)
seq_out = np.cos(x)
# モデルの学習
NUM_ENC_TOKENS = 1
NUM_DEC_TOKENS = 1
NUM_HIDDEN_PARAMS = 10
NUM_STEPS = 24
tf.keras.backend.clear_session()
e_input = tf.keras.layers.Input(shape=(NUM_STEPS, NUM_ENC_TOKENS), name='e_input')
_, e_state = tf.keras.layers.SimpleRNN(NUM_HIDDEN_PARAMS, return_state=True, name='e_rnn')(e_input)
d_input = tf.keras.layers.Input(shape=(NUM_STEPS, NUM_DEC_TOKENS), name='d_input')
d_rnn = tf.keras.layers.SimpleRNN(NUM_HIDDEN_PARAMS, return_sequences=True, return_state=True, name='d_rnn')
d_rnn_out, _ = d_rnn(d_input, initial_state=[e_state])
d_dense = tf.keras.layers.Dense(NUM_DEC_TOKENS, activation='linear', name='d_output')
d_output = d_dense(d_rnn_out)
model_train = tf.keras.models.Model(inputs=[e_input, d_input], outputs=d_output)
model_train.compile(optimizer='adam', loss='mean_squared_error')
model_train.summary()
n = len(x) - NUM_STEPS
ex = np.zeros((n, NUM_STEPS))
dx = np.zeros((n, NUM_STEPS))
dy = np.zeros((n, NUM_STEPS))
for i in range(0, n):
ex[i] = seq_in[i:i + NUM_STEPS]
dx[i, 1:] = seq_out[i:i + NUM_STEPS - 1]
dy[i] = seq_out[i: i + NUM_STEPS]
ex = ex.reshape(n, NUM_STEPS, 1)
dx = dx.reshape(n, NUM_STEPS, 1)
dy = dy.reshape(n, NUM_STEPS, 1)
BATCH_SIZE = 16
EPOCHS = 80
history = model_train.fit([ex, dx], dy, batch_size=BATCH_SIZE, epochs=EPOCHS, validation_split=0.2, verbose=False)
loss = history.history['loss']
plt.plot(np.arange(len(loss)), loss, label='loss')
loss = history.history['val_loss']
plt.plot(np.arange(len(loss)), loss, label='val_loss')
plt.grid()
plt.legend()
plt.show()
# 推論モデルの準備
model_pred_e = tf.keras.models.Model(inputs=[e_input], outputs=[e_state])
pred_d_input = tf.keras.layers.Input(shape=(1, 1))
pred_d_state_in = tf.keras.layers.Input(shape=(NUM_HIDDEN_PARAMS))
pred_d_output, pred_d_state = d_rnn(pred_d_input, initial_state=[pred_d_state_in])
pred_d_output = d_dense(pred_d_output)
pred_d_model = tf.keras.Model(inputs=[pred_d_input, pred_d_state_in], outputs=[pred_d_output, pred_d_state])
def predict(input_data):
state_value = model_pred_e.predict(input_data)
_dy = np.zeros((1, 1, 1))
output_data = []
for i in range(0, NUM_STEPS):
y_output, state_value = pred_d_model.predict([_dy, state_value])
output_data.append(y_output[0, 0, 0])
_dy[0, 0, 0] = y_output
return output_data
# 推論の実行
init_points = [0, 24, 49, 74]
for i in init_points:
_x = ex[i : i + 1]
_y = predict(_x)
if i == 0:
plt.plot(x[i : i + NUM_STEPS], _y, color="red", label='output')
else:
plt.plot(x[i : i + NUM_STEPS], _y, color="red")
plt.plot(x, seq_out, color = 'blue', linestyle = "dashed", label = 'correct')
plt.grid()
plt.legend()
plt.show()
- モデルのパラメータ確認
- 学習曲線 (過学習を起こさずにしっかり収束している)
11. [フレームワーク演習]data-augmentation
11-1. 要点
- データの水増し
- 画像認識モデルを学習する際に手元の学習データを擬似的に生成するアプローチ
- 画像の反転や回転やCropなどさまざまな方法がある
- 例
- Horizontal Flip
- Vertical Flip
- Crop
- Contrast
- Brightness
- Hue
- Rotate
- Mixup
11-2. 関連学習
- Cutout
- モデルの汎化能力を向上させるための効果的なデータ拡張手法の一つ
- cutoutは画像中の一部をランダムにマスクすることによってデータを変換
- このマスクされた領域は、画像の一部を切り取ったようになる
- この操作により、モデルは欠損した情報やノイズが存在する状況に対しても頑健になるように学習することが期待される
- 利点
- オクルージョン問題においては、物体が一部隠れている状況をシミュレートするためにcutoutを使用することができる
- これにより、モデルは一部の情報が欠落している場合でも正確に物体を検出したり分類したりする能力を獲得することができる
- また、ノイズに対する頑健性を向上させるためにも、cutoutが有効
- ノイズの存在により画像が劣化する状況を模倣することで、モデルがノイズに対しても安定した予測を行うことができるようになる
- 参考
12. [フレームワーク演習]activation_functions
12-1. 要点
- 活性化関数
- ニューラルネットワークの順伝播(forward)では、線形変換で得た値に対して、非線形な変換を行う。非線形な変換を行う際に用いられる関数を、活性化関数という
- 中間層に用いる活性化関数
- ステップ関数
- シグモイド関数
- tanh
- ReLU
- Leaky ReLU
- 出力層に用いる活性化関数
- シグモイド関数
- ソフトマックス関数
- 恒等関数
終わりに
自然言語まわりはCV周りより弱いので改めて復習が必要だと感じました。時間ある時に更新して行きたいと思います。