DEEP-13, DEEP-39 AI Model completely reimplemented. Dashboard UI implemented. Namespace style changed

This commit is contained in:
Andrey Shabarshov 2023-07-30 16:20:41 +01:00
parent af6c75a5ac
commit bfb3de9331
36 changed files with 1180 additions and 1088 deletions

View File

@ -2,8 +2,8 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Text; using System.Text;
namespace DeepTrace.Controllers namespace DeepTrace.Controllers;
{
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
public class DownloadController : Controller public class DownloadController : Controller
@ -28,4 +28,3 @@ namespace DeepTrace.Controllers
}; };
} }
} }
}

View File

@ -7,10 +7,16 @@
@inject IDialogService DialogService @inject IDialogService DialogService
@inject IModelStorageService ModelService @inject IModelStorageService ModelService
@inject ITrainedModelStorageService TrainedModelService @inject ITrainedModelStorageService TrainedModelService
@inject ILogger<MLProcessor> MLProcessorLogger
@inject ILogger<ModelCard> Logger @inject ILogger<ModelCard> Logger
@inject IMLProcessorFactory MlProcessorFactory
<MudCard Class="mb-3"> <style>
.card {
max-width: 250pt;
}
</style>
<MudCard Class="card mb-3">
<MudCardHeader> <MudCardHeader>
<CardHeaderContent> <CardHeaderContent>
<MudText Typo="Typo.h6">@Model?.Name</MudText> <MudText Typo="Typo.h6">@Model?.Name</MudText>
@ -21,6 +27,7 @@
</MudCardHeader> </MudCardHeader>
<MudCardContent> <MudCardContent>
<MudText>Current state: @_prediction.PredictedLabel</MudText> <MudText>Current state: @_prediction.PredictedLabel</MudText>
<MudText>@_updated.ToString("HH:mm:ss")</MudText>
</MudCardContent> </MudCardContent>
</MudCard> </MudCard>
@ -30,6 +37,8 @@
private ModelDefinition _modelDefinition = new(); private ModelDefinition _modelDefinition = new();
private Prediction _prediction = new(); private Prediction _prediction = new();
private IMLProcessor? _mlProcessor;
private DateTime _updated = DateTime.MinValue;
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
@ -38,6 +47,7 @@
return; return;
} }
_modelDefinition = (await ModelService.Load(Model.Id)) ?? _modelDefinition; _modelDefinition = (await ModelService.Load(Model.Id)) ?? _modelDefinition;
_mlProcessor = MlProcessorFactory.Create();
#pragma warning disable CS4014 #pragma warning disable CS4014
Task.Run(PredictionLoop); Task.Run(PredictionLoop);
@ -87,15 +97,20 @@
await PredictAnomaly(startDate, endDate); await PredictAnomaly(startDate, endDate);
startDate = endDate; startDate = endDate;
} }
catch(Exception) catch(Exception e)
{ {
//ignore Logger.LogError(e, e.Message);
} }
} }
} }
private async Task PredictAnomaly(DateTime startDate, DateTime endDate) private async Task PredictAnomaly(DateTime startDate, DateTime endDate)
{ {
if (Model == null || !Model.IsEnabled)
{
_prediction = new Prediction { PredictedLabel = "Idle" };
return;
}
// use automatic step value to always request 500 elements // use automatic step value to always request 500 elements
var seconds = (endDate - startDate).TotalSeconds / 500.0; var seconds = (endDate - startDate).TotalSeconds / 500.0;
@ -150,8 +165,9 @@
); );
} }
var mlProcessor = new MLProcessor(MLProcessorLogger); _prediction = await _mlProcessor!.Predict(Model, _modelDefinition, data);
_prediction = await mlProcessor.Predict(Model, _modelDefinition, data); _updated = DateTime.Now;
await InvokeAsync(StateHasChanged);
} }
private async Task ShowError(string text) private async Task ShowError(string text)

View File

