Nach dem Versuch des maschinellen Lernens besteht das unmittelbare Problem darin, einen Datensatz für das Training vorzubereiten. Sie werden zuerst mit MINIST oder etwas anderem testen, dann bestenfalls logische Operationen lernen und sich dann fragen, was als nächstes zu tun ist. Selbst wenn Sie Bilder klassifizieren möchten, macht es keinen Spaß, dies mit einem abgelegten Datensatz zu tun. Wenn Sie Ihre eigenen Bilder vorbereiten, ist das Beschriften dieser Bilder tödlich mühsam.
So kann ich die Daten selbst angemessen aufbereiten und die Kuchen-Knetumwandlung und die Klassifizierung von Pseudozufallszahlen als nicht triviale Klassifizierung versuchen.
Die Quelle ist hier. https://github.com/kaityo256/chainer_bakermap
Die Version von Chainer ist 2.0.1.
Hinweis: Dieser Artikel wurde von einem Amateur für maschinelles Lernen verfasst.
Die Ziele sind wie folgt.
Die Daten werden als eindimensionale Folge von Zahlen $ \ {v_n } $ angegeben. Einer wird durch den Python-Standard "random.random ()" angegeben, der andere ist nur der Anfangswert "random.random ()" und danach
v = 3.0 * v - int(3.0*v)
Nachgeben. Dies ist eine sogenannte Kuchen-Knetumwandlung (Bäcker-Karte), die auf den ersten Blick wie eine Zufallszahl aussieht, aber Sie können den Unterschied erkennen, indem Sie $ (v_n, v_ {n + 1}) $ zeichnen.
Erstens gibt es keine bestimmte Struktur bei der Verwendung von Standard-Zufallszahlen.
Auf der anderen Seite ist es bei der Umwandlung von Kuchenkneten sofort offensichtlich.
Wird das neuronale Netz in der Lage sein, den Unterschied zwischen den beiden durch Lernen zu erkennen? Das Problem. Damit ist es einfach, Lehrerdaten zu erstellen, und das Anpassen der Größe ist auch sehr einfach. Genau genommen haben Standard-Zufallszahlen auch Perioden und Strukturen, sollten jedoch im Bereich von 200 überhaupt nicht sichtbar sein, sodass sie in diesem Bereich zufällig aussehen sollten.
Lassen Sie uns vorerst mit dieser Einstellung lernen.
Alle Zahlen wurden entsprechend entschieden.
Ich denke, die erste Barriere (wenn auch nicht so sehr) von Chainer ist die Aufbereitung von Daten. Einzelheiten finden Sie unter Separater Artikel, jedoch kurz
numpy.float32
) ein (dies ist ein Array von Numpy-Objekten).numpy.int32
) an (geben Sie ein Numpy-Objekt an).Stellen Sie dann den Eingang und den Ausgang auf "x" bzw. "y" ein.
dataset = chainer.datasets.TupleDataset(x,y)
In diesem Fall handelt es sich um ein Datensatzformat, das Chainer essen kann.
Es ist nicht sehr lang, also werde ich ein Modul veröffentlichen, das Daten erstellt.
data.py
import random
import numpy as np
import chainer
def make_baker(n):
a = []
x = random.random()
for i in range(n):
x = x * 3.0
x = x - int(x)
a.append(x)
return a
def make_random(n):
a = []
for i in range(n):
a.append(random.random())
return a
def make_data(ndata,units):
data = []
for i in range(ndata):
a = make_baker(units)
data.append([a,0])
for i in range(ndata):
a = make_random(units)
data.append([a,1])
return data
def make_dataset(ndata,units):
data = make_data(ndata,units)
random.shuffle(data)
n = len(data)
xn = len(data[0][0])
x = np.empty((n,xn),dtype=np.float32)
y = np.empty(n,dtype=np.int32)
for i in range(n):
x[i] = np.asarray(data[i][0])
y[i] = data[i][1]
return chainer.datasets.TupleDataset(x,y)
def main():
dataset = make_dataset(2,3)
print(dataset)
if __name__ == '__main__':
random.seed(1)
np.random.seed(1)
main()
Ich denke nicht, dass es schwierig ist, den Inhalt zu verstehen. Erstellen Sie einmal ein "Daten", das die (Eingabe-, Ausgabe-) Paare auflistet, konvertieren Sie es in das Numpy-Format und machen Sie es zu einem "Datensatz". später
import data
units = 200
ndata = 10000
dataset = data.make_dataset(ndata,units)
In diesem Fall erhalten Sie einen Datensatz, von dem Chainer profitieren kann. Wenn Sie nur die Funktion make_data
richtig umschreiben, sollten Sie in der Lage sein, alle Daten zu verarbeiten.
Zuerst habe ich eine Klasse gemacht, die Chainers Modell richtig verpackt hat. So was.
model.py
import chainer
import chainer.functions as F
import chainer.links as L
import collections
import struct
from chainer import training
from chainer.training import extensions
class MLP(chainer.Chain):
def __init__(self, n_units, n_out):
super(MLP, self).__init__(
l1 = L.Linear(None, n_units),
l2 = L.Linear(None, n_out)
)
def __call__(self, x):
return self.l2(F.relu(self.l1(x)))
class Model:
def __init__(self,n_unit):
self.unit = n_unit
self.model = L.Classifier(MLP(n_unit, 2))
def load(self,filename):
chainer.serializers.load_npz(filename, self.model)
def save(self,filename):
chainer.serializers.save_npz(filename, self.model)
def predictor(self, x):
return self.model.predictor(x)
def get_model(self):
return self.model
def export(self,filename):
p = self.model.predictor
l1W = p.l1.W.data
l1b = p.l1.b.data
l2W = p.l2.W.data
l2b = p.l2.b.data
d = bytearray()
for v in l1W.reshape(l1W.size):
d += struct.pack('f',v)
for v in l1b:
d += struct.pack('f',v)
for v in l2W.reshape(l2W.size):
d += struct.pack('f',v)
for v in l2b:
d += struct.pack('f',v)
open(filename,'w').write(d)
Es ist in einem Durcheinander geschrieben, aber als Verwendung,
python
m = Model(units) #Erstellen Sie eine Wrapper-Klasse für das Modell
model = m.get_model() #Modellobjekt abrufen(Wird für das Training verwendet)
m.save("baker.model") #Modell speichern(Serialisieren)
m.load("baker.model") #Modell laden(Deserialisieren)
m.export("baker.dat") # C++Exportieren für
Benutzen als.
Wenn Sie das Modell der Modellklasse erhalten, ist der Rest das Chainer-Beispiel, so wie es ist. Ich denke, dass es kein besonderes Problem gibt. Wenn Sie sich vorerst train.py ansehen, können Sie sehen, dass es so ist, wie es ist. Das Modell wird jedoch nach dem Lernen serialisiert.
So sieht die test.py
aus, die das Modell nach dem Training testet.
test.py
from model import Model
import numpy as np
import random
import data
import math
def main():
ndata = 1000
unit = 200
model = Model(unit)
model.load("baker.model")
d = data.make_data(ndata,unit)
x = np.array([v[0] for v in d], dtype=np.float32)
y = model.predictor(x).data
r = [np.argmax(v) for v in y]
bs = sum(r[:ndata])
rs = sum(r[ndata:])
print("Check Baker")
print "Success/Fail",ndata-bs,"/",bs
print("Check Random")
print "Success/Fail",rs,"/",ndata-rs
def test():
unit = 200
model = Model(unit)
model.load("baker.model")
a = []
for i in range(unit):
a.append(0.5)
x = np.array([a], dtype=np.float32)
y = model.predictor(x).data
print(y)
if __name__ == '__main__':
random.seed(2)
np.random.seed(2)
test()
main()
Ich habe gerade eine Instanz der Model-Klasse erstellt, sie deserialisiert und getestet [^ 1]. Das Ausführungsergebnis sieht so aus.
[^ 1]: Rückblickend sind die Funktionsnamen wie "main" und "test" nicht gut, und ich hätte jedem eine Instanz der Model-Klasse übergeben sollen ...
$ python test.py
[[-0.84465003 0.10021734]]
Check Baker
Success/Fail 929 / 71
Check Random
Success/Fail 913 / 87
Der Erste
[[-0.84465003 0.10021734]]
Gibt das Gewicht aus, wenn die Daten "200 Stück sind alle 0,5" eingegeben werden. Dies bedeutet, dass das als 0 erkannte Gewicht, dh die Kuchen-Knetumwandlung, "-0,84465003" beträgt und das als Zufallszahl erkannte Gewicht "0,10021734" beträgt. Mit anderen Worten, wenn eine Konstante gespeist wird, wird sie als zufällig erkannt [^ 2]. Dies wird später verwendet, um zu überprüfen, ob das in C ++ geladene Modell ordnungsgemäß funktioniert.
[^ 2]: Wenn das Verhältnis der verdreifachten benachbarten Zahlen hoch ist, wird es wahrscheinlich als Kuchenknetumwandlung erkannt, daher denke ich, dass es in Ordnung ist zu erkennen, dass die Konstante keine Kuchenknetumwandlung ist.
Nachdem
Check Baker
Success/Fail 929 / 71
Die Ausgabe ist, dass beim Verzehr von 1000 Sätzen von Kuchen-Knet-Konvertierungen 929 Sätze als Kuchen-Knet-Konvertierungen erkannt wurden und 71 Sätze fälschlicherweise als zufällig erkannt wurden.
Nachdem
Check Random
Success/Fail 913 / 87
Bedeutet, dass 1000 Sätze von Zufallszahlen gegessen wurden und 913 Sätze korrekt als Zufallszahlen erkannt wurden.
Informationen zum Exportieren und Importieren nach C ++ finden Sie unter Separater Artikel. Das Exportieren bleibt der Wrapper-Klasse überlassen, daher ist es einfach.
export.py
from model import Model
def main():
unit = 200
model = Model(unit)
model.load("baker.model")
model.export("baker.dat")
if __name__ == '__main__':
main()
Es liest "baker.model" und spuckt "baker.dat" aus.
Es ist einfach zu importieren, aber lassen Sie es uns später der Einfachheit halber klassifizieren. So was.
model.hpp
#pragma once
#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>
#include <algorithm>
//------------------------------------------------------------------------
typedef std::vector<float> vf;
//------------------------------------------------------------------------
class Link {
private:
vf W;
vf b;
float relu(float x) {
return (x > 0) ? x : 0;
}
const int n_in, n_out;
public:
Link(int in, int out) : n_in(in), n_out(out) {
W.resize(n_in * n_out);
b.resize(n_out);
}
void read(std::ifstream &ifs) {
ifs.read((char*)W.data(), sizeof(float)*n_in * n_out);
ifs.read((char*)b.data(), sizeof(float)*n_out);
}
vf get(vf x) {
vf y(n_out);
for (int i = 0; i < n_out; i++) {
y[i] = 0.0;
for (int j = 0; j < n_in; j++) {
y[i] += W[i * n_in + j] * x[j];
}
y[i] += b[i];
}
return y;
}
vf get_relu(vf x) {
vf y = get(x);
for (int i = 0; i < n_out; i++) {
y[i] = relu(y[i]);
}
return y;
}
};
//------------------------------------------------------------------------
class Model {
private:
Link l1, l2;
public:
const int n_in, n_out;
Model(int in, int n_units, int out):
n_in(in), n_out(out),
l1(in, n_units), l2(n_units, out) {
}
void load(const char* filename) {
std::ifstream ifs(filename);
l1.read(ifs);
l2.read(ifs);
}
vf predict(vf &x) {
return l2.get(l1.get_relu(x));
}
int argmax(vf &x) {
vf y = predict(x);
auto it = std::max_element(y.begin(), y.end());
auto index = std::distance(y.begin(), it);
return index;
}
};
//------------------------------------------------------------------------
mit diesem,
#include "model.hpp"
int
main(void){
const int n_in = 200;
const int n_units = 200;
const int n_out = 2;
Model model(n_in, n_units, n_out);
model.load("baker.dat");
}
Das Modell kann als gelesen werden.
Versuchen Sie zunächst, dasselbe zu füttern und genau dasselbe Gewicht auszuspucken.
Schreiben wir diesen Code.
void
test(Model &model) {
vf x;
for (int i = 0; i < model.n_in; i++) {
x.push_back(0.5);
}
vf y = model.predict(x);
printf("%f %f\n", y[0], y[1]);
}
Jedoch,
typedef std::vector<float> vf;
Ist. Das Ausführungsergebnis ist
-0.844650 0.100217
Es stellt sich heraus, dass es dem Ergebnis von Python richtig entspricht.
Darüber hinaus wird auch die korrekte Antwortrate untersucht, wenn die Kuchen-Knetumwandlung und Zufallszahlen eingegeben werden.
int
test_baker(Model &model) {
static std::mt19937 mt;
std::uniform_real_distribution<float> ud(0.0, 1.0);
vf x;
float v = ud(mt);
for (int i = 0; i < model.n_in; i++) {
x.push_back(v);
v = v * 3.0;
v = v - int(v);
}
return model.argmax(x);
}
//------------------------------------------------------------------------
int
test_random(Model &model) {
static std::mt19937 mt;
std::uniform_real_distribution<float> ud(0.0, 1.0);
vf x;
for (int i = 0; i < model.n_in; i++) {
x.push_back(ud(mt));
}
return model.argmax(x);
}
//------------------------------------------------------------------------
int
main(void) {
const int n_in = 200;
const int n_units = 200;
const int n_out = 2;
Model model(n_in, n_units, n_out);
model.load("baker.dat");
test(model);
const int TOTAL = 1000;
int bn = 0;
for (int i = 0; i < TOTAL; i++) {
bn += test_baker(model);
}
std::cout << "Check Baker" << std::endl;
std::cout << "Success/Fail:" << (TOTAL - bn) << "/" << bn << std::endl;
int rn = 0;
for (int i = 0; i < TOTAL; i++) {
rn += test_random(model);
}
std::cout << "Check Random" << std::endl;
std::cout << "Success/Fail:" << rn << "/" << (TOTAL - rn) << std::endl;
}
Das Ergebnis jeder Ausführung ist wie folgt.
Check Baker
Success/Fail:940/60
Check Random
Success/Fail:923/77
Es scheint, dass die richtige Antwortrate fast gleich ist.
Mit Chainer habe ich einen Test versucht, um zwischen der durch die Kuchenknetumwandlung erhaltenen Zahlenfolge und der Standardzufallszahl zu unterscheiden. Ich dachte, es wäre einfacher zu unterscheiden, aber mit 3 Schichten und 200 Einheiten / Schicht ist es so etwas? Vorläufig konnte ich mit Python → mithilfe von C ++ einen Lernfluss erstellen, daher möchte ich ihn auf verschiedene Arten anwenden.
Es tut mir leid für den Artikel, den ich geschrieben habe.