Zum Abschluss unserer Serie über den Aufbau von Empfehlungsmodellen mit Sketchfab-Datenwerde ich mich weit weg von der vorherige (Beiträge)({{ ref “/weblog/implicit-mf-part-2” >}}) Faktorisierungsbasierte Methoden und erkunden stattdessen ein unbeaufsichtigtes, auf Deep Studying basierendes Modell. Sie werden feststellen, dass die Implementierung ziemlich einfach ist und bemerkenswert vielversprechende Ergebnisse liefert, die quick ein Schlag ins Gesicht all der zuvor investierten Anstrengungen sind.
Wir werden einen Modell-zu-Modell-Empfehlungsdienst mit Miniaturbildern von 3D erstellen Skizzenfab Modelle als Enter und die visuelle Ähnlichkeit zwischen den Modellen als Empfehlungswert. Ich wurde dazu inspiriert, nachdem ich Christopher Bonnetts Put up zur Produktklassifizierung, daher werden wir einen ähnlichen Ansatz verfolgen.
Da unser Ziel darin besteht, visuelle Ähnlichkeiten zu messen, müssen wir Merkmale aus unseren Bildern generieren und dann anhand dieser Merkmale ein Ähnlichkeitsmaß zwischen verschiedenen Bildern berechnen. Früher hätte man vielleicht ausgefallene Wavelets oder SIFT-Keypoints oder ähnliches verwendet, um Merkmale zu erstellen, aber wir leben im Zeitalter des Deep Studying und die manuelle Merkmalsextraktion ist etwas für alte Leute.
Um im Pattern zu bleiben, verwenden wir ein vorab trainiertes neuronales Netzwerk (NN), um Merkmale zu extrahieren. Das NN wurde ursprünglich darauf trainiert, Bilder anhand von 1000 Bezeichnungen (z. B. „Hund“, „Zug“ usw.) zu klassifizieren. Wir werden die letzten drei vollständig verbundenen Schichten des Netzwerks abtrennen, die die endgültige Zuordnung zwischen tiefen Merkmalen und Klassenbezeichnungen vornehmen, und die viertletzte Schicht als langen Merkmalsvektor verwenden, der unsere Bilder beschreibt.
Glücklicherweise ist dies alles mit den vorab trainierten Modellen in Keras. Keras ermöglicht es, Deep-Studying-Modelle auf Foundation von Tensorflow oder Theano einfach zu erstellen. Keras verfügt jetzt auch über vortrainierte Modelle, die geladen und verwendet werden können. Weitere Informationen zu den verfügbaren Modellen finden Sie unter Anwendungen Abschnitt der Dokumentation. Für unsere Zwecke verwenden wir die VGG16 Modell, weil es das ist, was andere Leute anscheinend verwendet haben und ich nicht genug weiß, um einen zwingenden Grund zu haben, von der Norm abzuweichen.
Unsere Aufgabe ist nun folgende:
- Bilder laden und verarbeiten
- Bilder durch NN leiten.
- Bildähnlichkeiten berechnen.
- Modelle empfehlen!
Bilder laden und verarbeiten
Der erste Schritt, den wir hier nicht durchgehen, battle das Herunterladen aller Bildvorschaubilder. Es scheint ein Standardvorschaubild für jedes Sketchfab-Modell zu geben, das über die API zugänglich ist, additionally habe ich eine Funktion zum rechne eine Skizze vor crawlen.py Skript zum automatischen Herunterladen aller Miniaturansichten.
Laden wir unsere Bibliotheken und schauen uns eines dieser Bilder an.
import csv
import sys
import requests
import skimage.io
import os
import glob
import pickle
import time
from IPython.show import show, Picture, HTML
from keras.functions import VGG16
from keras.functions.vgg16 import preprocess_input
from keras.preprocessing import picture as kimage
import numpy as np
import pandas as pd
import scipy.sparse as sp
import skimage.io
sys.path.append('../')
import helpers
rand_img = np.random.alternative(glob.glob('../knowledge/model_thumbs/*_thumb200.jpg'))
img = skimage.io.imread(rand_img)
img.form
(200, 200, 3)
Wir sehen, dass das Bild als 3D-Matrix mit zwei räumlichen Dimensionen (200 x 200) und einer dritten RGB-Dimension dargestellt werden kann. Bevor wir ein Bild durch das VGG16-Modell leiten, müssen wir einige Vorverarbeitungsschritte durchführen. Die Größe der Bilder muss auf 224 x 224 geändert werden, die Farbkanäle müssen normalisiert werden und eine zusätzliche Dimension muss hinzugefügt werden, da Keras mehrere Modelle erwartet. Glücklicherweise verfügt Keras über integrierte Funktionen, die das meiste davon bewältigen.
img = kimage.load_img(rand_img, target_size=(224, 224))
x = kimage.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
print(x.form)
(1, 224, 224, 3)
Wir können jetzt unser Modell laden und versuchen, das Bild durchzuspielen.
# image_top=False removes remaining related layers
mannequin = VGG16(include_top=False, weights='imagenet')
pred = mannequin.predict(x)
print(pred.form)
print(pred.ravel().form)
(1, 7, 7, 512)
(25088,)
Wir müssen die Ausgabe des Modells später in einen langen Merkmalsvektor umwandeln. Dabei sollte man die Zeit beachten, die es braucht, um ein einzelnes Modell durch das NN auf meiner 4-Core-Maschine laufen zu lassen:
%%timeit -n5
pred = mannequin.predict(x)
5 loops, greatest of three: 905 ms per loop
Das ist ziemlich umfangreich, wenn man bedenkt, dass wir 25.000 Bilder verarbeiten werden! Wir werden jetzt die oben genannten Vorverarbeitungsschritte für jedes Modell durchgehen, das wir in den vorherigen Blogbeiträgen zum Thema Empfehlung trainiert haben. Wir können diese Modelle finden, indem wir unsere „Likes“-Daten importieren, Modelle und Benutzer mit geringer Interaktion herausfiltern (wie zuvor) und die übrig gebliebenen Modelle auswählen.
df = pd.read_csv('../knowledge/model_likes_anon.psv',
sep='|', quoting=csv.QUOTE_MINIMAL,
quotechar='')
df.drop_duplicates(inplace=True)
df = helpers.threshold_interactions_df(df, 'uid', 'mid', 5, 5)
# model_ids to maintain
valid_mids = set(df.mid.distinctive())
Bilder durch NN einspeisen
Mit unserem Satz gültiger Modell-IDs in der Hand können wir nun den langen Prozess des Ladens aller Bilddateien, deren Vorverarbeitung und deren Ausführung durch den VGG
Vorhersage. Das dauert lange und bestimmte Schritte sprengen den Speicher. Ich habe mich entschieden, die Dinge unten zu bündeln und einige Druckanweisungen einzuschließen, damit man den Fortschritt verfolgen kann. Vorsicht: das dauert lange!
# Seize related filenames
get_mid = lambda x: x.cut up(os.path.sep)(-1).cut up('_')(0)
fnames = glob.glob('../knowledge/model_thumbs/*_thumb200.jpg')
fnames = (f for f in fnames if get_mid(f) in valid_mids)
idx_to_mid = {}
batch_size = 500
min_idx = 0
max_idx = min_idx + batch_size
total_max = len(fnames)
n_dims = preds.ravel().form(0)
px = 224
# Initialize predictions matrix
preds = sp.lil_matrix((len(fnames), n_dims))
whereas min_idx < total_max - 1:
t0 = time.time()
X = np.zeros(((max_idx - min_idx), px, px, 3))
# For every file in batch,
# load as row into X
for i in vary(min_idx, max_idx):
fname = fnames(i)
mid = get_mid(fname)
idx_to_mid(i) = mid
img = picture.load_img(fname, target_size=(px, px))
img_array = picture.img_to_array(img)
X(i - min_idx, :, :, :) = img_array
if i % 200 == 0 and i != 0:
t1 = time.time()
print('{}: {}'.format(i, (t1 - t0) / i))
t0 = time.time()
max_idx = i
t1 = time.time()
print('{}: {}'.format(i, (t1 - t0) / i))
print('Preprocess enter')
t0 = time.time()
X = preprocess_input(X)
t1 = time.time()
print('{}'.format(t1 - t0))
print('Predicting')
t0 = time.time()
these_preds = mannequin.predict(X)
shp = ((max_idx - min_idx) + 1, n_dims)
# Place predictions inside full preds matrix.
preds(min_idx:max_idx + 1, :) = these_preds.reshape(shp)
t1 = time.time()
print('{}'.format(t1 - t0))
min_idx = max_idx
max_idx = np.min((max_idx + batch_size, total_max))
Bildähnlichkeiten berechnen
Ich würde empfehlen, die Vorhersagen hier auf die Festplatte zu schreiben (Sie wollen ja nicht, dass der Kernel abstürzt und die ganze Arbeit verloren geht!). preds
Matrix besteht aus einer einzelnen Zeile für jedes Bild mit 25.088 spärlichen Merkmalen als Spalten. Um Artikel-Artikel-Empfehlungen zu berechnen, müssen wir diese Merkmalsmatrix in eine Ähnlichkeitsmatrix umwandeln.
def cosine_similarity(rankings):
sim = rankings.dot(rankings.T)
if not isinstance(sim, np.ndarray):
sim = sim.toarray()
norms = np.array((np.sqrt(np.diagonal(sim))))
return (sim / norms / norms.T)
preds = preds.tocsr()
sim = cosine_similarity(preds)
Modelle empfehlen!
Mithilfe der Ähnlichkeitsmatrix können wir einige alte Funktionen aus früheren Beiträgen wiederverwenden, um einige der Empfehlungen zu visualisieren. Ich habe etwas HTML hinzugefügt, sodass beim Klicken auf die Bilder Hyperlinks zu ihren Sketchfab-Seiten angezeigt werden. Schauen wir uns ein paar davon an!
def get_thumbnails(sim, idx, idx_to_mid, N=10):
row = sim(idx, :)
thumbs = ()
mids = ()
for x in np.argsort(-row)(:N):
response = requests.get('https://sketchfab.com/i/fashions/{}'
.format(idx_to_mid(x))).json()
thumb = (x('url') for x in response('thumbnails')('photographs')
if x('width') == 200 and x('top')==200)
if not thumb:
print('no thumbnail')
else:
thumb = thumb(0)
thumbs.append(thumb)
mids.append(idx_to_mid(x))
return thumbs, mids
def display_thumbs(thumbs, mids, N=5):
thumb_html = "<a href='{}' goal='_blank'>
<img model='width: 160px; margin: 0px;
border: 1px stable black; show:inline-block'
src='{}' /></a>"
photographs = "<div class='line' model='max-width: 640px; show: block;'>"
show(HTML('<font dimension=5>'+'Enter Mannequin'+'</font>'))
hyperlink = 'http://sketchfab.com/fashions/{}'.format(mids(0))
url = thumbs(0)
show(HTML(thumb_html.format(hyperlink, url)))
show(HTML('<font dimension=5>'+'Related Fashions'+'</font>'))
for (url, mid) in zip(thumbs(1:N+1), mids(1:N+1)):
hyperlink = 'http://sketchfab.com/fashions/{}'.format(mid)
photographs += thumb_html.format(hyperlink, url)
photographs += '</div>'
show(HTML(photographs))
Abschluss
Wow! Mit dieser völlig unbeaufsichtigten Methode und ohne Hyperparameter-Tuning erhalten wir auffallend intestine übereinstimmende Bilder. Das magazine etwas frustrierend sein – warum haben wir all diese Zeit mit diesen mathematisch aufwändigen, hirnfordernden Faktorisierungsalgorithmen verbracht, wenn wir einfach alles durch ein Deep-Studying-Modell laufen lassen könnten? Erstens kann es schwierig sein, Benutzer-zu-Artikel-Empfehlungen oder die Tag-Empfehlungen aus dem letzten Beitrag umzusetzen. Zweitens scheinen dieses visuelle Ähnlichkeitsmodell und die impliziten Suggestions-Modelle unterschiedlichen Zwecken zu dienen.
Das NN macht genau das, was wir erwarten – es findet ähnliche Bilder. Das implizite Suggestions-Modell findet andere Modelle, die ähnlichen Benutzern gefallen haben. Was normalerweise passiert, ist, dass die auf Likes basierenden Empfehlungen Modelle finden, die ähnliche Themen haben oder bestimmte Benutzergruppen ansprechen. Beispielsweise sehen wir möglicherweise, dass verschiedene Anime-Figuren zusammen gruppiert werden oder Darstellungen mittelalterlicher Rüstungen und Waffen. Wenn wir eine der mittelalterlichen Waffen in das NN einspeisen würden, würden wir andere Beispiele finden von nur genau diese Waffe, die sich wahrscheinlich über viele Zeiträume erstreckt.
Ich habe versucht, das LightFM-Modell mit diesem NN-Modell zu kombinieren, indem ich die NN-Ausgabefunktionen als Nebeninformationen im LightFM-Modell verwendet habe. Es gab normalerweise ~2500 von Null verschiedene NN-Funktionen für jedes Modell, was die Trainingszeit des LightFM-Modells völlig in die Höhe trieb. Es dauerte 30 Minuten, um die Präzision bei ok zu berechnen. Ich battle erschrocken bei der Vorstellung, Lernkurven und Rastersuchen zu berechnen, additionally habe ich aufgegeben! Vielleicht werde ich eines Tages eine riesige EC2-Field hochfahren und sehen, was passiert.
Im nächsten Beitrag schließe ich diese Serie ab, indem ich darüber schreibe, wie ich eine Flask-App auf AWS namens Rec-a-Sketch erstellt habe, um interaktive Sketchfab-Empfehlungen bereitzustellen. Danke fürs Lesen!