@ -14,12 +14,12 @@
<h4>@Text</h4> <h4>@Text</h4>
<MudTextField T="string" ReadOnly="true" Text="@_progressText"></MudTextField> <MudTextField T="string" ReadOnly="true" Text="@_progressText"></MudTextField>
@if (_isTraining == false) @if (_isTraining == false && _evaluationMetrics != null)
{ {
<MudText>MicroAccuracy: @_evaluationMetrics!.MicroAccuracy.ToString("N2")</MudText> <MudText>MicroAccuracy: @_evaluationMetrics.MicroAccuracy.ToString("N6")</MudText>
<MudText>MacroAccuracy: @_evaluationMetrics!.MacroAccuracy.ToString("N2")</MudText> <MudText>MacroAccuracy: @_evaluationMetrics.MacroAccuracy.ToString("N6")</MudText>
<MudText>LogLoss: @_evaluationMetrics!.LogLoss.ToString("N2")</MudText> <MudText>LogLoss: @_evaluationMetrics.LogLoss.ToString("N6")</MudText>
<MudText>LogLossReduction: @_evaluationMetrics!.LogLossReduction.ToString("N2")</MudText> <MudText>LogLossReduction: @_evaluationMetrics.LogLossReduction.ToString("N6")</MudText>
} }
@ -47,10 +47,21 @@
return; return;
} }
try
{
_evaluationMetrics = await Processor.Train(Model, UpdateProgress); _evaluationMetrics = await Processor.Train(Model, UpdateProgress);
}
catch (Exception e)
{
_progressText = "ERROR: " + e.Message;
}
finally
{
_isTraining = false; _isTraining = false;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
}
private async void UpdateProgress(string message) private async void UpdateProgress(string message)
{ {

View File

@ -29,4 +29,55 @@ public class DataSourceDefinition
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
public override string ToString() => Name; public override string ToString() => Name;
public List<string> GetColumnNames()
{
var measureNames = new[] { "min", "max", "avg", "mean" };
var columnNames = new List<string>();
foreach (var item in Queries)
{
columnNames.AddRange(measureNames.Select(x => $"{item.Query}_{x}"));
}
return columnNames;
}
public static string ConvertToCsv(List<TimeSeriesDataSet> source)
{
var data = "";
for (var i = 0; i < source.Count; i++)
{
var queryData = source[i];
var min = queryData.Data.Min(x => x.Value);
var max = queryData.Data.Max(x => x.Value);
var avg = queryData.Data.Average(x => x.Value);
var mean = queryData.Data.Sum(x => x.Value) / queryData.Data.Count;
data += min + "," + max + "," + avg + "," + mean + ",";
}
return data.TrimEnd(',');
}
public static float[] ToFeatures(List<TimeSeriesDataSet> source)
{
var data = new float[source.Count * 4];
for (var i = 0; i < source.Count; i++)
{
var queryData = source[i];
var min = queryData.Data.Min(x => x.Value);
var max = queryData.Data.Max(x => x.Value);
var avg = queryData.Data.Average(x => x.Value);
var mean = queryData.Data.Sum(x => x.Value) / queryData.Data.Count;
data[i*4 + 0] = min;
data[i*4 + 1] = max;
data[i*4 + 2] = avg;
data[i*4 + 3] = mean;
}
return data;
}
} }

View File

@ -1,5 +1,5 @@
namespace DeepTrace.Data namespace DeepTrace.Data;
{
public class IntervalDefinition public class IntervalDefinition
{ {
public IntervalDefinition() { } public IntervalDefinition() { }
@ -19,4 +19,3 @@
public List<TimeSeriesDataSet> Data { get; set; } = new(); public List<TimeSeriesDataSet> Data { get; set; } = new();
} }
}

View File

@ -5,8 +5,10 @@ using System;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
namespace DeepTrace namespace DeepTrace;
{
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public partial class MLModel1 public partial class MLModel1
{ {
/// <summary> /// <summary>
@ -164,6 +166,8 @@ namespace DeepTrace
#endregion #endregion
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static string MLNetModelPath = Path.GetFullPath("MLModel1.zip"); private static string MLNetModelPath = Path.GetFullPath("MLModel1.zip");
public static readonly Lazy<PredictionEngine<ModelInput, ModelOutput>> PredictEngine = new Lazy<PredictionEngine<ModelInput, ModelOutput>>(() => CreatePredictEngine(), true); public static readonly Lazy<PredictionEngine<ModelInput, ModelOutput>> PredictEngine = new Lazy<PredictionEngine<ModelInput, ModelOutput>>(() => CreatePredictEngine(), true);
@ -186,4 +190,3 @@ namespace DeepTrace
return mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel); return mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel);
} }
} }
}

View File

@ -9,8 +9,8 @@ using Microsoft.ML.Trainers.FastTree;
using Microsoft.ML.Trainers; using Microsoft.ML.Trainers;
using Microsoft.ML; using Microsoft.ML;
namespace DeepTrace namespace DeepTrace;
{
public partial class MLModel1 public partial class MLModel1
{ {
/// <summary> /// <summary>
@ -61,4 +61,3 @@ namespace DeepTrace
return pipeline; return pipeline;
} }
} }
}

View File

