L'autre jour, je suis allé au Groupe d'étude TensorFlow en tant que présentateur. Les gens autour de moi m'ont demandé si je n'écrirais rien sur le contenu de la session d'étude, mais j'avais peur du nombre de personnes annonce que personne ne serait heureux ), Donc à la place, je vais consommer le matériel mort que j'avais initialement prévu d'utiliser.
Ce que vous voulez faire est très simple comme le titre l'indique. Si vous entrez le nom du Pokémon, je veux que vous voyiez la valeur de la race et le type de chose. C'est comme essayer quelque chose qui est souvent le cas avec les fabricants de diagnostics Twitter, un peu plus sérieusement.
Nous avons décomposé le nom du Pokémon caractère par caractère et utilisé le nombre d'occurrences de chaque caractère et 2 grammes comme quantité de caractéristiques. Par exemple, dans le cas de Dedenne:
{
De: 2,Vers le bas: 1,Ne: 1,
Dédé: 1,Tanière: 1,Nne: 1
}
Créer vous-même des fonctionnalités n-gram est un problème, mais utiliser le [Vectorizer] de scikit-learn (http://scikit-learn.org/stable/modules/feature_extraction.html) 2, 3 Vous pouvez créer des fonctions n-gram tout en définissant des paramètres détaillés dans les lignes. Vous pouvez facilement enregistrer un vectoriseur qui stocke toutes les informations nécessaires, c'est pourquoi Ultra Super Miracle est recommandé. À moins que vous n'ayez à accumuler de la vertu en préparation du monde suivant ou qu'il y ait des circonstances spéciales, vous devez absolument l'utiliser.
La sortie est un peu encombrante et vous devez la diviser grossièrement en trois sorties.
Vous pouvez faire un modèle pour chacun et faire une prédiction, mais la flexibilité de tout regrouper est la force du réseau neuronal (je pense personnellement que c'est le cas), alors j'ai essayé de tout mettre dans un seul réseau. T. En d'autres termes, c'est ce que c'est.
Le nombre d'unités dans la dernière couche est de 6 + 18 + 19. La partie correspondant à la valeur de race était sortie telle quelle et la perte était définie par l'erreur quadratique. La sortie correspond à 18 ou 19 types, dont chacun est poussé dans softmax, et la perte est définie par chaque entropie croisée. La fonction objectif finale est l'addition pondérée de ces pertes.
~~ Ce ne sont pas des données confidentielles, donc si vous faites de votre mieux, vous vous réunirez. Faites de votre mieux. ~~ Seules les données utilisées ont été [téléchargées sur GitHub] avec le code (https://github.com/sfujiwara/pn2bs).
# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.externals import joblib
from sklearn.feature_extraction import DictVectorizer
def inference(x_placeholder, n_in, n_hidden1, n_hidden2):
"""
Description
-----------
Forward step which build graph.
Parameters
----------
x_placeholder: Placeholder for feature vectors
n_in: Number of units in input layer which is dimension of feature
n_hidden1: Number of units in hidden layer 1
n_hidden2: Number of units in hidden layer 2
Returns
-------
y_bs: Output tensor of predicted values for base stats
y_type1: Output tensor of predicted values for type 1
y_type2: Output tensor of predicted values for type 2
"""
# Hidden1
with tf.name_scope('hidden1') as scope:
weights = tf.Variable(
tf.truncated_normal([n_in, n_hidden1]),
name='weights'
)
biases = tf.Variable(tf.zeros([n_hidden1]))
hidden1 = tf.nn.sigmoid(tf.matmul(x_placeholder, weights) + biases)
# Hidden2
with tf.name_scope('hidden2') as scope:
weights = tf.Variable(
tf.truncated_normal([n_hidden1, n_hidden2]),
name='weights'
)
biases = tf.Variable(tf.zeros([n_hidden2]))
hidden2 = tf.nn.sigmoid(tf.matmul(hidden1, weights) + biases)
# Output layer for base stats
with tf.name_scope('output_base_stats') as scope:
weights = tf.Variable(
tf.truncated_normal([n_hidden2, 6]),
name='weights'
)
biases = tf.Variable(tf.zeros([6]))
y_bs = tf.matmul(hidden2, weights) + biases
# Output layer for type1
with tf.name_scope('output_type1') as scope:
weights = tf.Variable(
tf.truncated_normal([n_hidden2, 18]),
name='weights'
)
biases = tf.Variable(tf.zeros([18]))
# y_type1 = tf.nn.softmax(tf.matmul(hidden2, weights) + biases)
y_type1 = tf.matmul(hidden2, weights) + biases
# Output layer for type2
with tf.name_scope('output_type2') as scope:
weights = tf.Variable(
tf.truncated_normal([n_hidden2, 19]),
name='weights'
)
biases = tf.Variable(tf.zeros([19]))
y_type2 = tf.matmul(hidden2, weights) + biases
# y_type2 = tf.nn.softmax(tf.matmul(hidden2, weights) + biases)
return [y_bs, y_type1, y_type2]
def build_loss_bs(y_bs, t_ph_bs):
"""
Parameters
----------
y_bs: Output tensor of predicted values for base stats
t_ph_bs: Placeholder for base stats
Returns
-------
Loss tensor which includes placeholder of features and labels
"""
loss_bs = tf.reduce_mean(tf.nn.l2_loss(t_ph_bs - y_bs), name='LossBaseStats')
return loss_bs
def build_loss_type1(y_type1, t_ph_type1):
"""
Parameters
----------
y_type1: Output tensor of predicted values for base stats
t_ph_type1: Placeholder for base stats
Returns
-------
Loss tensor which includes placeholder of features and labels
"""
loss_type1 = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(y_type1, t_ph_type1),
name='LossType1'
)
return loss_type1
def build_loss_type2(y_type2, t_ph_type2):
"""
Parameters
----------
y_type2: Output tensor of predicted values for base stats
t_ph_type2: Placeholder for base stats
Returns
-------
Loss tensor which includes placeholder of features and labels
"""
loss_type2 = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(y_type2, t_ph_type2),
name='LossType2'
)
return loss_type2
def build_optimizer(loss, step_size):
"""
Parameters
----------
loss: Tensor of objective value to be minimized
step_size: Step size for gradient descent
Returns
-------
Operation of optimization
"""
optimizer = tf.train.GradientDescentOptimizer(step_size)
global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)
return train_op
if __name__ == '__main__':
# Set seed
tf.set_random_seed(0)
# Load data set and extract features
df = pd.read_csv('data/poke_selected.csv')
# Fill nulls in type2
df.loc[df.type2.isnull(), 'type2'] = 'Rien'
# Vectorize pokemon name
pokename_vectorizer = CountVectorizer(analyzer='char', min_df=1, ngram_range=(1, 2))
x = pokename_vectorizer.fit_transform(list(df['name_jp'])).toarray()
t_bs = np.array(df[['hp', 'attack', 'block', 'contact', 'defense', 'speed']])
# Vectorize pokemon type1
poketype1_vectorizer = DictVectorizer(sparse=False)
d = df[['type1']].to_dict('record')
t_type1 = poketype1_vectorizer.fit_transform(d)
# Vectorize pokemon type2
poketype2_vectorizer = DictVectorizer(sparse=False)
d = df[['type2']].to_dict('record')
t_type2 = poketype2_vectorizer.fit_transform(d)
# Placeholders
x_ph = tf.placeholder(dtype=tf.float32)
t_ph_bs = tf.placeholder(dtype=tf.float32)
t_ph_type1 = tf.placeholder(dtype=tf.float32)
t_ph_type2 = tf.placeholder(dtype=tf.float32)
# build graph, loss, and optimizer
y_bs, y_type1, y_type2 = inference(x_ph, n_in=1403, n_hidden1=512, n_hidden2=256)
loss_bs = build_loss_bs(y_bs, t_ph_bs)
loss_type1 = build_loss_type1(y_type1, t_ph_type1)
loss_type2 = build_loss_type2(y_type2, t_ph_type2)
loss = tf.add_n([1e-4 * loss_bs, loss_type1, loss_type2], name='ObjectiveFunction')
optim = build_optimizer(loss, 1e-1)
# Create session
sess = tf.Session()
# Initialize variables
init = tf.initialize_all_variables()
sess.run(init)
# Create summary writer and saver
summary_writer = tf.train.SummaryWriter('log', graph_def=sess.graph_def)
tf.scalar_summary(loss.op.name, loss)
tf.scalar_summary(loss_bs.op.name, loss_bs)
tf.scalar_summary(loss_type1.op.name, loss_type1)
tf.scalar_summary(loss_type2.op.name, loss_type2)
summary_op = tf.merge_all_summaries()
saver = tf.train.Saver()
# Run optimization
for i in range(1500):
# Choose indices for mini batch update
ind = np.random.choice(802, 802)
batch_xs = x[ind]
batch_ts_bs = t_bs[ind]
batch_ts_type1 = t_type1[ind]
batch_ts_type2 = t_type2[ind]
# Create feed dict
fd = {
x_ph: batch_xs,
t_ph_bs: batch_ts_bs,
t_ph_type1: batch_ts_type1,
t_ph_type2: batch_ts_type2
}
# Run optimizer and update variables
sess.run(optim, feed_dict=fd)
# Show information and write summary in every n steps
if i % 100 == 99:
# Show num of epoch
print 'Epoch:', i + 1, 'Mini-Batch Loss:', sess.run(loss, feed_dict=fd)
# Write summary and save checkpoint
summary_str = sess.run(summary_op, feed_dict=fd)
summary_writer.add_summary(summary_str, i)
name_model_file = 'model_lmd1e-4_epoch_' + str(i+1) + '.ckpt'
save_path = saver.save(sess, 'model/tensorflow/'+name_model_file)
else:
name_model_file = 'model_lmd1e-4_epoch_' + str(i+1) + '.ckpt'
save_path = saver.save(sess, 'model/tensorflow/'+name_model_file)
# Show example
poke_name = 'Tonnerre'
v = pokename_vectorizer.transform([poke_name]).toarray()
pred_bs = sess.run(y_bs, feed_dict={x_ph: v})
pred_type1 = np.argmax(sess.run(y_type1, feed_dict={x_ph: v}))
pred_type2 = np.argmax(sess.run(y_type2, feed_dict={x_ph: v}))
print poke_name
print pred_bs
print pred_type1, pred_type2
print poketype1_vectorizer.get_feature_names()[pred_type1]
print poketype2_vectorizer.get_feature_names()[pred_type2]
# Save vectorizer of scikit-learn
joblib.dump(pokename_vectorizer, 'model/sklearn/pokemon-name-vectorizer')
joblib.dump(poketype1_vectorizer, 'model/sklearn/pokemon-type1-vectorizer')
joblib.dump(poketype2_vectorizer, 'model/sklearn/pokemon-type2-vectorizer')
Cela ne signifie pas que le matériau est sérieusement réglé parce que c'est un matériau, alors j'ai juste regardé TensorBoard pour voir si la perte sur le type minimum et la perte sur la valeur de course ont été réduites de manière équilibrée.
Chargeons le modèle et jouons avec comme ça.
# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.externals import joblib
import pn2bs
# Placeholder
x_ph = tf.placeholder(dtype=tf.float32)
t_ph = tf.placeholder(dtype=tf.float32)
y_bs, y_type1, y_type2 = pn2bs.inference(x_ph, n_in=1403, n_hidden1=512, n_hidden2=256)
# Create session
sess = tf.Session()
# Load TensorFlow model
saver = tf.train.Saver()
saver.restore(sess, "model/tensorflow/model_lmd1e-4_epoch_1500.ckpt")
# Load vectorizer of scikit-learn
pokename_vectorizer = joblib.load("model/sklearn/pokemon-name-vectorizer")
poketype1_vectorizer = joblib.load("model/sklearn/pokemon-type1-vectorizer")
poketype2_vectorizer = joblib.load("model/sklearn/pokemon-type2-vectorizer")
poke_name = 'Gonzales'
v = pokename_vectorizer.transform([poke_name]).toarray()
pred_bs = sess.run(y_bs, feed_dict={x_ph: v})
pred_type1 = np.argmax(sess.run(y_type1, feed_dict={x_ph: v}))
pred_type2 = np.argmax(sess.run(y_type2, feed_dict={x_ph: v}))
result = {
'name' : poke_name,
'hp' : pred_bs[0][0],
'attack' : pred_bs[0][1],
'block' : pred_bs[0][2],
'contact': pred_bs[0][3],
'defense': pred_bs[0][4],
'speed' : pred_bs[0][5],
'type1' : poketype1_vectorizer.get_feature_names()[pred_type1],
'type2' : poketype2_vectorizer.get_feature_names()[pred_type2],
}
print result['name']
print result['hp']
print result['attack']
print result['block']
print result['contact']
print result['defense']
print result['speed']
print result['type1']
print result['type2']
Un exemple du résultat. Il n'y a rien de spécial à dire, mais devinez quelque chose sur chacun d'eux.
Name | H | A | B | C | D | S | Type 1 | Type 2 |
---|---|---|---|---|---|---|---|---|
Flux Tensol | 92 | 102 | 84 | 85 | 65 | 73 | Fée | mal |
Mega Pikachu | 74 | 80 | 50 | 97 | 85 | 80 | Électricité | Rien |
Gonzales | 81 | 103 | 107 | 86 | 103 | 65 | Dragon | Électricité |
Mega Gonzales | 100 | 137 | 131 | 118 | 117 | 103 | Dragon | mal |
J'ai pensé que la flexibilité de la modélisation est excellente parce que le réseau neuronal jette les bonnes propriétés de convexité et fonctionne à la colombe de toutes ses forces. Par exemple, si vous essayez de faire la même chose avec SVM, vous cesserez d'abord de vous demander quoi faire lorsque l'on vous demandera une sortie multidimensionnelle telle que les valeurs de race, et vous résoudrez même le problème de classification lié aux types ensemble. Le jour où on m'a dit, j'ai l'impression que je devrais dormir et dire dormir. Cependant, quand il s'agit de laisser les 2 outs en bas de la 9e manche au réseau neuronal, cela ressemble à hmm. quelque chose comme ca.
2016-01-26
Je suis un peu désolé de ne pas l'avoir publié dans un état où je peux l'essayer correctement même s'il y a des gens qui le stockent encore de temps en temps, j'ai donc publié quelque chose qui fonctionne [sur GitHub](https: / /github.com/sfujiwara/pn2bs). Je pensais que ce serait cool de publier un graphique radar avec Google Charts ou quelque chose selon le nom entré, mais je voulais laisser le bot le frapper et jouer avec, alors j'en ai fait une API Web.
Je pense qu'il sortira probablement lorsque je l'utilisera, donc si je l'avoue en premier, lorsque je mets un Pokémon existant, il se fanera si le statut est complètement différent de celui actuel, alors j'ai intentionnellement adopté un modèle qui semble surappris.
Recommended Posts