Freigeben über



Januar 2019

Band 34, Nummer 1

[.NET]

Machine Learning über probabilistische Programmierung

Von Yordan Zaykov | Januar 2019

Programmierung, die probabilistisch ist? Wirklich? Das macht nicht viel Sinn... So habe ich jedenfalls gedacht, als ich anfing, in diesem Bereich zu arbeiten. Die Wissenschaftler, denen ich zugehört habe, hatten nicht die traditionelle Sichtweise, Machine Learning-Probleme (ML) in Kategorien einzuteilen. Stattdessen drückten sie nur die realen Prozesse aus, die zur Generierung ihrer Daten in einer computerlesbaren Form führen. Aus diesem Grund nennen sie ein Modell und seine Darstellung ein probabilistisches Programm.

Das Paradigma ist eigentlich recht ansprechend. Erstens müssen Sie nicht Hunderte von ML-Algorithmen erlernen, die da draußen verfügbar sind. Sie müssen nur lernen, wie Sie Ihre Probleme in einem probabilistischen Programm ausdrücken können. Dies erfordert einige Kenntnisse der Statistik, da Sie die Unsicherheit der realen Welt modellieren. Nehmen wir zum Beispiel an, Sie möchten den Preis eines Hauses vorhersagen. Sie halten diese Vorhersage für eine lineare Kombination einiger Merkmale (Lage, Größe usw.). Ihr Modell besagt, dass der Preis die Summe der Produkte jedes Merkmals und einer gewissen Gewichtung ist, mit etwas zusätzlichem Rauschen. Dies wird auch als linearer Regressor bezeichnet. Abstrakt ausgedrückt:

For each house:
  score = Weights * house.Features;
  house.Price = score + Noise;

Sie können die Gewichtungen während des Trainings erlernen und dann direkt in der Vorhersage verwenden. Wenn Sie eine kleine Änderung in Ihrem Modell vornehmen, indem Sie den Rauschfaktor am Endschwellenwert gegen Null gehen lassen, ist die neue Bezeichnung plötzlich eine von zwei Klassen. Sie haben soeben einen binären linearen Klassifizierer modelliert, vielleicht ohne überhaupt zu wissen, wie er genannt wird.

Zweitens müssen Sie nicht versuchen, Ihr Problem und Ihre Daten an einen der vorhandenen ML-Algorithmen anzupassen. Das sollte offensichtlich sein: Sie haben das Modell für Ihr Problem entworfen, daher passt es zu Ihren Daten. Moderne probabilistische Programmiertools können mithilfe einer allgemeinen Inferenzmethode automatisch einen ML-Algorithmus aus dem von Ihnen angegebenen Modell generieren. Sie müssen nicht einmal viel darüber wissen, denn es wurde bereits für Sie implementiert. Ein anwendungsspezifisches Modell in Kombination mit einer allgemeinen Inferenzmethode ergibt also einen anwendungsspezifischen ML-Algorithmus.

Schließlich scheint der Ansatz dem Praxistest standzuhalten. Die meisten erfolgreichen ML-Algorithmen passen in dieses Paradigma: ein Modell, das eine Reihe von Annahmen darstellt, sowie eine Inferenzmethode, die die Berechnungen durchführt. Die Methodik hat sich im Lauf der Zeit weiterentwickelt. Heutzutage sind Deep Neural Networks sehr beliebt. Das Modell ist eine Kombination von linearen Schwellenwertfunktionen, und die Inferenzmethode wird als stochastischer Gradientenabstieg (Stochastic Gradient Descent, SGD) bezeichnet. Die Änderung der Struktur des Netzwerks, um rekurrent oder konvolutionär zu sein (d.h. die Änderung des Modells), ermöglicht die Ausrichtung auf verschiedene Anwendungen. Aber die Inferenzmethode kann gleich bleiben: SGD, wie in Abbildung 1 gezeigt. Obwohl sich die Wahl des Modells im Lauf der Jahre weiterentwickelt hat, ist der allgemeine Ansatz, ein Modell zu entwerfen und eine Inferenzmethode anzuwenden, gleich geblieben.

