(Ajout) Ce n'est pas TensorFlow, mais j'ai trouvé qu'il a été résolu plus magnifiquement par Chainer. Apprenez le Zundokokiyoshi en utilisant LSTM
J'ai l'impression d'avoir raté le boom, mais depuis que j'ai récemment touché TensorFlow, j'ai utilisé TensorFlow pour utiliser __convolutional neural network (CNN, ConvNet: Convolutional neural network) __. J'ai essayé de résoudre Zundokokiyoshi en l'utilisant. Vous n'avez même plus besoin de dépenser votre intelligence humaine pour résoudre Zundokokiyoshi.
Le code complet est ici.
Tout d'abord, nous devons réfléchir à ce que le réseau de neurones peut résoudre pour Zundokokiyoshi.
Par exemple, vous pouvez découper 5 éléments d'un Zundcolist (une liste dans laquelle "Zun" et "Doko" sont stockés au hasard) et les laisser déterminer s'ils sont "Zun, Dung, Dung, Dung, Doco". Il est trop facile de créer un problème et ce n'est pas intéressant. Vous n'avez pas besoin de créer un réseau de neurones, il n'y a que des modèles $ 2 ^ 5 = 32 $, il vous suffit donc de vous souvenir de tous les modèles plutôt que de l'apprentissage automatique.
Cela dit, je ne pouvais pas penser à un bon moyen de transmettre un Zund Collist infini comme entrée à un réseau de neurones. C'est pourquoi je vais passer un Zund Collist avec 100 éléments et retourner un index où le premier "Zun" de "Zun, Dung, Dung, Dung, Doco" apparaît pour la première fois. Par exemple, la bonne réponse est de renvoyer «2» pour un Zund Collist qui dit «Dung, Doco, Dung, Dung, Dung, Dung, Doco, ...». Puisque le nombre d'éléments est de 100, il peut y avoir des solutions de "0" à "95". De plus, si "Dung, Dung, Dung, Dung, Doko" n'apparaît pas, supposons que "96" soit renvoyé. Ceci est considéré comme un problème d'identification de 97 classes qui identifie les classes de «0» à «96». Dans ce cas, il existe un motif de 2 $ ^ 100 $, il n'est donc pas possible d'entraîner tous les motifs. Il est nécessaire d'apprendre la logique de jugement par apprentissage automatique.
Pour faciliter la gestion dans un réseau neuronal, l'entrée Zundcolist représente "Zun" sous la forme "-1.0", "Doco" est représenté par "1.0" et la sortie est Tutoriel TensorFlow. Il est représenté par un vecteur one-hot comme dans les versions / 0.6.0 / tutorials / mnist / beginners /). En d'autres termes, pour le Zundcolist "Zun, Doco, Dung, Dung, Dung, Dung, Doco, ...", [-1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0 ,. ..] ʻest entraîné comme entrée et
[0.0, 0.0, 1.0, 0.0, ...] ʻest entraîné comme sortie.
Étant donné que le Zund Collist connaît la relation entre l'entrée et la sortie, les données d'apprentissage peuvent être générées indéfiniment. Je suis heureux que la partie qui collecte les données de formation, qui est l'un des points les plus difficiles de l'apprentissage automatique, ait été effacée!
Tout d'abord, créons une fonction make_zundoko_list
qui génère aléatoirement un Zundcolist et une fonctionresolve_zundoko_list
qui le résout.
from random import choice
zun = -1.0
doko = 1.0
zun_length = 4
zundoko_length = zun_length + 1
def make_zundoko_list(length):
return [choice([zun, doko]) for _ in range(length)]
def solve_zundoko_list(zundoko_list, index=0, zun_count=0):
if len(zundoko_list) == 0:
return index - zun_length
elif zun_count >= zun_length and zundoko_list[0] == doko:
return index - zun_length
else:
return solve_zundoko_list(zundoko_list[1:], index + 1, zun_count + 1 if zundoko_list[0] == zun else 0)
Cependant, si vous utilisez un Zund Collist généré aléatoirement comme données d'entraînement, la sortie sera biaisée, ce qui n'est pas bon. Nous voulons nous entraîner uniformément de «0» à «96», donc créons également une fonction «make_solved_zundoko_list» qui spécifie une solution et génère un Zundcolist.
def make_solved_zundoko_list(length, answer):
zundoko_list = make_zundoko_list(length)
while True:
solved_answer = solve_zundoko_list(zundoko_list)
if solved_answer >= answer:
break
zundoko_list[solved_answer] = doko
if answer + zundoko_length <= length:
zundoko_list[answer:answer + zundoko_length] = [zun for _ in range(zun_length)] + [doko]
return zundoko_list
Comme mentionné ci-dessus, la solution doit être convertie en un vecteur one-hot, donc créons également une fonction dense_to_one_hot
pour cela.
def dense_to_one_hot(index, num_classes):
return [(1.0 if i == index else 0.0) for i in range(num_classes)]
Utilisez-le pour préparer les données d'entraînement et les données de test. Supposons qu'il existe 100 000 données d'entraînement et 1 000 données de test.
Notez que nous utilisons make_solved_zundoko_list
pour les données d'entraînement et make_zundoko_list
pour les données de test.
list_length = 100
num_classes = list_length - zundoko_length + 1 + 1
zundoko_lists = [make_solved_zundoko_list(list_length, i % num_classes) for i in range(100000)]
zundoko_answers = [dense_to_one_hot(solve_zundoko_list(z), num_classes) for z in zundoko_lists]
test_zundoko_lists = [make_zundoko_list(list_length) for _ in range(1000)]
test_zundoko_answers = [dense_to_one_hot(solve_zundoko_list(z), num_classes) for z in test_zundoko_lists]
Voici le problème principal. Jusqu'à présent, j'utilise juste Python.
Puisque Zundoko Kiyoshi veut détecter des motifs adjacents («Zun, Dung, Dung, Dung, Doco») dans la liste, __Convolution Neural Network (CNN) __ convient. Le [Second Tutorial] de TensorFlow (https://www.tensorflow.org/versions/r0.7/tutorials/mnist/pros/index.html) est un exemple de CNN, alors écrivez-le comme référence. Allons-y.
CNN est souvent utilisé pour la reconnaissance d'image. Puisque l'image est bidimensionnelle, la [convolution] bidimensionnelle (http://www.clg.niigata-u.ac.jp/~medimg/practice_medical_imaging/imgproc_scion/4filter/index.htm) est effectuée dans la reconnaissance d'image. Cependant, puisque Zund Collist est une chaîne de données unidimensionnelle, il est nécessaire d'effectuer une convolution unidimensionnelle. Le motif que je veux détecter en ce moment est «Dung, Dung, Dung, Dung, Doco», il semble donc bon de le plier avec un noyau de taille 5.
Cependant, j'ai cherché la [Référence API] de TensorFlow (https://www.tensorflow.org/versions/r0.7/api_docs/index.html) et n'ai trouvé aucune fonction pour la convolution unidimensionnelle [^ 1]. ]. Puisqu'il n'y a aucune aide pour cela, le Zund Collist fera «100» x «1», et le noyau fera «remodeler» en «5» x «1» et le traitera comme une valeur bidimensionnelle. Vous pouvez maintenant utiliser la convolution bidimensionnelle conv2d
pour obtenir une pseudo convolution unidimensionnelle.
La partie à plier à partir de la couche d'entrée est la suivante.
x = tf.placeholder(tf.float32, [None, list_length])
x_reshaped = tf.reshape(x, [-1, list_length, 1, 1])
W1 = tf.Variable(tf.truncated_normal([5, 1, 1, 1], stddev=0.1))
b1 = tf.Variable(tf.truncated_normal([1], stddev=0.1))
h1_reshaped = tf.nn.relu(tf.nn.conv2d(x_reshaped, W1, strides=[1, 1, 1, 1], padding='SAME') + b1)
h1 = tf.reshape(h1_reshaped, [-1, list_length])
On s'attend à ce que cette convolution détecte les modèles «excréments, excréments, excréments, excréments, doco». Normalement, CNN effectue une mise en commun après la convolution, mais cette fois, elle ne se regroupe pas car le résultat de la convolution indiquera directement si "bouse, bouse, bouse, bouse, doco" a été détectée. ..
Au fait, même si vous pouvez détecter le motif de "Zun, Dung, Dung, Dung, Doco" en pliant, cela ne suffit pas. Par exemple, si l'entrée «Dung, Doco, Dung, Dung, Dung, Dung, Doco, Dung, Dung, Dung, Dung, Doco, ...» est passée, «7» est détecté en plus de «2». On s'attend à ce que cela finisse. Cependant, il est mécaniquement difficile de détecter uniquement "2" par convolution. Alors ajoutons une autre couche et débarrassons-nous des valeurs comme «7».
Comme il est difficile d'exprimer un tel calcul par convolution, la couche suivante est entièrement connectée. Cependant, le calcul consistant à ne supprimer que «7» en laissant «2» semble compliqué. Une connexion complète (multipliée simplement par une matrice) a-t-elle autant de pouvoir expressif? Si vous ne pouvez pas exprimer le calcul que vous souhaitez réaliser avec cette formule, vous ne pouvez pas l'entraîner.
Avec «2» et «7» détectés («[0, 0, 1, 0, 0, 0, 0, 1, 0, ...]»), quel type de matrice doit être appliqué à «2 Est-il possible d'obtenir le résultat de la détection uniquement (
[0, 0, 1, 0, 0, 0, 0, 0, ...] `)? Par exemple, si vous appliquez la matrice suivante (à partir de la droite), il semble que vous puissiez passer aux valeurs négatives sauf pour le premier "Zun, Zun, Zun, Zun, Doko" détecté. Si vous pouvez séparer les valeurs que vous souhaitez détecter des valeurs que vous ne souhaitez pas, la fonction d'activation fera le reste.
\left(\begin{matrix}
1.0 & -1.0 & -1.0 & \cdots \\
0.0 & 1.0 & -1.0 & \cdots \\
0.0 & 0.0 & 1.0 & \cdots \\
\vdots & \vdots & \vdots & \ddots \\
\end{matrix}\right)
Maintenant, nous savons que la simple multiplication de la matrice est suffisamment puissante pour supprimer des valeurs comme «7». L'expressivité ne signifie pas que l'apprentissage réussira, mais appliquons une matrice de «100» × «97» (y compris le biais et «softmax») à la sortie. Le décrochage est omis car il semble incompatible en raison de la nature du problème (en raison de la configuration du réseau neuronal?).
W2 = tf.Variable(tf.truncated_normal([list_length, num_classes], stddev=0.1))
b2 = tf.Variable(tf.truncated_normal([num_classes], stddev=0.1))
y = tf.nn.softmax(tf.matmul(h1, W2) + b2)
Après cela, comme dans le didacticiel, optimisez pour minimiser l'entropie croisée et trouvez «W1», «b1», «W2», «b2».
y_ = tf.placeholder(tf.float32, [None, num_classes])
cross_entropy = -tf.reduce_sum(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
train_step = tf.train.GradientDescentOptimizer(1e-5).minimize(cross_entropy)
answer = tf.argmax(y, 1)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(1000):
sess.run(train_step, feed_dict={x: zundoko_lists, y_: zundoko_answers})
J'ai modifié divers détails du didacticiel, tels que l'entraînement avec toutes les données d'entraînement à chaque étape et l'utilisation de GradientDescentOptimizer
. La «cross_entropy» a été corrigée en se référant à cette Réponse de StackOverflow.
Enfin, affichez Zundokokiyoshi sur le réseau neuronal entraîné et vous avez terminé.
zundoko_list = make_zundoko_list(list_length)
zundoko_answer = sess.run(answer, feed_dict={x: [zundoko_list]})[0]
zundoko_string_list = ['Bouse' if zundoko == zun else 'Doco' for zundoko in zundoko_list]
zundoko_string_list = zundoko_string_list[:min(zundoko_answer + zundoko_length, len(zundoko_string_list))]
for zundoko_string in zundoko_string_list:
print(zundoko_string)
if zundoko_answer + zundoko_length == len(zundoko_string_list):
print('Ki yo shi!')
Bouse
Doco
Bouse
Doco
Doco
Bouse
Doco
Doco
Doco
Bouse
Bouse
Doco
Bouse
Doco
Doco
Doco
Doco
Bouse
Bouse
Bouse
Bouse
Doco
Ki yo shi!
Oh! !! Il était affiché correctement!
Cependant, après avoir essayé plusieurs fois avec des paramètres légèrement différents, le taux de réponse correct dans les données de test n'était que de 98% au maximum. Avant de le faire, je pensais que je pourrais atteindre 100% avec une logique aussi simple, mais c'est assez difficile. Dans la configuration actuelle, je pense qu'il est difficile si toutes les combinaisons (l'indice de la solution et l'index suivant qui est ignoré) ne sont pas incluses dans les données d'entraînement, donc je peux exprimer des fonctionnalités plus abstraites telles que l'ajout de couches. Alors ça a peut-être été bon.
[^ 1]: Je viens de faire une recherche rapide sur conv
, alors peut-être que j'ai raté quelque chose.
Recommended Posts