True Skill -- MBML

如何衡量一个玩家/学生的真实战斗力/水平?
这篇文章将会带着您从一个光环五中使用的机器学习例子出发,了解如何用MBML来匹配实力相近的玩家。

训练数据

数据表:

Game Winner Loser
1 player0 player1
2 player0 player3
3 player0 player4
4 player1 player2
5 player3 player1
6 player4 player2

模型

模型非常简单,我们假设所有玩家的水平呈现高斯分布。他们水平的集合为图中Skills[player]。每次比赛有winner也有losser,他们的水平分别是图中Skills[Winner[Game]]Skills[Loser[Game]],由于每次比赛的时候参赛者发挥的水平可能在真是水平的上下波动,我们把他们发挥出的水平分布看作一个以他们的实际水准为平均值,标准差为1的高斯分布,然后比较他们发挥出的水平的大小,大的人获胜。
而如何训练这个模型呢?我们向模型里代入实际的数据,使用贝叶斯定理计算玩家水平分布的后验概率(这是框架帮我们计算的),替换掉原本我们假设的先验概率。在迭代次数够多、数据够大的情况下,我们的模型就会得出各玩家的准确水平,并且能对各个玩家之间的胜负进行准确的预测。
这个模型已经在xbox中的数款游戏(光环五等)中得到应用,其升级版TrueSkill2在实际运用中达到了百分之68的预测准确度。

代码

using System;
using Range = Microsoft.ML.Probabilistic.Models.Range;
using System.Linq;
using Microsoft.ML.Probabilistic;
using Microsoft.ML.Probabilistic.Distributions;
using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Compiler.Visualizers;

namespace TrueSkill
{
    class Program
    {
        static void Main(string[] args)
        {
            // The winner and loser in each of 6 samples games
            var winnerData = new[] { 0, 0, 0, 1, 3, 4 };
            var loserData = new[] { 1, 3, 4, 2, 1, 2 };

            // Define the statistical model as a probabilistic program
            var game = new Range(winnerData.Length);
            var player = new Range(winnerData.Concat(loserData).Max() + 1);
            var playerSkills = Variable.Array<double>(player);
            playerSkills[player] = Variable.GaussianFromMeanAndVariance(6, 9).ForEach(player);

            var winners = Variable.Array<int>(game).Named("Winner");
            var losers = Variable.Array<int>(game).Named("Loser");

            using (Variable.ForEach(game))
            {
                // The player performance is a noisy version of their skill
                var winnerPerformance = Variable.GaussianFromMeanAndVariance(playerSkills[winners[game]], 1.0);
                var loserPerformance = Variable.GaussianFromMeanAndVariance(playerSkills[losers[game]], 1.0);

                // The winner performed better in this game
                Variable.ConstrainTrue(winnerPerformance > loserPerformance);
            }

            // Attach the data to the model
            winners.ObservedValue = winnerData;
            losers.ObservedValue = loserData;

            // Run inference
            var inferenceEngine = new InferenceEngine();
            inferenceEngine.SaveFactorGraphToFolder = "graph";
            inferenceEngine.ShowFactorGraph = true;
            InferenceEngine.Visualizer = new WindowsVisualizer();
            var inferredSkills = inferenceEngine.Infer<Gaussian[]>(playerSkills);

            // The inferred skills are uncertain, which is captured in their variance
            var orderedPlayerSkills = inferredSkills
               .Select((s, i) => new { Player = i, Skill = s })
               .OrderByDescending(ps => ps.Skill.GetMean());

            foreach (var playerSkill in orderedPlayerSkills)
            {
                Console.WriteLine($"Player {playerSkill.Player} skill: {playerSkill.Skill}");
            }
        }
    }
}


结果

Compiling model...done.
Iterating:
.........|.........|.........|.........|.........| 50
Player 0 skill: Gaussian(9.517, 3.926)
Player 3 skill: Gaussian(6.834, 3.892)
Player 4 skill: Gaussian(6.054, 4.731)
Player 1 skill: Gaussian(4.955, 3.503)
Player 2 skill: Gaussian(2.639, 4.288)

可以看到我们的模型认为玩家0的水平最高


本文章使用limfx的vsocde插件快速发布