推荐算法项目实践笔记(DataWhale组队学习)(持续更新中)

问题分析

本次比赛是一个经典点击率预估(CTR)的数据挖掘赛,需要选手通过训练集数据构建模型,然后对验证集数据进行预测,预测结果进行提交。

本题的任务是构建一种模型,根据用户的测试数据来预测这个用户是否点击广告。这种类型的任务是典型的二分类问题,模型的预测输出为 0 或 1 (点击:1,未点击:0)

用户的数据包括不同种类的信息,需要对数据进行相应的分析,来对不同信息进行相应的处理。

在进行数据探索之前,我们先分析一下用户的可能类型:

1、根据用户点击量数据大小,我们将用户分为两部分:A、数据充足型;B、数据稀疏型

对于A类型的用户,我们在构建模型后,能够相对较好的拟合和预测。对于B类型的用户,稀疏数据不能很好的体现用户的点击量分布,因此模型就很难预测。

对于B类用户,点击的数据不能支撑起模型的预测,就需要根据用户的其他信息来预测,就比如年龄段、、性别等数据。

2、数据集中,很可能有很多脏数据,信息不全或者信息虚假等,都需要在训练前对数据进行预处理。


数据探索

我们从官网下载数据集后,根据赛事官方手册,对数据信息进行相应的分析:

1、目标域信息

Label:用户是否点击,是我们要训练和预测的二分类对象。

ID信息:user_id,用户ID,同一个用户ID包含多行数据,分别表示同一个用户的多次行为数据,构建样本特征时,需要根据相同用户来进行提取mean、nunique等特征,用来反应同一个用户的特征信息;

              log_id,样本ID,是采样时的标号,无实际意义。

用户基本信息:age、gender、residence、city、city_rank、series_dev、series_group、emui_dev、device_name、device_size,参与模型训练,对结果的影响程度不同,后续可以考虑对这些数据进行组合和加权,来提取更高级的特征。

广告相关信息:task_id、adv_id、creat_type_cd、adv_prim_id、inter_type_cd、slot_id、site_id、spread_app_id、Tags、app_second_class、app_score、ad_click_list_001、ad_click_list_002、ad_click_list_003、ad_close_list_001、ad_close_list_002、ad_close_list_003,参与模型训练,预计广告部分信息对结果的预测影响较大,后续同样可以考虑对信息的重要程度进行评估,从而提取更高级的特征。

行为状态信息、时间信息:net_type(行为发生的网络状态)、pt_d(时间戳)。

本次比赛官方禁止使用穿越特征

测试集数据不得用于全局特征的统计计算;

测试数据集样本中不得使用当前时刻“之后”的数据构造特征

2、源域信息

Label、cilLabel、pro:用户点击、点赞、文章浏览进度,可以根据点赞、浏览进度构建特征预测点击率。

ID信息:u_userId,用户ID,原理同目标域ID信息。

用户硬件软件相关信息:u_phonePrice、u_browserLifeCycle、u_browserMode,参与训练,重要程度需要后续评估。

信息流相关信息:u_feedLifeCycle、u_refreshTimes、u_newsCatInterests、u_newsCatDislike、u_newsCatInteres tsST、u_click_ca2_news,同上。

文章相关信息:i_docId、i_s_sourceId、i_regionEntity、i_cat、i_entities、i_dislikeTimes、i_upTimes、I_dtype,同上。

其他信息:e_ch、e_m、e_po、e_pl、e_rn、e_section,同上,具体信息内涵未明确说明。

时间信息:e_et,时间戳,原理同目标域时间戳

3、数据读取

在官网下载数据到本地后,对训练集、测试集的目标域、源域信息分别读取

train_data_ads = pd.read_csv('./2022_3_data/train/train_data_ads.csv')
train_data_feeds = pd.read_csv('./2022_3_data/train/train_data_feeds.csv')

test_data_ads = pd.read_csv('./2022_3_data/test/test_data_ads.csv')
test_data_feeds = pd.read_csv('./2022_3_data/test/test_data_feeds.csv')

4、数据观察

输出一下数据表格的shape可以看到,目标域数据包括31列特征;源域包括28列特征。

我们对每列数据进行统计,来观察数据的nunique和缺失值个数等信息:

