Je lis un chef-d'œuvre, ** "Deep Learning from Zero 2" **. Cette fois, c'est un mémo du chapitre 8. Pour exécuter le code, téléchargez le code complet depuis Github et utilisez le notebook jupyter dans ch08.
2.Attention Vous trouverez ci-dessous un diagramme schématique de Attention dans le modèle Seq2seq.
Mettez ** Attention ** entre ** LSTM ** et ** Affine ** à tout moment du ** Decoder **. Ensuite, entrez ** hs **, qui est une collection de ** vecteurs cachés (hs0 à hs4) ** à chaque fois de ** Encoder **, dans toutes les Attentions.
Dans chaque Attention, la similitude (produit vectoriel) entre l'entrée de LSTM et hs est calculée, et l'importance des vecteurs cachés (hs0 à hs4) est calculée par une distribution de probabilité. Ensuite, le vecteur caché est pondéré par sa distribution de probabilité, et le composite est envoyé à Affine.
Il s'agit d'un diagramme schématique à l'intérieur de la couche Attention et de l'image d'opération de la matrice (lorsque la taille du lot N = 1) y est effectuée.
Tout d'abord, la partie ** Attention_weight **. Trouvez le ** produit ** des ** vecteurs hs0 à hs4 ** du codeur et du ** vecteur h ** du décodeur, et ajoutez-les dans la ** direction de la colonne (axe = 2) **, respectivement, pour obtenir les deux vecteurs. Plus la ** similarité de ** est élevée, plus la **. Passer Softmax à travers ce nombre donne un ** vecteur a ** qui représente la ** pondération des vecteurs hs0 à hs4 **.
Vient ensuite la partie ** Weight_Sum **. Le ** produit ** des ** vecteurs hs0 à hs4 ** et ** vecteur a ** est calculé, et ** vecteur C ** ajouté par ** direction de ligne (axe = 1) ** est ** attention ( Les informations à (voir) sont pondérées et combinées (ajoutées) pour former un vecteur **. En envoyant cela à Affine, Affine peut obtenir ** des informations côté Encodeur auxquelles il faut se référer à ce moment-là en plus des informations du LSTM ** conventionnel.
À propos, lors du calcul de np.sum (x, axis =?) Of x = (N, T, H), sum (addition dans le sens du batch) de sorte que l'axe N soit effacé lorsque ** axis = 0. Ensuite, lorsque ** axis = 1, additionnez (addition dans le sens des lignes) pour effacer l'axe T **, et lorsque ** axis = 2, effacez l'axe H ** (addition dans le sens des colonnes). Faire.
Jetons un coup d'œil au code de Attention_weight.
class AttentionWeight:
def __init__(self):
self.params, self.grads = [], []
self.softmax = Softmax()
self.cache = None
def forward(self, hs, h):
N, T, H = hs.shape
#Puisque hr est utilisé pour la diffusion, seule la dimension est ajustée sans se répéter.
hr = h.reshape(N, 1, H) #.repeat(T, axis=1)
t = hs * hr #Prenez le produit des vecteurs
s = np.sum(t, axis=2) #Ajout dans le sens des colonnes
a = self.softmax.forward(s) #Calculez la distribution de probabilité a qui représente l'importance de chaque vecteur caché
self.cache = (hs, hr)
return a
def backward(self, da):
hs, hr = self.cache
N, T, H = hs.shape
ds = self.softmax.backward(da)
dt = ds.reshape(N, T, 1).repeat(H, axis=2) # (N, T, H)Conversion en
dhs = dt * hr
dhr = dt * hs
dh = np.sum(dhr, axis=1)
return dhs, dh
Pour la propagation aller, t = hs * hr est calculé par diffusion, donc hr n'est pas répété. Ensuite, jetons un coup d'œil à Weight_sum.
class WeightSum:
def __init__(self):
self.params, self.grads = [], []
self.cache = None
def forward(self, hs, a):
N, T, H = hs.shape
#Comme ar est utilisé pour la diffusion, seules les dimensions sont ajustées sans répétition.
ar = a.reshape(N, T, 1) #.repeat(T, axis=1)
t = hs * ar #Prenez le produit des vecteurs
c = np.sum(t, axis=1) #Ajout de direction de ligne
self.cache = (hs, ar)
return c
def backward(self, dc):
hs, ar = self.cache
N, T, H = hs.shape
dt = dc.reshape(N, 1, H).repeat(T, axis=1)
dar = dt * hs
dhs = dt * ar
da = np.sum(dar, axis=2)
return dhs, da
Pour la propagation vers l'avant, t = hs * ar est calculé par diffusion, donc ar n'est pas répété. Mettez ces deux classes ensemble pour former une «classe Attention».
class Attention:
def __init__(self):
self.params, self.grads = [], []
self.attention_weight_layer = AttentionWeight()
self.weight_sum_layer = WeightSum()
self.attention_weight = None
def forward(self, hs, h):
a = self.attention_weight_layer.forward(hs, h)
out = self.weight_sum_layer.forward(hs, a)
self.attention_weight = a
return out
def backward(self, dout):
dhs0, da = self.weight_sum_layer.backward(dout)
dhs1, dh = self.attention_weight_layer.backward(da)
dhs = dhs0 + dhs1
return dhs, d
De plus, il est résumé dans class TimeAttention
pour correspondre à l'heure.
class TimeAttention:
def __init__(self):
self.params, self.grads = [], []
self.layers = None
self.attention_weights = None
def forward(self, hs_enc, hs_dec):
N, T, H = hs_dec.shape
out = np.empty_like(hs_dec)
self.layers = []
self.attention_weights = []
for t in range(T):
layer = Attention()
out[:, t, :] = layer.forward(hs_enc, hs_dec[:,t,:])
self.layers.append(layer)
self.attention_weights.append(layer.attention_weight)
return out
def backward(self, dout):
N, T, H = dout.shape
dhs_enc = 0
dhs_dec = np.empty_like(dout)
for t in range(T):
layer = self.layers[t]
dhs, dh = layer.backward(dout[:, t, :])
dhs_enc += dhs
dhs_dec[:,t,:] = dh
return dhs_enc, dhs_dec
Maintenant, exécutons l'exemple de code (conversion de format de date) ** train.py ** de seq2seq en utilisant ce mécanisme Attention.
import sys
sys.path.append('..')
import numpy as np
import matplotlib.pyplot as plt
from dataset import sequence
from common.optimizer import Adam
from common.trainer import Trainer
from common.util import eval_seq2seq
from attention_seq2seq import AttentionSeq2seq
from ch07.seq2seq import Seq2seq
#Lire les données
(x_train, t_train), (x_test, t_test) = sequence.load_data('date.txt')
char_to_id, id_to_char = sequence.get_vocab()
#Inverser l'instruction d'entrée
x_train, x_test = x_train[:, ::-1], x_test[:, ::-1]
#Paramètres des hyper paramètres
vocab_size = len(char_to_id)
wordvec_size = 16
hidden_size = 256
batch_size = 128
max_epoch = 10
max_grad = 5.0
model = AttentionSeq2seq(vocab_size, wordvec_size, hidden_size)
# model = Seq2seq(vocab_size, wordvec_size, hidden_size)
# model = PeekySeq2seq(vocab_size, wordvec_size, hidden_size)
optimizer = Adam()
trainer = Trainer(model, optimizer)
acc_list = []
for epoch in range(max_epoch):
trainer.fit(x_train, t_train, max_epoch=1,
batch_size=batch_size, max_grad=max_grad)
correct_num = 0
for i in range(len(x_test)):
question, correct = x_test[[i]], t_test[[i]]
verbose = i < 10
correct_num += eval_seq2seq(model, question, correct,
id_to_char, verbose, is_reverse=True)
acc = float(correct_num) / len(x_test)
acc_list.append(acc)
print('val acc %.3f%%' % (acc * 100))
model.save_params()
#Dessiner un graphique
x = np.arange(len(acc_list))
plt.plot(x, acc_list, marker='o')
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.ylim(-0.05, 1.05)
plt.show()
Après 2 époques, la précision est de 100%. Comme prévu, c'est le pouvoir de l'attention.
Recommended Posts