Press "Enter" to skip to content

回归、个性化和Kaggle综合征

Photo by Artem Beliaikin on Unsplash

最近,我参与了一个使用Kaggle黑色星期五预测数据集的案例研究,该数据集创建于六年前,已经被下载超过32,000次。虽然在Kaggle上有100多个公开可用的笔记本针对该数据集,而且其他地方可能还有更多,但我发现这些解决方案大多实施不当。

简单指出别人工作中的缺陷是没有帮助的。然而,当很多人犯同样的错误时,值得调查其中的潜在模式。在本文中,我将讨论现有解决方案的常见问题,解释为何我不再是Kaggle的粉丝,提出更好的解决方案,并概述个性化预测方法。

议程

· EDA· 目标被忽视· Kaggle综合征· 有效的回归· 个性化· 结束语· 参考资料

EDA

尽管Kaggle黑色星期五预测数据集很受欢迎,但其目的并不清楚,也没有数据字典来详细解释数据。在我们进行进一步分析之前,我们需要了解数据集的目标、数据是如何准备的以及为什么以特定的方式设计。这些信息对于特征工程、模型选择和评估非常重要。在实际的机器学习项目中,这种初步分析也很重要,因为最好的机器学习解决方案只能建立在对数据的深入理解基础上。

让我们跳过EDA。示例数据如下所示:

回归、个性化和Kaggle综合征 四海 第2张

数据集共有537577行和12列,分为用户资料功能组和产品功能组,如下所述:

用户特征组:

  • 用户ID:用户的唯一ID。数据集中总共有5891个用户。
  • 性别:以M/F格式表示进行交易的人的性别。
  • 年龄:表示进行交易的人的年龄组别。
  • 职业:显示用户的职业,用数字0到20标记。
  • 城市类别:用户所在的城市类别。城市分为三个不同的类别:A、B和C。
  • 在当前城市居住年限:表示用户在该城市居住的时间。
  • 婚姻状况:如果用户未婚,则为0;否则为1。

产品特征组:

  • 产品ID:产品的唯一ID。数据集中总共有3623个产品。
  • 产品类别1至3:产品的类别。所有三个都以数字标记。

目标:

  • 购买:购买金额。

如数据集的概要所示,其中既有整数特征,又有分类特征。31.6%的Product_Category_2存在缺失值,而Product_Category_3的缺失值率为69.7%。

回归、个性化和Kaggle综合征 四海 第3张

数据中有一些有趣的细节。例如,Product_Category_1始终大于Product_Category_2,而Product_Category_2始终大于Product_Category_3。这意味着这三个特征之间存在逻辑关联,但我们无法找出原因,因为没有解释。也许这三个类别是时间限制的。例如,运输时间vs.上架时间vs.下架时间等。然而,这只是一个猜测。我们无法充分利用这个模式。

回归、个性化和Kaggle综合征 四海 第4张

由于数据集的目标没有被描述,让我们戴上侦探帽子,问问自己:“数据集设计者把他们真正的意图藏在哪里?” 答案在测试集中。毕竟,测试集是数据集设计者评估模型性能的方式。所以让我们比较训练集和测试集,看看我们能发现什么。

回归、个性化和Kaggle综合征 四海 第5张

事实证明,通过比较训练集和测试集,可以清楚地看到测试集包含了训练集中的所有用户和产品,但是测试集中的用户-产品对与训练集没有共享。这意味着预测实际上是一个个性化推荐系统,而不仅仅是普通的回归。换句话说,它要求分析师基于用户的先前购买信息预测每个用户对其他产品的购买。

被忽视的目标

回归和个性化之间的区别在于,回归模型学习全局模式,而个性化模型学习用户和产品之间的交互。当数据规模较小时,普通的回归模型仍然可以学习到这些交互。真正的问题在于当数据规模变大时。

为了让模型学习个性化的交互,用户和产品特征都必须被视为分类特征。在大多数情况下,它们都具有很多级别。在我们的黑色星期五预测案例中,有5891个用户和3623个产品。虽然这已经是一个非常小的数据集,但对于普通的回归模型来说,它已经超出了舒适区。

回归模型处理分类特征的标准方法是使用独热编码,即将每个分类级别作为一个新的列。然而,这种技术在个性化任务中不起作用,因为5891 x 3623将产生一个非常大且非常稀疏的二维数组。数组中的单元格数量比数据集中的行数还要大。结果数组非常稀疏,其中大多数单元格将为空。这使得计算非常具有挑战性,更重要的是,由于高维度的问题,一般的回归模型无法从数组中学到任何东西。

为了避免高纬度维度问题,所有开放的分析要么删除产品列,要么删除用户列,要么两者都删除。删除产品列等价于说我想预测用户在特定产品类别中的行为,而删除用户列等价于说我想预测特定产品在共享某些人口统计特征的用户组中的销售情况。删除用户和产品特征意味着我想预测某个用户群体在某个产品类别中的购买情况。你可以看到它们都不是属于单一用户对单一产品的预测。如果没有对目标的明确理解,所有的工作都会失败。

