Files
2020-04-13 15:16:46 +01:00

237 lines
10 KiB
C#

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<HorseOddsTrainingData>();
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<string, ITrainerEstimator<ISingleFeaturePredictionTransformer<object>, 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<ColumnConcatenatingTransformer> 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<HorseOddsTrainingData, HorseOddsTrainingPrediction>(model);
var prediction = predictionFunction.Predict(values);
Console.WriteLine($"Predicted time for {values.RacehorseName}: {prediction.FinishTimeInMilliseconds}");
return prediction.FinishTimeInMilliseconds;
}
}
}