Letztes Mal (1/3): https://qiita.com/tfull_tf/items/6015bee4af7d48176736 Nächstes Mal (3/3): https://qiita.com/tfull_tf/items/d9fe3ab6c1e47d1b2e1e
Code: https://github.com/tfull/character_recognition
Bei der Erstellung des Kana-Erkennungssystems haben wir zunächst ein Modell mit CNN erstellt und bestätigt, wie genau es mit MNIST sein würde. Bereiten Sie als Nächstes die Bilddaten von Kana vor, erstellen Sie das Modell auf die gleiche Weise und verbessern Sie das Modell.
Ich kann mir keine Bilddaten für Kana vorstellen, daher werde ich sie automatisch erstellen. Derzeit scheinen einige Datensätze für die Öffentlichkeit zugänglich zu sein.
Verwenden Sie ImageMagick zur automatischen Generierung. Sie können dem Bild mit dem Befehl convert Text hinzufügen. Erstellen Sie also zuerst ein schwarzes Bild und schreiben Sie dann nur einen weißen Text darauf.
Um mehrere Bilder für ein Zeichen vorzubereiten, haben wir eine Methode zum Erhöhen der Daten vorbereitet.
Wenn Sie in verschiedenen Schriftarten schreiben, können Sie anhand der Anzahl der Schriftarten unterschiedliche Bilder mit denselben Zeichen erstellen.
Sie können die Schriftart mit dem folgenden Befehl anzeigen. Nehmen Sie also die Schriftart, die als verwendbar erscheint.
convert -list font
Eine Sache, die Sie beachten sollten, ist, dass nicht alle Japanisch unterstützen. Selbst wenn Sie versuchen, Kana auszugeben, wird möglicherweise nichts ausgeschrieben.
Mac OS 10.15, an dem ich hauptsächlich arbeitete, hatte keine Schriftart, die gut aussah, also habe ich das Bild unter Ubuntu generiert. Die folgenden Schriftarten waren von Anfang an enthalten, daher habe ich mich für sie entschieden.
font_list = [
"Noto-Sans-CJK-JP-Thin",
"Noto-Sans-CJK-JP-Medium",
"Noto-Serif-CJK-JP"
]
Sie können verschiedene Bilder erzeugen, indem Sie Volltext auf den Bildschirm schreiben oder ein wenig konservativ schreiben. Dieses Mal schrieb ich die Briefe, während ich die Größe schrittweise von etwa der Hälfte auf die Größe knapp darunter erhöhte.
Wenn Sie kleine Buchstaben schreiben, können Sie oben, unten, links und rechts Leerzeichen erstellen, sodass Sie die Technik zum vertikalen und horizontalen Verschieben der Buchstaben verwenden können. Wenn Sie beispielsweise daran denken, Blank / 2 und Blank / 3 nach oben, unten, links und rechts zu verschieben, können Sie 5 x 5 verschiedene Bilder generieren.
Sie können die Zeichen mit konvertieren drehen. Sie können die Anzahl der Bilder erhöhen, indem Sie sie leicht im oder gegen den Uhrzeigersinn drehen.
Sie können die Anzahl der Bilder erhöhen, indem Sie ein unscharfes Bild vorbereiten. Wie steht es jedoch mit einem Bild, bei dem die Hälfte des Bildes unscharf ist? Ich habe es nicht getan, weil ich dachte. Eine ausreichende Anzahl von Bildern kann mit 1 bis 4 gesichert werden.
Durch Hinzufügen von Rauschen wie kleinen Punkten zum Bild kann das Bild nicht nur vergrößert, sondern auch widerstandsfähiger gegen Rauschen werden. Ich habe es nicht getan, weil ich keinen einfachen Weg gefunden habe, um nette Geräusche hinzuzufügen, aber es könnte eine gute zukünftige Aufgabe sein.
Erzeugt ein Bild mit Zeichen durch Kombination von 1 bis 4 (Multiplikation). Bei 256 Pixel in Höhe und Breite wurden mehr als 4000 Bilder für jedes Zeichen erhalten. Sie können die Anzahl der Blätter ändern, indem Sie mit den verschiedenen in der Methode verwendeten Parametern spielen. Es gibt 169 Arten von Hiragana (0x3041 ~ 0x3093) und Katakana (0x30A1 ~ 0x30F6), daher ist die Kapazität ziemlich groß.
data_directory = "/path/to/data"
image_size = 256
#Ein schwarzes Bild erstellen
def make_template():
res = subprocess.call([
"convert",
"-size", "{s}x{s}".format(s = image_size),
"xc:black",
"{}/tmp.png ".format(data_directory)
])
#Erstellen Sie ein Bild mit weißen Zeichen
def generate(path, font, pointsize, character, rotation, dx, dy):
res = subprocess.call([
"convert",
"-gravity", "Center",
"-font", font,
"-pointsize", str(pointsize),
"-fill", "White",
"-annotate", format_t(rotation, dx, dy), character,
"{}/tmp.png ".format(data_directory), path
])
#Formatfunktion verschieben
def format_t(rotation, x, y):
xstr = "+" + str(x) if x >= 0 else str(x)
ystr = "+" + str(y) if y >= 0 else str(y)
return "{r}x{r}{x}{y}".format(r = rotation, x = xstr, y = ystr)
Erstellen Sie zum ersten Mal nur einmal ein schwarzes Bild und erstellen Sie ein Bild mit weißen Zeichen, während Sie die Parameter Schriftart, Punktgröße, Zeichen, Drehung, dx und dy in einer Schleife ändern.
Nachdem wir das Image haben, werden wir das Modell auf die gleiche Weise wie MNIST erstellen, aber es hat von Anfang an nicht funktioniert. Der Wert des Cross-Entropy-Fehlers ist für jeden Stapel gleich. Wenn Sie den Wert in der Ebene beim Training als Debug beobachten, enthält der Absolutwert einen großen Wert wie Hunderte oder Tausende und die Ausgabe War immer das gleiche. Aus diesem Grund konnten wir die Stapelnormalisierung einfügen, um die Genauigkeit erheblich zu verbessern.
import torch.nn as nn
class Model(nn.Module):
def __init__(self, image_size, output):
super(Model, self).__init__()
n = ((image_size - 4) // 2 - 4) // 2
self.conv1 = nn.Conv2d(1, 4, 5)
self.relu1 = nn.ReLU()
self.normal1 = nn.BatchNorm2d(4)
self.pool1 = nn.MaxPool2d(2, 2)
self.dropout1 = nn.Dropout2d(0.3)
self.conv2 = nn.Conv2d(4, 16, 5)
self.relu2 = nn.ReLU()
self.normal2 = nn.BatchNorm2d(16)
self.pool2 = nn.MaxPool2d(2, 2)
self.dropout2 = nn.Dropout2d(0.3)
self.flatten = nn.Flatten()
self.linear1 = nn.Linear(n * n * 16, 1024)
self.relu3 = nn.ReLU()
self.normal3 = nn.BatchNorm1d(1024)
self.dropout3 = nn.Dropout(0.3)
self.linear2 = nn.Linear(1024, 256)
self.relu4 = nn.ReLU()
self.normal4 = nn.BatchNorm1d(256)
self.dropout4 = nn.Dropout(0.3)
self.linear3 = nn.Linear(256, output)
self.softmax = nn.Softmax(dim = 1)
def forward(self, x):
x = self.conv1(x)
x = self.relu1(x)
x = self.normal1(x)
x = self.pool1(x)
x = self.dropout1(x)
x = self.conv2(x)
x = self.relu2(x)
x = self.normal2(x)
x = self.pool2(x)
x = self.dropout2(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.relu3(x)
x = self.normal3(x)
x = self.dropout3(x)
x = self.linear2(x)
x = self.relu4(x)
x = self.normal4(x)
x = self.dropout4(x)
x = self.linear3(x)
x = self.softmax(x)
return x
Grundsätzlich werden wir nach dem gleichen Verfahren wie in MNIST trainieren. Ich habe Cross Entropy Loss, Adam, verwendet (Lernrate = 0,001).
Da das Bild beim Ändern der Parameter in einer Schleife erzeugt wurde, vermeiden Sie es, da die Daten verzerrt zu sein scheinen, wenn sie in der richtigen Reihenfolge trainiert werden. Da ich jeden Charakter gleichmäßig lernen möchte, möchte ich sie auch der Reihe nach lernen.
Wenn Sie trainieren, während Sie das Bild in einer Schleife lesen, lernen Sie ein Blatt in einem Stapel. Es gibt jedoch viele Bilddaten. Wenn Sie also alle auf einmal lesen, geht Ihnen möglicherweise der Speicher aus. Um beides zu vermeiden, entschied ich mich, Yield zu verwenden, um die Daten in Blöcken zu lesen.
# a1,Ermitteln Sie die Doppelschleife von a2 anhand der Anzahl der Chunks
def double_range(a1, a2, chunk = 100):
records = []
for x1 in a1:
for x2 in a2:
records.append((x1, x2))
if len(records) >= chunk:
yield records
records = []
if len(records) > 0:
yield records
Eine Funktion, die zwei Arrays ergibt und die in einer Doppelschleife erhaltenen Paare um die Anzahl der Chunks zurückgibt. Geben Sie dies für weiter.
Pseudocode
for indices in double_range("1~Gemischte Bildnummern bis zu N.", "Nummer, die dem Charakter zugewiesen ist(0~168)"):
inputs = []
for i_character, i_image in indices:
inputs.append("i_Zeichen i des zweiten Zeichens_Bild Lädt das erste Bild")
model.train(inputs) #Lernen
Auf diese Weise kann die Speichernutzung reduziert werden, indem eine Schleife ausgeführt wird, die Bilder für die Stapelgröße liest und trainiert.
4236 [Blätter / Zeichen] ✕ 169 [Zeichen] Ich habe das Experiment gestartet, nachdem ich die Bilddaten erstellt hatte. Unter Verwendung von 5% der Gesamtzahl als Testdaten haben wir mit 2 Epochen trainiert und die korrekte Antwortrate der Testdaten gemessen. Sie betrug etwa 71,4%. Zuerst habe ich einen Fehler im Programm gemacht und 4236 anstelle von 169 gewählt, aber zu dieser Zeit war es ein Rätsel, dass ungefähr 80% aus waren. Ich möchte die Leistung ein wenig verbessern, aber es scheint, dass ich ein Erkennungssystem erstellen und es vorerst ausführen kann.
Recommended Posts