@ -2,6 +2,7 @@
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson; using MongoDB.Bson;
using System.Text; using System.Text;
using DeepTrace.ML;
namespace DeepTrace.Data; namespace DeepTrace.Data;
@ -21,17 +22,10 @@ public class ModelDefinition
public string AIparameters { get; set; } = string.Empty; public string AIparameters { get; set; } = string.Empty;
public List<IntervalDefinition> IntervalDefinitionList { get; set; } = new(); public List<IntervalDefinition> IntervalDefinitionList { get; set; } = new();
public List<string> GetColumnNames() public List<string> GetColumnNames() => DataSource.GetColumnNames()
{ .Concat(new[] { "Name" })
var measureNames = new[] { "min", "max", "avg", "mean" }; .ToList()
var columnNames = new List<string>(); ;
foreach (var item in DataSource.Queries)
{
columnNames.AddRange(measureNames.Select(x => $"{item.Query}_{x}"));
}
columnNames.Add("Name");
return columnNames;
}
public string ToCsv() public string ToCsv()
{ {
@ -45,30 +39,24 @@ public class ModelDefinition
foreach (var currentInterval in IntervalDefinitionList) foreach (var currentInterval in IntervalDefinitionList)
{ {
var source = currentInterval.Data; var source = currentInterval.Data;
string data = ConvertToCsv(source); string data = DataSourceDefinition.ConvertToCsv(source);
data += "," + currentInterval.Name; data += $",\"{currentInterval.Name}\"";
writer.AppendLine(data); writer.AppendLine(data);
} }
return writer.ToString(); return writer.ToString();
} }
public static string ConvertToCsv(List<TimeSeriesDataSet> source) public IEnumerable<MLInputData> ToInput()
{ {
var data = ""; foreach (var currentInterval in IntervalDefinitionList)
for (var i = 0; i < source.Count; i++)
{ {
var source = currentInterval.Data;
var queryData = source[i]; yield return new MLInputData
var min = queryData.Data.Min(x => x.Value); {
var max = queryData.Data.Max(x => x.Value); Features = DataSourceDefinition.ToFeatures(source),
var avg = queryData.Data.Average(x => x.Value); Label = currentInterval.Name
var mean = queryData.Data.Sum(x => x.Value) / queryData.Data.Count; };
}
data += min + "," + max + "," + avg + "," + mean + ",";
}
return data+"\"ignoreMe\"";
} }
} }

View File

@ -4,9 +4,9 @@ namespace DeepTrace.Data;
public class Prediction public class Prediction
{ {
[ColumnName(@"PredictedLabel")] [ColumnName("PredictedLabel")]
public string PredictedLabel { get; set; } public string PredictedLabel { get; set; } = string.Empty;
[ColumnName(@"Score")] [ColumnName("Score")]
public float[] Score { get; set; } public float[] Score { get; set; } = Array.Empty<float>();
} }

View File

@ -1,14 +1,14 @@
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson; using MongoDB.Bson;
namespace DeepTrace.Data namespace DeepTrace.Data;
{
public class TrainedModelDefinition public class TrainedModelDefinition
{ {
[BsonId] [BsonId]
public ObjectId? Id { get; set; } public ObjectId? Id { get; set; }
public bool IsEnabled { get; set; } = false; public bool IsEnabled { get; set; } = false;
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public DataSourceDefinition? DataSource{ get; set;}
public byte[] Value { get; set; } = Array.Empty<byte>(); //base64 public byte[] Value { get; set; } = Array.Empty<byte>(); //base64
} }
}

View File

