J'ai rassemblé des articles de presse sur le virus corona et j'aimerais l'utiliser pour contester la génération de phrases.
J'ai commencé à étudier le Deep Learning en utilisant Pytorch à la maison, alors laissez-moi le sortir. J'étudie toujours, alors comprenez qu'il peut y avoir des erreurs ...
import torch
import torch.nn as nn
import torch.optim as optimizers
from torch.utils.data import DataLoader
import torch.nn.functional as F
from tensorflow.keras.preprocessing.sequence import pad_sequences
from sklearn.utils import shuffle
import random
from tqdm import tqdm_notebook as tqdm
import pickle
import matplotlib.pyplot as plt
import logging
import numpy as np
Lit le texte pré-traité et séparé des nouvelles sur le virus corona gratté. Le prétraitement n'est pas un gros problème.
Les nouvelles sur le virus corona de la semaine dernière ont été recueillies sur Yahoo News, alors je les ai obtenues à partir de là. Je l'ai gratté sans remarquer que c'était pendant une semaine, donc la quantité de données que j'ai réellement obtenue était trop petite et elle était foirée. Je suis trop inquiet si je peux bien apprendre avec ça, mais je vais l'essayer. ..
data_news = pickle.load(open("Destination/corona_wakati.pickle", "rb"))
Les données utilisées sont comme ça
data_news[0]
['patient', «Ya», 'Les travailleurs du domaine de la santé', 'La', 'Mais', 'Nouveau coronavirus', 'À', 'infection', 'Shi', «Ta», 'chose', 'Mais', 'Révélé', 'Shi', «Ta», «Ikuno-ku, ville d'Osaka», 'de', '「', «Nami», 'Ha', «Ya», 'Réhabilitation', 'hôpital', '」', 'sur', '、', «Préfecture d'Osaka», 'Ha', '0', 'Journée', 'Nuit', '、', 'plus loin', '0', 'Homme', 'de', 'infection', 'Mais', 'clair', 'À', 'Maintenant', «Ta», 'Quand', 'Présentation', 'Shi', «Ta», '。']
Puisque le mot lui-même ne peut pas être traité par le réseau neuronal, il est défini sur id. Puisqu'il est nécessaire de revenir d'id en mot lors de la génération effective d'une phrase, nous implémenterons également le décodeur.
Je voulais définir une classe à usage général, j'ai donc essayé de mettre des symboles au début et à la fin de la phrase, mais cette fois il y a toujours un signe de ponctuation à la fin de la phrase, donc ce n'est pas nécessaire.
class EncoderDecoder(object):
def __init__(self):
# word_to_dictionnaire d'identification
self.w2i = {}
# id_to_dictionnaire de mots
self.i2w = {}
#Mot reservé(Rembourrage,Le début de la phrase)
self.special_chars = ['<pad>', '<s>', '</s>', '<unk>']
self.bos_char = self.special_chars[1]
self.eos_char = self.special_chars[2]
self.oov_char = self.special_chars[3]
#Fonction à appeler
def __call__(self, sentence):
return self.transform(sentence)
#Créer un dictionnaire
def fit(self, sentences):
self._words = set()
#Créer un ensemble de mots inconnus
for sentence in sentences:
self._words.update(sentence)
#Décalez le mot réservé et secouez l'identifiant
self.w2i = {w: (i + len(self.special_chars))
for i, w in enumerate(self._words)}
#Ajouter des mots réservés au dictionnaire(<pad>:0, <s>:1, </s>:2, <unk>:3)
for i, w in enumerate(self.special_chars):
self.w2i[w] = i
# word_to_id en utilisant le dictionnaire id_to_Créer un dictionnaire de mots
self.i2w = {i: w for w, i in self.w2i.items()}
#Convertissez les données lues en identifiant à la fois
def transform(self, sentences, bos=False, eos=False):
output = []
#Ajouter des symboles de début et de fin si spécifié
for sentence in sentences:
if bos:
sentence = [self.bos_char] + sentence
if eos:
sentence = sentence + [self.eos_char]
output.append(self.encode(sentence))
return output
#Faire une phrase à la fois
def encode(self, sentence):
output = []
for w in sentence:
if w not in self.w2i:
idx = self.w2i[self.oov_char]
else:
idx = self.w2i[w]
output.append(idx)
return output
#Convertir phrase par phrase en liste de mots
def decode(self, sentence):
return [self.i2w[id] for id in sentence]
Utilisez la classe définie comme suit
en_de = EncoderDecoder()
en_de.fit(data_news)
data_news_id = en_de(data_news)
data_news_id[0]
[7142,
5775,
3686,
4630,
5891,
4003,
358,
3853,
4139,
4604,
4591,
5891,
2233,
4139,
4604,
5507,
7378,
2222,
6002,
3277,
5775,
7380,
7234,
5941,
5788,
2982,
4901,
3277,
6063,
5812,
4647,
2982,
1637,
6063,
6125,
7378,
3853,
5891,
1071,
358,
7273,
4604,
5835,
1328,
4139,
4604,
1226]
Le décodage revient à l'instruction d'origine
en_de.decode(data_news_id[0])
['patient', «Ya», 'Les travailleurs du domaine de la santé', 'La', 'Mais', 'Nouveau coronavirus', 'À', 'infection', 'Shi', «Ta», 'chose', 'Mais', 'Révélé', 'Shi', «Ta», «Ikuno-ku, ville d'Osaka», 'de', '「', «Nami», 'Ha', «Ya», 'Réhabilitation', 'hôpital', '」', 'sur', '、', «Préfecture d'Osaka», 'Ha', '0', 'Journée', 'Nuit', '、', 'plus loin', '0', 'Homme', 'de', 'infection', 'Mais', 'clair', 'À', 'Maintenant', «Ta», 'Quand', 'Présentation', 'Shi', «Ta», '。']
Dans cette tâche de génération de phrases, vous apprendrez comme indiqué dans l'image ci-dessous. Par conséquent, l'étiquette de réponse correcte est celle avec une donnée décalée de l'étiquette. Cette fois, je vais créer mon propre jeu de données spécifique à Pytorch et y créer les données et les étiquettes.
En outre, complétez avec 0 à la longueur spécifiée pour uniformiser la longueur des données, puis renvoyez-les sous forme de type Long Tensor.
À propos, la pad_sequence de keras est utilisée ici, mais pour le moment, une séquence similaire est préparée pour pytorch. Cependant, j'utilise le keras car la longueur à rembourrer et à associer ne peut pas être spécifiée pour le pyroch.
class MyDataset(torch.utils.data.Dataset):
def __init__(self, data, max_length=50):
self.data_num = len(data)
#Décaler les données de 1
self.x = [d[:-1] for d in data]
self.y = [d[1:] for d in data]
#Longueur à garnir et à assortir
self.max_length = max_length
def __len__(self):
return self.data_num
def __getitem__(self, idx):
out_data = self.x[idx]
out_label = self.y[idx]
#Pad pour correspondre à la longueur
out_data = pad_sequences([out_data], padding='post', maxlen=self.max_length)[0]
out_label = pad_sequences([out_label], padding='post', maxlen=self.max_length)[0]
#Convertir en type LongTensor
out_data = torch.LongTensor(out_data)
out_label = torch.LongTensor(out_label)
return out_data, out_label
dataset = MyDataset(data_news_id, max_length=50)
dataset[0]
(tensor([7142, 5775, 3686, 4630, 5891, 4003, 358, 3853, 4139, 4604, 4591, 5891,
2233, 4139, 4604, 5507, 7378, 2222, 6002, 3277, 5775, 7380, 7234, 5941,
5788, 2982, 4901, 3277, 6063, 5812, 4647, 2982, 1637, 6063, 6125, 7378,
3853, 5891, 1071, 358, 7273, 4604, 5835, 1328, 4139, 4604, 0, 0,
0, 0]),
tensor([5775, 3686, 4630, 5891, 4003, 358, 3853, 4139, 4604, 4591, 5891, 2233,
4139, 4604, 5507, 7378, 2222, 6002, 3277, 5775, 7380, 7234, 5941, 5788,
2982, 4901, 3277, 6063, 5812, 4647, 2982, 1637, 6063, 6125, 7378, 3853,
5891, 1071, 358, 7273, 4604, 5835, 1328, 4139, 4604, 1226, 0, 0,
0, 0]))
Enfin, le chargeur de données de Pytorch divise les données en lots. Si le nombre de données n'est pas divisible par la taille du lot, il sera différent du dernier nombre de lots, alors définissez drop_last sur True.
data_loader = DataLoader(dataset, batch_size=50, drop_last=True)
Vérifiez uniquement le premier lot
for (x, y) in data_loader:
print("x_dim: {}, y_dim: {}".format(x.shape, y.shape))
break
x_dim: torch.Size([50, 50]), y_dim: torch.Size([50, 50])
C'est difficile à comprendre car la taille du lot et le nombre de données d'une donnée sont identiques, mais dans le processus de génération de données jusqu'à présent, la dimension des données est (taille du lot, nombre de séries temporelles (lorsqu'on considère les phrases comme des séries chronologiques de mots), Bien qu'il soit (dimension d'entrée), Pytorch utilise par défaut (numéro de série chronologique, taille du lot, dimension d'entrée), donc batch_first = True doit être spécifié. A part ça, il n'y a rien de spécial à mentionner.
class RNNLM(nn.Module):
def __init__(self, embedding_dim, hidden_dim, vocab_size, batch_size=100, num_layers=1, device="cuda"):
super().__init__()
self.num_layers = num_layers
self.batch_size = batch_size
self.hidden_dim = hidden_dim
self.device = device
self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=0)
self.dropout1 = nn.Dropout(0.5)
self.lstm1 = nn.LSTM(embedding_dim, hidden_dim, batch_first=True, num_layers=self.num_layers)
self.dropout2 = nn.Dropout(0.5)
self.lstm2 = nn.LSTM(hidden_dim, hidden_dim, batch_first=True, num_layers=self.num_layers)
self.dropout3 = nn.Dropout(0.5)
self.lstm3 = nn.LSTM(hidden_dim, hidden_dim, batch_first=True, num_layers=self.num_layers)
self.linear = nn.Linear(hidden_dim, vocab_size)
nn.init.xavier_normal_(self.lstm1.weight_ih_l0)
nn.init.orthogonal_(self.lstm1.weight_hh_l0)
nn.init.xavier_normal_(self.lstm2.weight_ih_l0)
nn.init.orthogonal_(self.lstm2.weight_hh_l0)
nn.init.xavier_normal_(self.lstm3.weight_ih_l0)
nn.init.orthogonal_(self.lstm3.weight_hh_l0)
nn.init.xavier_normal_(self.linear.weight)
def init_hidden(self):
self.hidden_state = (torch.zeros(self.num_layers, self.batch_size, self.hidden_dim, device=self.device), torch.zeros(self.num_layers, self.batch_size, self.hidden_dim, device=self.device))
def forward(self, x):
x = self.embedding(x)
x = self.dropout1(x)
h, self.hidden_state = self.lstm1(x, self.hidden_state)
h = self.dropout2(h)
h, self.hidden_state = self.lstm2(h, self.hidden_state)
h = self.dropout3(h)
h, self.hidden_state = self.lstm3(h, self.hidden_state)
y = self.linear(h)
return y
Les paramètres sont tout à fait appropriés. Pardon. ..
Comme le nombre de données est petit, j'ai augmenté le nombre d'époques et je les ai beaucoup tournées pour les enregistrer fréquemment.
Dans d'autres tâches d'apprentissage en profondeur, il peut être possible d'évaluer si le surapprentissage ne se fait pas à l'aide des données d'évaluation, ou de mettre fin à l'apprentissage tôt en fonction de cela, mais de la probabilité comme cette fois. La tâche de sortie de la distribution est difficile à évaluer quantitativement. Cette fois, nous évaluons la progression de l'apprentissage et du modèle à l'aide d'un indice appelé perplexité. La perplexité est un peu compliquée lorsqu'elle est exprimée dans une formule mathématique, mais intuitivement, c'est l'inverse de la probabilité de sortie et représente le nombre de branches. En d'autres termes, dans le cas de la tâche de prédire le mot suivant comme cette fois, si la perplexité est de 2, la prédiction de mot est réduite à 2 choix.
if __name__ == '__main__':
np.random.seed(123)
torch.manual_seed(123)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
EMBEDDING_DIM = HIDDEN_DIM = 256
VOCAB_SIZE = len(en_de.i2w)
BATCH_SIZE=50
model = RNNLM(EMBEDDING_DIM, HIDDEN_DIM, VOCAB_SIZE, batch_size=BATCH_SIZE).to(device)
criterion = nn.CrossEntropyLoss(reduction='mean', ignore_index=0)
optimizer = optimizers.Adam(model.parameters(),
lr=0.001,
betas=(0.9, 0.999), amsgrad=True)
hist = {'train_loss': [], 'ppl':[]}
epochs = 1000
def compute_loss(label, pred):
return criterion(pred, label)
def train_step(x, t):
model.train()
model.init_hidden()
preds = model(x)
loss = compute_loss(t.view(-1),
preds.view(-1, preds.size(-1)))
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss, preds
for epoch in tqdm(range(epochs)):
print('-' * 20)
print('epoch: {}'.format(epoch+1))
train_loss = 0.
loss_count = 0
for (x, t) in data_loader:
x, t = x.to(device), t.to(device)
loss, _ = train_step(x, t)
train_loss += loss.item()
loss_count += 1
# perplexity
ppl = np.exp(train_loss / loss_count)
train_loss /= len(data_loader)
print('train_loss: {:.3f}, ppl: {:.3f}'.format(
train_loss, ppl
))
hist["train_loss"].append(train_loss)
hist["ppl"].append(ppl)
#Sauver toutes les 20 époques.
if epoch % 20 == 0:
model_name = "Destination/embedding{}_v{}.pt".format(EMBEDDING_DIM, epoch)
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': train_loss
}, model_name)
logging.info("Saving the checkpoint...")
torch.save(model.state_dict(), "Destination/embedding{}_v{}.model".format(EMBEDDING_DIM, epoch))
--------------------
epoch: 1
train_loss: 6.726, ppl: 833.451
--------------------
epoch: 2
train_loss: 6.073, ppl: 433.903
--------------------
epoch: 3
train_loss: 6.014, ppl: 409.209
--------------------
epoch: 4
train_loss: 5.904, ppl: 366.649
--------------------
epoch: 5
train_loss: 5.704, ppl: 300.046
・ ・ epoch: 995 train_loss: 0.078, ppl: 1.081 -------------------- epoch: 996 train_loss: 0.077, ppl: 1.081 -------------------- epoch: 997 train_loss: 0.076, ppl: 1.079 -------------------- epoch: 998 train_loss: 0.077, ppl: 1.080 -------------------- epoch: 999 train_loss: 0.077, ppl: 1.080 -------------------- epoch: 1000 train_loss: 0.077, ppl: 1.080
Voyons la transition de train_loss et de la perplexité
#Visualisation des erreurs
train_loss = hist['train_loss']
fig = plt.figure(figsize=(10, 5))
plt.plot(range(len(train_loss)), train_loss,
linewidth=1,
label='train_loss')
plt.xlabel('epochs')
plt.ylabel('train_loss')
plt.legend()
plt.savefig('output.jpg')
plt.show()
ppl = hist['ppl']
fig = plt.figure(figsize=(10, 5))
plt.plot(range(len(ppl)), ppl,
linewidth=1,
label='perplexity')
plt.xlabel('epochs')
plt.ylabel('perplexity')
plt.legend()
plt.show()
train_loss diminue régulièrement. La perplexité a fortement chuté au début et est restée faible au second semestre, atteignant finalement une valeur assez faible de 1,08. La seconde moitié n'a pas beaucoup changé, donc je n'ai peut-être pas dû étudier 1000 fois.
Ensuite, générons une phrase. Choisissez un mot de départ et laissez-le deviner le mot qui le suit. En donnant la sortie de probabilité comme poids au choix dans la partie np.random.choice, le mot sélectionné change à chaque fois qu'il est exécuté.
def generate_sentence(morphemes, model_path, embedding_dim, hidden_dim, vocab_size, batch_size=1):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = RNNLM(embedding_dim, hidden_dim, vocab_size, batch_size).to(device)
checkpoint = torch.load(model_path)
model.load_state_dict(checkpoint)
model.eval()
with torch.no_grad():
for morpheme in morphemes:
model.init_hidden()
sentence = [morpheme]
for _ in range(50):
input_index = en_de.encode([morpheme])
input_tensor = torch.tensor([input_index], device=device)
outputs = model(input_tensor)
probs = F.softmax(torch.squeeze(outputs))
p = probs.cpu().detach().numpy()
morpheme = en_de.i2w[np.random.choice(len(p), p=p)]
sentence.append(morpheme)
if morpheme in ["。", "<pad>"]:
break
print("".join(sentence))
print('-' * 50)
EMBEDDING_DIM = HIDDEN_DIM = 256
VOCAB_SIZE = len(en_de.i2w)
model_path ="Destination/embedding{}_v{}.model"
morphemes = ["premier ministre", "Gouverneur de Tokyo", "couronne", "Nouveau modèlecouronneウイルス", "Nouveau modèle", "Japon", "Tokyo", "Personne infectée", "Urgence"]
generate_sentence(morphemes, model_path, EMBEDDING_DIM, HIDDEN_DIM, VOCAB_SIZE)
La voix du changement de sprint du Premier ministre est trop pour élever l'année! " Dites au médecin et que le secrétaire général est parti
--------------------------------------------------
Le gouverneur de Tokyo l'a également demandé, mais même s'il n'y avait pas de lit, je pensais que c'était une décision de magasin.Que dois-je me dépêcher de la nouvelle infection à coronavirus, mesures liées à la vitesse d'infection du nouveau coronavirus LDP Ce nombre de pauses de pratique d'excuses est interdit Anxiété Passé la fièvre de Crémone au moins
--------------------------------------------------
Corona Avantages Psychologie Le gouvernement n'a pas l'échelle et les ressources nécessaires à l'échelle nationale, et il a envie de l'éviter tout en le disant. Sentiment d'évitement 0 minute d'augmentation Taux de participation Fourni de cette manière Traiter le «Nombre, Monohealth découvert à minuit Avertissement maximum, anxiété, effets secondaires De la nouvelle infection à coronavirus
--------------------------------------------------
Un nouveau commis au fonds contre le virus corona a également signalé et présenté ses excuses pour la chaleur et la réponse du passé Tokyo.
--------------------------------------------------
Il n'y a pas de magasins annoncés comme de nouvelles appellations, et plus que des résidents (expliquez ..). "On rapporte que le nombre de Yuriko va diminuer. Pourquoi le gouvernement demande-t-il une augmentation?"
--------------------------------------------------
Le pays voisin du Japon appelle à un match. Désinfection des tee-shirts. Explication de la marche à suivre.
--------------------------------------------------
De Tokyo ou au-dessus, de 0 yen à 0 aller-retour par évaluation de l'épidémie à 0 aller-retour. Nombre d'effondrements (le moment de l'installation de secours à domicile et le nombre de commentaires demandés ont également diminué. Il n'y a aucune perspective à minuit.
--------------------------------------------------
0 établissements médicaux aller-retour de personnes infectées, etc. LINE Tokyo Diminuer la déclaration de transition Un peu de pratique pour les clients Environ 0 établissements médicaux Confirmé à minuit dans le rapport précédent Tele-east Il n'y a pas de vitesse d'infection merveilleuse Administration Trump Il n'y a pas de Komei Party Magasins à réduire ou étrangers ... "Avec la nouvelle infection à coronavirus
--------------------------------------------------
Confirmer les situations d'urgence pour les nouveaux arrivants Faire face et fournir une vitesse estimée (Merci d'avoir réduit le nombre de personnes dans l'année. Désinfection. Leader émis Problème passé
--------------------------------------------------
Je n'ai pas fait de phrase qui ait du sens, mais il semble que j'ai un peu appris sur la position des paroles des parties.
La génération de phrases était difficile. Je souhaite poursuivre mes études et m'améliorer
Si vous avez des conseils ou des suggestions, je vous serais reconnaissant de bien vouloir commenter ..!
Recommended Posts