Datawhale AI夏令营-机器学习-task2.1-理解赛题,也理解baseline

本文档旨在帮助参赛者深入理解 Baseline 方案,并以此为起点,探索更优的解题思路。我们将首先解读赛题,然后剖析 Baseline 的核心思想,最后提供一系列可行的优化方向。
理解赛题的内容看datawhale夏令营的材料即可:科大讯飞AI大赛(机器学习方向) - Datawhale
飞书链接:https://z1ewj4b0oao.feishu.cn/wiki/RvmCwRcQkiM4qwkZK2xcRC3ankb?from=from_copylink
 

 

一、赛题解读:我们要解决什么问题?

 
在深入代码之前,我们先明确目标。
 
- 核心任务:预测一个用户(由 `did` 标识)在产生某条行为日志时,是否为“新用户”(`is_new_did`=1)。这是一个典型的二分类问题。
- 数据特性
- 行为日志数据:每一行代表用户的一次行为事件,而非一个独立的用户。同一个用户 `did` 会有多条记录。
- 时序性:数据带有毫秒级时间戳 `common_ts`,暗示用户的行为具有时间序列特征。
- 类别特征丰富:包含大量类别特征,如 `device_brand`, `common_city`, `appver` 等。
- 半结构化数据:`udmap` 字段是 JSON 格式,内部含有 `botId` 和 `pluginId` 等信息,需要解析。
- 评估指标F1-Score。这个指标同时关注模型的精确率(Precision)和召回率(Recall),在类别不均衡的场景下(新用户和老用户的比例可能差异很大)是比准确率(Accuracy)更合适的评估标准。为了得到高 F1-Score,模型需要尽可能多地找出新用户,同时又要避免将老用户错判为新用户。
 

 

二、Baseline 代码流程剖析与优化思路

 
接下来,我们将逐一分析 Baseline.ipynb 中的核心代码单元,理解其功能并探讨相应的优化策略。
 

1. 数据加载与时间特征工程 (对应 Cell 3)

 
代码作用
此代码块负责数据的基础准备工作:
1. 加载数据: 使用 `pandas` 加载 `train.csv` 和 `testA_data.csv`。
2. 时间戳转换: 将毫秒级的 `common_ts` 转换为标准的日期时间格式。
3. 提取初级时间特征: 从转换后的时间中提取出 `day`(几号), `dayofweek`(周几), `hour`(小时)作为模型的输入特征。这是为了让模型能捕捉到以日、周、小时为单位的用户行为周期性。
 
优化方向
这是特征工程的起点,也是最值得深挖的地方。
- 更精细的时间特征:
- 可以创建组合特征,如 hour + dayofweek,来区分工作日的白天和周末的白天。
- 可以增加 is_weekend (是否周末) 这样的布尔特征。
- 计算时间差,比如用户本次行为与他/她首次行为的时间间隔。
- 强大的聚合特征 (关键):
- Baseline 的建模粒度是“事件”,但同一个 did 的多条记录之间存在关联。我们应该以 did 为核心进行分组 (groupby),构造出描述用户画像的统计特征。例如:
- 历史行为次数: 一个 `did` 在数据集中出现了多少次? (`count`)
- 行为多样性: 一个 `did` 体验过多少种不同的 `eid` (事件) 或 `mid` (模块)? (`nunique`)
- 活跃时间跨度: 一个 `did` 最早和最晚出现的时间戳之差是多少? (`max` - `min`)
- 地理足迹: 一个 `did` 在多少个不同的城市 (`common_city`) 出现过?
 

2. 类别特征编码 (对应 Cell 5)

 
代码作用
此代码块处理的是机器学习模型无法直接理解的文本类特征:
1. 遍历类别特征: 循环处理 `device_brand`, `ntt`, `appver` 等所有指定的类别特征。
2. 标签编码: 使用 `sklearn.preprocessing.LabelEncoder` 将每个独特的类别值(如 "Apple")转换成一个唯一的整数(如 10)。
3. 统一编码空间: 为了防止测试集中出现训练集未见过的新类别而导致报错,代码先用 `pd.concat` 合并训练集和测试集的特征值,在全量数据上进行 `fit`,然后再分别对两部分数据进行 `transform`,确保同一类别值在训练集和测试集中被编码为同一个整数。
 