@ -1,47 +1,31 @@
using DeepTrace.Data; using DeepTrace.Data;
using Microsoft.ML; using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers; using Microsoft.ML.Trainers;
namespace DeepTrace.ML namespace DeepTrace.ML;
{
public class EstimatorBuilder : IEstimatorBuilder public class EstimatorBuilder : IEstimatorBuilder
{ {
public IEstimator<ITransformer> BuildPipeline(MLContext mlContext, ModelDefinition model) public IEstimator<ITransformer> BuildPipeline(MLContext mlContext, ModelDefinition model)
{ {
IEstimator<ITransformer>? pipeline = null; return
var ds = model.DataSource; mlContext.Transforms.NormalizeMinMax(inputColumnName: nameof(MLInputData.Features),outputColumnName: "Features")
.Append(mlContext.Transforms.Conversion.MapValueToKey(inputColumnName: nameof(MLInputData.Label), outputColumnName: "Label"))
var measureNames = new[] { "min", "max", "avg", "mean" }; // .AppendCacheCheckpoint(mlContext)
var columnNames = new List<string>(); .Append(mlContext.MulticlassClassification.Trainers.OneVersusAll(
foreach (var item in ds.Queries) binaryEstimator: mlContext.BinaryClassification.Trainers.LbfgsLogisticRegression(
new LbfgsLogisticRegressionBinaryTrainer.Options
{ {
var estimators = measureNames.Select(x => mlContext.Transforms.Text.FeaturizeText(inputColumnName: $"{item.Query}_{x}", outputColumnName: $"{item.Query}_{x}")); L1Regularization = 1F,
columnNames.AddRange(measureNames.Select(x => $"{item.Query}_{x}")); L2Regularization = 1F,
LabelColumnName = "Label",
foreach (var e in estimators) FeatureColumnName = "Features"
{
if (pipeline == null)
{
pipeline = e;
} }
else ))
{ )
pipeline = pipeline.Append(e); .Append(mlContext.Transforms.Conversion.MapKeyToValue(nameof(MLOutputData.PredictedLabel), inputColumnName: "PredictedLabel"));
}
}
}
pipeline = pipeline!
.Append(mlContext.Transforms.Concatenate(@"Features", columnNames.ToArray()))
.Append(mlContext.Transforms.Conversion.MapValueToKey(outputColumnName: @"Name", inputColumnName: @"Name"))
.Append(mlContext.Transforms.NormalizeMinMax(@"Features", @"Features"))
.Append(mlContext.MulticlassClassification.Trainers.OneVersusAll(binaryEstimator: mlContext.BinaryClassification.Trainers.LbfgsLogisticRegression(new LbfgsLogisticRegressionBinaryTrainer.Options() { L1Regularization = 1F, L2Regularization = 1F, LabelColumnName = @"Name", FeatureColumnName = @"Features" }), labelColumnName: @"Name"))
.Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumnName: @"PredictedLabel", inputColumnName: @"PredictedLabel"));
return pipeline;
} }
} }
}

View File

@ -1,10 +1,9 @@
using DeepTrace.Data; using DeepTrace.Data;
using Microsoft.ML; using Microsoft.ML;
namespace DeepTrace.ML namespace DeepTrace.ML;
{
public interface IEstimatorBuilder public interface IEstimatorBuilder
{ {
IEstimator<ITransformer> BuildPipeline(MLContext mlContext, ModelDefinition model); IEstimator<ITransformer> BuildPipeline(MLContext mlContext, ModelDefinition model);
} }
}

View File

@ -10,3 +10,8 @@ public interface IMLProcessor
void Import(byte[] data); void Import(byte[] data);
Task<Prediction> Predict(TrainedModelDefinition trainedModel, ModelDefinition model, List<TimeSeriesDataSet> data); Task<Prediction> Predict(TrainedModelDefinition trainedModel, ModelDefinition model, List<TimeSeriesDataSet> data);
} }
public interface IMLProcessorFactory
{
IMLProcessor Create();
}

View File

@ -1,11 +1,10 @@
using PrometheusAPI; using PrometheusAPI;
namespace DeepTrace.ML namespace DeepTrace.ML;
{
public interface IMeasure public interface IMeasure
{ {
public string Name { get; } public string Name { get; }
void Reset(); void Reset();
float Calculate(IEnumerable<TimeSeries> data); float Calculate(IEnumerable<TimeSeries> data);
} }
}

View File

@ -1,5 +1,5 @@
namespace DeepTrace.ML namespace DeepTrace.ML;
{
public class MLEvaluationMetrics public class MLEvaluationMetrics
{ {
public MLEvaluationMetrics() public MLEvaluationMetrics()
@ -13,4 +13,3 @@
public double LogLossReduction { get; set; } public double LogLossReduction { get; set; }
} }
}

View File