Unterschiedliche Modelle für verschiedene Anwendungen, die alle die gleiche Inferenzmethode verwenden
Abbildung 1: Unterschiedliche Modelle für verschiedene Anwendungen, die alle die gleiche Inferenzmethode verwenden

Die Aufgabe für den Entwickler besteht also darin, ein Modell für seine Anwendung zu entwerfen. Schauen wir uns an, wie das funktioniert.

Hello, Uncertain World!

Das erste Programm, das jeder Entwickler in einer neuen Sprache schreibt, ist „Hello World“. Das Äquivalent in einem probabilistischen Ansatz ist natürlich „Hello uncertain world“. Sie können sich das probabilistische Programm als Simulation oder Datensammler vorstellen. Ich beginne mit einigen Parametern und verwende sie, um Daten zu generieren. Gehen wir beispielsweise von zwei Zeichenfolgen aus, die völlig unbekannt sind. Das heißt, sie können eine beliebige Zeichenfolge sein. Statistisch gesehen sind sie Zeichenfolgen-Zufallsvariablen, die aus einer einheitlichen Verteilung stammen. Ich verkette sie mit einem Leerzeichen in der Mitte und schränke das Ergebnis auf die Zeichenfolge „Hello, uncertain world“ ein:

str1 = String.Uniform()
str2 = String.Uniform()
Constrain(str1 + " " + str2 == "Hello, uncertain world")
Print(str1)
Print(str2)

Dies ist ein probabilistisches Modell: Ich habe meine Annahmen darüber dargelegt, wie die Daten generiert wurden. Jetzt kann ich eine Inferenzmethode ausführen, die die notwendigen Berechnungen durchführt. Das Leerzeichen zwischen den beiden Zeichenfolgen kann eines der beiden Leerzeichen sein: das Leerzeichen zwischen „Hello“ und „uncertain“ oder das Leerzeichen zwischen „uncertain“ und „world“. Daher kann ich mir über den Wert der beiden Variablen nicht sicher sein. Das Ergebnis ist daher eine Verteilung, die die Unsicherheit zu den möglichen Werte erfasst. Str1 ist mit gleicher Wahrscheinlichkeit „Hello“ oder „Hello uncertain“, und str2 ist zu 50 Prozent „uncertain world“ und zu 50 Prozent „world“.

Ein Beispiel aus der Praxis

Betrachten wir nun ein realistischeres Beispiel. Probabilistische Programmierung kann verwendet werden, um eine enorme Bandbreite von ML-Problemen zu lösen. So hat mein Team beispielsweise vor einiger Zeit ein Empfehlungssystem entwickelt und in Azure Machine Learning bereitgestellt. Davor haben wir in Exchange einen E-Mail-Klassifizierer realisiert. Zurzeit arbeiten wir daran, die Spielerzusammenführung in Xbox zu verbessern, indem wir das Qualifikationsbewertungssystem verbessern. Wir sind außerdem dabei, ein System für Bing zu entwickeln, das durch die Modellierung von unstrukturiertem Text automatisch Wissen aus dem Internet extrahiert. All dies wird mit dem gleichen Paradigma erreicht: Definieren des Modells als eine Reihe von Annahmen, die in einem probabilistischen Programm dargestellt werden, und anschließendes Vornehmen der erforderlichen Berechnungen durch eine allgemeine Inferenzmethode.

Das Qualifikationsbewertungssystem namens TrueSkill demonstriert viele der Vorteile der probabilistischen Programmierung, einschließlich der Fähigkeit, das Verhalten des Systems zu interpretieren, ein Wissensgebiet in das Modell zu integrieren und zu lernen, wenn neue Daten eingehen. Implementieren wir also eine vereinfachte Version des Modells, das in der Produktion in Blockbustern wie „Halo“ und „Gears of War“ zum Einsatz kommt.

