Genshi Yonezu vend à chaque fois qu'il compose. Les paroles qui sortent semblent avoir le pouvoir de fasciner les gens. Cette fois, j'ai décidé de laisser l'apprentissage profond apprendre son charme.
Cet article s'intitule "** Implémentation **". Voir l'article précédent pour le code "pré-traitement". Le flux général de la mise en œuvre est le suivant.
Cadre: Pytorch Modèle: seq2seq avec attention Module d'analyse morphologique: jonome Environnement: Google Colaboratory
Pour le mécanisme de seq2seq et Attention, voir Article précédent.
Le diagramme schématique de ce modèle est le suivant. Article de référence
Ici, SOS est "_".
Après avoir téléchargé le module personnalisé requis sur Google colab Copiez et exécutez main.py décrit plus loin.
** Module personnalisé requis **
Veuillez vous référer à github pour le code de ces modules auto-créés.
Comme indiqué ci-dessous, Genji Yonezu prédit le "prochain passage" du "passage unique" des chansons qui ont été publiées jusqu'à présent.
|Texte de saisie|Texte de sortie| |-------+-------| |Je suis vraiment content de te voir| _Tous sont tristes bien sûr| |Tous sont tristes bien sûr| _J'ai des souvenirs douloureusement heureux maintenant| |J'ai des souvenirs douloureusement heureux maintenant| _Levez et faites les adieux qui viendront un jour| |Levez et faites les adieux qui viendront un jour| _Il suffit de prendre la place de quelqu'un et de vivre|
Ceci a été créé en grattant sur Lyrics Net.
Voici un supplément sur le contenu précédent.
La dernière fois, mon objectif était de créer un "texte d'entrée" et un "texte de sortie", qui étaient en japonais, mais en réalité ils sont identifiés (quantifiés) avec yonedu_dataset.prepare ()
pour que DL puisse être lu. Je vais.
Le ** hyper paramètre ** est spécifié à partir du haut comme suit.
--Nombre de nœuds dans la couche intégrée du codeur --Nombre de nœuds de couche intermédiaire dans la couche LSTM du codeur --Taille du lot --Nombre de vocabulaire de paroles écrites par M. Yonezu jusqu'à présent (jonome est utilisé pour l'analyse morphologique)
Le ** modèle ** est seq2seq, il divise donc son rôle en un encodeur et un décodeur.
encoder: embedding layer + hidden layer with LSTM decoder with attention: embedding layer + hidden layer with LSTM + attention system + softmax layer
La ** fonction de perte ** utilise la fonction d'erreur d'entropie croisée et la ** méthode d'optimisation ** utilise Adam à la fois pour le codeur et le décodeur.
De plus, s'il existe un paramètre de modèle, il sera chargé.
from datasets import LyricDataset
import torch
import torch.optim as optim
from modules import *
from device import device
from utils import *
from dataloaders import SeqDataLoader
import math
import os
from utils
==========================================
# Préparation des données
==========================================
# Chemin Genshi Yonezu_lyrics.txt
file_path = "paroles / Genshi Yonezu_lyrics.txt"
edit_file_path = "paroles / Genshi Yonezu_lyrics_edit.txt"
yonedu_dataset = LyricDataset(file_path, edited_file_path)
yonedu_dataset.prepare()
check
print(yonedu_dataset[0])
# Divisez en train et testez à 8: 2
train_rate = 0.8
data_num = len(yonedu_dataset)
train_set = yonedu_dataset[:math.floor(data_num * train_rate)]
test_set = yonedu_dataset[math.floor(data_num * train_rate):]
# Si loin la dernière fois
================================================
# Hyper paramétrage / modèle / fonction de perte / méthode d'optimisation
================================================
# Hyper paramètres
embedding_dim = 200
hidden_dim = 128
BATCH_NUM = 100
EPOCH_NUM = 100
vocab_size = len (yonedu_dataset.word2id) #nombre de vocabulaire
padding_idx = yonedu_dataset.word2id [""] # ID vide
# modèle
encoder = Encoder(vocab_size, embedding_dim, hidden_dim, padding_idx).to(device)
attn_decoder = AttentionDecoder(vocab_size, embedding_dim, hidden_dim, BATCH_NUM, padding_idx).to(device)
# Fonction de perte
criterion = nn.CrossEntropyLoss()
# Méthode d'optimisation
encoder_optimizer = optim.Adam(encoder.parameters(), lr=0.001)
attn_decoder_optimizer = optim.Adam(attn_decoder.parameters(), lr=0.001)
# Charger les paramètres si vous avez un modèle entraîné
encoder_weights_path = "yonedsu_lyric_encoder.pth"
decoder_weights_path = "yonedsu_lyric_decoder.pth"
if os.path.exists(encoder_weights_path):
encoder.load_state_dict(torch.load(encoder_weights_path))
if os.path.exists(decoder_weights_path):
attn_decoder.load_state_dict(torch.load(decoder_weights_path))
Vient ensuite le code d'apprentissage. Je pense que seq2seq avec Attention ressemblera à ceci, mais je n'ajouterai qu'un seul point. En utilisant ** mon propre chargeur de données **, j'obtiens un mini-lot pour 100 tailles de lots pour chaque époque, je rétropropage la perte totale de ces données, j'obtiens le gradient et je mets à jour les paramètres.
Pour le ** chargeur de données auto-conçu **, reportez-vous au [Code source pour Deep Learning 3 from scratch] de M. Yasuki Saito (https://github.com/oreilly-japan/deep-learning-from-scratch-3). Je le fais.
================================================
# Apprentissage
================================================
all_losses = []
print("training ...")
for epoch in range(1, EPOCH_NUM+1):
epoch_loss = 0
# Divisez les données en mini-batch
dataloader = SeqDataLoader(train_set, batch_size=BATCH_NUM, shuffle=False)
for train_x, train_y in dataloader:
#Initialisation du dégradé
encoder_optimizer.zero_grad()
attn_decoder_optimizer.zero_grad()
# Propagation vers l'avant de l'encodeur
hs, h = encoder(train_x)
# Attention Entrée du décodeur
source = train_y[:, :-1]
Corriger les données de réponse de # Attention Decoder
target = train_y[:, 1:]
loss = 0
decoder_output, _, attention_weight = attn_decoder(source, hs, h)
for j in range(decoder_output.size()[1]):
loss += criterion(decoder_output[:, j, :], target[:, j])
epoch_loss += loss.item()
# Erreur de propagation de retour
loss.backward()
# Mise à jour des paramètres
encoder_optimizer.step()
attn_decoder_optimizer.step()
# Afficher la perte
print("Epoch %d: %.2f" % (epoch, epoch_loss))
all_losses.append(epoch_loss)
if epoch_loss < 0.1: break
print("Done")
import matplotlib.pyplot as plt
plt.plot(all_losses)
plt.savefig("attn_loss.png ")
# Enregistrer le modèle
torch.save(encoder.state_dict(), encoder_weights_path)
torch.save(attn_decoder.state_dict(), decoder_weights_path)
Voici le code de test. Ce que je fais, c'est créer le tableau affiché dans ** [Résultats] **. Il y a deux points à noter.
=======================================
# tester
=======================================
# Dictionnaire de conversion Word-> ID
word2id = yonedu_dataset.word2id
# ID-> dictionnaire de conversion de mots
id2word = get_id2word(word2id)
# Nombre d'éléments dans une donnée de réponse correcte
output_len = len(yonedu_dataset[0][1])
# Données d'évaluation
test_dataloader = SeqDataLoader(test_set, batch_size=BATCH_NUM, shuffle=False)
# Trame de données pour afficher le résultat
df = pd.DataFrame(None, columns=["input", "answer", "predict", "judge"])
# Tournez le chargeur de données pour remplir le bloc de données qui affiche les résultats
for test_x, test_y in test_dataloader:
with torch.no_grad():
hs, encoder_state = encoder(test_x)
Tout d'abord, "_" indiquant le début de la génération de la chaîne de caractères est entré dans # Decoder, donc
# Créer un tenseur "_" pour la taille du lot
start_char_batch = [[word2id["_"]] for _ in range(BATCH_NUM)]
decoder_input_tensor = torch.tensor(start_char_batch, device=device)
decoder_hidden = encoder_state
batch_tmp = torch.zeros(100,1, dtype=torch.long, device=device)
for _ in range(output_len - 1):
decoder_output, decoder_hidden, _ = attn_decoder(decoder_input_tensor, hs, decoder_hidden)
# Lors de l'obtention du caractère prédit, il devient l'entrée du décodeur suivant tel quel
decoder_input_tensor = get_max_index(decoder_output.squeeze(), BATCH_NUM)
batch_tmp = torch.cat([batch_tmp, decoder_input_tensor], dim=1)
predicts = batch_tmp [:, 1:] # Recevoir les lots prévus
if test_dataloader.reverse:
test_x = [list (line) [:: -1] pour la ligne dans test_x] #Retourner l'inversé
df = predict2df(test_x, test_y, predicts, df)
df.to_csv("predict_yonedsu_lyric.csv", index=False)
Toutes les questions sont incorrectes. Cependant, l'objectif était cette fois "** Capturer les caractéristiques des paroles de M. Genji Yonezu **". Un extrait du tableau.
** input **: texte d'entrée ** sortie **: texte de sortie correct ** prédire **: texte prédit DL ** juge **: la sortie et la prédiction correspondent-elles?
input | output | predict | judge ---------+----------------+----------------+------------ Je m'en fichais si c'était une erreur ou une bonne réponse|Dans la brume pâle qui tomba en un clin d'œil|Je suis triste parce que je veux être aimé, alors peut-être que tu es le seul|X J'ai senti que tout avait changé depuis ce jour|Ne soyez pas emporté par le vent, un coin de printemps profond|L'endroit chaleureux est toujours beau|X Découvrons un par un|Comme un enfant qui se réveille|Bleu flétri, même cette couleur|X Peu importe ce que tu fais aujourd'hui|je te chercherai|Je cherchais une ville qui ne changerait pas|X
--La déclaration de prédiction n'est pas claire (la grammaire est précise comme "encore")
Puisqu'aucun surapprentissage n'a été observé cette fois, on considère que la cause du manque d'apprentissage est principalement le petit nombre de données. Non, nous sommes les seuls à avoir décidé qu'ils sont "insuffisants en apprentissage", et ils pensent peut-être à leur manière ...
Recommended Posts