.. _sec_rnn_scratch: Yinelemeli Sinir Ağlarının Sıfırdan Uygulanması =============================================== Bu bölümde, :numref:`sec_rnn` içindeki tanımlamalarımıza göre, karakter düzeyinde bir dil modeli için sıfırdan bir RNN uygulayacağız. Bu tarz bir model H. G. Wells'in *Zaman Makinesi* ile eğitilecek. Daha önce olduğu gibi, önce :numref:`sec_language_model` içinde tanıtılan veri kümesini okuyarak başlıyoruz. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %matplotlib inline import math from d2l import mxnet as d2l from mxnet import autograd, gluon, np, npx npx.set_np() batch_size, num_steps = 32, 35 train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %matplotlib inline import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l batch_size, num_steps = 32, 35 train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %matplotlib inline import math import tensorflow as tf from d2l import tensorflow as d2l batch_size, num_steps = 32, 35 train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps) train_random_iter, vocab_random_iter = d2l.load_data_time_machine( batch_size, num_steps, use_random_iter=True) .. raw:: html
.. raw:: html
Bire Bir Kodlama ---------------- Her andıcın ``train_iter``'te sayısal bir indeks olarak temsil edildiğini hatırlayın. Bu indeksleri doğrudan sinir ağına beslemek öğrenmeyi zorlaştırabilir. Genellikle her andıcı daha açıklayıcı bir öznitelik vektörü olarak temsil ediyoruz. En kolay gösterim, :numref:`subsec_classification-problem` içinde tanıtılan *bire bir kodlama*\ dır (one-hot coding). Özetle, her bir indeksi farklı bir birim vektörüne eşleriz: Kelime dağarcığındaki farklı andıçların sayısının :math:`N` (``len(vocab)``) olduğunu ve andıç indekslerinin :math:`0` ile :math:`N-1` arasında değiştiğini varsayalım. Bir andıç indeksi :math:`i` tamsayısı ise, o zaman :math:`N` uzunluğunda tümü 0 olan bir vektör oluştururuz ve :math:`i` konumundaki elemanı 1'e ayarlarız. Bu vektör, orijinal andıcın bire bir kodlama vektörüdür. İndeksleri 0 ve 2 olan bire bir kodlama vektörleri aşağıda gösterilmiştir. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python npx.one_hot(np.array([0, 2]), len(vocab)) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python F.one_hot(torch.tensor([0, 2]), len(vocab)) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python tf.one_hot(tf.constant([0, 2]), len(vocab)) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Her seferinde örnek aldığımız minigrubun şeklidir (grup boyutu, zaman adımlarının sayısı). ``one_hot`` işlevi, böyle bir minigrubu, son boyutun kelime dağarcığı uzunluğuna eşit olduğu üç boyutlu bir tensöre dönüştürür (``len(vocab)``). Girdiyi sıklıkla deviririz (transpose), böylece (zaman adımlarının sayısı, grup boyutu, kelime dağarcığı uzunluğu) şeklinde bir çıktı elde edeceğiz. Bu, bir minigrubun gizli durumlarını zaman adımlarıyla güncellerken en dıştaki boyutta daha rahat döngü yapmamızı sağlayacaktır. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = np.arange(10).reshape((2, 5)) npx.one_hot(X.T, 28).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (5, 2, 28) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = torch.arange(10).reshape((2, 5)) F.one_hot(X.T, 28).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output torch.Size([5, 2, 28]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = tf.reshape(tf.range(10), (2, 5)) tf.one_hot(tf.transpose(X), 28).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output TensorShape([5, 2, 28]) .. raw:: html
.. raw:: html
Model Parametrelerini İlkleme ----------------------------- Ardından, RNN modeli için model parametrelerini ilkliyoruz. Gizli birimlerin sayısı ``num_hiddens`` ayarlanabilir bir hiper parametredir. Dil modellerini eğitirken, girdiler ve çıktılar aynı kelime dağarcığındandır. Bu nedenle, kelime dağarcığı uzunluğuna eşit olan aynı boyuta sahiptirler. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) # Gizli katman parametreleri W_xh = normal((num_inputs, num_hiddens)) W_hh = normal((num_hiddens, num_hiddens)) b_h = np.zeros(num_hiddens, ctx=device) # Çıktı katmanı parametreleri W_hq = normal((num_hiddens, num_outputs)) b_q = np.zeros(num_outputs, ctx=device) # Gradyanları iliştir params = [W_xh, W_hh, b_h, W_hq, b_q] for param in params: param.attach_grad() return params .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 # Gizli katman parametreleri W_xh = normal((num_inputs, num_hiddens)) W_hh = normal((num_hiddens, num_hiddens)) b_h = torch.zeros(num_hiddens, device=device) # Çıktı katmanı parametreleri W_hq = normal((num_hiddens, num_outputs)) b_q = torch.zeros(num_outputs, device=device) # Gradyanları iliştir params = [W_xh, W_hh, b_h, W_hq, b_q] for param in params: param.requires_grad_(True) return params .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def get_params(vocab_size, num_hiddens): num_inputs = num_outputs = vocab_size def normal(shape): return tf.random.normal(shape=shape,stddev=0.01,mean=0,dtype=tf.float32) # Gizli katman parametreleri 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(tf.zeros(num_hiddens), dtype=tf.float32) # Çıktı katmanı parametreleri W_hq = tf.Variable(normal((num_hiddens, num_outputs)), dtype=tf.float32) b_q = tf.Variable(tf.zeros(num_outputs), dtype=tf.float32) params = [W_xh, W_hh, b_h, W_hq, b_q] return params .. raw:: html
.. raw:: html
RNN Modeli ---------- Bir RNN modeli tanımlamak için, ilk olarak ilkleme esnasında gizli durumu döndürmek için bir ``init_rnn_state`` işlevi gerekir. Şekli (grup boyutu, gizli birimlerin sayısı) olan 0 ile doldurulmuş bir tensör döndürür. Çokuzlu (tuple) kullanmak, daha sonraki bölümlerde karşılaşacağımız gibi gizli durumun birden çok değişken içerdiği halleri işlemeyi kolaylaştırır. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def init_rnn_state(batch_size, num_hiddens, device): return (np.zeros((batch_size, num_hiddens), ctx=device), ) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def init_rnn_state(batch_size, num_hiddens, device): return (torch.zeros((batch_size, num_hiddens), device=device), ) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def init_rnn_state(batch_size, num_hiddens): return (tf.zeros((batch_size, num_hiddens)), ) .. raw:: html
.. raw:: html
Aşağıdaki ``rnn`` işlevi, gizli durumu ve çıktıyı bir zaman adımında nasıl hesaplayacağınızı tanımlar. RNN modelinin ``inputs`` değişkeninin en dış boyutunda döngü yaptığını, böylece minigrubun gizli durumları ``H``'nin her zaman adımında güncellediğini unutmayın. Ayrıca, burada etkinleştirme fonksiyonu olarak :math:`\tanh` işlevi kullanılır. :numref:`sec_mlp` içinde açıklandığı gibi, :math:`\tanh` işlevinin ortalama değeri, elemanlar gerçel sayılar üzerinde eşit olarak dağıtıldığında 0'dır. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def rnn(inputs, state, params): # `inputs`'un şekli: (`num_steps`, `batch_size`, `vocab_size`) W_xh, W_hh, b_h, W_hq, b_q = params H, = state outputs = [] # `X`'in şekli: (`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,) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def rnn(inputs, state, params): # `inputs`'un şekli: (`num_steps`, `batch_size`, `vocab_size`) W_xh, W_hh, b_h, W_hq, b_q = params H, = state outputs = [] # `X`'in şekli: (`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,) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def rnn(inputs, state, params): # `inputs`'un şekli: (`num_steps`, `batch_size`, `vocab_size`) W_xh, W_hh, b_h, W_hq, b_q = params H, = state outputs = [] # `X`'in şekli: (`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 tf.concat(outputs, axis=0), (H,) .. raw:: html
.. raw:: html
Gerekli tüm işlevler tanımlandıktan sonra, bu işlevleri sarmalamak ve sıfırdan uygulanan bir RNN modelinin parametreleri depolamak için bir sınıf oluşturuyoruz. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python class RNNModelScratch: #@save """Sıfırdan uygulanan bir RNN modeli.""" 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python class RNNModelScratch: #@save """Sıfırdan uygulanan bir RNN modeli.""" 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python class RNNModelScratch: #@save """Sıfırdan uygulanan bir RNN modeli.""" 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) .. raw:: html
.. raw:: html
Çıktıların doğru şekillere sahip olup olmadığını kontrol edelim, örn. gizli durumun boyutunun değişmeden kalmasını sağlamak için. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output ((10, 28), 1, (2, 512)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (torch.Size([10, 28]), 1, torch.Size([2, 512])) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python # 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (TensorShape([10, 28]), 1, TensorShape([2, 512])) .. raw:: html
.. raw:: html
Çıktı şekli (zaman sayısı :math:`\times` grup boyutu, kelime dağarcığı uzunluğu) iken, gizli durum şeklinin aynı kaldığını, yani (grup boyutu, gizli birimlerin sayısı) olduğunu görebiliriz. Tahmin ------ Önce kullanıcı tarafından sağlanan ``prefix``'i (önek), ki birkaç karakter içeren bir dizgidir, takip eden yeni karakterler oluşturmak için tahmin işlevi tanımlayalım. ``prefix``'teki bu başlangıç karakterleri arasında döngü yaparken, herhangi bir çıktı oluşturmadan gizli durumu bir sonraki adımına geçirmeye devam ediyoruz. Buna, modelin kendisini güncellediği (örn. gizli durumu güncellediği) ancak tahminlerde bulunmadığı *ısınma* dönemi denir. Isınma döneminden sonra, gizli durum genellikle başlangıçtaki ilkleme değerinden daha iyidir. Artık tahmin edilen karakterleri oluşturup yayarız. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def predict_ch8(prefix, num_preds, net, vocab, device): #@save """`prefix`'i takip eden yeni karakterler üret.""" state = net.begin_state(batch_size=1, ctx=device) outputs = [vocab[prefix[0]]] get_input = lambda: np.array([outputs[-1]], ctx=device).reshape((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]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def predict_ch8(prefix, num_preds, net, vocab, device): #@save """`prefix`'i takip eden yeni karakterler üret.""" state = net.begin_state(batch_size=1, device=device) outputs = [vocab[prefix[0]]] get_input = lambda: torch.tensor([outputs[-1]], device=device).reshape((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]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def predict_ch8(prefix, num_preds, net, vocab): #@save """`prefix`'i takip eden yeni karakterler üret.""" state = net.begin_state(batch_size=1, dtype=tf.float32) outputs = [vocab[prefix[0]]] get_input = lambda: tf.reshape(tf.constant([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]) .. raw:: html
.. raw:: html
Şimdi ``predict_ch8`` işlevini test edebiliriz. Öneki ``time traveller`` olarak belirtiyoruz ve 10 ek karakter üretiyoruz. Ağı eğitmediğimiz göz önüne alındığında, saçma tahminler üretecektir. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python predict_ch8('time traveller ', 10, net, vocab, d2l.try_gpu()) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 'time traveller iiiiiiiiii' .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python predict_ch8('time traveller ', 10, net, vocab, d2l.try_gpu()) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 'time traveller lfbrpfbehs' .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python predict_ch8('time traveller ', 10, net, vocab) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 'time traveller wqlmuydh ' .. raw:: html
.. raw:: html
Gradyan Kırpma -------------- :math:`T` uzunluğunda bir dizi için, bir yinelemede bu :math:`T` zaman adımlarının üzerindeki gradyanları hesaplarız, bu da geri yayma sırasında :math:`\mathcal{O}(T)` uzunluğunda bir matris çarpımları zincirine neden olur. :numref:\`sec\_numerical\_stability içinde belirtildiği gibi, bu da sayısal kararsızlığa neden olabilir, örneğin :math:`T` büyük olduğunda gradyanlar patlayabilir veya kaybolabilir. Bu nedenle, RNN modelleri genellikle eğitimi kararlı tutmak için ekstra yardıma ihtiyaç duyar. Genel olarak, bir eniyileme problemini çözerken, model parametresi için güncelleme adımları atıyoruz, mesela :math:`\mathbf{x}` vektör formunda, bir minigrup üzerinden negatif gradyan :math:`\mathbf{g}` yönünde. Örneğin, öğrenme oranı :math:`\eta > 0` ise, bir yinelemede :math:`\mathbf{x}`'i :math:`\mathbf{x} - \eta \mathbf{g}` olarak güncelleriz. :math:`f` amaç fonksiyonunun iyi davrandığını varsayalım, diyelim ki, :math:`L` sabiti ile *Lipschitz sürekli* olsun. Yani, herhangi bir :math:`\mathbf{x}` ve :math:`\mathbf{y}` için: .. math:: |f(\mathbf{x}) - f(\mathbf{y})| \leq L \|\mathbf{x} - \mathbf{y}\|. Bu durumda, parametre vektörünü :math:`\eta \mathbf{g}` ile güncellediğimizi varsayarsak, o zaman .. math:: |f(\mathbf{x}) - f(\mathbf{x} - \eta\mathbf{g})| \leq L \eta\|\mathbf{g}\|, demektir ki :math:`L \eta \|\mathbf{g}\|`'den daha büyük bir değişiklik gözlemlemeyeceğiz. Bu hem bir lanet hem de bir nimettir. Lanet tarafında, ilerleme kaydetme hızını sınırlar; oysa nimet tarafında, yanlış yönde hareket edersek işlerin ne ölçüde ters gidebileceğini sınırlar. Bazen gradyanlar oldukça büyük olabilir ve eniyileme algoritması yakınsamada başarısız olabilir. Öğrenme hızı :math:`\eta`'yı azaltarak bunu ele alabiliriz. Ama ya sadece nadiren büyük gradyanlar alırsak? Bu durumda böyle bir yaklaşım tamamen yersiz görünebilir. Popüler bir alternatif, :math:`\mathbf{g}` gradyanını belirli bir yarıçaptaki bir küreye geri yansıtmaktır, diyelim ki :math:`\theta` için: .. math:: \mathbf{g} \leftarrow \min\left(1, \frac{\theta}{\|\mathbf{g}\|}\right) \mathbf{g}. Bunu yaparak, gradyanın normunun :math:`\theta`'yı asla geçmediğini ve güncellenen gradyanın :math:`\mathbf{g}`'nin orijinal yönüyle tamamen hizalandığını biliyoruz. Ayrıca, herhangi bir minigrubun (ve içindeki herhangi bir örneklemin) parametre vektörü üzerinde uygulayabileceği etkiyi sınırlaması gibi arzu edilen yan etkiye sahiptir. Bu, modele belirli bir derecede gürbüzlük kazandırır. Gradyan kırpma gradyan patlaması için hızlı bir düzeltme sağlar. Sorunu tamamen çözmese de, onu hafifletmede kullanılan birçok teknikten biridir. Aşağıda, sıfırdan uygulanan veya üst düzey API'ler tarafından oluşturulan bir modelin gradyanlarını kırpmak için bir işlev tanımlıyoruz. Ayrıca, tüm model parametrelerinin üzerinden gradyan normunu hesapladığımızı unutmayın. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def grad_clipping(net, theta): #@save """Gradyanı kırp.""" 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def grad_clipping(net, theta): #@save """Gradyanı kırp.""" 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def grad_clipping(grads, theta): #@save """Gradyanı kırp.""" 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 .. raw:: html
.. raw:: html
Eğitim ------ Modeli eğitmeden önce, modeli bir dönemde eğitmek için bir işlev tanımlayalım. :numref:`sec_softmax_scratch` içindeki model eğitimimizden üç yerde farklılık gösterir: 1. Dizili veriler için farklı örnekleme yöntemleri (rastgele örnekleme ve sıralı bölümleme), gizli durumların ilklenmesinde farklılıklara neden olacaktır. 2. Model parametrelerini güncellemeden önce gradyanları kırpıyoruz. Bu, eğitim süreci sırasında bir noktada gradyanlar patladığında bile modelin ıraksamamasını sağlar. 3. Modeli değerlendirmek için şaşkınlığı kullanıyoruz. :numref:`subsec_perplexity` içinde tartışıldığı gibi, bu farklı uzunluktaki dizilerin karşılaştırılabilir olmasını sağlar. Özellikle, sıralı bölümleme kullanıldığında, gizli durumu yalnızca her dönemin başında ilkleriz. Sonraki minigruptaki :math:`i`. altdizi örneği şimdiki :math:`i`. altdizi örneğine bitişik olduğundan, şimdiki minigrup sonundaki gizli durum sonraki minigrup başında gizli durumu ilklemek için kullanılır. Bu şekilde, gizli durumda saklanan dizinin tarihsel bilgileri bir dönem içinde bitişik altdizilere aktarılabilir. Ancak, herhangi bir noktada gizli durumun hesaplanması, aynı dönemdeki tüm önceki minigruplara bağlıdır, ki bu da gradyan hesaplamasını zorlaştırır. Hesaplama maliyetini azaltmak için, herhangi bir minigrup işleminden önce gradyanı koparıp ayırırız, böylece gizli durumun gradyan hesaplaması her zaman minigrup içindeki zaman adımlarıyla sınırlı kalır. Rastgele örneklemeyi kullanırken, her örnek rastgele bir konumla örneklendiğinden, her yineleme için gizli durumu yeniden ilklememiz gerekir. :numref:`sec_softmax_scratch` içindeki ``train_epoch_ch3`` işlevi gibi, ``updater`` da model parametrelerini güncellemek için genel bir işlevdir. Sıfırdan uygulanan ``d2l.sgd`` işlevi veya derin bir öğrenme çerçevesindeki yerleşik eniyileme işlevi olabilir. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): """Bir modeli bir dönem içinde eğitin (Bölüm 8'de tanımlanmıştır).""" state, timer = None, d2l.Timer() metric = d2l.Accumulator(2) # Eğitim kaybı toplamı, andıç sayısı for X, Y in train_iter: if state is None or use_random_iter: # İlk yineleme olduğunda veya rastgele örnekleme kullanıldığında # `state`'i (durumu) ilklet 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) # `mean` işlevi çağrıldığından dolayı metric.add(l * d2l.size(y), d2l.size(y)) return math.exp(metric[0] / metric[1]), metric[1] / timer.stop() .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): """Bir modeli bir dönem içinde eğitin (Bölüm 8'de tanımlanmıştır).""" state, timer = None, d2l.Timer() metric = d2l.Accumulator(2) # Eğitim kaybı toplamı, andıç sayısı for X, Y in train_iter: if state is None or use_random_iter: # İlk yineleme olduğunda veya rastgele örnekleme kullanıldığında # `state`'i (durumu) ilklet state = net.begin_state(batch_size=X.shape[0], device=device) else: if isinstance(net, nn.Module) and not isinstance(state, tuple): # `state` (durum) `nn.GRU` için bir tensördür state.detach_() else: # `state`, `nn.LSTM` ve özel sıfırdan uygulamamız için # bir tensör grubudur 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) # `mean` işlevi çağrıldığından dolayı updater(batch_size=1) metric.add(l * y.numel(), y.numel()) return math.exp(metric[0] / metric[1]), metric[1] / timer.stop() .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def train_epoch_ch8(net, train_iter, loss, updater, use_random_iter): """Bir modeli bir dönem içinde eğitin (Bölüm 8'de tanımlanmıştır).""" state, timer = None, d2l.Timer() metric = d2l.Accumulator(2) # Eğitim kaybı toplamı, andıç sayısı for X, Y in train_iter: if state is None or use_random_iter: # İlk yineleme olduğunda veya rastgele örnekleme kullanıldığında # `state`'i (durumu) ilklet 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 = tf.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)) # Varsayılan Keras kaybı, bir minigrup içindeki ortalama # kaybı döndürür # 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() .. raw:: html
.. raw:: html
Eğitim fonksiyonu sıfırdan veya üst düzey API'ler kullanılarak uygulanan bir RNN modelini destekler. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def train_ch8(net, train_iter, vocab, lr, num_epochs, device, #@save use_random_iter=False): """Bir modeli eğitin (Bölüm 8'de tanımlanmıştır).""" loss = gluon.loss.SoftmaxCrossEntropyLoss() animator = d2l.Animator(xlabel='epoch', ylabel='perplexity', legend=['train'], xlim=[10, num_epochs]) # İlkleyin 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) # Eğit ve tahminle 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')) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def train_ch8(net, train_iter, vocab, lr, num_epochs, device, use_random_iter=False): """Bir modeli eğitin (Bölüm 8'de tanımlanmıştır).""" loss = nn.CrossEntropyLoss() animator = d2l.Animator(xlabel='epoch', ylabel='perplexity', legend=['train'], xlim=[10, num_epochs]) # İlkleyin 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) # Eğit ve tahminle 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')) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def train_ch8(net, train_iter, vocab, lr, num_epochs, strategy, use_random_iter=False): """Bir modeli eğitin (Bölüm 8'de tanımlanmıştır).""" 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) # Eğit ve tahminle 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')) .. raw:: html
.. raw:: html
Şimdi RNN modelini eğitebiliriz. Veri kümesinde yalnızca 10000 andıç kullandığımızdan, modelin daha iyi yakınsaması için daha fazla döneme ihtiyacı var. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python num_epochs, lr = 500, 1 train_ch8(net, train_iter, vocab, lr, num_epochs, d2l.try_gpu()) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output perplexity 1.1, 34268.1 tokens/sec on gpu(0) time traveller for so it will be convenient to speak of himwas e travelleryou can show black is white by argument said filby .. figure:: output_rnn-scratch_546c4d_159_1.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python num_epochs, lr = 500, 1 train_ch8(net, train_iter, vocab, lr, num_epochs, d2l.try_gpu()) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output perplexity 1.0, 68806.3 tokens/sec on cuda:0 time travelleryou can show black is white by argument said filby travelleryou can show black is white by argument said filby .. figure:: output_rnn-scratch_546c4d_162_1.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python num_epochs, lr = 500, 1 train_ch8(net, train_iter, vocab, lr, num_epochs, strategy) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output perplexity 1.1, 10524.6 tokens/sec on /GPU:0 time travelleryou can show black is white by argument said filby travellerybut af when twon a matte mither has a sove time u .. figure:: output_rnn-scratch_546c4d_165_1.svg .. raw:: html
.. raw:: html
Son olarak, rastgele örnekleme yöntemini kullanmanın sonuçlarını kontrol edelim. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output perplexity 1.4, 34394.2 tokens/sec on gpu(0) time travellerit s against reason said filbywas a guten flomm ha travellerit s against reason said filbywas a guten flomm ha .. figure:: output_rnn-scratch_546c4d_171_1.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output perplexity 1.4, 52954.3 tokens/sec on cuda:0 time travellerit s against reason said filbywhar gethematical pi travellerit s against reason said filbywhar gethematical pi .. figure:: output_rnn-scratch_546c4d_174_1.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output perplexity 1.3, 9260.5 tokens/sec on /GPU:0 time travellerit s against reason said filbywhre time traveller traveller held in his hand was a glitteringmetallic framewo .. figure:: output_rnn-scratch_546c4d_177_1.svg .. raw:: html
.. raw:: html
Yukarıdaki RNN modelini sıfırdan uygulamak öğretici olsa da, pek de uygun değildir. Bir sonraki bölümde, RNN modelinin nasıl geliştirileceğini göreceğiz, örneğin nasıl daha kolay uygulanmasını ve daha hızlı çalışmasını sağlarız. Özet ---- - Kullanıcı tarafından sağlanan metin önekini takip eden metin üretmek için RNN tabanlı karakter düzeyinde bir dil modeli eğitebiliriz. - Basit bir RNN dil modeli girdi kodlama, RNN modelleme ve çıktı üretme içerir. - RNN modellerinin eğitim için durum ilklenmesi gerekir, ancak rasgele örnekleme ve sıralı bölümleme farklı yollar kullanır. - Sıralı bölümleme kullanırken, hesaplama maliyetini azaltmak için gradyanı koparıp ayırmamız gerekir. - Isınma süresi, herhangi bir tahmin yapmadan önce bir modelin kendisini güncellemesine (örneğin, ilkleme değerinden daha iyi bir gizli durum elde etmesine) olanak tanır. - Gradyan kırpma gradyan patlamasını önler, ancak kaybolan gradyanları düzeltemez. Alıştırmalar ------------ 1. Bire bir kodlamanın, her nesne için farklı bir gömme seçmeye eşdeğer olduğunu gösterin. 2. Şaşkınlığı iyileştirmek için hiper parametreleri (örn. dönemlerin sayısı, gizli birimlerin sayısı, bir minigruptaki zaman adımlarının sayısı ve öğrenme oranı) ayarlayın. - Ne kadar aşağıya düşebilirsiniz? - Bire bir kodlamayı öğrenilebilir gömmelerle değiştirin. Bu daha iyi bir performansa yol açar mı? - H. G. Wells'in, örn. `*Dünyalar Savaşı* `__ gibi, diğer kitaplarıyla ne kadar iyi çalışacaktır? 3. En olası sonraki karakteri seçmek yerine örnekleme kullanarak tahmin işlevini değiştirin. - Ne olur? - Modeli, örneğin :math:`\alpha > 1` için :math:`q(x_t \mid x_{t-1}, \ldots, x_1) \propto P(x_t \mid x_{t-1}, \ldots, x_1)^\alpha`'ten örnekleme yaparak daha olası çıktılara doğru yanlı yapın. 4. Gradyan kırpmadan bu bölümdeki kodu çalıştırın. Ne olur? 5. Sıralı bölümlemeyi gizli durumları hesaplama çizgesinden ayırmayacak şekilde değiştirin. Çalışma süresi değişiyor mu? Şaşkınlığa ne olur? 6. Bu bölümde kullanılan etkinleştirme işlevini ReLU ile değiştirin ve bu bölümdeki deneyleri tekrarlayın. Hala gradyan kırpmaya ihtiyacımız var mı? Neden? .. raw:: html
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html