Problem und Daten: Das zu lösende Problem ist die Bewertung von Spielern in Spielen. Für dieses Problem gibt es viele Einsatzmöglichkeiten, z.B. die Vermittlung von Spielern (um faire und unterhaltsame Spiele durch passende Spieler oder Teams mit einer ähnlichen Qualifikation zu erreichen). Nehmen wir der Einfachheit halber an, dass es in jedem Spiel nur zwei Teilnehmer gibt und dass das Ergebnis ein Sieg oder eine Niederlage ohne Unentschieden ist. Die Daten für jede Paarung sind also die eindeutigen Bezeichner der beiden Spieler und ein Indikator dafür, wer gewinnt. Ich werde in diesem Artikel ein kleines manuell erstelltes Dataset verwenden, aber der Ansatz lässt sich auf Hunderte von Millionen von Paarungen in Xbox skalieren.

Ich möchte die Qualifikation aller Spieler kennenlernen und den Gewinner zukünftiger Paarungen vorhersagen können. Ein naiver Ansatz wäre es, einfach die Anzahl der Gewinne und Niederlagen jedes Spielers zu zählen, aber dabei wird die Stärke der Gegner in diesen Spielen nicht berücksichtigt. Es gibt eine viel bessere Möglichkeit.

Modellentwurf: Beginnen wir mit Annahmen darüber, wie die Daten entstanden sind. Erstens hat jeder Spieler eine versteckte (oder latente) Qualifikation, die nie direkt beobachtet wird: Es werden nur die Auswirkungen seiner Qualifikation sichtbar. Ich gehe davon aus, dass dies eine reelle Zahl ist, aber ich muss auch angeben, wie sie generiert wurde. Eine vernünftige Annahme ist, dass sie aus einer Normalverteilung (oder Gauß-Verteilung) generiert wurde. In einem komplexeren Beispiel wären die Parameter dieser Gauß-Verteilung unbekannt und lernbar. Aus Gründen der Einfachheit lege ich sie direkt fest.

Das Modell der Qualifikation kann grafisch dargestellt werden, wie in der Skizze ganz links in Abbildung 2 gezeigt. Es besagt einfach nur, dass die zufällige variable Qualifikation aus einer Normalverteilung abgeleitet wird.

Die Zusammensetzung eines Spiels mit zwei Spielern
Abbildung 2: Die Zusammensetzung eines Spiels mit zwei Spielern

Eine weitere Annahme: In jedem Spiel haben die Spieler eine Leistungszahl. Diese Zahl liegt nahe an ihrer latenten Qualifikation, kann aber abhängig davon, ob der Spieler besser oder schlechter spielt als für sein typisches Niveau, über oder unter ihr variieren. Mit anderen Worten: Die Leistung ist eine verrauschte Version der Qualifikation. Und Rauschen wird typischerweise auch als Gauß-Verteilung modelliert, wie im mittleren Diagramm in Abbildung 2 gezeigt. Hier ist die Leistung eine Zufallsvariable, die aus einer Gauß-Verteilung stammt, deren Mittelwert die Qualifikation des Spielers und deren Varianz ein fester Wert ist, angegeben durch die schraffierte Rauschvariable. In einem komplizierteren Modell würde ich versuchen, die Rauschvarianz aus den Daten zu lernen, aber der Einfachheit halber werde ich sie hier auf 1 festlegen.

Der Spieler, der eine bessere Leistung zeigt, gewinnt. In einem Spiel mit zwei Spielern kann ich dies grafisch darstellen, wie im Diagramm ganz rechts in Abbildung 2 gezeigt. Ich habe in dieser Notation ein wenig geschummelt, indem ich die boolesche Variable „Player 1 Wins“ „irgendwie“ schraffiert habe. Das liegt daran, dass ihr Wert während des Trainings beobachtet wird, wenn die Ergebnisse der Spiele angegeben werden, aber nicht während der Vorhersage.

Bevor ich dies alles zusammenfüge, muss ich einige neue Notationen vorstellen. Die erste wird als „Plate“ bezeichnet und stellt eine Foreach-Schleife dar. Es handelt sich um ein Rechteck, das Teile des Modells erfasst, die über einen bestimmten Bereich wiederholt werden müssen (z.B. über Spieler oder über Spiele). Zweitens werde ich gestrichelte Linien verwenden, um die Auswahl anzuzeigen, beispielsweise, wenn ich die Qualifikationen der beiden Spieler in jedem Spiel auswähle. Das vereinfachte TrueSkill-Modell ist in Abbildung 3 dargestellt.

