欢迎光临
我们一直在努力

从零搭建 ML.NET 智能系统:全栈开发实战

很多人接触 ML.NET 都止步于控制台 Demo:加载模型、调用 Predict、输出结果,看似跑通了,真要落地成完整的业务系统时却无从下手——不知道怎么分层架构、不知道怎么和现有业务对接、不知道怎么做生产级稳定性保障,最终只能停留在玩具阶段。

实际上,一套可上线的 ML.NET 智能系统,本质是工程化优先,算法为辅:算法决定效果上限,工程决定能不能稳定跑起来。本文以工业场景最常用的「加工质量智能预测系统」为实战案例,从零开始完整拆解从数据处理、模型构建、服务封装、业务集成到部署运维的全栈开发流程,所有代码均可直接复用到实际项目中。

一、项目目标与整体架构

1.1 业务背景与目标

我们要实现一套面向离散制造的加工质量预测系统:实时采集设备的温度、压力、转速、进给量等工艺参数,通过 AI 模型实时预测当前产品的合格概率与潜在缺陷类型,提前拦截不良品,同时辅助工艺人员优化参数。

核心要求:

  • 支持上位机本地内嵌调用,也支持 Web API 服务化调用
  • 单条推理耗时 < 1ms,支持每秒百级并发
  • 模型可热更新,无需重启业务系统
  • 断网可正常运行,数据本地留存
  • 异常自动降级,不影响主生产流程

1.2 技术栈选型

完全基于 .NET 生态,零 Python 依赖,全链路 C# 实现:

层级技术选型说明
模型训练 ML.NET + LightGBM 训练器 表格数据场景效果最优,原生支持
推理引擎 ML.NET + ONNX Runtime 兼顾原生模型与外部模型,性能最优
后端服务 ASP.NET Core Web API 企业级服务化部署
客户端 WinForms 上位机 工业现场标准形态
数据存储 SQLite + SqlServer 本地缓存 + 业务库持久化
日志监控 Serilog + 本地监控面板 离线可观测

1.3 分层架构设计

采用四层解耦架构,各层职责单一,边界清晰,后续替换模型、调整业务、更换接入方式都不会牵一发而动全身。

#mermaid-svg-v7m3uDODy1KCPlAw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-v7m3uDODy1KCPlAw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-v7m3uDODy1KCPlAw .error-icon{fill:#552222;}#mermaid-svg-v7m3uDODy1KCPlAw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v7m3uDODy1KCPlAw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v7m3uDODy1KCPlAw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v7m3uDODy1KCPlAw .marker.cross{stroke:#333333;}#mermaid-svg-v7m3uDODy1KCPlAw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v7m3uDODy1KCPlAw p{margin:0;}#mermaid-svg-v7m3uDODy1KCPlAw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-v7m3uDODy1KCPlAw .cluster-label text{fill:#333;}#mermaid-svg-v7m3uDODy1KCPlAw .cluster-label span{color:#333;}#mermaid-svg-v7m3uDODy1KCPlAw .cluster-label span p{background-color:transparent;}#mermaid-svg-v7m3uDODy1KCPlAw .label text,#mermaid-svg-v7m3uDODy1KCPlAw span{fill:#333;color:#333;}#mermaid-svg-v7m3uDODy1KCPlAw .node rect,#mermaid-svg-v7m3uDODy1KCPlAw .node circle,#mermaid-svg-v7m3uDODy1KCPlAw .node ellipse,#mermaid-svg-v7m3uDODy1KCPlAw .node polygon,#mermaid-svg-v7m3uDODy1KCPlAw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-v7m3uDODy1KCPlAw .rough-node .label text,#mermaid-svg-v7m3uDODy1KCPlAw .node .label text,#mermaid-svg-v7m3uDODy1KCPlAw .image-shape .label,#mermaid-svg-v7m3uDODy1KCPlAw .icon-shape .label{text-anchor:middle;}#mermaid-svg-v7m3uDODy1KCPlAw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-v7m3uDODy1KCPlAw .rough-node .label,#mermaid-svg-v7m3uDODy1KCPlAw .node .label,#mermaid-svg-v7m3uDODy1KCPlAw .image-shape .label,#mermaid-svg-v7m3uDODy1KCPlAw .icon-shape .label{text-align:center;}#mermaid-svg-v7m3uDODy1KCPlAw .node.clickable{cursor:pointer;}#mermaid-svg-v7m3uDODy1KCPlAw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-v7m3uDODy1KCPlAw .arrowheadPath{fill:#333333;}#mermaid-svg-v7m3uDODy1KCPlAw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-v7m3uDODy1KCPlAw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-v7m3uDODy1KCPlAw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v7m3uDODy1KCPlAw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-v7m3uDODy1KCPlAw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v7m3uDODy1KCPlAw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-v7m3uDODy1KCPlAw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-v7m3uDODy1KCPlAw .cluster text{fill:#333;}#mermaid-svg-v7m3uDODy1KCPlAw .cluster span{color:#333;}#mermaid-svg-v7m3uDODy1KCPlAw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-v7m3uDODy1KCPlAw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-v7m3uDODy1KCPlAw rect.text{fill:none;stroke-width:0;}#mermaid-svg-v7m3uDODy1KCPlAw .icon-shape,#mermaid-svg-v7m3uDODy1KCPlAw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v7m3uDODy1KCPlAw .icon-shape p,#mermaid-svg-v7m3uDODy1KCPlAw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-v7m3uDODy1KCPlAw .icon-shape .label rect,#mermaid-svg-v7m3uDODy1KCPlAw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v7m3uDODy1KCPlAw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-v7m3uDODy1KCPlAw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-v7m3uDODy1KCPlAw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

