7 분 소요

순환 신경망(RNN)

1. 확률과 언어 모델

1.1 word2vec을 확률 관점으로

from IPython.display import Image
Image('./deep_learning_2_images/fig 5-1.png', width=500)

png

Image('./deep_learning_2_images/e 5-1.png', width=150)

png

Image('./deep_learning_2_images/fig 5-2.png', width=500)

png

Image('./deep_learning_2_images/e 5-2.png', width=150)

png

Image('./deep_learning_2_images/e 5-3.png', width=250)

png

1.2 언어 모델

Image('./deep_learning_2_images/e 5-4.png', width=500)

png

Image('./deep_learning_2_images/e 5-5.png', width=200)

png

Image('./deep_learning_2_images/e 5-6.png', width=500)

png

Image('./deep_learning_2_images/e 5-7.png', width=600)

png

Image('./deep_learning_2_images/fig 5-3.png', width=700)

png

1.3 CBOW 모델을 언어 모델로

Image('./deep_learning_2_images/e 5-8.png', width=500)

png

Image('./deep_learning_2_images/fig 5-4.png', width=600)

png

Image('./deep_learning_2_images/fig 5-5.png', width=600)

png

2. RNN이란

2.1 순환하는 신경망

Image('./deep_learning_2_images/fig 5-6.png', width=250)

png

Image('./deep_learning_2_images/fig 5-7.png', width=400)

png

2.2 순환하는 구조 펼치기

Image('./deep_learning_2_images/fig 5-8.png', width=700)

png

Image('./deep_learning_2_images/e 5-9.png', width=250)

png

2.3 BPTT

Image('./deep_learning_2_images/fig 5-10.png', width=600)

png

2.4 Truncated BPTT

Image('./deep_learning_2_images/fig 5-11.png', width=700)

png

Image('./deep_learning_2_images/fig 5-12.png', width=600)

png

Image('./deep_learning_2_images/fig 5-13.png', width=500)

png

Image('./deep_learning_2_images/fig 5-14.png', width=700)

png

2.5 Truncated BPTT의 미니배치 학습

Image('./deep_learning_2_images/fig 5-15.png', width=600)

png

3. RNN 구현

Image('./deep_learning_2_images/fig 5-16.png', width=600)

png

Image('./deep_learning_2_images/fig 5-17.png', width=600)

png

3.1 RNN 계층 구현

Image('./deep_learning_2_images/e 5-10.png', width=300)

png

Image('./deep_learning_2_images/fig 5-18.png', width=500)

png

Image('./deep_learning_2_images/fig 5-19.png', width=500)

png

Image('./deep_learning_2_images/fig 5-20.png', width=500)