Kaggle综合症

我注意到所有开放的分析都从开始就失败了:它们没有足够努力去找出数据集的目的。他们将EDA限制在训练数据上,没有将分析扩展到测试集,并将任务视为普通的回归问题,而没有完全理解任务。我发现这是Kaggle中的一个常见问题。

我在我的机器学习旅程开始时是一个Kaggle的狂热爱好者,但我的兴趣很快就消失了。我偶尔仍会浏览Kaggle以获得技术上的想法,但我不再是它的大粉丝了。一个原因是我不能在排行榜上排名靠前。另一个原因是我发现Kaggle比赛的风格与现实世界的机器学习实践不兼容。

每个Kaggle竞争者都过于专注于从模型中挤取出最后一滴性能。在现实世界中,首要任务完全相反:提供商业价值是更重要的首要任务。在许多情况下,模型的性能并不是一个很重要的要求。如果你的模型在业务中非常适用,没有人会质疑为什么准确度是0.95而不是0.96,除非性能对项目至关重要。对模型性能非常敏感的项目是罕见的。

竞赛中的另一个问题是缺乏互动。一旦给定了数据集,那就是全部了。参与者几乎没有机会提问或调整需求。就像在黑色星期五的数据集中,我们可以看到一些有趣的模式;然而,我们无法看到为什么,更不用说利用这些模式了。在现实项目中,提问是防止项目失败的最简单策略。

更重要的是,Kaggle竞赛使我们逐渐与商业背景“脱节”。

我知道一些雇主雇佣Kaggle竞赛的获胜者。这是参与者的一个动力之一。然而,我担心Kaggle风格的机器学习在实际业务中可能会有害。其中一个例子是Zillow。Zillow赞助了一个Kaggle房价预测竞赛,并向获胜者提供工作机会。当房地产市场持续上涨时,Zillow从机器学习中获益颇丰。当房地产市场转向时,Zillow几乎崩溃了。

出了什么问题?可以公正地说,问题在于Zillow的数据科学家们对业务的了解不够。否则,他们应该会花时间研究市场的转折点和发生某些事件时的风险水平等主题。他们不会像我们看到的那样对市场的下滑毫无准备。

我鼓励那些在Kaggle上花费日夜进行机器学习学习的初学者,抽出一些时间思考业务场景,并培养将机器学习应用于现实世界中的敏锐视觉。这是一项任何人都无法教授的软技能。但是,只需多问一些问题,我们就能够做到。

有效的回归

让我们看看如何实现一个常规回归模型,以更接近我们的目标。正如我们讨论的,以一般回归方式进行个性化的一个主要问题是高维度。传统的独热编码不起作用。标签编码看起来不好,因为它暗示了特征的标签与目标之间存在序列关系。我们也不喜欢分箱或散列。让我们尝试一种不同的编码方式:目标编码。

目标编码不会用整数替换类别级别,而是用平均目标值替换它们。它很好地解决了高维度问题,并且可以将信息带入代表值中。

在我们开始训练模型之前,我们将所有的11个特征都转化为分类变量,并对它们进行目标编码。我们还将目标通过sqrt()函数进行标准化,然后将目标缩放到[0.0, 10.0]的范围内,因为我尝试过的一些算法只接受在该范围内的目标值。

代码如下:

# 划分训练集和测试集X_train, X_test, y_train, y_test = train_test_split(df_blackfriday_data.drop('Purchase', axis=1), df_blackfriday_data['Purchase'], random_state=random_seed, test_size=0.25)print(X_train.shape, y_train.shape)print(X_test.shape, y_test.shape)# 目标编码from category_encoders import TargetEncoderencoder = TargetEncoder()enc = encoder.fit(X=X_train, y=y_train)df_train_X = enc.transform(X_train)df_test_X = enc.transform(X_test)# 模型训练xgb_reg = XGBRegressor( seed=random_seed)xgb_reg.fit(df_train_X, y_train)xgb_y_pred = xgb_reg.predict(df_test_X)print('Scaled RMSE:', sqrt(mean_squared_error(y_test, xgb_y_pred)))print("RMSE on the original test data is ",sqrt(mean_squared_error((y_test*15 + 3.464)*(y_test*15 + 3.464), (xgb_y_pred*15 +
3.464)*(xgb_y_pred*15 + 3.464))))

以上代码段产生了两个RMSE,其中缩放的RMSE是针对转换后的目标的,另一个是针对原始目标的。结果如下所示:

缩放的RMSE: 0.8845836109788747

在原始测试数据上的RMSE为2522.1269855375003

它的表现超过了所有开放分析结果。从特征重要性图中,我们可以清楚地了解原因:

回归、个性化和Kaggle综合征 四海 第6张

最重要的特征是用户ID和产品ID特征,这些特征大多数分析都忽视了。目标编码并不是一个秘密武器,但是导致正确决策的见解却起到了关键作用。让我们看看如何进一步提高。