@ -7,6 +7,21 @@ namespace DeepTrace.ML;
public record ModelRecord(MLContext Context, DataViewSchema Schema, ITransformer Transformer); public record ModelRecord(MLContext Context, DataViewSchema Schema, ITransformer Transformer);
public class MLInputData
{
public string Label { get; set; } = "Normal operation";
public float[] Features { get; set; } = Array.Empty<float>();
}
public class MLOutputData
{
public string PredictedLabel { get; set; } = string.Empty;
public float[] Score { get; set; } = Array.Empty<float>();
}
public static class MLHelpers public static class MLHelpers
{ {
public static byte[] ExportSingleModel( ModelRecord model) public static byte[] ExportSingleModel( ModelRecord model)
@ -32,10 +47,22 @@ public static class MLHelpers
await File.WriteAllTextAsync(fileName, csv); await File.WriteAllTextAsync(fileName, csv);
return LoadFromCsv(mlContext, model, fileName); return (LoadFromCsv(mlContext, model, fileName), fileName);
} }
public static (IDataView View, string FileName) LoadFromCsv(MLContext mlContext, ModelDefinition model, string fileName) public static Task<IDataView> ToInput(MLContext mlContext, ModelDefinition model)
{
var input = model.ToInput().ToList();
// VectorType attribute with dynamic dimension
// https://github.com/dotnet/machinelearning/issues/164
var schemaDef = SchemaDefinition.Create(typeof(MLInputData));
schemaDef["Features"].ColumnType = new VectorDataViewType(NumberDataViewType.Single, input.First().Features.Length );
return Task.FromResult(mlContext.Data.LoadFromEnumerable(input, schemaDef));
}
public static IDataView LoadFromCsv(MLContext mlContext, ModelDefinition model, string fileName)
{ {
var columnNames = model.GetColumnNames(); var columnNames = model.GetColumnNames();
var columns = columnNames var columns = columnNames
@ -43,8 +70,14 @@ public static class MLHelpers
.ToArray() .ToArray()
; ;
var view = mlContext.Data.LoadFromTextFile(fileName, columns, separatorChar: ',', hasHeader: true, allowQuoting: true, trimWhitespace: true); var view = mlContext.Data.LoadFromTextFile(
fileName,
columns,
separatorChar: ',',
hasHeader: true,
allowQuoting: true,
trimWhitespace: true);
return (view, fileName); return view;
} }
} }

View File