def StatsTrain(train):
    stats = []
    for col in train.columns:
        stats.append(
            (col, train[col].nunique(), round(train[col].isnull().sum() * 100 / train.shape[0], 3), round(train[col].value_counts(normalize=True, dropna=False).values[0] * 100, 3), train[col].dtype))

    stats_df = pd.DataFrame(stats, columns=['特征', '属性个数', '缺失值占比', '最多属性占比', '特征类型'])
    stats_df.sort_values('缺失值占比', ascending=False)[:10]
    return stats_df

StatAds = StatsTrain(train_data_ads_)
StatFeeds = StatsTrain(train_data_feeds_)

其中,label只有0和1两个值,但0占据了绝大多数数据,数据严重的不平衡,后续训练时要注意对准确率和召回率的设计。源域中,u_userId有大量不同值,且与目标域的user_id并不匹配,因此训练时源域信息实际上没有与目标域过多的配合使用。

5、数据预处理

训练集是7天的数据,测试集是1天的数据。由于训练集和测试集的数据之间有时间重叠的情况,因此如果我们用未来的数据进行训练来预测,实际上是不合理的,因此我们可以将训练集中时间戳大于测试集最小时间戳的信息去掉,来避免穿越特征的情况。

# 处理特征穿越, 删除测试集期间内的数据
minDate = min(test_data_ads.pt_d.min(), test_data_feeds.e_et.min())
train_data_ads = train_data_ads[train_data_ads.pt_d < minDate]
train_data_feeds = train_data_feeds[train_data_feeds.e_et < minDate]

我们将训练集测试集的数据合并到一起,方便统一进行特征工程。为此,我们增加一个编号来区分原始的训练集和测试集。将数据集合并,并释放内存。

train_data_ads['istest'] = 0
test_data_ads['istest'] = 1
data_ads = pd.concat([train_data_ads, test_data_ads], axis=0, ignore_index=True)

train_data_feeds['istest'] = 0
test_data_feeds['istest'] = 1
data_feeds = pd.concat([train_data_feeds, test_data_feeds], axis=0, ignore_index=True)

del train_data_ads, test_data_ads, train_data_feeds, test_data_feeds
gc.collect()

:以上对原始数据的处理来自DataWhale提供的内容(鱼佬和坤佬的代码)。


特征工程

注意到,部分数据是‘object’类型,因此我们要对其进行编码。

在编码后,根据user_id来将源域数据合并到目标域数据中:

train_feeds = data_feeds[data_feeds.istest==0]
cols = [f for f in train_feeds.columns if f not in ['label','istest','u_userId']]
for col in tqdm(cols):
    tmp = train_feeds.groupby(['u_userId'])[col].nunique().reset_index()
    tmp.columns = ['user_id', col+'_feeds_nuni']
    data_ads = data_ads.merge(tmp, on='user_id', how='left')

cols = [f for f in train_feeds.columns if f not in ['istest','u_userId','u_newsCatInterests','u_newsCatDislike','u_newsCatInterestsST','u_click_ca2_news','i_docId','i_s_sourceId','i_entities']]
for col in tqdm(cols):
    tmp = train_feeds.groupby(['u_userId'])[col].mean().reset_index()
    tmp.columns = ['user_id', col+'_feeds_mean']
    data_ads = data_ads.merge(tmp, on='user_id', how='left')

在这里,我们只提取了源域数据的nunique值和mean值来构建特征。

构建好特征后,观察到源域数据的缺失值在18%左右。

我们简单的用后一个出现的非缺失值填充数据。

data_ads = data_ads.fillna(method='bfill', axis=0).fillna(0)

然后将数据划分为训练集和测试集:

cols = [f for f in data_ads.columns if f not in ['label','istest']]
x_train = data_ads[data_ads.istest==0][cols]
x_test = data_ads[data_ads.istest==1][cols]
y_train = data_ads[data_ads.istest==0]['label']

模型训练

我们使用CatBoost来训练模型,为了构建合适的验证集,我们运用k折交叉验证的方法。

模型验证

结果评估方式

统计广告域的样本 ctr 预估值,计算 GAUC 和 AUC 评测指标,具体公式如下:

\textbf{xAUC}=\alpha\times\textbf{GAUC}+\beta\times\textbf{AUC}

(其中,初赛:α 为 0.7,为 0.3)

AUC 为全体样本的 AUC 统计;

GAUC 为分组 AUC 的加权求和,以用户为维度分组,分组权值为分组内曝光量/总曝光)

\textbf{GAUC}=\frac{\sum_{k=i}^n\textbf{AUC}_i\times\textbf{Impression}_i}{\sum_{k=i}^n\textbf{Impression}_i}

你可能感兴趣的:(推荐算法,机器学习)