优化方向
- **解析 `udmap`**: Baseline 将 `udmap` 整个 JSON 字符串当作一个类别进行了编码,这会丢失其内部的结构化信息。首先应该解析它,提取 `botId` 和 `pluginId` 作为新特征,然后再对这两个新特征进行编码。
- 尝试其他编码方式:
- LabelEncoder 会引入人为的大小关系(例如,城市A编码为1,城市B编码为2,模型可能会误认为“城市B > 城市A”)。对于树模型虽有一定鲁棒性,但仍非最优。
- One-Hot Encoding: 可以避免上述问题,但对于高基数类别(一个特征有很多种取值,如 `appver`)会导致维度爆炸,需要谨慎使用。
- Count Encoding / Target Encoding: 这些是更高级且在比赛中常用的编码方法。例如,`Count Encoding` 可以将类别值替换为其在数据集中出现的频次,这本身就是一种很有用的统计特征。
 

3. 模型训练与交叉验证 (对应 Cell 7)

 
代码作用
这是整个流程的核心,负责模型的训练和验证:
1. 核心模型: 定义了 `LightGBM` 模型的参数 `params`,选用 `binary` 作为目标函数。
2. 验证策略: 使用 `StratifiedKFold` 进行5折分层交叉验证。这保证了每次划分出的验证集中,目标变量 `is_new_did` 的正负样本比例与整体数据一致,评估结果更可靠。
3. F1阈值寻优: 定义了 `find_optimal_threshold` 函数。由于模型的直接输出是概率,而赛题的评估指标是F1,此函数通过在验证集上尝试不同的概率阈值,找到能让F1分数最高的阈值。这是针对性优化F1指标的关键一步。
4. 训练与预测: 在交叉验证的循环中,对每一折数据训练一个 LightGBM 模型,并在对应的验证集和全体测试集上进行预测。对测试集的最终预测结果是5个模型预测值的平均,这样可以增加模型的稳定性。
 
优化方向
- 超参数调优: Baseline 中的 `params` 是一组通用参数。使用 `Optuna` 或 `GridSearch` 等自动化调参工具,结合交叉验证,可以找到更适合本数据集的参数组合,从而提升模型性能。
- 更换/融合模型:
- 可以尝试 XGBoostCatBoost 模型,它们在不同数据集上表现各有千秋。
- 将多个不同模型(甚至不同参数的LGBM模型)的预测结果进行加权融合(Ensemble),通常能获得比单模型更好、更稳定的效果。
- 时序验证: 对于有时序信息的数据,随机K折划分可能存在“数据穿越”(用未来的数据预测过去)的风险。可以尝试更严格的时序验证,例如,按时间戳排序后,用前80%的数据做训练,后20%做验证,这样更能模拟真实上线环境,得到的本地分数也更可信。
 

4. 结果评估与特征重要性分析 (对应 Cell 8)

 
代码作用
此代码块负责收尾工作,评估结果并为后续迭代提供方向:
1. OOF评估: 计算了 OOF (Out-of-Fold) 预测的总 F1 分数。OOF 分数是模型在整个训练集上的交叉验证性能表现,通常是比单折验证分数更可靠的本地评估指标。
2. 生成提交文件: 使用交叉验证中找到的平均最优阈值,将测试集上的平均预测概率转换为0/1标签,并生成符合要求的 `submit.csv` 文件。
3. 特征重要性: 打印出模型学到的特征重要性排序(基于 `gain`,即该特征每次分裂带来的总增益)。
 
优化方向
这部分代码本身是结果展示,无需优化,但它的输出是下一轮优化的宝贵输入
- 迭代优化: 仔细分析特征重要性列表:
- 强特征: 哪些特征最重要?我们能否基于它们构造出更强的交叉特征?(例如,如果 `mid` 和 `appver` 很重要,那 `mid` 和 `appver` 的组合会不会是强特征?)
- 弱特征: 哪些特征重要性很低?它们是否是噪声?我们是否可以考虑移除它们,以简化模型、降低过拟合风险?
- 验证新特征: 如果你新构造的特征(如之前的聚合特征)出现在了重要性列表的前列,这便有力地证明了你的特征工程是有效的。
 

 
posted @ 2025-07-17 13:26  windiest  阅读(20)  评论(0)    收藏  举报