Das vereinfachte TrueSkill-Modell
Abbildung 3. Das vereinfachte TrueSkill-Modell

Hier verwende ich eine Plate über den Bereich der Spieler. Dann wähle ich für jedes Spiel die latenten Qualifikationen der beiden Spieler im Spiel aus, füge etwas Rauschen hinzu und vergleiche die Leistungen. Die Rauschvariable befindet sich nicht innerhalb einer Plate, da angenommen wird, dass sich ihr Wert pro Spieler oder Spiel nicht ändert.

Diese Visualisierung des Modells, auch Faktorgraph genannt, ist in diesem einfachen Fall eine übersichtliche Darstellung. Aber wenn die Modelle größer werden, wird das Diagramm verwirrend und schwierig zu verwalten. Deshalb ziehen es Entwickler vor, das Modell in Code auszudrücken: als probabilistisches Programm.

Infer.NET

Das probabilistische Programmierframework für .NET heißt Infer.NET. Es wurde von Microsoft Research entwickelt und vor einigen Monaten zu einem Open-Source-Projekt erklärt. Infer.NET stellt eine Modellierungs-API zum Angeben des statistischen Modells, einen Compiler zum Generieren des ML-Algorithmus aus dem benutzerdefinierten Modell und eine Runtime zur Verfügung, mit der der Algorithmus ausgeführt wird.

Infer.NET wird immer enger in ML.NET integriert und befindet sich nun im Namespace Microsoft.ML.Probabilistic. Sie können Infer.NET installieren, indem Sie Folgendes ausführen:

dotnet add package Microsoft.ML.Probabilistic.Compiler

Der Compiler ruft automatisch das Laufzeitpaket ab. Beachten Sie, dass Infer.NET unter .NET Standard und damit unter Windows, Linux und macOS ausgeführt werden kann.

Wir verwenden C# und beginnen mit dem Einbeziehen des Infer.NET-Namespace:

using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Utilities;
using Microsoft.ML.Probabilistic.Distributions;

Dann implementiere ich das zuvor definierte Modell und überlege, wie ich es trainieren und Vorhersagen treffen kann.

Modellimplementierung: Das Modell ist wie ein Programm geschrieben, das ein Spiel simuliert (in Bezug auf Spielerqualifikation, Leistung und Ergebnis), aber dieses Programm wird nicht tatsächlich ausgeführt. Hinter den Kulissen erstellt es eine Datenstruktur, die das Modell darstellt. Wenn diese Datenstruktur an eine Inferenz-Engine übergeben wird, wird sie in ML-Code kompiliert, der dann ausgeführt wird, um die eigentliche Berechnung durchzuführen. Implementieren wir das Modell in drei Schritten: Definieren wir das Gerüst der Plates über Spieler und Spiele, definieren wir den Inhalt der Spielerplate, und definieren wir den Inhalt der Spielplate.

Plates werden in Infer.NET mithilfe der Range-Klasse implementiert. Ihre Instanzen sind im Grunde Steuervariablen im probabilistischen Foreach-Schleifen. Ich muss die Größe dieser Plates definieren, also die Anzahl der Spieler bzw. Spiele. Da ich diese im Voraus nicht kenne, handelt es sich um Variablen, die als Platzhalter für diese Werte verwendet werden. Infer.NET stellt bequemerweise die Variable-Klasse für genau diesen Zweck zur Verfügung:

var playerCount = Variable.New<int>();
var player = new Range(playerCount);
var gameCount = Variable.New<int>();
var game = new Range(gameCount);