@ -1,32 +1,51 @@
using DeepTrace.Data; using DeepTrace.Data;
using Microsoft.ML; using Microsoft.ML;
using Microsoft.ML.Data;
using PrometheusAPI;
using System.Data; using System.Data;
using static DeepTrace.MLModel1;
namespace DeepTrace.ML namespace DeepTrace.ML;
internal class MLProcessorFactory : IMLProcessorFactory
{ {
private readonly ILogger<MLProcessor> _logger;
private IEstimatorBuilder _estimatorBuilder;
public MLProcessorFactory(ILogger<MLProcessor> logger, IEstimatorBuilder estimatorBuilder)
{
_logger = logger;
_estimatorBuilder = estimatorBuilder;
}
public IMLProcessor Create() => new MLProcessor(_logger, _estimatorBuilder);
}
/// <summary>
/// Wrapper for ML.NET operations.
/// </summary>
public class MLProcessor : IMLProcessor public class MLProcessor : IMLProcessor
{ {
private readonly ILogger<MLProcessor> _logger;
private MLContext _mlContext = new MLContext(); private MLContext _mlContext = new MLContext();
private EstimatorBuilder _estimatorBuilder = new EstimatorBuilder(); private IEstimatorBuilder _estimatorBuilder;
private DataViewSchema? _schema; private DataViewSchema? _schema;
private ITransformer? _transformer; private ITransformer? _transformer;
private static string _signature = "DeepTrace-Model-v1-" + typeof(MLProcessor).Name; private static string _signature = "DeepTrace-Model-v1-" + typeof(MLProcessor).Name;
private readonly ILogger<MLProcessor> _logger; private PredictionEngine<MLInputData, MLOutputData>? _predictionEngine;
public MLProcessor(ILogger<MLProcessor> logger) public MLProcessor(ILogger<MLProcessor> logger, IEstimatorBuilder estimatorBuilder)
{ {
_logger = logger; _logger = logger;
_estimatorBuilder = estimatorBuilder;
} }
private string Name { get; set; } = "TestModel"; private string Name { get; set; } = "TestModel";
public async Task<MLEvaluationMetrics> Train(ModelDefinition modelDef, Action<string> log) public async Task<MLEvaluationMetrics> Train(ModelDefinition modelDef, Action<string> log)
{ {
_logger.LogInformation("Training started");
Name = modelDef.Name;
var pipeline = _estimatorBuilder.BuildPipeline(_mlContext, modelDef); var pipeline = _estimatorBuilder.BuildPipeline(_mlContext, modelDef);
var (data, filename) = await MLHelpers.Convert(_mlContext, modelDef); var data = await MLHelpers.ToInput(_mlContext, modelDef);
DataOperationsCatalog.TrainTestData dataSplit = _mlContext.Data.TrainTestSplit(data, testFraction: 0.2); DataOperationsCatalog.TrainTestData dataSplit = _mlContext.Data.TrainTestSplit(data, testFraction: 0.2);
@ -35,13 +54,13 @@ namespace DeepTrace.ML
{ {
_schema = data.Schema; _schema = data.Schema;
_transformer = pipeline.Fit(dataSplit.TrainSet); _transformer = pipeline.Fit(dataSplit.TrainSet);
return Evaluate(dataSplit.TestSet); return Evaluate(dataSplit.TestSet);
} }
finally finally
{ {
File.Delete(filename); _logger.LogInformation("Training finished");
} }
} }
private void LogEvents(Action<string> log, LoggingEventArgs e) private void LogEvents(Action<string> log, LoggingEventArgs e)
@ -56,8 +75,10 @@ namespace DeepTrace.ML
private MLEvaluationMetrics Evaluate(IDataView testData) private MLEvaluationMetrics Evaluate(IDataView testData)
{ {
// https://learn.microsoft.com/en-us/dotnet/api/microsoft.ml.standardtrainerscatalog.lbfgslogisticregression?view=ml-dotnet
var predictions = _transformer!.Transform(testData); var predictions = _transformer!.Transform(testData);
var metrics = _mlContext.MulticlassClassification.Evaluate(predictions, "Name"); var metrics = _mlContext.MulticlassClassification.Evaluate(predictions, nameof(MLInputData.Label));
var evaluationMetrics = new MLEvaluationMetrics() var evaluationMetrics = new MLEvaluationMetrics()
{ {
MicroAccuracy = metrics.MicroAccuracy, MicroAccuracy = metrics.MicroAccuracy,
@ -110,28 +131,25 @@ namespace DeepTrace.ML
(_mlContext, _schema, _transformer) = MLHelpers.ImportSingleModel(bytes); (_mlContext, _schema, _transformer) = MLHelpers.ImportSingleModel(bytes);
} }
public async Task<Prediction> Predict(TrainedModelDefinition trainedModel, ModelDefinition model, List<TimeSeriesDataSet> data) public Task<Prediction> Predict(TrainedModelDefinition trainedModel, ModelDefinition model, List<TimeSeriesDataSet> data)
{ {
Name = trainedModel.Name;
if (_transformer == null )
Import(trainedModel.Value); Import(trainedModel.Value);
var headers = string.Join(",", model.GetColumnNames().Select(x => $"\"{x}\""));
var row = ModelDefinition.ConvertToCsv(data);
var csv = headers+"\n"+row; if (_predictionEngine == null)
var fileName = Path.GetTempFileName();
try
{ {
await File.WriteAllTextAsync(fileName, csv); _predictionEngine = _mlContext.Model.CreatePredictionEngine<MLInputData, MLOutputData>(_transformer, _schema);
var (dataView, _) = MLHelpers.LoadFromCsv(_mlContext, model, fileName);
var predictionEngine = _mlContext.Model.CreatePredictionEngine<IDataView, Prediction>(_transformer);
var prediction = predictionEngine.Predict(dataView);
return prediction;
} }
finally
var input = new MLInputData
{ {
File.Delete(fileName); Features = DataSourceDefinition.ToFeatures(data)
} };
}
var prediction = _predictionEngine.Predict( input );
return Task.FromResult( new Prediction { PredictedLabel = prediction.PredictedLabel, Score = prediction.Score } );
} }
} }

View File

@ -1,7 +1,7 @@
using PrometheusAPI; using PrometheusAPI;
namespace DeepTrace.ML namespace DeepTrace.ML;
{
public class MeasureMin : IMeasure public class MeasureMin : IMeasure
{ {
public string Name => "Min"; public string Name => "Min";
@ -85,5 +85,3 @@ namespace DeepTrace.ML
/// </summary> /// </summary>
public class MeasureDiffSum : MeasureDiff<MeasureSum> { } public class MeasureDiffSum : MeasureDiff<MeasureSum> { }
public class MeasureDiffMedian : MeasureDiff<MeasureMedian> { } public class MeasureDiffMedian : MeasureDiff<MeasureMedian> { }
}

View File

@ -58,8 +58,8 @@
int pos = i; int pos = i;
<MudItem xs="10"> <MudItem xs="10">
@*<MudTextField Label="Query" @bind-Value="_queryForm.Source.Queries[pos].Query" Variant="Variant.Text" InputType="InputType.Search" Lines="2" />*@ <MudTextField Label="Query" @bind-Value="_queryForm.Source.Queries[pos].Query" Variant="Variant.Text" InputType="InputType.Search" Lines="2" />
<MudAutocomplete Label="Query" @bind-Value="_queryForm.Source.Queries[pos].Query" Lines="1" Variant="Variant.Text" SearchFunc="@SearchForQuery"></MudAutocomplete> @*<MudAutocomplete Label="Query" @bind-Value="_queryForm.Source.Queries[pos].Query" Lines="1" Variant="Variant.Text" SearchFunc="@SearchForQuery"></MudAutocomplete>*@
</MudItem> </MudItem>
<MudItem xs="1"> <MudItem xs="1">
<MudIconButton Icon="@Icons.Material.Outlined.Add" Variant="Variant.Outlined" aria-label="add" OnClick="@(() => AddQuery(pos))" /> <MudIconButton Icon="@Icons.Material.Outlined.Add" Variant="Variant.Outlined" aria-label="add" OnClick="@(() => AddQuery(pos))" />

View File

@ -2,8 +2,8 @@
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics; using System.Diagnostics;
namespace DeepTrace.Pages namespace DeepTrace.Pages;
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken] [IgnoreAntiforgeryToken]
public class ErrorModel : PageModel public class ErrorModel : PageModel
@ -24,4 +24,3 @@ namespace DeepTrace.Pages
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
} }
} }
}

