言語モデルとRNN#
言語モデル#
embeddingを直接推定するのではなく、単語予測モデル(言語モデル)を構築して副次的にembeddingを取得することになる。
言語モデルとは、尤もらしい文章を生成できるような確率分布を習得するために、文章の確率を推定するモデルのこと。
ある文章\(S\)をトークン化したのを\((w_1, w_2, \cdots, w_n)\)と表記するならば、
を求めたいということになる。これは条件付き確率の積として表せる
ここで\(\boldsymbol{c}_i\)は\(w_i\)より前のトークン列\(\boldsymbol{c}_i=(w_1,w_2,\cdots,w_{i-1})\)で、文脈(context)と呼ばれる
Note
同時確率の分解
これは確率の乗法定理
に基づく。
\(w_m\)までの単語を\(C_m\)とすると、
さらに
となる。これを繰り返すことで上記のようになる
例#
言語モデルは文脈をもとに次の単語を予測する。例えば
Alice is reading a book in the room. Bob comes into the room and says hi to
?
の?に入る語を予測する
語順の問題#
Word2Vecに使われたcontinuous bag-of words (CBOW) のようなFeed-Forward Networkによる言語モデルでは、コンテキストの語順が考慮されない
RNN型ニューラル言語モデル(Mikolov + 2010)#
Mikolov et al. (2010). Recurrent neural network based language model.
RNN#
RNNは前のトークンまでの情報を次のトークンの出力に渡すパスが存在する。 トークンの系列を時系列モデルになぞらえて時刻と表現すると、時刻\(t\)の出力\(\boldsymbol{h}_t\)は
となる。ここで\(\boldsymbol{x}_t\)は入力で、\(\boldsymbol{h}_{t-1}\)は1時刻前の出力、\(\b{W}_h, \b{W}_x\)は重みで\(\b{b}\)はバイアスである。
なお、出力\(\b{h}\)は隠れ状態(hidden state)と呼ばれる事が多い
Note
双曲線正接(hyperbolic tangent: tanh)関数
RNNの場合、活性化関数にはtanh
を用いることが多い。tanhはシグモイド関数と形状が似ているが、値域は\([-1, 1]\)と負の値を許しており、また二次微分の減衰がゆっくりとゼロになるため勾配消失が起きにくいという特性がある。
Fig. 1 tanh#
Truncated BPTT#
RNNの誤差逆伝播法は、時間方向への逆伝播法ということで**Backproagation Through Time(BPTT)**と呼ばれる。
しかし長い文章を扱う場合、すべてのトークンを使うと学習の際にメモリに乗り切らない問題や勾配が不安定になる問題がある。 そこで、逆伝播のときはトークン系列を分割して学習する(順伝播は全部つながるようにする)方法があり、これをTruncated BPTTという。
実装(PyTorch)#
# 最小構成
import torch
from torch import nn
n_input = 1
n_hidden = 3
n_layer = 2
rnn = nn.RNN(n_input, n_hidden, n_layer)
x = torch.randn(5, 3, n_input)
h0 = torch.randn(2, 3, n_hidden)
output, hn = rnn(x, h0)
output
tensor([[[ 0.1534, 0.6573, 0.9458],
[-0.4423, 0.6545, 0.3265],
[-0.8389, 0.1188, 0.3840]],
[[-0.3089, 0.7257, 0.6822],
[-0.3239, 0.5631, 0.7560],
[ 0.1298, 0.1704, 0.3802]],
[[ 0.0830, 0.5010, 0.5768],
[-0.5564, 0.6891, 0.8701],
[-0.7490, 0.7378, 0.7603]],
[[-0.2585, 0.6630, 0.5109],
[-0.5826, 0.6280, 0.9308],
[ 0.0104, 0.4070, 0.7900]],
[[-0.5301, 0.6908, 0.8422],
[ 0.0631, 0.4131, 0.7188],
[-0.3598, 0.6814, 0.6392]]], grad_fn=<StackBackward0>)
実装(Python)#
参考:ゼロから作るDeep Learning 2
import numpy as np
h = 2
w = 3
Wh = np.random.normal(size = (h, w))
import numpy as np
class RNN:
def __init__(self, Wx, Wh, b):
self.params = [Wx, Wh, b]
self.grads = [np.zeros_like(Wx),
np.zeros_like(Wh),
np.zeros_like(b)]
self.chache = None
def forward(self, x, h_prev):
Wx, Wh, b = self.params
t = Wh @ h_prev + Wx @ x + b
h_nrex = np.tanh(t)
self.cache = (x, h_prev, h_next)
return h_next
def backword(self, dh_next):
Wx, Wh, b = self.params
x, h_prev, h_next = self.cache
dt = dh_next * (1 - h_next ** 2)
db = np.sum(dt, axis=0)
dWh = h_prev.T @ dt
dh_prev = Wh @ dt
dWx = x @ dt
dx = Wx @ dt
self.grads[0][...] = dWx
self.grads[1][...] = dWh
self.grads[2][...] = db
return dx, dh_prev
RWKV#
RNNを利用しつつも並列計算を可能とした
従来の大規模言語モデルの制約だった「入力量の限界」を取り払った「RWKV」は一体どんな言語モデルなのか? - GIGAZINE