Nachdem die Plates definiert wurden, konzentrieren wir uns auf ihren Inhalt. Für die Spielerplate benötige ich eine Reihe von Double-Zufallsvariablen für die Qualifikationen jedes Spielers. In Infer.NET erfolgt dies mithilfe von Variable.Array <T>. Ich benötige außerdem auch eine Reihe von Gauß-Zufallsvariablen für die A-priori-Verteilung der Qualifikationen der Spieler. Dann wende ich mich den Spielern zu und verbinde ihre Qualifikationen mit den A-priori-Angaben. Dies wird mit der Variable<T>.Random-Methode erreicht. Beachten Sie, wie Variable.ForEach(Range) die Möglichkeit bietet, die Interna der Plate zu implementieren:

var playerSkills = Variable.Array<double>(player);
var playerSkillsPrior = Variable.Array<Gaussian>(player);
using (Variable.ForEach(player))
{
  playerSkills[player] = Variable<double>.Random(playerSkillsPrior[player]);
}

Der letzte Teil des Modells ist die Spielplate. Ich beginne, indem ich die Arrays definiere, die die Trainingsdaten enthalten: den ersten und zweiten Spieler in jedem Spiel und die Ergebnisse der Spiele. Beachten Sie, wie ich ein Modell speziell für meine Daten erstelle, anstatt zu versuchen, die Daten zu überarbeiten, um sie an einen Algorithmus anzupassen. Wenn die Datencontainer fertig sind, muss ich die Spiele durchgehen, die Qualifikationen der Spieler in jedem Spiel auswählen, ihnen etwas Rauschen hinzufügen und die Leistungen vergleichen, um das Spielergebnis zu generieren:

var players1 = Variable.Array<int>(game);
var players2 = Variable.Array<int>(game);
var player1Wins = Variable.Array<bool>(game);
  const double noise = 1.0;
    using (Variable.ForEach(game))
{
  var player1Skill = playerSkills[players1[game]];
  var player1Performance =
    Variable.GaussianFromMeanAndVariance(player1Skill, noise);
  var player2Skill = playerSkills[players2[game]];
  var player2Performance =
    Variable.GaussianFromMeanAndVariance(player2Skill, noise);
      player1Wins[game] = player1Performance > player2Performance;
}

Interessanterweise wird das gleiche Modell sowohl für das Training als auch für die Vorhersage verwendet. Der Unterschied besteht darin, dass sich die beobachteten Daten unterscheiden. Während des Trainings kennen Sie die Spielergebnisse, in der Vorhersage hingegen nicht. Während das Modell also gleich bleibt, unterscheiden sich die generierten Algorithmen. Glücklicherweise kümmert sich der Infer.NET-Compiler um alle diese Dinge.

Training: Alle Abfragen des Modells (Training, Vorhersage usw.) durchlaufen drei Schritte: Festlegen von A-priori-Werten, Beobachten von Daten, Ausführen der Inferenz. Sowohl Training als auch Vorhersage werden als „Inferenz“ bezeichnet, weil sie im Grunde genommen die gleiche Aufgabe ausführen: Sie verwenden beobachtete Daten, um von einer A-priori-Verteilung zu einer A-posteriori-Verteilung zu gelangen. Im Training gehen Sie von einer breiten A-priori-Verteilung für die Qualifikationen aus, was anzeigt, dass die Unsicherheit bei den Qualifikationen hoch ist. Ich verwende eine Gauß-Verteilung für die A-priori-Werte. Nach der Beobachtung der Daten erhalte ich eine engere A-posteriori-Gauß-Verteilung bei den Qualifikationen.

Für die A-priori-Werte für die Qualifikationen leihe ich mir einfach Parameter aus, die ich durch „Halo 5“ gelernt habe: eine gute Wahl für Mittelwert und Varianz sind 6,0 bzw. 9,0. Ich lege diese Werte fest, indem ich sie der ObservedValue-Eigenschaft der Variablen zuweise, die den A-priori-Wert enthält. Für vier Spieler sieht der Code wie folgt aus:

const int PlayerCount = 4;
playerSkillsPrior.ObservedValue =
  Enumerable.Repeat(Gaussian.FromMeanAndVariance(6, 9),
  PlayerCount).ToArray();

