[PYTHON] J'ai fait réfléchir AI aux paroles de Genshi Yonezu (pré-traitement)

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 concerne le "Prétraitement des données". Voici les étapes générales:

  1. Gratter et obtenir toutes les paroles écrites par M. Yonezu
  2. Formatez les données pour qu'elles correspondent aux paramètres du problème d'apprentissage en profondeur
  3. Divisez en données d'entraînement et données de test à 8: 2.

Généralement, le «prétraitement» fait référence au traitement des données pour améliorer la précision comme la normalisation, mais ce «prétraitement» signifie la mise en forme de sorte qu'il devienne une entrée ou une sortie de l'apprentissage en profondeur. ..


Modèle utilisé

Cadre: Pytorch Modèle: seq2seq avec attention


seq2seq et fond d'attention

C'est l'une des méthodes utilisées pour la "traduction automatique". Ce qui suit est une image de seq2seq. seq2seq.png

Article cité: [Encoder-decoder model and Teacher Forcing, Scheduled Sampling, Professor Forcing](https://satopirka.com/2018/02/encoder-decoder%E3%83%A2%E3%83%87%E3%83] % AB% E3% 81% A8professeur-forçageprofesseur-forçage-d'échantillonnage programmé


Cela permet à Decoder de générer des phrases basées sur les informations codées du côté de l'encodeur, mais il y a en fait quelques problèmes. Autrement dit, l'entrée Decoder ne peut être représentée que par un vecteur de longueur fixe. La sortie de Encoder est la couche cachée $ h $, mais cette taille est fixe. Par conséquent, un ensemble de données avec une longueur de série d'entrée trop longue ne pourra pas compresser correctement les informations en $ h $, et un ensemble de données avec une longueur de série d'entrée trop courte incorporera des informations inutiles dans $ h $. Je vais. Par conséquent, vous voudrez utiliser ** non seulement l'état de la dernière couche cachée de l'encodeur, mais également l'état de la couche cachée au milieu **.

C'est le fond de l'inventeur d'Attention.


Mécanisme d'attention

L'attention est une méthode pour prêter attention aux points importants du passé (= Attention) lors du traitement des données de séries chronologiques. Cette fois, le "prochain passage" est prédit pour le "passage lyrique" d'une certaine chanson, donc afin de prédire le passage suivant, à quoi devons-nous prêter attention dans le passage précédent? Devenir. Ci-dessous, une image de Attention.

attention.png

source: Effective Approaches to Attention-based Neural Machine Translation


Selon le document de référence, il est plus précisément appelé le modèle de l'attention globale. En collectant tous les états cachés de l'encodeur en tant que vecteur et en prenant le produit interne de ceux-ci et la sortie du décodeur, ** "La similitude entre tous les états cachés de l'encodeur et la sortie du décodeur" ** peut être obtenue. La mesure de cette similitude par produit interne est la raison pour laquelle on l'appelle Attention qui "se concentre sur des facteurs importants".


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


Problème de réglage

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.


Préparation des données

Grattage

Obtenez les paroles en grattant avec le code ci-dessous Ceux-ci sont exécutés par Google Colab.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.select import Select
import requests
from bs4 import BeautifulSoup
import re
import time

 setting
# Option Chrome pour lancer Selenium dans n'importe quel environnement
options = Options()
options.add_argument('--disable-gpu');
options.add_argument('--disable-extensions');
options.add_argument('--proxy-server="direct://"');
options.add_argument('--proxy-bypass-list=*');
options.add_argument('--start-maximized');
options.add_argument('--headless');

class DriverConrol():
    def __init__(self, driver):
        self.driver = driver
        
    def get(self, url):
        self.driver.get(url)
        
    def get_text(self, selector):
        element = self.driver.find_element_by_css_selector(selector)
        return element.text
        
    def get_text_by_attribute(self, selector, attribute='value'):
        element = self.driver.find_element_by_css_selector(selector)
        return element.get_attribute(attribute)
    
    def input_text(self, selector, text):
        element = self.driver.find_element_by_css_selector(selector)
        element.clear()
        element.send_keys(text)
        
    def select_option(self, selector, text):
        element = driver.find_element_by_css_selector(selector)
        Select(element).select_by_visible_text(text)
        
    def click(self, selector):
        element = self.driver.find_element_by_css_selector(selector)
        element.click()
        
    def get_lyric(self, url):
        self.get(url)
        time.sleep(2)
        element = self.driver.find_element_by_css_selector('#kashi_area')
        lyric = element.text
        return lyric
    
    def get_url(self):
        return self.driver.current_url
        
    def quit(self):
        self.driver.quit()

BASE_URL = 'https://www.uta-net.com/'
 search_word = 'Genshi Yonezu'
 search_jenre = 'nom de l'auteur'
driver = webdriver.Chrome(chrome_options=options)
dc = DriverConrol(driver)
 dc.get (BASE_URL) #access

# Chercher
dc.input_text('#search_form > div:nth-child(1) > input.search_input', search_word)
dc.select_option('#search_form > div:nth-child(2) > select', search_jenre)
dc.click('#search_form > div:nth-child(1) > input.search_submit')
time.sleep(2)

# Obtenez l'URL à la fois avec les demandes
response = requests.get(dc.get_url())
 response.encoding = response.apparent_encoding # Caractères anti-déformés
soup = BeautifulSoup(response.text, "html.parser")
 side_td1s = soup.find_all (class _ = "side td1") # Récupère tous les éléments td avec le côté classe td1
 lyric_urls = [side_td1.find ('a', href = re.compile ('song')). get ('href') pour side_td1 dans side_td1s] # side_td1s contient, href contient une balise Obtenez l'élément href de
 music_names = [side_td1.find ('a', href = re.compile ('song')). text for side_td1 in side_td1s] # Récupère tous les titres de chansons

# Obtenez les paroles et ajoutez-les à lyric_lis
lyric_lis = list()
for lyric_url in lyric_urls:
    lyric_lis.append(dc.get_lyric(BASE_URL + lyric_url))
with open(search_word + '_lyrics.txt', 'wt') as f_lyric, open(search_word + '_musics.txt', 'wt') as f_music:
    for lyric, music in zip(lyric_lis, music_names):
        f_lyric.write(lyric + '\n\n')
        f_music.write(music + '\n')

** Extrait des paroles acquises **

 Je suis vraiment content de te voir
 Tous sont tristes bien sûr
 J'ai des souvenirs douloureusement heureux maintenant
 Levez et faites les adieux qui viendront un jour

 Il suffit de prendre la place de quelqu'un et de vivre
 J'aimerais pouvoir être une pierre
 Si tel est le cas, il n'y a pas de malentendu ni de confusion
 De cette façon sans même te connaître

...

Mise en forme des données

À l'heure actuelle, il est loin des données affichées dans [Configuration du problème], donc "formater les données" est effectué.

Autrement dit, il fait cela.

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

Formatez les données avec le code suivant Le code prête à confusion, mais le prétraitement est maintenant terminé.

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 = "lyrique / 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):]

from sklearn.model_selection import train_test_split
from janome.tokenizer import Tokenizer
import torch
from utils import *

class LyricDataset(torch.utils.data.Dataset):
    def __init__(self, file_path, edited_file_path, transform=None):
        self.file_path = file_path
        self.edited_file_path = edited_file_path
        self.tokenizer = Tokenizer(wakati=True)

 self.input_lines = [] # NN input array (chaque élément est du texte)
 self.output_lines = [] # tableau de données correct de NN (chaque élément est du texte)
        self.word2id = {}  # e.g.) {'word0': 0, 'word1': 1, ...}

 self.input_data = [] # Un passage de paroles dans lequel chaque mot est ID
 self.output_data = [] # Le prochain passage où chaque mot est ID

        self.word_num_max = None
        self.transform = transform

        self._no_brank()

    def prepare(self):
 # Renvoie un tableau (texte) qui est l'entrée de NN et un tableau qui est les données de réponse correctes (texte) de NN.
        self.get_text_lines()

 Attribuez un identifiant à chaque caractère qui apparaît dans # date.txt
 pour la ligne dans self.input_lines + self.output_lines: # Premier passage et passages suivants
            self.get_word2id(line)

 # Trouver le nombre maximum de mots dans un passage
        self.get_word_num_max()
 # Renvoie un tableau (ID) qui est l'entrée de NN et un tableau qui est la donnée de réponse correcte (ID) de NN.
        for input_line, output_line in zip(self.input_lines, self.output_lines):
            self.input_data.append([self.word2id[word] for word in self.line2words(input_line)] \
            + [self.word2id[" "] for _ in range(self.word_num_max - len(self.line2words(input_line)))])
            self.output_data.append([self.word2id[word] for word in self.line2words(output_line)] \
            + [self.word2id[" "] for _ in range(self.word_num_max - len(self.line2words(output_line)))])

    def _no_brank(self):
 # Prenez un espace entre les lignes
        with open(self.file_path, "r") as fr, open(self.edited_file_path, "w") as fw:
            for line in fr.readlines():
                if isAlpha(line) or line == "\n":
 continue # Ignorer les lettres et les espaces
                fw.write(line)

    def get_text_lines(self, to_file=True):
        """
 Prend le chemin file_path du fichier de paroles sans lignes vides et renvoie un tableau similaire au suivant
        """
 # Yonezu Genshi_lyrics.txt est lu ligne par ligne, divisé en "un passage de paroles" et "prochain passage", et séparé par entrée et sortie.
        with open(self.edited_file_path, "r") as f:
 line_list = f.readlines () #Lyrics passage ... ligne
            line_num = len(line_list)
            for i, line in enumerate(line_list):
                if i == line_num - 1:
 continue # Il n'y a pas de "prochain passage" à la fin
                self.input_lines.append(line.replace("\n", ""))
                self.output_lines.append("_" + line_list[i+1].replace("\n", ""))

        if to_file:
            with open(self.edited_file_path, "w") as f:
                for input_line, output_line in zip(self.input_lines, self.output_lines):
                    f.write(input_line + " " + output_line + "\n")


    def line2words(self, line: str) -> list:
        word_list = [token for token in self.tokenizer.tokenize(line)]
        return word_list

    def get_word2id(self, line: str) -> dict:
        word_list = self.line2words(line)
        for word in word_list:
            if not word in self.word2id.keys():
                 self.word2id[word] = len(self.word2id)

    def get_word_num_max(self):
 # À la recherche du plus long
        word_num_list = []
        for line in self.input_lines + self.output_lines:
            word_num_list.append(len([self.word2id[word] for word in self.line2words(line)]))
        self.word_num_max = max(word_num_list)

    def __len__(self):
        return len(self.input_data)

    def __getitem__(self, idx):
        out_data = self.input_data[idx]
        out_label = self.output_data[idx]

        if self.transform:
            out_data = self.transform(out_data)

        return out_data, out_label

Cette fois jusqu'au prétraitement

Le code semble être plus long que prévu, donc cette fois je vais le limiter au "prétraitement des données".

Recommended Posts

J'ai fait réfléchir AI aux paroles de Genshi Yonezu (pré-traitement)
J'ai fait réfléchir AI aux paroles de Genshi Yonezu (implémentation)
Le modèle de projet Python auquel je pense.
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
Pensez à la nouvelle génération de Rack et WSGI
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 fait un bot mou qui m'informe de la température
[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 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)