🏷️sec_rnn_scratch
이 섹션에서는 :numref:sec_rnn
의 설명에 따라 문자 수준 언어 모델에 대해 처음부터 RNN을 구현합니다.이러한 모델은 H.G. Wells의타임머신에 대한 교육을 받게 됩니다.이전과 마찬가지로 :numref:sec_language_model
에 소개된 데이터세트를 먼저 읽는 것으로 시작합니다.
%matplotlib inline
from d2l import mxnet as d2l
import math
from mxnet import autograd, gluon, np, npx
npx.set_np()
#@tab pytorch
%matplotlib inline
from d2l import torch as d2l
import math
import torch
from torch import nn
from torch.nn import functional as F
#@tab tensorflow
%matplotlib inline
from d2l import tensorflow as d2l
import math
import tensorflow as tf
#@tab all
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
#@tab tensorflow
train_random_iter, vocab_random_iter = d2l.load_data_time_machine(
batch_size, num_steps, use_random_iter=True)
각 토큰은 train_iter
에서 숫자 인덱스로 표시됩니다.이러한 지표를 신경망에 직접 공급하면 배우기가 어려울 수 있습니다.우리는 종종 각 토큰을 좀 더 표현적인 특징 벡터로 표현합니다.가장 쉬운 표현은 :numref:subsec_classification-problem
에 도입된원-핫 인코딩입니다.
간단히 말해서 각 인덱스를 다른 단위 벡터에 매핑합니다. 어휘의 서로 다른 토큰 수가 len(vocab)
) 이고 토큰 인덱스의 범위가
npx.one_hot(np.array([0, 2]), len(vocab))
#@tab pytorch
F.one_hot(torch.tensor([0, 2]), len(vocab))
#@tab tensorflow
tf.one_hot(tf.constant([0, 2]), len(vocab))
매번 샘플링하는 (미니배치의 모양) (는 (배치 크기, 시간 단계 수) 입니다.one_hot
함수는 이러한 미니 배치를 마지막 차원이 어휘 크기 (len(vocab)
) 와 동일한 3 차원 텐서로 변환합니다.) 우리는 종종 모양의 출력 (시간 단계 수, 배치 크기, 어휘 크기) 을 얻을 수 있도록 입력을 전치합니다.이렇게 하면 미니배치의 숨겨진 상태를 시간 단계별로 업데이트하기 위해 가장 바깥쪽 차원을 보다 편리하게 반복할 수 있습니다.
X = d2l.reshape(d2l.arange(10), (2, 5))
npx.one_hot(X.T, 28).shape
#@tab pytorch
X = d2l.reshape(d2l.arange(10), (2, 5))
F.one_hot(X.T, 28).shape
#@tab tensorflow
X = d2l.reshape(d2l.arange(10), (2, 5))
tf.one_hot(tf.transpose(X), 28).shape
다음으로 [RNN 모델의 모델 매개 변수를 초기화] 합니다.은닉 유닛 수 num_hiddens
는 조정 가능한 하이퍼파라미터입니다.언어 모델을 학습할 때 입력과 출력은 동일한 어휘에서 나옵니다.따라서 어휘 크기와 동일한 차원이 있습니다.
def get_params(vocab_size, num_hiddens, device):
num_inputs = num_outputs = vocab_size
def normal(shape):
return np.random.normal(scale=0.01, size=shape, ctx=device)
# Hidden layer parameters
W_xh = normal((num_inputs, num_hiddens))
W_hh = normal((num_hiddens, num_hiddens))
b_h = d2l.zeros(num_hiddens, ctx=device)
# Output layer parameters
W_hq = normal((num_hiddens, num_outputs))
b_q = d2l.zeros(num_outputs, ctx=device)
# Attach gradients
params = [W_xh, W_hh, b_h, W_hq, b_q]
for param in params:
param.attach_grad()
return params
#@tab pytorch
def get_params(vocab_size, num_hiddens, device):
num_inputs = num_outputs = vocab_size
def normal(shape):
return torch.randn(size=shape, device=device) * 0.01
# Hidden layer parameters
W_xh = normal((num_inputs, num_hiddens))
W_hh = normal((num_hiddens, num_hiddens))
b_h = d2l.zeros(num_hiddens, device=device)
# Output layer parameters
W_hq = normal((num_hiddens, num_outputs))
b_q = d2l.zeros(num_outputs, device=device)
# Attach gradients
params = [W_xh, W_hh, b_h, W_hq, b_q]
for param in params:
param.requires_grad_(True)
return params
#@tab tensorflow
def get_params(vocab_size, num_hiddens):
num_inputs = num_outputs = vocab_size
def normal(shape):
return d2l.normal(shape=shape,stddev=0.01,mean=0,dtype=tf.float32)
# Hidden layer parameters
W_xh = tf.Variable(normal((num_inputs, num_hiddens)), dtype=tf.float32)
W_hh = tf.Variable(normal((num_hiddens, num_hiddens)), dtype=tf.float32)
b_h = tf.Variable(d2l.zeros(num_hiddens), dtype=tf.float32)
# Output layer parameters
W_hq = tf.Variable(normal((num_hiddens, num_outputs)), dtype=tf.float32)
b_q = tf.Variable(d2l.zeros(num_outputs), dtype=tf.float32)
params = [W_xh, W_hh, b_h, W_hq, b_q]
return params
RNN 모델을 정의하려면 먼저 초기화시 숨겨진 상태를 반환하는 [init_rnn_state
함수가 필요합니다.] 0으로 채워진 텐서와 모양 (배치 크기, 은닉 유닛 수) 을 반환합니다.튜플을 사용하면 숨겨진 상태가 여러 변수를 포함하는 상황을 더 쉽게 처리 할 수 있습니다. 이 문제는 이후 섹션에서 보게 될 것입니다.
def init_rnn_state(batch_size, num_hiddens, device):
return (d2l.zeros((batch_size, num_hiddens), ctx=device), )
#@tab pytorch
def init_rnn_state(batch_size, num_hiddens, device):
return (d2l.zeros((batch_size, num_hiddens), device=device), )
#@tab tensorflow
def init_rnn_state(batch_size, num_hiddens):
return (d2l.zeros((batch_size, num_hiddens)), )
[다음 rnn
함수는 시간 단계에서 은닉 상태와 출력값을 계산하는 방법을 정의합니다.] RNN 모델은 가장 바깥쪽 차원 inputs
을 반복하여 미니배치의 숨겨진 상태 H
을 시간 단계별로 업데이트합니다.또한 여기서 활성화 함수는 sec_mlp
에 설명된 대로 요소가 실수에 균일하게 분포되어 있는 경우
def rnn(inputs, state, params):
# Shape of `inputs`: (`num_steps`, `batch_size`, `vocab_size`)
W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
# Shape of `X`: (`batch_size`, `vocab_size`)
for X in inputs:
H = np.tanh(np.dot(X, W_xh) + np.dot(H, W_hh) + b_h)
Y = np.dot(H, W_hq) + b_q
outputs.append(Y)
return np.concatenate(outputs, axis=0), (H,)
#@tab pytorch
def rnn(inputs, state, params):
# Here `inputs` shape: (`num_steps`, `batch_size`, `vocab_size`)
W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
# Shape of `X`: (`batch_size`, `vocab_size`)
for X in inputs:
H = torch.tanh(torch.mm(X, W_xh) + torch.mm(H, W_hh) + b_h)
Y = torch.mm(H, W_hq) + b_q
outputs.append(Y)
return torch.cat(outputs, dim=0), (H,)
#@tab tensorflow
def rnn(inputs, state, params):
# Here `inputs` shape: (`num_steps`, `batch_size`, `vocab_size`)
W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
# Shape of `X`: (`batch_size`, `vocab_size`)
for X in inputs:
X = tf.reshape(X,[-1,W_xh.shape[0]])
H = tf.tanh(tf.matmul(X, W_xh) + tf.matmul(H, W_hh) + b_h)
Y = tf.matmul(H, W_hq) + b_q
outputs.append(Y)
return d2l.concat(outputs, axis=0), (H,)
필요한 모든 함수가 정의되면, 다음으로 처음부터 구현된 RNN 모델에 대해 [이 함수를 래핑하고 매개 변수를 저장하는 클래스를 만들기] 합니다.
class RNNModelScratch: #@save
"""An RNN Model implemented from scratch."""
def __init__(self, vocab_size, num_hiddens, device, get_params,
init_state, forward_fn):
self.vocab_size, self.num_hiddens = vocab_size, num_hiddens
self.params = get_params(vocab_size, num_hiddens, device)
self.init_state, self.forward_fn = init_state, forward_fn
def __call__(self, X, state):
X = npx.one_hot(X.T, self.vocab_size)
return self.forward_fn(X, state, self.params)
def begin_state(self, batch_size, ctx):
return self.init_state(batch_size, self.num_hiddens, ctx)
#@tab pytorch
class RNNModelScratch: #@save
"""A RNN Model implemented from scratch."""
def __init__(self, vocab_size, num_hiddens, device,
get_params, init_state, forward_fn):
self.vocab_size, self.num_hiddens = vocab_size, num_hiddens
self.params = get_params(vocab_size, num_hiddens, device)
self.init_state, self.forward_fn = init_state, forward_fn
def __call__(self, X, state):
X = F.one_hot(X.T, self.vocab_size).type(torch.float32)
return self.forward_fn(X, state, self.params)
def begin_state(self, batch_size, device):
return self.init_state(batch_size, self.num_hiddens, device)
#@tab tensorflow
class RNNModelScratch: #@save
"""A RNN Model implemented from scratch."""
def __init__(self, vocab_size, num_hiddens,
init_state, forward_fn, get_params):
self.vocab_size, self.num_hiddens = vocab_size, num_hiddens
self.init_state, self.forward_fn = init_state, forward_fn
self.trainable_variables = get_params(vocab_size, num_hiddens)
def __call__(self, X, state):
X = tf.one_hot(tf.transpose(X), self.vocab_size)
X = tf.cast(X, tf.float32)
return self.forward_fn(X, state, self.trainable_variables)
def begin_state(self, batch_size, *args, **kwargs):
return self.init_state(batch_size, self.num_hiddens)
예를 들어 숨겨진 상태의 차원이 변경되지 않도록 [출력이 올바른 모양을 가지고 있는지 확인] 하겠습니다.
#@tab mxnet
num_hiddens = 512
net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
init_rnn_state, rnn)
state = net.begin_state(X.shape[0], d2l.try_gpu())
Y, new_state = net(X.as_in_context(d2l.try_gpu()), state)
Y.shape, len(new_state), new_state[0].shape
#@tab pytorch
num_hiddens = 512
net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
init_rnn_state, rnn)
state = net.begin_state(X.shape[0], d2l.try_gpu())
Y, new_state = net(X.to(d2l.try_gpu()), state)
Y.shape, len(new_state), new_state[0].shape
#@tab tensorflow
# defining tensorflow training strategy
device_name = d2l.try_gpu()._device_name
strategy = tf.distribute.OneDeviceStrategy(device_name)
num_hiddens = 512
with strategy.scope():
net = RNNModelScratch(len(vocab), num_hiddens, init_rnn_state, rnn,
get_params)
state = net.begin_state(X.shape[0])
Y, new_state = net(X, state)
Y.shape, len(new_state), new_state[0].shape
출력 모양은 (시간 단계 수
먼저 사용자가 제공 한 prefix
다음에 새 문자를 생성하는 예측 함수를 정의하겠습니다. 이 함수는 여러 문자를 포함하는 문자열입니다.prefix
에서 이러한 시작 문자를 반복할 때 출력을 생성하지 않고 숨겨진 상태를 다음 시간 단계로 계속 전달합니다.이를워밍업 기간이라고 하며, 이 기간 동안 모델은 자체 업데이트 (예: 숨겨진 상태 업데이트) 를 수행하지만 예측하지는 않습니다.워밍업 기간이 지나면 숨겨진 상태가 일반적으로 처음에 초기화된 값보다 낫습니다.따라서 예측된 문자를 생성하고 방출합니다.
def predict_ch8(prefix, num_preds, net, vocab, device): #@save
"""Generate new characters following the `prefix`."""
state = net.begin_state(batch_size=1, ctx=device)
outputs = [vocab[prefix[0]]]
get_input = lambda: d2l.reshape(
d2l.tensor([outputs[-1]], ctx=device), (1, 1))
for y in prefix[1:]: # Warm-up period
_, state = net(get_input(), state)
outputs.append(vocab[y])
for _ in range(num_preds): # Predict `num_preds` steps
y, state = net(get_input(), state)
outputs.append(int(y.argmax(axis=1).reshape(1)))
return ''.join([vocab.idx_to_token[i] for i in outputs])
#@tab pytorch
def predict_ch8(prefix, num_preds, net, vocab, device): #@save
"""Generate new characters following the `prefix`."""
state = net.begin_state(batch_size=1, device=device)
outputs = [vocab[prefix[0]]]
get_input = lambda: d2l.reshape(d2l.tensor(
[outputs[-1]], device=device), (1, 1))
for y in prefix[1:]: # Warm-up period
_, state = net(get_input(), state)
outputs.append(vocab[y])
for _ in range(num_preds): # Predict `num_preds` steps
y, state = net(get_input(), state)
outputs.append(int(y.argmax(dim=1).reshape(1)))
return ''.join([vocab.idx_to_token[i] for i in outputs])
#@tab tensorflow
def predict_ch8(prefix, num_preds, net, vocab): #@save
"""Generate new characters following the `prefix`."""
state = net.begin_state(batch_size=1, dtype=tf.float32)
outputs = [vocab[prefix[0]]]
get_input = lambda: d2l.reshape(d2l.tensor([outputs[-1]]), (1, 1)).numpy()
for y in prefix[1:]: # Warm-up period
_, state = net(get_input(), state)
outputs.append(vocab[y])
for _ in range(num_preds): # Predict `num_preds` steps
y, state = net(get_input(), state)
outputs.append(int(y.numpy().argmax(axis=1).reshape(1)))
return ''.join([vocab.idx_to_token[i] for i in outputs])
이제 predict_ch8
함수를 테스트할 수 있습니다.접두사를 time traveller
로 지정하고 10자를 추가로 생성하도록 합니다.네트워크를 훈련시키지 않았으므로 무의미한 예측이 생성됩니다.
#@tab mxnet,pytorch
predict_ch8('time traveller ', 10, net, vocab, d2l.try_gpu())
#@tab tensorflow
predict_ch8('time traveller ', 10, net, vocab)
길이 sec_numerical_stability
에서 언급했듯이 수치적 불안정성이 발생할 수 있습니다. 예를 들어
일반적으로 최적화 문제를 풀 때 모델 매개 변수에 대한 업데이트 단계 (예: 벡터 형식
이 경우 매개 변수 벡터를
이는 우리가
때로는 그래디언트가 상당히 커서 최적화 알고리즘이 수렴하지 못할 수 있습니다.학습률
(
이렇게 하면 기울기 노름이
아래에서는 처음부터 구현되는 모델 또는 상위 수준 API로 구성된 모델의 그래디언트를 클립하는 함수를 정의합니다.또한 모든 모델 매개 변수에 대해 기울기 노름을 계산합니다.
def grad_clipping(net, theta): #@save
"""Clip the gradient."""
if isinstance(net, gluon.Block):
params = [p.data() for p in net.collect_params().values()]
else:
params = net.params
norm = math.sqrt(sum((p.grad ** 2).sum() for p in params))
if norm > theta:
for param in params:
param.grad[:] *= theta / norm
#@tab pytorch
def grad_clipping(net, theta): #@save
"""Clip the gradient."""
if isinstance(net, nn.Module):
params = [p for p in net.parameters() if p.requires_grad]
else:
params = net.params
norm = torch.sqrt(sum(torch.sum((p.grad ** 2)) for p in params))
if norm > theta:
for param in params:
param.grad[:] *= theta / norm
#@tab tensorflow
def grad_clipping(grads, theta): #@save
"""Clip the gradient."""
theta = tf.constant(theta, dtype=tf.float32)
new_grad = []
for grad in grads:
if isinstance(grad, tf.IndexedSlices):
new_grad.append(tf.convert_to_tensor(grad))
else:
new_grad.append(grad)
norm = tf.math.sqrt(sum((tf.reduce_sum(grad ** 2)).numpy()
for grad in new_grad))
norm = tf.cast(norm, tf.float32)
if tf.greater(norm, theta):
for i, grad in enumerate(new_grad):
new_grad[i] = grad * theta / norm
else:
new_grad = new_grad
return new_grad
모델을 훈련시키기 전에 [한 epoch에서 모델을 훈련시키는 함수를 정의] 하겠습니다.:numref:sec_softmax_scratch
의 모델을 세 곳에서 훈련시키는 방법과 다릅니다.
- 순차 데이터에 대한 샘플링 방법이 다르면 (랜덤 샘플링 및 순차 파티셔닝) 숨겨진 상태의 초기화에 차이가 발생합니다.
- 모델 매개 변수를 업데이트하기 전에 그라디언트를 자릅니다.이렇게 하면 훈련 프로세스 중 특정 지점에서 기울기가 터지더라도 모델이 분기되지 않습니다.
- 당혹감을 사용하여 모델을 평가합니다.:numref:
subsec_perplexity
에서 설명한 것처럼 길이가 다른 시퀀스를 비교할 수 있습니다.
특히 순차적 파티셔닝을 사용하는 경우 각 에포크의 시작 부분에서만 숨겨진 상태를 초기화합니다.다음 미니배치의
랜덤 샘플링을 사용할 때는 각 예제가 임의의 위치로 샘플링되므로 각 반복마다 숨겨진 상태를 다시 초기화해야합니다.:numref:sec_softmax_scratch
의 train_epoch_ch3
함수와 마찬가지로 updater
은 모델 매개변수를 업데이트하는 일반적인 함수입니다.이는 처음부터 구현된 d2l.sgd
함수이거나 딥러닝 프레임워크의 내장 최적화 함수일 수 있습니다.
#@save
def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter):
"""Train a model within one epoch (defined in Chapter 8)."""
state, timer = None, d2l.Timer()
metric = d2l.Accumulator(2) # Sum of training loss, no. of tokens
for X, Y in train_iter:
if state is None or use_random_iter:
# Initialize `state` when either it is the first iteration or
# using random sampling
state = net.begin_state(batch_size=X.shape[0], ctx=device)
else:
for s in state:
s.detach()
y = Y.T.reshape(-1)
X, y = X.as_in_ctx(device), y.as_in_ctx(device)
with autograd.record():
y_hat, state = net(X, state)
l = loss(y_hat, y).mean()
l.backward()
grad_clipping(net, 1)
updater(batch_size=1) # Since the `mean` function has been invoked
metric.add(l * d2l.size(y), d2l.size(y))
return math.exp(metric[0] / metric[1]), metric[1] / timer.stop()
#@tab pytorch
#@save
def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter):
"""Train a net within one epoch (defined in Chapter 8)."""
state, timer = None, d2l.Timer()
metric = d2l.Accumulator(2) # Sum of training loss, no. of tokens
for X, Y in train_iter:
if state is None or use_random_iter:
# Initialize `state` when either it is the first iteration or
# using random sampling
state = net.begin_state(batch_size=X.shape[0], device=device)
else:
if isinstance(net, nn.Module) and not isinstance(state, tuple):
# `state` is a tensor for `nn.GRU`
state.detach_()
else:
# `state` is a tuple of tensors for `nn.LSTM` and
# for our custom scratch implementation
for s in state:
s.detach_()
y = Y.T.reshape(-1)
X, y = X.to(device), y.to(device)
y_hat, state = net(X, state)
l = loss(y_hat, y.long()).mean()
if isinstance(updater, torch.optim.Optimizer):
updater.zero_grad()
l.backward()
grad_clipping(net, 1)
updater.step()
else:
l.backward()
grad_clipping(net, 1)
# Since the `mean` function has been invoked
updater(batch_size=1)
metric.add(l * d2l.size(y), d2l.size(y))
return math.exp(metric[0] / metric[1]), metric[1] / timer.stop()
#@tab tensorflow
#@save
def train_epoch_ch8(net, train_iter, loss, updater, use_random_iter):
"""Train a model within one epoch (defined in Chapter 8)."""
state, timer = None, d2l.Timer()
metric = d2l.Accumulator(2) # Sum of training loss, no. of tokens
for X, Y in train_iter:
if state is None or use_random_iter:
# Initialize `state` when either it is the first iteration or
# using random sampling
state = net.begin_state(batch_size=X.shape[0], dtype=tf.float32)
with tf.GradientTape(persistent=True) as g:
y_hat, state = net(X, state)
y = d2l.reshape(tf.transpose(Y), (-1))
l = loss(y, y_hat)
params = net.trainable_variables
grads = g.gradient(l, params)
grads = grad_clipping(grads, 1)
updater.apply_gradients(zip(grads, params))
# Keras loss by default returns the average loss in a batch
# l_sum = l * float(d2l.size(y)) if isinstance(
# loss, tf.keras.losses.Loss) else tf.reduce_sum(l)
metric.add(l * d2l.size(y), d2l.size(y))
return math.exp(metric[0] / metric[1]), metric[1] / timer.stop()
[훈련 함수는 처음부터 구현되거나 상위 수준 API를 사용하여 구현된 RNN 모델을 지원합니다.]
def train_ch8(net, train_iter, vocab, lr, num_epochs, device, #@save
use_random_iter=False):
"""Train a model (defined in Chapter 8)."""
loss = gluon.loss.SoftmaxCrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', ylabel='perplexity',
legend=['train'], xlim=[10, num_epochs])
# Initialize
if isinstance(net, gluon.Block):
net.initialize(ctx=device, force_reinit=True,
init=init.Normal(0.01))
trainer = gluon.Trainer(net.collect_params(),
'sgd', {'learning_rate': lr})
updater = lambda batch_size: trainer.step(batch_size)
else:
updater = lambda batch_size: d2l.sgd(net.params, lr, batch_size)
predict = lambda prefix: predict_ch8(prefix, 50, net, vocab, device)
# Train and predict
for epoch in range(num_epochs):
ppl, speed = train_epoch_ch8(
net, train_iter, loss, updater, device, use_random_iter)
if (epoch + 1) % 10 == 0:
animator.add(epoch + 1, [ppl])
print(f'perplexity {ppl:.1f}, {speed:.1f} tokens/sec on {str(device)}')
print(predict('time traveller'))
print(predict('traveller'))
#@tab pytorch
#@save
def train_ch8(net, train_iter, vocab, lr, num_epochs, device,
use_random_iter=False):
"""Train a model (defined in Chapter 8)."""
loss = nn.CrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', ylabel='perplexity',
legend=['train'], xlim=[10, num_epochs])
# Initialize
if isinstance(net, nn.Module):
updater = torch.optim.SGD(net.parameters(), lr)
else:
updater = lambda batch_size: d2l.sgd(net.params, lr, batch_size)
predict = lambda prefix: predict_ch8(prefix, 50, net, vocab, device)
# Train and predict
for epoch in range(num_epochs):
ppl, speed = train_epoch_ch8(
net, train_iter, loss, updater, device, use_random_iter)
if (epoch + 1) % 10 == 0:
print(predict('time traveller'))
animator.add(epoch + 1, [ppl])
print(f'perplexity {ppl:.1f}, {speed:.1f} tokens/sec on {str(device)}')
print(predict('time traveller'))
print(predict('traveller'))
#@tab tensorflow
#@save
def train_ch8(net, train_iter, vocab, lr, num_epochs, strategy,
use_random_iter=False):
"""Train a model (defined in Chapter 8)."""
with strategy.scope():
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
updater = tf.keras.optimizers.SGD(lr)
animator = d2l.Animator(xlabel='epoch', ylabel='perplexity',
legend=['train'], xlim=[10, num_epochs])
predict = lambda prefix: predict_ch8(prefix, 50, net, vocab)
# Train and predict
for epoch in range(num_epochs):
ppl, speed = train_epoch_ch8(net, train_iter, loss, updater,
use_random_iter)
if (epoch + 1) % 10 == 0:
print(predict('time traveller'))
animator.add(epoch + 1, [ppl])
device = d2l.try_gpu()._device_name
print(f'perplexity {ppl:.1f}, {speed:.1f} tokens/sec on {str(device)}')
print(predict('time traveller'))
print(predict('traveller'))
[이제 RNN 모델을 훈련시킬 수 있습니다.] 데이터셋에서 10000개의 토큰만 사용하기 때문에 모델이 더 잘 수렴하려면 더 많은 Epoch가 필요합니다.
#@tab mxnet,pytorch
num_epochs, lr = 500, 1
train_ch8(net, train_iter, vocab, lr, num_epochs, d2l.try_gpu())
#@tab tensorflow
num_epochs, lr = 500, 1
train_ch8(net, train_iter, vocab, lr, num_epochs, strategy)
[마지막으로 랜덤 샘플링 방법을 사용한 결과를 확인해보겠습니다.]
#@tab mxnet,pytorch
net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
init_rnn_state, rnn)
train_ch8(net, train_iter, vocab, lr, num_epochs, d2l.try_gpu(),
use_random_iter=True)
#@tab tensorflow
with strategy.scope():
net = RNNModelScratch(len(vocab), num_hiddens, init_rnn_state, rnn,
get_params)
train_ch8(net, train_iter, vocab_random_iter, lr, num_epochs, strategy,
use_random_iter=True)
위의 RNN 모델을 처음부터 구현하는 것은 유익하지만 편리하지는 않습니다.다음 섹션에서는 RNN 모델을 더 쉽게 구현하고 더 빠르게 실행하는 방법과 같이 RNN 모델을 개선하는 방법을 살펴볼 것입니다.
- RNN 기반 문자 수준 언어 모델을 훈련시켜 사용자가 제공 한 텍스트 접두사 다음에 텍스트를 생성 할 수 있습니다.
- 간단한 RNN 언어 모델은 입력 인코딩, RNN 모델링 및 출력 생성으로 구성됩니다.
- RNN 모델은 훈련을 위해 상태 초기화가 필요하지만 랜덤 샘플링과 순차 분할은 다른 방식을 사용합니다.
- 순차 분할을 사용할 때는 계산 비용을 줄이기 위해 그래디언트를 분리해야 합니다.
- 워밍업 기간을 사용하면 예측하기 전에 모델이 자체적으로 업데이트 (예: 초기화된 값보다 더 나은 은닉 상태 획득) 할 수 있습니다.
- 그라디언트 클리핑은 그라디언트 폭발을 방지하지만 사라지는 그라디언트를 수정할 수는 없습니다.
- 원-핫 인코딩은 각 객체에 대해 다른 임베딩을 선택하는 것과 동일하다는 것을 보여줍니다.
- 하이퍼파라미터 (예: Epoch 수, 은닉 유닛 수, 미니배치의 시간 스텝 수, 학습률) 를 조정하여 난처한 상황을 개선합니다.
- 얼마나 낮게 갈 수 있니?
- 원-핫 인코딩을 학습 가능한 임베딩으로 대체합니다.이로 인해 성능이 향상됩니까?
- H.G. Wells의 다른 책 (예: The War of the Worlds) 에서도 얼마나 잘 작동할까요?
- 가장 가능성이 높은 다음 문자를 선택하는 대신 샘플링을 사용하도록 예측 함수를 수정합니다.
- 어떻게 되나요?
- 예를 들어
$\alpha > 1$ 에 대해$q(x_t \mid x_{t-1}, \ldots, x_1) \propto P(x_t \mid x_{t-1}, \ldots, x_1)^\alpha$ 에서 샘플링하여 더 가능성이 높은 출력으로 모델을 편향시킵니다.
- 그래디언트를 클리핑하지 않고 이 섹션의 코드를 실행합니다.어떻게 되나요?
- 계산 그래프에서 은닉 상태를 분리하지 않도록 순차 분할을 변경합니다.실행 시간이 변경되나요?당혹감은 어때?
- 이 섹션에 사용된 활성화 함수를 ReLU로 바꾸고 이 섹션의 실험을 반복합니다.그래디언트 클리핑이 여전히 필요한가요?왜요?
:begin_tab:mxnet
Discussions
:end_tab:
:begin_tab:pytorch
Discussions
:end_tab:
:begin_tab:tensorflow
Discussions
:end_tab: