零基础玩转新闻推荐Python实战天池大赛Baseline全解析第一次参加数据竞赛的新手们面对天池新闻推荐大赛的海量数据往往手足无措。36万篇文章、20万用户行为记录光是看一眼数据规模就让人望而生畏。但别担心这份指南将带你用Python一步步构建可运行的Baseline从数据加载到最终提交每个环节都有详细解释和完整代码。1. 环境准备与数据初探工欲善其事必先利其器。在开始之前我们需要确保开发环境配置正确。推荐使用Anaconda创建独立的Python环境避免包版本冲突conda create -n news_rec python3.8 conda activate news_rec pip install pandas numpy tqdm scikit-learn数据集包含四个关键文件结构如下文件名称描述大小train_click_log.csv训练集用户点击日志43.5MBtestA_click_log.csv测试集A用户点击日志20.47MBarticles.csv新闻文章元数据9.89MBarticles_emb.csv文章嵌入向量973.15MB提示articles_emb.csv文件较大下载时可能需要耐心等待。如果本地内存有限可以先处理小文件调试代码。数据加载是第一步也是容易出错的地方。使用pandas读取CSV文件时指定正确的数据类型可以显著减少内存占用import pandas as pd def load_data(data_path): # 读取点击日志 click_log pd.read_csv(f{data_path}train_click_log.csv, dtype{ user_id: int32, click_article_id: int32, click_timestamp: int32 }) # 读取文章信息 articles pd.read_csv(f{data_path}articles.csv, dtype{ article_id: int32, category_id: int16 }) return click_log, articles2. 内存优化技巧实战处理大规模数据集时内存管理至关重要。以下方法可以将内存占用减少60%以上数据类型降级将int64转为int32/int16float64转为float32分类数据转换对低基数字段使用category类型分块处理对于超大文件使用chunksize参数分批读取这里有一个实用的内存优化函数def reduce_mem_usage(df): 迭代式内存优化函数 start_mem df.memory_usage().sum() / 1024**2 print(f初始内存占用: {start_mem:.2f} MB) for col in df.columns: col_type df[col].dtype if col_type ! object: c_min df[col].min() c_max df[col].max() if str(col_type)[:3] int: if c_min np.iinfo(np.int8).min and c_max np.iinfo(np.int8).max: df[col] df[col].astype(np.int8) elif c_min np.iinfo(np.int16).min and c_max np.iinfo(np.int16).max: df[col] df[col].astype(np.int16) # 类似处理int32/int64... else: if c_min np.finfo(np.float16).min and c_max np.finfo(np.float16).max: df[col] df[col].astype(np.float16) # 类似处理float32... end_mem df.memory_usage().sum() / 1024**2 print(f优化后内存占用: {end_mem:.2f} MB (减少{100*(start_mem-end_mem)/start_mem:.1f}%)) return df3. 协同过滤Baseline实现协同过滤是推荐系统的经典算法特别适合作为竞赛Baseline。我们实现基于物品的协同过滤(ItemCF)计算物品相似度矩阵统计物品共现次数应用Jaccard或余弦相似度加入时间衰减因子from collections import defaultdict from tqdm import tqdm import math def itemcf_sim(df): 物品相似度矩阵计算 user_item_time get_user_item_time(df) i2i_sim {} item_cnt defaultdict(int) # 统计共现矩阵 for user, items in tqdm(user_item_time.items()): for i, t1 in items: item_cnt[i] 1 i2i_sim.setdefault(i, {}) for j, t2 in items: if i j: continue # 加入时间衰减因子 time_decay 1.0 / (1 0.1 * abs(t1 - t2)) i2i_sim[i][j] i2i_sim[i].get(j, 0) time_decay / math.log(len(items) 1) # 归一化 i2i_sim_normalized {} for i, related_items in i2i_sim.items(): for j, wij in related_items.items(): i2i_sim_normalized.setdefault(i, {}) i2i_sim_normalized[i][j] wij / math.sqrt(item_cnt[i] * item_cnt[j]) return i2i_sim_normalized生成推荐列表对每个用户的历史点击物品找出相似度最高的K个物品按相似度加权排序def item_based_recommend(user, user_item_time_dict, i2i_sim, sim_topk10, recall_num10): 基于物品的推荐 user_hist user_item_time_dict[user] item_rank {} for i, _ in user_hist: for j, wij in sorted(i2i_sim.get(i, {}).items(), keylambda x: x[1], reverseTrue)[:sim_topk]: if j in [x[0] for x in user_hist]: continue item_rank[j] item_rank.get(j, 0) wij # 热门物品兜底 if len(item_rank) recall_num: for i in hot_items[:recall_num-len(item_rank)]: if i not in item_rank: item_rank[i] -1 # 给一个较低的分数 return sorted(item_rank.items(), keylambda x: x[1], reverseTrue)[:recall_num]4. 结果提交与技巧分享天池竞赛要求特定的提交格式我们需要将预测结果转换为规定的CSV格式user_id,article_1,article_2,article_3,article_4,article_5 200000,12345,23456,34567,45678,56789转换代码示例def generate_submission(recall_df, topk5): 生成提交文件 # 按用户分组对每个用户的推荐物品排序 recall_df[rank] recall_df.groupby(user_id)[score].rank( ascendingFalse, methodfirst) # 筛选每个用户topk推荐 topk_recall recall_df[recall_df[rank] topk].copy() # 转换为宽格式 submission topk_recall.pivot( indexuser_id, columnsrank, valuesarticle_id ).reset_index() # 重命名列 submission.columns [ user_id, article_1, article_2, article_3, article_4, article_5 ] return submission几个提升成绩的实用技巧多路召回融合结合热门推荐、协同过滤、内容相似度等多种策略特征工程挖掘用户活跃时间段、点击时间间隔等时序特征模型融合将ItemCF与矩阵分解等方法的预测结果加权平均第一次运行完整流程可能会遇到各种问题比如内存不足、长尾物品处理不当等。建议先用小样本调试确保流程跑通后再扩展到全量数据。