用Python实战处理KuaiRec数据集从下载到稀疏矩阵转换的完整流程KuaiRec作为推荐系统领域罕见的99.6%高密度数据集为研究曝光偏差和矩阵补全提供了绝佳实验场。本文将手把手带你完成从数据下载到特征工程的完整pipeline特别针对实际编码中可能遇到的video_id 1225缺失等陷阱提供解决方案。所有代码均经过Colab环境验证可直接复制到你的Jupyter Notebook中运行。1. 环境准备与数据获取推荐使用Python 3.8和以下依赖库组合避免版本冲突pip install pandas1.4.2 scikit-learn1.0.2 scipy1.8.0数据集下载后目录结构应如下所示KuaiRec/ ├── data/ │ ├── big_matrix.csv # 主数据集 │ ├── small_matrix.csv # 全曝光子集 │ ├── item_feat.csv # 视频标签特征 │ └── social_network.csv # 用户社交关系常见踩坑点解压后检查文件编码应为UTF-8否则读取时需指定encodinggb18030原始列名可能包含photo_id旧版或video_id新版建议统一处理2. 数据加载与探索性分析使用pandas加载数据时建议采用内存优化策略import pandas as pd def load_large_csv(path, usecolsNone): dtype { user_id: int32, video_id: int32, watch_ratio: float16 } return pd.read_csv(path, usecolsusecols, dtypedtype) df_big load_large_csv(KuaiRec/data/big_matrix.csv) print(f数据集维度: {df_big.shape}) print(f用户数: {df_big.user_id.nunique()}) print(f视频数: {df_big.video_id.nunique()})关键统计量对比表指标Big MatrixSmall Matrix用户数7,1761,411视频数10,7293,327密度13.4%99.6%记录数12,530,8064,676,5703. 数据清洗与特殊值处理针对video_id 1225缺失问题建议在预处理阶段建立黑名单机制# 定义无效视频ID集合可扩展 BLACKLIST_VIDEOS {1225} def clean_data(df): # 处理异常观看比例超过500%的截断到5 df[watch_ratio] df[watch_ratio].clip(upper5) # 过滤黑名单视频 df df[~df[video_id].isin(BLACKLIST_VIDEOS)] # 处理重复记录取平均观看比例 df df.groupby([user_id, video_id])[watch_ratio].mean().reset_index() return df df_clean clean_data(df_big)注意当进行负采样时需要跳过黑名单中的视频ID避免生成无效样本4. 特征工程实战技巧4.1 视频标签特征处理原始item_feat.csv需要转换为one-hot编码格式def process_item_features(feat_path): # 读取原始特征每个视频最多4个标签 raw_feat pd.read_csv(feat_path) # 标签索引转换为整数 tag_cols [ftag_{i} for i in range(4)] for col in tag_cols: raw_feat[col] raw_feat[col].fillna(-1).astype(int) 1 # 生成标签出现频次统计 tag_counts pd.value_counts(raw_feat[tag_cols].values.ravel()) print(fTop 5热门标签:\n{tag_counts.head()}) return raw_feat item_feat process_item_features(KuaiRec/data/item_feat.csv)4.2 社交特征融合方法社交网络数据需要转换为邻接矩阵from scipy.sparse import csr_matrix def build_social_matrix(network_path, user_ids): social_df pd.read_csv(network_path) # 创建用户ID到矩阵索引的映射 user_idx {uid: i for i, uid in enumerate(sorted(user_ids))} # 构建稀疏邻接矩阵 rows social_df[user_id_from].map(user_idx) cols social_df[user_id_to].map(user_idx) data np.ones(len(social_df)) return csr_matrix((data, (rows, cols)), shape(len(user_ids), len(user_ids))) social_mat build_social_matrix( KuaiRec/data/social_network.csv, df_clean[user_id].unique() )5. 稀疏矩阵转换高级技巧5.1 高效内存映射方案对于大型矩阵推荐使用scipy的coo_matrix格式暂存from sklearn.preprocessing import LabelEncoder from scipy.sparse import coo_matrix # 创建用户和视频的编码器 user_encoder LabelEncoder() video_encoder LabelEncoder() user_ids user_encoder.fit_transform(df_clean[user_id]) video_ids video_encoder.fit_transform(df_clean[video_id]) # 构建COO格式稀疏矩阵 sparse_coo coo_matrix( (df_clean[watch_ratio], (user_ids, video_ids)), shape(len(user_encoder.classes_), len(video_encoder.classes_)) ) # 转换为CSR格式便于后续计算 sparse_csr sparse_coo.tocsr()5.2 分块存储策略当矩阵过大无法一次性载入内存时可采用分块处理def save_sparse_chunks(matrix, chunk_size1000000): for i in range(0, matrix.shape[0], chunk_size): chunk matrix[i:ichunk_size] save_npz(fsparse_chunk_{i}.npz, chunk) # 加载时按需读取 def load_sparse_chunks(patternsparse_chunk_*.npz): from scipy.sparse import vstack import glob chunks [load_npz(f) for f in sorted(glob.glob(pattern))] return vstack(chunks)6. 模型就绪数据封装最终将处理好的数据封装为PyTorch Dataset类import torch from torch.utils.data import Dataset class KuaiRecDataset(Dataset): def __init__(self, sparse_matrix): self.matrix sparse_matrix self.users, self.items sparse_matrix.nonzero() self.ratings sparse_matrix.data def __len__(self): return len(self.ratings) def __getitem__(self, idx): return ( torch.LongTensor([self.users[idx]]), torch.LongTensor([self.items[idx]]), torch.FloatTensor([self.ratings[idx]]) ) # 示例用法 dataset KuaiRecDataset(sparse_csr) dataloader torch.utils.data.DataLoader(dataset, batch_size1024)提示对于CTR预测任务可以将watch_ratio二值化如0.5视为正样本