View File

@ -18,7 +18,7 @@
@inject IEstimatorBuilder EstimatorBuilder @inject IEstimatorBuilder EstimatorBuilder
@inject NavigationManager NavManager @inject NavigationManager NavManager
@inject IJSRuntime Js @inject IJSRuntime Js
@inject ILogger<MLProcessor> MLProcessorLogger @inject IMLProcessorFactory MlProcessorFactory
<PageTitle>Training</PageTitle> <PageTitle>Training</PageTitle>
@ -531,14 +531,14 @@
private async Task HandleTrain() private async Task HandleTrain()
{ {
var mlProcessor = new MLProcessor(MLProcessorLogger);
MLProcessorLogger.LogInformation("Training started");
var options = new DialogOptions var options = new DialogOptions
{ {
CloseOnEscapeKey = true CloseOnEscapeKey = true
}; };
var parameters = new DialogParameters(); var parameters = new DialogParameters();
var mlProcessor = MlProcessorFactory.Create();
parameters.Add(nameof(Controls.TrainingDialog.Text), _modelForm!.CurrentModel.Name); parameters.Add(nameof(Controls.TrainingDialog.Text), _modelForm!.CurrentModel.Name);
parameters.Add(nameof(Controls.TrainingDialog.Processor), mlProcessor); parameters.Add(nameof(Controls.TrainingDialog.Processor), mlProcessor);
parameters.Add(nameof(Controls.TrainingDialog.Model), _modelForm.CurrentModel); parameters.Add(nameof(Controls.TrainingDialog.Model), _modelForm.CurrentModel);
@ -546,7 +546,6 @@
var d = DialogService.Show<Controls.TrainingDialog>("Training", parameters, options); var d = DialogService.Show<Controls.TrainingDialog>("Training", parameters, options);
var res = await d.Result; var res = await d.Result;
MLProcessorLogger.LogInformation("Training finished");
var bytes = mlProcessor.Export(); var bytes = mlProcessor.Export();
//save to Mongo //save to Mongo

View File

@ -29,6 +29,7 @@ builder.Services
.AddSingleton<IModelStorageService, ModelStorageService>() .AddSingleton<IModelStorageService, ModelStorageService>()
.AddSingleton<ITrainedModelStorageService, TrainedModelStorageService>() .AddSingleton<ITrainedModelStorageService, TrainedModelStorageService>()
.AddSingleton<IEstimatorBuilder, EstimatorBuilder>() .AddSingleton<IEstimatorBuilder, EstimatorBuilder>()
.AddSingleton<IMLProcessorFactory, MLProcessorFactory>()
; ;
var app = builder.Build(); var app = builder.Build();

View File

@ -1,8 +1,8 @@
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
namespace DeepTrace.Services namespace DeepTrace.Services;
{
public class DataSourceStorageService : IDataSourceStorageService public class DataSourceStorageService : IDataSourceStorageService
{ {
@ -55,4 +55,3 @@ namespace DeepTrace.Services
await collection.DeleteOneAsync($"_id = {source.Id}"); await collection.DeleteOneAsync($"_id = {source.Id}");
} }
} }
}

View File

@ -2,8 +2,8 @@
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson; using MongoDB.Bson;
namespace DeepTrace.Services namespace DeepTrace.Services;
{
public class DataSourceStorage : DataSourceDefinition, IEquatable<DataSourceStorage> public class DataSourceStorage : DataSourceDefinition, IEquatable<DataSourceStorage>
{ {
[BsonId] [BsonId]
@ -35,4 +35,3 @@ namespace DeepTrace.Services
Task<List<DataSourceStorage>> Load(); Task<List<DataSourceStorage>> Load();
Task Store(DataSourceStorage source); Task Store(DataSourceStorage source);
} }
}