png

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.cache = None

    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)
        self.cache = (x, h_prev, h_next)
        return h_next

    def backward(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 = np.dot(h_prev.T, dt)
        dh_prev = np.dot(dt, Wh.T)
        dWx = np.dot(x.T, dt)
        dx = np.dot(dt, Wx.T)

        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db

        return dx, dh_prev

3.2 Time RNN 계층 구현

Image('./deep_learning_2_images/fig 5-21.png', width=600)

png

Image('./deep_learning_2_images/fig 5-22.png', width=600)

png

Image('./deep_learning_2_images/fig 5-23.png', width=400)

png

Image('./deep_learning_2_images/fig 5-24.png', width=500)

png

class TimeRNN:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None

        self.h, self.dh = None, None
        self.stateful = stateful
        
    def set_state(self, h):
        self.h = h

    def reset_state(self):
        self.h = None        

    def forward(self, xs):
        Wx, Wh, b = self.params
        N, T, D = xs.shape
        D, H = Wx.shape

        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = RNN(*self.params)
            self.h = layer.forward(xs[:, t, :], self.h)
            hs[:, t, :] = self.h
            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        Wx, Wh, b = self.params
        N, T, H = dhs.shape
        D, H = Wx.shape

        dxs = np.empty((N, T, D), dtype='f')
        dh = 0
        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh = layer.backward(dhs[:, t, :] + dh)
            dxs[:, t, :] = dx

            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh

        return dxs

4. 시계열 데이터 처리 계층 구현

4.1 RNNLM의 전체 그림

Image('./deep_learning_2_images/fig 5-25.png', width=600)

png

Image('./deep_learning_2_images/fig 5-26.png', width=600)

png

4.2 Time 계층 구현

Image('./deep_learning_2_images/fig 5-27.png', width=600)

png

Image('./deep_learning_2_images/fig 5-28.png', width=600)

png

Image('./deep_learning_2_images/fig 5-29.png', width=600)

png

Image('./deep_learning_2_images/e 5-11.png', width=250)

png

5. RNNLM 학습과 평가

5.1 RNNLM 구현

Image('./deep_learning_2_images/fig 5-30.png', width=450)

png

SimpleRnnlm 클래스

from common.time_layers import *
class SimpleRnnlm:
    def __init__(self, vocab_size, wordvec_size, hidden_size):
        V, D, H = vocab_size, wordvec_size, hidden_size
        rn = np.random.randn

        # 가중치 초기화
        embed_W = (rn(V, D) / 100).astype('f')
        rnn_Wx = (rn(D, H) / np.sqrt(D)).astype('f')
        rnn_Wh = (rn(H, H) / np.sqrt(H)).astype('f')
        rnn_b = np.zeros(H).astype('f')
        affine_W = (rn(H, V) / np.sqrt(H)).astype('f')
        affine_b = np.zeros(V).astype('f')

        # 계층 생성
        self.layers = [
            TimeEmbedding(embed_W),
            TimeRNN(rnn_Wx, rnn_Wh, rnn_b, stateful=True),
            TimeAffine(affine_W, affine_b)
        ]
        self.loss_layer = TimeSoftmaxWithLoss()
        self.rnn_layer = self.layers[1]

        # 모든 가중치와 기울기를 리스트에 모은다.
        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

    def forward(self, xs, ts):
        for layer in self.layers:
            xs = layer.forward(xs)
        loss = self.loss_layer.forward(xs, ts)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout

    def reset_state(self):
        self.rnn_layer.reset_state()

5.2 언어 모델의 평가

Image('./deep_learning_2_images/fig 5-32.png', width=600)

png

Image('./deep_learning_2_images/e 5-12.png', width=200)

png

Image('./deep_learning_2_images/e 5-13.png', width=200)

png

Note. 퍼플렉서티 관련

# 퍼플렉서티는 확률의 역수(샘플이 한개)
1/0.8
1.25
import numpy as np

# 샘플 하나일 경우의 Cross entropy
log_p= -np.log(0.8)
log_p
0.2231435513142097
np.exp(log_p)
1.25
# 퍼플렉서티는 확률의 역수(샘플이 두개)
(1/0.8) * (1/0.9)
1.3888888888888888
# 샘플 두개일 경우의 Cross entropy
log_p1 = -np.log(0.8)
log_p2 = -np.log(0.9)
log_p = log_p1 + log_p2
log_p 
0.328504066972036
np.exp(log_p)
1.3888888888888888

5.3 RNNLM의 학습 코드

from dataset import ptb
from common.optimizer import SGD
# 하이퍼파라미터 설정
batch_size = 10
wordvec_size = 100
hidden_size = 100 # RNN의 은닉 상태 벡터의 원소 수
time_size = 5     # Truncated BPTT가 한 번에 펼치는 시간 크기
lr = 0.1
max_epoch = 100

# 학습 데이터 읽기(전체 중 1000개만)
corpus, word_to_id, id_to_word = ptb.load_data('train')
corpus_size = 1000
corpus = corpus[:corpus_size]
vocab_size = int(max(corpus) + 1)

xs = corpus[:-1]  # 입력
ts = corpus[1:]   # 출력(정답 레이블)
data_size = len(xs)
print('말뭉치 크기: %d, 어휘 수: %d' % (corpus_size, vocab_size))

# 학습 시 사용하는 변수
max_iters = data_size // (batch_size * time_size)
time_idx = 0
total_loss = 0
loss_count = 0
ppl_list = []

# 모델 생성
model = SimpleRnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)

# 1. 미니배치의 각 샘플의 읽기 시작 위치를 계산
jump = (corpus_size - 1) // batch_size
offsets = [i * jump for i in range(batch_size)]

for epoch in range(max_epoch):
    for iter in range(max_iters):
        # 2. 미니배치 취득
        batch_x = np.empty((batch_size, time_size), dtype='i')
        batch_t = np.empty((batch_size, time_size), dtype='i')
        for t in range(time_size):
            for i, offset in enumerate(offsets):
                batch_x[i, t] = xs[(offset + time_idx) % data_size]
                batch_t[i, t] = ts[(offset + time_idx) % data_size]
            time_idx += 1

        # 기울기를 구하여 매개변수 갱신
        loss = model.forward(batch_x, batch_t)
        model.backward()
        optimizer.update(model.params, model.grads)
        total_loss += loss
        loss_count += 1

    # 3. 에폭마다 퍼플렉서티 평가
    ppl = np.exp(total_loss / loss_count)
    print('| 에폭 %d | 퍼플렉서티 %.2f'
          % (epoch+1, ppl))
    ppl_list.append(float(ppl))
    total_loss, loss_count = 0, 0
