【发布时间】:2020-01-13 05:25:57
【问题描述】:
对于网上商店,我想创建一个模型,根据某人的愿望清单上的内容提供建议:“某人的愿望清单上有 X,我们也推荐 Y”场景。问题是,由于缺少我的数据集中没有的适当标签或完全缺乏足够的数据,培训师无法工作。这会导致不准确的数据或 float.NAN 的预测分数(所有或大多数分数最终都是这样)
我拥有所有现有的心愿单以及随后的 ProfileId 和 ItemId(都是整数)。这些被分组在 ProfileID-ItemID 组合中(代表愿望清单上的一个项目,因此拥有 3 个项目的用户将有 3 个组合)。总的来说,我可以为 16.000 个用户和 50.000 个项目使用大约 150.000 种组合。仅出现在单个愿望清单上(或根本不出现)的项目或仅在其愿望清单中包含一个项目的用户将从训练数据中排除(上述数字已被过滤)。如果我愿意,我可以添加额外的数据列,表示项目所属的类别(玩具、书籍等)、价格和其他元数据。
我没有评级,因为网上商店不使用这些评级。因此,我不能用它们来表示“标签”
public class WishlistItem
{
// these variables are either uint32 or a Single (float) based on the training algorithm.
public uint ProfileId;
public uint ItemId;
public float Label;
}
我认为我需要解决这个问题:
三者的组合或其中之一:
1) 我需要使用不同的教练。如果是这样,哪个最适合?
2) 我需要为 Label 变量插入不同的值。如果有,应该如何生成?
3) 我需要生成不同的“假”数据集来填充训练数据。如果有,应该如何生成?
对问题的解释以及解决问题的失败尝试
我尝试使用不同的训练器解析数据,以查看最适合我的数据集的方法:FieldAwareFactorizationMachine、MatrixFactorizationMachine 和 OLSTrainer时间>。我还尝试将 MatrixFactorizationMachine 用于 LossFunctionType.SquareLossOneClass,而不是插入愿望清单上 ItemIds 的 ProfileID-ItemID 组合。 (例如,存在 3 件商品的愿望清单中的 item1-item2、item2-item3、item1-item3)
这些机器基于其后续教程中的信息:
矩阵分解:https://docs.microsoft.com/en-us/dotnet/machine-learning/tutorials/movie-recommendation
矩阵分解(OneClass):https://medium.com/machinelearningadvantage/build-a-product-recommender-using-c-and-ml-net-machine-learning-ab890b802d25
OLS:https://docs.microsoft.com/en-us/dotnet/api/microsoft.ml.mklcomponentscatalog.ols?view=ml-dotnet
这是其中一个管道的示例,其他管道非常相似:
string profileEncoded = nameof(WishlistItem.ProfileId) + "Encoded";
string itemEncoded = nameof(WishlistItem.ItemId) + "Encoded";
// the Matrix Factorization pipeline
var options = new MatrixFactorizationTrainer.Options {
MatrixColumnIndexColumnName = profileEncoded,
MatrixRowIndexColumnName = itemEncoded,
LabelColumnName = nameof(WishlistItem.Label),
NumberOfIterations = 100,
ApproximationRank = 100
};
trainerEstimator = Context.Transforms.Conversion.MapValueToKey(outputColumnName: profileEncoded, inputColumnName: nameof(WishlistItem.ProfileId))
.Append(Context.Transforms.Conversion.MapValueToKey(outputColumnName: itemEncoded, inputColumnName: nameof(WishlistItem.ItemId)))
.Append(Context.BinaryClassification.Trainers.FieldAwareFactorizationMachine(new string[] { "Features" }));
为了缓解缺少标签的问题,我尝试了几种解决方法:
- 将它们留空(0f 浮点值)
- 使用 itemid、profileid 或两者组合的哈希码
- 计算包含特定 itemid 或 profileid 的项目数量,还可以操纵该数字以创建更少的极端值,以防一个项目被表示数百次。 (使用平方根或对数函数,创建
Label = Math.Log(amountoftimes);或Label = Math.Ceiling(Math.Log(amountoftimes) - 对于 FieldAware 机器,其中 Label 是布尔值而不是浮点数,上面的计算用于确定浮点结果是高于平均值还是低于所有项目的平均值
在测试时,我使用以下 2 种可能的方法进行测试,以确定可以为项目“X”创建哪些建议“Y”:
- 将 ItemID X 与所有现有项目与用户的 ProfileID 进行比较。
List<WishlistItem> predictionsForUser = profileMatrix.DistinctBy(x => x.ItemID).Select(x => new WishlistItem(userId, x.GiftId, x.Label));
IDataView transformed = trainedModel.Transform(Context.Data.LoadFromEnumerable(predictionsForUser));
CoPurchasePrediction[] predictions = Context.Data.CreateEnumerable<CoPurchasePrediction>(transformed, false).ToArray();
IEnumerable<KeyValuePair<WishlistItem, CoPurchasePrediction>> results = Enumerable.Range(0, predictions.Length).ToDictionary(x => predictionsForUser[x], x => predictions[x]).Where(x => OrderByDescending(x => x.Value.Score).Take(10);
return results.Select(x => x.Key.GiftId.ToString()).ToArray();
- 将 ItemID X 与其他人的愿望清单上的项目进行比较,其中 X 也存在。这个用于 FieldAware Factorization Trainer,它使用布尔值作为标签。
public IEnumerable<WishlistItem> CreatePredictDataForUser(string userId, IEnumerable<WishlistItem> userItems)
{
Dictionary<string, IEnumerable<WishlistItem>> giftIdGroups = profileMatrix.GroupBy(x => x.GiftId).ToDictionary(x => x.Key, x => x.Select(y => y));
Dictionary<string, IEnumerable<WishlistItem>> profileIdGroup = profileMatrix.GroupBy(x => x.ProfileId).ToDictionary(x => x.Key, x => x.Select(y => y));
profileIdGroup.Add(userId, userItems);
List<WishlistItem> results = new List<WishlistItem>();
foreach (WishlistItem wi in userItems)
{
IEnumerable<WishlistItem> giftIdGroup = giftIdGroups[wi.GiftId];
foreach(WishlistItem subwi in giftIdGroup)
{
results.AddRange(profileIdGroup[subwi.ProfileId]);
}
}
IEnumerable<WishlistItem> filtered = results.ExceptBy(userItems, x => x.GiftId);
// get duplicates
Dictionary<string, float> duplicates = filtered.GroupBy(x => x.GiftId).ToDictionary(x => x.Key, x => giftLabelValues[x.First().GiftId]);
float max = duplicates.Values.Max();
return filtered.DistinctBy(x => x.GiftId).Select(x => new WishlistItem(userId, x.GiftId, duplicates[x.GiftId] * 2 > max));
}
但是,无论插入的项目如何,测试数据要么完全或部分不可用(float.NAN),要么总是创建相同的推荐结果(我们建议项目 X 的 Y 和 Z)。
使用 testdataview (DataOperationsCatalog.TrainTestData split = Context.Data.TrainTestSplit(data, 0.2)) 评估数据时,它要么以高精度显示有希望的结果,要么在各处显示随机值,并且与我得到的结果不符;高精度仍会导致 float.NAN 或“始终相同”
网上有人指出float.NAN可能是小数据集的结果。作为补偿,我尝试创建“假”数据集;根据现有的 profileid 和 itemid 随机生成的 profile-item 组合(标签为 0f 或 false,其余为 0f+ 或 true)。 (事先检查以排除这些随机的“负”数据不是偶然的“真实”组合集)。然而,这几乎没有效果。
【问题讨论】:
-
如果你没有标签,那么我不相信任何有监督的机器学习算法会有任何用处。尝试一种无监督算法,例如使用 KMeans 进行聚类。
-
KMeans 似乎不是这样 - 如果我创建一个培训师并为其提供配置文件项目组合并创建 (amountofitems / 100) 集群,那么它仍然总是返回完全相同的数据,无论物品。有没有其他无监督学习算法可以解决这个问题?
标签: c# machine-learning ml.net