(Ergänzung) Es ist nicht TensorFlow, aber ich habe festgestellt, dass es von Chainer besser gelöst wurde. Lernen Sie Zundokokiyoshi mit LSTM
Ich habe das Gefühl, den Boom verpasst zu haben, aber seit ich kürzlich TensorFlow berührt habe, habe ich TensorFlow verwendet, um __convolutional neuronales Netzwerk (CNN, ConvNet: Convolutional neuronales Netzwerk) __ zu verwenden. Ich habe versucht, Zundokokiyoshi damit zu lösen. Sie müssen nicht einmal mehr Ihre menschliche Intelligenz ausgeben, um Zundokokiyoshi zu lösen.
Der gesamte Code ist hier.
Zuerst müssen wir darüber nachdenken, was das neuronale Netzwerk für Zundokokiyoshi lösen kann.
Sie können beispielsweise 5 Elemente aus einem Zundcolist ausschneiden (eine Liste, in der "Zun" und "Doko" zufällig gespeichert sind) und sie bestimmen lassen, ob es sich um "Zun, Dung, Dung, Dung, Doco" handelt. Es ist zu einfach, ein Problem einzurichten, und es ist nicht interessant. Es gibt nur $ 2 ^ 5 = 32 $ Muster, ohne dass ein neuronales Netzwerk aufgerufen werden muss. Es reicht also aus, alle Muster zu lernen, anstatt maschinell zu lernen.
Trotzdem konnte ich mir keinen guten Weg vorstellen, einen unendlichen Zund Collist als Eingabe für ein neuronales Netzwerk zu übergeben. Deshalb werde ich einen Zund Collist mit 100 Elementen übergeben und einen Index zurückgeben, in dem das erste "Zun" von "Zun, Dung, Dung, Dung, Doco" zum ersten Mal erscheint. Die richtige Antwort ist beispielsweise, "2" für einen Zund Collist zurückzugeben, der "Dung, Doco, Dung, Dung, Dung, Dung, Doco, ..." sagt. Da die Anzahl der Elemente 100 beträgt, kann es Lösungen von "0" bis "95" geben. Wenn "Dung, Dung, Dung, Dung, Doko" nicht angezeigt wird, nehmen wir an, dass "96" zurückgegeben wird. Dies wird als 97-Klassenidentifikationsproblem angesehen, das Klassen von "0" bis "96" identifiziert. In diesem Fall gibt es ein Muster von $ 2 ^ 100 $, sodass nicht alle Muster trainiert werden können. Es ist notwendig, die Beurteilungslogik durch maschinelles Lernen zu lernen.
Um die Handhabung in einem neuronalen Netzwerk zu vereinfachen, steht die Eingabe Zundcolist für "Zun" als "-1,0", "Doco" für "1,0" und die Ausgabe für TensorFlow Tutorial. Es wird durch einen One-Hot-Vektor dargestellt, wie dies in den Versionen / 0.6.0 / tutorials / mnist / beginners /) erfolgt. Mit anderen Worten, für den Zundcolisten "Zun, Doco, Dung, Dung, Dung, Dung, Doco, ...", [-1,0, 1,0, -1,0, -1,0, -1,0, -1,0, 1,0,. ..]
wird als Eingabe trainiert und [0.0, 0.0, 1.0, 0.0, ...]
wird als Ausgabe trainiert.
Da der Zund Collist die Beziehung zwischen Eingabe und Ausgabe kennt, können Trainingsdaten auf unbestimmte Zeit generiert werden. Ich bin froh, dass der Teil, der Trainingsdaten sammelt, was einer der schwierigsten Punkte beim maschinellen Lernen ist, gelöscht wurde!
Zuerst erstellen wir eine Funktion make_zundoko_list
, die zufällig eine Zundcolist generiert, und eine Funktion remove_zundoko_list
, die sie löst.
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)
Wenn Sie jedoch einen zufällig generierten Zund Collist als Trainingsdaten verwenden, wird die Ausgabe verzerrt, was nicht gut ist. Wir wollen gleichmäßig von "0" bis "96" trainieren, also erstellen wir auch eine Funktion "make_solved_zundoko_list", die eine Lösung angibt und eine Zundcolist generiert.
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
Wie oben erwähnt, muss die Lösung in einen One-Hot-Vektor konvertiert werden. Erstellen wir daher auch eine Funktion dens_to_one_hot
.
def dense_to_one_hot(index, num_classes):
return [(1.0 if i == index else 0.0) for i in range(num_classes)]
Verwenden Sie diese Option, um Trainingsdaten und Testdaten vorzubereiten. Nehmen wir an, es gibt 100.000 Trainingsdaten und 1000 Testdaten.
Beachten Sie, dass wir make_solved_zundoko_list
für Trainingsdaten und make_zundoko_list
für Testdaten verwenden.
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]
Hier ist das Hauptproblem. Bis zu diesem Punkt verwende ich nur Python.
Da Zundoko Kiyoshi benachbarte Muster ("Zun, Dung, Dung, Dung, Doco") in der Liste erkennen möchte, ist __Convolution Neural Network (CNN) __ geeignet. TensorFlows Zweites Tutorial ist ein Beispiel für CNN. Schreiben Sie es daher als Referenz. Lass uns gehen.
CNN wird häufig zur Bilderkennung verwendet. Da das Bild zweidimensional ist, wird bei der Bilderkennung eine zweidimensionale Faltung durchgeführt. Da der Zund Corist jedoch eine eindimensionale Datenzeichenfolge ist, muss eine eindimensionale Faltung durchgeführt werden. Das Muster, das ich jetzt erkennen möchte, ist "Dung, Dung, Dung, Dung, Doco", daher scheint es gut, es mit einem Kern der Größe 5 zu falten.
Ich suchte jedoch in TensorFlows API-Referenz und fand keine Funktion für eindimensionale Faltung [^ 1]. ]. Da es keine Hilfe dafür gibt, wird der Zund Collist "100" x "1" machen, und der Kernel wird "umformen" zu "5" x "1" machen und es als zweidimensionalen Wert behandeln. Sie können jetzt die zweidimensionale Faltung "conv2d" verwenden, um eine pseudo-eindimensionale Faltung zu erreichen.
Der Teil, der aus der Eingabeebene gefaltet werden soll, ist wie folgt.
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])
Es wird erwartet, dass diese Faltung "Mist, Mist, Mist, Mist, Doco" -Muster erkennt. Normalerweise sammelt sich CNN nach der Faltung, diesmal jedoch nicht, da das Ergebnis der Faltung direkt anzeigt, ob "Mist, Mist, Mist, Mist, Doco" erkannt wurde. ..
Übrigens, selbst wenn Sie das Muster von "Zun, Dung, Dung, Dung, Doco" durch Falten erkennen können, reicht das nicht aus. Wenn beispielsweise die Eingabe "Mist, Doco, Mist, Mist, Mist, Mist, Mist, Mist, Mist, Mist, Mist, Mist, ..." übergeben wird, wird "7" zusätzlich zu "2" erkannt. Es wird erwartet, dass es enden wird. Es ist jedoch mechanisch schwierig, nur "2" durch Faltung zu erfassen. Fügen wir also eine weitere Ebene hinzu und entfernen Sie Werte wie "7".
Da es schwierig ist, eine solche Berechnung durch Faltung auszudrücken, ist die nächste Schicht vollständig verbunden. Die Berechnung, nur "7" zu entfernen, während "2" verlassen wird, erscheint jedoch kompliziert. Hat die vollständige Verbindung (nur mit einer Matrix multiplizieren) so viel Ausdruckskraft? Wenn Sie die Berechnung, die Sie mit dieser Formel erreichen möchten, nicht ausdrücken können, können Sie sie nicht trainieren.
Wenn "2" und "7" erkannt werden ("[0, 0, 1, 0, 0, 0, 0, 1, 0, ...]"), welche Art von Matrix sollte auf "2" angewendet werden Ist es möglich, das Ergebnis der Erkennung nur "(" [0, 0, 1, 0, 0, 0, 0, 0, ...] ") zu erhalten? Wenn Sie beispielsweise die folgende Matrix (von rechts) anwenden, können Sie anscheinend zu negativen Werten springen, mit Ausnahme des ersten erkannten "Zun, Dung, Dung, Dung, Doco". Wenn Sie die Werte, die Sie erkennen möchten, von den Werten trennen können, die Sie nicht erkennen, erledigt die Aktivierungsfunktion den Rest.
\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)
Jetzt wissen wir, dass das Multiplizieren der Matrix stark genug ist, um Werte wie "7" zu entfernen. Ausdruckskraft bedeutet nicht, dass das Lernen erfolgreich sein wird, aber lassen Sie uns eine Matrix von "100" × "97" (einschließlich Bias und "Softmax") auf die Ausgabe anwenden. Dropout wird weggelassen, da es aufgrund der Art des Problems (aufgrund der Konfiguration des neuronalen Netzwerks?) Inkompatibel zu sein scheint.
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)
Optimieren Sie danach wie im Tutorial, um die Kreuzentropie zu minimieren und "W1", "b1", "W2", "b2" zu finden.
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})
Ich habe verschiedene Details aus dem Tutorial geändert, z. B. das Training mit allen Trainingsdaten bei jedem Schritt und die Verwendung von "GradientDescentOptimizer". In Bezug auf "cross_entropy" habe ich es unter Bezugnahme auf diese Antwort von StackOverflow geändert.
Zeigen Sie schließlich Zundokokiyoshi im trainierten neuronalen Netzwerk an und Sie sind fertig.
zundoko_list = make_zundoko_list(list_length)
zundoko_answer = sess.run(answer, feed_dict={x: [zundoko_list]})[0]
zundoko_string_list = ['Dung' 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!')
Dung
Doco
Dung
Doco
Doco
Dung
Doco
Doco
Doco
Dung
Dung
Doco
Dung
Doco
Doco
Doco
Doco
Dung
Dung
Dung
Dung
Doco
Ki yo shi!
Oh! !! Es wurde richtig angezeigt!
Nach mehrmaligem Versuch mit leicht unterschiedlichen Parametern betrug die korrekte Antwortrate in den Testdaten jedoch maximal 98%. Bevor ich es tat, dachte ich, dass ich mit einer so einfachen Logik 100% erreichen könnte, aber es ist ziemlich schwierig. In der aktuellen Konfiguration halte ich es für schwierig, wenn nicht alle Kombinationen (der Index der Lösung und der nachfolgende Index, der ignoriert wird) in den Trainingsdaten enthalten sind, sodass ich abstraktere Funktionen wie das Erhöhen der Anzahl der Ebenen ausdrücken kann. Dann könnte es gut gewesen sein.
[^ 1]: Ich habe gerade eine schnelle Suche nach "conv" durchgeführt, also habe ich vielleicht gerade etwas verpasst.
Recommended Posts