Likes vorhersagen: In den Algorithmen einer einfachen Empfehlungsmaschine
Veröffentlicht: 2022-03-11Eine Empfehlungsmaschine (manchmal auch als Empfehlungssystem bezeichnet) ist ein Tool, mit dem Algorithmusentwickler vorhersagen können, was einem Benutzer in einer Liste bestimmter Elemente gefallen könnte oder nicht. Empfehlungsmaschinen sind eine ziemlich interessante Alternative zu Suchfeldern, da Empfehlungsmaschinen Benutzern helfen, Produkte oder Inhalte zu entdecken, auf die sie sonst vielleicht nicht stoßen würden. Dies macht Empfehlungsmaschinen zu einem großen Teil von Websites und Diensten wie Facebook, YouTube, Amazon und mehr.
Empfehlungsmaschinen funktionieren idealerweise auf zwei Arten. Es kann sich auf die Eigenschaften der Artikel verlassen, die ein Benutzer mag, die analysiert werden, um zu bestimmen, was dem Benutzer sonst noch gefallen könnte; oder es kann sich auf die Vorlieben und Abneigungen anderer Benutzer verlassen, die die Empfehlungsmaschine dann verwendet, um einen Ähnlichkeitsindex zwischen Benutzern zu berechnen und ihnen entsprechend Artikel zu empfehlen. Es ist auch möglich, diese beiden Methoden zu kombinieren, um eine viel robustere Empfehlungsmaschine aufzubauen. Wie bei allen anderen informationsbezogenen Problemen ist es jedoch wichtig, einen Algorithmus auszuwählen, der für das zu behandelnde Problem geeignet ist.
In diesem Tutorial führen wir Sie durch den Prozess zum Erstellen einer Empfehlungsmaschine, die kollaborativ und speicherbasiert ist. Diese Empfehlungsmaschine empfiehlt den Benutzern Filme basierend auf ihren Vorlieben und Abneigungen und funktioniert wie das zweite zuvor erwähnte Beispiel. Für dieses Projekt verwenden wir grundlegende Mengenoperationen, ein wenig Mathematik und Node.js/CoffeeScript. Den gesamten für dieses Tutorial relevanten Quellcode finden Sie hier.
Mengen und Gleichungen
Bevor wir eine kollaborative speicherbasierte Empfehlungsmaschine implementieren, müssen wir zunächst die Kernidee hinter einem solchen System verstehen. Für diese Engine ist jedes Element und jeder Benutzer nichts anderes als Identifikatoren. Daher werden wir bei der Generierung von Empfehlungen keine anderen Attribute eines Films (z. B. Besetzung, Regisseur, Genre usw.) berücksichtigen. Die Ähnlichkeit zwischen zwei Benutzern wird durch eine Dezimalzahl zwischen -1,0 und 1,0 dargestellt. Wir nennen diese Zahl den Ähnlichkeitsindex. Schließlich wird die Möglichkeit, dass einem Benutzer ein Film gefällt, durch eine andere Dezimalzahl zwischen -1,0 und 1,0 dargestellt. Nachdem wir die Welt um dieses System herum mit einfachen Begriffen modelliert haben, können wir eine Handvoll eleganter mathematischer Gleichungen aufstellen, um die Beziehung zwischen diesen Bezeichnern und Zahlen zu definieren.
In unserem Empfehlungsalgorithmus werden wir eine Reihe von Sätzen pflegen. Jeder Benutzer hat zwei Sätze: einen Satz Filme, die der Benutzer mag, und einen Satz Filme, die der Benutzer nicht mag. Jedem Film sind außerdem zwei Gruppen zugeordnet: eine Gruppe von Benutzern, denen der Film gefallen hat, und eine Gruppe von Benutzern, denen der Film nicht gefallen hat. Während der Phasen, in denen Empfehlungen generiert werden, wird eine Reihe von Sets erstellt – meist Vereinigungen oder Schnittmengen der anderen Sets. Wir werden auch geordnete Listen mit Vorschlägen und ähnlichen Benutzern für jeden Benutzer haben.
Um den Ähnlichkeitsindex zu berechnen, verwenden wir eine Variation der Jaccard-Indexformel. Ursprünglich bekannt als „Coefficient de Commaute“ (geprägt von Paul Jaccard), vergleicht die Formel zwei Sätze und erzeugt eine einfache Dezimalstatistik zwischen 0 und 1,0:
Die Formel beinhaltet die Division der Anzahl gemeinsamer Elemente in beiden Mengen durch die Anzahl aller Elemente (nur einmal gezählt) in beiden Mengen. Der Jaccard-Index von zwei identischen Mengen ist immer 1, während der Jaccard-Index von zwei Mengen ohne gemeinsame Elemente immer 0 ergibt. Nachdem wir nun wissen, wie man zwei Mengen vergleicht, wollen wir uns eine Strategie überlegen, mit der wir zwei vergleichen können Benutzer. Wie bereits erwähnt, bestehen die Benutzer aus der Sicht des Systems aus drei Dingen: einem Identifizierer, einem Satz von Filmen, die ihm gefallen, und einem Satz von Filmen, die ihm nicht gefallen. Wenn wir den Ähnlichkeitsindex unserer Benutzer nur basierend auf den Filmen definieren würden, die ihnen gefallen, könnten wir direkt die Jaccard-Indexformel verwenden:
Hier sind U1 und U2 die beiden Benutzer, die wir vergleichen, und L1 und L2 sind die Filmsets, die U1 bzw. U2 gefallen haben. Nun, wenn Sie darüber nachdenken, zwei Benutzer, die dieselben Filme mögen, sind ähnlich, dann sollten zwei Benutzer, die dieselben Filme nicht mögen, auch ähnlich sein. Hier modifizieren wir die Gleichung ein wenig:
Anstatt nur die gemeinsamen Vorlieben im Zähler der Formel zu berücksichtigen, addieren wir jetzt auch die Anzahl der gemeinsamen Abneigungen. Im Nenner nehmen wir die Anzahl aller Artikel, die einem der Benutzer gefallen oder nicht gefallen haben. Nachdem wir sowohl Vorlieben als auch Abneigungen auf unabhängige Weise betrachtet haben, sollten wir auch über den Fall nachdenken, in dem zwei Benutzer in ihren Vorlieben völlig gegensätzlich sind. Der Ähnlichkeitsindex zweier Benutzer, bei denen der eine einen Film mag und der andere ihn nicht mag, sollte nicht 0 sein:
Das ist eine lange Formel! Aber es ist einfach, versprochen. Sie ähnelt unserer vorherigen Formel mit einem kleinen Unterschied im Zähler. Wir subtrahieren nun die Anzahl der widersprüchlichen Vorlieben und Abneigungen der beiden Benutzer von der Anzahl ihrer gemeinsamen Vorlieben und Abneigungen. Dadurch weist die Ähnlichkeitsindexformel einen Wertebereich zwischen -1,0 und 1,0 auf. Zwei Benutzer mit identischem Geschmack haben einen Ähnlichkeitsindex von 1,0, während zwei Benutzer mit völlig widersprüchlichen Filmvorlieben einen Ähnlichkeitsindex von -1,0 haben.
Jetzt, da wir wissen, wie man zwei Benutzer basierend auf ihrem Filmgeschmack vergleicht, müssen wir eine weitere Formel untersuchen, bevor wir mit der Implementierung unseres hausgemachten Empfehlungsmaschinenalgorithmus beginnen können:
Lassen Sie uns diese Gleichung ein wenig herunterbrechen. Was wir mit P(U,M)
meinen, ist die Möglichkeit, dass ein Benutzer U
den Film M
mag. ZL
und ZD
sind die Summe der Ähnlichkeitsindizes des Benutzers U
mit allen Benutzern, denen der Film M
gefallen bzw. nicht gefallen hat. |ML|+|MD|
stellt die Gesamtzahl der Benutzer dar, die den Film M
gemocht oder nicht gemocht haben. Das Ergebnis P(U,M)
ergibt eine Zahl zwischen -1,0 und 1,0.
Das ist alles. Im nächsten Abschnitt können wir diese Formeln verwenden, um mit der Implementierung unserer kollaborativen speicherbasierten Empfehlungsmaschine zu beginnen.
Aufbau der Empfehlungsmaschine
Wir werden diese Empfehlungsmaschine als sehr einfache Node.js-Anwendung erstellen. Es wird auch sehr wenig Arbeit am Frontend geben, hauptsächlich einige HTML-Seiten und Formulare (wir werden Bootstrap verwenden, damit die Seiten ordentlich aussehen). Auf der Serverseite verwenden wir CoffeeScript. Die Anwendung verfügt über einige GET- und POST-Routen. Obwohl wir in der Anwendung die Vorstellung von Benutzern haben werden, werden wir keinen aufwändigen Registrierungs-/Anmeldemechanismus haben. Für die Persistenz verwenden wir das über NPM verfügbare Bourne-Paket, das es einer Anwendung ermöglicht, Daten in einfachen JSON-Dateien zu speichern und grundlegende Datenbankabfragen darauf durchzuführen. Wir werden Express.js verwenden, um die Verwaltung der Routen und Handler zu vereinfachen.
Wenn Sie neu in der Node.js-Entwicklung sind, möchten Sie an dieser Stelle vielleicht das GitHub-Repository klonen, damit Sie diesem Tutorial leichter folgen können. Wie bei jedem anderen Node.js-Projekt beginnen wir mit der Erstellung einer package.json-Datei und der Installation einer Reihe von Abhängigkeitspaketen, die für dieses Projekt erforderlich sind. Wenn Sie das geklonte Repository verwenden, sollte die Datei „package.json“ bereits vorhanden sein, von wo aus Sie zum Installieren der Abhängigkeiten „$ npm install“ ausführen müssen. Dadurch werden alle Pakete installiert, die in der Datei „package.json“ aufgeführt sind.
Die Node.js-Pakete, die wir für dieses Projekt benötigen, sind:
- asynchron
- geboren
- Kaffee-Skript
- ausdrücken
- Jade
- unterstreichen
Wir werden die Empfehlungsmaschine bauen, indem wir alle relevanten Methoden in vier separate CoffeeScript-Klassen aufteilen, von denen jede unter „lib/engine“ gespeichert wird: Engine, Rater, Similars und Suggestions. Die Klasse Engine ist für die Bereitstellung einer einfachen API für die Empfehlungsmaschine verantwortlich und verbindet die anderen drei Klassen miteinander. Der Bewerter ist für das Verfolgen von Vorlieben und Abneigungen verantwortlich (als zwei separate Instanzen der Klasse Bewerter). Similars and Suggestions ist dafür verantwortlich, ähnliche Benutzer bzw. empfohlene Artikel für die Benutzer zu ermitteln und zu verfolgen.
Vorlieben und Abneigungen verfolgen
Beginnen wir zunächst mit unserer Rater-Klasse. Dies ist eine einfache:
class Rater constructor: (@engine, @kind) -> add: (user, item, done) -> remove: (user, item, done) -> itemsByUser: (user, done) -> usersByItem: (item, done) ->
Wie bereits in diesem Tutorial erwähnt, haben wir eine Instanz von Rater für Likes und eine andere für Dislikes. Um aufzuzeichnen, dass einem Benutzer ein Artikel gefällt, leiten wir ihn an „Rater#add()“ weiter. Um die Bewertung zu entfernen, übergeben wir sie auf ähnliche Weise an „Rater#remove()“.
Da wir Bourne als serverlose Datenbanklösung verwenden, speichern wir diese Bewertungen in einer Datei mit dem Namen „./db-#{@kind}.json“, wobei kind entweder „likes“ oder „dislikes“ ist. Wir öffnen die Datenbank im Konstruktor der Rater-Instanz:
constructor: (@engine, @kind) -> @db = new Bourne "./db-#{@kind}.json"
Dadurch wird das Hinzufügen von Bewertungsdatensätzen so einfach wie das Aufrufen einer Bourne-Datenbankmethode innerhalb unserer „Rater#add()“-Methode:
@db.insert user: user, item: item, (err) =>
Und es ist ähnlich, sie zu entfernen („db.delete“ statt „db.insert“). Bevor wir jedoch etwas hinzufügen oder entfernen, müssen wir sicherstellen, dass es nicht bereits in der Datenbank vorhanden ist. Mit einer echten Datenbank hätten wir das idealerweise in einem einzigen Vorgang erledigen können. Bei Bourne müssen wir zuerst eine manuelle Überprüfung durchführen; und sobald das Einfügen oder Löschen abgeschlossen ist, müssen wir sicherstellen, dass wir die Ähnlichkeitsindizes für diesen Benutzer neu berechnen und dann eine Reihe neuer Vorschläge generieren. Die Methoden „Rater#add()“ und „Rater#remove()“ sehen in etwa so aus:
add: (user, item, done) -> @db.find user: user, item: item, (err, res) => if res.length > 0 return done() @db.insert user: user, item: item, (err) => async.series [ (done) => @engine.similars.update user, done (done) => @engine.suggestions.update user, done ], done remove: (user, item, done) -> @db.delete user: user, item: item, (err) => async.series [ (done) => @engine.similars.update user, done (done) => @engine.suggestions.update user, done ], done
Der Kürze halber überspringen wir die Teile, in denen wir nach Fehlern suchen. Dies mag in einem Artikel sinnvoll sein, ist aber keine Entschuldigung dafür, Fehler in echtem Code zu ignorieren.
Die anderen beiden Methoden, „Rater#itemsByUser()“ und „Rater#usersByItem()“ dieser Klasse beinhalten das, was ihre Namen andeuten – das Nachschlagen von Elementen, die von einem Benutzer bzw. von Benutzern bewertet wurden, die ein Element bewertet haben. Wenn der Bewerter beispielsweise mit kind = “likes”
instanziiert wird, findet „Bewerter#itemsByUser()“ alle Elemente, die der Benutzer bewertet hat.
Ähnliche Benutzer finden
Weiter zu unserer nächsten Klasse: Similars. Diese Klasse hilft uns, die Ähnlichkeitsindizes zwischen den Benutzern zu berechnen und zu verfolgen. Wie bereits erwähnt, beinhaltet die Berechnung der Ähnlichkeit zwischen zwei Benutzern die Analyse der Sätze von Elementen, die sie mögen und nicht mögen. Dazu verlassen wir uns auf die Rater-Instanzen, um die Sätze relevanter Elemente abzurufen, und bestimmen dann den Ähnlichkeitsindex für bestimmte Benutzerpaare mithilfe der Ähnlichkeitsindexformel.
Genau wie unsere vorherige Klasse Rater werden wir alles in einer Bourne-Datenbank namens „./db-similars.json“ ablegen, die wir im Konstruktor von Rater öffnen werden. Die Klasse wird eine Methode „Similars#byUser()“ haben, die es uns ermöglicht, Benutzer zu finden, die einem bestimmten Benutzer durch eine einfache Datenbanksuche ähnlich sind:
@db.findOne user: user, (err, {others}) =>
Die wichtigste Methode dieser Klasse ist jedoch „Similars#update()“, die funktioniert, indem sie einen Benutzer nimmt und eine Liste anderer Benutzer berechnet, die ähnlich sind, und die Liste zusammen mit ihren Ähnlichkeitsindizes in der Datenbank speichert. Es beginnt damit, die Vorlieben und Abneigungen des Benutzers zu finden:

async.auto userLikes: (done) => @engine.likes.itemsByUser user, done userDislikes: (done) => @engine.dislikes.itemsByUser user, done , (err, {userLikes, userDislikes}) => items = _.flatten([userLikes, userDislikes])
Wir finden auch alle Benutzer, die diese Artikel bewertet haben:
async.map items, (item, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.usersByItem item, done , done , (err, others) =>
Als nächstes berechnen wir für jeden dieser anderen Benutzer den Ähnlichkeitsindex und speichern alles in der Datenbank:
async.map others, (other, done) => async.auto otherLikes: (done) => @engine.likes.itemsByUser other, done otherDislikes: (done) => @engine.dislikes.itemsByUser other, done , (err, {otherLikes, otherDislikes}) => done null, user: other similarity: (_.intersection(userLikes, otherLikes).length+_.intersection(userDislikes, otherDislikes).length-_.intersection(userLikes, otherDislikes).length-_.intersection(userDislikes, otherLikes).length) / _.union(userLikes, otherLikes, userDislikes, otherDislikes).length , (err, others) => @db.insert user: user others: others , done
Im obigen Snippet werden Sie feststellen, dass wir einen Ausdruck haben, der von Natur aus mit unserer Ähnlichkeitsindexformel, einer Variante der Jaccard-Indexformel, identisch ist.
Generieren von Empfehlungen
In unserer nächsten Klasse, Vorschläge, finden alle Vorhersagen statt. Wie die Klasse Similars verlassen wir uns auf eine andere Bourne-Datenbank namens „./db-suggestions.json“, die im Konstruktor geöffnet wird.
Die Klasse hat eine Methode „Suggestions#forUser()“, um berechnete Vorschläge für den angegebenen Benutzer zu suchen:
forUser: (user, done) -> @db.findOne user: user, (err, {suggestions}={suggestion: []}) -> done null, suggestions
Die Methode, die diese Ergebnisse berechnet, ist „Suggestions#update()“. Diese Methode, wie „Similars#update()“, nimmt einen Benutzer als Argument. Die Methode beginnt mit der Auflistung aller Benutzer, die dem angegebenen Benutzer ähnlich sind, und aller Elemente, die der angegebene Benutzer nicht bewertet hat:
@engine.similars.byUser user, (err, others) => async.auto likes: (done) => @engine.likes.itemsByUser user, done dislikes: (done) => @engine.dislikes.itemsByUser user, done items: (done) => async.map others, (other, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.itemsByUser other.user, done , done , done , (err, {likes, dislikes, items}) => items = _.difference _.unique(_.flatten items), likes, dislikes
Sobald wir alle anderen Benutzer und die nicht bewerteten Elemente aufgelistet haben, können wir mit der Berechnung eines neuen Satzes von Empfehlungen beginnen, indem wir alle vorherigen Empfehlungen entfernen, über jeden Artikel iterieren und die Möglichkeit berechnen, dass der Benutzer es auf der Grundlage der verfügbaren Informationen mag:
@db.delete user: user, (err) => async.map items, (item, done) => async.auto likers: (done) => @engine.likes.usersByItem item, done dislikers: (done) => @engine.dislikes.usersByItem item, done , (err, {likers, dislikers}) => numerator = 0 for other in _.without _.flatten([likers, dislikers]), user other = _.findWhere(others, user: other) if other? numerator += other.similarity done null, item: item weight: numerator / _.union(likers, dislikers).length , (err, suggestions) =>
Sobald dies erledigt ist, speichern wir es wieder in der Datenbank:
@db.insert user: user suggestions: suggestions , done
Verfügbarmachen der Bibliotheks-API
Innerhalb der Engine-Klasse binden wir alles in einer ordentlichen API-ähnlichen Struktur für den einfachen Zugriff von der Außenwelt:
class Engine constructor: -> @likes = new Rater @, 'likes' @dislikes = new Rater @, 'dislikes' @similars = new Similars @ @suggestions = new Suggestions @
Sobald wir ein Engine-Objekt instanziiert haben:
e = new Engine
Wir können Vorlieben und Abneigungen ganz einfach hinzufügen oder entfernen:
e.likes.add user, item, (err) -> e.dislikes.add user, item, (err) ->
Wir können auch mit der Aktualisierung von Benutzerähnlichkeitsindizes und -vorschlägen beginnen:
e.similars.update user, (err) -> e.suggestions.update user, (err) ->
Schließlich ist es wichtig, diese Engine-Klasse (und alle anderen Klassen) aus ihren jeweiligen „.coffee“-Dateien zu exportieren:
module.exports = Engine
Exportieren Sie dann die Engine aus dem Paket, indem Sie eine „index.coffee“-Datei mit einer einzigen Zeile erstellen:
module.exports = require './engine'
Erstellen der Benutzeroberfläche
Um den Algorithmus der Empfehlungsmaschine in diesem Tutorial verwenden zu können, möchten wir eine einfache Benutzeroberfläche über das Web bereitstellen. Dazu erstellen wir eine Express-App in unserer „web.iced“-Datei und handhaben einige Routen:
movies = require './data/movies.json' Engine = require './lib/engine' e = new Eengine app = express() app.set 'views', "#{__dirname}/views" app.set 'view engine', 'jade' app.route('/refresh') .post(({query}, res, next) -> async.series [ (done) => e.similars.update query.user, done (done) => e.suggestions.update query.user, done ], (err) => res.redirect "/?user=#{query.user}" ) app.route('/like') .post(({query}, res, next) -> if query.unset is 'yes' e.likes.remove query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" else e.dislikes.remove query.user, query.movie, (err) => e.likes.add query.user, query.movie, (err) => if err? return next err res.redirect "/?user=#{query.user}" ) app.route('/dislike') .post(({query}, res, next) -> if query.unset is 'yes' e.dislikes.remove query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" else e.likes.remove query.user, query.movie, (err) => e.dislikes.add query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" ) app.route('/') .get(({query}, res, next) -> async.auto likes: (done) => e.likes.itemsByUser query.user, done dislikes: (done) => e.dislikes.itemsByUser query.user, done suggestions: (done) => e.suggestions.forUser query.user, (err, suggestions) => done null, _.map _.sortBy(suggestions, (suggestion) -> -suggestion.weight), (suggestion) => _.findWhere movies, id: suggestion.item , (err, {likes, dislikes, suggestions}) => res.render 'index', movies: movies user: query.user likes: likes dislikes: dislikes suggestions: suggestions[...4] )
Innerhalb der App verwalten wir vier Routen. In der Indexroute „/“ stellen wir das Front-End-HTML bereit, indem wir ein Jade-Template rendern. Das Generieren der Vorlage erfordert eine Liste von Filmen, den Benutzernamen des aktuellen Benutzers, die Vorlieben und Abneigungen des Benutzers und die vier besten Vorschläge für den Benutzer. Der Quellcode des Jade-Templates wird im Artikel weggelassen, ist aber im GitHub-Repository verfügbar.
Auf den Routen „/like“ und „/dislike“ akzeptieren wir POST-Anforderungen, um die Vorlieben und Abneigungen des Benutzers aufzuzeichnen. Beide Routen fügen eine Bewertung hinzu, indem sie ggf. zunächst alle widersprüchlichen Bewertungen entfernen. Wenn beispielsweise ein Benutzer etwas mag, das ihm zuvor nicht gefallen hat, veranlasst dies den Handler, zuerst die Bewertung „Mag ich nicht“ zu entfernen. Diese Routen ermöglichen es dem Benutzer auch, einen Artikel bei Bedarf „nicht zu mögen“ oder „nicht zu mögen“.
Schließlich ermöglicht die „/refresh“-Route dem Benutzer, seinen Satz von Empfehlungen bei Bedarf zu regenerieren. Diese Aktion wird jedoch automatisch ausgeführt, wenn der Benutzer ein Element bewertet.
Probefahrt
Wenn Sie versucht haben, diese Anwendung anhand dieses Artikels von Grund auf neu zu implementieren, müssen Sie einen letzten Schritt ausführen, bevor Sie sie testen können. Sie müssen eine „.json“-Datei unter „data/movies.json“ erstellen und sie mit einigen Filmdaten wie folgt füllen:
[ { "id": "1", "name": "Transformers: Age of Extinction", "thumb": { "url": "//upload.wikimedia.org/wikipedia/en/7/7f/Inception_ver3.jpg" } }, // … ]
Vielleicht möchten Sie die im GitHub-Repository verfügbare kopieren, die mit einer Handvoll Filmnamen und Miniaturbild-URLs vorbelegt ist.
Sobald der gesamte Quellcode fertig und miteinander verbunden ist, muss zum Starten des Serverprozesses der folgende Befehl aufgerufen werden:
$ npm start
Vorausgesetzt, dass alles reibungslos gelaufen ist, sollten Sie den folgenden Text auf dem Terminal sehen:
Listening on 5000
Da wir kein echtes Benutzerauthentifizierungssystem implementiert haben, stützt sich die Prototypanwendung nur auf einen Benutzernamen, der nach dem Besuch von „http://localhost:5000“ ausgewählt wurde. Sobald ein Benutzername eingegeben und das Formular abgeschickt wurde, sollten Sie zu einer anderen Seite mit zwei Abschnitten weitergeleitet werden: „Empfohlene Filme“ und „Alle Filme“. Da uns das wichtigste Element einer kollaborativen speicherbasierten Empfehlungsmaschine (Daten) fehlt, können wir diesem neuen Benutzer keine Filme empfehlen.
An dieser Stelle sollten Sie ein weiteres Browserfenster mit „http://localhost:5000“ öffnen und sich dort als anderer Benutzer anmelden. Liken und disliken Sie einige Filme als dieser zweite Benutzer. Kehren Sie zum Browserfenster des ersten Benutzers zurück und bewerten Sie auch einige Filme. Stellen Sie sicher, dass Sie mindestens ein paar gemeinsame Filme für beide Benutzer bewerten. Sie sollten sofort Empfehlungen sehen.
Verbesserungen
In diesem Algorithmus-Tutorial haben wir einen Prototyp einer Empfehlungsmaschine erstellt. Es gibt sicherlich Möglichkeiten, diesen Motor zu verbessern. In diesem Abschnitt werden einige Bereiche kurz angesprochen, in denen Verbesserungen erforderlich sind, damit dies in großem Umfang eingesetzt werden kann. In Fällen, in denen Skalierbarkeit, Stabilität und ähnliche Eigenschaften erforderlich sind, sollten Sie jedoch immer auf eine gute und bewährte Lösung zurückgreifen. Wie der Rest des Artikels besteht die Idee hier darin, einen Einblick in die Funktionsweise einer Empfehlungsmaschine zu geben. Anstatt die offensichtlichen Mängel der aktuellen Methode (wie z. B. Race Condition in einigen der von uns implementierten Methoden) zu diskutieren, werden Verbesserungen auf einer höheren Ebene diskutiert.
Eine sehr offensichtliche Verbesserung hier ist die Verwendung einer echten Datenbank anstelle unserer dateibasierten Lösung. Die dateibasierte Lösung mag in einem kleinen Prototypen gut funktionieren, aber für den realen Einsatz ist sie überhaupt keine vernünftige Wahl. Eine Option unter vielen ist Redis. Redis ist schnell und verfügt über spezielle Funktionen, die beim Umgang mit satzartigen Datenstrukturen nützlich sind.
Ein weiteres Problem, das wir einfach umgehen können, ist die Tatsache, dass wir jedes Mal neue Empfehlungen berechnen, wenn ein Benutzer seine Bewertungen für Filme vornimmt oder ändert. Anstatt spontane Neuberechnungen in Echtzeit durchzuführen, sollten wir diese Empfehlungsaktualisierungsanfragen für die Benutzer in eine Warteschlange stellen und sie hinter den Kulissen durchführen – vielleicht durch Festlegen eines zeitgesteuerten Aktualisierungsintervalls.
Neben diesen „technischen“ Entscheidungen gibt es auch einige strategische Entscheidungen, die getroffen werden können, um die Empfehlungen zu verbessern. Mit zunehmender Anzahl von Artikeln und Benutzern wird es immer kostspieliger (in Bezug auf Zeit und Systemressourcen), Empfehlungen zu generieren. Es ist möglich, dies zu beschleunigen, indem Sie nur eine Teilmenge von Benutzern auswählen, von denen Empfehlungen generiert werden, anstatt jedes Mal die gesamte Datenbank zu verarbeiten. Wenn es sich beispielsweise um eine Empfehlungsmaschine für Restaurants handelt, könnten Sie den Satz ähnlicher Benutzer so einschränken, dass er nur die Benutzer enthält, die in derselben Stadt oder demselben Bundesstaat leben.
Andere Verbesserungen können einen hybriden Ansatz beinhalten, bei dem Empfehlungen sowohl basierend auf kollaborativer Filterung als auch auf inhaltsbasierter Filterung generiert werden. Dies wäre besonders gut bei Inhalten wie Filmen, wo die Eigenschaften des Inhalts gut definiert sind. Netflix zum Beispiel geht diesen Weg und empfiehlt Filme basierend auf den Aktivitäten anderer Benutzer und den Attributen der Filme.
Fazit
Speicherbasierte kollaborative Empfehlungs-Engine-Algorithmen können eine ziemlich mächtige Sache sein. Das, mit dem wir in diesem Artikel experimentiert haben, mag primitiv sein, aber es ist auch einfach: einfach zu verstehen und einfach zu erstellen. Es mag alles andere als perfekt sein, aber robuste Implementierungen von Empfehlungsmaschinen wie Recommendable basieren auf ähnlichen Grundideen.
Wie bei den meisten anderen Informatikproblemen, die viele Daten beinhalten, hängt das Erhalten korrekter Empfehlungen stark von der Auswahl des richtigen Algorithmus und der geeigneten Attribute des zu bearbeitenden Inhalts ab. Ich hoffe, dieser Artikel hat Ihnen einen Einblick in das gegeben, was in einer kollaborativen speicherbasierten Empfehlungsmaschine passiert, wenn Sie sie verwenden.