我在探索ML.Net,打算预测员工离职情况。我有一个包含数值和字符串值的数据集可用。
这完全是探索性质的尝试,目的是为了更好地了解ML.Net。所以我的方法是,逐步探索各种选项,尽可能深入理解每一个步骤。
- 加载数据
- 准备数据集并对字符串特征进行分类转换
- 显示应用转换后的数据集
- 然后将数据集拆分为训练和测试数据集
- 使用分类算法训练模型
- 针对测试数据集进行评估
- 输出模型的特征权重
- 用它做一些有趣的事情
模型如下,基于IBM的开源员工流失数据集。 https://www.kaggle.com/pavansubhasht/ibm-hr-analytics-attrition-dataset
模型如下:
public class Employee { [LoadColumn(0)] public int Age { get; set; } [LoadColumn(1)] //[ColumnName("Label")] public string Attrition { get; set; } [LoadColumn(2)] public string BusinessTravel { get; set; } [LoadColumn(3)] public int DailyRate { get; set; } [LoadColumn(4)] public string Department { get; set; } [LoadColumn(5)] public int DistanceFromHome { get; set; } [LoadColumn(6)] public int Education { get; set; } [LoadColumn(7)] public string EducationField { get; set; } [LoadColumn(8)] public int EmployeeCount { get; set; } [LoadColumn(9)] public int EmployeeNumber { get; set; } [LoadColumn(10)] public int EnvironmentSatisfaction { get; set; } [LoadColumn(11)] public string Gender { get; set; } [LoadColumn(12)] public int HourlyRate { get; set; } [LoadColumn(13)] public int JobInvolvement { get; set; } [LoadColumn(14)] public int JobLevel { get; set; } [LoadColumn(15)] public string JobRole { get; set; } [LoadColumn(16)] public int JobSatisfaction { get; set; } [LoadColumn(17)] public string MaritalStatus { get; set; } [LoadColumn(18)] public int MonthlyIncome { get; set; } [LoadColumn(19)] public int MonthlyRate { get; set; } [LoadColumn(20)] public int NumCompaniesWorked { get; set; } [LoadColumn(21)] public string Over18 { get; set; } [LoadColumn(22)] public string OverTime { get; set; } [LoadColumn(23)] public int PercentSalaryHike { get; set; } [LoadColumn(24)] public int PerformanceRating{ get; set; } [LoadColumn(25)] public int RelationshipSatisfaction{ get; set; } [LoadColumn(26)] public int StandardHours{ get; set; } [LoadColumn(27)] public int StockOptionLevel{ get; set; } [LoadColumn(28)] public int TotalWorkingYears{ get; set; } [LoadColumn(29)] public int TrainingTimesLastYear{ get; set; } [LoadColumn(30)] public int WorkLifeBalance{ get; set; } [LoadColumn(31)] public int YearsAtCompany{ get; set; } [LoadColumn(32)] public int YearsInCurrentRole{ get; set; } [LoadColumn(33)] public int YearsSinceLastPromotion{ get; set; } [LoadColumn(34)] public int YearsWithCurrManager { get; set; } }
然后对字符串属性进行转换(如这里所解释的 https://learn.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/prepare-data-ml-net#work-with-categorical-data)
var categoricalEstimator = mlContext.Transforms.Categorical.OneHotEncoding("Attrition") .Append(mlContext.Transforms.Categorical.OneHotEncoding("BusinessTravel")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("EducationField")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("Gender")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("JobRole")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("MaritalStatus")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("Over18")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("OverTime")); ITransformer categoricalTransformer = categoricalEstimator.Fit(dataView); IDataView transformedData = categoricalTransformer.Transform(dataView);
现在我想检查发生了什么变化(https://learn.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/inspect-intermediate-data-ml-net#convert-idataview-to-ienumerable)。我现在面临的挑战是,在对字符串属性应用转换后,模式已经改变,现在包含了预期的向量。
所以发生了以下情况。Employee模型的模式不再与transformedData对象的模式匹配,并试图将Vector属性适配到String属性中,从而抛出以下错误 “无法将类型为’Vector’的IDataView列’Attrition’绑定到类型为’System.String’的字段或属性’Attrition’。”
IEnumerable<Employee> employeeDataEnumerable = mlContext.Data.CreateEnumerable<Employee>(transformedData, reuseRowObject: true);
CreateEnumerable还有一个SchemaDefinition参数,所以我的第一反应是提取transformedData的模式,并将其提供给CreateEnumerable。然而它期望一个Microsoft.ML.DataViewSchema,而转换产生的模式是Microsoft.ML.Data.SchemaDefinition。所以这也没用。
希望有人能给我一些建议。我应该做些什么不同的吗?
完整的控制器操作:
public ActionResult Turnover(){ MLContext mlContext = new MLContext(); var _appPath = AppDomain.CurrentDomain.BaseDirectory; var _dataPath = Path.Combine(_appPath, "Datasets", "WA_Fn-UseC_-HR-Employee-Attrition.csv"); // 从文件加载数据 IDataView dataView = mlContext.Data.LoadFromTextFile<Employee>(_dataPath, hasHeader: true); // 0. 获取输入特征的列名。 string[] featureColumnNames = dataView.Schema .Select(column => column.Name) .Where(columnName => columnName != "Label") .ToArray(); // 定义分类转换估计器 var categoricalEstimator = mlContext.Transforms.Categorical.OneHotEncoding("Attrition") .Append(mlContext.Transforms.Categorical.OneHotEncoding("BusinessTravel")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("EducationField")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("Gender")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("JobRole")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("MaritalStatus")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("Over18")) .Append(mlContext.Transforms.Categorical.OneHotEncoding("OverTime")); ITransformer categoricalTransformer = categoricalEstimator.Fit(dataView); IDataView transformedData = categoricalTransformer.Transform(dataView); // 检查(失败,因为Employee(35列)无法映射到新模式(52列)) IEnumerable<Employee> employeeDataEnumerable = mlContext.Data.CreateEnumerable<Employee>(transformedData, reuseRowObject: true, schemaDefinition : transformedData.Schema); // 将转换后的数据集拆分为训练和测试数据集 DataOperationsCatalog.TrainTestData dataSplit = mlContext.Data.TrainTestSplit(transformedData, testFraction: 0.2); IDataView trainData = dataSplit.TrainSet; IDataView testData = dataSplit.TestSet; return View();}
回答:
我最近遇到了这个问题,作为一个快速解决方案,我简单地创建了一个与转换后数据模式匹配的新类。例如,您可以创建一个EmoloyeeTransformed类,包含正确的属性(即向量而不是字符串),然后这样使用它:
CreateEnumerable<EmployeeTransformed>
如果您要创建各种转换后的模式,这不是最佳选择,但它确实有效。
希望这对您有帮助。