编辑
2025-11-27
C#
00

目录

概述
环境准备
数据模型定义
完整实现代码
使用示例
1. R² Score (决定系数)
2. RMSE (Root Mean Squared Error, 均方根误差)
3. MAE (Mean Absolute Error, 平均绝对误差)
4. 综述
总结

概述

本文将介绍如何使用ML.NET框架中的随机梯度下降算法来预测房屋租金。我们将使用印度房屋租赁数据集,该数据集包含了约4700条房屋租赁信息,包括卧室数量、面积、位置等特征。

环境准备

安装 .NET 6.0 或更高版本

**创建控制台项目并引用 **ML.NET

text
dotnet add package Microsoft.ML

image.png

数据模型定义

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.ML.Data; namespace AppHousingRent { public class HouseRentData { [LoadColumn(0)] public float CRIM { get; set; } // 城镇人均犯罪率 [LoadColumn(1)] public float ZN { get; set; } // 占地面积超过25000平方呎的住宅用地比例 [LoadColumn(2)] public float INDUS { get; set; } // 城镇非零售商业用地比例 [LoadColumn(3)] public float CHAS { get; set; } // 是否临近查尔斯河 [LoadColumn(4)] public float NOX { get; set; } // 一氧化氮浓度 [LoadColumn(5)] public float RM { get; set; } // 住宅平均房间数 [LoadColumn(6)] public float AGE { get; set; } // 1940年之前建成的自用房屋比例 [LoadColumn(7)] public float DIS { get; set; } // 到波士顿五个就业中心的加权距离 [LoadColumn(8)] public float RAD { get; set; } // 到径向公路的可达性指数 [LoadColumn(9)] public float TAX { get; set; } // 每10000美元的全值财产税率 [LoadColumn(10)] public float PTRATIO { get; set; } // 城镇师生比例 [LoadColumn(11)] public float B { get; set; } // 1000(Bk - 0.63)^2,其中Bk为城镇中黑人的比例 [LoadColumn(12)] public float LSTAT { get; set; } // 人口中地位较低者的比例 [LoadColumn(13)] [ColumnName("Label")] // 将MEDV标记为Label列 public float MEDV { get; set; } // 自住房的平均房价,以千美元计 } public class HouseRentPrediction { [ColumnName("Score")] public float PredictedPrice { get; set; } } }

完整实现代码

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.ML; namespace AppHousingRent { public class HouseRentPredictor { private static readonly string _dataPath = "housing.csv"; public static void Train() { try { // 创建ML.NET上下文 var mlContext = new MLContext(seed: 0); // 加载数据 Console.WriteLine($"正在加载数据..."); IDataView dataView = mlContext.Data.LoadFromTextFile<HouseRentData>( path: _dataPath, hasHeader: false, // 数据集中没有标题行 separatorChar: ' ' // 数据是用空格分隔的 ); // 分割数据为训练集和测试集 var trainTestData = mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2, seed: 0); // 创建数据处理管道 var pipeline = mlContext.Transforms.Concatenate("Features", new[] { "CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B", "LSTAT" }) .Append(mlContext.Transforms.NormalizeMinMax("Features")) // 添加特征标准化 .Append(mlContext.Regression.Trainers.Sdca( labelColumnName: "Label", // 使用Label作为标签列名 maximumNumberOfIterations: 100)); // 训练模型 Console.WriteLine("开始训练模型..."); var model = pipeline.Fit(trainTestData.TrainSet); Console.WriteLine("模型训练完成。"); // 对测试集进行预测 var predictions = model.Transform(trainTestData.TestSet); // 评估模型 var metrics = mlContext.Regression.Evaluate( data: predictions, labelColumnName: "Label", // 使用Label作为标签列名 scoreColumnName: "Score"); // 输出评估指标 Console.WriteLine($"\n模型评估指标:"); Console.WriteLine($"R² Score: {metrics.RSquared:0.###}"); Console.WriteLine($"均方根误差: {metrics.RootMeanSquaredError:0.###}"); Console.WriteLine($"平均绝对误差: {metrics.MeanAbsoluteError:0.###}"); // 保存模型 mlContext.Model.Save(model, dataView.Schema, "BostonHouseModel.zip"); Console.WriteLine("\n模型已保存到 BostonHouseModel.zip"); } catch (Exception ex) { Console.WriteLine($"\n发生错误: {ex.Message}"); Console.WriteLine($"堆栈跟踪: {ex.StackTrace}"); } } // 用于预测的方法 public static float PredictPrice(HouseRentData inputData) { var mlContext = new MLContext(); DataViewSchema modelSchema; var model = mlContext.Model.Load("BostonHouseModel.zip", out modelSchema); var predEngine = mlContext.Model.CreatePredictionEngine<HouseRentData, HouseRentPrediction>(model); var prediction = predEngine.Predict(inputData); return prediction.PredictedPrice; } } }

使用示例

C#
public static void Main(string[] args) { // 训练模型 HouseRentPredictor.Train(); // 创建示例数据 var sampleData = new HouseRentData() { CRIM = 0.00632f, ZN = 18f, INDUS = 2.31f, CHAS = 0f, NOX = 0.538f, RM = 6.575f, AGE = 65.2f, DIS = 4.09f, RAD = 1f, TAX = 296f, PTRATIO = 15.3f, B = 396.9f, LSTAT = 4.98f, MEDV = 24.00f }; // 进行预测 var prediction = HouseRentPredictor.PredictPrice(sampleData); Console.WriteLine($"\n示例预测结果:"); Console.WriteLine($"预测房价: ${prediction * 1000:N0}"); }

image.png


1. R² Score (决定系数)

  • 定义:决定系数(R²)衡量的是模型预测值与真实值之间的线性相关程度。它的取值范围通常在 0~1(如果模型特别差,也可能出现负值)。
  • 解释
    • R² 越接近 1,说明模型可以解释越多的目标变量(标签)变化,也就是说,模型拟合效果越好。
    • 若 R² = 1,表示模型完美拟合了数据(在真实数据中通常不可能出现)。
    • 若 R² = 0,表示模型与一个单纯的“用平均值作为预测”的估计没多大差别。
    • 若 R² < 0,说明该模型甚至比简单的“用平均值来预测”还要糟糕。

2. RMSE (Root Mean Squared Error, 均方根误差)

  • 定义:RMSE 是均方误差(Mean Squared Error, MSE)再开平方后得到的值。

image.png

其中 $(y_i)$ 是实际值,$(\hat{y}_i)$ 是预测值。
  • 解释
    • RMSE 表征的是预测误差的标准差,数值越小越好。
    • RMSE 同时惩罚了较大的误差(因为在平方时,会放大大的差值),因此对离群值比较敏感。
    • 如果想要让模型减少大误差事件的发生,RMSE 就是一个很好的参考指标。

3. MAE (Mean Absolute Error, 平均绝对误差)

  • 定义:MAE 以绝对值的形式度量预测值与实际标签之间的平均误差。

image.png

  • 解释
    • MAE 比 RMSE 更直观,因为它直接体现了“预测值与实际差了多少”。
    • MAE 越小,说明平均意义上,预测结果更贴近真实值。
    • 与 RMSE 相比,MAE 对少量大值误差的敏感度没有那么高,如果你的场景中关心的是整体误差,而不希望少数“极端值”对指标造成过度影响,可以关注 MAE。

4. 综述

  • R² Score 和(RMSE / MAE)经常结合起来看。R² 表示模型的整体拟合优度,RMSE / MAE 则直接反映误差大小。
  • 在很多场景,模型训练完成后,“R² Score 越高、RMSE/MAE 越低”就是我们希望看到的。实际上,仍要结合业务需求来判断模型的表现,尤其要结合真实的价格区间或误差容忍度——如果均值水平在几十万(单位)时,能接受的 MAE 可能在几千到几万之间不等。

因此,日志信息里输出的:

  • R² Score:反映模型与真实数据拟合程度,越高说明模型“解释力”越好。
  • 均方根误差 (RMSE):预测与真实值差异的平方后取平均,再开平方而来,数值越小越好。
  • 平均绝对误差 (MAE):直接计算每个样本误差的绝对值再平均,也越小越好。

总结

本文展示了如何使用ML.NET框架中的SGD算法来预测房屋租金。通过合理的特征工程和模型训练,我们可以构建一个实用的房屋租金预测系统。该实现考虑了实际应用中的各种因素,包括分类特征处理、模型评估和预测接口设计。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!