个性化

上述的回归解决方案使用了target_encoding技巧来处理高基数维度。target_encoding可能不是理想的解决方案,因为它模糊了具有相似目标平均值的用户和产品之间的差异。还有另一类技术直接处理庞大的用户-产品数组,称为推荐系统。我们也可以使用推荐技术来提供个性化预测。在典型的推荐系统中,输入数据结构是一个巨大的二维数组,用户作为一个维度,项目作为另一个维度,购买作为单元格值。

推荐和个性化分析的最经典方法是协同过滤。换句话说,它根据用户到用户或项目到项目的相似性评分来预测用户的偏好。如果你在Google上搜索推荐或协同过滤,你经常会看到下面这样的图表,因为这些想法是在一次电影推荐比赛中发明的。在那种情况下,概念是用户、电影和评分,与我们的情况相当于用户、产品和购买。

回归、个性化和Kaggle综合征 四海 第7张

此后出现了许多其他技术。其中之一是矩阵分解,即将巨大的矩阵分解成几个低秩矩阵的乘积。另一个重要的解决方案是深度学习。

让我们使用FastAI尝试一个基准DNN模型。请注意,我们的模型只使用用户和产品特征进行训练,忽略了所有其他特征。

from fastai.collab import CollabDataLoaders, collab_learner# 构造用户-项目-评级数据帧ratings_dict = {'item': list(trainset.Product_ID),                'user': list(trainset.User_ID),                'rating': l

ist((trainset.Purchase.pow(1/2)- 3.464)/15)}ratings = pd.DataFrame(ratings_dict)ratings_test_dict = {'item': list(testset.Product_ID),                'user': list(testset.User_ID),                'rating': list((testset.Purchase.pow(1/2) -3
.464)/15)}ratings_test = pd.DataFrame(ratings_test_dict)# 模型训练learn = collab_learner(dls, n_factors=160, use_nn=True, y_range=(0, 10))import warningswith warnings.catch_warnings():    warnings.simplefilter("ignore")    learn.fit_one_cycle(5, 5e-3, wd=0.1)# 评估dl = learn.dls.test_dl(ratings_test, with_labels=True)with warnings.catch_warnings():    warnings.simplefilter("ignore")    aaa = learn.get_preds(dl=dl)    aaates
tset['y'] = (testset['Purchase'].pow(1/2) -3.464)/15testset['y_pred'] = [x.tolist()[0] for x in aaa[0]]# testset['y_pred'] = testset['y_pred']*testset['y_pred']*225from sklearn.metrics import mean_squared_errorfrom math import sqrtprint('Scaled RMSE', sqrt(mean_squared_error(testset['y'], testset['y_pred']
)))testset['Purchase_pred'] = (testset.y_pred*15 +3.464) * (testset.y_pred*15 +3.464)print('RMSE on the original test set', sqrt(mean_squared_error(testset['Purchase'], testset['Purchase_pred'])))

结果如下所示:

Scaled RMSE 0.8624311160502426

RMSE on the original test set 2460.1524061340824

这个结果比基于target_encoded user_id和product_id的之前的XGBoost模型要好得多。训练的DNN模型非常简单:

回归、个性化和Kaggle综合征 四海 第8张

该模型使用嵌入来转换用户ID和产品ID,并仅使用一个线性层。你可能会想知道为什么我们必须放弃所有的用户属性和产品信息。我们能否将所有这些特征包含在预测中?

这是一个合理的问题。在实际业务场景中,典型推荐系统的一个缺点是我们无法控制学到的模式。它更像是一个黑箱,没有透明度,也没有处理结果的手柄。它无法处理与其他记录没有共同性的新用户或新产品。这被称为冷启动问题。在协同过滤或矩阵分解结构中,这个问题需要更多的努力来解决,因为这些技术无法处理多变量数据。而使用DNN模型可以轻松解决这个问题。我们只需要将其他特征的嵌入添加为输入,修改模型参数即可。

离别的话

在我们的实验中,我们发现在看似贫瘠的用户ID和产品ID特征中蕴藏着丰富的信息。我们可以仅基于用户自己的历史行为而非其他任何因素来挖掘非常有用的规律。我们没有将我们的努力扩展到超参数调整或任何性能增强技术。一旦我们走上了正确的轨道,取得良好的结果就同样简单。

在这个案例研究中,最重要的事情是理解上下文并选择合适的技术。商业眼光在这个分析中起到了重要的作用。事实上,商业问题是大多数机器学习解决方案的灵感来源,比如开发用于解决电影推荐问题的协同过滤系统。同样,商业理解也是我们学习机器学习的一个很好的加速器。

参考资料

黑色星期五

Kaggle是全球最大的数据科学社区,拥有强大的工具和资源,帮助您实现数据分析…

www.kaggle.com

Leave a Reply

Your email address will not be published. Required fields are marked *