Ich habe ein Github-Repository erstellt, weil es eine große Sache ist, bitte Stern (Bettler) SSAM-SimilaritySearchAppwithMNIST
Wie der Titel schon sagt. Dieses Mal können Sie die Daten leicht abrufen und die Datengröße ist klein. Wenn Sie das MNIST-Bild in das MNIST einfügen, finden Sie ein ähnliches MNIST-Bild. Wenn Sie eine solche App erstellen, können Sie über die ungefähre Nachbarschaftssuche und die Flasche sprechen. Ich werde auch mein eigenes Memo über das Erstellen einer App mit schreiben.
Es ist in Ordnung, sich für einen Namen zu entscheiden, aber ich habe mich verstanden und ihm einen ausgefallenen Namen wie SSAM-SimilaritySearchAppwithMNIST gegeben.
--Verzeichnis für MNIST
Als Nächstes erstellen wir einen Ordner zum Speichern der MNIST-Daten. Andernfalls müssen Sie es jedes Mal herunterladen, was problematisch ist. Erstellen Sie einen Ordner mit einem Namen wie static / mnist \ _data.
――Die aktuelle Verzeichnisstruktur sieht folgendermaßen aus
SSAM-SimilaritySearchAppwithMNIST
└── static
└── mnist_data
Es ist berühmt, also glaube ich nicht, dass ich es mehr erklären muss, aber für diejenigen, die es nicht wissen, ist es ein handgeschriebenes Zahlenbild in der Größe 28x28. Da Daten einfach zu verwenden sind, werden sie häufig zur Bewertung der Genauigkeit von Modellen für maschinelles Lernen verwendet. Da es sich jedoch um saubere Daten handelt, ist die Genauigkeit im Allgemeinen hoch. (Wie dieser Artikel) Ich habe mit MNIST ein Modell mit hoher Genauigkeit erstellt! Bei solchen Artikeln sollten Sie vorsichtig sein. Oh, es ist sicherer, es mit der Haltung zu betrachten, dass es Artikel gibt, die so lebhaft sind wie MNIST. (Dies ist nur eine masochistische Geschichte, also fächere ich andere Artikel, die sich mit MNIST befassen, nicht auf, es ist wahr.)
Ich mache mir ein wenig Sorgen, ob es einfacher zu verstehen ist, ob der Code in kleine Teile unterteilt und einzeln mit Erklärungen geschrieben ist, oder ob es einfacher ist, den Code sofort zu schreiben und später zusätzliche Erklärungen hinzuzufügen. Ich würde gerne in Zukunft ein wenig darüber nachdenken, daher würde ich mich freuen, wenn Sie eine Referenzmeinung haben. (Ich weiß nicht einmal, ob Qiita eine Kommentarfunktion hat.)
Zumindest denke ich vorerst, dass es freundlicher wäre, die diesmal verwendeten Bibliotheken zusammen zu schreiben, also werde ich es tun. Bitte installieren Sie pip gegebenenfalls selbst.
import os
import gzip
import pickle
import numpy as np
import urllib.request
from mnist import MNIST
from annoy import AnnoyIndex
from sklearn.metrics import accuracy_score
Ich werde den Code sofort schreiben. Wie ich oben geschrieben habe, mache ich mir ein wenig Sorgen, ob es sinnvoll ist, den Code in kleine Teile zu teilen und ihn zu schreiben, während Erklärungen einzeln hinzugefügt werden, oder ob es freundlicher ist, alles auf einmal zu schreiben und später zusätzliche Erklärungen hinzuzufügen. Ich habe einige Kommentare sorgfältig geschrieben, daher werde ich sie diesmal alle auf einmal schreiben, aber ich würde gerne hier und da Ihre Meinung hören.
Erstens ist der Code, der MNIST herunterlädt.
def load_mnist():
#Laden Sie MNIST-Daten herunter
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
'train_img':'train-images-idx3-ubyte.gz',
'train_label':'train-labels-idx1-ubyte.gz',
'test_img':'t10k-images-idx3-ubyte.gz',
'test_label':'t10k-labels-idx1-ubyte.gz'
}
#Download-Datei(.gz-Format)
for filename in key_file.values():
file_path = f'./static/mnist_data/{filename}' #Einlesen.gz Dateipfad
if os.path.isfile(file_path.replace('.gz', '')): continue #Überspringen, wenn bereits eine Datei vorhanden ist
urllib.request.urlretrieve(url_base + filename, file_path)
# .Mit gz Dekompression.Gz-Datei löschen
with gzip.open(file_path, mode='rb') as f:
mnist = f.read()
#Entpacken und speichern
with open(file_path.replace('.gz', ''), 'wb') as w:
w.write(mnist)
os.remove(file_path) # .Gz-Datei löschen
#np die Daten von mnist.Lesen und zurück in Form eines Arrays
mndata = MNIST('./static/mnist_data/')
# train
images, labels = mndata.load_training()
train_images, train_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.Das Bild von mnist, das in ein Array konvertiert werden soll, ist 28x28
# test
images, labels = mndata.load_testing()
test_images, test_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.Das Bild von mnist, das in ein Array konvertiert werden soll, ist 28x28
return train_images, train_labels, test_images, test_labels
Wenn Sie den obigen Code ausführen, wird zuerst eine komprimierte Datei namens .gz-Datei von dem Speicherort heruntergeladen, an dem sich die MNIST-Daten befinden, und unter static / mnist_data / abgelegt. (Wenn Sie nicht im Voraus einen Ordner für ./static/mnist_data/ erstellen, wird möglicherweise die Fehlermeldung angezeigt, dass kein Ordner vorhanden ist. Es tut mir leid.) Entpacken Sie dann die .gz-Datei und löschen Sie die .gz-Datei, da Sie sie nicht benötigen. Tatsächlich ist diese dekomprimierte Datei im Binärformat, daher ist die Handhabung schwierig.
from mnist import MNIST
Es scheint, dass wenn Sie eine Bibliothek mit einem dummen Namen verwenden, die Daten für den Zug und die Daten für den Test getrennt und zu einem Array-Typ gemacht werden. Eigentlich ist es das einzige Geheimnis hier, dass ich mir große Sorgen machen und mehrere Stunden hier verbringen muss. Mit diesem Gefühl wurde die Funktion zum Herunterladen von MNIST-Daten abgeschlossen, bevor ich es wusste.
Wenn ich die Definition aus Wikipedia zitiere, ist sie wie folgt geschrieben.
Die Suche nach dem nächsten Nachbarn (Nearest Neighbour Search, NNS) ist eine Art Optimierungsproblem, bei dem der nächstgelegene Punkt im Entfernungsraum oder eine Lösung dafür gefunden wird. Es wird auch als Näherungssuche (Englisch: Näherungssuche), Ähnlichkeitssuche (Englisch: Ähnlichkeitssuche) und Suche nach dem nächsten Punkt (Englisch: Suche nach dem nächsten Punkt) bezeichnet. Das Problem ist, dass wenn es eine Menge von Punkten S im Distanzraum M gibt und es einen Abfragepunkt q ∈ M gibt, wir den Punkt in S finden, der q am nächsten liegt. In vielen Fällen nimmt M einen d-dimensionalen euklidischen Raum an, und die Entfernung wird als euklidische Entfernung oder Manhattan-Entfernung gemessen. Für niedrigdimensionale und hochdimensionale Fälle werden unterschiedliche Algorithmen verwendet. ~ Aus Wikipedia
Wenn dies ins Japanische übersetzt wird, wird geschrieben, dass es sich um einen Algorithmus handelt, der eine Funktion bestimmt, die einen gewissen Grad an Ähnlichkeit misst und darauf basierend Punkte mit hoher Ähnlichkeit findet. Das ist was es bedeutet.
Der diesmal verwendete Algorithmus ist ärgerlich, was als "ungefähre" Suche nach dem nächsten Nachbarn bezeichnet wird. Was ist das "ungefähre"? Tatsächlich verbraucht dieser Nachbarschaftssuchalgorithmus viele Rechenressourcen, wenn versucht wird, richtig zu berechnen. Zum Beispiel wird es an dem Tag schwierig sein, an dem Sie denken, dass die Differenz zwischen allen Pixelwerten am einfachsten berechnet werden sollte. Insbesondere bei Bildern nimmt die Berechnungszeit explosionsartig zu, da es vertikale x horizontale x Anzahl von Kanälen gibt. Dieser MNIST ist immerhin 28x28 und eine Graustufe (Schwarzweißbild), also keine große Sache, aber Papa, heute bin ich begeistert von der Suche nach ähnlichen Bildern von Full HD 1920 x 1080. Papa ist verzweifelt und kann nicht schlafen. Der Berechnungsbetrag Schwester wird Ihnen über die Angst vor einer exponentiellen Explosion erzählen. Es ist interessant und der stärkste Inhalt zum Lernen. Wenn Sie es nicht wissen, werfen Sie einen Blick darauf und weinen Sie über die Besessenheit Ihrer Schwester. "Wie man Fukashigi zählt" Mit deiner Schwester! Zählen wir zusammen!
Um ähnliche Bilder zu finden, verwenden wir eine ungefähre Suchbibliothek für den nächsten Nachbarn namens ärgern. Der Grund, warum ich es benutze, ist, dass ich daran gewöhnt bin und der Code meiner Meinung nach relativ einfach zu lesen ist.
Weitere Informationen zu Algorithmen finden Sie in diesem Blog des ärgerlichen Autors Nächste Nachbarn und Vektormodelle - Teil 2 - Algorithmen und Datenstrukturen -teil-2-wie-man-in-hochdimensionalen-räumen sucht.html) und auf Japanisch [die Spitze der ungefähren Suche nach nächsten Nachbarn](https://speakerdeck.com/matsui_528/jin- si-zui-jin-bang-tan-suo-falsezui-qian-xian? slide = 43) Die Erklärung dieser Folienfreigabe ist leicht zu verstehen. Es ist hilfreich, dies zu lesen, bevor Sie diesen Artikel lesen.
Für diejenigen, die nicht gestört werden möchten, finden Sie hier eine kurze Erklärung, damit Sie die Umgebung von O (logn) mit hoher Geschwindigkeit durchsuchen können, indem Sie den Raum, in dem die Datenpunkte vorhanden sind, rekursiv teilen und einige gegabelte Bäume erstellen. Es ist ein Algorithmus, der gemacht wurde. Ich muss jedoch einen Entscheidungsbaum erstellen.
Tatsächlich vergleicht der Autor von ärger mehrere ungefähre Suchbibliotheken für den nächsten Nachbarn und empfiehlt andere Bibliotheken. Neue ungefähre Benchmarks für den nächsten Nachbarn Wenn Sie wirklich Geschwindigkeit wollen, ist es wahrscheinlich besser, von Facebook erstellte Faiss zu verwenden, aber es scheint, dass es nur von Conda aus installiert werden kann, also habe ich keine Lust, es zu verwenden.
Die Erklärung ist etwas länger. Lassen Sie uns die Nachbarschaftssuche mit ärger sofort versuchen.
def make_annoy_db(train_imgs):
#Verwenden Sie die ungefähre Nachbarschaftssuchbibliothek ärgerlich
#Legen Sie die Form und Metrik der einzugebenden Daten fest und fügen Sie die Daten ein
annoy_db = AnnoyIndex((28*28), metric='euclidean') #MNIST hat eine Größe von 28x28, die die Eingabedaten und die Berechnung der Ähnlichkeit angibt.*Ich schreibe 28
for i, train_img in enumerate(train_imgs):
annoy_db.add_item(i, train_img.flatten()) #Geben Sie den Index und die entsprechenden Daten ein
annoy_db.build(n_trees=10) #Bauen
annoy_db.save('./static/mnist_db.ann') #Speichern Sie die unter statisch erstellte Datenbank
Wird es so aussehen? Ich verwende den Begriff Datenbank, der nicht gerade eine Datenbank ist, nur weil ich vielleicht keine anderen guten und angemessenen Begriffe kenne. Übrigens kann AnnoyIndex nur ein eindimensionales Array zugewiesen werden. Wenn Sie also Bilddaten einfügen möchten, verwenden Sie flatten (), formen Sie sie um oder codieren Sie sie wie folgt fest.
Natürlich wird es einige Fehler geben, da die genaue Ähnlichkeit nicht berechnet wird, nur weil es sich um eine ungefähre Suche nach dem nächsten Nachbarn handelt. (Ärgerlich löst dies, indem mehrere Bäume oder ähnliches verwendet werden. Einzelheiten finden Sie unter der obigen URL.) Lassen Sie uns ein wenig überprüfen, ob es wirklich genau ist.
Glücklicherweise hat MNIST bereits jedes Bild und die entsprechende korrekte Bezeichnung. Bitten Sie sie, ein ähnliches Bild für die Testdaten zu ziehen und zu überprüfen, ob es mit der richtigen Antwort übereinstimmt.
train_imgs, train_lbls, test_imgs, test_lbls = load_mnist()
if not os.path.isfile('./static/mnist_db.ann'):
make_annoy_db(train_imgs) # .Erstellen Sie annoydb, wenn Sie noch keine ann-Dateien haben
annoy_db = AnnoyIndex((28*28), metric='euclidean')
annoy_db.load('./static/mnist_db.ann') #Laden Sie die Datenbank von nerv
#Versuchen Sie, die Genauigkeit zu ermitteln, indem Sie Testdaten eingeben, die Nähe abrufen und mit den tatsächlichen vergleichen
y_pred = [train_lbls[annoy_db.get_nns_by_vector(test_img.flatten(), 1)[0]] for test_img in test_imgs]
score = accuracy_score(test_lbls, y_pred)
print('acc:', score)
#Ausgabe gem: 0.9595
Es kam eine sehr hohe Genauigkeit heraus. Wenn Sie 0,2525 sagen möchten, können Sie mit Nico Kitchen eine Geschichte schreiben, aber die Realität ist nicht so süß. (Was um alles in der Welt rede ich?)
Im Fall eines tatsächlichen Bildes bedeutet nur, dass das richtige Etikett dasselbe ist, nicht, dass es sich um ein wirklich ähnliches Bild handelt (z. B. wenn beide Katzenbilder sind, der Hintergrund jedoch unterschiedlich ist oder wenn es sich um schwarze und weiße Katzen handelt). Das menschliche Gehirn beurteilt nicht, dass es sich um ein ähnliches Bild handelt. Wenn es jedoch auf diesen MNIST beschränkt ist, handelt es sich um einen schönen Datensatz, sodass möglicherweise ähnliche Bilder herauskommen.
Der gesamte Code ist bisher wie folgt.
import os
import gzip
import pickle
import numpy as np
import urllib.request
from mnist import MNIST
from annoy import AnnoyIndex
from sklearn.metrics import accuracy_score
def load_mnist():
#Laden Sie MNIST-Daten herunter
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
'train_img':'train-images-idx3-ubyte.gz',
'train_label':'train-labels-idx1-ubyte.gz',
'test_img':'t10k-images-idx3-ubyte.gz',
'test_label':'t10k-labels-idx1-ubyte.gz'
}
#Download-Datei(.gz-Format)
for filename in key_file.values():
file_path = f'./static/mnist_data/{filename}' #Einlesen.gz Dateipfad
if os.path.isfile(file_path.replace('.gz', '')): continue #Überspringen, wenn bereits eine Datei vorhanden ist
urllib.request.urlretrieve(url_base + filename, file_path)
# .Mit gz Dekompression.Gz-Datei löschen
with gzip.open(file_path, mode='rb') as f:
mnist = f.read()
#Entpacken und speichern
with open(file_path.replace('.gz', ''), 'wb') as w:
w.write(mnist)
os.remove(file_path) # .Gz-Datei löschen
#np die Daten von mnist.Lesen und zurück in Form eines Arrays
mndata = MNIST('./static/mnist_data/')
# train
images, labels = mndata.load_training()
train_images, train_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.Das Bild von mnist, das in ein Array konvertiert werden soll, ist 28x28
# test
images, labels = mndata.load_testing()
test_images, test_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.Das Bild von mnist, das in ein Array konvertiert werden soll, ist 28x28
return train_images, train_labels, test_images, test_labels
def make_annoy_db(train_imgs):
#Verwenden Sie die ungefähre Nachbarschaftssuchbibliothek ärgerlich
#Legen Sie die Form und Metrik der einzugebenden Daten fest und fügen Sie die Daten ein
annoy_db = AnnoyIndex((28*28), metric='euclidean')
for i, train_img in enumerate(train_imgs):
annoy_db.add_item(i, train_img.flatten())
annoy_db.build(n_trees=10) #Bauen
annoy_db.save('./static/mnist_db.ann')
def main():
#mnist Bildladevorgang
train_imgs, train_lbls, test_imgs, test_lbls = load_mnist()
print(train_imgs.shape, train_lbls.shape, test_imgs.shape, test_lbls.shape) #Ich wollte überprüfen, wie viele Daten MNIST hat
if not os.path.isfile('./static/mnist_db.ann'):
make_annoy_db(train_imgs) # .Erstellen Sie annoydb, wenn Sie noch keine ann-Dateien haben
annoy_db = AnnoyIndex((28*28), metric='euclidean')
annoy_db.load('./static/mnist_db.ann') #Laden Sie die Datenbank von nerv
#Versuchen Sie, die Genauigkeit zu ermitteln, indem Sie Testdaten eingeben, die Nähe abrufen und mit den tatsächlichen vergleichen
y_pred = [train_lbls[annoy_db.get_nns_by_vector(test_img.flatten(), 1)[0]] for test_img in test_imgs]
score = accuracy_score(test_lbls, y_pred)
print('acc:', score)
if __name__ == "__main__":
main()
Irgendwie hat sich der Betrag bisher bereits erhöht, also werde ich das nächste Mal weitermachen. Diesmal ging ich zu dem Punkt, an dem ich mit ärger nach ähnlichen Bildern suchen konnte. Erstellen wir beim nächsten Mal eine App, die ähnliche Bilder erzeugt, wenn Sie ein Bild mit Flask auswählen. (Wenn ich nicht gelangweilt oder beschäftigt bin) Weiter: -> Nächstes Mal
Bonus: Es ist mir wirklich egal, aber ich liebe Emacs 'Orgmode und habe angefangen, diesen Artikel im Orgmode zu schreiben, aber leider konnte ich die schwache Kompatibilität nicht bewältigen. Immerhin habe ich es in Markdown geschrieben. Wenn ich alleine spreche, denke ich, dass der Organisationsmodus die stärkste Software zur Dokumentenerstellung ist (Ist Markdown nicht schwierig zu verwenden? Welcher Idiot denkt darüber nach, einen Zeilenumbruch mit zwei Leerzeichen halber Breite zu machen), aber auf jeden Fall Es ist traurig, dass ich das nicht sagen kann, wenn ich über eine Zusammenarbeit mit anderen nachdenke. Es gibt so etwas wie Export als Markdown im Organisationsmodus, und ich habe darüber nachgedacht, etwas dagegen zu tun, aber es wäre nicht so sauber, wie ich dachte, es wäre auf Qiita ... es ist traurig. Emacs hat auch eine eindeutige Tastenbelegung und ist schwierig zu verwenden, wenn Sie VScode genießen können. VScode verfügt jedoch auch über eine Erweiterung des Organisationsmodus. Die wichtigste Tabulatortaste ist jedoch eine Funktion zum Umschalten des Anzeigestatus, um die Augen zu falten. Und ich denke, dass es keine Exportfunktion für HTML usw. gibt. Ich denke an etwas wie VHS gegen Betamax, es ist schwer zu wissen.
Ich war übrigens traurig, das Cover der August 2020-Ausgabe von Software Design zu sehen. Emacs kämpft gegen Vim, war das nicht schon lange so? Ist es nicht mehr möglich? Emacs? Ich möchte den Artikel so schließen.
Recommended Posts