Kapitel 4. Aufbau einer Reverse Image Search Engine: Das Verständnis von Einbettungen

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Bob hat gerade ein neues Haus gekauft und möchte es mit schicken, modernen Möbeln einrichten. Er blättert endlos in Möbelkatalogen und besucht Möbelausstellungsräume, aber er hat noch nichts gefunden, was ihm gefällt. Eines Tages entdeckt er das Sofa seiner Träume - ein einzigartiges, L-förmiges, weißes, modernes Sofa in einem Empfangsraum eines Büros. Die gute Nachricht ist, dass er weiß, was er will. Die schlechte Nachricht ist, dass er nicht weiß, wo er es kaufen kann. Die Marke und die Modellnummer stehen nicht auf dem Sofa. Den Büroleiter zu fragen, hilft auch nicht weiter. Also macht er ein paar Fotos aus verschiedenen Blickwinkeln, um sich in den örtlichen Möbelgeschäften umzuhören, aber Pech gehabt: Keiner kennt diese bestimmte Marke. Und die Suche im Internet mit Stichwörtern wie "weißes L-förmiges" oder "modernes Sofa" liefert ihm Tausende von Ergebnissen, aber nicht das, wonach er sucht.

Alice hört Bobs Frustration und fragt: "Warum versuchst du nicht die umgekehrte Bildersuche?" Bob lädt seine Bilder bei der umgekehrten Bildersuche von Google und Bing hoch und entdeckt schnell ein ähnlich aussehendes Bild auf einer Online-Shopping-Website. Mit diesem perfekteren Bild von der Website macht er ein paar weitere umgekehrte Bildersuchen und findet andere Websites, die das gleiche Sofa zu günstigeren Preisen anbieten. Nach ein paar Minuten im Internet hat Bob offiziell sein Traumsofa bestellt!

Die umgekehrte Bildersuche (oder, wie es technisch heißt, instance retrieval) ermöglicht es Entwicklern und Forschern, Szenarien zu entwickeln, die über die einfache Stichwortsuche hinausgehen. Von der Entdeckung visuell ähnlicher Objekte auf Pinterest über die Empfehlung ähnlicher Songs auf Spotify bis hin zur kamerabasierten Produktsuche auf Amazon wird eine ähnliche Technologie unter der Haube verwendet. Websites wie TinEye warnen Fotografen vor Urheberrechtsverletzungen, wenn ihre Fotos ohne Zustimmung im Internet veröffentlicht werden. Auch die Gesichtserkennung in verschiedenen Sicherheitssystemen nutzt ein ähnliches Konzept, um die Identität einer Person festzustellen.

Das Beste daran ist, dass du mit dem richtigen Wissen in wenigen Stunden ein funktionierendes Replikat von vielen dieser Produkte bauen kannst. Also lass uns gleich loslegen!

Das machen wir in diesem Kapitel:

  1. Merkmalsextraktion und Ähnlichkeitssuche in den Datensätzen Caltech101 und Caltech256

  2. Lernen, wie man große Datensätze (bis zu Milliarden von Bildern) skalieren kann

  3. Das System genauer machen und optimieren

  4. Analyse von Fallstudien, um zu sehen, wie diese Konzepte in Mainstream-Produkten verwendet werden

Bildähnlichkeit

Die erste und wichtigste Frage lautet: Sind sich zwei Bilder ähnlich oder nicht?

Für dieses Problem gibt es mehrere Ansätze. Ein Ansatz ist der Vergleich von Flächenabschnitten zwischen zwei Bildern. Auf diese Weise lassen sich zwar exakte oder fast exakte Bilder finden (die möglicherweise beschnitten wurden), aber selbst eine leichte Drehung würde zu einer Unähnlichkeit führen. Durch die Speicherung der Hashes der Patches können Duplikate eines Bildes gefunden werden. Ein Anwendungsfall für diesen Ansatz wäre die Erkennung von Plagiaten in Fotos.

Ein anderer naiver Ansatz besteht darin, das Histogramm der RGB-Werte zu berechnen und ihre Ähnlichkeiten zu vergleichen. Das kann helfen, ähnliche Bilder zu finden, die in der gleichen Umgebung aufgenommen wurden, ohne dass sich der Inhalt stark verändert hat. In Abbildung 4-1 wird diese Technik z. B. in einer Software zur Bilddeduplizierung verwendet, die darauf abzielt, Fotostrecken auf deiner Festplatte zu finden, damit du die besten auswählen und den Rest löschen kannst. Natürlich steigt die Wahrscheinlichkeit von Fehlalarmen, je größer dein Datensatz ist. Ein weiterer Nachteil dieses Ansatzes ist, dass kleine Änderungen der Farbe, des Farbtons oder des Weißabgleichs die Erkennung erschweren würden.

RGB histogram-based “Similar Image Detector” program
Abbildung 4-1. RGB-Histogramm-basiertes "Similar Image Detector"-Programm

Ein robusterer traditioneller, auf Computer Vision basierender Ansatz besteht darin, visuelle Merkmale in der Nähe von Kanten mit Algorithmen wie Scale-Invariant Feature Transform (SIFT), Speeded Up Robust Features (SURF) und Oriented FAST and Rotated BRIEF (ORB) zu finden und dann die Anzahl ähnlicher Merkmale zu vergleichen, die die beiden Fotos gemeinsam haben. So kannst du von einem allgemeinen Verständnis auf Bildebene zu einem relativ robusten Verständnis auf Objektebene gelangen. Das ist zwar toll für Bilder mit starren Objekten, die wenig Variationen aufweisen, wie z. B. die bedruckten Seiten einer Müslischachtel, die fast immer gleich aussehen, aber es ist weniger hilfreich für den Vergleich von verformbaren Objekten wie Menschen und Tieren, die unterschiedliche Posen einnehmen können. Als Beispiel kannst du dir die Merkmale ansehen, die bei der kamerabasierten Produktsuche in der Amazon-App angezeigt werden. Die App zeigt diese Merkmale in Form von blauen Punkten an(Abbildung 4-2). Wenn sie eine ausreichende Anzahl von Merkmalen erkennt, sendet sie diese an die Amazon-Server, um Produktinformationen abzurufen .

Product scanner in Amazon app with visual features highlighted
Abbildung 4-2. Produktscanner in der Amazon-App mit hervorgehobenen visuellen Merkmalen

Ein weiterer Ansatz besteht darin, die Kategorie (z. B. Sofa) eines Bildes mithilfe von Deep Learning zu ermitteln und dann andere Bilder innerhalb derselben Kategorie zu finden. Das entspricht dem Extrahieren von Metadaten aus einem Bild, so dass es anschließend indiziert und in einer typischen textbasierten Suche verwendet werden kann. Dies lässt sich leicht skalieren, indem die Metadaten in Open-Source-Suchmaschinen wie ElasticSearch verwendet werden. Viele E-Commerce-Websites zeigen Empfehlungen auf der Grundlage von Tags an, die aus einem Bild extrahiert wurden, während intern eine abfragebasierte Suche durchgeführt wird. Wie zu erwarten, gehen durch die Extraktion der Tags bestimmte Informationen wie Farbe, Pose, Beziehungen zwischen Objekten in der Szene und so weiter verloren. Ein großer Nachteil dieses Ansatzes ist außerdem, dass enorme Mengen an markierten Daten benötigt werden, um den Klassifikator für die Extraktion dieser Markierungen auf neue Bilder zu trainieren. Und jedes Mal, wenn eine neue Kategorie hinzugefügt werden soll, muss das Modell neu trainiert werden.

Da unser Ziel die Suche in Millionen von Bildern ist, brauchen wir idealerweise eine Möglichkeit, die Informationen, die in den Millionen von Pixeln eines Bildes enthalten sind, in einer kleineren Darstellung (von sagen wir ein paar tausend Dimensionen) zusammenzufassen und dafür zu sorgen, dass diese zusammengefasste Darstellung bei ähnlichen Objekten nahe beieinander und bei unähnlichen Gegenständen weiter entfernt liegt.

Zum Glück gibt es tiefe neuronale Netze, die hier Abhilfe schaffen. Wie wir in Kapitel 2 und Kapitel 3 gesehen haben, wandeln CNNs ein Bild in tausenddimensionale Merkmalsvektoren um, die dann als Eingabe für einen Klassifikator dienen, der die wichtigsten Identitäten ausgibt, zu denen das Bild gehören könnte (z. B. Hund oder Katze). Die Merkmalsvektoren (auch Einbettungen oder Engpassmerkmale genannt) sind im Wesentlichen eine Sammlung von einigen tausend Fließkommawerten. Das Durchlaufen der Faltungsschichten und der Pooling-Schichten in einem CNN ist im Grunde ein Akt der Reduktion, um die im Bild enthaltenen Informationen auf die wichtigsten und auffälligsten Bestandteile zu filtern, die wiederum die Bottleneck-Features bilden. Durch das Training des CNN werden diese Werte so geformt, dass Elemente, die zur gleichen Klasse gehören, einen kleinen euklidischen Abstand haben (oder einfach die Quadratwurzel aus der Summe der Quadrate der Differenz zwischen den entsprechenden Werten) und Elemente aus verschiedenen Klassen durch größere Abstände getrennt sind. Dies ist eine wichtige Eigenschaft, die bei der Lösung vieler Probleme hilft, bei denen ein Klassifikator nicht eingesetzt werden kann, vor allem bei unüberwachten Problemen, weil es an geeigneten markierten Daten fehlt.

Ein idealer Weg, um ähnliche Bilder zu finden, wäre die Anwendung von Transfer Learning. Zum Beispiel, indem man die Bilder durch ein vortrainiertes neuronales Faltungsnetzwerk wie ResNet-50 laufen lässt, die Merkmale extrahiert und dann eine Metrik wie den euklidischen Abstand zur Berechnung der Fehlerrate verwendet.

Genug geredet, lasst uns programmieren!

Merkmalsextraktion

Ein Bild von sagt mehr als tausend Worte.

In diesem Abschnitt lernen wir spielerisch die Konzepte der Merkmalsextraktion kennen, zunächst mit dem Caltech 101-Datensatz (131 MB, ca. 9.000 Bilder) und später mit Caltech 256 (1,2 GB, ca. 30.000 Bilder). Caltech 101 besteht, wie der Name schon sagt, aus etwa 9.000 Bildern in 101 Kategorien, mit etwa 40 bis 800 Bildern pro Kategorie. Wichtig ist, dass es eine 102. Kategorie namens "BACKGROUND_Google" gibt, die aus zufälligen Bildern besteht, die nicht in den ersten 101 Kategorien enthalten sind, und die gelöscht werden muss, bevor wir mit dem Experimentieren beginnen. Erinnere dich daran, dass der gesamte Code, den wir schreiben, auch im GitHub-Repository verfügbar ist.

Bitte beachte (ab dem 01. September 2020), dass der Caltech 101-Datensatz umgezogen ist und nun über Google Drive mit gdown heruntergeladen werden muss:

$ gdown https://drive.google.com/uc?id=137RyRjvTBkBiIfeYBNZBtViDHQ6_Ewsp
--output 101_ObjectCategories.tar.gz
$ tar -xvf 101_ObjectCategories.tar.gz
$ mv 101_ObjectCategories caltech101
$ rm -rf caltech101/BACKGROUND_Google

Importiere nun alle notwendigen Module:

import numpy as np
from numpy.linalg import norm
import pickle
from tqdm import tqdm, tqdm_notebook
import os
import time
from tf.keras.preprocessing import image
from tf.keras.applications.resnet50 import ResNet50, preprocess_input

Lade das ResNet-50-Modell ohne die oberen Klassifizierungsschichten, damit wir nur die Engpassmerkmale erhalten . Definiere dann eine Funktion, die einen Bildpfad nimmt, das Bild lädt, die Größe auf die von ResNet-50 unterstützten Dimensionen anpasst, die Merkmale extrahiert und sie dann normalisiert:

model = ResNet50(weights='imagenet', include_top=False,
                 input_shape=(224, 224, 3))
def extract_features(img_path, model):
    input_shape = (224, 224, 3)
    img = image.load_img(img_path, target_size=(
        input_shape[0], input_shape[1]))
    img_array = image.img_to_array(img)
    expanded_img_array = np.expand_dims(img_array, axis=0)
    preprocessed_img = preprocess_input(expanded_img_array)
    features = model.predict(preprocessed_img)
    flattened_features = features.flatten()
    normalized_features = flattened_features / norm(flattened_features)
    return normalized_features

Die Funktion, die im vorherigen Beispiel definiert wurde, ist die Funktion key, die wir in Keras für fast jeden Bedarf an Merkmalsextraktion verwenden.

Das war's! Schauen wir uns die Feature-Länge an, die das Modell erzeugt:

features = extract_features('../../sample_images/cat.jpg', model)
print(len(features))
> 2048

Das ResNet-50-Modell generierte 2.048 Merkmale aus dem bereitgestellten Bild. Jedes Merkmal ist ein Fließkommawert zwischen 0 und 1.

Wenn dein Modell auf einem Datensatz trainiert oder feinabgestimmt wurde, der nicht mit ImageNet vergleichbar ist, definiere den Schritt "preprocess_input(img)" entsprechend um. Die in der Funktion verwendeten Mittelwerte sind spezifisch für den ImageNet-Datensatz. Jedes Modell in Keras hat seine eigene Vorverarbeitungsfunktion, also stelle sicher, dass du die richtige verwendest.

Jetzt ist es an der Zeit, die Merkmale für den gesamten Datensatz zu extrahieren. Zuerst erhalten wir alle Dateinamen mit dieser praktischen Funktion, die rekursiv nach allen Bilddateien (definiert durch ihre Erweiterungen) in einem Verzeichnis sucht:

extensions = ['.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG']
def get_file_list(root_dir):
    file_list = []
    counter = 1
    for root, directories, filenames in os.walk(root_dir):
        for filename in filenames:
            if any(ext in filename for ext in extensions):
                file_list.append(os.path.join(root, filename))
                counter += 1
    return file_list

Dann geben wir den Pfad zu unserem Datensatz an und rufen die Funktion auf:

# path to the datasets
root_dir = '../../datasets/caltech101'
filenames = sorted(get_file_list(root_dir))

Wir definieren nun eine Variable, die alle Merkmale speichert, alle Dateinamen im Datensatz durchgeht, ihre Merkmale extrahiert und sie an die zuvor definierte Variable anhängt:

feature_list = []
for i in tqdm_notebook(range(len(filenames))):
    feature_list.append(extract_features(filenames[i], model))

Auf einer CPU sollte das weniger als eine Stunde dauern. Auf einem Grafikprozessor nur ein paar Minuten.

Um ein besseres Gefühl für die Zeit zu bekommen, kannst du das praktische Tool tqdm verwenden, das eine Fortschrittsanzeige(Abbildung 4-3) mit der Geschwindigkeit pro Iteration sowie der verstrichenen und der erwarteten Endzeit anzeigt. In Python kannst du eine Iterable mit tqdm; einpacken, zum Beispiel mit tqdm(range(10)). Die Jupyter Notebook-Variante ist tqdm_notebook.

Progress bar shown with tqdm_notebook
Abbildung 4-3. Fortschrittsbalken angezeigt mit tqdm_notebook

Schließlich schreibst du diese Merkmale in eine Pickle-Datei, damit wir sie in Zukunft verwenden können, ohne sie neu berechnen zu müssen:

pickle.dump(feature_list, open('data/features-caltech101-resnet.pickle', 'wb'))
pickle.dump(filenames, open('data/filenames-caltech101.pickle','wb'))

Das war's, Leute! Wir sind fertig mit dem Teil der Merkmalsextraktion.

Visualisierung von Bildclustern mit t-SNE

Auf kannst du den gesamten Datensatz visualisieren!

Dazu müssen wir die Dimensionen der Merkmalsvektoren reduzieren, denn es ist nicht möglich, einen 2.048-dimensionalen Vektor (die Merkmalslänge) in zwei Dimensionen darzustellen (das Papier). Der t-distributed stochastic neighbor embedding (t-SNE)-Algorithmus reduziert den hochdimensionalen Merkmalsvektor auf 2D und liefert so eine Vogelperspektive des Datensatzes, die bei der Erkennung von Clustern und nahegelegenen Bildern hilfreich ist. t-SNE lässt sich nur schwer auf große Datensätze skalieren, daher ist es eine gute Idee, die Dimensionalität mithilfe der Hauptkomponentenanalyse (PCA) zu reduzieren und dann t-SNE aufzurufen:

# Perform PCA over the features
num_feature_dimensions=100      # Set the number of features
pca = PCA(n_components = num_feature_dimensions)
pca.fit(featureList)
feature_list_compressed = pca.transform(featureList)

# For speed and clarity, we'll analyze about first half of the dataset.
selected_features = feature_list_compressed[:4000]
selected_class_ids = class_ids[:4000]
selected_filenames = filenames[:4000]

tsne_results =
TSNE(n_components=2,verbose=1,metric='euclidean')
    .fit_transform(selected_features)

# Plot a scatter plot from the generated t-SNE results
colormap = plt.cm.get_cmap('coolwarm')
scatter_plot = plt.scatter(tsne_results[:,0],tsne_results[:,1], c =
               selected_class_ids, cmap=colormap)
plt.colorbar(scatter_plot)
plt.show()

Auf die PCA gehen wir in den folgenden Abschnitten näher ein. Um auf größere Dimensionen zu skalieren, kannst du Uniform Manifold Approximation and Projection (UMAP) verwenden.

Abbildung 4-8 zeigt Cluster ähnlicher Klassen und wie sie dicht beieinander liegen.

t-SNE visualizing clusters of image features, each cluster represented one object class in the same color
Abbildung 4-8. t-SNE visualisiert Cluster von Bildmerkmalen, wobei jeder Cluster eine Objektklasse in derselben Farbe darstellt

Jede Farbe in Abbildung 4-8 steht für eine andere Klasse. Um es noch deutlicher zu machen, können wir eine weitere Hilfsfunktion, plot_images_in_2d(), verwenden, um die Bilder in diesen Clustern darzustellen, wie in Abbildung 4-9 gezeigt.

t-SNE visualizations showing image clusters; similar images are in the same cluster
Abbildung 4-9. t-SNE-Visualisierung mit Bildclustern; ähnliche Bilder befinden sich im selben Cluster

Toll! Es gibt eine klar abgegrenzte Gruppe von menschlichen Gesichtern, Blumen, Oldtimern, Schiffen, Fahrrädern und eine etwas verstreute Gruppe von Land- und Meerestieren. Da viele Bilder übereinander liegen, ist die Abbildung 4-9 etwas unübersichtlich. Versuchen wir also, das t-SNE mit der Hilfsfunktion tsne_to_grid_plotter_manual() als klare Kacheln darzustellen, deren Ergebnisse du in Abbildung 4-10 siehst.

tsne_to_grid_plotter_manual(tsne_results[:,0], tsne_results[:,1],
                            selected_filenames)
t-SNE visualization with tiled images; similar images are close together
Abbildung 4-10. t-SNE-Visualisierung mit gekachelten Bildern; ähnliche Bilder liegen nahe beieinander

Das ist definitiv viel klarer. Wir können sehen, dass ähnliche Bilder in den Clustern von menschlichen Gesichtern, Stühlen, Fahrrädern, Flugzeugen, Schiffen, Laptops, Tieren, Uhren, Blumen, umgekippten Minaretten, Oldtimern, Ankerschildern und Kameras zu finden sind, die alle nahe beieinander stehen. Gleich und gleich gesellt sich gern!

2D-Cluster von sind toll, aber sie in 3D zu visualisieren, wäre fantastisch. Noch besser wäre es, wenn sie gedreht, vergrößert und verkleinert und mit der Maus manipuliert werden könnten - ganz ohne Programmierung. Und Bonuspunkte, wenn die Daten interaktiv durchsucht werden könnten, um ihre Nachbarn zu finden. Der TensorFlow Embedding Projektor bietet all dies und mehr in einem browserbasierten GUI-Tool. Die vorgeladenen Embeddings aus Bild- und Textdatensätzen sind hilfreich, um ein besseres Gefühl für die Leistungsfähigkeit von Embeddings zu bekommen. Und wie Abbildung 4-11 zeigt, ist es beruhigend zu sehen, wie Deep Learning herausfindet, dass John Lennon, Led Zeppelin und Eric Clapton in der englischen Sprache in einem ähnlichen Kontext wie die Beatles verwendet werden .

TensorFlow Embedding projector showing a 3D representation of common 10,000 English words and highlighting related words to “Beatles”
Abbildung 4-11. Der TensorFlow Embedding Projektor zeigt eine 3D-Darstellung von 10.000 gebräuchlichen englischen Wörtern und hebt Wörter hervor, die mit "Beatles" verwandt sind.

Skalierende Ähnlichkeitssuche mit approximativen nächsten Nachbarn

Welche wollen wir? Nächste Nachbarn. Was ist unsere Basislinie? Brute-force-Suche. Obwohl sie bequem in zwei Zeilen implementiert werden kann, geht sie über jedes Element und skaliert daher linear mit der Datengröße (Anzahl der Elemente und der Dimensionen). Wenn wir unseren Merkmalsvektor mit PCA von 2.048 auf 100 verkleinern, verringert sich nicht nur die Datengröße um das 20-fache, sondern auch die Geschwindigkeit steigt um das 20-fache, wenn wir Brute-Force einsetzen. PCA zahlt sich aus!

Nehmen wir an, dass die Ähnlichkeitssuche in einer kleinen Sammlung von 10.000 Bildern, die jetzt mit 100 Vektoren der Merkmalslänge dargestellt werden, etwa 1 ms dauert. Auch wenn das für 10.000 Objekte schnell aussieht, wird die Suche in einem echten Produktionssystem mit größeren Daten, vielleicht 10 Millionen Objekten, mehr als eine Sekunde dauern. Unser System ist möglicherweise nicht in der Lage, mehr als eine Anfrage pro Sekunde und CPU-Kern zu bearbeiten. Wenn du 100 Anfragen pro Sekunde von Nutzern erhältst, bräuchtest du selbst bei mehreren CPU-Kernen der Maschine (und dem Laden des Suchindex per Thread) mehrere Maschinen, um den Datenverkehr bedienen zu können. Mit anderen Worten: Ein ineffizienter Algorithmus bedeutet Geld, viel Geld, das für Hardware ausgegeben wird.

Brute-Force ist unsere Basis für jeden Vergleich. Wie bei den meisten algorithmischen Ansätzen ist Brute-Force der langsamste Ansatz. Jetzt, wo wir unsere Basis haben, werden wir die Näherungsalgorithmen untersuchen. Anstatt wie bei der Brute-Force-Methode das richtige Ergebnis zu garantieren, erzielen die Näherungsalgorithmen in der Regel das richtige Ergebnis, weil sie... nun ja, Näherungen sind. Die meisten Algorithmen lassen sich in irgendeiner Form einstellen, um ein Gleichgewicht zwischen Korrektheit und Geschwindigkeit herzustellen. Es ist möglich, die Qualität der Ergebnisse zu bewerten, indem man sie mit den Ergebnissen der Brute-Force-Basislösung vergleicht.

Näherungsweise nächstgelegener Benchmark

Es gibt mehrere ANN-Bibliotheken (Approximate Nearest Neighbor), darunter so bekannte wie Annoy von Spotify, FLANN, Faiss von Facebook, NGT von Yahoo und NMSLIB. Sie alle zu vergleichen, wäre eine mühsame Aufgabe (vorausgesetzt, du schaffst es, einige von ihnen zu installieren). Zum Glück haben die guten Leute von ann-benchmarks.com (Martin Aumueller, Erik Bernhardsson und Alec Faitfull) die Arbeit für uns übernommen und reproduzierbare Benchmarks für 19 Bibliotheken mit großen öffentlichen Datensätzen erstellt. Wir werden die Vergleiche mit einem Datensatz von Feature Embeddings, die Wörter (statt Bilder) darstellen, namens GloVe durchführen. Dieser 350 MB große Datensatz besteht aus 400.000 Feature-Vektoren, die Wörter in 100 Dimensionen darstellen. Abbildung 4-15 zeigt die Rohleistung, wenn sie auf Korrektheit getrimmt ist. Die Leistung wird anhand der Fähigkeit der Bibliothek gemessen, Anfragen pro Sekunde zu beantworten. Ein Maß für die Korrektheit ist der Anteil der am nächsten liegenden Top-n-Einträge im Verhältnis zu den tatsächlichen am nächsten liegenden Top-n-Einträgen. Diese Grundwahrheit wird mit der Brute-Force-Suche gemessen.

Comparison of ANN libraries (data from ann-benchmarks.com)
Abbildung 4-15. Vergleich der ANN-Bibliotheken (Daten von ann-benchmarks.com)

Die besten Ergebnisse in diesem Datensatz liefern fast mehrere tausend Abfragen pro Sekunde bei einer akzeptablen Trefferquote von 0,8. Zum Vergleich: Unsere Brute-Force-Suche liefert weniger als eine Anfrage pro Sekunde. Einige dieser Bibliotheken (wie z. B. NGT) können bis zu 15.000 Ergebnisse pro Sekunde liefern (allerdings bei einer niedrigen Trefferquote, was sie für die Nutzung unpraktisch macht).

Welche Bibliothek soll ich benutzen?

Es versteht sich von selbst, dass die Bibliothek, die du verwendest, stark von deinem Szenario abhängt. Jede Bibliothek bietet einen Kompromiss zwischen Suchgeschwindigkeit, Genauigkeit, Indexgröße, Speicherverbrauch, Hardware-Nutzung (CPU/GPU) und einfacher Einrichtung. Tabelle 4-3 gibt einen Überblick über die verschiedenen Szenarien und Empfehlungen, welche Bibliothek für jedes Szenario am besten geeignet ist.

Tabelle 4-3. Empfehlungen für die ANN-Bibliothek
Szenario Empfehlung
Ich möchte schnell in Python experimentieren, ohne zu viele Einstellungen vorzunehmen, aber ich lege auch Wert auf eine hohe Geschwindigkeit. Annoy oder NMSLIB verwenden
Ich habe einen großen Datensatz (bis zu 10 Millionen Einträge oder mehrere tausend Dimensionen) und lege größten Wert auf Geschwindigkeit. NGT verwenden
Ich habe einen lächerlich großen Datensatz (über 100 Millionen Einträge) und habe auch einen Cluster von GPUs. Faiss verwenden
Ich möchte eine Basiswahrheit mit 100%iger Korrektheit festlegen. Dann wechsle ich sofort zu einer schnelleren Bibliothek, beeindrucke meinen Chef mit der um Größenordnungen höheren Geschwindigkeit und bekomme einen Bonus. Brute-Force-Ansatz verwenden