말뭉치 크기: 1000, 어휘 수: 418
| 에폭 1 | 퍼플렉서티 381.94
| 에폭 2 | 퍼플렉서티 252.34
| 에폭 3 | 퍼플렉서티 221.96
| 에폭 4 | 퍼플렉서티 215.07
| 에폭 5 | 퍼플렉서티 204.93
| 에폭 6 | 퍼플렉서티 200.68
| 에폭 7 | 퍼플렉서티 198.25
| 에폭 8 | 퍼플렉서티 196.93
| 에폭 9 | 퍼플렉서티 191.36
| 에폭 10 | 퍼플렉서티 193.19
| 에폭 11 | 퍼플렉서티 188.74
| 에폭 12 | 퍼플렉서티 192.97
| 에폭 13 | 퍼플렉서티 190.91
| 에폭 14 | 퍼플렉서티 191.39
| 에폭 15 | 퍼플렉서티 189.61
| 에폭 16 | 퍼플렉서티 187.36
| 에폭 17 | 퍼플렉서티 185.26
| 에폭 18 | 퍼플렉서티 181.10
| 에폭 19 | 퍼플렉서티 183.33
| 에폭 20 | 퍼플렉서티 185.30
| 에폭 21 | 퍼플렉서티 183.47
| 에폭 22 | 퍼플렉서티 179.39
| 에폭 23 | 퍼플렉서티 176.64
| 에폭 24 | 퍼플렉서티 179.82
| 에폭 25 | 퍼플렉서티 178.83
| 에폭 26 | 퍼플렉서티 177.29
| 에폭 27 | 퍼플렉서티 171.48
| 에폭 28 | 퍼플렉서티 172.40
| 에폭 29 | 퍼플렉서티 171.34
| 에폭 30 | 퍼플렉서티 164.06
| 에폭 31 | 퍼플렉서티 165.36
| 에폭 32 | 퍼플렉서티 165.06
| 에폭 33 | 퍼플렉서티 165.33
| 에폭 34 | 퍼플렉서티 158.09
| 에폭 35 | 퍼플렉서티 158.03
| 에폭 36 | 퍼플렉서티 150.05
| 에폭 37 | 퍼플렉서티 148.82
| 에폭 38 | 퍼플렉서티 149.25
| 에폭 39 | 퍼플렉서티 139.72
| 에폭 40 | 퍼플렉서티 136.11
| 에폭 41 | 퍼플렉서티 138.48
| 에폭 42 | 퍼플렉서티 131.25
| 에폭 43 | 퍼플렉서티 125.54
| 에폭 44 | 퍼플렉서티 120.50
| 에폭 45 | 퍼플렉서티 118.27
| 에폭 46 | 퍼플렉서티 116.50
| 에폭 47 | 퍼플렉서티 115.49
| 에폭 48 | 퍼플렉서티 108.25
| 에폭 49 | 퍼플렉서티 103.07
| 에폭 50 | 퍼플렉서티 97.36
| 에폭 51 | 퍼플렉서티 93.61
| 에폭 52 | 퍼플렉서티 90.94
| 에폭 53 | 퍼플렉서티 85.82
| 에폭 54 | 퍼플렉서티 81.91
| 에폭 55 | 퍼플렉서티 77.61
| 에폭 56 | 퍼플렉서티 74.04
| 에폭 57 | 퍼플렉서티 70.79
| 에폭 58 | 퍼플렉서티 66.88
| 에폭 59 | 퍼플렉서티 63.76
| 에폭 60 | 퍼플렉서티 59.75
| 에폭 61 | 퍼플렉서티 57.97
| 에폭 62 | 퍼플렉서티 55.46
| 에폭 63 | 퍼플렉서티 51.23
| 에폭 64 | 퍼플렉서티 47.76
| 에폭 65 | 퍼플렉서티 45.95
| 에폭 66 | 퍼플렉서티 43.11
| 에폭 67 | 퍼플렉서티 43.20
| 에폭 68 | 퍼플렉서티 39.59
| 에폭 69 | 퍼플렉서티 36.99
| 에폭 70 | 퍼플렉서티 35.31
| 에폭 71 | 퍼플렉서티 34.74
| 에폭 72 | 퍼플렉서티 32.00
| 에폭 73 | 퍼플렉서티 30.01
| 에폭 74 | 퍼플렉서티 27.93
| 에폭 75 | 퍼플렉서티 28.19
| 에폭 76 | 퍼플렉서티 25.58
| 에폭 77 | 퍼플렉서티 23.10
| 에폭 78 | 퍼플렉서티 22.35
| 에폭 79 | 퍼플렉서티 22.01
| 에폭 80 | 퍼플렉서티 21.05
| 에폭 81 | 퍼플렉서티 19.99
| 에폭 82 | 퍼플렉서티 18.81
| 에폭 83 | 퍼플렉서티 17.75
| 에폭 84 | 퍼플렉서티 16.46
| 에폭 85 | 퍼플렉서티 15.94
| 에폭 86 | 퍼플렉서티 15.29
| 에폭 87 | 퍼플렉서티 13.98
| 에폭 88 | 퍼플렉서티 13.83
| 에폭 89 | 퍼플렉서티 13.18
| 에폭 90 | 퍼플렉서티 12.04
| 에폭 91 | 퍼플렉서티 12.08
| 에폭 92 | 퍼플렉서티 10.71
| 에폭 93 | 퍼플렉서티 10.69
| 에폭 94 | 퍼플렉서티 10.14
| 에폭 95 | 퍼플렉서티 9.54
| 에폭 96 | 퍼플렉서티 9.22
| 에폭 97 | 퍼플렉서티 8.75
| 에폭 98 | 퍼플렉서티 7.97
| 에폭 99 | 퍼플렉서티 7.69
| 에폭 100 | 퍼플렉서티 7.34
import matplotlib.pyplot as plt
# 그래프 그리기
x = np.arange(len(ppl_list))
plt.plot(x, ppl_list, label='train')
plt.xlabel('epochs')
plt.ylabel('perplexity')
plt.show()

png

Reference

댓글남기기