第 18 章 机器学习基础

18.1 机器学习概念与工作流

ML.NET是微软开源的跨平台机器学习框架,支持.NET 开发者用 C#/F# 构建模型,核心工作流:数据加载→数据预处理→模型训练→模型评估→模型部署

核心概念

  • 特征(Feature):模型输入数据(如文本长度、图片像素);
  • 标签(Label):模型预测目标(如 “垃圾邮件”/“正常邮件”、房价);
  • 训练集 / 测试集:训练集用于模型学习,测试集用于评估模型精度;
  • 模型(Model):训练后生成的预测规则(如分类器、回归器)。

18.2ML.NET架构与管道设计

ML.NET通过管道(Pipeline) 串联数据处理与模型训练步骤,管道包含Estimator(数据转换器 / 模型训练器)和Transformer(训练后生成的执行器),支持模块化组合。

实战:ML.NET管道创建(房价预测)

  1. 安装 NuGet 包:Microsoft.ML、Microsoft.ML.Regression。
  2. 定义数据模型
// 输入模型(特征+标签)
public class HouseData
{
    [LoadColumn(0)] public float Size { get; set; } // 特征1:房屋面积(平方米)
    [LoadColumn(1)] public int Rooms { get; set; } // 特征2:房间数
    [LoadColumn(2)] public float Price { get; set; } // 标签:房价(万元)
}

// 预测输出模型
public class HousePricePrediction
{
    [ColumnName("Score")] public float PredictedPrice { get; set; } // 预测房价
}
  1. 构建管道与训练模型
public class HousePricePredictor
{
    private readonly MLContext _mlContext;
    private ITransformer _trainedModel;

    public HousePricePredictor()
    {
        _mlContext = new MLContext(seed: 1); // 固定种子,确保结果可复现
    }

    // 训练模型
    public void TrainModel(string dataPath)
    {
        // 1. 加载数据(CSV文件)
        var dataView = _mlContext.Data.LoadFromTextFile<HouseData>(
            path: dataPath,
            separatorChar: ',',
            hasHeader: true); // CSV第一行为表头

        // 2. 拆分训练集(80%)和测试集(20%)
        var trainTestSplit = _mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2);
        var trainData = trainTestSplit.TrainSet;
        var testData = trainTestSplit.TestSet;

        // 3. 构建管道
        var pipeline = _mlContext.Transforms.Concatenate(
                "Features", // 合并特征列到"Features"列
                nameof(HouseData.Size), 
                nameof(HouseData.Rooms))
            .Append(_mlContext.Transforms.NormalizeMinMax("Features")) // 特征归一化(0-1范围)
            .Append(_mlContext.Regression.Trainers.Sdca(labelColumnName: nameof(HouseData.Price), featureColumnName: "Features")); // 回归训练器(SDCA算法)

        // 4. 训练模型
        _trainedModel = pipeline.Fit(trainData);

        // 5. 评估模型
        var predictions = _trainedModel.Transform(testData);
        var metrics = _mlContext.Regression.Evaluate(predictions, labelColumnName: nameof(HouseData.Price));
        // 输出评估指标(越小越好)
        Console.WriteLine($"平均绝对误差(MAE):{metrics.MeanAbsoluteError:F2}");
        Console.WriteLine($"均方误差(MSE):{metrics.MeanSquaredError:F2}");
        Console.WriteLine($"R²得分(拟合度,越接近1越好):{metrics.RSquared:F2}");

        // 6. 保存模型
        _mlContext.Model.Save(_trainedModel, trainData.Schema, "house_price_model.zip");
        Console.WriteLine("模型已保存到:house_price_model.zip");
    }

    // 加载模型并预测
    public HousePricePrediction Predict(HouseData newHouse)
    {
        if (_trainedModel == null)
        {
            // 加载已保存的模型
            var modelPath = "house_price_model.zip";
            var modelSchema = _mlContext.Data.ReadSchemaFromModel(modelPath);
            _trainedModel = _mlContext.Model.Load(modelPath, out modelSchema);
        }

        // 创建预测引擎
        var predictionEngine = _mlContext.Model.CreatePredictionEngine<HouseData, HousePricePrediction>(_trainedModel);
        // 执行预测
        return predictionEngine.Predict(newHouse);
    }
}
  1. 使用模型
// 训练模型(数据文件house_data.csv包含Size,Rooms,Price)
var predictor = new HousePricePredictor();
predictor.TrainModel("house_data.csv");

// 预测新房屋价格(100㎡,3个房间)
var newHouse = new HouseData { Size = 100, Rooms = 3 };
var prediction = predictor.Predict(newHouse);
Console.WriteLine($"预测房价:{prediction.PredictedPrice:F2}万元");

18.3 数据加载与预处理

ML.NET支持加载 CSV、TSV、JSON、数据库等数据源,数据预处理是关键步骤(如缺失值填充、特征编码、文本分词),直接影响模型精度。

实战:文本数据预处理(垃圾邮件分类)

// 数据模型
public class EmailData
{
    [LoadColumn(0)] 
    public string Text { get; set; } // 邮件文本(特征)
    
    [LoadColumn(1), ColumnName("Label")]
    public bool IsSpam { get; set; } // 是否垃圾邮件(标签)
}