Nun kommen die Daten. Für jedes Spiel habe ich zwei Spieler und das Ergebnis. Arbeiten wir mit einem festen Beispiel. Abbildung 4 zeigt drei Spiele, die von vier Spielern gespielt wurden, mit Pfeilen, die ein gespieltes Spiel anzeigen und jeweils auf den Verlierer des Spiels zeigen.

Die Ergebnisse von drei Spielen
Abbildung 4: Die Ergebnisse von drei Spielen

Um den Code zu vereinfachen, gehe ich davon aus, dass jedem Spieler eine eindeutige ID zugewiesen wurde. In diesem Beispiel findet das erste Spiel zwischen Alice und Bob statt, und der Pfeil zeigt an, dass Alice gewinnt. Im zweiten Spiel besiegt Bob Charlie, und schließlich besiegt Donna Charlie. Dies wird hier in Code ausgedrückt, wiederum unter Verwendung von ObservedValue:

playerCount.ObservedValue = PlayerCount;
gameCount.ObservedValue = 3;
players1.ObservedValue = new[] { 0, 1, 2 };
players2.ObservedValue = new[] { 1, 2, 3 };
player1Wins.ObservedValue = new[] { true, true, false };

Zuletzt führe ich die Inferenz aus, indem ich eine Inferenz-Engine instanziiere und „Infer“ für die Variablen aufrufe, die ich abrufen möchte. In diesem Fall interessiere ich mich nur für die A-posteriori-Werte der Spielerqualifikationen:

var inferenceEngine = new InferenceEngine();
var inferredSkills = inferenceEngine.Infer<Gaussian[]>(playerSkills);

