using Microsoft.ML; using Microsoft.ML.Data; using System; using System.Data.SqlClient; using System.Linq; using AutoBet.Learn.Extensions; using Microsoft.ML.Trainers; using System.Collections.Generic; namespace AutoBet.Learn { public class Program { static void Main(string[] args) { Learn(); Console.WriteLine("Hello World!"); } public static DateTime DateToPredict = DateTime.Now.Date.AddDays(-3); private static void Learn() { var daysAgo = DateToPredict.AddMonths(-3); daysAgo = new DateTime(daysAgo.Year, daysAgo.Month, 1); while (DateToPredict != daysAgo) { var mlContext = new MLContext(); var model = Train(mlContext); Evaluate(mlContext, model); UpdatePredictions(mlContext, model); DateToPredict = DateToPredict.AddDays(-1); } } private static void UpdatePredictions(MLContext mlContext, ITransformer model) { using (var db = new Db.HorseRacingDataContext()) { var races = db.Races.Where(e => e.RaceDate == DateToPredict.Date).ToList(); foreach (var race in races) { var runners = db.Runners.Where(e => e.RaceId == race.RaceId && e.DecimalOdds != null).ToList(); foreach (var runner in runners) { var fixture = db.Fixtures.FirstOrDefault(e => e.FixtureId == race.FixtureId); if (fixture == null) continue; var prediction = TestSinglePrediction(mlContext, model, new HorseOddsTrainingData() { AgeYears = runner.AgeYears, CourseName = fixture.CourseName, FixtureType = (int)fixture.FixtureType, BhaRating = String.IsNullOrWhiteSpace(runner.BhaRating) ? 0 : int.Parse(runner.BhaRating), CourseWinner = runner.CourseWinner, DecimalOdds = (float)runner.DecimalOdds.Value, DistanceValue = race.DistanceValue, DistanceWinner = runner.DistanceWinner, DivisionSequence = race.DivisionSequence, DrawnStall = runner.DrawnStall ?? 0, FinishTimeInMilliseconds = 0, HeadGearWearingCode = runner.HeadGearWearingCode ?? "Unknown", HeadGearWearingSheepSkin = runner.HeadGearWearingSheepSkin, JockeyName = runner.JockeyName, OwnerName = runner.OwnerName, PenaltyValue = runner.PenaltyValue, PrizeAmount = race.PrizeAmount, RaceClass = race.RaceClass, RaceCriteriaRaceType = race.RaceCriteriaRaceType, RaceDateTime = race.RaceDate + race.RaceTime, RacehorseName = runner.RaceHorseNameWithoutCountry, SexType = (int)runner.SexType, TrainerName = runner.TrainerName, WeightInGrams = (float)runner.WeightInGrams, WeightsJockeyClaiming = runner.WeightsJockeyClaiming, WeightValue = runner.WeightValue, RaceDay = (float)race.RaceDate.Day, RaceMonth = (float)race.RaceDate.Month, RaceYear = (float)race.RaceDate.Year }); runner.Prediction = prediction; } } db.SaveChanges(); } } private static IDataView LoadData(MLContext mlContext, string filter = "") { var loader = mlContext.Data.CreateDatabaseLoader(); var sqlCommand = @"SELECT [CourseName] ,[FixtureType] ,[RaceCriteriaRaceType] ,[DistanceValue] ,[RaceClass] ,[PrizeAmount] ,[HeadGearWearingCode] ,[HeadGearWearingSheepSkin] ,[CourseWinner] ,[DistanceWinner] ,[RacehorseName] ,[TrainerName] ,[OwnerName] ,[JockeyName] ,[WeightsJockeyClaiming] ,[DecimalOdds] ,[DivisionSequence] ,[AgeYears] ,[DrawnStall] ,[BhaRating] ,[WeightValue] ,[PenaltyValue] ,[SexType] ,ISNULL([FinishTimeInMilliseconds],0) as FinishTimeInMilliseconds ,[RaceDateTime] ,[RaceMonth] ,[RaceYear] ,[RaceDay] ,[WeightInGrams] FROM [dbo].[ResultsView] WHERE DecimalOdds IS NOT NULL " + filter; var dbSource = new DatabaseSource(SqlClientFactory.Instance, Db.HorseRacingDataContext.ConnectionString, sqlCommand); var data = loader.Load(dbSource); return data; } public static ITransformer Train(MLContext mlContext) { var data = LoadData(mlContext, $"AND FinishTimeInMilliseconds IS NOT NULL AND RaceDate < '{DateToPredict.AddDays(-1).ToString("yyyy/MM/dd", null)}'"); //var trainers = new Dictionary, object>>() //{ // { "FastForest", mlContext.Regression.Trainers.FastForest() }, // { "FastTree", mlContext.Regression.Trainers.FastTree() }, // { "FastTreeTweedie", mlContext.Regression.Trainers.FastTreeTweedie() }, // { "LbfgsPoissonRegression", mlContext.Regression.Trainers.LbfgsPoissonRegression() }, // { "Scda", mlContext.Regression.Trainers.Sdca() } //}; //foreach (var trainer in trainers) //{ // var modelPipeline = DataTransform(mlContext); // var pipelineWithTrainer = modelPipeline.Append(trainer.Value); // var model = pipelineWithTrainer.Fit(data); // Console.WriteLine($">>>>>>> Evaluating algorithm: {trainer.Key} <<<<<<<"); // Evaluate(mlContext, model); //} Console.WriteLine($"Loading data"); var pipeline = DataTransform(mlContext); //var pipelineWithTrainer = pipeline.Append(mlContext.Regression.Trainers.LbfgsPoissonRegression()); var selectedPipelineWithTrainer = pipeline.Append(pipeline.Append(mlContext.Regression.Trainers.LbfgsPoissonRegression())); var finalModel = selectedPipelineWithTrainer.Fit(data); return finalModel; } private static EstimatorChain DataTransform(MLContext mlContext) { var pipeline = mlContext.Transforms .CopyColumns(outputColumnName: "Label", inputColumnName: "FinishTimeInMilliseconds") .EncodeColumn(mlContext, "CourseName") .EncodeColumn(mlContext, "FixtureType") .EncodeColumn(mlContext, "RaceCriteriaRaceType") .EncodeColumn(mlContext, "HeadGearWearingCode") .EncodeColumn(mlContext, "RacehorseName") .EncodeColumn(mlContext, "TrainerName") .EncodeColumn(mlContext, "OwnerName") .EncodeColumn(mlContext, "JockeyName") .EncodeColumn(mlContext, "HeadGearWearingSheepSkin") .EncodeColumn(mlContext, "CourseWinner") .EncodeColumn(mlContext, "DistanceWinner") .EncodeColumn(mlContext, "WeightValue") .EncodeColumn(mlContext, "SexType") .Append(mlContext.Transforms.Concatenate("Features", "CourseNameEncoded", "FixtureTypeEncoded", "RaceCriteriaRaceTypeEncoded", "DistanceValue", "RaceClass", "PrizeAmount", "HeadGearWearingCodeEncoded", "HeadGearWearingSheepSkinEncoded", "CourseWinnerEncoded", "DistanceWinnerEncoded", "RacehorseNameEncoded", "TrainerNameEncoded", "OwnerNameEncoded", "JockeyNameEncoded", "WeightsJockeyClaiming", "DecimalOdds", "DivisionSequence", "AgeYears", "DrawnStall", "WeightValueEncoded", "PenaltyValue", "SexTypeEncoded", "RaceMonth", "RaceYear", "RaceDay")); return pipeline; } private static RegressionMetrics Evaluate(MLContext mlContext, ITransformer model) { var dataView = LoadData(mlContext, $"AND FinishTimeInMilliseconds IS NOT NULL AND RaceDate = '{DateToPredict.AddDays(-1).ToString("yyyy/MM/dd", null)}'"); var predictions = model.Transform(dataView); var metrics = mlContext.Regression.Evaluate(predictions, "Label", "Score"); Console.WriteLine(); var result = $"* Model quality metrics evaluation R2: {metrics.RSquared:0.####} Root Mean Squared Error: {metrics.RootMeanSquaredError:#.####} *"; var stars = String.Concat(Enumerable.Range(0, result.Length).Select(e => "*")); Console.WriteLine(stars); Console.WriteLine(result); Console.WriteLine(stars); Console.WriteLine(); return metrics; } private static float TestSinglePrediction(MLContext mlContext, ITransformer model, HorseOddsTrainingData values) { var predictionFunction = mlContext.Model.CreatePredictionEngine(model); var prediction = predictionFunction.Predict(values); Console.WriteLine($"Predicted time for {values.RacehorseName}: {prediction.FinishTimeInMilliseconds}"); return prediction.FinishTimeInMilliseconds; } } }