View File

@ -3,8 +3,8 @@ using MongoDB.Bson;
using DeepTrace.Data; using DeepTrace.Data;
using System.Text; using System.Text;
namespace DeepTrace.Services namespace DeepTrace.Services;
{
public interface IModelStorageService public interface IModelStorageService
{ {
@ -13,4 +13,3 @@ namespace DeepTrace.Services
Task<ModelDefinition?> Load(BsonObjectId id); Task<ModelDefinition?> Load(BsonObjectId id);
Task Store(ModelDefinition source); Task Store(ModelDefinition source);
} }
}

View File

@ -1,11 +1,10 @@
using DeepTrace.Data; using DeepTrace.Data;
namespace DeepTrace.Services namespace DeepTrace.Services;
{
public interface ITrainedModelStorageService public interface ITrainedModelStorageService
{ {
Task Delete(TrainedModelDefinition source, bool ignoreNotStored = false); Task Delete(TrainedModelDefinition source, bool ignoreNotStored = false);
Task<List<TrainedModelDefinition>> Load(); Task<List<TrainedModelDefinition>> Load();
Task Store(TrainedModelDefinition source); Task Store(TrainedModelDefinition source);
} }
}

View File

@ -2,8 +2,8 @@
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
namespace DeepTrace.Services namespace DeepTrace.Services;
{
public class ModelStorageService : IModelStorageService public class ModelStorageService : IModelStorageService
{ {
@ -30,8 +30,9 @@ namespace DeepTrace.Services
{ {
var db = _client.GetDatabase(MongoDBDatabaseName); var db = _client.GetDatabase(MongoDBDatabaseName);
var collection = db.GetCollection<ModelDefinition>(MongoDBCollection); var collection = db.GetCollection<ModelDefinition>(MongoDBCollection);
var res = (await (await collection.FindAsync($"{{_id:ObjectId(\"{id}\")}}")).ToListAsync()).FirstOrDefault();
return res; var res = await (await collection.FindAsync($"{{ _id : ObjectId(\"{id}\") }}")).ToListAsync();
return res.FirstOrDefault();
} }
public async Task Store(ModelDefinition source) public async Task Store(ModelDefinition source)
@ -65,4 +66,3 @@ namespace DeepTrace.Services
await collection.DeleteOneAsync(filter: new BsonDocument("_id", source.Id)); await collection.DeleteOneAsync(filter: new BsonDocument("_id", source.Id));
} }
} }
}

View File

@ -2,8 +2,8 @@
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
namespace DeepTrace.Services namespace DeepTrace.Services;
{
public class TrainedModelStorageService: ITrainedModelStorageService public class TrainedModelStorageService: ITrainedModelStorageService
{ {
private const string MongoDBDatabaseName = "DeepTrace"; private const string MongoDBDatabaseName = "DeepTrace";
@ -55,4 +55,3 @@ namespace DeepTrace.Services
await collection.DeleteOneAsync(filter: new BsonDocument("_id", source.Id)); await collection.DeleteOneAsync(filter: new BsonDocument("_id", source.Id));
} }
} }
}

View File

@ -6,8 +6,8 @@ using System.Text.Json.Serialization;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace PrometheusAPI namespace PrometheusAPI;
{
public static class JsonSetializerSetup public static class JsonSetializerSetup
{ {
private static JsonSerializerOptions _options = new JsonSerializerOptions private static JsonSerializerOptions _options = new JsonSerializerOptions
@ -22,4 +22,3 @@ namespace PrometheusAPI
public static JsonSerializerOptions Options => _options; public static JsonSerializerOptions Options => _options;
} }
}

View File

@ -1,7 +1,7 @@
using System.Text.Json; using System.Text.Json;
namespace PrometheusAPI namespace PrometheusAPI;
{
public class PrometheusClient public class PrometheusClient
{ {
private readonly HttpClient _client; private readonly HttpClient _client;
@ -116,4 +116,3 @@ namespace PrometheusAPI
} }
} }
}

View File

@ -1,8 +1,8 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace PrometheusAPI namespace PrometheusAPI;
{
internal class TimeSeriesCoverter : JsonConverter<TimeSeries?> internal class TimeSeriesCoverter : JsonConverter<TimeSeries?>
{ {
public override TimeSeries? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override TimeSeries? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
@ -49,4 +49,3 @@ namespace PrometheusAPI
} }
} }
}