Predict yes/no outcomes from multiple input tags using ML.NET FastTree Binary Classification. The AI generates the full C# Script Class with multi-feature training pipeline, connects it to live tags, creates output tags for predicted label and probability, and configures model persistence.
Category | Items |
|---|---|
Tools |
|
Tables |
|
get_table_schema('UnsTags')
{
"table_type": "UnsTags",
"data": [
{ "Name": "<AssetPath>/ML/PredictedLabel", "DataType": "Boolean", "Description": "Predicted outcome (true/false)" },
{ "Name": "<AssetPath>/ML/Probability", "DataType": "Double", "Description": "Prediction probability (0-1)" },
{ "Name": "<AssetPath>/ML/LastPrediction", "DataType": "DateTime", "Description": "Timestamp of last prediction" }
]
}
Replace <AssetPath> with the actual asset folder path.
Critical: The field AddMLNetNamespaces does not exist and is silently ignored. Always use NamespaceDeclarations.
"NamespaceDeclarations": "Microsoft.ML;Microsoft.ML.Data;Microsoft.ML.Transforms;Microsoft.ML.Transforms.TimeSeries;Microsoft.ML.Transforms.Text;Microsoft.ML.Trainers;Microsoft.ML.TimeSeries"
Always use @Tag. prefix. ML.NET expects float but FrameworX tags use double — always cast with (float) when feeding ML.NET and (double) when writing to tags.
var pipeline = mlContext.Transforms.Concatenate("Features", "Feature1", "Feature2", "Feature3")
.Append(mlContext.BinaryClassification.Trainers.FastTree(
labelColumnName: "Label",
featureColumnName: "Features"));
Output: bool PredictedLabel, float Score, float Probability
public class ProcessData
{
public float Feature1 { get; set; } // e.g., Vibration
public float Feature2 { get; set; } // e.g., Temperature
public float Feature3 { get; set; } // e.g., Current
public bool Label { get; set; } // e.g., DidFault (true/false)
}
public class ClassificationPrediction
{
public bool PredictedLabel { get; set; }
public float Score { get; set; }
public float Probability { get; set; }
}
private static MLContext mlContext = new MLContext(seed: 0);
private static ITransformer model;
private static IDataView lastTrainingDataView;
private static PredictionEngine<ProcessData, ClassificationPrediction> predictionEngine;
private static bool modelTrained = false;
private static List<ProcessData> trainingBuffer = new List<ProcessData>();
private const int MinTrainingSize = 200;
private static readonly string ModelPath = Path.Combine(@Info.GetExecutionPath(), "<ClassName>.mlnet");
public void Predict(double input1, double input2, double input3, bool label)
{
trainingBuffer.Add(new ProcessData
{
Feature1 = (float)input1,
Feature2 = (float)input2,
Feature3 = (float)input3,
Label = label
});
if (!modelTrained && trainingBuffer.Count >= MinTrainingSize)
TrainModel();
if (modelTrained)
RunPrediction(input1, input2, input3);
}
public void LoadModel()
{
if (File.Exists(ModelPath))
{
model = mlContext.Model.Load(ModelPath, out _);
predictionEngine = mlContext.Model.CreatePredictionEngine<ProcessData, ClassificationPrediction>(model);
modelTrained = true;
}
}
private void TrainModel()
{
lastTrainingDataView = mlContext.Data.LoadFromEnumerable(trainingBuffer);
var pipeline = mlContext.Transforms.Concatenate("Features",
nameof(ProcessData.Feature1),
nameof(ProcessData.Feature2),
nameof(ProcessData.Feature3))
.Append(mlContext.BinaryClassification.Trainers.FastTree(
labelColumnName: "Label",
featureColumnName: "Features"));
model = pipeline.Fit(lastTrainingDataView);
predictionEngine = mlContext.Model.CreatePredictionEngine<ProcessData, ClassificationPrediction>(model);
modelTrained = true;
SaveModel();
}
private void SaveModel()
{
mlContext.Model.Save(model, lastTrainingDataView.Schema, ModelPath);
}
private void RunPrediction(double input1, double input2, double input3)
{
var input = new ProcessData
{
Feature1 = (float)input1,
Feature2 = (float)input2,
Feature3 = (float)input3
};
var result = predictionEngine.Predict(input);
@Tag.<AssetPath>/ML/PredictedLabel.Value = result.PredictedLabel;
@Tag.<AssetPath>/ML/Probability.Value = (double)result.Probability;
@Tag.<AssetPath>/ML/LastPrediction.Value = DateTime.Now;
}
Note on training data: The label parameter is needed during training — it's the known fault/pass flag. After training, only the feature inputs are needed. The AI should adapt the signature based on whether a label tag exists.
get_table_schema('ScriptsClasses')
{
"table_type": "ScriptsClasses",
"data": [
{
"Name": "<ClassName>",
"Code": "CSharp",
"Domain": "Server",
"ClassContent": "Methods",
"NamespaceDeclarations": "Microsoft.ML;Microsoft.ML.Data;Microsoft.ML.Transforms;Microsoft.ML.Transforms.TimeSeries;Microsoft.ML.Transforms.Text;Microsoft.ML.Trainers;Microsoft.ML.TimeSeries",
"Contents": "<AI-generated C# code from full class example above>"
}
]
}
Field names matter: Code = language (not Language), Contents = code body (not Code). Wrong names = silent data loss.
get_table_schema('ScriptsTasks')
{
"table_type": "ScriptsTasks",
"data": [
{
"Name": "ML_Classify_Periodic",
"Language": "CSharp",
"Execution": "Periodic",
"Period": 5000,
"Code": "@Script.Class.<ClassName>.Predict(\n @Tag.Plant/Motor1/VibrationX.Value,\n @Tag.Plant/Motor1/Temperature.Value,\n @Tag.Plant/Motor1/Current.Value,\n @Tag.Plant/Motor1/DidFault.Value);"
}
]
}
The @ prefix is mandatory when referencing runtime objects inside ScriptsTasks. Without it: CS0234.
Read the existing ServerStartup task first (document object — read-modify-write), then append:
Script.Class.<ClassName>.LoadModel();
MinTrainingSize data points (default 200)LastPrediction updates, PredictedLabel and Probability have reasonable valuesMistake | Why It Happens | How to Avoid |
|---|---|---|
Missing ML.NET namespaces | Used | Always set |
| Missing | Always |
| Digital tags are | Use ternary: |
| Build order issue | Set |
| .NET 4.8 target | Switch to Multiplatform |
Wrong data types | ML.NET=float, tags=double | Cast |
Model lost on restart | SaveModel/LoadModel missing | Include both + ServerStartup |
PredictionEngine null | Forgot to create after loading | Call |
Imbalanced training data | Very few fault=true examples | Ensure training buffer has enough positive examples before training |