📥 请求/指令

🧠 推理调用

💾 读写操作

📊 结果返回/数据加载

🔄 模型加载/配置读取

⚡ 实时响应/告警推送

数据层

原始采集数据

特征数据集

模型文件库

推理结果库

AI 推理层

模型管理

推理实例池

特征处理管线

热更新控制器

业务逻辑层

参数校验

规则兜底

结果落库

告警联动

接入层

WinForms 上位机

Web API 接口

第三方系统对接

1.4 项目结构

标准 .NET 解决方案分层,训练与推理分离,业务与AI解耦:

SmartQuality.sln
├── SmartQuality.Models // 公共实体:输入输出、配置类
├── SmartQuality.MLService // AI 核心:推理、模型管理、特征工程
├── SmartQuality.Business // 业务层:规则校验、业务逻辑
├── SmartQuality.Api // Web 服务:ASP.NET Core API
├── SmartQuality.WinClient // 客户端:上位机内嵌程序
└── SmartQuality.Training // 训练工具:控制台程序,离线训练用

二、环境搭建与项目初始化

2.1 开发环境

  • Visual Studio 2022 / Rider
  • .NET 8 LTS(长期支持版,工业场景首选)
  • 可选:Python 3.10 + PyTorch/LightGBM(仅训练用,生产环境不需要)

2.2 核心 NuGet 包

各层按需安装,不要把训练相关包引入生产项目:

<!– MLService 层核心包 –>
<PackageReference Include="Microsoft.ML" Version="3.0.1" />
<PackageReference Include="Microsoft.ML.LightGbm" Version="3.0.1" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.17.3" />

<!– API 层基础包 –>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" />

注意:生产环境不要安装训练相关的冗余包,只保留推理必需的包,减小发布体积,降低兼容性风险。

三、数据准备与特征工程管线

3.1 定义数据实体

首先定义原始输入与预测输出实体,全程复用,避免多份定义导致不一致。

// 原始输入:工艺参数
public class ProcessInput
{
public float Temperature { get; set; } // 加工温度
public float Pressure { get; set; } // 压力
public float RotateSpeed { get; set; } // 转速
public float FeedRate { get; set; } // 进给量
public float MaterialHardness { get; set; }// 原料硬度
public string MaterialBatch { get; set; } // 原料批次
}

