言語モデル¶
embeddingを直接推定するのではなく、単語予測モデル(言語モデル)を構築して副次的にembeddingを取得することになる。
言語モデルとは、尤もらしい文章を生成できるような確率分布を習得するために、文章の確率を推定するモデルのこと。
ある文章をトークン化したのをと表記するならば、
を求めたいということになる。これは条件付き確率の積として表せる
ここではより前のトークン列で、文脈(context)と呼ばれる
例¶
言語モデルは文脈をもとに次の単語を予測する。例えば
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は前のトークンまでの情報を次のトークンの出力に渡すパスが存在する。 トークンの系列を時系列モデルになぞらえて時刻と表現すると、時刻の出力は
となる。ここでは入力で、は1時刻前の出力、\b{W}_h, \b{W}_xは重みで\b{b}はバイアスである。
なお、出力\b{h}は隠れ状態(hidden state)と呼ばれる事が多い
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)
outputtensor([[[ 0.4427, 0.9229, 0.5728],
[ 0.1982, -0.0123, 0.3031],
[ 0.3001, 0.3558, 0.2073]],
[[-0.4958, 0.5230, -0.5726],
[ 0.2084, 0.5869, 0.0893],
[-0.0264, 0.5945, -0.3003]],
[[ 0.3149, 0.3611, 0.0945],
[-0.0669, 0.5235, -0.2458],
[ 0.0957, 0.5043, -0.2213]],
[[-0.2301, 0.3040, -0.3211],
[-0.1218, 0.2889, -0.3467],
[-0.2557, 0.2502, -0.5259]],
[[ 0.3510, 0.5464, 0.0433],
[ 0.1098, 0.4022, -0.2408],
[ 0.4425, 0.5785, 0.0343]]], 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