ML.NET商品推荐使用大ID时MatrixColumnIndex U4键报错如何解决
问题根因
- ML.NET 内置的
MatrixFactorizationTrainer存在硬编码限制:作为矩阵行列索引的键列仅支持 UInt32(也就是异常信息里的U4)类型,不支持 UInt64 类型,哪怕你配置了超大的KeyCount也无法绕过这个限制。 - 你的代码还存在隐藏问题:当前使用的CSV数据集只有ProductID、CoPurchaseProductID两列,没有Label列,你在TextLoader里配置Label读取第0列(也就是ProductID列),数据类型完全不匹配,后续也会触发报错。
修复方案
核心逻辑是先把所有大数值的long型原始ID,映射为连续的UInt32类型ID,再送入训练,预测时做反向映射还原原始ID即可。
完整可运行修改后代码
using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Trainers.Recommender; public class ProductEntry { // 映射后的UInt32类型ID [KeyType(count: 100000)] // 这里填你实际去重后的ID总数量即可,不要设远大于实际值的数 public uint ProductID { get; set; } [KeyType(count: 100000)] public uint CoPurchaseProductID { get; set; } // 统一设为1,OneClass推荐任务正样本Label固定为1 public float Label { get; set; } = 1; } // 原始数据读取类,用来读CSV里的大ID public class RawProductEntry { public long ProductID { get; set; } public long CoPurchaseProductID { get; set; } } class Program { static void Main(string[] args) { string path = Environment.CurrentDirectory + @"\datafile.txt"; if (!File.Exists(path)) { string createText = "ProductID,CoPurchaseProductID" + Environment.NewLine + "55006911127001,55006910976001" + Environment.NewLine + "55006910976001,55006911127001" + Environment.NewLine + "55006911328001,55006909963001" + Environment.NewLine + "55006907339001,55006909963001" + Environment.NewLine; File.WriteAllText(path, createText); } MLContext mlContext = new MLContext(); // 第一步:先读取原始的long型ID数据 var rawData = mlContext.Data.LoadFromTextFile<RawProductEntry>(path, hasHeader: true, separatorChar: ','); var rawEntries = mlContext.Data.CreateEnumerable<RawProductEntry>(rawData, reuseRowObject: false).ToList(); // 第二步:生成ID映射字典,把所有原始long ID映射为连续uint var allIds = rawEntries.SelectMany(x => new[] { x.ProductID, x.CoPurchaseProductID }).Distinct().ToList(); var idMap = allIds.Select((id, idx) => (id, idx)).ToDictionary(x => x.id, x => (uint)x.idx); int idCount = allIds.Count; // 第三步:构造训练用的映射后数据集 var trainEntries = rawEntries.Select(x => new ProductEntry { ProductID = idMap[x.ProductID], CoPurchaseProductID = idMap[x.CoPurchaseProductID], Label = 1 }).ToList(); var trainData = mlContext.Data.LoadFromEnumerable(trainEntries); // 第四步:配置训练参数,KeyCount和实际ID数量对齐 MatrixFactorizationTrainer.Options options = new MatrixFactorizationTrainer.Options(); options.MatrixColumnIndexColumnName = nameof(ProductEntry.ProductID); options.MatrixRowIndexColumnName = nameof(ProductEntry.CoPurchaseProductID); options.LabelColumnName = nameof(ProductEntry.Label); options.LossFunction = MatrixFactorizationTrainer.LossFunctionType.SquareLossOneClass; options.Alpha = 0.01; options.Lambda = 0.025; var est = mlContext.Recommendation().Trainers.MatrixFactorization(options); ITransformer model = est.Fit(trainData); // 后续预测的时候,把输入的原始long ID先通过idMap转成uint,预测后如果需要可以再反向映射回原始ID } }
注意事项
- 如果你的ID总量超过UInt32的最大上限(4294967295),那你需要先做商品的分层聚合或者过滤低热度商品,压缩ID总量到UInt32范围内,否则无法使用ML.NET原生的矩阵分解训练器。
- 映射字典可以序列化保存下来,后续线上预测的时候加载使用即可。
内容的提问来源于stack exchange,提问作者Vicente Moreno
相关产品推荐
相关产品推荐