// 预测输出
public class QualityPrediction
{
[ColumnName("PredictedLabel")]
public bool IsQualified { get; set; } // 是否合格

[ColumnName("Score")]
public float[] Probability { get; set; } // 各类别概率

public float QualifiedProbability => Probability[1]; // 合格概率
public float DefectProbability => Probability[0]; // 缺陷概率
}

3.2 构建特征处理管线

特征工程是效果的核心,必须把所有特征转换逻辑写入 ML.NET 管线,和模型一起序列化保存,推理端直接加载,从根源保证训练与推理口径一致。

// 训练与推理共用的特征管线构建方法
public static IEstimator<ITransformer> BuildFeaturePipeline(MLContext mlContext)
{
var pipeline = mlContext.Transforms.ReplaceMissingValues(
replacementMode: MissingValueReplacingEstimator.ReplacementMode.Median,
inputColumnName: nameof(ProcessInput.Temperature),
outputColumnName: "Temperature_Filled")

// 数值特征归一化
.Append(mlContext.Transforms.NormalizeMinMax(
new[]
{
"Temperature_Filled",
nameof(ProcessInput.Pressure),
nameof(ProcessInput.RotateSpeed),
nameof(ProcessInput.FeedRate),
nameof(ProcessInput.MaterialHardness)
},
outputColumnName: "NumericFeatures"))

// 类别特征编码
.Append(mlContext.Transforms.Categorical.OneHotEncoding(
inputColumnName: nameof(ProcessInput.MaterialBatch),
outputColumnName: "Batch_OneHot"))

// 合并所有特征
.Append(mlContext.Transforms.Concatenate(
"Features",
"NumericFeatures",
"Batch_OneHot"));

return pipeline;
}

核心原则:特征处理逻辑只写一次,训练用它,推理也用它。绝对不允许训练端和推理端各写一套特征计算逻辑。

四、模型训练与验证

4.1 完整训练流程

训练项目为独立控制台程序,离线运行,训练完成后输出模型文件与评估报告。

class Program
{
static void Main(string[] args)
{
var mlContext = new MLContext(seed: 1); // 固定种子,结果可复现

// 1. 加载数据
var data = mlContext.Data.LoadFromTextFile<ProcessInput>(
path: "dataset/train_data.csv",
separatorChar: ',',
hasHeader: true);

// 划分训练集 80%,测试集 20%
var trainTestSplit = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);
var trainData = trainTestSplit.TrainSet;
var testData = trainTestSplit.TestSet;

// 2. 构建完整管线:特征工程 + 训练器
var featurePipeline = BuildFeaturePipeline(mlContext);
var trainer = mlContext.BinaryClassification.Trainers.LightGbm(
labelColumnName: "Label",
featureColumnName: "Features",
numberOfLeaves: 31,
minimumExampleCountPerLeaf: 20,
learningRate: 0.05f,
numberOfIterations: 200);

var fullPipeline = featurePipeline.Append(trainer);

// 3. 训练模型
Console.WriteLine("开始训练模型…");
var model = fullPipeline.Fit(trainData);

// 4. 评估效果
var predictions = model.Transform(testData);
var metrics = mlContext.BinaryClassification.Evaluate(predictions);

Console.WriteLine($"模型评估结果:");
Console.WriteLine($"准确率: {metrics.Accuracy:F4}");
Console.WriteLine($"AUC: {metrics.AreaUnderRocCurve:F4}");
Console.WriteLine($"召回率: {metrics.Recall:F4}");
Console.WriteLine($"精确率: {metrics.Precision:F4}");

// 5. 保存完整模型(包含特征转换 + 训练好的模型)
mlContext.Model.Save(model, trainData.Schema, "models/quality_model.zip");
Console.WriteLine("模型已保存到 models/quality_model.zip");
}
}