public class EmailPrediction
{
    [ColumnName("PredictedLabel")] 
    public bool IsSpam { get; set; } // 预测结果
    
    public float Score { get; set; } // 预测置信度
}

// 数据预处理管道
var pipeline = _mlContext.Transforms.Text.FeaturizeText(
        outputColumnName: "TextFeatures", 
        inputColumnName: nameof(EmailData.Text)) // 文本特征提取(分词、词袋模型)
    .Append(_mlContext.Transforms.Concatenate(
        "Features", 
        "TextFeatures")) // 合并特征列
    .Append(_mlContext.Transforms.NormalizeLpNorm(
        "Features", 
        normKind: NormalizationNormalizerKind.L2)) // L2归一化
    .Append(_mlContext.BinaryClassification.Trainers.SdcaLogisticRegression(
        labelColumnName: "Label", 
        featureColumnName: "Features")); // 二分类训练器

常见数据预处理操作

操作

用途

API 示例

文本特征提取

将文本转为数值向量

Text.FeaturizeText()

缺失值填充

处理空值(用均值 / 中位数 / 默认值)

ReplaceMissingValues()

独热编码

处理分类特征(如 “城市”→0/1 向量)

Categorical.OneHotEncoding()

特征归一化

缩放特征到统一范围(0-1 或 – 1-1)

NormalizeMinMax()

/NormalizeStandard()

特征选择

筛选重大特征,减少维度

SelectFeaturesBasedOnMutualInformation()

18.4 特征工程与数据转换

特征工程是 “从原始数据提取有效特征” 的过程,ML.NET提供CustomMapping实现自定义特征转换(如计算衍生特征),提升模型表达能力。

实战:自定义特征转换(计算房屋单价)

// 1. 定义输入/输出模型(包含衍生特征)
public class HouseDataWithDerived
{
    public float Size { get; set; }
    public int Rooms { get; set; }
    public float Price { get; set; }
}

public class HouseDataWithUnitPrice
{
    public float Size { get; set; }
    public int Rooms { get; set; }
    public float Price { get; set; }
    public float UnitPrice { get; set; } // 衍生特征:单价(Price/Size)
}

// 2. 定义自定义映射逻辑
Action<HouseDataWithDerived, HouseDataWithUnitPrice> mapping = (input, output) =>
{
    // 复制原始字段
    output.Size = input.Size;
    output.Rooms = input.Rooms;
    output.Price = input.Price;
    // 计算衍生特征(单价),避免除以0
    output.UnitPrice = input.Size > 0 ? input.Price / input.Size : 0;
};

// 3. 管道中添加自定义转换
var pipeline = _mlContext.Transforms.CustomMapping(mapping, "HouseUnitPriceMapping")
    .Append(_mlContext.Transforms.Concatenate(
        "Features", 
        nameof(HouseDataWithUnitPrice.Size), 
        nameof(HouseDataWithUnitPrice.Rooms), 
        nameof(HouseDataWithUnitPrice.UnitPrice)))
    .Append(_mlContext.Regression.Trainers.Sdca());

18.5 模型训练与评估

ML.NET按任务类型提供对应训练器(分类、回归、聚类、推荐),训练后需通过评估指标判断模型性能,选择最优模型。

常见任务类型与训练器:

任务类型

目标

常用训练器

评估指标

二分类

预测二值标签(是 / 否)

SdcaLogisticRegression

、LbfgsLogisticRegression

准确率、召回率、F1 分数、AUC-ROC

多分类

预测多值标签(如颜色:红 / 绿 / 蓝)

SdcaNonCalibrated

、OneVersusAll

多分类准确率、混淆矩阵

回归

预测连续值(如房价、温度)

Sdca

、FastTree

、LinearRegression

MAE、MSE、R² 得分

聚类

无监督分组(如客户分群)

KMeans

轮廓系数(Silhouette Coefficient)

推荐系统

预测用户偏好(如商品推荐)

MatrixFactorizationTrainer

均方根误差(RMSE)

评估模型示例(二分类):

// 训练二分类模型(垃圾邮件分类)
var pipeline = _mlContext.Transforms.Text.FeaturizeText("Features", nameof(EmailData.Text))
    .Append(_mlContext.BinaryClassification.Trainers.SdcaLogisticRegression());

var trainedModel = pipeline.Fit(trainData);
var predictions = trainedModel.Transform(testData);

// 评估模型
var metrics = _mlContext.BinaryClassification.Evaluate(
    predictions,
    labelColumnName: "Label",
    scoreColumnName: "Score");

// 输出评估结果
Console.WriteLine($"准确率(Accuracy):{metrics.Accuracy:F4}"); // 正确预测比例
Console.WriteLine($"准确率(Precision):{metrics.Precision:F4}"); // 预测为正例的正确比例
Console.WriteLine($"召回率(Recall):{metrics.Recall:F4}"); // 实际为正例的正确预测比例
Console.WriteLine($"F1分数:{metrics.F1Score:F4}"); // 准确率与召回率的调和平均
Console.WriteLine($"AUC-ROC:{metrics.AreaUnderRocCurve:F4}"); // ROC曲线下面积(越接近1越好)
© 版权声明

相关文章

暂无评论

none
暂无评论...