Die Infer-Anweisung hier leistet tatsächlich viel Arbeit, weil sie zwei Kompilierungen (Infer.NET und C#) und eine Ausführung durchführt. Folgendes passiert: Der Infer.NET-Compiler führt eine Ablaufverfolgung der übergebenen playerSkills-Variable an das probabilistische Modell aus, erstellt einen abstrakten Syntaxbaum aus dem Modellcode und generiert einen Inferenzalgorithmus in C#. Dann wird der C#-Compiler dynamisch aufgerufen, und der Algorithmus wird anhand der beobachteten Daten ausgeführt. Aus diesem Grund können die Aufrufe von Infer etwas langsam sein, auch wenn Sie hier mit sehr wenigen Daten arbeiten. Das Infer.NET-Handbuch erläutert, wie Sie diesen Prozess beschleunigen können, indem Sie mit vorkompilierten Algorithmen arbeiten. Das heißt, Sie führen die Kompilierungsschritte im Voraus durch, sodass in der Produktion nur der Berechnungsteil ausgeführt wird.

Für dieses Beispiel werden folgende Qualifikationen abgeleitet:

Alice: Gaussian(8.147, 5.685)
Bob: Gaussian(5.722, 4.482)
Charlie: Gaussian(3.067, 4.814)
Donna: Gaussian(7.065, 6.588)

Vorhersage: Nachdem ich die Spielerqualifikationen abgeleitet habe, kann ich nun Vorhersagen für zukünftige Spiele treffen. Ich werde die gleichen drei Schritte wie im Training befolgen, aber dieses Mal bin ich daran interessiert, den A-posteriori-Wert über die player1Wins-Variable abzuleiten. Ich stelle mir dabei gerne vor, dass beim Training die Informationen im Faktorgraph nach oben fließen: von den unteren Daten bis zu den Modellparametern ganz oben. Im Gegensatz dazu verfügen Sie in der Vorhersage bereits über die Modellparameter (im Training gelernt), und der Informationsfluss verläuft nach unten.

In meinem Dataset haben sowohl Alice als auch Donna einen Sieg und keine Niederlage. Intuitiv fühlt es sich jedoch so an, als hätte Alice in einem Match zwischen den beiden höhere Gewinnchancen, weil ihr Sieg von größerer Signifikanz ist: Sie gewinnt gegen Bob, der wahrscheinlich ein stärkerer Spieler als Charlie ist. Lassen Sie uns versuchen, das Ergebnis des Spiels zwischen Alice und Donna vorherzusagen (siehe Abbildung 5).

Vorhersage des Ergebnisses eines Spiels
Abbildung 5: Vorhersage des Ergebnisses eines Spiels

In diesem Fall ist die A-priori-Verteilung über die Qualifikationen die A-posteriori-Verteilung, die im Training abgeleitet wird. Die beobachteten Daten bestehen aus einem Spiel zwischen Spieler 0 (Alice) und Spieler 3 (Donna) mit einem unbekannten Ergebnis. Um das Ergebnis unbekannt zu machen, muss ich den zuvor beobachteten Wert von player1Wins löschen, weil ich für diesen Wert einen A-posteriori-Wert ermitteln möchte:

playerSkillsPrior.ObservedValue = inferredSkills;
gameCount.ObservedValue = 1;
players1.ObservedValue = new[] { 0 };
players2.ObservedValue = new[] { 3 };
player1Wins.ClearObservedValue();
var player0Vs3 = inferenceEngine.Infer<Bernoulli[]>(player1Wins).First();

Es ist erwähnenswert, dass die Unsicherheit durch dieses Modell bis zum Ausgang des Spiels weitergegeben wird. Dies bedeutet, dass der abgerufene A-posteriori-Wert für das vorhergesagte Ergebnis nicht einfach eine boolesche Variable ist, sondern ein Wert, der die Wahrscheinlichkeit angibt, dass der erste Spieler das Spiel gewinnt. Eine solche Verteilung wird als „Bernoulli-Verteilung“ bezeichnet.

Der Wert der abgeleitete Variablen ist Bernoulli(0.6127). Dies bedeutet, dass Alice eine Chance von mehr als 60 Prozent hat, das Spiel gegen Donna zu gewinnen, was mit meiner Intuition übereinstimmt.

Evaluierung: Dieses Beispiel zeigt, wie ein bekanntes probabilistisches Modell – TrueSkill – erstellt werden kann. In der Praxis erfordert die Entwicklung des richtigen Modells mehrere Iterationen über den Entwurf. Verschiedene Modelle werden durch sorgfältige Auswahl einer Reihe von Metriken verglichen, die die Modellleistung für die angegebenen Daten anzeigen.

Evaluierung ist ein zentrales Thema in ML, das viel zu weit gefasst ist, um es hier zu behandeln. Es ist auch nicht spezifisch für probabilistische Programmierung. Es ist jedoch erwähnenswert, dass Sie mit einem Modell eine eindeutige „Metrik“ berechnen können: die Modellevidenz. Dies ist die Wahrscheinlichkeit, dass die Trainingsdaten von diesem speziellen Modell generiert wurden. Dies ist ideal für den Vergleich verschiedener Modelle – ohne dass ein Testsatz erforderlich ist!

Vorteile der probabilistisch Programmierung

Der in diesem Artikel gezeigte Ansatz sieht wahrscheinlich schwieriger aus als alles das, was Sie zuvor gesehen haben. In der Sonderausgabe von Magazine zur Connect(); (msdn.com/magazine/mt848634) wurde z.B. ML.NET vorgestellt, das sich weniger auf den Modellentwurf als vielmehr auf die Datentransformation konzentriert. Und in vielen Fällen ist das der richtige Weg – wenn die Daten mit einem vorhandenen Algorithmus übereinstimmen und Sie das Modell wie eine Blackbox behandeln möchten, beginnen Sie mit einer Standardlösung. Wenn Sie jedoch ein maßgeschneidertes Modell benötigen, kann die probabilistische Programmierung die richtige Wahl für Sie sein. 

Auch in anderen Fällen kann es sinnvoll sein, probabilistische Programmierung zu verwenden. Einer der Hauptvorteile eines Modells, das Sie verstehen, ist Ihre verbesserte Fähigkeit, das Verhalten des Systems zu erklären. Wenn das Modell keine Blackbox ist, können Sie seine Interna untersuchen und sich die gelernten Parameter ansehen. Diese ergeben einen Sinn für Sie, weil Sie das Modell entworfen haben. Im vorherigen Beispiel können Sie sich beispielsweise die gelernten Qualifikationen der Spieler ansehen. In einer weiterentwickelten Version von TrueSkill (genannt TrueSkill 2) werden viele weitere Aspekte des Spiels modelliert, einschließlich der Frage, wie die Leistung in einem Spielmodus mit der in einem anderen verbunden ist. Das Verständnis dieser Verbindung hilft Spieleentwicklern zu erkennen, wie ähnlich die verschiedenen Spielmodi sind. Die Interpretierbarkeit eines ML-Systems ist auch für das Debuggen von zentraler Bedeutung. Wenn ein Blackboxmodell nicht die gewünschten Ergebnisse liefert, wissen Sie vielleicht nicht einmal, wo Sie mit der Suche nach dem Problem beginnen sollen.

Ein weiterer Vorteil der probabilistischen Programmierung ist die Möglichkeit, ein Wissensgebiet in das Modell zu integrieren. Dieses wird sowohl in der Struktur des Modells als auch in der Fähigkeit, A-priori-Werte festzulegen, erfasst. Im Gegensatz dazu betrachten traditionelle Ansätze in der Regel nur Daten, ohne dass der Wissensgebietexperte das Verhalten des Systems beeinflussen kann. Diese Fähigkeit ist in bestimmten Bereichen erforderlich, z.B. im Gesundheitswesen, wo es starke Wissensgebiete gibt und Daten sehr spärlich sein können.

Ein Vorteil des bayesschen Ansatzes, der in Infer.NET sehr gut unterstützt wird, ist die Fähigkeit, beim Eintreffen neuer Daten zu lernen. Dies wird als Onlineinferenz bezeichnet und ist besonders nützlich in Systemen, die mit Benutzerdaten interagieren. So muss TrueSkill beispielsweise die Qualifikationen der Spieler nach jedem Spiel aktualisieren, und das Wissensextraktionssystem muss kontinuierlich aus dem Internet lernen, wenn es wächst. Dies ist jedoch alles so einfach: Sie verwenden einfach die abgeleiteten A-posteriori-Werte als neue A-priori-Werte, und das System ist bereit, aus neuen Daten zu lernen!

Probabilistische Programmierung ist natürlich auch auf Probleme mit bestimmten Datenmerkmalen anwendbar, z.B. heterogene Daten, spärliche Daten, unbezeichnete Daten, Daten mit fehlenden Teilen und Daten, die mit bekanntem Bias erfasst wurden.

Wie geht es weiter?

Es gibt zwei Voraussetzungen, um ein probabilistisches Modell erfolgreich zu erstellen. Die erste besteht natürlich darin, zu lernen, wie die Modellierung erfolgt. In diesem Artikel habe ich die wichtigsten Konzepte und Techniken der Methodik vorgestellt. Wenn Sie mehr darüber erfahren möchten, empfehle ich Ihnen ein kostenlos verfügbares Onlinebuch: „Model-Based Machine Learning“ (mbmlbook.com). Es enthält eine einfache Einführung in modellbasiertes ML in der Produktion und richtet sich an Entwickler ( und nicht an Wissenschaftler).

Sobald Sie wissen, wie Modelle entworfen werden, müssen Sie lernen, wie Sie sie in Code ausdrücken können. Der Infer.NET-Benutzerleitfaden ist dafür eine ausgezeichnete Ressource. Es sind außerdem Tutorials und Beispiele für viele Szenarien verfügbar, die alle unserem Repository unter github.com/dotnet/infer entnommen werden können. Dort heißen wir Sie auch herzlich willkommen, mitzuwirken und Beiträge zu verfassen.


Yordan Zaykov ist der Principal Research Software Engineering Lead des Probabilistic Inference Development-Teams bei Microsoft Research Cambridge. Sein Schwerpunkt liegt auf Anwendungen, die auf dem Infer.NET ML-Framework basieren. Er arbeitet z.B. an E-Mail-Klassifizierung, Empfehlungen, Spielerranglisten und Spielpartnervermittlung, Fragen des Gesundheitswesens und Knowledge Mining..

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: John Guiver, James McCaffrey, Tom Minka, John Winn