[PYTHON] Génération de Pokémon la plus puissante utilisant LSTM

Motivation

Dans l'article que j'ai lu auparavant, il y avait un article intéressant avec le titre ** Reproduction de symboles sonores par apprentissage automatique: Génération du Pokémon le plus fort **. Je l'ai arrangé moi-même en référence à cet article et je l'ai utilisé pour la présentation du séminaire, je vais donc le publier. Je l'ai fait avec un peu d'énergie, alors je l'ai posté parce que je voulais le lancer dans le monde extérieur.

Je m'occupe généralement de systèmes d'images, et comme le traitement du langage naturel est un premier article amateur, je pense qu'il y a beaucoup de points qui ne peuvent être atteints, mais pardonnez-moi s'il vous plaît. * Lien matériel utilisé dans la présentation du séminaire

Que faire cette fois

Cette fois, en se référant aux idées des articles suivants, nous viserons à générer le Pokémon le plus fort par la même méthode tout en ajoutant l'accent de l'apprentissage profond (LSTM). Dans l'article, le questionnaire sujet a été utilisé pour quantifier l'impression de son, mais comme nous ne disposons pas de telles données, nous utiliserons la valeur de race à la place.

Reproduction de symboles sonores par apprentissage automatique: Génération du Pokémon le plus puissant Satoshi Miura ∗ 1 Masaki Murata ∗ 1 Sho Yasuda ∗ 2 Mai Miyabe ∗ 2 Eiji Aramaki ∗ 2 ∗ 1 École supérieure de l'Université Tottori * 2 Actes de la 18e réunion annuelle de la Société de traitement du langage de l'Université (mars 2012) http://luululu.com/paper/2012/C1-1.pdf

Contour du papier original (3 lignes)

・ Prédisez la force de Pokémon par comparaison de paires avec 8 sujets (qui ne connaissent pas Pokémon). -Utilisez-les comme données d'entraînement pour générer un modèle avec SVM. -Utilisez le modèle, changez le nom du Pokémon et répétez le jugement de force pour générer le Pokémon le plus fort.

Prétraitement des données

Données d'utilisation

Les données utilisées cette fois sont les données de table de Pokémon jusqu'à la 7ème génération. Étant donné que le nom d'un Pokémon peut comporter jusqu'à 6 caractères, supprimez la valeur de Pokémon (Landros Beast, Jigarde 10%, etc.) avec un nom supérieur à cela. De plus, cette fois, la méga évolution Pokemon était également exclue.

Les données Pokémon sont empruntées au lien ci-dessous.

https://rikapoke.hatenablog.jp/entry/pokemon_datasheet_gne7

import pandas as pd
status = pd.read_csv("pokemon_status.csv", encoding="shift_jis")
status

Données CSV traitées cette fois ↓ スクリーンショット 2020-11-07 17.54.24.png

Nous ajouterons un prétraitement à cela.

