转换后如何处理架构和模型之间的不匹配?

时间:2019-06-17 16:23:00

标签: c# machine-learning .net-core ml.net

探索ML.Net,我想预测员工流动率。我有一个可用的数据集,在数字和字符串值之间混合使用。

这仅仅是我在尝试了解ML.net时的探索。因此,我的方法是,仅一步一步地探索各种选择,因此我真的会尽可能地理解每一步。

  1. 加载数据
  2. 准备数据集并对字符串特征进行分类转换
  3. 应用转换后显示数据集
  4. 然后将数据集分为训练和测试数据集
  5. 使用分类算法训练模型
  6. 根据测试数据集进行评估
  7. 输出模型的特征权重
  8. 用它做一些很酷的事情

该模型如下,并基于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://docs.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://docs.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中提取Schema,并将其提供给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");

    // Load data from file
    IDataView dataView = mlContext.Data.LoadFromTextFile<Employee>(_dataPath, hasHeader: true);

    // 0. Get the column name of input features.
    string[] featureColumnNames =
        dataView.Schema
            .Select(column => column.Name)
            .Where(columnName => columnName != "Label")
            .ToArray();

    // Define categorical transform estimator
    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);

    // Inspect (fails because Employee (35 cols) cannot be mapped to new schema (52 cols)
    IEnumerable<Employee> employeeDataEnumerable =
        mlContext.Data.CreateEnumerable<Employee>(transformedData, reuseRowObject: true, schemaDefinition : transformedData.Schema);

    // split the transformed dataset into training and a testing datasets
    DataOperationsCatalog.TrainTestData dataSplit = mlContext.Data.TrainTestSplit(transformedData, testFraction: 0.2);
    IDataView trainData = dataSplit.TrainSet;
    IDataView testData = dataSplit.TestSet;

    return View();
}

2 个答案:

答案 0 :(得分:1)

我最近遇到了这个问题,作为一种快速的解决方法,我只是创建了一个与转换后的数据模式匹配的新类。例如,您可以使用正确的属性(即使用矢量而不是字符串)创建EmoloyeeTransformed类,并按如下方式使用它:

CreateEnumerable<EmployeeTransformed>

如果要创建各种转换后的架构,这不是最佳选择,但是它可以工作。

希望有帮助。

答案 1 :(得分:0)

出于调试目的,您还可以调用transformedData.Preview()并查看数据和生成的Schema。