4.2 工业场景训练要点

  • 样本不平衡处理:缺陷样本通常远少于合格样本,使用 Weight 参数给缺陷样本加权,或使用过采样/欠采样平衡数据集。
  • 优先保障召回率:工业场景漏检代价远大于误检,训练后可通过调整阈值提升召回率,牺牲少量精确率。
  • 可解释性优先:优先选择树模型而非深度学习,可输出特征重要性,方便工艺人员理解和验证,避免黑盒模型难以落地。
  • // 输出特征重要性,辅助工艺验证
    var featureImportance = model.LastTransformer.GetFeatureImportance(
    mlContext, trainData.Schema, "Features");

    五、推理服务层工程化封装

    这是从 Demo 到生产系统的核心一步:把简单的 Predict 封装成稳定、安全、高性能的推理服务。

    5.1 定义抽象接口

    面向接口编程,屏蔽底层实现细节,后续更换模型、切换 ONNX 都不会影响业务层。

    public interface IQualityInferenceService
    {
    string CurrentVersion { get; }
    QualityPrediction Predict(ProcessInput input);
    List<QualityPrediction> PredictBatch(List<ProcessInput> inputs);
    bool UpdateModel(string modelPath, string version);
    }

    5.2 线程安全的推理实现

    使用 PredictionEnginePool 管理推理实例,解决 PredictionEngine 非线程安全的问题,同时支持对象复用,避免频繁创建销毁的开销。

    public class QualityInferenceService : IQualityInferenceService
    {
    private readonly MLContext _mlContext;
    private PredictionEnginePool<ProcessInput, QualityPrediction> _enginePool;
    private readonly ReaderWriterLockSlim _lock = new();
    private readonly ILogger _logger;

    public string CurrentVersion { get; private set; }

    public QualityInferenceService(ILogger<QualityInferenceService> logger)
    {
    _logger = logger;
    _mlContext = new MLContext(seed: 1);
    }

    // 初始化加载模型
    public void Init(string modelPath, string version)
    {
    var model = _mlContext.Model.Load(modelPath, out _);
    _enginePool = new PredictionEnginePool<ProcessInput, QualityPrediction>(
    _mlContext, model, poolSize: Environment.ProcessorCount);
    CurrentVersion = version;
    _logger.LogInformation($"模型加载完成,版本:{version}");
    }

    // 单条推理
    public QualityPrediction Predict(ProcessInput input)
    {
    try
    {
    _lock.EnterReadLock();
    return _enginePool.Predict(input);
    }
    catch (Exception ex)
    {
    _logger.LogError(ex, "推理异常");
    // 异常兜底:默认返回合格,避免阻塞生产,同时记录告警
    return new QualityPrediction { IsQualified = true, Probability = new[] { 0f, 1f } };
    }
    finally
    {
    _lock.ExitReadLock();
    }
    }

    // 批量推理
    public List<QualityPrediction> PredictBatch(List<ProcessInput> inputs)
    {
    return inputs.Select(Predict).ToList();
    }

    // 热更新模型
    public bool UpdateModel(string modelPath, string version)
    {
    try
    {
    // 1. 后台加载新模型
    var newModel = _mlContext.Model.Load(modelPath, out _);
    var newPool = new PredictionEnginePool<ProcessInput, QualityPrediction>(
    _mlContext, newModel, poolSize: Environment.ProcessorCount);

    // 2. 基准校验
    if (!ValidateBenchmark(newPool))
    {
    _logger.LogError("新模型基准校验失败,取消更新");
    return false;
    }

    // 3. 原子切换
    _lock.EnterWriteLock();
    var oldPool = _enginePool;
    _enginePool = newPool;
    CurrentVersion = version;
    _lock.ExitWriteLock();

    // 4. 延迟释放旧模型
    _ = Task.Delay(TimeSpan.FromSeconds(30)).ContinueWith(_ => oldPool.Dispose());

    _logger.LogInformation($"模型热更新成功,新版本:{version}");
    return true;
    }
    catch (Exception ex)
    {
    _logger.LogError(ex, "模型更新失败");
    return false;
    }
    }

    // 基准样本校验,保证模型输出符合预期
    private bool ValidateBenchmark(PredictionEnginePool<ProcessInput, QualityPrediction> pool)
    {
    var testInput = new ProcessInput
    {
    Temperature = 180,
    Pressure = 2.5f,
    RotateSpeed = 1200,
    FeedRate = 0.8f,
    MaterialHardness = 45,
    MaterialBatch = "B001"
    };
    var result = pool.Predict(testInput);
    // 基准样本合格概率应在 0.92~0.96 之间,超出范围说明模型异常
    return result.QualifiedProbability is > 0.92f and < 0.96f;
    }
    }

    5.3 性能优化要点

  • 池大小设置:CPU 密集型推理,池大小设为物理核心数即可,不是越大越好,过多线程会导致上下文切换开销飙升。
  • 输入复用:高频场景下复用输入对象,避免频繁 new 产生 GC 压力。
  • 关闭日志:生产环境关闭 ML.NET 内部调试日志,减少 IO 开销。
  • 启用 SIMD:ONNX Runtime 自动启用 CPU 指令集优化,确保发布时选择 x64 平台,不要用 AnyCPU。
  • 六、业务系统集成

    6.1 ASP.NET Core Web API 服务化

    适合多系统共享 AI 能力的企业场景,通过依赖注入注册服务,对外提供标准 HTTP 接口。

    // Program.cs 注册服务
    builder.Services.AddSingleton<IQualityInferenceService, QualityInferenceService>(sp =>
    {
    var service = new QualityInferenceService(sp.GetRequiredService<ILogger<QualityInferenceService>>());
    service.Init("models/quality_model.zip", "v1.0.0");
    return service;
    });

    // 控制器
    [ApiController]
    [Route("api/quality")]
    public class QualityController : ControllerBase
    {
    private readonly IQualityInferenceService _inferenceService;

    public QualityController(IQualityInferenceService inferenceService)
    {
    _inferenceService = inferenceService;
    }

    [HttpPost("predict")]
    public ActionResult<QualityPrediction> Predict([FromBody] ProcessInput input)
    {
    // 1. 参数校验
    if (!input.IsValid()) return BadRequest("参数超出工艺范围");

    // 2. 推理
    var result = _inferenceService.Predict(input);

    // 3. 业务规则兜底:模型置信度低于阈值时,触发人工复核
    if (result.QualifiedProbability is > 0.7f and < 0.9f)
    {
    result.IsQualified = true;
    // 标记为待复核
    }

    // 4. 异步落库
    _ = Task.Run(() => SaveResult(input, result));

    return Ok(result);
    }
    }

    6.2 WinForms 上位机内嵌

    适合工业现场边缘部署,推理直接运行在上位机进程内,零网络延迟,断网可用。

    public partial class MainForm : Form
    {
    private IQualityInferenceService _inferenceService;
    private Thread _collectThread;
    private volatile bool _isRunning;

    private void MainForm_Load(object sender, EventArgs e)
    {
    // 初始化推理服务,UI 线程不做 heavy work
    _inferenceService = new QualityInferenceService(NullLogger.Instance);
    _inferenceService.Init("models/quality_model.zip", "v1.0.0");

    // 采集线程独立运行,不阻塞 UI
    _isRunning = true;
    _collectThread = new Thread(CollectAndDetectLoop)
    {
    IsBackground = true,
    Priority = ThreadPriority.Normal // 低于采集与控制线程
    };
    _collectThread.Start();
    }

    private void CollectAndDetectLoop()
    {
    while (_isRunning)
    {
    // 1. 从 PLC 读取实时工艺参数
    var input = PlcClient.ReadCurrentProcessParams();

    // 2. AI 推理
    var result = _inferenceService.Predict(input);

    // 3. 结果回显到 UI(跨线程更新)
    this.Invoke(() => UpdateUi(result));

    // 4. 缺陷触发告警与剔除
    if (!result.IsQualified && result.DefectProbability > 0.9f)
    {
    PlcClient.TriggerReject();
    AlarmHelper.Beep();
    }

    Thread.Sleep(100); // 10Hz 检测频率
    }
    }
    }

    工业上位机核心原则:推理永远不要阻塞 UI 线程和控制线程。单独开后台线程做推理,优先级设为低于主线程,避免抢占控制资源。

    七、监控与运维体系

    7.1 核心指标埋点

    在推理服务中加入指标统计,实时掌握服务状态:

    • 吞吐量:每分钟推理次数
    • 延迟:平均耗时、P95、P99 耗时
    • 错误率:推理异常次数占比
    • 输出分布:合格/缺陷比例,监控模型漂移
    • 资源占用:CPU、内存、GC 次数

    7.2 本地监控面板

    WinForms 客户端集成简易监控面板,实时展示上述指标,现场运维人员一眼就能看到 AI 系统运行状态,无需依赖外部监控平台。

    7.3 数据回流与模型迭代

    • 自动留存边界样本、误检漏检样本,本地存储
    • 定期导出标注后的样本,加入训练集
    • 增量训练新版本模型,通过热更新下发
    • 形成「运行-采集-标注-训练-更新」的闭环,模型越跑越准

    八、生产部署

    8.1 发布方式

    工业场景优先选择自包含单文件发布,目标机器不需要安装 .NET 运行时,拷贝过去就能跑。

    dotnet publish -c Release -r win-x64 –self-contained true
    /p:PublishSingleFile=true
    /p:IncludeNativeLibrariesForSelfExtract=true
    /p:EnableCompressionInSingleFile=true

    8.2 部署形态

    • 上位机内嵌:直接把 DLL 和模型文件拷入现有上位机目录,修改配置即可启用,零侵入。
    • Windows 服务:服务化部署时,使用 Worker Service 模板,安装为 Windows 服务,开机自启,异常自动重启。
    • Linux Docker:企业内网部署时,打包为 Docker 镜像,配合 K8s 弹性扩缩容。

    8.3 老工控机兼容

    • 启动时检测 CPU 是否支持 AVX,不支持自动切换兼容模式。
    • 关闭图形优化、禁用并行度,牺牲部分性能换取兼容性。
    • 优先使用 .NET 6,对老系统 Windows 7 兼容性更好。

    九、高频踩坑与解决方案

  • 训练推理结果不一致

    • 根因:两边特征处理逻辑不同,或者归一化参数不一致。
    • 解决:永远保存完整管线,推理端直接加载,不手写特征逻辑;上线前做双端一致性校验。
  • 并发调用偶发崩溃

    • 根因:单例 PredictionEngine 跨线程调用,原生层内存访问冲突。
    • 解决:使用官方 PredictionEnginePool 或 ThreadLocal 封装,绝对不要共享单实例。
  • 运行越久内存越高

    • 根因:频繁创建销毁推理实例,大对象堆碎片化。
    • 解决:全局复用推理池,输入对象复用,低峰期主动 GC 压缩大对象堆。
  • 老机器启动直接报错

    • 根因:ONNX Runtime 默认依赖 AVX 指令集,老 CPU 不支持。
    • 解决:启动时做指令集检测,自动降级,或使用纯 ML.NET 原生模型。
  • 现场数据变化后效果下降

    • 根因:模型漂移,生产数据分布与训练集偏离。
    • 解决:监控输出分布,定期回流样本增量更新模型,不要一劳永逸。
  • 写在最后

    一套完整的 ML.NET 智能系统,算法只占很小一部分,更多的工作在工程化:分层架构、线程安全、异常兜底、性能优化、监控运维、兼容适配。这些东西看似和 AI 无关,却直接决定了系统能不能在生产环境稳定跑起来。

    对于 .NET 开发者来说,ML.NET 最大的价值不是替代 Python 做算法研究,而是让你用熟悉的技术栈,把 AI 能力无缝融入现有业务系统,不用跨语言、不用搭额外服务、不用维护两套技术栈,低成本、低风险地完成智能化升级。

    赞(0)
    未经允许不得转载:171主机测评 » 从零搭建 ML.NET 智能系统:全栈开发实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址