#Suppression de méga Pokémon supplémentaires
status = status[~status['Numéro du livre d'images'].str.contains('-')]
status
#Pokémon supprimé dont le nom est 7 ou plus
status['len'] = status['Nom du Pokémon'].map(lambda x: len(x))
de = status[status['len']>6]
status = status[status['len']<7]
de
#Pour les données uniquement les données utilisées
status = status.loc[:, ['Nom du Pokémon','total']]
status

Complétion des données utilisées cette fois! スクリーンショット 2020-11-07 18.00.59.png

De là, nous continuerons le prétraitement pour alimenter le LSTM.

#tokenize
def function(name):
    n_gram = ''
    for n in name:
        n_gram = n_gram + n + ' '
    return n_gram

status['Nom du Pokémon'] = status['Nom du Pokémon'].map(function)
status
#Normalisation de la valeur raciale& 0or1
from sklearn import preprocessing

def labeling(pred, p=0.5):
    if pred < p:
        pred_label = 0
    else:
        pred_label = 1
    
    return pred_label

status['total'] = preprocessing.minmax_scale(status['total'])
status['total'] = status['total'].map(labeling)
status

Données après prétraitement ↓ スクリーンショット 2020-11-07 18.07.12.png

Classez les données créées en train et val et enregistrez.

from sklearn.model_selection import train_test_split

train_df, val_df = train_test_split(status, random_state=1234, test_size=0.2)
train_df.to_csv("./train_df.tsv", sep='\t')
val_df.to_csv("./val_df.tsv", sep='\t')

Construire un modèle

Puisque je suis un partisan du pytorch, je vais construire un modèle avec une torche et présenter brièvement le code. Le code complet est disponible sur le lien ci-dessous, donc si vous êtes intéressé, veuillez vous y référer.

https://github.com/drop-ja/pokemon

from torchtext import data
import torchtext

batch_size = 4
max_len = 6

#méthode tokenize
tokenizer = lambda x: x.split()

#Informations sur les étiquettes, etc.
TEXT = data.Field(sequential=True, tokenize=tokenizer, include_lengths=True, 
                 batch_first=True, fix_length=max_len)
LABEL = data.LabelField()

fields_train = [('id', None), ('name', TEXT), ('bs', LABEL)]

dataset_train, dataset_valid = data.TabularDataset.splits(
                             path = './',
                             format='TSV',
                             skip_header=True, 
                             train="train_df.tsv",
                             validation="val_df.tsv", 
                             fields=fields_train)
TEXT.build_vocab(dataset_train)
LABEL.build_vocab(dataset_train)

train_iter = data.BucketIterator(dataset=dataset_train, batch_size=batch_size, 
                                 sort_key=lambda x: len(x.name), repeat=False, shuffle=True)
val_iter = data.BucketIterator(dataset=dataset_valid, batch_size=1, 
                                 sort_key=lambda x: len(x.name), repeat=False, shuffle=False)
#Définition du modèle
import torch
import torch.nn as nn
import torch.nn.init as init
import torch.optim as optim
import torch.nn.functional as F

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

class LSTMPVClassifier(nn.Module):
    def __init__(self, vocab_size, embedding_dim, lstm_hidden_size,  mlp_hidden_size, output_size):
        super(LSTMPVClassifier, self).__init__()
        self.lstm_hidden_size = lstm_hidden_size
   
        self.embed = nn.Embedding(vocab_size, embedding_dim, padding_idx=1)
        self.lstm = nn.LSTM(embedding_dim, self.lstm_hidden_size, batch_first=True, 
                        num_layers=1, bidirectional=False, dropout=0.0)
    
        self.fc1 = nn.Linear(self.lstm_hidden_size, mlp_hidden_size)
        self.fc2 = nn.Linear(mlp_hidden_size, output_size)
    
    def forward(self, x):
        b_size = x.size(0) #Taille du lot
        seq_len  = x.size(1) #Longueur du nom Pokemon

        x = self.embed(x)
        h0 = torch.zeros(1, b_size, self.lstm_hidden_size).to(device)
        c0 = torch.zeros(1, b_size, self.lstm_hidden_size).to(device)
  
        lstm_output_seq, (h_n, c_n) = self.lstm(x, (h0, c0))
   
        out = torch.relu(self.fc1(lstm_output_seq))
        out = torch.sigmoid(self.fc2(out)) 

        return out

résultat

Voici le résultat de l'obtention de 10 Epoch avec le modèle ci-dessus. «Total» est l'étiquette de réponse correcte et «Valeur prédite» est le résultat prédit par le modèle.

スクリーンショット 2020-11-07 18.19.30.png

Comment afficher la valeur F du résultat ci-dessus. Une valeur assez bonne est renvoyée pour un modèle approprié.

スクリーンショット 2020-11-07 18.21.24.png

Jouez avec le modèle généré

J'ai mis un nom et j'ai joué avec. J'ai mis les noms des membres du laboratoire et je les ai classés, et c'était assez excitant de jouer avec ça.

スクリーンショット 2020-11-07 18.25.41.png

Code ci-dessous

#Base de données
#Vérifiez la valeur convertie
def to_dataset(list_obj, pri=True):
    index = pd.DataFrame(list_obj)
    index[0] = index[0].map(function)
    index.to_csv('./test.tsv', sep='\t')
    
    fields_test = [('id', None), ('name', TEXT)]
    dataset_test = data.TabularDataset(path='./test.tsv',
                             format='TSV', skip_header=True, fields=fields_test)
    
    test_iter = data.BucketIterator(dataset=dataset_test, batch_size=1, 
                                 sort_key=lambda x: len(x.name), repeat=False, shuffle=False)
    
    batch = next(iter(test_iter))
    
    if pri:
        print(batch.name)
    
    return test_iter

list_obj = ['Bouilloire Denshi', 'Gagigugego', 'four micro onde', 'Poêle à frire', 'Jisaboke', 'Pokémon']
test_iter = to_dataset(list_obj)
def result_show(test_iter, pri=True):
    test_predicted = []
    
    for batch in test_iter:
        text = batch.name[0]
        text = text.to(device)
        outputs = eval_net(text)
        outputs = outputs[:, -1]
        tmp_pred_label = outputs.to('cpu').detach().numpy().copy()
        test_predicted.extend(tmp_pred_label[0])
    
    if pri:
        print(test_predicted)
    
    return test_predicted
        
result = result_show(test_iter)

df = pd.DataFrame(list_obj, columns=['Nom'])
df['Valeur prédite'] = result
df['0,1 étiquette'] = labeling(df['Valeur prédite'])
df

Générez le Pokémon le plus puissant

C'est enfin le sujet.

La méthode de génération de Pokémon la plus puissante suit le papier et est générée par la même méthode.

** Méthode papier ** --Sélectionnez un échantillon de manière appropriée --Remplacer un personnage au hasard

Dans l'article, il a été généré à partir de "Parasect" et "Nidoquin", donc je vais le suivre. Le code ci-dessous.

def generate_pokemon(string):
    history = []
    score = []
    history.append(string)
    
    for i in range(50):
        changed_string = change_name(string, 1)
        cd_result = result_show(to_dataset([string, changed_string], False), False)
        
        #Ajouter seulement au début
        if i ==0:
            score.append(cd_result[0])
            
        if cd_result[0] > cd_result[1]:
            score.append(cd_result[0])
        else:
            string = changed_string
            score.append(cd_result[1])
            
        history.append(string)
        
    cd_df = pd.DataFrame(history, columns=['Nom'])
    cd_df['Valeur prédite'] = score
    
    return string, cd_df


string = 'Parasect'
saikyou, port = generate_pokemon(string)

print('Nom le plus fort: ', saikyou)
pd.DataFrame(port)

Résultat de génération de parasect (processus 10/50) スクリーンショット 2020-11-07 18.35.14.png

Le résultat final est "Egineo". Comme les personnages sont échangés au hasard, il était intéressant que le résultat change à chaque fois que je l'ai tourné, et je l'ai tourné plusieurs fois. C'est amusant de bouger tout en vérifiant les résultats de cette façon.

Aussi, j'ai utilisé Pokemon cette fois, mais il semble intéressant de le faire avec le nom du magasin de ramen et l'évaluation du journal de consommation.

Recommended Posts

Génération de Pokémon la plus puissante utilisant LSTM
[Génération de phrases LSTM] Utiliser ml5js
Prévision du cours de l'action avec LSTM_1
Automatisation de la génération d'algorithmes à l'aide d'algorithmes génétiques
[PyTorch] Génération de phrases japonaises à l'aide de Transformer