Im Rahmen unserer »Fusonic Discovery Days« haben wir uns mit Produktempfehlungen beschäftigt, da wir gerade einen Shop umsetzen und das Thema perfekt passt. Innerhalb von zwei Tagen konnten wir Einblicke in die Welt des Machine-Learnings gewinnen und Erfahrungen sammeln.
- Author
- Thomas Rzipa
- Date
- October 25, 2023
- Reading time
- 5 Minutes
Recommender Systeme
Wer sich mit KI-Produktempfehlungen beschäftigt, findet schnell heraus, dass es verschiedene »Recommender Systeme« gibt, die je nach Anforderung auf andere Art und Weise funktionieren. Im Rahmen eines Onlineshops versucht ein »Recommender System« eine Vorhersage zu machen, wie stark sich ein:e Benutzer:in für ein bestimmtes Produkt interessieren könnte. Dafür gibt es zwei Ansätze:
Inhaltsbasierte Recommender Systeme
Diese Produktempfehlungen basieren auf ähnlichen Attributen verschiedener Produkte. Voraussetzung ist, dass die Produkte miteinander vergleichbar sind und definiert werden kann, wie ähnlich sie sich sind, z.B. durch die Anzahl der identischen Attribute wie Größe und Farbe, oder identische Produktkategorien. Mit diesen Informationen könnten zum Beispiel Alternativen zu nicht lieferbaren Produkten angezeigt werden, oder ähnliche Produkte für die Produktsuche generiert werden.
Kollaborative Recommender Systeme
Hier werden Produktempfehlungen anhand der Interaktion anderer User erstellt. Dadurch können z.B. Produkte vorgeschlagen werden, die oft miteinander gekauft werden, oder Produkte, die bei ähnlichen Benutzer:innen beliebt sind. Die Eigenschaften der Produkte sind dabei irrelevant.
Unsere Überlegungen zum Einbau von KI
Nachdem wir uns am ersten Tag über die Möglichkeiten schlau gemacht hatten, machten wir uns am zweiten Tag an die Umsetzung. Die Zeit war knapp, deswegen entschieden wir uns für ein einfaches System, das Produktempfehlungen ähnlich zu Amazons »Wird oft zusammen gekauft« generiert. Das kann recht einfach über einen modellbasierten kollaborativen Empfehlungsdienst auf Grundlage von Matrix-Factorization erstellt werden. Die Matrix ist simpel: Zeilen und Spalten bestehen aus den Ids aller Produkte und der Wert an Stelle XY bestimmt, ob die Produkte miteinander gekauft wurden. Die Informationen lassen sich direkt über die Bestellungen aus der Datenbank auslesen und damit wird ein Modell berechnet. Der Nachteil: Neue Produkte oder Bestellungen werden nicht berücksichtigt. Es gibt zwar die Möglichkeit, das Modell nachträglich zu erweitern, wir haben uns aber aus Zeitgründen dafür entschieden, es bei Bedarf neu zu erstellen.
Auf der Suche nach Beispielen und Tutorials für die Umsetzung von Produktvergleichen findet man sehr viel Material, oft wird Python verwendet. Ich kenne Python zwar, aber richtig wohl fühle ich mich in der .NET Welt. Umso größer war meine Freude, dass es eine komplette Bibliothek von Microsoft mit passendem Beispiel für genau unseren Anwendungsfall gab.
Unser Weg: Backend für Produktempfehlungen
Auf Basis unserer Überlegungen erstellten wir in kürzester Zeit erstellten ein Backend. Eine Klasse zur Repräsentation eines Eintrags in der Matrix. Den Key-Count der Properties geben wir einfachheitshalber als fixe Größe an:
public class ProductEntry
{
[KeyType(count: 35418)]
public uint ProductID { get; set; }
[KeyType(count: 35418)]
public uint CoPurchaseProductID { get; set; }
public Single Label { get; set; } = 0;
}
Eine Klasse, die den Vorhersage-Score darstellt:
public class Copurchase_prediction
{
public float Score { get; set; }
}
Und eine Klasse, die das generierte Modell aufnimmt und global zur Verfügung stellt. Normalerweise könnte das Modell abgespeichert und beim Start geladen werden.
public static class MyModel
{
// The prediction model
public static ITransformer model;
// The prediction engine
public static PredictionEngine<ProductEntry, Copurchase_prediction> predictionEngine;
// Helper dictionary mapping the productId to the matrix-index
public static Dictionary<uint, uint> productIdToIdMap = new ();
}
Ein Endpunkt zum Erstellen des Modells:
app.MapPost("/train", ([FromServices] MySqlConnection connection) =>
{
MLContext mlContext = new MLContext();
MyModel.productIdToIdMap.Clear();
// Step 1: Read data from database
var trainingDataQuery = connection.Query<(uint Productid, uint CoProductId)>(
"select pi.product_id as ProductId, pi2.product_id as CoProductId
from purchaseorder_items pi INNER JOIN purchaseorder_items pi2
on pi.purchase_order_id = pi2.purchase_order_id
AND pi.product_id != pi2.product_id AND pi2.product_id is not null
WHERE pi.product_id is not null;")
.Select(x => new ProductEntry { ProductID = x.Productid, CoPurchaseProductID = x.CoProductId})
.ToList();
uint index = 1;
foreach (var item in trainingDataQuery)
{
MyModel.productIdToIdMap.TryAdd(item.ProductID, index);
index++;
}
var trainingData = mlContext.Data.LoadFromEnumerable(trainingDataQuery);
//STEP 2: Specify options for MatrixFactorizationTrainer with a few extra hyperparameters
// LossFunction, Alpa, Lambda and a few others like K and C as shown below and call the trainer.
// This values are taken from the official example
MatrixFactorizationTrainer.Options options = new MatrixFactorizationTrainer.Options();
options.MatrixColumnIndexColumnName = nameof(ProductEntry.ProductID);
options.MatrixRowIndexColumnName = nameof(ProductEntry.CoPurchaseProductID);
options.LabelColumnName= nameof(ProductEntry.Label);
options.LossFunction = MatrixFactorizationTrainer.LossFunctionType.SquareLossOneClass;
options.Alpha = 0.01;
options.Lambda = 0.025;
// For better results use the following parameters
// options.K = 100;
options.C = 0.00001;
//Step 3: Call the MatrixFactorization trainer by passing options.
var trainer = mlContext.Recommendation().Trainers.MatrixFactorization(options);
//STEP 4: Train the model fitting to the DataSet
MyModel.model = trainer.Fit(traindata);
MyModel.predictionEngine = mlContext.Model.CreatePredictionEngine<ProductEntry, Copurchase_prediction>(MyModel.model);
return Results.Ok();
});
Im Frontend wurde noch auf der Produktdetail-Seite ein Aufruf der neuen API eingebaut und die Top-5-Produkte werden angezeigt.
Qualität der Vorhersage
Wir hatten zwar ein funktionierendes System, aber die Vorhersagen erschienen teilweise sehr willkürlich. Wir lernten schnell, dass das Vorhersagemodell nur so gut ist wie die Daten, mit denen es trainiert wird. Da über unseren Onlineshop oft Großbestellungen mit vielen Artikelarten getätigt werden, entstehen viele unsinnig erscheinende Vorschläge. Eventuell wäre ein Produktvergleichs-Modell, das zusätzlich inhaltsbasiert funktioniert, besser geeignet, um z.B. gemeinsam bestellte Artikel aus denselben Produktkategorien zu finden.
Unser Fazit zu Produktempfehlungen
Wir haben in den zwei Tagen zwar nur an der Oberfläche der Möglichkeiten von Machine-Learning und KI Produktempfehlungen gekratzt, dennoch konnten wir in kürzester Zeit und mit gut 100 Zeilen Code eine funktionierende Produktempfehlung umzusetzen. Dabei sind die Menge und Qualität der Trainingsdaten sowie das verwendete Machine-Learning-Modell ausschlaggebend für das Ergebnis.