Wir bieten auf der GitHub-Website des Buches (siehe http://PracticalDeepLearning.ai) viel detailliertere Code-Beispiele für verschiedene Bibliotheken an, aber für unsere Zwecke hier stellen wir unsere Lieblingsbibliothek Annoy im Detail vor und vergleichen sie mit der Brute-Force-Suche auf einem synthetischen Datensatz. Außerdem gehen wir kurz auf Faiss und NGT ein.

Erstellen eines synthetischen Datensatzes

Um einen direkten Vergleich zwischen den verschiedenen Bibliotheken anzustellen, erstellen wir zunächst einen Datensatz mit einer Million Einträgen, der aus zufälligen Fließkommawerten mit Mittelwert 0 und Varianz 1 besteht. Außerdem wählen wir einen zufälligen Merkmalsvektor als Abfrage, um die nächsten Nachbarn zu finden:

num_items = 1000000
num_dimensions = 100
dataset = np.random.randn(num_items, num_dimensions)
dataset /= np.linalg.norm(dataset, axis=1).reshape(-1, 1)

random_index = random.randint(0,num_items)
query = dataset[random_index]

Brachiale Gewalt

Zuerst berechnen wir die Zeit für die Suche mit dem Brute-Force-Algorithmus. Er geht die gesamten Daten der Reihe nach durch und berechnet den Abstand zwischen der Abfrage und dem aktuellen Element nacheinander. Wir verwenden den Befehl timeit, um die Zeit zu berechnen. Zuerst erstellen wir den Suchindex, um die fünf nächsten Nachbarn zu finden, und suchen dann mit einer Abfrage:

neighbors = NearestNeighbors(n_neighbors=5, algorithm='brute',
metric='euclidean').fit(dataset)
%timeit distances, indices = neighbors.kneighbors([query])
> 177 ms ± 136 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Der Befehl timeit ist ein praktisches Werkzeug. Um die Zeit einer einzelnen Operation zu messen, stellst du ihr diesen Befehl voran. Im Gegensatz zum Befehl time, der eine Anweisung nur einmal ausführt, führt timeit die nachfolgende Zeile mehrmals aus, um genauere aggregierte Statistiken zusammen mit der Standardabweichung zu erhalten. Standardmäßig wird die Speicherbereinigung ausgeschaltet, damit die unabhängigen Zeitangaben besser vergleichbar sind. Allerdings kann es sein, dass die Zeitangaben nicht den tatsächlichen Produktionslasten entsprechen, wenn die Speicherbereinigung eingeschaltet ist.

Ärgern Sie

Annoy (Approximate Nearest Neighbors Oh Yeah) ist eine C++-Bibliothek mit Python-Bindungen für die Suche nach den nächsten Nachbarn. Sie ist ein Synonym für Geschwindigkeit und wurde von Spotify veröffentlicht und wird in der Produktion für die Musikempfehlungen verwendet. Anders als ihr Name vermuten lässt, ist sie tatsächlich unterhaltsam und einfach zu benutzen.

Um Annoy zu nutzen, installieren wir es mit pip:

$ pip install annoy

Die Anwendung ist ziemlich einfach. Zuerst erstellen wir einen Suchindex mit zwei Hyperparametern: die Anzahl der Dimensionen des Datensatzes und die Anzahl der Bäume:

from annoy import AnnoyIndex
annoy_index = AnnoyIndex(num_dimensions) # Length of item vector that will be
indexed
for i in range(num_items):
    annoy_index.add_item(i, dataset[i])
annoy_index.build(40) # 40 trees

Jetzt wollen wir herausfinden, wie lange es dauert, die fünf nächsten Nachbarn eines Bildes zu suchen:

%timeit indexes=t.get_nns_by_vector(query, 5, include_distances=True)
> 34.9 μs ± 165 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Das ist rasend schnell! Um das zu verdeutlichen: Selbst für unseren Datensatz mit einer Million Einträgen können fast 28.000 Anfragen auf einem einzigen CPU-Kern bearbeitet werden. Wenn man bedenkt, dass die meisten CPUs mehrere Kerne haben, sollte es in der Lage sein, mehr als 100.000 Anfragen auf einem einzigen System zu bearbeiten. Das Beste daran ist, dass du denselben Index im Speicher für mehrere Prozesse freigeben kannst. So kann der größte Index der Größe deines gesamten Arbeitsspeichers entsprechen, wodurch es möglich ist, mehrere Anfragen auf einem einzigen System zu bedienen.

Zu den weiteren Vorteilen gehört, dass es einen Index von bescheidener Größe erzeugt. Außerdem entkoppelt es das Erstellen von Indizes vom Laden, so dass du einen Index auf einem Rechner erstellen, ihn weitergeben und dann auf deinem Serving-Rechner in den Speicher laden und ausliefern kannst.

Du fragst dich, wie viele Bäume du verwenden sollst? Mehr Bäume ergeben eine höhere Genauigkeit, aber auch größere Indizes. Normalerweise werden nicht mehr als 50 Bäume benötigt, um die höchste Genauigkeit zu erreichen.

NGT

Die Bibliothek Neighborhood Graph and Tree (NGT) von Yahoo Japan führt derzeit die meisten Benchmarks an und eignet sich am besten für große Datensätze (in Millionen von Elementen) mit großen Dimensionen (in mehreren Tausend). Die Bibliothek gibt es zwar schon seit 2016, aber ihr wirklicher Einstieg in die Benchmark-Szene erfolgte 2018 mit der Implementierung des ONNG-Algorithmus (kurz für Optimization of indexing based on k-Nearest Neighbor Graph for proximity). Da NGT auf einem Server von mehreren Threads ausgeführt werden kann, kann er den Index mit Hilfe von Memory-Mapped-Dateien im gemeinsamen Speicher ablegen und so den Speicherverbrauch reduzieren und die Ladezeit erhöhen.

Faiss

Faiss ist die effiziente Ähnlichkeitssuchbibliothek von Facebook. Sie kann Milliarden von Vektoren auf einem einzigen Server im Arbeitsspeicher speichern, indem sie eine komprimierte Darstellung der Vektoren (kompakte Quantisierungscodes) anstelle der Originalwerte speichert. Sie ist besonders für dichte Vektoren geeignet. Es eignet sich besonders gut für Rechner mit Grafikprozessoren, da der Index im GPU-Speicher (VRAM) gespeichert wird. Das funktioniert sowohl auf Single-GPU- als auch auf Multi-GPU-Systemen. Es bietet die Möglichkeit, die Leistung auf der Grundlage von Suchzeit, Genauigkeit, Speichernutzung und Indexierungszeit zu konfigurieren. Es ist eine der schnellsten bekannten Implementierungen der ANN-Suche auf der GPU. Hey, wenn es für Facebook gut genug ist, ist es auch für die meisten von uns gut genug (solange wir genug Daten haben).

Obwohl es den Rahmen dieses Buches sprengen würde, den gesamten Prozess zu zeigen, empfehlen wir, Faiss mit Anaconda zu installieren oder die Docker-Container zu verwenden, um schnell loszulegen.

Verbesserung der Genauigkeit durch Feinabstimmung

Viele der vortrainierten Modelle wurden auf dem ImageNet-Datensatz trainiert. Daher bieten sie in den meisten Fällen eine hervorragende Ausgangsbasis für Ähnlichkeitsberechnungen. Wenn du diese Modelle jedoch an dein spezielles Problem anpasst, werden sie noch besser ähnliche Bilder finden.

In diesem Teil des Kapitels identifizieren wir die Kategorien, die am schlechtesten abschneiden, visualisieren sie mit t-SNE, nehmen eine Feinabstimmung vor und sehen dann, wie sich ihr t-SNE-Diagramm verändert.

Was ist eine gute Kennzahl, um zu überprüfen, ob du tatsächlich ähnliche Bilder bekommst?

Schmerzhafte Option 1

Gehe den gesamten Datensatz ein Bild nach dem anderen durch und bewerte manuell, ob die zurückgegebenen Bilder tatsächlich ähnlich aussehen.

Glücklichere Option 2

Berechne einfach die Genauigkeit. Das heißt, wenn ein Bild der Kategorie X angehört, gehören die ähnlichen Bilder dann auch zur gleichen Kategorie? Wir nennen das die Ähnlichkeitsgenauigkeit.

Welches sind also die Kategorien, die am schlechtesten abschneiden? Und warum sind sie die schlechtesten? Um diese Frage zu beantworten, haben wir eine Hilfsfunktion worst_classes vordefiniert. Sie findet für jedes Bild im Datensatz die nächsten Nachbarn mit dem Brute-Force-Algorithmus und gibt dann die sechs Klassen mit der geringsten Genauigkeit zurück. Um die Auswirkungen der Feinabstimmung zu sehen, führen wir unsere Analyse an einem schwierigeren Datensatz durch: Caltech-256. Wenn du diese Funktion aufrufst, werden die ungenauesten Klassen aufgedeckt:

names_of_worst_classes_before_finetuning, accuracy_per_class_before_finetuning =
worst_classes(feature_list[:])
    Accuracy is 56.54
    Top 6 incorrect classifications
    059.drinking-straw    Accuracy: 11.76%
    135.mailbox             Accuracy: 16.03%
    108.hot-dog             Accuracy: 16.72%
    163.playing-card      Accuracy: 17.29%
    195.soda-can            Accuracy: 19.68%
    125.knife         Accuracy: 20.53%

Um zu sehen, warum sie in bestimmten Klassen so schlecht abschneiden, haben wir ein t-SNE-Diagramm erstellt, um die Einbettungen im 2D-Raum zu visualisieren (siehe Abbildung 4-16). Um eine Überfüllung des Diagramms zu vermeiden, verwenden wir nur 50 Elemente aus jeder der 6 Klassen.

Um die Sichtbarkeit des Diagramms zu verbessern, können wir verschiedene Markierungen und Farben für jede Klasse festlegen. Matplotlib bietet eine große Auswahl an Markierungen und Farben.

markers = [ "^", ".","s", "o","x", "P" ]
colors = ['red', 'blue', 'fuchsia', 'green', 
'purple', 'orange']
t-SNE visualization of feature vectors of least accurate classes before fine-tuning
Abbildung 4-16. t-SNE-Visualisierung der Merkmalsvektoren der ungenauesten Klassen vor der Feinabstimmung

Aah, diese Merkmalsvektoren liegen überall herum und übereinander. Die Verwendung dieser Merkmalsvektoren in anderen Anwendungen, wie z. B. der Klassifizierung, wäre keine gute Idee, denn es wäre schwierig, eine saubere Trennlinie zwischen ihnen zu finden. Kein Wunder, dass sie in diesem Klassifizierungstest auf Basis der nächsten Nachbarn so schlecht abgeschnitten haben.

Was denkst du, wird das Ergebnis sein, wenn wir diese Schritte mit dem feinabgestimmten Modell wiederholen? Wir vermuten etwas Interessantes. Schauen wir uns Abbildung 4-17 an, um das zu sehen.

t-SNE visualization of feature vectors of least accurate classes after fine tuning
Abbildung 4-17. t-SNE-Visualisierung der Merkmalsvektoren der ungenauesten Klassen nach der Feinabstimmung

Das ist so viel sauberer. Mit und einem kleinen Feintuning, wie in Kapitel 3 gezeigt, beginnen sich die Einbettungen zu gruppieren. Vergleiche die verrauschten/verstreuten Einbettungen der vortrainierten Modelle mit denen des fein abgestimmten Modells. Ein Klassifizierungssystem mit maschinellem Lernen wäre in der Lage, eine Trennlinie zwischen diesen Klassen zu finden, was zu einer besseren Klassifizierungsgenauigkeit und zu mehr ähnlichen Bildern führen würde, wenn kein Klassifizierungssystem verwendet würde. Und denk daran, dass dies die Klassen mit den höchsten Fehlklassifizierungen waren; stell dir vor, wie schön die Klassen mit der ursprünglich höheren Genauigkeit nach der Feinabstimmung sein würden.

Zuvor erreichten die vortrainierten Einbettungen eine Genauigkeit von 56 %. Die neuen Einbettungen liefern nach der Feinabstimmung satte 87 % Genauigkeit! Ein bisschen Magie kann viel bewirken.

Die einzige Einschränkung bei der Feinabstimmung ist das Erfordernis von beschrifteten Daten, die nicht immer vorhanden sind. Je nach Anwendungsfall kann es also sein, dass du eine gewisse Menge an Daten kennzeichnen musst.

Allerdings gibt es einen kleinen unkonventionellen Trainingstrick, den wir im nächsten Abschnitt besprechen.

Feinabstimmung ohne vollständig verbundene Schichten

Wie wir bereits wissen, besteht ein neuronales Netz aus drei Teilen:

  • Faltungsschichten, die am Ende die Merkmalsvektoren erzeugen

  • Vollständig verbundene Schichten

  • Die letzte Klassifizierungsschicht

Beim Feintuning geht es, wie der Name schon sagt, darum, ein neuronales Netz leicht zu optimieren, um es an einen neuen Datensatz anzupassen. In der Regel werden dabei die voll verknüpften Schichten (oberste Schichten) entfernt, durch neue ersetzt und das neu zusammengesetzte neuronale Netz dann mit diesem Datensatz trainiert. Diese Art des Trainings bewirkt zwei Dinge:

  • Die Gewichte in allen neu hinzugefügten voll verknüpften Schichten werden erheblich beeinflusst.

  • Die Gewichte in den Faltungsschichten werden nur leicht verändert.

Die voll verknüpften Schichten übernehmen einen Großteil der schweren Arbeit, um eine maximale Klassifizierungsgenauigkeit zu erreichen. Daher wird sich der Großteil des Netzwerks, das die Merkmalsvektoren erzeugt, nur unwesentlich verändern. Daher werden sich die Merkmalsvektoren trotz Feinabstimmung kaum verändern.

Unser Ziel ist es, dass ähnlich aussehende Objekte engere Merkmalsvektoren haben, was mit der oben beschriebenen Feinabstimmung nicht erreicht werden kann. Wenn wir das gesamte aufgabenspezifische Lernen in die Faltungsschichten verlagern, können wir viel bessere Ergebnisse erzielen. Wie erreichen wir das? Indem wir alle voll verknüpften Schichten entfernen und eine Klassifizierungsschicht direkt nach den Faltungsschichten (die die Merkmalsvektoren erzeugen) einfügen. Dieses Modell ist für die Ähnlichkeitssuche und nicht für die Klassifizierung optimiert.

Um den Prozess der Feinabstimmung eines für Klassifizierungsaufgaben optimierten Modells mit dem der Ähnlichkeitssuche zu vergleichen, erinnern wir uns daran, wie wir unser Modell in Kapitel 3 für die Klassifizierung feinabgestimmt haben:

from tf.keras.applications.resnet50 import ResNet50
model = ResNet50(weights='imagenet', include_top=False,
input_shape = (224,224,3))
input = Input(shape=(224, 224, 3))
x = model(input)
x = GlobalAveragePooling2D()(x)
x = Dense(64, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(NUM_CLASSES, activation='softmax')(x)
model_classification_optimized = Model(inputs=input, outputs=x)

Und hier sehen wir, wie wir unser Modell für die Ähnlichkeitssuche feinabstimmen. Beachte die fehlende versteckte dichte Schicht in der Mitte:

from tf.keras.applications.resnet50 import ResNet50
model = ResNet50(weights='imagenet', include_top=False,
input_shape = (224,224,3))
input = Input(shape=(224, 224, 3))
x = model(input)
x = GlobalAveragePooling2D()(x)
# No dense or dropout layers
x = Dense(NUM_CLASSES, activation='softmax')(x)
model_similarity_optimized = Model(inputs=input, outputs=x)

Um nach der Feinabstimmung die model_similarity_optimized für die Extraktion von Merkmalen zu verwenden, anstatt Wahrscheinlichkeiten für Klassen anzugeben, musst du nur die letzte Ebene pop (d.h. entfernen):

model_similarity_optimized.layers.pop()
model = Model(model_similarity_optimized.input,
model_similarity_optimized.layers[-1].output)

Das Wichtigste an dieser Stelle ist, dass wir bei Verwendung des regulären Feinabstimmungsprozesses eine geringere Ähnlichkeitsgenauigkeit als model_similarity_optimized erhalten würden. Natürlich würden wir model_classification_optimized für Klassifizierungsszenarien und model_similarity_optimized für die Extraktion von Einbettungen für die Ähnlichkeitssuche verwenden wollen.

Mit all diesem Wissen kannst du jetzt ein schnelles und genaues Ähnlichkeitssystem für jedes Szenario entwickeln, an dem du arbeitest. Es ist an der Zeit zu sehen, wie die Giganten der KI-Branche ihre Produkte entwickeln.

Siamesische Netzwerke für die One-Shot-Gesichtsüberprüfung

Ein Gesichtserkennungssystem versucht in der Regel, anhand von zwei Bildern von Gesichtern festzustellen, ob die beiden Bilder von derselben Person stammen. Dabei handelt es sich um einen hochpräzisen binären Klassifikator, der mit unterschiedlichen Beleuchtungen, Kleidungsstücken, Frisuren, Hintergründen und Gesichtsausdrücken zurechtkommen muss. Erschwerend kommt hinzu, dass es zwar Bilder von vielen Personen gibt, z. B. in einer Mitarbeiterdatenbank, aber nur eine Handvoll Bilder von ein und derselben Person vorhanden sind. Auch die Identifizierung von Unterschriften in Banken und die Identifizierung von Produkten auf Amazon haben mit der gleichen Herausforderung zu kämpfen: Es gibt nur wenige Bilder pro Artikel.

Wie würdest du einen solchen Klassifikator trainieren? Die Auswahl von Embeddings aus einem Modell wie ResNet, das auf ImageNet trainiert wurde, könnte diese feinen Gesichtsmerkmale nicht erkennen. Ein Ansatz besteht darin, jede Person als eigene Klasse zu betrachten und dann wie ein normales Netzwerk zu trainieren. Dabei ergeben sich zwei wichtige Probleme:

  • Wenn wir eine Million Menschen hätten, wäre eine Ausbildung für eine Million Kategorien nicht machbar.

  • Das Training mit wenigen Bildern pro Klasse führt zu Übertraining.

Ein weiterer Gedanke: Anstatt verschiedene Kategorien zu lehren, könnten wir einem Netzwerk beibringen, direkt zu vergleichen und zu entscheiden, ob ein Bildpaar ähnlich oder unähnlich ist, indem wir während des Trainings Hinweise auf ihre Ähnlichkeit geben. Und das ist der Kerngedanke von Siamesischen Netzen. Du nimmst ein Modell, gibst zwei Bilder ein, extrahierst zwei Einbettungen und berechnest dann den Abstand zwischen den beiden Einbettungen. Wenn der Abstand unter einem Schwellenwert liegt, werden sie als ähnlich angesehen, andernfalls nicht. Indem du ein Bildpaar mit dem zugehörigen Label "ähnlich" oder "unähnlich" einspeist und das Netzwerk von einem Ende zum anderen trainierst, beginnen die Einbettungen, die feinkörnige Darstellung der Eingaben zu erfassen. Dieser in Abbildung 4-18 dargestellte Ansatz der direkten Optimierung für die Distanzmetrik wird metrisches Lernen genannt.

A Siamese network for signature verification; note the same CNN was used for both input images
Abbildung 4-18. Ein siamesisches Netzwerk für die Unterschriftenprüfung; beachte, dass für beide Eingangsbilder dasselbe CNN verwendet wurde

Wir könnten diese Idee erweitern und sogar drei Bilder einspeisen. Wähle ein Ankerbild, ein weiteres positives Beispiel (aus derselben Kategorie) und ein weiteres negatives Beispiel (aus einer anderen Kategorie). Nun trainieren wir dieses Netzwerk so, dass der Abstand zwischen ähnlichen Objekten minimiert und der Abstand zwischen unähnlichen Objekten maximiert wird. Die Verlustfunktion, die uns dabei hilft, nennt man Triplett-Verlustfunktion. Im vorherigen Fall mit einem Bildpaar wird die Verlustfunktion als kontrastive Verlustfunktion bezeichnet. Die Triplett-Verlustfunktion liefert in der Regel bessere Ergebnisse.

Nachdem das Netzwerk trainiert hat, brauchen wir nur noch ein Referenzbild eines Gesichts, um zum Testzeitpunkt zu entscheiden, ob es sich um dieselbe Person handelt. Diese Methode öffnet die Türen für One-Shot-Learning. Weitere gängige Anwendungen sind die Erkennung von Unterschriften und Logos. Eine bemerkenswert kreative Anwendung von Saket Maheshwary und Hemant Misra ist die Verwendung eines siamesischen Netzwerks für den Abgleich von Lebensläufen mit Bewerbern, indem die semantische Ähnlichkeit zwischen beiden berechnet wird.

Fallstudien

Schauen wir uns unter ein paar interessante Beispiele an, die zeigen, wie das, was wir bisher gelernt haben, in der Branche angewendet wird.

Flickr

Flickr ist eine der größten Foto-Sharing-Websites, die besonders bei professionellen Fotografen beliebt ist. Um Fotografen bei der Suche nach Inspiration zu helfen und Inhalte zu präsentieren, die für die Nutzer interessant sein könnten, hat Flickr eine Ähnlichkeitssuche entwickelt, die auf der gleichen semantischen Bedeutung basiert. Wie in Abbildung 4-19 dargestellt, führt die Suche nach einem Wüstenmuster zu mehreren ähnlich gemusterten Ergebnissen. Unter der Haube verwendet Flickr einen ANN-Algorithmus namens Locally Optimized Product Quantization (LOPQ), der sowohl in Python als auch in Spark-Implementierungen als Open Source verfügbar ist.

Similar patterns of a desert photo (image source: code.flickr.com]
Abbildung 4-19. Ähnliche Muster eines Wüstenfotos(Bildquelle)

Pinterest

Pinterest ist eine Anwendung, die vor allem wegen ihrer visuellen Suchfunktionen genutzt wird, genauer gesagt wegen der Funktionen Similar Pins und Related Pins. Andere Unternehmen wie Baidu und Alibaba haben ähnliche visuelle Suchsysteme eingeführt. Auch Zappos, Google Shopping und like.com nutzen Computer Vision für Empfehlungen.

Auf Pinterest ist "Damenmode" eines der beliebtesten Themen für Pins und die Funktion "Similar Looks"(Abbildung 4-20) hilft den Leuten, ähnliche Produkte zu entdecken. Außerdem berichtet Pinterest, dass seine Related Pins-Funktion die Repin-Rate erhöht hat. Nicht jeder Pin auf Pinterest verfügt über zugehörige Metadaten, was die Empfehlung aufgrund des fehlenden Kontexts zu einem schwierigen Problem für den Kaltstart macht. Die Entwickler von Pinterest haben dieses Problem gelöst, indem sie die visuellen Funktionen für die Generierung der verwandten Pins genutzt haben. Darüber hinaus implementiert Pinterest einen inkrementellen Fingerabdruckdienst, der neue digitale Signaturen erzeugt, wenn entweder ein neues Bild hochgeladen wird oder wenn sich die Merkmale weiterentwickeln (aufgrund von Verbesserungen oder Änderungen an den zugrunde liegenden Modellen durch die Ingenieure).

The Similar Looks feature of the Pinterest application (image source: Pinterest blog)
Abbildung 4-20. Die Funktion Similar Looks der Pinterest-Anwendung (Bildquelle: Pinterest Blog)

Berühmte Doppelgänger

Auf der Website suchen Anwendungen wie Celebslike.me, die 2015 viral gingen, nach dem nächsten Nachbarn unter den Berühmtheiten, wie in Abbildung 4-21 gezeigt. Einen ähnlichen viralen Ansatz verfolgte 2018 die Google Arts & Culture App, die das nächstgelegene existierende Porträt zu deinem Gesicht zeigt. Twins or not ist eine weitere Anwendung mit einem ähnlichen Ziel.

Testing our friend Pete Warden’s photo (technical lead for mobile and embedded TensorFlow at Google) on the celebslike.me website
Abbildung 4-21. Testen des Fotos unseres Freundes Pete Warden (technischer Leiter für mobiles und eingebettetes TensorFlow bei Google) auf der Website celebslike.me

Spotify

Spotify verwendet Nearest Neighbors, um Musik zu empfehlen und automatische Wiedergabelisten und Radiosender zu erstellen, die auf den aktuell gespielten Songs basieren. Normalerweise sind kollaborative Filtertechniken, die für die Empfehlung von Inhalten wie Filmen auf Netflix eingesetzt werden, inhaltsunabhängig, d. h. die Empfehlung erfolgt, weil große Gruppen von Nutzern mit ähnlichem Geschmack ähnliche Filme ansehen oder ähnliche Songs hören. Dies stellt ein Problem für neue und noch nicht beliebte Inhalte dar, da die Nutzer/innen immer wieder Empfehlungen für bereits beliebte Inhalte erhalten. Dies wird auch als das bereits erwähnte Kaltstartproblem bezeichnet. Die Lösung besteht darin, das latente Verständnis der Inhalte zu nutzen. Ähnlich wie bei Bildern können wir mit Hilfe von MFCC-Merkmalen (Mel Frequency Cepstral Coefficients) Merkmalsvektoren aus Musik erstellen, die wiederum ein 2D-Spektrogramm erzeugen, das als Bild betrachtet und zur Erzeugung von Merkmalen verwendet werden kann. Die Lieder werden in dreisekündige Fragmente unterteilt und ihre Spektrogramme werden zur Erstellung von Merkmalen verwendet. Diese Merkmale werden dann gemittelt, um den kompletten Song darzustellen. Abbildung 4-22 zeigt Künstler, deren Songs in bestimmten Bereichen projiziert werden. Wir können Hip-Hop (oben links), Rock (oben rechts), Pop (unten links) und elektronische Musik (unten rechts) erkennen. Wie bereits erwähnt, verwendet Spotify Annoy im Hintergrund.

t-SNE visualization of the distribution of predicted usage patterns, using latent factors predicted from audio (image source: Deep content-based music recommendation by Aaron van den Oord, Sander Dieleman, Benjamin Schrauwen)
Abbildung 4-22. t-SNE-Visualisierung der Verteilung der vorhergesagten Nutzungsmuster unter Verwendung von latenten Faktoren, die aus Audiodaten vorhergesagt werden (Bildquelle: "Deep content-based music recommendation" von Aaron van den Oord, Sander Dieleman, Benjamin Schrauwen, NIPS 2013)

Bildbeschriftung

Bildbeschriftung ist die Wissenschaft von der Übersetzung eines Bildes in einen Satz (wie in Abbildung 4-23 dargestellt). Dies geht über das bloße Taggen von Objekten hinaus und erfordert ein tieferes visuelles Verständnis des gesamten Bildes und der Beziehungen zwischen den Objekten. Um diese Modelle zu trainieren, wurde 2014 ein Open-Source-Datensatz namens MS COCO veröffentlicht, der mehr als 300.000 Bilder zusammen mit Objektkategorien, Satzbeschreibungen, visuellen Frage-Antwort-Paaren und Objektsegmentierungen enthält. Er dient als Benchmark für einen jährlichen Wettbewerb, bei dem die Fortschritte bei der Bildbeschriftung, Objekterkennung und -segmentierung überprüft werden.

Image captioning feature in Seeing AI: the Talking Camera App for the blind community
Abbildung 4-23. Bildunterschriftenfunktion in Seeing AI: die sprechende Kamera-App für Blinde

Eine gängige Strategie, die im ersten Jahr der Challenge (2015) angewandt wurde, bestand darin, ein Sprachmodell (LSTM/RNN) mit einem CNN so zu verknüpfen, dass die Ausgabe eines CNN-Merkmalsvektors als Eingabe für das Sprachmodell (LSTM/RNN) verwendet wird. Dieses kombinierte Modell wurde durchgängig gemeinsam trainiert und führte zu sehr beeindruckenden Ergebnissen, die die Welt in Erstaunen versetzten. Obwohl jedes Forschungslabor versuchte, sich gegenseitig zu übertrumpfen, stellte sich später heraus, dass man mit einer einfachen Suche nach den nächsten Nachbarn die besten Ergebnisse erzielen kann. Finde für ein bestimmtes Bild ähnliche Bilder anhand der Ähnlichkeit der Einbettungen. Dann notierst du die gemeinsamen Wörter in den Bildunterschriften der ähnlichen Bilder und druckst die Bildunterschrift mit den häufigsten Wörtern aus. Kurz gesagt, ein fauler Ansatz würde immer noch besser abschneiden als der modernste, was eine kritische Verzerrung des Datensatzes offenbart.

Diese Voreingenommenheit wurde von Larry Zitnick als Giraffen-Baum-Problem bezeichnet. Mach eine Bildersuche nach "Giraffe" in einer Suchmaschine. Sieh genau hin: Ist neben der Giraffe auch Gras auf fast jedem Bild zu sehen? Die Chancen stehen gut, dass du die meisten dieser Bilder mit "Eine Giraffe, die in einem Grasfeld steht" beschreiben kannst. Wenn ein Suchbild wie das Foto ganz links in Abbildung 4-24 eine Giraffe und einen Baum enthält, können fast alle ähnlichen Bilder (rechts) als "eine Giraffe, die im Gras neben einem Baum steht" beschrieben werden. Auch ohne ein tieferes Verständnis des Bildes würde man mit einer einfachen Suche nach den nächsten Nachbarn zur richtigen Beschriftung kommen. Das zeigt, dass wir mehr semantisch neue/ursprüngliche Bilder in der Testmenge brauchen, um die wirkliche Intelligenz eines Systems zu messen.

The Giraffe-Tree problem (image source: Measuring Machine Intelligence Through Visual Question Answering, C. Lawrence Zitnick, Aishwarya Agrawal, Stanislaw Antol, Margaret Mitchell, Dhruv Batra, Devi Parikh)
Abbildung 4-24. Das Giraffe-Baum-Problem (Bildquelle: Measuring Machine Intelligence Through Visual Question Answering, C. Lawrence Zitnick, Aishwarya Agrawal, Stanislaw Antol, Margaret Mitchell, Dhruv Batra, Devi Parikh)

Kurz gesagt: Unterschätze nicht den einfachen Nearest Neighbor-Ansatz!

Zusammenfassung

Jetzt sind wir am Ende einer erfolgreichen Expedition angelangt, bei der wir das Auffinden ähnlicher Bilder mit Hilfe von Einbettungen untersucht haben. Wir sind noch einen Schritt weiter gegangen und haben untersucht, wie sich die Suche mit Hilfe von ANN-Algorithmen und -Bibliotheken wie Annoy, NGT und Faiss von ein paar Tausend auf ein paar Milliarden Dokumente erweitern lässt. Wir haben auch gelernt, dass die Feinabstimmung des Modells auf deinen Datensatz die Genauigkeit und Repräsentativität von Einbettungen in einer überwachten Umgebung verbessern kann. Außerdem haben wir uns angeschaut, wie Siamesische Netze eingesetzt werden können, die die Leistung von Einbettungen für One-Shot-Lernen nutzen, z. B. für Gesichtsverifikationssysteme. Schließlich haben wir untersucht, wie Nearest-Neighbor-Ansätze in verschiedenen Anwendungsfällen in der Industrie eingesetzt werden. Nächste Nachbarn sind ein einfaches, aber leistungsstarkes Werkzeug, das du in deinem Werkzeugkasten haben solltest.

Get Praktisches Deep Learning für Cloud, Mobile und Edge now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.