[PYTHON] J'ai fait réfléchir AI aux paroles de Genshi Yonezu (implémentation)

introduction

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.

  1. Hyper paramètre / modèle / fonction de perte / réglage de la méthode d'optimisation
  2. Code d'apprentissage
  3. Code de test

Modèle utilisé

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

スクリーンショット 2020-05-10 19.40.37.png

Ici, SOS est "_".


la mise en oeuvre

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 **

スクリーンショット 2020-05-09 18.28.25.png

Veuillez vous référer à github pour le code de ces modules auto-créés.


Réglage du problème (republié la dernière fois)

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.


Hyper paramètre / modèle / fonction de perte / réglage de la méthode d'optimisation

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))

Code d'apprentissage

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)

Code de test

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)

résultat

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


Ce que j'ai trouvé

--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

J'ai fait réfléchir AI aux paroles de Genshi Yonezu (implémentation)
J'ai fait réfléchir AI aux paroles de Genshi Yonezu (pré-traitement)
Le modèle de projet Python auquel je pense.
J'ai lu l'implémentation de range (Objects / rangeobject.c)
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai suivi la mise en place de la commande du (première moitié)
Pensez à la nouvelle génération de Rack et WSGI
À propos des tests dans la mise en œuvre de modèles d'apprentissage automatique
J'ai suivi la mise en place de la commande du (seconde moitié)
J'ai jeté un œil au contenu de sklearn (scikit-learn) (1) ~ Qu'en est-il de l'implémentation de CountVectorizer? ~
Un mémorandum sur la mise en œuvre des recommandations en Python
Pensez à l'environnement d'analyse (Partie 1: Vue d'ensemble) * Depuis janvier 2017
J'ai fait une fonction pour vérifier le modèle de DCGAN
J'ai fait une image ponctuelle de l'image d'Irasutoya. (partie 1)
J'ai fait une image ponctuelle de l'image d'Irasutoya. (partie 2)
À propos des composants de Luigi
À propos des fonctionnalités de Python
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Implémentation ~
[Python] J'ai expliqué en détail la théorie et la mise en œuvre de la régression logistique
J'ai fait un bot mou qui m'informe de la température
J'ai essayé de résumer la méthode de mise en œuvre fréquemment utilisée de pytest-mock
[Kaggle] J'ai fait une collection de problèmes en utilisant le didacticiel Titanic
Pensez au problème de changement minimum
J'ai étudié le mécanisme de connexion flask!
À propos de la valeur de retour de pthread_mutex_init ()
À propos de la valeur de retour de l'histogramme.
À propos du type de base de Go
À propos de la limite supérieure de threads-max
À propos du comportement de yield_per de SqlAlchemy
À propos de la taille des points dans matplotlib
À propos de la liste de base des bases de Python
Pensez grossièrement à la fonction de perte
J'ai fait un calendrier qui met à jour automatiquement le calendrier de distribution de Vtuber
Je voulais faire attention au comportement des arguments par défaut de Python
Je veux exprimer mes sentiments avec les paroles de Mr. Children
J'ai essayé de résumer la manière logique de penser l'orientation objet.
J'ai fait GAN avec Keras, donc j'ai fait une vidéo du processus d'apprentissage.
J'ai fait un programme pour vérifier la taille d'un fichier avec Python
J'ai fait une erreur en récupérant la hiérarchie avec MultiIndex of pandas
Je pense que la limite du sac à dos n'est pas le poids mais le volume w_11 / 22update
J'ai créé une fonction pour voir le mouvement d'un tableau à deux dimensions (Python)