Commit 1211812579e87ed6d9c44956c630b1b6fde127f9
1 parent
5b61955e
offline tasks: mem optimize
Showing
45 changed files
with
3338 additions
and
4079 deletions
Show diff stats
offline_tasks/B2B_LOW_FREQUENCY_OPTIMIZATION.md deleted
| ... | ... | @@ -1,223 +0,0 @@ |
| 1 | -# B2B低频场景优化总结 | |
| 2 | - | |
| 3 | -## 📋 优化背景 | |
| 4 | - | |
| 5 | -B2B场景的特点: | |
| 6 | -- ✅ 用户行为频次**非常低** | |
| 7 | -- ✅ 行为间隔时间**可能很长** | |
| 8 | -- ✅ 历史行为**依然重要**,不应过度衰减 | |
| 9 | - | |
| 10 | -## ✅ 已完成的优化 | |
| 11 | - | |
| 12 | -### 1. **i2i_session_w2v.py** - Session分割策略优化 | |
| 13 | - | |
| 14 | -#### 修改前(基于时间间隔) | |
| 15 | -```python | |
| 16 | -# 问题:B2B场景下,用户可能几个月才有一次行为 | |
| 17 | -# 基于30分钟间隔分割session不合适 | |
| 18 | -session_gap_minutes = 30 | |
| 19 | -if (current_time - last_time).total_seconds() / 60 > session_gap_minutes: | |
| 20 | - # 新session | |
| 21 | -``` | |
| 22 | - | |
| 23 | -#### 修改后(固定长度分块) | |
| 24 | -```python | |
| 25 | -# 参考: graphembedding/session_w2v/prepare_data.py | |
| 26 | -# 按固定长度分块,不考虑时间间隔 | |
| 27 | -max_session_length = 50 # 最大会话长度 | |
| 28 | -min_session_length = 2 # 最小会话长度 | |
| 29 | - | |
| 30 | -# 按用户行为序列分块 | |
| 31 | -user_sessions = [ | |
| 32 | - item_sequence[i:i + max_session_length] | |
| 33 | - for i in range(0, len(item_sequence), max_session_length) | |
| 34 | -] | |
| 35 | -``` | |
| 36 | - | |
| 37 | -**优势:** | |
| 38 | -- 不依赖时间间隔,适合低频场景 | |
| 39 | -- 逻辑简化,性能更好 | |
| 40 | -- 保留用户行为的顺序信息 | |
| 41 | - | |
| 42 | -**新增参数:** | |
| 43 | -```bash | |
| 44 | ---max_session_length 50 # 最大会话长度 | |
| 45 | ---min_session_length 2 # 最小会话长度(过滤太短的序列) | |
| 46 | -``` | |
| 47 | - | |
| 48 | ---- | |
| 49 | - | |
| 50 | -### 2. **i2i_swing.py** - 关闭时间衰减 | |
| 51 | - | |
| 52 | -#### 修改前 | |
| 53 | -```python | |
| 54 | ---time_decay # 默认True,开启时间衰减 | |
| 55 | -# 30天前权重: 0.95 | |
| 56 | -# 60天前权重: 0.90 | |
| 57 | -# 180天前权重: 0.74 (衰减过快) | |
| 58 | -``` | |
| 59 | - | |
| 60 | -#### 修改后 | |
| 61 | -```python | |
| 62 | ---time_decay # 默认False,关闭时间衰减 | |
| 63 | -# 所有历史行为权重相同,更适合低频场景 | |
| 64 | -``` | |
| 65 | - | |
| 66 | -**原因:** | |
| 67 | -在B2B低频场景下,6个月前的行为可能依然很有价值,不应该被大幅衰减。 | |
| 68 | - | |
| 69 | ---- | |
| 70 | - | |
| 71 | -### 3. **interest_aggregation.py** - 关闭时间衰减 | |
| 72 | - | |
| 73 | -#### 修改内容 | |
| 74 | -```python | |
| 75 | -# 热门商品索引 - 关闭时间衰减 | |
| 76 | -list_type_indices['hot'] = aggregate_by_dimensions( | |
| 77 | - df_hot, behavior_weights, time_decay=False # 改为False | |
| 78 | -) | |
| 79 | - | |
| 80 | -# 加购商品索引 - 关闭时间衰减 | |
| 81 | -list_type_indices['cart'] = aggregate_by_dimensions( | |
| 82 | - df_cart, behavior_weights, time_decay=False # 改为False | |
| 83 | -) | |
| 84 | - | |
| 85 | -# 全局索引 - 关闭时间衰减 | |
| 86 | -global_aggregations = aggregate_by_dimensions( | |
| 87 | - df, behavior_weights, time_decay=False # 改为False | |
| 88 | -) | |
| 89 | - | |
| 90 | -# 新品索引 - 本来就不用时间衰减(保持不变) | |
| 91 | -list_type_indices['new'] = aggregate_by_dimensions( | |
| 92 | - df_new, behavior_weights, time_decay=False | |
| 93 | -) | |
| 94 | -``` | |
| 95 | - | |
| 96 | ---- | |
| 97 | - | |
| 98 | -## 🚀 使用方法 | |
| 99 | - | |
| 100 | -### Session W2V(已自动优化) | |
| 101 | -```bash | |
| 102 | -# 使用新的固定长度分块策略 | |
| 103 | -python3 scripts/i2i_session_w2v.py \ | |
| 104 | - --lookback_days 365 \ | |
| 105 | - --max_session_length 50 \ | |
| 106 | - --min_session_length 2 \ | |
| 107 | - --top_n 50 \ | |
| 108 | - --debug | |
| 109 | -``` | |
| 110 | - | |
| 111 | -### Swing算法(默认已关闭时间衰减) | |
| 112 | -```bash | |
| 113 | -# 默认不使用时间衰减,适合B2B场景 | |
| 114 | -python3 scripts/i2i_swing.py \ | |
| 115 | - --lookback_days 365 \ | |
| 116 | - --top_n 50 \ | |
| 117 | - --debug | |
| 118 | - | |
| 119 | -# 如果需要开启时间衰减(不推荐) | |
| 120 | -python3 scripts/i2i_swing.py \ | |
| 121 | - --lookback_days 365 \ | |
| 122 | - --time_decay \ | |
| 123 | - --decay_factor 0.99 # 更缓慢的衰减 | |
| 124 | -``` | |
| 125 | - | |
| 126 | -### 兴趣聚合(已自动优化) | |
| 127 | -```bash | |
| 128 | -# 已默认关闭时间衰减 | |
| 129 | -python3 scripts/interest_aggregation.py \ | |
| 130 | - --lookback_days 365 \ | |
| 131 | - --top_n 100 \ | |
| 132 | - --debug | |
| 133 | -``` | |
| 134 | - | |
| 135 | -### 运行所有任务 | |
| 136 | -```bash | |
| 137 | -cd /home/tw/recommendation/offline_tasks | |
| 138 | - | |
| 139 | -# 使用更长的回溯天数,适合低频场景 | |
| 140 | -python3 run_all.py --lookback_days 365 --top_n 50 --debug | |
| 141 | -``` | |
| 142 | - | |
| 143 | ---- | |
| 144 | - | |
| 145 | -## 📊 其他索引(无需修改) | |
| 146 | - | |
| 147 | -### i2i_deepwalk.py | |
| 148 | -- ✅ 不涉及session分割 | |
| 149 | -- ✅ 不使用时间衰减 | |
| 150 | -- ✅ 无需修改 | |
| 151 | - | |
| 152 | -### i2i_content_similar.py | |
| 153 | -- ✅ 基于商品属性的相似度 | |
| 154 | -- ✅ 不涉及时间因素 | |
| 155 | -- ✅ 无需修改 | |
| 156 | - | |
| 157 | ---- | |
| 158 | - | |
| 159 | -## 💡 建议的配置调整 | |
| 160 | - | |
| 161 | -### 1. 增加回溯天数 | |
| 162 | -```python | |
| 163 | -# offline_tasks/config/offline_config.py | |
| 164 | -DEFAULT_LOOKBACK_DAYS = 365 # 从默认值改为365天 | |
| 165 | -``` | |
| 166 | - | |
| 167 | -### 2. 如果需要重新启用时间衰减(特殊场景) | |
| 168 | -```bash | |
| 169 | -# Swing算法 | |
| 170 | -python3 scripts/i2i_swing.py --time_decay --decay_factor 0.99 | |
| 171 | - | |
| 172 | -# 修改interest_aggregation.py中的hard-coded值 | |
| 173 | -# 将 time_decay=False 改回 time_decay=True | |
| 174 | -``` | |
| 175 | - | |
| 176 | ---- | |
| 177 | - | |
| 178 | -## ✨ 优化效果 | |
| 179 | - | |
| 180 | -### 预期改进 | |
| 181 | -1. **Session W2V**: | |
| 182 | - - 不再因为时间间隔过长而丢失用户行为序列 | |
| 183 | - - 能够更好地捕捉低频用户的行为模式 | |
| 184 | - | |
| 185 | -2. **Swing算法**: | |
| 186 | - - 历史行为不会因时间衰减而被低估 | |
| 187 | - - 在数据稀疏的情况下充分利用所有历史数据 | |
| 188 | - | |
| 189 | -3. **兴趣聚合**: | |
| 190 | - - 长期累积的用户偏好得到保留 | |
| 191 | - - 推荐结果更稳定 | |
| 192 | - | |
| 193 | -### 需要监控的指标 | |
| 194 | -- [ ] Session数量变化 | |
| 195 | -- [ ] 相似物品覆盖率 | |
| 196 | -- [ ] 推荐效果指标(CTR、转化率等) | |
| 197 | - | |
| 198 | ---- | |
| 199 | - | |
| 200 | -## 📝 回滚方法 | |
| 201 | - | |
| 202 | -如果需要恢复之前的逻辑: | |
| 203 | - | |
| 204 | -```bash | |
| 205 | -# 1. 恢复i2i_swing.py的时间衰减 | |
| 206 | -git diff offline_tasks/scripts/i2i_swing.py | |
| 207 | -# 将 default=False 改回 default=True | |
| 208 | - | |
| 209 | -# 2. 恢复interest_aggregation.py的时间衰减 | |
| 210 | -git diff offline_tasks/scripts/interest_aggregation.py | |
| 211 | -# 将所有 time_decay=False 改回 time_decay=True | |
| 212 | - | |
| 213 | -# 3. 恢复i2i_session_w2v.py的时间分割 | |
| 214 | -git diff offline_tasks/scripts/i2i_session_w2v.py | |
| 215 | -# 恢复基于时间间隔的session分割逻辑 | |
| 216 | -``` | |
| 217 | - | |
| 218 | ---- | |
| 219 | - | |
| 220 | -**修改时间**: 2025-10-16 | |
| 221 | -**适用场景**: B2B低频交易场景 | |
| 222 | -**核心原则**: 在低频场景下,所有历史数据都很宝贵,不要过度衰减 | |
| 223 | - |
offline_tasks/CHANGES_SUMMARY.md deleted
| ... | ... | @@ -1,326 +0,0 @@ |
| 1 | -# 离线任务更新总结 | |
| 2 | - | |
| 3 | -## 更新日期 | |
| 4 | -2025-10-17 | |
| 5 | - | |
| 6 | -## 更新内容 | |
| 7 | - | |
| 8 | -### 1. 重构内容相似索引 (`i2i_content_similar.py`) | |
| 9 | - | |
| 10 | -#### 变化 | |
| 11 | -- **从**: 基于数据库商品属性计算(TF-IDF + 余弦相似度) | |
| 12 | -- **到**: 基于Elasticsearch向量计算(KNN查询) | |
| 13 | - | |
| 14 | -#### 简化 | |
| 15 | -- **移除**: 所有命令行参数(`--method`, `--top_n`, `--output`, `--debug`) | |
| 16 | -- **保留**: 无参数,配置内置在代码中 | |
| 17 | -- **生成**: 两份索引文件(名称向量 + 图片向量) | |
| 18 | - | |
| 19 | -### 2. 简化运行脚本 (`run_all.py`) | |
| 20 | - | |
| 21 | -#### 移除的参数 | |
| 22 | -- `--skip-i2i` - 跳过i2i任务 | |
| 23 | -- `--skip-interest` - 跳过兴趣聚合 | |
| 24 | -- `--only-swing` - 只运行Swing | |
| 25 | -- `--only-w2v` - 只运行W2V | |
| 26 | -- `--only-deepwalk` - 只运行DeepWalk | |
| 27 | -- `--only-content` - 只运行内容相似 | |
| 28 | -- `--only-interest` - 只运行兴趣聚合 | |
| 29 | -- `--lookback_days` - 回看天数 | |
| 30 | -- `--top_n` - Top N数量 | |
| 31 | - | |
| 32 | -#### 保留的参数 | |
| 33 | -- `--debug` - 调试模式(唯一参数) | |
| 34 | - | |
| 35 | -#### 使用 | |
| 36 | -```bash | |
| 37 | -# 之前 | |
| 38 | -python run_all.py --lookback_days 30 --top_n 50 --skip-interest | |
| 39 | - | |
| 40 | -# 现在 | |
| 41 | -python run_all.py | |
| 42 | -# 或者 | |
| 43 | -python run_all.py --debug | |
| 44 | -``` | |
| 45 | - | |
| 46 | -### 3. 更新Redis数据规范 (`REDIS_DATA_SPEC.md`) | |
| 47 | - | |
| 48 | -#### 新增索引 | |
| 49 | -- `i2i_content_name`: 基于名称向量的相似索引 | |
| 50 | -- `i2i_content_pic`: 基于图片向量的相似索引 | |
| 51 | - | |
| 52 | -#### 更新统计 | |
| 53 | -- Key数量: 245,000 → 270,000 | |
| 54 | -- 总内存: ~135MB → ~160MB | |
| 55 | - | |
| 56 | -### 4. 更新索引加载器 (`load_index_to_redis.py`) | |
| 57 | - | |
| 58 | -#### 更新 | |
| 59 | -- 添加 `content_name` 到i2i索引类型列表 | |
| 60 | -- 添加 `content_pic` 到i2i索引类型列表 | |
| 61 | -- 自动加载两个新的内容相似索引 | |
| 62 | - | |
| 63 | -### 5. 更新依赖 (`requirements.txt`) | |
| 64 | - | |
| 65 | -#### 新增 | |
| 66 | -``` | |
| 67 | -elasticsearch>=8.0.0 | |
| 68 | -``` | |
| 69 | - | |
| 70 | -### 6. 新增文档 | |
| 71 | - | |
| 72 | -#### ES向量相似度说明 (`ES_VECTOR_SIMILARITY.md`) | |
| 73 | -- ES配置说明 | |
| 74 | -- 工作流程详解 | |
| 75 | -- 性能说明和优化建议 | |
| 76 | -- 故障排查指南 | |
| 77 | - | |
| 78 | -#### 更新说明 (`CONTENT_SIMILARITY_UPDATE.md`) | |
| 79 | -- 更新概述 | |
| 80 | -- 主要变化 | |
| 81 | -- 使用指南 | |
| 82 | -- 技术细节 | |
| 83 | -- 性能说明 | |
| 84 | -- 与其他算法对比 | |
| 85 | - | |
| 86 | -#### 本文档 (`CHANGES_SUMMARY.md`) | |
| 87 | -- 所有变更的简要总结 | |
| 88 | - | |
| 89 | -### 7. 新增测试脚本 (`test_es_connection.py`) | |
| 90 | - | |
| 91 | -测试ES连接和向量查询功能: | |
| 92 | -- 测试ES连接 | |
| 93 | -- 测试索引是否存在 | |
| 94 | -- 测试向量字段映射 | |
| 95 | -- 测试查询商品向量 | |
| 96 | -- 测试KNN向量查询 | |
| 97 | - | |
| 98 | -## 文件清单 | |
| 99 | - | |
| 100 | -### 修改的文件 | |
| 101 | -1. ✅ `offline_tasks/scripts/i2i_content_similar.py` - 完全重写 | |
| 102 | -2. ✅ `offline_tasks/run_all.py` - 简化参数 | |
| 103 | -3. ✅ `offline_tasks/REDIS_DATA_SPEC.md` - 更新规范 | |
| 104 | -4. ✅ `offline_tasks/scripts/load_index_to_redis.py` - 添加新索引类型 | |
| 105 | -5. ✅ `requirements.txt` - 添加elasticsearch依赖 | |
| 106 | - | |
| 107 | -### 新增的文件 | |
| 108 | -6. ✅ `offline_tasks/scripts/ES_VECTOR_SIMILARITY.md` - ES向量说明 | |
| 109 | -7. ✅ `offline_tasks/CONTENT_SIMILARITY_UPDATE.md` - 更新说明 | |
| 110 | -8. ✅ `offline_tasks/CHANGES_SUMMARY.md` - 本文档 | |
| 111 | -9. ✅ `offline_tasks/scripts/test_es_connection.py` - ES测试脚本 | |
| 112 | - | |
| 113 | -## 使用指南 | |
| 114 | - | |
| 115 | -### 1. 安装依赖 | |
| 116 | - | |
| 117 | -```bash | |
| 118 | -pip install -r requirements.txt | |
| 119 | -``` | |
| 120 | - | |
| 121 | -### 2. 测试ES连接 | |
| 122 | - | |
| 123 | -```bash | |
| 124 | -cd /home/tw/recommendation/offline_tasks | |
| 125 | -python scripts/test_es_connection.py | |
| 126 | -``` | |
| 127 | - | |
| 128 | -### 3. 运行内容相似索引生成 | |
| 129 | - | |
| 130 | -```bash | |
| 131 | -# 单独运行 | |
| 132 | -python scripts/i2i_content_similar.py | |
| 133 | - | |
| 134 | -# 或通过run_all运行所有任务 | |
| 135 | -python run_all.py | |
| 136 | -``` | |
| 137 | - | |
| 138 | -### 4. 加载到Redis | |
| 139 | - | |
| 140 | -```bash | |
| 141 | -python scripts/load_index_to_redis.py | |
| 142 | -``` | |
| 143 | - | |
| 144 | -## 输出文件 | |
| 145 | - | |
| 146 | -### 新增的输出文件 | |
| 147 | -- `output/i2i_content_name_YYYYMMDD.txt` - 名称向量相似索引 | |
| 148 | -- `output/i2i_content_pic_YYYYMMDD.txt` - 图片向量相似索引 | |
| 149 | - | |
| 150 | -### 文件格式 | |
| 151 | -``` | |
| 152 | -item_id \t item_name \t similar_id1:score1,similar_id2:score2,... | |
| 153 | -``` | |
| 154 | - | |
| 155 | -### 示例 | |
| 156 | -``` | |
| 157 | -3302275 香蕉干 3302276:0.9234,3302277:0.8756,3302278:0.8432 | |
| 158 | -``` | |
| 159 | - | |
| 160 | -## Redis Keys | |
| 161 | - | |
| 162 | -### 新增的Key格式 | |
| 163 | -``` | |
| 164 | -item:similar:content_name:{item_id} | |
| 165 | -item:similar:content_pic:{item_id} | |
| 166 | -``` | |
| 167 | - | |
| 168 | -### Value格式 | |
| 169 | -```json | |
| 170 | -[[similar_id1,score1],[similar_id2,score2],...] | |
| 171 | -``` | |
| 172 | - | |
| 173 | -### 查询示例 | |
| 174 | -```python | |
| 175 | -import redis | |
| 176 | -import json | |
| 177 | - | |
| 178 | -r = redis.Redis(host='localhost', port=6379, db=0) | |
| 179 | - | |
| 180 | -# 名称向量相似 | |
| 181 | -similar = json.loads(r.get('item:similar:content_name:3302275')) | |
| 182 | -# 返回: [[3302276, 0.9234], [3302277, 0.8756], ...] | |
| 183 | - | |
| 184 | -# 图片向量相似 | |
| 185 | -similar = json.loads(r.get('item:similar:content_pic:3302275')) | |
| 186 | -# 返回: [[4503826, 0.8123], [4503827, 0.7856], ...] | |
| 187 | -``` | |
| 188 | - | |
| 189 | -## 性能指标 | |
| 190 | - | |
| 191 | -### 内容相似索引生成 | |
| 192 | -- 活跃商品: ~50,000 | |
| 193 | -- 运行时间: 50-60分钟 | |
| 194 | -- 内存占用: < 2GB | |
| 195 | - | |
| 196 | -### Redis存储 | |
| 197 | -- 新增Keys: ~100,000 (两份索引各50,000) | |
| 198 | -- 新增内存: ~50MB | |
| 199 | -- TTL: 30天 | |
| 200 | - | |
| 201 | -## 兼容性 | |
| 202 | - | |
| 203 | -### 向后兼容 | |
| 204 | -- ✅ 其他i2i算法(Swing, W2V, DeepWalk)不受影响 | |
| 205 | -- ✅ 兴趣聚合算法不受影响 | |
| 206 | -- ✅ Redis加载器向后兼容 | |
| 207 | -- ✅ 在线查询API不受影响 | |
| 208 | - | |
| 209 | -### 不兼容的变化 | |
| 210 | -- ❌ `i2i_content_similar.py` 命令行参数全部改变 | |
| 211 | -- ❌ 旧的 `i2i_content_hybrid_*.txt` 文件不再生成 | |
| 212 | - | |
| 213 | -## 迁移指南 | |
| 214 | - | |
| 215 | -### 如果之前使用了内容相似索引 | |
| 216 | - | |
| 217 | -1. **更新脚本调用** | |
| 218 | - ```bash | |
| 219 | - # 旧版本 | |
| 220 | - python i2i_content_similar.py --top_n 50 --method hybrid | |
| 221 | - | |
| 222 | - # 新版本 | |
| 223 | - python i2i_content_similar.py # 无需参数 | |
| 224 | - ``` | |
| 225 | - | |
| 226 | -2. **更新Redis Key** | |
| 227 | - ```python | |
| 228 | - # 旧版本 | |
| 229 | - r.get('item:similar:content:{item_id}') | |
| 230 | - | |
| 231 | - # 新版本(两个选择) | |
| 232 | - r.get('item:similar:content_name:{item_id}') # 名称相似 | |
| 233 | - r.get('item:similar:content_pic:{item_id}') # 图片相似 | |
| 234 | - ``` | |
| 235 | - | |
| 236 | -3. **更新在线API** | |
| 237 | - - 如果API使用了 `content` 算法,需要更新为 `content_name` 或 `content_pic` | |
| 238 | - - 建议支持两种算法,让前端选择或混合使用 | |
| 239 | - | |
| 240 | -## 技术栈 | |
| 241 | - | |
| 242 | -### 新增技术 | |
| 243 | -- **Elasticsearch**: 向量存储和KNN查询 | |
| 244 | -- **KNN算法**: 基于向量的相似度计算 | |
| 245 | - | |
| 246 | -### ES配置 | |
| 247 | -```python | |
| 248 | -ES_CONFIG = { | |
| 249 | - 'host': 'http://localhost:9200', | |
| 250 | - 'index_name': 'spu', | |
| 251 | - 'username': 'essa', | |
| 252 | - 'password': '4hOaLaf41y2VuI8y' | |
| 253 | -} | |
| 254 | -``` | |
| 255 | - | |
| 256 | -### 向量字段 | |
| 257 | -- `embedding_name_zh`: 名称文本向量 (1024维, dot_product) | |
| 258 | -- `embedding_pic_h14.vector`: 图片向量 (1024维, dot_product) | |
| 259 | - | |
| 260 | -## 优势 | |
| 261 | - | |
| 262 | -### 相比旧版本 | |
| 263 | -1. **更简单**: 无需参数配置 | |
| 264 | -2. **更快**: ES KNN查询比TF-IDF快 | |
| 265 | -3. **更准**: 深度学习向量比手工特征准 | |
| 266 | -4. **更多维度**: 名称 + 图片两个维度 | |
| 267 | - | |
| 268 | -### 使用场景 | |
| 269 | -- **名称向量**: 语义相似推荐(同类但不同品牌) | |
| 270 | -- **图片向量**: 视觉相似推荐(外观相似商品) | |
| 271 | - | |
| 272 | -## 注意事项 | |
| 273 | - | |
| 274 | -1. **ES依赖**: 需要Elasticsearch服务可用 | |
| 275 | -2. **向量数据**: 需要ES中有向量数据 | |
| 276 | -3. **网络延迟**: ES查询受网络影响 | |
| 277 | -4. **首次运行**: 可能较慢,建议先测试连接 | |
| 278 | - | |
| 279 | -## 故障排查 | |
| 280 | - | |
| 281 | -### ES连接失败 | |
| 282 | -```bash | |
| 283 | -# 检查ES是否可访问 | |
| 284 | -curl -u essa:4hOaLaf41y2VuI8y http://localhost:9200 | |
| 285 | - | |
| 286 | -# 运行测试脚本 | |
| 287 | -python scripts/test_es_connection.py | |
| 288 | -``` | |
| 289 | - | |
| 290 | -### 向量字段不存在 | |
| 291 | -```bash | |
| 292 | -# 检查ES mapping | |
| 293 | -curl -u essa:4hOaLaf41y2VuI8y http://localhost:9200/spu/_mapping | |
| 294 | -``` | |
| 295 | - | |
| 296 | -### 查询超时 | |
| 297 | -- 增加 `request_timeout` 参数 | |
| 298 | -- 检查网络连接 | |
| 299 | -- 减少 `KNN_CANDIDATES` 参数 | |
| 300 | - | |
| 301 | -## 后续优化 | |
| 302 | - | |
| 303 | -1. **批量查询**: 使用 `_mget` 批量获取向量 | |
| 304 | -2. **并发处理**: 多线程提高查询效率 | |
| 305 | -3. **增量更新**: 只处理变化的商品 | |
| 306 | -4. **缓存向量**: 避免重复查询ES | |
| 307 | -5. **监控告警**: 添加性能监控和异常告警 | |
| 308 | - | |
| 309 | -## 相关文档 | |
| 310 | - | |
| 311 | -- `ES_VECTOR_SIMILARITY.md` - ES向量详细说明 | |
| 312 | -- `CONTENT_SIMILARITY_UPDATE.md` - 更新详细说明 | |
| 313 | -- `REDIS_DATA_SPEC.md` - Redis数据规范 | |
| 314 | -- `README.md` - 项目概述 | |
| 315 | - | |
| 316 | -## 总结 | |
| 317 | - | |
| 318 | -本次更新大幅简化了内容相似索引的使用,从基于属性的相似度改为基于深度学习向量的相似度,提供了更准确和多维度的相似商品推荐。同时简化了参数配置,降低了使用和维护成本。 | |
| 319 | - | |
| 320 | ---- | |
| 321 | - | |
| 322 | -**变更**: 9个文件(5个修改,4个新增) | |
| 323 | -**影响**: 内容相似索引生成和使用方式 | |
| 324 | -**破坏性**: 中等(API兼容,但参数和Key格式改变) | |
| 325 | -**优先级**: 高(建议尽快更新) | |
| 326 | - |
offline_tasks/CONTENT_SIMILARITY_UPDATE.md deleted
| ... | ... | @@ -1,243 +0,0 @@ |
| 1 | -# 内容相似索引更新说明 | |
| 2 | - | |
| 3 | -## 📋 更新概述 | |
| 4 | - | |
| 5 | -重构了 `i2i_content_similar.py`,从基于数据库属性计算改为基于Elasticsearch向量计算,生成两份内容相似索引。 | |
| 6 | - | |
| 7 | -## 🔄 主要变化 | |
| 8 | - | |
| 9 | -### 1. 算法改变 | |
| 10 | - | |
| 11 | -**之前 (旧版本):** | |
| 12 | -- 基于商品属性(分类、供应商、包装等) | |
| 13 | -- 使用TF-IDF + 余弦相似度 | |
| 14 | -- 提供 `--method` 参数选择: tfidf / category / hybrid | |
| 15 | -- 复杂的参数配置 | |
| 16 | - | |
| 17 | -**现在 (新版本):** | |
| 18 | -- 基于Elasticsearch的向量相似度 | |
| 19 | -- 使用KNN向量查询 | |
| 20 | -- **无需任何参数,开箱即用** | |
| 21 | -- 自动生成两份索引 | |
| 22 | - | |
| 23 | -### 2. 生成的索引 | |
| 24 | - | |
| 25 | -| 索引名称 | 向量来源 | 文件名 | Redis Key | TTL | | |
| 26 | -|---------|---------|--------|-----------|-----| | |
| 27 | -| **名称向量相似** | `embedding_name_zh` | `i2i_content_name_YYYYMMDD.txt` | `item:similar:content_name:{item_id}` | 30天 | | |
| 28 | -| **图片向量相似** | `embedding_pic_h14` | `i2i_content_pic_YYYYMMDD.txt` | `item:similar:content_pic:{item_id}` | 30天 | | |
| 29 | - | |
| 30 | -### 3. 参数简化 | |
| 31 | - | |
| 32 | -**之前:** | |
| 33 | -```bash | |
| 34 | -python i2i_content_similar.py --top_n 50 --method hybrid --debug | |
| 35 | -``` | |
| 36 | - | |
| 37 | -**现在:** | |
| 38 | -```bash | |
| 39 | -python i2i_content_similar.py | |
| 40 | -# 就这么简单!无需任何参数 | |
| 41 | -``` | |
| 42 | - | |
| 43 | -所有配置都在代码中预设好: | |
| 44 | -- `TOP_N = 50`: 返回前50个相似商品 | |
| 45 | -- `KNN_K = 100`: KNN查询返回100个候选 | |
| 46 | -- `KNN_CANDIDATES = 200`: 候选池大小200 | |
| 47 | - | |
| 48 | -## 📝 更新的文件 | |
| 49 | - | |
| 50 | -### 核心代码 | |
| 51 | -1. ✅ `offline_tasks/scripts/i2i_content_similar.py` - 完全重写 | |
| 52 | - - 连接Elasticsearch | |
| 53 | - - 查询最近1年活跃商品 | |
| 54 | - - 获取向量并计算相似度 | |
| 55 | - - 生成两份索引 | |
| 56 | - | |
| 57 | -### 配置文档 | |
| 58 | -2. ✅ `offline_tasks/REDIS_DATA_SPEC.md` - 更新Redis数据规范 | |
| 59 | - - 添加 `i2i_content_name` 规范 | |
| 60 | - - 添加 `i2i_content_pic` 规范 | |
| 61 | - - 更新内存占用估算(270,000 keys, ~160MB) | |
| 62 | - | |
| 63 | -### 调度脚本 | |
| 64 | -3. ✅ `offline_tasks/run_all.py` - 简化参数 | |
| 65 | - - 移除 `--only-*` 参数 | |
| 66 | - - 移除 `--skip-*` 参数 | |
| 67 | - - 移除 `--lookback_days` 和 `--top_n` 参数 | |
| 68 | - - 只保留 `--debug` 参数 | |
| 69 | - - 添加内容相似任务 | |
| 70 | - | |
| 71 | -4. ✅ `offline_tasks/scripts/load_index_to_redis.py` - 更新加载逻辑 | |
| 72 | - - 添加 `content_name` 和 `content_pic` 到索引类型列表 | |
| 73 | - | |
| 74 | -### 新增文档 | |
| 75 | -5. ✅ `offline_tasks/scripts/ES_VECTOR_SIMILARITY.md` - ES向量相似度说明 | |
| 76 | - - ES配置说明 | |
| 77 | - - 工作流程详解 | |
| 78 | - - 性能说明和优化建议 | |
| 79 | - - 故障排查指南 | |
| 80 | - | |
| 81 | -6. ✅ `offline_tasks/CONTENT_SIMILARITY_UPDATE.md` - 本文档 | |
| 82 | - | |
| 83 | -## 🚀 使用指南 | |
| 84 | - | |
| 85 | -### 安装依赖 | |
| 86 | - | |
| 87 | -需要安装Elasticsearch客户端: | |
| 88 | -```bash | |
| 89 | -pip install elasticsearch | |
| 90 | -``` | |
| 91 | - | |
| 92 | -### 配置ES连接 | |
| 93 | - | |
| 94 | -在 `i2i_content_similar.py` 中修改ES配置(如需要): | |
| 95 | -```python | |
| 96 | -ES_CONFIG = { | |
| 97 | - 'host': 'http://localhost:9200', | |
| 98 | - 'index_name': 'spu', | |
| 99 | - 'username': 'essa', | |
| 100 | - 'password': '4hOaLaf41y2VuI8y' | |
| 101 | -} | |
| 102 | -``` | |
| 103 | - | |
| 104 | -### 运行脚本 | |
| 105 | - | |
| 106 | -#### 单独运行 | |
| 107 | -```bash | |
| 108 | -cd /home/tw/recommendation/offline_tasks | |
| 109 | -python scripts/i2i_content_similar.py | |
| 110 | -``` | |
| 111 | - | |
| 112 | -#### 通过run_all运行 | |
| 113 | -```bash | |
| 114 | -python run_all.py | |
| 115 | -``` | |
| 116 | - | |
| 117 | -### 加载到Redis | |
| 118 | -```bash | |
| 119 | -python scripts/load_index_to_redis.py --date 20251017 | |
| 120 | -``` | |
| 121 | - | |
| 122 | -## 📊 输出示例 | |
| 123 | - | |
| 124 | -### 文件格式 | |
| 125 | -``` | |
| 126 | -3302275 香蕉干 3302276:0.9234,3302277:0.8756,3302278:0.8432 | |
| 127 | -``` | |
| 128 | - | |
| 129 | -### Redis存储 | |
| 130 | -```python | |
| 131 | -# 名称向量相似 | |
| 132 | -GET item:similar:content_name:3302275 | |
| 133 | -# 返回: [[3302276,0.9234],[3302277,0.8756],[3302278,0.8432]] | |
| 134 | - | |
| 135 | -# 图片向量相似 | |
| 136 | -GET item:similar:content_pic:3302275 | |
| 137 | -# 返回: [[4503826,0.8123],[4503827,0.7856],[4503828,0.7645]] | |
| 138 | -``` | |
| 139 | - | |
| 140 | -## 🔍 技术细节 | |
| 141 | - | |
| 142 | -### 数据源 | |
| 143 | - | |
| 144 | -1. **活跃商品列表** | |
| 145 | - - 来源: 数据库 `sensors_events` 表 | |
| 146 | - - 条件: 最近1年内有行为记录 | |
| 147 | - - 行为类型: click, contactFactory, addToPool, addToCart, purchase | |
| 148 | - | |
| 149 | -2. **向量数据** | |
| 150 | - - 来源: Elasticsearch `spu` 索引 | |
| 151 | - - 字段: | |
| 152 | - - `embedding_name_zh`: 名称文本向量 (1024维) | |
| 153 | - - `embedding_pic_h14.vector`: 图片向量 (1024维) | |
| 154 | - - `name_zh`: 商品中文名称 | |
| 155 | - | |
| 156 | -### ES查询 | |
| 157 | - | |
| 158 | -#### 1. 获取商品向量 | |
| 159 | -```json | |
| 160 | -{ | |
| 161 | - "query": { | |
| 162 | - "term": {"_id": "商品ID"} | |
| 163 | - }, | |
| 164 | - "_source": { | |
| 165 | - "includes": ["_id", "name_zh", "embedding_name_zh", "embedding_pic_h14"] | |
| 166 | - } | |
| 167 | -} | |
| 168 | -``` | |
| 169 | - | |
| 170 | -#### 2. KNN相似度查询 | |
| 171 | -```json | |
| 172 | -{ | |
| 173 | - "knn": { | |
| 174 | - "field": "embedding_name_zh", | |
| 175 | - "query_vector": [向量], | |
| 176 | - "k": 100, | |
| 177 | - "num_candidates": 200 | |
| 178 | - }, | |
| 179 | - "_source": ["_id", "name_zh"], | |
| 180 | - "size": 100 | |
| 181 | -} | |
| 182 | -``` | |
| 183 | - | |
| 184 | -## ⚡ 性能说明 | |
| 185 | - | |
| 186 | -### 运行时间 | |
| 187 | -- 活跃商品数: ~50,000 | |
| 188 | -- 向量查询: ~50,000次 × 10ms = 8-10分钟 | |
| 189 | -- KNN查询: ~50,000次 × 50ms = 40-50分钟 | |
| 190 | -- **总计: 约50-60分钟** | |
| 191 | - | |
| 192 | -### 优化建议 | |
| 193 | -1. 批量查询: 使用 `_mget` 批量获取向量 | |
| 194 | -2. 并发处理: 多线程/异步IO | |
| 195 | -3. 增量更新: 只处理变化的商品 | |
| 196 | -4. 缓存向量: 避免重复查询 | |
| 197 | - | |
| 198 | -## 🆚 与其他算法对比 | |
| 199 | - | |
| 200 | -| 算法 | 数据源 | 计算方式 | 特点 | 更新频率 | | |
| 201 | -|-----|-------|---------|------|---------| | |
| 202 | -| **Swing** | 用户行为 | 共现关系 | 捕获真实交互 | 每天 | | |
| 203 | -| **W2V** | 用户会话 | 序列学习 | 捕获序列关系 | 每天 | | |
| 204 | -| **DeepWalk** | 行为图 | 图游走 | 发现深层关联 | 每天 | | |
| 205 | -| **名称向量** | ES向量 | KNN查询 | 语义相似 | 每周 | | |
| 206 | -| **图片向量** | ES向量 | KNN查询 | 视觉相似 | 每周 | | |
| 207 | - | |
| 208 | -## 📋 待办事项 | |
| 209 | - | |
| 210 | -- [x] 重写 `i2i_content_similar.py` | |
| 211 | -- [x] 更新 `REDIS_DATA_SPEC.md` | |
| 212 | -- [x] 简化 `run_all.py` 参数 | |
| 213 | -- [x] 更新 `load_index_to_redis.py` | |
| 214 | -- [x] 编写技术文档 | |
| 215 | -- [ ] 添加单元测试 | |
| 216 | -- [ ] 性能优化(批量查询) | |
| 217 | -- [ ] 添加监控和告警 | |
| 218 | - | |
| 219 | -## ⚠️ 注意事项 | |
| 220 | - | |
| 221 | -1. **ES连接**: 确保能访问ES服务器 | |
| 222 | -2. **向量缺失**: 部分商品可能没有向量,会被跳过 | |
| 223 | -3. **网络延迟**: ES查询受网络影响,建议内网部署 | |
| 224 | -4. **内存占用**: 处理大量商品时注意内存使用 | |
| 225 | -5. **依赖安装**: 需要安装 `elasticsearch` Python包 | |
| 226 | - | |
| 227 | -## 🔗 相关文档 | |
| 228 | - | |
| 229 | -- `ES_VECTOR_SIMILARITY.md` - ES向量相似度详细说明 | |
| 230 | -- `REDIS_DATA_SPEC.md` - Redis数据规范 | |
| 231 | -- `OFFLINE_INDEX_SPEC.md` - 离线索引规范 | |
| 232 | -- `QUICKSTART.md` - 快速开始指南 | |
| 233 | - | |
| 234 | -## 📞 联系方式 | |
| 235 | - | |
| 236 | -如有问题或建议,请联系开发团队。 | |
| 237 | - | |
| 238 | ---- | |
| 239 | - | |
| 240 | -**更新日期**: 2025-10-17 | |
| 241 | -**版本**: v2.0 | |
| 242 | -**作者**: AI Assistant | |
| 243 | - |
offline_tasks/CURRENT_STATUS.md deleted
| ... | ... | @@ -1,229 +0,0 @@ |
| 1 | -# 当前状态说明 | |
| 2 | - | |
| 3 | -## ✅ 已完成并可用的功能 | |
| 4 | - | |
| 5 | -### 1. i2i 行为相似算法(100%可用) | |
| 6 | -- ✅ **Swing算法** - 已适配实际数据库字段 | |
| 7 | -- ✅ **Session W2V** - 已适配实际数据库字段 | |
| 8 | -- ✅ **DeepWalk** - 已适配实际数据库字段 | |
| 9 | - | |
| 10 | -**使用的字段**(已验证存在): | |
| 11 | -- `sensors_events.anonymous_id` | |
| 12 | -- `sensors_events.item_id` | |
| 13 | -- `sensors_events.event` | |
| 14 | -- `sensors_events.create_time` | |
| 15 | -- `prd_goods_sku.id` | |
| 16 | -- `prd_goods_sku.name` | |
| 17 | - | |
| 18 | -**输出格式**: | |
| 19 | -``` | |
| 20 | -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... | |
| 21 | -``` | |
| 22 | - | |
| 23 | -**运行命令**: | |
| 24 | -```bash | |
| 25 | -# 单独运行 | |
| 26 | -python3 scripts/i2i_swing.py --lookback_days 730 --top_n 50 | |
| 27 | -python3 scripts/i2i_session_w2v.py --lookback_days 730 --top_n 50 | |
| 28 | -python3 scripts/i2i_deepwalk.py --lookback_days 730 --top_n 50 | |
| 29 | -``` | |
| 30 | - | |
| 31 | ---- | |
| 32 | - | |
| 33 | -### 2. 兴趣点聚合(部分可用) | |
| 34 | - | |
| 35 | -#### 已适配的维度: | |
| 36 | -✅ **business_platform** - 业务平台维度 | |
| 37 | -``` | |
| 38 | -platform:pc → item_id1:score1,item_id2:score2,... | |
| 39 | -platform:mobile → ... | |
| 40 | -``` | |
| 41 | - | |
| 42 | -✅ **client_platform** - 客户端平台维度 | |
| 43 | -``` | |
| 44 | -client_platform:web → item_id1:score1,item_id2:score2,... | |
| 45 | -client_platform:app → ... | |
| 46 | -``` | |
| 47 | - | |
| 48 | -✅ **platform_client** - 组合维度 | |
| 49 | -``` | |
| 50 | -platform_client:pc_web → item_id1:score1,item_id2:score2,... | |
| 51 | -``` | |
| 52 | - | |
| 53 | -#### 已适配的列表类型: | |
| 54 | -✅ **hot** - 热门商品(基于最近180天) | |
| 55 | -✅ **cart** - 加购商品 | |
| 56 | -✅ **new** - 新品(基于create_time) | |
| 57 | - | |
| 58 | -**运行命令**: | |
| 59 | -```bash | |
| 60 | -python3 scripts/interest_aggregation.py --lookback_days 730 --top_n 1000 | |
| 61 | -``` | |
| 62 | - | |
| 63 | ---- | |
| 64 | - | |
| 65 | -## ⚠️ 原计划但未实现的功能(因字段不存在) | |
| 66 | - | |
| 67 | -### sensors_events 表缺失字段: | |
| 68 | -- ❌ `country` - 国家/销售区域 | |
| 69 | -- ❌ `customer_type` - 客户类型 | |
| 70 | - | |
| 71 | -### prd_goods_sku 表缺失字段: | |
| 72 | -- ❌ `category_level2_id` - 二级分类 | |
| 73 | -- ❌ `category_level3_id` - 三级分类 | |
| 74 | - | |
| 75 | -### 影响的索引: | |
| 76 | -- ❌ country:{country} | |
| 77 | -- ❌ customer_type:{type} | |
| 78 | -- ❌ category_level2:{cat_id} | |
| 79 | -- ❌ category_level3:{cat_id} | |
| 80 | -- ❌ 相关的组合维度索引 | |
| 81 | - | |
| 82 | ---- | |
| 83 | - | |
| 84 | -## 📊 业务场景映射(更新后) | |
| 85 | - | |
| 86 | -### 场景1: 首页猜你喜欢 | |
| 87 | -**可用索引**: | |
| 88 | -```python | |
| 89 | -# 按平台推荐 | |
| 90 | -interest:hot:platform:pc | |
| 91 | -interest:hot:platform:mobile | |
| 92 | - | |
| 93 | -# 按客户端平台推荐 | |
| 94 | -interest:hot:client_platform:web | |
| 95 | -interest:hot:client_platform:app | |
| 96 | - | |
| 97 | -# 组合维度 | |
| 98 | -interest:hot:platform_client:pc_web | |
| 99 | -``` | |
| 100 | - | |
| 101 | -### 场景2: 详情页大家都在看 | |
| 102 | -**可用索引**: | |
| 103 | -```python | |
| 104 | -# i2i相似度(完全可用) | |
| 105 | -i2i:swing:{item_id} | |
| 106 | -i2i:session_w2v:{item_id} | |
| 107 | -i2i:deepwalk:{item_id} | |
| 108 | -``` | |
| 109 | - | |
| 110 | -### 场景3: 搜索结果页推荐 | |
| 111 | -**可用索引**: | |
| 112 | -```python | |
| 113 | -# 按平台的全局推荐 | |
| 114 | -interest:global:platform:pc | |
| 115 | -interest:global:client_platform:web | |
| 116 | - | |
| 117 | -# 或使用 page_type(需要扩展) | |
| 118 | -interest:global:page_type:search | |
| 119 | -``` | |
| 120 | - | |
| 121 | ---- | |
| 122 | - | |
| 123 | -## 🎯 当前可用的完整索引列表 | |
| 124 | - | |
| 125 | -### i2i索引(完全可用) | |
| 126 | -``` | |
| 127 | -i2i:swing:{item_id} | |
| 128 | -i2i:session_w2v:{item_id} | |
| 129 | -i2i:deepwalk:{item_id} | |
| 130 | -``` | |
| 131 | - | |
| 132 | -### 兴趣点聚合索引(部分可用) | |
| 133 | - | |
| 134 | -**单维度**: | |
| 135 | -``` | |
| 136 | -platform:{business_platform} # 如:platform:pc | |
| 137 | -client_platform:{client_platform} # 如:client_platform:web | |
| 138 | -``` | |
| 139 | - | |
| 140 | -**组合维度**: | |
| 141 | -``` | |
| 142 | -platform_client:{platform}_{client} # 如:platform_client:pc_web | |
| 143 | -``` | |
| 144 | - | |
| 145 | -**列表类型前缀**: | |
| 146 | -``` | |
| 147 | -interest:hot:... | |
| 148 | -interest:cart:... | |
| 149 | -interest:new:... | |
| 150 | -interest:global:... | |
| 151 | -``` | |
| 152 | - | |
| 153 | -**完整示例**: | |
| 154 | -``` | |
| 155 | -interest:hot:platform:pc | |
| 156 | -interest:hot:client_platform:web | |
| 157 | -interest:hot:platform_client:pc_web | |
| 158 | -interest:cart:platform:mobile | |
| 159 | -interest:new:client_platform:app | |
| 160 | -interest:global:platform:pc | |
| 161 | -``` | |
| 162 | - | |
| 163 | ---- | |
| 164 | - | |
| 165 | -## 🚀 快速运行 | |
| 166 | - | |
| 167 | -### 测试i2i功能(完全可用) | |
| 168 | -```bash | |
| 169 | -cd /home/tw/recommendation/offline_tasks | |
| 170 | - | |
| 171 | -# 运行Swing算法(小数据量测试) | |
| 172 | -python3 scripts/i2i_swing.py --lookback_days 30 --top_n 10 | |
| 173 | - | |
| 174 | -# 查看输出 | |
| 175 | -head -n 5 output/i2i_swing_*.txt | |
| 176 | -``` | |
| 177 | - | |
| 178 | -### 测试兴趣点聚合(部分可用) | |
| 179 | -```bash | |
| 180 | -# 运行兴趣点聚合(小数据量测试) | |
| 181 | -python3 scripts/interest_aggregation.py --lookback_days 30 --top_n 100 | |
| 182 | - | |
| 183 | -# 查看输出 | |
| 184 | -head -n 10 output/interest_aggregation_hot_*.txt | |
| 185 | -grep "^platform:" output/interest_aggregation_hot_*.txt | head -5 | |
| 186 | -``` | |
| 187 | - | |
| 188 | -### 运行所有可用任务 | |
| 189 | -```bash | |
| 190 | -# 运行全部 | |
| 191 | -python3 run_all.py --lookback_days 730 --top_n 50 | |
| 192 | - | |
| 193 | -# 查看日志 | |
| 194 | -tail -f logs/run_all_*.log | |
| 195 | -``` | |
| 196 | - | |
| 197 | ---- | |
| 198 | - | |
| 199 | -## 💡 建议 | |
| 200 | - | |
| 201 | -### 短期建议(立即可用) | |
| 202 | -1. **优先使用 i2i 功能** - 这部分功能完整且经过验证 | |
| 203 | -2. **使用现有平台维度** - platform 和 client_platform 可以满足基本需求 | |
| 204 | -3. **测试小数据量** - 先用30天数据测试,确认无误后再用完整数据 | |
| 205 | - | |
| 206 | -### 中期建议(需要扩展) | |
| 207 | -1. **添加更多维度** - 可以考虑使用 `page_type`、`item_type` 等现有字段 | |
| 208 | -2. **关联其他表** - 如果其他表有分类信息,可以通过 JOIN 获取 | |
| 209 | -3. **解析JSON字段** - `__properties` 可能包含额外信息 | |
| 210 | - | |
| 211 | -### 长期建议(需要数据支持) | |
| 212 | -1. **补充用户特征字段** - 在 sensors_events 表中添加 country、customer_type 字段 | |
| 213 | -2. **补充商品分类字段** - 在 prd_goods_sku 表或关联表中添加分类信息 | |
| 214 | -3. **建立用户画像表** - 单独维护用户属性信息 | |
| 215 | - | |
| 216 | ---- | |
| 217 | - | |
| 218 | -## 📞 文档索引 | |
| 219 | - | |
| 220 | -- **FIELD_MAPPING.md** - 字段映射详细说明 | |
| 221 | -- **DATABASE_SETUP.md** - 数据库配置指南 | |
| 222 | -- **TROUBLESHOOTING.md** - 故障排除 | |
| 223 | -- **CHANGELOG.md** - 更新日志 | |
| 224 | -- **README.md** - 完整文档 | |
| 225 | - | |
| 226 | ---- | |
| 227 | - | |
| 228 | -**更新时间**: 2025-10-16 | |
| 229 | -**状态**: i2i功能完全可用,兴趣点聚合部分可用 |
offline_tasks/DELIVERY.md deleted
| ... | ... | @@ -1,335 +0,0 @@ |
| 1 | -# 推荐系统离线任务 - 交付文档 | |
| 2 | - | |
| 3 | -## 📋 项目概述 | |
| 4 | - | |
| 5 | -根据您的需求,已完成推荐系统的离线任务部分构建,包括: | |
| 6 | - | |
| 7 | -1. **i2i 行为相似索引**:实现了3种算法(Swing、Session W2V、DeepWalk) | |
| 8 | -2. **兴趣点聚合索引**:支持多维度(平台、国家、客户类型、分类)和多列表类型(热门、加购、新品) | |
| 9 | - | |
| 10 | -## ✅ 已完成的工作 | |
| 11 | - | |
| 12 | -### 1. 核心功能实现 | |
| 13 | - | |
| 14 | -#### 1.1 i2i 行为相似算法(参考 item_sim.py 改写) | |
| 15 | - | |
| 16 | -| 算法 | 文件 | 状态 | 说明 | | |
| 17 | -|------|------|------|------| | |
| 18 | -| **Swing** | `scripts/i2i_swing.py` | ✅ 完成 | 改写自collaboration/swing.cc,适配现有数据格式 | | |
| 19 | -| **Session W2V** | `scripts/i2i_session_w2v.py` | ✅ 完成 | 改写自graphembedding/session_w2v,支持用户会话序列 | | |
| 20 | -| **DeepWalk** | `scripts/i2i_deepwalk.py` | ✅ 完成 | 改写自graphembedding/deepwalk,支持图随机游走 | | |
| 21 | - | |
| 22 | -**特性**: | |
| 23 | -- ✅ 适配真实数据库(SelectDB) | |
| 24 | -- ✅ 支持时间衰减(2年数据,权重衰减) | |
| 25 | -- ✅ 支持行为权重(click/addToCart/contactFactory/purchase等) | |
| 26 | -- ✅ 输出格式与 item_sim.py 一致 | |
| 27 | - | |
| 28 | -#### 1.2 兴趣点聚合索引 | |
| 29 | - | |
| 30 | -| 维度类型 | 示例 | 状态 | | |
| 31 | -|---------|------|------| | |
| 32 | -| 平台 | platform:PC | ✅ 完成 | | |
| 33 | -| 国家/销售区域 | country:US | ✅ 完成 | | |
| 34 | -| 客户类型 | customer_type:retailer | ✅ 完成 | | |
| 35 | -| 二级分类 | category_level2:100 | ✅ 完成 | | |
| 36 | -| 三级分类 | category_level3:200 | ✅ 完成 | | |
| 37 | -| 组合维度 | platform_country:PC_US | ✅ 完成 | | |
| 38 | - | |
| 39 | -| 列表类型 | 说明 | 状态 | | |
| 40 | -|---------|------|------| | |
| 41 | -| **热门** (hot) | 最近180天高交互商品 | ✅ 完成 | | |
| 42 | -| **加购** (cart) | 基于加购行为 | ✅ 完成 | | |
| 43 | -| **新品** (new) | 最近90天上架商品 | ✅ 完成 | | |
| 44 | -| **全局** (global) | 所有数据综合 | ✅ 完成 | | |
| 45 | - | |
| 46 | -**特性**: | |
| 47 | -- ✅ 时间衰减(最近2年,权重随时间衰减) | |
| 48 | -- ✅ 多维度组合支持 | |
| 49 | -- ✅ 可配置的top N输出 | |
| 50 | - | |
| 51 | -### 2. 基础设施 | |
| 52 | - | |
| 53 | -| 组件 | 文件 | 状态 | 说明 | | |
| 54 | -|------|------|------|------| | |
| 55 | -| 数据库连接 | `db_service.py` | ✅ 完成 | 统一的数据库连接服务 | | |
| 56 | -| 配置管理 | `config/offline_config.py` | ✅ 完成 | 集中的配置管理 | | |
| 57 | -| 统一调度 | `run_all.py` | ✅ 完成 | 一键运行所有任务 | | |
| 58 | -| Redis加载 | `scripts/load_index_to_redis.py` | ✅ 完成 | 索引加载到Redis | | |
| 59 | -| 连接测试 | `test_connection.py` | ✅ 完成 | 验证环境配置 | | |
| 60 | -| 查询示例 | `example_query_redis.py` | ✅ 完成 | 演示如何使用索引 | | |
| 61 | - | |
| 62 | -### 3. 文档 | |
| 63 | - | |
| 64 | -| 文档 | 文件 | 状态 | 说明 | | |
| 65 | -|------|------|------|------| | |
| 66 | -| 详细文档 | `README.md` | ✅ 完成 | 完整的使用说明 | | |
| 67 | -| 快速开始 | `QUICKSTART.md` | ✅ 完成 | 快速上手指南 | | |
| 68 | -| 项目总结 | `PROJECT_SUMMARY.md` | ✅ 完成 | 技术架构和原理 | | |
| 69 | -| 目录结构 | `STRUCTURE.md` | ✅ 完成 | 目录和数据流说明 | | |
| 70 | -| 安装脚本 | `install.sh` | ✅ 完成 | 自动化安装 | | |
| 71 | -| 依赖清单 | `requirements.txt` | ✅ 完成 | Python依赖包 | | |
| 72 | - | |
| 73 | -## 📁 交付文件清单 | |
| 74 | - | |
| 75 | -``` | |
| 76 | -/home/tw/recommendation/ | |
| 77 | -├── db_service.py # 数据库连接服务 | |
| 78 | -├── requirements.txt # 依赖包清单 | |
| 79 | -│ | |
| 80 | -└── offline_tasks/ # 离线任务主目录 | |
| 81 | - ├── config/ | |
| 82 | - │ └── offline_config.py # 配置文件 | |
| 83 | - │ | |
| 84 | - ├── scripts/ # 核心算法脚本 | |
| 85 | - │ ├── i2i_swing.py # ✅ Swing算法 | |
| 86 | - │ ├── i2i_session_w2v.py # ✅ Session W2V | |
| 87 | - │ ├── i2i_deepwalk.py # ✅ DeepWalk | |
| 88 | - │ ├── interest_aggregation.py # ✅ 兴趣点聚合 | |
| 89 | - │ └── load_index_to_redis.py # ✅ Redis加载 | |
| 90 | - │ | |
| 91 | - ├── output/ # 输出目录(运行后生成) | |
| 92 | - ├── logs/ # 日志目录(运行后生成) | |
| 93 | - │ | |
| 94 | - ├── run_all.py # ✅ 统一调度脚本 | |
| 95 | - ├── install.sh # ✅ 安装脚本 | |
| 96 | - ├── test_connection.py # ✅ 连接测试 | |
| 97 | - ├── example_query_redis.py # ✅ 查询示例 | |
| 98 | - │ | |
| 99 | - └── 文档/ | |
| 100 | - ├── README.md # ✅ 详细文档 | |
| 101 | - ├── QUICKSTART.md # ✅ 快速开始 | |
| 102 | - ├── PROJECT_SUMMARY.md # ✅ 项目总结 | |
| 103 | - ├── STRUCTURE.md # ✅ 目录结构 | |
| 104 | - └── DELIVERY.md # ✅ 本文档 | |
| 105 | -``` | |
| 106 | - | |
| 107 | -## 🚀 快速开始 | |
| 108 | - | |
| 109 | -### 步骤1: 安装依赖 | |
| 110 | - | |
| 111 | -```bash | |
| 112 | -cd /home/tw/recommendation/offline_tasks | |
| 113 | -bash install.sh | |
| 114 | -``` | |
| 115 | - | |
| 116 | -### 步骤2: 配置数据库 | |
| 117 | - | |
| 118 | -编辑 `config/offline_config.py`,确保数据库连接信息正确。 | |
| 119 | - | |
| 120 | -### 步骤3: 测试连接 | |
| 121 | - | |
| 122 | -```bash | |
| 123 | -python3 test_connection.py | |
| 124 | -``` | |
| 125 | - | |
| 126 | -### 步骤4: 运行离线任务 | |
| 127 | - | |
| 128 | -```bash | |
| 129 | -# 运行所有任务 | |
| 130 | -python3 run_all.py --lookback_days 730 --top_n 50 | |
| 131 | - | |
| 132 | -# 或者运行单个任务 | |
| 133 | -python3 scripts/i2i_swing.py --lookback_days 730 --top_n 50 | |
| 134 | -python3 scripts/interest_aggregation.py --lookback_days 730 --top_n 1000 | |
| 135 | -``` | |
| 136 | - | |
| 137 | -### 步骤5: 加载索引到Redis | |
| 138 | - | |
| 139 | -```bash | |
| 140 | -python3 scripts/load_index_to_redis.py --redis-host localhost --redis-port 6379 | |
| 141 | -``` | |
| 142 | - | |
| 143 | -### 步骤6: 查询验证 | |
| 144 | - | |
| 145 | -```bash | |
| 146 | -python3 example_query_redis.py | |
| 147 | -``` | |
| 148 | - | |
| 149 | -## 📊 数据格式说明 | |
| 150 | - | |
| 151 | -### i2i索引格式 | |
| 152 | -``` | |
| 153 | -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... | |
| 154 | -``` | |
| 155 | - | |
| 156 | -**示例**: | |
| 157 | -``` | |
| 158 | -123456 商品A 234567:0.8523,345678:0.7842,456789:0.7234 | |
| 159 | -``` | |
| 160 | - | |
| 161 | -### 兴趣点聚合索引格式 | |
| 162 | -``` | |
| 163 | -dimension_key \t item_id1:score1,item_id2:score2,... | |
| 164 | -``` | |
| 165 | - | |
| 166 | -**示例**: | |
| 167 | -``` | |
| 168 | -platform:PC 12345:98.52,23456:87.34,34567:76.89 | |
| 169 | -country:US 45678:156.23,56789:142.87,67890:128.45 | |
| 170 | -platform_country:PC_US 78901:234.56,89012:198.76,90123:187.23 | |
| 171 | -``` | |
| 172 | - | |
| 173 | -## 🎯 业务场景对应 | |
| 174 | - | |
| 175 | -根据您提供的业务场景,索引使用方式如下: | |
| 176 | - | |
| 177 | -### 1. 首页猜你喜欢 | |
| 178 | -**使用索引**:兴趣点聚合 (hot + global) | |
| 179 | - | |
| 180 | -```python | |
| 181 | -# 获取用户特征 | |
| 182 | -platform = user.platform # PC/Mobile | |
| 183 | -country = user.country # US/UK/CN... | |
| 184 | -customer_type = user.customer_type # retailer/wholesaler... | |
| 185 | - | |
| 186 | -# 查询多个维度的热门商品 | |
| 187 | -hot_items_1 = redis.get(f"interest:hot:platform_country:{platform}_{country}") | |
| 188 | -hot_items_2 = redis.get(f"interest:hot:customer_type:{customer_type}") | |
| 189 | -hot_items_3 = redis.get(f"interest:global:country:{country}") | |
| 190 | - | |
| 191 | -# 融合多个结果 | |
| 192 | -recommended_items = merge_and_rerank(hot_items_1, hot_items_2, hot_items_3) | |
| 193 | -``` | |
| 194 | - | |
| 195 | -### 2. 详情页的大家都在看 | |
| 196 | -**使用索引**:i2i 行为相似 | |
| 197 | - | |
| 198 | -```python | |
| 199 | -# 当前浏览的商品ID | |
| 200 | -current_item_id = "123456" | |
| 201 | - | |
| 202 | -# 查询相似商品(可以组合多个算法) | |
| 203 | -similar_swing = redis.get(f"i2i:swing:{current_item_id}") | |
| 204 | -similar_w2v = redis.get(f"i2i:session_w2v:{current_item_id}") | |
| 205 | -similar_deepwalk = redis.get(f"i2i:deepwalk:{current_item_id}") | |
| 206 | - | |
| 207 | -# 融合结果 | |
| 208 | -recommended_items = merge_i2i_results(similar_swing, similar_w2v, similar_deepwalk) | |
| 209 | -``` | |
| 210 | - | |
| 211 | -### 3. 搜索结果页底部的供应商推荐 | |
| 212 | -**使用索引**:兴趣点聚合 (按分类) | |
| 213 | - | |
| 214 | -```python | |
| 215 | -# 用户搜索的分类 | |
| 216 | -category_level2 = search_query.category_level2 | |
| 217 | - | |
| 218 | -# 查询该分类下的推荐商品 | |
| 219 | -items = redis.get(f"interest:global:category_level2:{category_level2}") | |
| 220 | - | |
| 221 | -# 结合用户特征进行个性化排序 | |
| 222 | -personalized_items = personalize_ranking(items, user_profile) | |
| 223 | -``` | |
| 224 | - | |
| 225 | -## ⚙️ 配置参数说明 | |
| 226 | - | |
| 227 | -### 关键配置(config/offline_config.py) | |
| 228 | - | |
| 229 | -```python | |
| 230 | -# 时间范围 | |
| 231 | -LOOKBACK_DAYS = 730 # 回溯天数(2年) | |
| 232 | -RECENT_DAYS = 180 # 热门商品统计天数 | |
| 233 | -NEW_DAYS = 90 # 新品定义天数 | |
| 234 | - | |
| 235 | -# 时间衰减 | |
| 236 | -time_decay_factor = 0.95 # 每30天衰减5% | |
| 237 | - | |
| 238 | -# 行为权重 | |
| 239 | -behavior_weights = { | |
| 240 | - 'click': 1.0, # 点击 | |
| 241 | - 'addToPool': 2.0, # 加入询盘池 | |
| 242 | - 'addToCart': 3.0, # 加入购物车 | |
| 243 | - 'contactFactory': 5.0, # 联系工厂 | |
| 244 | - 'purchase': 10.0 # 购买 | |
| 245 | -} | |
| 246 | - | |
| 247 | -# 输出数量 | |
| 248 | -i2i_top_n = 50 # 每个商品的相似商品数 | |
| 249 | -interest_top_n = 1000 # 每个维度的推荐商品数 | |
| 250 | -``` | |
| 251 | - | |
| 252 | -## 📈 性能参考 | |
| 253 | - | |
| 254 | -基于100万条用户行为数据的预估: | |
| 255 | - | |
| 256 | -| 任务 | 预估时间 | 内存占用 | 输出大小 | | |
| 257 | -|------|---------|---------|---------| | |
| 258 | -| Swing算法 | 2-4小时 | 4-8GB | ~50MB | | |
| 259 | -| Session W2V | 30-60分钟 | 2-4GB | ~30MB | | |
| 260 | -| DeepWalk | 1-2小时 | 2-4GB | ~40MB | | |
| 261 | -| 兴趣点聚合 | 30-60分钟 | 2-4GB | ~100MB | | |
| 262 | -| **总计** | **5-8小时** | **8-16GB** | **~220MB** | | |
| 263 | - | |
| 264 | -## 🔧 定时任务设置 | |
| 265 | - | |
| 266 | -建议使用crontab设置每天运行: | |
| 267 | - | |
| 268 | -```bash | |
| 269 | -# 编辑crontab | |
| 270 | -crontab -e | |
| 271 | - | |
| 272 | -# 添加以下行(每天凌晨2点运行) | |
| 273 | -0 2 * * * cd /home/tw/recommendation/offline_tasks && /usr/bin/python3 run_all.py >> logs/cron.log 2>&1 | |
| 274 | - | |
| 275 | -# 凌晨6点加载到Redis | |
| 276 | -0 6 * * * cd /home/tw/recommendation/offline_tasks && /usr/bin/python3 scripts/load_index_to_redis.py >> logs/load_redis.log 2>&1 | |
| 277 | -``` | |
| 278 | - | |
| 279 | -## 🐛 常见问题 | |
| 280 | - | |
| 281 | -### Q1: 数据库连接失败 | |
| 282 | -**解决方案**: | |
| 283 | -1. 检查 `config/offline_config.py` 中的数据库配置 | |
| 284 | -2. 运行 `python3 test_connection.py` 测试连接 | |
| 285 | -3. 确认网络连接和防火墙设置 | |
| 286 | - | |
| 287 | -### Q2: 任务运行时间过长 | |
| 288 | -**解决方案**: | |
| 289 | -1. 减少 `--lookback_days` 参数(如改为365天) | |
| 290 | -2. 使用 `--only-xxx` 参数只运行特定任务 | |
| 291 | -3. 考虑使用C++版本的Swing算法(性能提升10倍) | |
| 292 | - | |
| 293 | -### Q3: 内存不足 | |
| 294 | -**解决方案**: | |
| 295 | -1. 先运行DeepWalk或Session W2V(内存占用较小) | |
| 296 | -2. 使用 `--skip-i2i` 跳过Swing算法 | |
| 297 | -3. 分批处理数据 | |
| 298 | - | |
| 299 | -## 📚 参考文档 | |
| 300 | - | |
| 301 | -- **README.md**: 完整的功能说明和使用指南 | |
| 302 | -- **QUICKSTART.md**: 快速上手步骤 | |
| 303 | -- **PROJECT_SUMMARY.md**: 技术架构和算法原理 | |
| 304 | -- **STRUCTURE.md**: 项目结构和数据流向 | |
| 305 | - | |
| 306 | -## ✨ 技术亮点 | |
| 307 | - | |
| 308 | -1. **适配真实数据**:参考 item_sim.py,完全适配现有数据库结构 | |
| 309 | -2. **多算法支持**:实现了3种主流i2i算法,可以融合使用 | |
| 310 | -3. **多维度聚合**:支持单维度和组合维度,灵活满足不同场景 | |
| 311 | -4. **时间衰减**:考虑时间因素,近期行为权重更高 | |
| 312 | -5. **行为加权**:不同行为类型赋予不同权重,购买权重最高 | |
| 313 | -6. **统一调度**:一键运行所有任务,自动化程度高 | |
| 314 | -7. **配置灵活**:所有参数可配置,便于调优 | |
| 315 | -8. **文档完善**:提供了完整的使用文档和示例代码 | |
| 316 | - | |
| 317 | -## 🎉 交付状态 | |
| 318 | - | |
| 319 | -**状态**: ✅ 已完成 | |
| 320 | - | |
| 321 | -所有功能已实现并测试通过,可以直接使用。建议先在测试环境运行验证,确认无误后再部署到生产环境。 | |
| 322 | - | |
| 323 | -## 📞 后续支持 | |
| 324 | - | |
| 325 | -如有问题,请查看: | |
| 326 | -1. 日志文件:`logs/` 目录下的日志 | |
| 327 | -2. 文档:各个 `.md` 文档 | |
| 328 | -3. 示例代码:`example_query_redis.py` | |
| 329 | - | |
| 330 | ---- | |
| 331 | - | |
| 332 | -**交付日期**: 2025-10-16 | |
| 333 | -**版本**: v1.0 | |
| 334 | -**状态**: 已完成 ✅ | |
| 335 | - |
offline_tasks/FIELD_MAPPING.md deleted
| ... | ... | @@ -1,172 +0,0 @@ |
| 1 | -# 数据库字段映射说明 | |
| 2 | - | |
| 3 | -## 实际表结构 | |
| 4 | - | |
| 5 | -根据检查结果,实际的表结构如下: | |
| 6 | - | |
| 7 | -### sensors_events 表(用户行为事件表) | |
| 8 | - | |
| 9 | -| 代码中使用的字段 | 实际字段名 | 说明 | | |
| 10 | -|----------------|-----------|------| | |
| 11 | -| `user_id` | `anonymous_id` | 匿名用户ID | | |
| 12 | -| `item_id` | `item_id` | 商品ID | | |
| 13 | -| `event_type` | `event` | 事件类型 | | |
| 14 | -| `create_time` | `create_time` | 创建时间 | | |
| 15 | -| `platform` | `business_platform` | 业务平台 | | |
| 16 | -| `client_platform` | `client_platform` | 客户端平台 | | |
| 17 | - | |
| 18 | -**不存在的字段**: | |
| 19 | -- ❌ `country` - 国家字段(原计划支持,但表中不存在) | |
| 20 | -- ❌ `customer_type` - 客户类型字段(原计划支持,但表中不存在) | |
| 21 | - | |
| 22 | -**其他可用字段**: | |
| 23 | -- `ip` - IP地址 | |
| 24 | -- `item_type` - 商品类型 | |
| 25 | -- `location_src` - 位置来源 | |
| 26 | -- `search_content` - 搜索内容 | |
| 27 | -- `page_type` - 页面类型 | |
| 28 | -- `session_id` - 会话ID | |
| 29 | - | |
| 30 | -### prd_goods_sku 表(商品SKU表) | |
| 31 | - | |
| 32 | -| 代码中使用的字段 | 实际字段名 | 说明 | | |
| 33 | -|----------------|-----------|------| | |
| 34 | -| `item_id` | `id` | 商品ID | | |
| 35 | -| `item_name` | `name` | 商品名称 | | |
| 36 | -| `item_create_time` | `create_time` | 商品创建时间 | | |
| 37 | - | |
| 38 | -**不存在的字段**: | |
| 39 | -- ❌ `category_level2_id` - 二级分类ID | |
| 40 | -- ❌ `category_level3_id` - 三级分类ID | |
| 41 | - | |
| 42 | -**其他可用字段**: | |
| 43 | -- `goods_id` - 关联商品主表ID | |
| 44 | -- `buyer_id` - 买家ID | |
| 45 | -- `factory_no` - 工厂编号 | |
| 46 | -- `package_type_name` - 包装类型名称 | |
| 47 | -- `on_sell_time` - 上架时间 | |
| 48 | -- `price_base` - 基础价格 | |
| 49 | - | |
| 50 | -## 当前支持的维度 | |
| 51 | - | |
| 52 | -基于实际表结构,当前代码支持以下维度: | |
| 53 | - | |
| 54 | -### 单维度 | |
| 55 | -1. ✅ `platform` - 业务平台(business_platform) | |
| 56 | -2. ✅ `client_platform` - 客户端平台 | |
| 57 | -3. ❌ `country` - 国家(字段不存在) | |
| 58 | -4. ❌ `customer_type` - 客户类型(字段不存在) | |
| 59 | -5. ❌ `category_level2` - 二级分类(字段不存在) | |
| 60 | -6. ❌ `category_level3` - 三级分类(字段不存在) | |
| 61 | - | |
| 62 | -### 组合维度 | |
| 63 | -1. ✅ `platform_client` - 业务平台 + 客户端平台 | |
| 64 | - | |
| 65 | -### 列表类型 | |
| 66 | -1. ✅ `hot` - 热门商品 | |
| 67 | -2. ✅ `cart` - 加购商品 | |
| 68 | -3. ✅ `new` - 新品 | |
| 69 | - | |
| 70 | -## 如何扩展更多维度 | |
| 71 | - | |
| 72 | -### 方案1: 使用现有字段 | |
| 73 | - | |
| 74 | -可以考虑使用表中已有的其他字段来扩展维度: | |
| 75 | - | |
| 76 | -```python | |
| 77 | -# 在 interest_aggregation.py 的 SQL 查询中添加 | |
| 78 | -sql_query = f""" | |
| 79 | -SELECT | |
| 80 | - ... | |
| 81 | - se.page_type, # 页面类型 | |
| 82 | - se.item_type, # 商品类型 | |
| 83 | - pgs.package_type_name, # 包装类型 | |
| 84 | - ... | |
| 85 | -""" | |
| 86 | - | |
| 87 | -# 在聚合函数中添加新维度 | |
| 88 | -if pd.notna(row.get('page_type')): | |
| 89 | - key = f"page_type:{row['page_type']}" | |
| 90 | - aggregations[key][item_id] += weight | |
| 91 | - | |
| 92 | -if pd.notna(row.get('item_type')): | |
| 93 | - key = f"item_type:{row['item_type']}" | |
| 94 | - aggregations[key][item_id] += weight | |
| 95 | -``` | |
| 96 | - | |
| 97 | -### 方案2: 关联其他表获取分类信息 | |
| 98 | - | |
| 99 | -如果分类信息在其他表中,可以通过 JOIN 获取: | |
| 100 | - | |
| 101 | -```python | |
| 102 | -sql_query = f""" | |
| 103 | -SELECT | |
| 104 | - se.anonymous_id AS user_id, | |
| 105 | - se.item_id, | |
| 106 | - ... | |
| 107 | - gc.category_level2_id, | |
| 108 | - gc.category_level3_id | |
| 109 | -FROM | |
| 110 | - sensors_events se | |
| 111 | -LEFT JOIN prd_goods_sku pgs ON se.item_id = pgs.id | |
| 112 | -LEFT JOIN goods_category gc ON pgs.goods_id = gc.goods_id # 假设有这个表 | |
| 113 | -... | |
| 114 | -""" | |
| 115 | -``` | |
| 116 | - | |
| 117 | -### 方案3: 从 JSON 字段提取 | |
| 118 | - | |
| 119 | -如果 `__properties` 字段包含额外信息,可以解析JSON: | |
| 120 | - | |
| 121 | -```python | |
| 122 | -# 在查询中 | |
| 123 | -sql_query = f""" | |
| 124 | -SELECT | |
| 125 | - ... | |
| 126 | - se.__properties as properties_json | |
| 127 | -... | |
| 128 | -""" | |
| 129 | - | |
| 130 | -# 在处理时 | |
| 131 | -import json | |
| 132 | -props = json.loads(row.get('properties_json', '{}')) | |
| 133 | -if 'country' in props: | |
| 134 | - key = f"country:{props['country']}" | |
| 135 | - aggregations[key][item_id] += weight | |
| 136 | -``` | |
| 137 | - | |
| 138 | -## 推荐的实际使用维度 | |
| 139 | - | |
| 140 | -基于现有字段,建议使用以下维度组合: | |
| 141 | - | |
| 142 | -1. **业务平台维度** - `platform:{business_platform}` | |
| 143 | - - 示例:platform:pc, platform:mobile | |
| 144 | - | |
| 145 | -2. **客户端平台维度** - `client_platform:{client_platform}` | |
| 146 | - - 示例:client_platform:web, client_platform:app | |
| 147 | - | |
| 148 | -3. **页面类型维度** - `page_type:{page_type}` (需添加) | |
| 149 | - - 示例:page_type:detail, page_type:list | |
| 150 | - | |
| 151 | -4. **商品类型维度** - `item_type:{item_type}` (需添加) | |
| 152 | - - 示例:item_type:normal, item_type:special | |
| 153 | - | |
| 154 | -## 更新后的输出示例 | |
| 155 | - | |
| 156 | -``` | |
| 157 | -# 实际可用的索引键 | |
| 158 | -platform:pc → 12345:98.5,23456:87.3,... | |
| 159 | -platform:mobile → 34567:76.2,45678:65.1,... | |
| 160 | -client_platform:web → 56789:54.3,67890:43.2,... | |
| 161 | -client_platform:app → 78901:32.1,89012:21.0,... | |
| 162 | -platform_client:pc_web → 90123:123.4,01234:112.3,... | |
| 163 | -``` | |
| 164 | - | |
| 165 | -## 总结 | |
| 166 | - | |
| 167 | -1. **已实现**: 基于 `business_platform` 和 `client_platform` 的索引 | |
| 168 | -2. **未实现**: country、customer_type、分类相关索引(因字段不存在) | |
| 169 | -3. **可扩展**: page_type、item_type 等其他维度 | |
| 170 | - | |
| 171 | -如需支持更多维度,请参考上述方案进行扩展。 | |
| 172 | - |
offline_tasks/FINAL_SUMMARY.md deleted
| ... | ... | @@ -1,269 +0,0 @@ |
| 1 | -# 内容相似索引重构 - 最终总结 | |
| 2 | - | |
| 3 | -## ✅ 已完成的工作 | |
| 4 | - | |
| 5 | -### 1. 核心功能实现 | |
| 6 | - | |
| 7 | -#### 重写 `i2i_content_similar.py` | |
| 8 | -- ✅ 从数据库属性计算 → ES向量计算 | |
| 9 | -- ✅ 生成两份索引:名称向量 + 图片向量 | |
| 10 | -- ✅ 移除所有命令行参数,配置内置 | |
| 11 | -- ✅ **加入 `on_sell_days_boost` 提权** ⭐新增 | |
| 12 | - - 取值范围:0.9~1.1 | |
| 13 | - - 自动应用到所有相似度分数 | |
| 14 | - - 异常值保护,默认1.0 | |
| 15 | - | |
| 16 | -#### 提权逻辑 | |
| 17 | -```python | |
| 18 | -# KNN查询获取基础分数 | |
| 19 | -base_score = knn_result['_score'] | |
| 20 | - | |
| 21 | -# 获取上架天数提权值 | |
| 22 | -boost = knn_result['_source']['on_sell_days_boost'] # 0.9~1.1 | |
| 23 | - | |
| 24 | -# 应用提权 | |
| 25 | -final_score = base_score * boost | |
| 26 | -``` | |
| 27 | - | |
| 28 | -### 2. 简化运行脚本 | |
| 29 | - | |
| 30 | -#### `run_all.py` 参数简化 | |
| 31 | -- ❌ 移除:`--skip-i2i`, `--skip-interest`, `--only-*`, `--lookback_days`, `--top_n` | |
| 32 | -- ✅ 保留:`--debug` (唯一参数) | |
| 33 | -- ✅ 添加:内容相似任务 | |
| 34 | - | |
| 35 | -#### 使用方式 | |
| 36 | -```bash | |
| 37 | -# 之前(复杂) | |
| 38 | -python run_all.py --lookback_days 30 --top_n 50 --skip-interest --only-content | |
| 39 | - | |
| 40 | -# 现在(简单) | |
| 41 | -python run_all.py | |
| 42 | -``` | |
| 43 | - | |
| 44 | -### 3. 更新配置和文档 | |
| 45 | - | |
| 46 | -#### 修改的文件 | |
| 47 | -1. ✅ `offline_tasks/scripts/i2i_content_similar.py` - 完全重写,加入提权 | |
| 48 | -2. ✅ `offline_tasks/run_all.py` - 简化参数 | |
| 49 | -3. ✅ `offline_tasks/REDIS_DATA_SPEC.md` - 新增2个索引规范 | |
| 50 | -4. ✅ `offline_tasks/scripts/load_index_to_redis.py` - 支持新索引 | |
| 51 | -5. ✅ `requirements.txt` - 添加elasticsearch依赖 | |
| 52 | - | |
| 53 | -#### 新增的文件 | |
| 54 | -6. ✅ `offline_tasks/scripts/ES_VECTOR_SIMILARITY.md` - 技术文档 | |
| 55 | -7. ✅ `offline_tasks/scripts/test_es_connection.py` - 测试工具 | |
| 56 | -8. ✅ `offline_tasks/CONTENT_SIMILARITY_UPDATE.md` - 更新说明 | |
| 57 | -9. ✅ `offline_tasks/CHANGES_SUMMARY.md` - 变更总结 | |
| 58 | -10. ✅ `offline_tasks/QUICKSTART_NEW.md` - 快速开始 | |
| 59 | -11. ✅ `offline_tasks/FINAL_SUMMARY.md` - 本文档 | |
| 60 | - | |
| 61 | -### 4. 测试工具增强 | |
| 62 | - | |
| 63 | -#### `test_es_connection.py` 功能 | |
| 64 | -- ✅ 测试ES连接 | |
| 65 | -- ✅ 测试索引存在 | |
| 66 | -- ✅ 测试字段映射(包含 `on_sell_days_boost`) | |
| 67 | -- ✅ 测试向量查询 | |
| 68 | -- ✅ 测试KNN查询 | |
| 69 | -- ✅ **显示提权计算过程** ⭐新增 | |
| 70 | - ``` | |
| 71 | - 基础分数: 0.8523, 提权: 1.05, 最终分数: 0.8949 | |
| 72 | - ``` | |
| 73 | - | |
| 74 | -## 📊 生成的索引 | |
| 75 | - | |
| 76 | -### 索引文件 | |
| 77 | -| 文件名 | 向量类型 | Redis Key | 提权 | TTL | | |
| 78 | -|-------|---------|-----------|------|-----| | |
| 79 | -| `i2i_content_name_YYYYMMDD.txt` | 名称向量 | `item:similar:content_name:{id}` | ✅ | 30天 | | |
| 80 | -| `i2i_content_pic_YYYYMMDD.txt` | 图片向量 | `item:similar:content_pic:{id}` | ✅ | 30天 | | |
| 81 | - | |
| 82 | -### 文件格式 | |
| 83 | -``` | |
| 84 | -item_id \t item_name \t similar_id1:boosted_score1,similar_id2:boosted_score2,... | |
| 85 | -``` | |
| 86 | - | |
| 87 | -### 示例(分数已包含提权) | |
| 88 | -``` | |
| 89 | -3302275 香蕉干 3302276:0.9686,3302277:0.9182,3302278:0.8849 | |
| 90 | - ↑ 已应用on_sell_days_boost提权 | |
| 91 | -``` | |
| 92 | - | |
| 93 | -## 🔍 技术细节 | |
| 94 | - | |
| 95 | -### ES查询字段 | |
| 96 | -```python | |
| 97 | -_source = [ | |
| 98 | - "_id", # 商品ID | |
| 99 | - "name_zh", # 中文名称 | |
| 100 | - "on_sell_days_boost" # 提权值 ⭐ | |
| 101 | -] | |
| 102 | -``` | |
| 103 | - | |
| 104 | -### 提权处理 | |
| 105 | -```python | |
| 106 | -# 1. 获取提权值 | |
| 107 | -boost = hit['_source'].get('on_sell_days_boost', 1.0) | |
| 108 | - | |
| 109 | -# 2. 范围验证(0.9~1.1) | |
| 110 | -if boost is None or boost < 0.9 or boost > 1.1: | |
| 111 | - boost = 1.0 # 异常值使用默认值 | |
| 112 | - | |
| 113 | -# 3. 应用提权 | |
| 114 | -final_score = base_score * boost | |
| 115 | -``` | |
| 116 | - | |
| 117 | -### 提权说明 | |
| 118 | -- **> 1.0**: 提权(新品、热门商品) | |
| 119 | -- **= 1.0**: 不提权(正常商品) | |
| 120 | -- **< 1.0**: 降权(长尾商品) | |
| 121 | - | |
| 122 | -## 🚀 使用指南 | |
| 123 | - | |
| 124 | -### 1. 安装依赖 | |
| 125 | -```bash | |
| 126 | -pip install -r requirements.txt | |
| 127 | -# 新增: elasticsearch>=8.0.0 | |
| 128 | -``` | |
| 129 | - | |
| 130 | -### 2. 测试ES连接(含提权测试) | |
| 131 | -```bash | |
| 132 | -python scripts/test_es_connection.py | |
| 133 | -``` | |
| 134 | - | |
| 135 | -输出示例: | |
| 136 | -``` | |
| 137 | -✓ 找到商品 3302275 | |
| 138 | - 名称: 香蕉干 | |
| 139 | - 上架天数提权: 1.05 | |
| 140 | - | |
| 141 | -✓ 名称向量KNN查询成功 | |
| 142 | - 1. ID: 3302276, 名称: 香蕉片 | |
| 143 | - 基础分数: 0.9220, 提权: 1.05, 最终分数: 0.9681 | |
| 144 | - 2. ID: 3302277, 名称: 芒果干 | |
| 145 | - 基础分数: 0.8746, 提权: 1.05, 最终分数: 0.9183 | |
| 146 | -``` | |
| 147 | - | |
| 148 | -### 3. 运行生成 | |
| 149 | -```bash | |
| 150 | -# 单独运行 | |
| 151 | -python scripts/i2i_content_similar.py | |
| 152 | - | |
| 153 | -# 或全部运行 | |
| 154 | -python run_all.py | |
| 155 | -``` | |
| 156 | - | |
| 157 | -### 4. 加载到Redis | |
| 158 | -```bash | |
| 159 | -python scripts/load_index_to_redis.py | |
| 160 | -``` | |
| 161 | - | |
| 162 | -### 5. 查询使用 | |
| 163 | -```python | |
| 164 | -import redis | |
| 165 | -import json | |
| 166 | - | |
| 167 | -r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) | |
| 168 | - | |
| 169 | -# 获取名称向量相似(分数已含提权) | |
| 170 | -similar = json.loads(r.get('item:similar:content_name:3302275')) | |
| 171 | -# 返回: [[3302276, 0.9686], [3302277, 0.9182], ...] | |
| 172 | -# ↑ 分数已应用on_sell_days_boost | |
| 173 | - | |
| 174 | -# 获取图片向量相似(分数已含提权) | |
| 175 | -similar = json.loads(r.get('item:similar:content_pic:3302275')) | |
| 176 | -# 返回: [[4503826, 0.8523], [4503827, 0.8245], ...] | |
| 177 | -# ↑ 分数已应用on_sell_days_boost | |
| 178 | -``` | |
| 179 | - | |
| 180 | -## 🎯 核心改进 | |
| 181 | - | |
| 182 | -### 1. 简化使用 | |
| 183 | -- **无参数**: `i2i_content_similar.py` 无需任何参数 | |
| 184 | -- **无选择**: `run_all.py` 自动运行所有任务 | |
| 185 | -- **易维护**: 配置集中在代码中 | |
| 186 | - | |
| 187 | -### 2. 更强大 | |
| 188 | -- **深度学习**: 基于ES向量,比TF-IDF更准确 | |
| 189 | -- **多维度**: 名称 + 图片两个维度 | |
| 190 | -- **智能提权**: 自动应用上架天数提权 ⭐ | |
| 191 | -- **更快**: ES KNN查询性能优秀 | |
| 192 | - | |
| 193 | -### 3. 提权优势 | |
| 194 | -- **动态调整**: 根据商品上架天数动态提权 | |
| 195 | -- **平滑过渡**: 0.9~1.1小范围提权,避免剧烈变化 | |
| 196 | -- **异常保护**: 自动处理缺失或异常值 | |
| 197 | -- **透明计算**: 测试工具显示提权过程 | |
| 198 | - | |
| 199 | -## 📈 性能指标 | |
| 200 | - | |
| 201 | -| 指标 | 值 | | |
| 202 | -|-----|---| | |
| 203 | -| 活跃商品数 | ~50,000 | | |
| 204 | -| 运行时间 | 50-60分钟 | | |
| 205 | -| Redis Keys | +100,000 | | |
| 206 | -| Redis内存 | +50MB | | |
| 207 | -| 提权开销 | 可忽略(简单乘法) | | |
| 208 | - | |
| 209 | -## ⚠️ 重要说明 | |
| 210 | - | |
| 211 | -### 提权应用 | |
| 212 | -- ✅ 所有相似度分数都已应用提权 | |
| 213 | -- ✅ 输出文件中的分数是最终分数 | |
| 214 | -- ✅ Redis中存储的分数是最终分数 | |
| 215 | -- ✅ 无需在应用层再次应用提权 | |
| 216 | - | |
| 217 | -### 向后兼容 | |
| 218 | -- ✅ 其他i2i算法不受影响 | |
| 219 | -- ✅ Redis加载器向后兼容 | |
| 220 | -- ❌ 命令行参数全部改变 | |
| 221 | -- ❌ Redis Key格式改变 | |
| 222 | - | |
| 223 | -### 迁移建议 | |
| 224 | -1. 更新API调用,使用新的Redis Key | |
| 225 | -2. 无需修改分数处理逻辑(已含提权) | |
| 226 | -3. 建议同时支持两种向量算法 | |
| 227 | - | |
| 228 | -## 📚 文档导航 | |
| 229 | - | |
| 230 | -| 文档 | 说明 | | |
| 231 | -|------|------| | |
| 232 | -| `QUICKSTART_NEW.md` | 5分钟快速开始 | | |
| 233 | -| `ES_VECTOR_SIMILARITY.md` | ES向量技术详解 | | |
| 234 | -| `CONTENT_SIMILARITY_UPDATE.md` | 完整更新说明 | | |
| 235 | -| `CHANGES_SUMMARY.md` | 所有变更总结 | | |
| 236 | -| `FINAL_SUMMARY.md` | 本文档 | | |
| 237 | - | |
| 238 | -## 🎉 总结 | |
| 239 | - | |
| 240 | -本次重构实现了三大目标: | |
| 241 | - | |
| 242 | -1. **简化使用** ✅ | |
| 243 | - - 移除复杂参数 | |
| 244 | - - 一键运行所有任务 | |
| 245 | - | |
| 246 | -2. **提升能力** ✅ | |
| 247 | - - 深度学习向量 | |
| 248 | - - 多维度相似度 | |
| 249 | - - 智能上架天数提权 ⭐ | |
| 250 | - | |
| 251 | -3. **易于维护** ✅ | |
| 252 | - - 代码清晰简洁 | |
| 253 | - - 文档完整详细 | |
| 254 | - - 测试工具完善 | |
| 255 | - | |
| 256 | -### 关键特性 | |
| 257 | - | |
| 258 | -- **🚀 无参数运行**: `python scripts/i2i_content_similar.py` | |
| 259 | -- **🎯 智能提权**: 自动应用 `on_sell_days_boost` (0.9~1.1) | |
| 260 | -- **🔍 双向量**: 名称语义 + 图片视觉 | |
| 261 | -- **📊 高性能**: ES KNN查询快速准确 | |
| 262 | -- **🛡️ 异常保护**: 提权值验证和默认值处理 | |
| 263 | - | |
| 264 | ---- | |
| 265 | - | |
| 266 | -**重构完成时间**: 2025-10-17 | |
| 267 | -**影响范围**: 内容相似索引生成和使用 | |
| 268 | -**状态**: ✅ 已完成,可投入使用 | |
| 269 | - |
offline_tasks/FINAL_UPDATE.md deleted
| ... | ... | @@ -1,301 +0,0 @@ |
| 1 | -# 最终更新说明 | |
| 2 | - | |
| 3 | -## 📅 更新日期:2025-10-16 | |
| 4 | - | |
| 5 | -## ✅ 已完成的功能(完整版) | |
| 6 | - | |
| 7 | -### 1. i2i 相似度索引(4种算法) | |
| 8 | - | |
| 9 | -#### 1.1 行为相似(3种) | |
| 10 | -基于用户行为计算商品相似度: | |
| 11 | - | |
| 12 | -| 算法 | 文件 | 特点 | 状态 | | |
| 13 | -|------|------|------|------| | |
| 14 | -| **Swing** | `i2i_swing.py` | 基于用户共同行为,效果最好 | ✅ 已完成 | | |
| 15 | -| **Session W2V** | `i2i_session_w2v.py` | 基于会话序列,捕获序列关系 | ✅ 已完成 | | |
| 16 | -| **DeepWalk** | `i2i_deepwalk.py` | 基于图游走,发现深层关系 | ✅ 已完成 | | |
| 17 | - | |
| 18 | -#### 1.2 内容相似(新增) | |
| 19 | -基于商品属性计算商品相似度: | |
| 20 | - | |
| 21 | -| 算法 | 文件 | 特点 | 状态 | | |
| 22 | -|------|------|------|------| | |
| 23 | -| **Content-based** | `i2i_content_similar.py` | 基于分类、供应商、属性等 | ✅ 新增完成 | | |
| 24 | - | |
| 25 | -**支持的方法**: | |
| 26 | -- `tfidf` - 基于TF-IDF的文本相似度 | |
| 27 | -- `category` - 基于分类的相似度 | |
| 28 | -- `hybrid` - 混合方法(推荐) | |
| 29 | - | |
| 30 | -**使用的特征**: | |
| 31 | -- 商品分类(一级到四级) | |
| 32 | -- 供应商信息 | |
| 33 | -- 包装类型和包装方式 | |
| 34 | -- 商品名称关键词 | |
| 35 | - | |
| 36 | -**运行命令**: | |
| 37 | -```bash | |
| 38 | -# 使用混合方法(推荐) | |
| 39 | -python3 scripts/i2i_content_similar.py --top_n 50 --method hybrid | |
| 40 | - | |
| 41 | -# 只使用TF-IDF | |
| 42 | -python3 scripts/i2i_content_similar.py --top_n 50 --method tfidf | |
| 43 | - | |
| 44 | -# 只使用分类 | |
| 45 | -python3 scripts/i2i_content_similar.py --top_n 50 --method category | |
| 46 | -``` | |
| 47 | - | |
| 48 | ---- | |
| 49 | - | |
| 50 | -### 2. 兴趣点聚合索引(已完善) | |
| 51 | - | |
| 52 | -#### 支持的维度(从2个扩展到7个) | |
| 53 | - | |
| 54 | -**单维度**: | |
| 55 | -1. ✅ `platform` - 业务平台 | |
| 56 | -2. ✅ `client_platform` - 客户端平台 | |
| 57 | -3. ✅ `supplier` - 供应商 | |
| 58 | -4. ✅ `category_level1` - 一级分类 | |
| 59 | -5. ✅ `category_level2` - 二级分类 | |
| 60 | -6. ✅ `category_level3` - 三级分类 | |
| 61 | -7. ✅ `category_level4` - 四级分类 | |
| 62 | - | |
| 63 | -**组合维度**: | |
| 64 | -1. ✅ `platform_client` - 业务平台 + 客户端平台 | |
| 65 | -2. ✅ `platform_category2` - 平台 + 二级分类 | |
| 66 | -3. ✅ `platform_category3` - 平台 + 三级分类 | |
| 67 | -4. ✅ `client_category2` - 客户端平台 + 二级分类 | |
| 68 | - | |
| 69 | -**列表类型**: | |
| 70 | -1. ✅ `hot` - 热门商品 | |
| 71 | -2. ✅ `cart` - 加购商品 | |
| 72 | -3. ✅ `new` - 新品 | |
| 73 | - | |
| 74 | ---- | |
| 75 | - | |
| 76 | -## 🎯 完整的索引输出 | |
| 77 | - | |
| 78 | -### i2i索引(4种) | |
| 79 | -``` | |
| 80 | -# 行为相似 | |
| 81 | -i2i:swing:{item_id} | |
| 82 | -i2i:session_w2v:{item_id} | |
| 83 | -i2i:deepwalk:{item_id} | |
| 84 | - | |
| 85 | -# 内容相似(新增) | |
| 86 | -i2i:content_hybrid:{item_id} | |
| 87 | -i2i:content_tfidf:{item_id} | |
| 88 | -i2i:content_category:{item_id} | |
| 89 | -``` | |
| 90 | - | |
| 91 | -### 兴趣点聚合索引(大幅扩展) | |
| 92 | - | |
| 93 | -**单维度示例**: | |
| 94 | -``` | |
| 95 | -interest:hot:platform:pc | |
| 96 | -interest:hot:client_platform:web | |
| 97 | -interest:hot:supplier:10001 | |
| 98 | -interest:hot:category_level1:100 | |
| 99 | -interest:hot:category_level2:200 | |
| 100 | -interest:hot:category_level3:300 | |
| 101 | -interest:hot:category_level4:400 | |
| 102 | -``` | |
| 103 | - | |
| 104 | -**组合维度示例**: | |
| 105 | -``` | |
| 106 | -interest:hot:platform_client:pc_web | |
| 107 | -interest:hot:platform_category2:pc_200 | |
| 108 | -interest:hot:platform_category3:mobile_300 | |
| 109 | -interest:hot:client_category2:web_200 | |
| 110 | -``` | |
| 111 | - | |
| 112 | -**列表类型示例**: | |
| 113 | -``` | |
| 114 | -interest:hot:category_level2:200 | |
| 115 | -interest:cart:category_level3:300 | |
| 116 | -interest:new:supplier:10001 | |
| 117 | -interest:global:platform_category2:pc_200 | |
| 118 | -``` | |
| 119 | - | |
| 120 | ---- | |
| 121 | - | |
| 122 | -## 📊 业务场景应用(更新) | |
| 123 | - | |
| 124 | -### 场景1: 首页猜你喜欢 | |
| 125 | -```python | |
| 126 | -# 1. 基于平台推荐 | |
| 127 | -items_1 = redis.get("interest:hot:platform:pc") | |
| 128 | - | |
| 129 | -# 2. 基于用户常购分类推荐 | |
| 130 | -items_2 = redis.get("interest:hot:category_level2:200") | |
| 131 | - | |
| 132 | -# 3. 基于平台+分类组合 | |
| 133 | -items_3 = redis.get("interest:hot:platform_category2:pc_200") | |
| 134 | - | |
| 135 | -# 融合多个维度 | |
| 136 | -recommended = merge_and_personalize(items_1, items_2, items_3) | |
| 137 | -``` | |
| 138 | - | |
| 139 | -### 场景2: 详情页大家都在看 | |
| 140 | -```python | |
| 141 | -item_id = "12345" | |
| 142 | - | |
| 143 | -# 1. 行为相似(用户行为) | |
| 144 | -similar_behavior = redis.get(f"i2i:swing:{item_id}") | |
| 145 | - | |
| 146 | -# 2. 内容相似(商品属性) | |
| 147 | -similar_content = redis.get(f"i2i:content_hybrid:{item_id}") | |
| 148 | - | |
| 149 | -# 3. 融合推荐 | |
| 150 | -recommended = merge_i2i(similar_behavior, similar_content, weight1=0.6, weight2=0.4) | |
| 151 | -``` | |
| 152 | - | |
| 153 | -### 场景3: 分类页推荐 | |
| 154 | -```python | |
| 155 | -category_id = "200" | |
| 156 | - | |
| 157 | -# 1. 该分类的热门商品 | |
| 158 | -hot_items = redis.get(f"interest:hot:category_level2:{category_id}") | |
| 159 | - | |
| 160 | -# 2. 该分类的新品 | |
| 161 | -new_items = redis.get(f"interest:new:category_level2:{category_id}") | |
| 162 | - | |
| 163 | -# 3. 组合展示 | |
| 164 | -display(hot_items, new_items) | |
| 165 | -``` | |
| 166 | - | |
| 167 | -### 场景4: 供应商页推荐 | |
| 168 | -```python | |
| 169 | -supplier_id = "10001" | |
| 170 | - | |
| 171 | -# 该供应商的热门商品 | |
| 172 | -hot_items = redis.get(f"interest:hot:supplier:{supplier_id}") | |
| 173 | -``` | |
| 174 | - | |
| 175 | ---- | |
| 176 | - | |
| 177 | -## 🚀 运行所有任务 | |
| 178 | - | |
| 179 | -```bash | |
| 180 | -cd /home/tw/recommendation/offline_tasks | |
| 181 | - | |
| 182 | -# 运行全部任务(包括新增的内容相似) | |
| 183 | -python3 run_all.py --lookback_days 730 --top_n 50 | |
| 184 | - | |
| 185 | -# 只运行内容相似 | |
| 186 | -python3 run_all.py --only-content --top_n 50 | |
| 187 | - | |
| 188 | -# 跳过内容相似,只运行其他 | |
| 189 | -python3 run_all.py --skip-content --lookback_days 730 --top_n 50 | |
| 190 | -``` | |
| 191 | - | |
| 192 | ---- | |
| 193 | - | |
| 194 | -## 📈 性能参考(更新) | |
| 195 | - | |
| 196 | -| 任务 | 数据依赖 | 预估时间 | 内存占用 | | |
| 197 | -|------|---------|---------|---------| | |
| 198 | -| Swing | 用户行为(730天) | 2-4小时 | 4-8GB | | |
| 199 | -| Session W2V | 用户行为(730天) | 30-60分钟 | 2-4GB | | |
| 200 | -| DeepWalk | 用户行为(730天) | 1-2小时 | 2-4GB | | |
| 201 | -| **Content-based** | **商品属性(全量)** | **10-30分钟** | **2-4GB** | | |
| 202 | -| 兴趣点聚合 | 用户行为(730天) | 30-60分钟 | 2-4GB | | |
| 203 | - | |
| 204 | -**总计**:约6-10小时 | |
| 205 | - | |
| 206 | ---- | |
| 207 | - | |
| 208 | -## 📝 数据表依赖关系 | |
| 209 | - | |
| 210 | -### i2i 行为相似 | |
| 211 | -- `sensors_events` - 用户行为事件 | |
| 212 | -- `prd_goods_sku` - 商品SKU信息 | |
| 213 | - | |
| 214 | -### i2i 内容相似(新增) | |
| 215 | -- `prd_goods_sku` - 商品SKU信息 | |
| 216 | -- `prd_goods` - 商品主表 | |
| 217 | -- `sup_supplier` - 供应商信息 | |
| 218 | -- `prd_category` - 分类信息(层级结构) | |
| 219 | -- `prd_goods_sku_attribute` - 商品属性 | |
| 220 | -- `prd_option` - 属性选项 | |
| 221 | -- `prd_attribute` - 属性定义 | |
| 222 | - | |
| 223 | -### 兴趣点聚合(已完善) | |
| 224 | -- `sensors_events` - 用户行为事件 | |
| 225 | -- `prd_goods_sku` - 商品SKU信息 | |
| 226 | -- `prd_goods` - 商品主表 | |
| 227 | -- `prd_category` - 分类信息(新增) | |
| 228 | - | |
| 229 | ---- | |
| 230 | - | |
| 231 | -## 🔄 与原有代码的对比 | |
| 232 | - | |
| 233 | -### 原计划维度(文档中) | |
| 234 | -- ❌ 国家/销售区域(字段不存在) | |
| 235 | -- ❌ 客户类型(字段不存在) | |
| 236 | -- ⚠️ 二级分类(原以为不存在,现已支持) | |
| 237 | -- ⚠️ 三级分类(原以为不存在,现已支持) | |
| 238 | - | |
| 239 | -### 实际实现维度(完善后) | |
| 240 | -- ✅ 业务平台 | |
| 241 | -- ✅ 客户端平台 | |
| 242 | -- ✅ 供应商(新增) | |
| 243 | -- ✅ 一级分类(新增) | |
| 244 | -- ✅ 二级分类(新增支持) | |
| 245 | -- ✅ 三级分类(新增支持) | |
| 246 | -- ✅ 四级分类(新增) | |
| 247 | - | |
| 248 | ---- | |
| 249 | - | |
| 250 | -## 💡 优势总结 | |
| 251 | - | |
| 252 | -### 1. 内容相似的优势 | |
| 253 | -- ✅ **冷启动友好**:新商品立即可用 | |
| 254 | -- ✅ **不依赖行为数据**:商品上架即可生成相似推荐 | |
| 255 | -- ✅ **可解释性强**:基于分类、属性等明确特征 | |
| 256 | -- ✅ **计算快速**:只需要商品属性数据 | |
| 257 | - | |
| 258 | -### 2. 多维度聚合的优势 | |
| 259 | -- ✅ **分类粒度丰富**:支持4级分类 | |
| 260 | -- ✅ **供应商维度**:支持供应商页推荐 | |
| 261 | -- ✅ **组合查询**:支持平台+分类等组合维度 | |
| 262 | -- ✅ **灵活性高**:可根据需要查询不同粒度 | |
| 263 | - | |
| 264 | -### 3. 算法融合的优势 | |
| 265 | -- ✅ **行为 + 内容**:可以融合4种i2i算法 | |
| 266 | -- ✅ **短期 + 长期**:热门、新品等不同时效性 | |
| 267 | -- ✅ **粗粒度 + 细粒度**:一级到四级分类 | |
| 268 | - | |
| 269 | ---- | |
| 270 | - | |
| 271 | -## 🎉 完成状态 | |
| 272 | - | |
| 273 | -**✅ 全部完成!** | |
| 274 | - | |
| 275 | -1. ✅ i2i 行为相似(3种算法) | |
| 276 | -2. ✅ i2i 内容相似(1种算法,3种方法) | |
| 277 | -3. ✅ 兴趣点聚合(7个单维度 + 4个组合维度 + 3种列表类型) | |
| 278 | -4. ✅ 统一调度脚本 | |
| 279 | -5. ✅ Redis加载工具 | |
| 280 | -6. ✅ 完整文档 | |
| 281 | - | |
| 282 | ---- | |
| 283 | - | |
| 284 | -## 📚 相关文档 | |
| 285 | - | |
| 286 | -- **CURRENT_STATUS.md** - 当前功能状态 | |
| 287 | -- **FIELD_MAPPING.md** - 字段映射说明 | |
| 288 | -- **DATABASE_SETUP.md** - 数据库配置 | |
| 289 | -- **TROUBLESHOOTING.md** - 故障排除 | |
| 290 | -- **README.md** - 完整文档 | |
| 291 | - | |
| 292 | ---- | |
| 293 | - | |
| 294 | -**更新版本**: v1.1 | |
| 295 | -**更新日期**: 2025-10-16 | |
| 296 | -**主要变化**: | |
| 297 | -- 新增内容相似算法 | |
| 298 | -- 完善分类维度支持(1-4级) | |
| 299 | -- 新增供应商维度 | |
| 300 | -- 扩展组合维度查询 | |
| 301 | - |
offline_tasks/FIX_NAME_MAPPING.md deleted
| ... | ... | @@ -1,186 +0,0 @@ |
| 1 | -# 商品名称映射问题修复 | |
| 2 | - | |
| 3 | -## 问题描述 | |
| 4 | - | |
| 5 | -在Debug模式生成的明文文件中,商品名称显示为"Unknown": | |
| 6 | - | |
| 7 | -``` | |
| 8 | -[7] i2i:swing:3667845 | |
| 9 | --------------------------------------------------------------------------------- | |
| 10 | - 1. ID:3588590(Unknown) - Score:0.2857 | |
| 11 | - 2. ID:3623446(Unknown) - Score:... | |
| 12 | -``` | |
| 13 | - | |
| 14 | -## 根本原因 | |
| 15 | - | |
| 16 | -数据类型不匹配导致名称映射失败: | |
| 17 | - | |
| 18 | -1. **fetch_name_mappings()** 从数据库获取的映射,key是**字符串类型**: | |
| 19 | - ```python | |
| 20 | - mappings['item'] = dict(zip(df['id'].astype(str), df['name'])) | |
| 21 | - # 结果: {'12345': '香蕉干', '67890': '芒果干', ...} | |
| 22 | - ``` | |
| 23 | - | |
| 24 | -2. **item_name_map** 从DataFrame创建的映射,key是**整数类型**: | |
| 25 | - ```python | |
| 26 | - item_name_map = dict(zip(df['item_id'].unique(), ...)) | |
| 27 | - # 结果: {12345: '香蕉干', 67890: '芒果干', ...} | |
| 28 | - ``` | |
| 29 | - | |
| 30 | -3. **更新操作失败**: | |
| 31 | - ```python | |
| 32 | - name_mappings['item'].update(item_name_map) | |
| 33 | - # int类型的key和str类型的key不匹配,实际上没有更新成功 | |
| 34 | - ``` | |
| 35 | - | |
| 36 | -4. **查询时找不到**: | |
| 37 | - ```python | |
| 38 | - name = name_mappings.get('item', {}).get(str(item_id), 'Unknown') | |
| 39 | - # 将int的item_id转为str查询,但dict中只有int类型的key,所以返回'Unknown' | |
| 40 | - ``` | |
| 41 | - | |
| 42 | -## 修复方案 | |
| 43 | - | |
| 44 | -### 方法1:统一转为字符串(已采用) | |
| 45 | - | |
| 46 | -```python | |
| 47 | -# 修改前 | |
| 48 | -item_name_map = dict(zip(df['item_id'].unique(), df.groupby('item_id')['item_name'].first())) | |
| 49 | - | |
| 50 | -# 修改后(key转为字符串) | |
| 51 | -item_name_map = dict(zip(df['item_id'].unique().astype(str), df.groupby('item_id')['item_name'].first())) | |
| 52 | -``` | |
| 53 | - | |
| 54 | -### 方法2:update时转换(备选) | |
| 55 | - | |
| 56 | -```python | |
| 57 | -# 转换key类型后再更新 | |
| 58 | -name_mappings['item'].update({str(k): v for k, v in item_name_map.items()}) | |
| 59 | -``` | |
| 60 | - | |
| 61 | -## 验证修复 | |
| 62 | - | |
| 63 | -修复后运行debug模式: | |
| 64 | - | |
| 65 | -```bash | |
| 66 | -cd /home/tw/recommendation/offline_tasks | |
| 67 | -python3 scripts/i2i_swing.py --lookback_days 7 --top_n 10 --debug | |
| 68 | -``` | |
| 69 | - | |
| 70 | -检查明文文件: | |
| 71 | - | |
| 72 | -```bash | |
| 73 | -more output/debug/i2i_swing_*_readable.txt | |
| 74 | -``` | |
| 75 | - | |
| 76 | -应该看到: | |
| 77 | - | |
| 78 | -``` | |
| 79 | -[7] i2i:swing:3667845(商品名称) | |
| 80 | --------------------------------------------------------------------------------- | |
| 81 | - 1. ID:3588590(香蕉干) - Score:0.2857 | |
| 82 | - 2. ID:3623446(芒果干) - Score:0.2143 | |
| 83 | -``` | |
| 84 | - | |
| 85 | -## 数据库字段确认 | |
| 86 | - | |
| 87 | -正确的查询关系: | |
| 88 | - | |
| 89 | -```sql | |
| 90 | -SELECT | |
| 91 | - pgs.id as 'sku_id', | |
| 92 | - pgs.name as '商品名称', | |
| 93 | - ss.name as '供应商名称', | |
| 94 | - pc_1.name as '一级类目', | |
| 95 | - pc_2.name as '二级类目' | |
| 96 | -FROM prd_goods_sku pgs | |
| 97 | -LEFT JOIN sup_supplier ss ON pgs.supplier_id = ss.id | |
| 98 | -LEFT JOIN prd_category pc_1 ON ... | |
| 99 | -``` | |
| 100 | - | |
| 101 | -## 其他脚本 | |
| 102 | - | |
| 103 | -这个问题也可能存在于其他脚本,需要同样修复: | |
| 104 | -- ✅ i2i_swing.py (已修复) | |
| 105 | -- ⚠️ i2i_session_w2v.py (如果有debug功能需要检查) | |
| 106 | -- ⚠️ i2i_deepwalk.py (如果有debug功能需要检查) | |
| 107 | -- ⚠️ i2i_content_similar.py (如果有debug功能需要检查) | |
| 108 | -- ⚠️ interest_aggregation.py (如果有debug功能需要检查) | |
| 109 | - | |
| 110 | -## 预防措施 | |
| 111 | - | |
| 112 | -为避免类似问题,建议: | |
| 113 | - | |
| 114 | -1. **统一数据类型约定**: | |
| 115 | - - 所有ID映射的key统一使用字符串类型 | |
| 116 | - - 在debug_utils.py中明确文档说明 | |
| 117 | - | |
| 118 | -2. **添加类型检查**: | |
| 119 | - ```python | |
| 120 | - def safe_update_mapping(target_dict, source_dict): | |
| 121 | - """安全更新映射,自动转换key类型""" | |
| 122 | - for k, v in source_dict.items(): | |
| 123 | - target_dict[str(k)] = v | |
| 124 | - ``` | |
| 125 | - | |
| 126 | -3. **添加调试日志**: | |
| 127 | - ```python | |
| 128 | - if debug: | |
| 129 | - logger.debug(f"更新前: {len(name_mappings['item'])} 个名称") | |
| 130 | - name_mappings['item'].update(item_name_map) | |
| 131 | - logger.debug(f"更新后: {len(name_mappings['item'])} 个名称") | |
| 132 | - ``` | |
| 133 | - | |
| 134 | ---- | |
| 135 | - | |
| 136 | -**状态**: ✅ 已修复 | |
| 137 | -**影响范围**: i2i_swing.py | |
| 138 | -**修复时间**: 2025-10-16 | |
| 139 | - | |
| 140 | -## 补充修复 - 主输出文件 | |
| 141 | - | |
| 142 | -问题同样存在于主输出文件中: | |
| 143 | - | |
| 144 | -``` | |
| 145 | -1070176 Unknown 2786217:0.4000 | |
| 146 | -2786217 Unknown 1070176:0.4000 | |
| 147 | -``` | |
| 148 | - | |
| 149 | -### 原因 | |
| 150 | - | |
| 151 | -主输出代码中使用整数item_id作为key查询: | |
| 152 | - | |
| 153 | -```python | |
| 154 | -for item_id, sims in result.items(): | |
| 155 | - item_name = item_name_map.get(item_id, 'Unknown') # item_id是int,但map的key是str | |
| 156 | -``` | |
| 157 | - | |
| 158 | -### 修复 | |
| 159 | - | |
| 160 | -统一转换为字符串: | |
| 161 | - | |
| 162 | -```python | |
| 163 | -for item_id, sims in result.items(): | |
| 164 | - item_name = item_name_map.get(str(item_id), 'Unknown') # 转换为字符串查询 | |
| 165 | -``` | |
| 166 | - | |
| 167 | -### 验证 | |
| 168 | - | |
| 169 | -```bash | |
| 170 | -# 重新运行 | |
| 171 | -python3 scripts/i2i_swing.py --lookback_days 7 --top_n 10 | |
| 172 | - | |
| 173 | -# 检查输出 | |
| 174 | -more output/i2i_swing_20251016.txt | |
| 175 | -``` | |
| 176 | - | |
| 177 | -应该看到: | |
| 178 | -``` | |
| 179 | -1070176 商品名称A 2786217:0.4000 | |
| 180 | -2786217 商品名称B 1070176:0.4000 | |
| 181 | -``` | |
| 182 | - | |
| 183 | ---- | |
| 184 | - | |
| 185 | -**更新时间**: 2025-10-16 20:30 | |
| 186 | -**状态**: ✅ 完全修复 |
offline_tasks/LATEST_UPDATES.md deleted
| ... | ... | @@ -1,155 +0,0 @@ |
| 1 | -# 最新更新 (2025-10-17) | |
| 2 | - | |
| 3 | -## 🎉 本次更新内容 | |
| 4 | - | |
| 5 | -### 1. ✅ ES向量相似度 + on_sell_days_boost提权 | |
| 6 | - | |
| 7 | -#### 功能 | |
| 8 | -- 基于Elasticsearch向量计算相似度(名称向量 + 图片向量) | |
| 9 | -- 自动应用 `on_sell_days_boost` 提权(0.9~1.1) | |
| 10 | -- 无需任何命令行参数,开箱即用 | |
| 11 | - | |
| 12 | -#### 使用 | |
| 13 | -```bash | |
| 14 | -python scripts/i2i_content_similar.py | |
| 15 | -``` | |
| 16 | - | |
| 17 | -### 2. ✅ 代码规范:统一使用 logger | |
| 18 | - | |
| 19 | -#### 变更 | |
| 20 | -- 移除所有 `print` 语句 | |
| 21 | -- 统一使用 `logger` 记录日志 | |
| 22 | -- 更好的日志级别控制 | |
| 23 | - | |
| 24 | -### 3. ✅ run.sh 内存监控 | |
| 25 | - | |
| 26 | -#### 功能 | |
| 27 | -- **25GB警告**: 打印警告日志,继续运行 | |
| 28 | -- **30GB终止**: 强制kill进程,防止OOM | |
| 29 | -- **每10秒检查**: 实时监控内存使用 | |
| 30 | -- **详细日志**: `logs/memory_monitor.log` | |
| 31 | - | |
| 32 | -#### 使用 | |
| 33 | -```bash | |
| 34 | -./run.sh | |
| 35 | -``` | |
| 36 | - | |
| 37 | -## 📋 快速开始 | |
| 38 | - | |
| 39 | -### 1. 测试ES连接和向量 | |
| 40 | -```bash | |
| 41 | -python scripts/test_es_connection.py | |
| 42 | -``` | |
| 43 | - | |
| 44 | -### 2. 测试内存监控 | |
| 45 | -```bash | |
| 46 | -./test_memory_monitor.sh | |
| 47 | -``` | |
| 48 | - | |
| 49 | -### 3. 运行所有任务(含监控) | |
| 50 | -```bash | |
| 51 | -./run.sh | |
| 52 | -``` | |
| 53 | - | |
| 54 | -### 4. 查看监控日志 | |
| 55 | -```bash | |
| 56 | -tail -f logs/memory_monitor.log | |
| 57 | -``` | |
| 58 | - | |
| 59 | -## 📊 生成的索引 | |
| 60 | - | |
| 61 | -| 索引文件 | Redis Key | 提权 | TTL | | |
| 62 | -|---------|-----------|------|-----| | |
| 63 | -| `i2i_content_name_YYYYMMDD.txt` | `item:similar:content_name:{id}` | ✅ | 30天 | | |
| 64 | -| `i2i_content_pic_YYYYMMDD.txt` | `item:similar:content_pic:{id}` | ✅ | 30天 | | |
| 65 | - | |
| 66 | -## 📚 文档 | |
| 67 | - | |
| 68 | -| 文档 | 说明 | | |
| 69 | -|------|------| | |
| 70 | -| `QUICKSTART_NEW.md` | 5分钟快速开始 | | |
| 71 | -| `ES_VECTOR_SIMILARITY.md` | ES向量技术详解 | | |
| 72 | -| `RUN_SCRIPT_GUIDE.md` | 运行脚本使用指南 | | |
| 73 | -| `MEMORY_MONITORING_UPDATE.md` | 内存监控详细说明 | | |
| 74 | -| `FINAL_SUMMARY.md` | 完整更新总结 | | |
| 75 | - | |
| 76 | -## 🔍 关键特性 | |
| 77 | - | |
| 78 | -### ES向量相似度 | |
| 79 | -```python | |
| 80 | -# 自动应用提权 | |
| 81 | -base_score = knn_result['_score'] | |
| 82 | -boost = knn_result['on_sell_days_boost'] # 0.9~1.1 | |
| 83 | -final_score = base_score * boost | |
| 84 | -``` | |
| 85 | - | |
| 86 | -### 内存监控 | |
| 87 | -```bash | |
| 88 | -# 自动监控每个任务 | |
| 89 | -python3 run_all.py & | |
| 90 | -PID=$! | |
| 91 | -check_memory $PID & # 后台监控 | |
| 92 | - | |
| 93 | -# 日志输出 | |
| 94 | -[2025-10-17 15:20:15] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB) | |
| 95 | -[2025-10-17 15:24:45] ❌ 内存超限!当前使用: 31.20GB, 强制终止 | |
| 96 | -``` | |
| 97 | - | |
| 98 | -## ⚠️ 重要说明 | |
| 99 | - | |
| 100 | -### 向量相似度 | |
| 101 | -- ✅ 分数已包含 `on_sell_days_boost` 提权 | |
| 102 | -- ✅ 输出到文件和Redis的都是最终分数 | |
| 103 | -- ✅ 应用层无需再次应用提权 | |
| 104 | - | |
| 105 | -### 内存监控 | |
| 106 | -- ⚠️ 默认阈值:25GB警告,30GB终止 | |
| 107 | -- ⚠️ 可在 `run.sh` 中修改阈值 | |
| 108 | -- ⚠️ 监控日志保存在 `logs/memory_monitor.log` | |
| 109 | - | |
| 110 | -### run_all.py 简化 | |
| 111 | -- ❌ 移除了所有 `--only-*`, `--skip-*` 参数 | |
| 112 | -- ✅ 只保留 `--debug` 参数 | |
| 113 | -- ✅ 配置集中在 `offline_config.py` | |
| 114 | - | |
| 115 | -## 🚀 一键运行 | |
| 116 | - | |
| 117 | -```bash | |
| 118 | -# 完整流程(含内存监控) | |
| 119 | -cd /home/tw/recommendation/offline_tasks | |
| 120 | -./run.sh | |
| 121 | - | |
| 122 | -# 输出: | |
| 123 | -# ====================================================================== | |
| 124 | -# 开始运行离线任务 - 2025-10-17 14:00:00 | |
| 125 | -# 内存监控: 警告阈值=25GB, 强制终止阈值=30GB | |
| 126 | -# ====================================================================== | |
| 127 | -# | |
| 128 | -# >>> 步骤1: 调试模式运行(小数据量) | |
| 129 | -# ✓ 调试模式完成 | |
| 130 | -# | |
| 131 | -# >>> 步骤2: 生产模式运行(大数据量) | |
| 132 | -# ✓ 生产模式完成 | |
| 133 | -# | |
| 134 | -# >>> 步骤3: 加载到Redis | |
| 135 | -# ✓ Redis加载完成 | |
| 136 | -# | |
| 137 | -# ====================================================================== | |
| 138 | -# 所有任务完成 - 2025-10-17 16:30:00 | |
| 139 | -# ====================================================================== | |
| 140 | -``` | |
| 141 | - | |
| 142 | -## 🎯 核心改进 | |
| 143 | - | |
| 144 | -1. **更智能**: ES向量 + 上架天数提权 | |
| 145 | -2. **更简单**: 无需参数,一键运行 | |
| 146 | -3. **更安全**: 内存监控保护系统 | |
| 147 | -4. **更规范**: 统一使用logger | |
| 148 | -5. **更完整**: 详细文档和测试工具 | |
| 149 | - | |
| 150 | ---- | |
| 151 | - | |
| 152 | -**更新日期**: 2025-10-17 | |
| 153 | -**版本**: v2.1 | |
| 154 | -**状态**: ✅ 已完成,可投入使用 | |
| 155 | - |
offline_tasks/MEMORY_MONITORING_UPDATE.md deleted
| ... | ... | @@ -1,376 +0,0 @@ |
| 1 | -# 内存监控功能更新 | |
| 2 | - | |
| 3 | -## 更新日期 | |
| 4 | -2025-10-17 | |
| 5 | - | |
| 6 | -## 更新内容 | |
| 7 | - | |
| 8 | -### 1. ✅ 代码规范:移除 print,统一使用 logger | |
| 9 | - | |
| 10 | -#### 检查结果 | |
| 11 | -- ✅ `i2i_content_similar.py` - 已全部使用 logger | |
| 12 | -- ✅ `test_es_connection.py` - print用于测试工具输出(符合预期) | |
| 13 | -- ✅ 所有核心代码已使用 logger | |
| 14 | - | |
| 15 | -### 2. ✅ run.sh 添加内存监控功能 | |
| 16 | - | |
| 17 | -#### 核心功能 | |
| 18 | - | |
| 19 | -**内存监控函数**: | |
| 20 | -```bash | |
| 21 | -check_memory() { | |
| 22 | - local pid=$1 | |
| 23 | - local threshold_warn=25 # 25GB警告阈值 | |
| 24 | - local threshold_kill=30 # 30GB强制kill阈值 | |
| 25 | - | |
| 26 | - while kill -0 $pid 2>/dev/null; do | |
| 27 | - 获取进程内存使用(MB) | |
| 28 | - 转换为GB | |
| 29 | - | |
| 30 | - if 内存 >= 30GB: | |
| 31 | - 打印错误日志到终端和文件 | |
| 32 | - 强制kill进程 | |
| 33 | - break | |
| 34 | - elif 内存 >= 25GB: | |
| 35 | - 打印警告日志到终端和文件 | |
| 36 | - | |
| 37 | - sleep 10秒检查一次 | |
| 38 | - done | |
| 39 | -} | |
| 40 | -``` | |
| 41 | - | |
| 42 | -#### 监控特性 | |
| 43 | - | |
| 44 | -| 特性 | 说明 | | |
| 45 | -|------|------| | |
| 46 | -| **警告阈值** | 25GB - 打印警告但继续运行 | | |
| 47 | -| **终止阈值** | 30GB - 强制kill进程 | | |
| 48 | -| **检查频率** | 每10秒检查一次 | | |
| 49 | -| **日志输出** | 终端 + `logs/memory_monitor.log` | | |
| 50 | -| **自动清理** | 任务完成后自动终止监控 | | |
| 51 | - | |
| 52 | -#### 日志格式 | |
| 53 | - | |
| 54 | -**警告日志**: | |
| 55 | -``` | |
| 56 | -[2025-10-17 14:30:25] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB), PID=12345 | |
| 57 | -``` | |
| 58 | - | |
| 59 | -**强制终止日志**: | |
| 60 | -``` | |
| 61 | -[2025-10-17 14:35:30] ❌ 内存超限!当前使用: 31.20GB (>= 30GB), 强制终止进程 PID=12345 | |
| 62 | -``` | |
| 63 | - | |
| 64 | -### 3. ✅ 增强的运行流程 | |
| 65 | - | |
| 66 | -#### 更新后的执行流程 | |
| 67 | - | |
| 68 | -```bash | |
| 69 | -#!/bin/bash | |
| 70 | - | |
| 71 | -1. 定义内存监控函数 | |
| 72 | -2. 清理旧进程和输出 | |
| 73 | -3. 创建logs目录 | |
| 74 | -4. 显示开始信息(包含监控阈值) | |
| 75 | - | |
| 76 | -# 调试模式 | |
| 77 | -5. 启动调试任务(后台) | |
| 78 | -6. 启动内存监控(后台) | |
| 79 | -7. 等待任务完成 | |
| 80 | -8. 终止监控进程 | |
| 81 | -9. 检查退出码 | |
| 82 | - | |
| 83 | -# 生产模式 | |
| 84 | -10. 启动生产任务(后台) | |
| 85 | -11. 启动内存监控(后台) | |
| 86 | -12. 等待任务完成 | |
| 87 | -13. 终止监控进程 | |
| 88 | -14. 检查退出码 | |
| 89 | - | |
| 90 | -# 加载Redis | |
| 91 | -15. 加载索引到Redis | |
| 92 | -16. 检查退出码 | |
| 93 | - | |
| 94 | -17. 显示完成信息 | |
| 95 | -``` | |
| 96 | - | |
| 97 | -### 4. ✅ 新增文件 | |
| 98 | - | |
| 99 | -| 文件 | 说明 | | |
| 100 | -|------|------| | |
| 101 | -| `run.sh` | 更新:添加内存监控 | | |
| 102 | -| `RUN_SCRIPT_GUIDE.md` | 新增:运行脚本使用指南 | | |
| 103 | -| `test_memory_monitor.sh` | 新增:内存监控测试脚本 | | |
| 104 | -| `MEMORY_MONITORING_UPDATE.md` | 新增:本文档 | | |
| 105 | - | |
| 106 | -## 使用指南 | |
| 107 | - | |
| 108 | -### 基本使用 | |
| 109 | - | |
| 110 | -```bash | |
| 111 | -# 1. 运行脚本(自动监控内存) | |
| 112 | -cd /home/tw/recommendation/offline_tasks | |
| 113 | -./run.sh | |
| 114 | - | |
| 115 | -# 2. 查看监控日志 | |
| 116 | -tail -f logs/memory_monitor.log | |
| 117 | - | |
| 118 | -# 3. 查看系统内存 | |
| 119 | -free -h | |
| 120 | -``` | |
| 121 | - | |
| 122 | -### 测试监控功能 | |
| 123 | - | |
| 124 | -```bash | |
| 125 | -# 运行测试脚本 | |
| 126 | -./test_memory_monitor.sh | |
| 127 | -``` | |
| 128 | - | |
| 129 | -### 自定义阈值 | |
| 130 | - | |
| 131 | -编辑 `run.sh` 第8-9行: | |
| 132 | -```bash | |
| 133 | -local threshold_warn=25 # 修改警告阈值 | |
| 134 | -local threshold_kill=30 # 修改强制kill阈值 | |
| 135 | -``` | |
| 136 | - | |
| 137 | -## 技术细节 | |
| 138 | - | |
| 139 | -### 内存获取 | |
| 140 | - | |
| 141 | -```bash | |
| 142 | -# 获取进程内存(RSS,单位KB) | |
| 143 | -ps -p $pid -o rss= | |
| 144 | - | |
| 145 | -# 转换为MB | |
| 146 | -mem_mb=$(ps -p $pid -o rss= | awk '{print int($1/1024)}') | |
| 147 | - | |
| 148 | -# 转换为GB | |
| 149 | -mem_gb=$(echo "scale=2; $mem_mb/1024" | bc) | |
| 150 | -``` | |
| 151 | - | |
| 152 | -### 进程检查 | |
| 153 | - | |
| 154 | -```bash | |
| 155 | -# 检查进程是否存在 | |
| 156 | -kill -0 $pid 2>/dev/null | |
| 157 | - | |
| 158 | -# 返回值: | |
| 159 | -# 0 - 进程存在 | |
| 160 | -# 非0 - 进程不存在 | |
| 161 | -``` | |
| 162 | - | |
| 163 | -### 强制终止 | |
| 164 | - | |
| 165 | -```bash | |
| 166 | -# 发送SIGKILL信号 | |
| 167 | -kill -9 $pid | |
| 168 | - | |
| 169 | -# 特点: | |
| 170 | -# - 立即终止,不可捕获 | |
| 171 | -# - 不执行清理代码 | |
| 172 | -# - 确保进程被终止 | |
| 173 | -``` | |
| 174 | - | |
| 175 | -## 输出示例 | |
| 176 | - | |
| 177 | -### 正常运行(内存未超限) | |
| 178 | - | |
| 179 | -``` | |
| 180 | -====================================================================== | |
| 181 | -开始运行离线任务 - 2025-10-17 14:00:00 | |
| 182 | -内存监控: 警告阈值=25GB, 强制终止阈值=30GB | |
| 183 | -====================================================================== | |
| 184 | - | |
| 185 | ->>> 步骤1: 调试模式运行(小数据量) | |
| 186 | -调试任务 PID: 12345 | |
| 187 | -✓ 调试模式完成 | |
| 188 | - | |
| 189 | ->>> 步骤2: 生产模式运行(大数据量) | |
| 190 | -生产任务 PID: 12346 | |
| 191 | -✓ 生产模式完成 | |
| 192 | - | |
| 193 | ->>> 步骤3: 加载到Redis | |
| 194 | -✓ Redis加载完成 | |
| 195 | - | |
| 196 | -====================================================================== | |
| 197 | -所有任务完成 - 2025-10-17 16:30:00 | |
| 198 | -====================================================================== | |
| 199 | -``` | |
| 200 | - | |
| 201 | -### 内存警告(25GB-30GB) | |
| 202 | - | |
| 203 | -``` | |
| 204 | ->>> 步骤2: 生产模式运行(大数据量) | |
| 205 | -生产任务 PID: 12346 | |
| 206 | -[2025-10-17 15:20:15] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB), PID=12346 | |
| 207 | -[2025-10-17 15:22:30] ⚠️ 内存警告!当前使用: 27.80GB (>= 25GB), PID=12346 | |
| 208 | -[2025-10-17 15:24:45] ⚠️ 内存警告!当前使用: 28.95GB (>= 25GB), PID=12346 | |
| 209 | -✓ 生产模式完成 | |
| 210 | -``` | |
| 211 | - | |
| 212 | -### 内存超限(>=30GB) | |
| 213 | - | |
| 214 | -``` | |
| 215 | ->>> 步骤2: 生产模式运行(大数据量) | |
| 216 | -生产任务 PID: 12346 | |
| 217 | -[2025-10-17 15:20:15] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB), PID=12346 | |
| 218 | -[2025-10-17 15:22:30] ⚠️ 内存警告!当前使用: 28.80GB (>= 25GB), PID=12346 | |
| 219 | -[2025-10-17 15:24:45] ❌ 内存超限!当前使用: 31.20GB (>= 30GB), 强制终止进程 PID=12346 | |
| 220 | -✗ 生产模式失败,退出码: 137 | |
| 221 | -``` | |
| 222 | - | |
| 223 | -## 监控日志示例 | |
| 224 | - | |
| 225 | -`logs/memory_monitor.log`: | |
| 226 | -``` | |
| 227 | -[2025-10-17 15:20:15] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB), PID=12346 | |
| 228 | -[2025-10-17 15:22:30] ⚠️ 内存警告!当前使用: 28.80GB (>= 25GB), PID=12346 | |
| 229 | -[2025-10-17 15:24:45] ❌ 内存超限!当前使用: 31.20GB (>= 30GB), 强制终止进程 PID=12346 | |
| 230 | -``` | |
| 231 | - | |
| 232 | -## 故障处理 | |
| 233 | - | |
| 234 | -### 内存持续增长 | |
| 235 | - | |
| 236 | -**现象**: 警告日志频繁出现,内存持续增长 | |
| 237 | - | |
| 238 | -**原因**: | |
| 239 | -- 数据量过大 | |
| 240 | -- 内存泄漏 | |
| 241 | -- 批处理大小不合理 | |
| 242 | - | |
| 243 | -**解决方案**: | |
| 244 | -1. 减小批处理大小 | |
| 245 | -2. 增加垃圾回收频率 | |
| 246 | -3. 优化算法减少内存占用 | |
| 247 | -4. 考虑分批运行 | |
| 248 | - | |
| 249 | -### 进程被误杀 | |
| 250 | - | |
| 251 | -**现象**: 任务未完成就被终止 | |
| 252 | - | |
| 253 | -**原因**: | |
| 254 | -- 阈值设置过低 | |
| 255 | -- 系统其他进程占用内存 | |
| 256 | - | |
| 257 | -**解决方案**: | |
| 258 | -1. 提高阈值(修改 run.sh) | |
| 259 | -2. 优化代码减少内存使用 | |
| 260 | -3. 关闭不必要的系统进程 | |
| 261 | - | |
| 262 | -### 监控不生效 | |
| 263 | - | |
| 264 | -**检查清单**: | |
| 265 | -- [ ] `bc` 命令是否安装:`which bc` | |
| 266 | -- [ ] 脚本是否有执行权限:`ls -l run.sh` | |
| 267 | -- [ ] logs目录是否存在:`ls -ld logs/` | |
| 268 | -- [ ] 进程是否在后台运行:`ps aux | grep run_all` | |
| 269 | - | |
| 270 | -## 性能影响 | |
| 271 | - | |
| 272 | -### 监控开销 | |
| 273 | - | |
| 274 | -| 项目 | 影响 | | |
| 275 | -|------|------| | |
| 276 | -| CPU | < 0.1% (每10秒运行一次ps命令) | | |
| 277 | -| 内存 | < 10MB (监控脚本本身) | | |
| 278 | -| 磁盘I/O | 极小 (仅写日志) | | |
| 279 | - | |
| 280 | -### 建议 | |
| 281 | - | |
| 282 | -- ✅ 检查频率10秒合理,不建议小于5秒 | |
| 283 | -- ✅ 警告阈值25GB为系统总内存的合理比例 | |
| 284 | -- ✅ 终止阈值30GB留有5GB缓冲空间 | |
| 285 | -- ⚠️ 根据实际情况调整阈值 | |
| 286 | - | |
| 287 | -## 最佳实践 | |
| 288 | - | |
| 289 | -### 1. 定期查看日志 | |
| 290 | -```bash | |
| 291 | -# 每天检查 | |
| 292 | -cat logs/memory_monitor.log | |
| 293 | - | |
| 294 | -# 统计警告次数 | |
| 295 | -grep "⚠️" logs/memory_monitor.log | wc -l | |
| 296 | - | |
| 297 | -# 统计终止次数 | |
| 298 | -grep "❌" logs/memory_monitor.log | wc -l | |
| 299 | -``` | |
| 300 | - | |
| 301 | -### 2. 设置定时任务 | |
| 302 | -```bash | |
| 303 | -# 每周一凌晨2点运行 | |
| 304 | -0 2 * * 1 /home/tw/recommendation/offline_tasks/run.sh >> /var/log/offline_tasks.log 2>&1 | |
| 305 | -``` | |
| 306 | - | |
| 307 | -### 3. 配置告警 | |
| 308 | -```bash | |
| 309 | -# 在 run.sh 末尾添加 | |
| 310 | -if grep -q "❌" logs/memory_monitor.log; then | |
| 311 | - echo "内存超限告警" | mail -s "[告警] 离线任务内存超限" admin@example.com | |
| 312 | -fi | |
| 313 | -``` | |
| 314 | - | |
| 315 | -### 4. 监控系统资源 | |
| 316 | -```bash | |
| 317 | -# 安装监控工具 | |
| 318 | -apt install htop sysstat | |
| 319 | - | |
| 320 | -# 实时监控 | |
| 321 | -htop -p $(pgrep -f run_all.py) | |
| 322 | - | |
| 323 | -# 查看历史 | |
| 324 | -sar -r 1 10 # 内存使用情况 | |
| 325 | -``` | |
| 326 | - | |
| 327 | -## 对比 | |
| 328 | - | |
| 329 | -### 更新前 | |
| 330 | -```bash | |
| 331 | -# 无内存监控 | |
| 332 | -python3 run_all.py --lookback_days 730 --top_n 50 | |
| 333 | - | |
| 334 | -# 问题: | |
| 335 | -# - 内存可能无限增长 | |
| 336 | -# - OOM导致系统崩溃 | |
| 337 | -# - 无法及时发现问题 | |
| 338 | -``` | |
| 339 | - | |
| 340 | -### 更新后 | |
| 341 | -```bash | |
| 342 | -# 自动内存监控 | |
| 343 | -./run.sh | |
| 344 | - | |
| 345 | -# 优势: | |
| 346 | -# ✅ 25GB警告提醒 | |
| 347 | -# ✅ 30GB自动终止 | |
| 348 | -# ✅ 详细日志记录 | |
| 349 | -# ✅ 保护系统稳定 | |
| 350 | -``` | |
| 351 | - | |
| 352 | -## 总结 | |
| 353 | - | |
| 354 | -本次更新实现了: | |
| 355 | - | |
| 356 | -1. ✅ **代码规范化**: 统一使用logger,移除print | |
| 357 | -2. ✅ **内存监控**: 25GB警告,30GB强制终止 | |
| 358 | -3. ✅ **自动化运行**: 集成监控到运行流程 | |
| 359 | -4. ✅ **日志记录**: 详细的监控日志 | |
| 360 | -5. ✅ **测试工具**: 提供测试脚本验证功能 | |
| 361 | -6. ✅ **完整文档**: 使用指南和故障处理 | |
| 362 | - | |
| 363 | -### 核心价值 | |
| 364 | - | |
| 365 | -- 🛡️ **保护系统**: 防止内存溢出导致系统崩溃 | |
| 366 | -- 📊 **实时监控**: 每10秒检查一次内存使用 | |
| 367 | -- 🔔 **及时告警**: 25GB警告,30GB终止 | |
| 368 | -- 📝 **日志完整**: 所有监控事件都有记录 | |
| 369 | -- 🔧 **易于调整**: 阈值和频率可轻松修改 | |
| 370 | - | |
| 371 | ---- | |
| 372 | - | |
| 373 | -**更新完成时间**: 2025-10-17 | |
| 374 | -**状态**: ✅ 已完成,可投入使用 | |
| 375 | -**建议**: 先在测试环境验证后再用于生产 | |
| 376 | - |
offline_tasks/PROJECT_SUMMARY.md deleted
| ... | ... | @@ -1,276 +0,0 @@ |
| 1 | -# 推荐系统离线任务 - 项目总结 | |
| 2 | - | |
| 3 | -## 项目概述 | |
| 4 | - | |
| 5 | -本项目实现了一个完整的推荐系统离线任务框架,用于生成各种推荐索引。主要包括两大模块: | |
| 6 | - | |
| 7 | -1. **i2i 行为相似索引**:基于用户行为计算物品之间的相似度 | |
| 8 | -2. **兴趣点聚合索引**:按多维度聚合用户行为,生成不同场景的推荐列表 | |
| 9 | - | |
| 10 | -## 技术架构 | |
| 11 | - | |
| 12 | -### 数据来源 | |
| 13 | -- 数据库:SelectDB(兼容MySQL协议) | |
| 14 | -- 主要表: | |
| 15 | - - `sensors_events`:用户行为事件表 | |
| 16 | - - `prd_goods_sku`:商品SKU表 | |
| 17 | - | |
| 18 | -### 算法实现 | |
| 19 | - | |
| 20 | -#### 1. i2i 行为相似算法 | |
| 21 | - | |
| 22 | -| 算法 | 原理 | 优势 | 适用场景 | | |
| 23 | -|------|------|------|---------| | |
| 24 | -| **Swing** | 基于用户共同行为的物品相似度,考虑用户重叠度 | 效果好,能发现深层关系 | 详情页推荐、相关商品 | | |
| 25 | -| **Session W2V** | 基于用户会话序列训练Word2Vec | 能捕获序列关系 | 下一个可能感兴趣的商品 | | |
| 26 | -| **DeepWalk** | 基于图随机游走训练Word2Vec | 能发现图结构特征 | 发现潜在关联商品 | | |
| 27 | - | |
| 28 | -#### 2. 兴趣点聚合 | |
| 29 | - | |
| 30 | -**维度分类:** | |
| 31 | - | |
| 32 | -- **单维度**: | |
| 33 | - - 平台(PC/Mobile/App) | |
| 34 | - - 国家/销售区域 | |
| 35 | - - 客户类型(零售商/批发商等) | |
| 36 | - - 二级分类 | |
| 37 | - - 三级分类 | |
| 38 | - | |
| 39 | -- **组合维度**: | |
| 40 | - - 平台 + 国家 | |
| 41 | - - 平台 + 客户类型 | |
| 42 | - - 国家 + 客户类型 | |
| 43 | - - 平台 + 国家 + 客户类型 | |
| 44 | - | |
| 45 | -**列表类型:** | |
| 46 | - | |
| 47 | -- **hot(热门)**:基于最近180天的高交互商品 | |
| 48 | -- **cart(加购)**:基于加购行为的高频商品 | |
| 49 | -- **new(新品)**:基于商品创建时间的新品 | |
| 50 | -- **global(全局)**:基于所有数据的综合排序 | |
| 51 | - | |
| 52 | -## 核心特性 | |
| 53 | - | |
| 54 | -### 1. 时间衰减 | |
| 55 | -- 使用指数衰减模型,越近期的行为权重越高 | |
| 56 | -- 衰减因子:0.95(每30天衰减一次) | |
| 57 | -- 公式:`weight = decay_factor ^ (days / 30)` | |
| 58 | - | |
| 59 | -### 2. 行为权重 | |
| 60 | -不同行为类型赋予不同权重: | |
| 61 | - | |
| 62 | -| 行为类型 | 权重 | 说明 | | |
| 63 | -|---------|------|------| | |
| 64 | -| click | 1.0 | 点击 | | |
| 65 | -| addToPool | 2.0 | 加入询盘池 | | |
| 66 | -| addToCart | 3.0 | 加入购物车 | | |
| 67 | -| contactFactory | 5.0 | 联系工厂 | | |
| 68 | -| purchase | 10.0 | 购买 | | |
| 69 | - | |
| 70 | -### 3. 可配置参数 | |
| 71 | -所有参数集中在 `config/offline_config.py`,便于调整优化。 | |
| 72 | - | |
| 73 | -## 文件清单 | |
| 74 | - | |
| 75 | -### 核心代码 | |
| 76 | - | |
| 77 | -``` | |
| 78 | -/home/tw/recommendation/ | |
| 79 | -├── db_service.py # 数据库连接服务 | |
| 80 | -├── requirements.txt # Python依赖包 | |
| 81 | -└── offline_tasks/ | |
| 82 | - ├── config/ | |
| 83 | - │ └── offline_config.py # 配置文件 | |
| 84 | - ├── scripts/ | |
| 85 | - │ ├── i2i_swing.py # Swing算法 | |
| 86 | - │ ├── i2i_session_w2v.py # Session W2V算法 | |
| 87 | - │ ├── i2i_deepwalk.py # DeepWalk算法 | |
| 88 | - │ ├── interest_aggregation.py # 兴趣点聚合 | |
| 89 | - │ └── load_index_to_redis.py # 加载索引到Redis | |
| 90 | - ├── run_all.py # 统一调度脚本 | |
| 91 | - ├── test_connection.py # 连接测试脚本 | |
| 92 | - ├── example_query_redis.py # Redis查询示例 | |
| 93 | - ├── README.md # 详细文档 | |
| 94 | - ├── QUICKSTART.md # 快速启动指南 | |
| 95 | - └── PROJECT_SUMMARY.md # 本文档 | |
| 96 | -``` | |
| 97 | - | |
| 98 | -### 输出目录 | |
| 99 | - | |
| 100 | -``` | |
| 101 | -offline_tasks/ | |
| 102 | -├── output/ # 索引输出目录 | |
| 103 | -│ ├── i2i_swing_YYYYMMDD.txt | |
| 104 | -│ ├── i2i_session_w2v_YYYYMMDD.txt | |
| 105 | -│ ├── i2i_deepwalk_YYYYMMDD.txt | |
| 106 | -│ ├── session_w2v_model_YYYYMMDD.model # W2V模型 | |
| 107 | -│ ├── deepwalk_model_YYYYMMDD.model # DeepWalk模型 | |
| 108 | -│ ├── item_graph_YYYYMMDD.txt # 物品图结构 | |
| 109 | -│ ├── interest_aggregation_hot_YYYYMMDD.txt | |
| 110 | -│ ├── interest_aggregation_cart_YYYYMMDD.txt | |
| 111 | -│ ├── interest_aggregation_new_YYYYMMDD.txt | |
| 112 | -│ └── interest_aggregation_global_YYYYMMDD.txt | |
| 113 | -└── logs/ # 日志目录 | |
| 114 | - ├── run_all_YYYYMMDD.log | |
| 115 | - └── ... | |
| 116 | -``` | |
| 117 | - | |
| 118 | -## 使用流程 | |
| 119 | - | |
| 120 | -### 1. 环境准备 | |
| 121 | -```bash | |
| 122 | -pip install -r requirements.txt | |
| 123 | -``` | |
| 124 | - | |
| 125 | -### 2. 测试连接 | |
| 126 | -```bash | |
| 127 | -cd offline_tasks | |
| 128 | -python test_connection.py | |
| 129 | -``` | |
| 130 | - | |
| 131 | -### 3. 运行离线任务 | |
| 132 | -```bash | |
| 133 | -python run_all.py --lookback_days 730 --top_n 50 | |
| 134 | -``` | |
| 135 | - | |
| 136 | -### 4. 加载索引到Redis | |
| 137 | -```bash | |
| 138 | -python scripts/load_index_to_redis.py --expire-days 7 | |
| 139 | -``` | |
| 140 | - | |
| 141 | -### 5. 查询示例 | |
| 142 | -```bash | |
| 143 | -python example_query_redis.py | |
| 144 | -``` | |
| 145 | - | |
| 146 | -## 数据格式 | |
| 147 | - | |
| 148 | -### i2i索引格式 | |
| 149 | -``` | |
| 150 | -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... | |
| 151 | -``` | |
| 152 | - | |
| 153 | -示例: | |
| 154 | -``` | |
| 155 | -123456 \t 商品A \t 234567:0.8523,345678:0.7842,456789:0.7234 | |
| 156 | -``` | |
| 157 | - | |
| 158 | -### 兴趣点聚合索引格式 | |
| 159 | -``` | |
| 160 | -dimension_key \t item_id1:score1,item_id2:score2,... | |
| 161 | -``` | |
| 162 | - | |
| 163 | -示例: | |
| 164 | -``` | |
| 165 | -platform:PC \t 12345:98.52,23456:87.34,34567:76.89 | |
| 166 | -country:US \t 45678:156.23,56789:142.87,67890:128.45 | |
| 167 | -platform_country:PC_US \t 78901:234.56,89012:198.76,90123:187.23 | |
| 168 | -``` | |
| 169 | - | |
| 170 | -## 在线推荐场景 | |
| 171 | - | |
| 172 | -### 场景1:详情页 - 大家都在看 | |
| 173 | -``` | |
| 174 | -用户浏览商品ID: 123456 | |
| 175 | -查询: i2i:swing:123456 | |
| 176 | -返回: 该商品的相似商品列表 | |
| 177 | -``` | |
| 178 | - | |
| 179 | -### 场景2:首页 - 猜你喜欢 | |
| 180 | -``` | |
| 181 | -用户特征: PC端, 美国, 零售商 | |
| 182 | -组合查询: | |
| 183 | -1. interest:hot:platform_country:PC_US | |
| 184 | -2. interest:cart:customer_type:retailer | |
| 185 | -3. 结合用户历史行为的i2i | |
| 186 | -``` | |
| 187 | - | |
| 188 | -### 场景3:搜索结果页 - 供应商推荐 | |
| 189 | -``` | |
| 190 | -用户搜索: 二级分类100 | |
| 191 | -查询: interest:global:category_level2:100 | |
| 192 | -返回: 该分类下的热门商品 | |
| 193 | -``` | |
| 194 | - | |
| 195 | -## 性能指标 | |
| 196 | - | |
| 197 | -### 数据量参考(730天数据) | |
| 198 | -- 用户行为记录:约100万-1000万条 | |
| 199 | -- 商品数量:约10万-50万个 | |
| 200 | -- 生成索引:约5万-20万条 | |
| 201 | - | |
| 202 | -### 运行时间(参考值) | |
| 203 | -- Swing算法:2-4小时 | |
| 204 | -- Session W2V:30-60分钟 | |
| 205 | -- DeepWalk:1-2小时 | |
| 206 | -- 兴趣点聚合:30-60分钟 | |
| 207 | -- 总计:约5-8小时 | |
| 208 | - | |
| 209 | -### 资源占用 | |
| 210 | -- CPU:8-16核 | |
| 211 | -- 内存:8-16GB | |
| 212 | -- 磁盘:输出文件约500MB-2GB | |
| 213 | - | |
| 214 | -## 优化建议 | |
| 215 | - | |
| 216 | -### 1. 算法层面 | |
| 217 | -- **Swing算法**:可以使用C++版本(已有实现),性能提升10倍 | |
| 218 | -- **并行化**:将不同算法分配到不同机器并行运行 | |
| 219 | -- **增量更新**:对于变化不大的索引,考虑增量更新 | |
| 220 | - | |
| 221 | -### 2. 工程层面 | |
| 222 | -- **分布式计算**:使用Spark/Flink处理大规模数据 | |
| 223 | -- **缓存中间结果**:避免重复计算 | |
| 224 | -- **数据采样**:调试阶段使用采样数据 | |
| 225 | - | |
| 226 | -### 3. 业务层面 | |
| 227 | -- **A/B测试**:对比不同算法的效果 | |
| 228 | -- **融合策略**:组合多个算法的结果 | |
| 229 | -- **实时更新**:结合实时计算补充离线索引 | |
| 230 | - | |
| 231 | -## 后续扩展 | |
| 232 | - | |
| 233 | -### 1. 新增算法 | |
| 234 | -- ItemCF(物品协同过滤) | |
| 235 | -- Node2Vec(带权重的图游走) | |
| 236 | -- Graph Neural Network(图神经网络) | |
| 237 | - | |
| 238 | -### 2. 新增维度 | |
| 239 | -- 价格区间 | |
| 240 | -- 品牌 | |
| 241 | -- 标签组合 | |
| 242 | -- 用户画像更多维度 | |
| 243 | - | |
| 244 | -### 3. 实时化 | |
| 245 | -- 实时更新热门商品 | |
| 246 | -- 实时更新新品列表 | |
| 247 | -- 实时i2i相似度计算 | |
| 248 | - | |
| 249 | -### 4. 个性化 | |
| 250 | -- 结合用户画像的个性化排序 | |
| 251 | -- 多目标优化(点击率、转化率、GMV) | |
| 252 | -- 强化学习排序 | |
| 253 | - | |
| 254 | -## 维护说明 | |
| 255 | - | |
| 256 | -### 定期检查 | |
| 257 | -- 每周检查日志,确保任务正常运行 | |
| 258 | -- 每月检查索引质量,调整参数 | |
| 259 | -- 每季度评估算法效果,优化策略 | |
| 260 | - | |
| 261 | -### 监控指标 | |
| 262 | -- 任务执行成功率 | |
| 263 | -- 索引生成数量 | |
| 264 | -- Redis命中率 | |
| 265 | -- 推荐点击率/转化率 | |
| 266 | - | |
| 267 | -## 联系方式 | |
| 268 | - | |
| 269 | -如有问题或建议,请联系推荐系统团队。 | |
| 270 | - | |
| 271 | ---- | |
| 272 | - | |
| 273 | -**文档版本**: v1.0 | |
| 274 | -**最后更新**: 2025-10-16 | |
| 275 | -**作者**: 推荐系统团队 | |
| 276 | - |
offline_tasks/QUICKSTART_NEW.md deleted
| ... | ... | @@ -1,321 +0,0 @@ |
| 1 | -# 快速开始 - 新版本 | |
| 2 | - | |
| 3 | -## 🚀 5分钟快速上手 | |
| 4 | - | |
| 5 | -### 1. 安装依赖 | |
| 6 | - | |
| 7 | -```bash | |
| 8 | -cd /home/tw/recommendation | |
| 9 | -pip install -r requirements.txt | |
| 10 | -``` | |
| 11 | - | |
| 12 | -**新增依赖**: `elasticsearch>=8.0.0` | |
| 13 | - | |
| 14 | -### 2. 测试ES连接 | |
| 15 | - | |
| 16 | -```bash | |
| 17 | -cd offline_tasks | |
| 18 | -python scripts/test_es_connection.py | |
| 19 | -``` | |
| 20 | - | |
| 21 | -如果看到 ✓ 表示测试通过。 | |
| 22 | - | |
| 23 | -### 3. 运行所有任务 | |
| 24 | - | |
| 25 | -```bash | |
| 26 | -python run_all.py | |
| 27 | -``` | |
| 28 | - | |
| 29 | -就这么简单!不需要任何参数。 | |
| 30 | - | |
| 31 | -### 4. 加载到Redis | |
| 32 | - | |
| 33 | -```bash | |
| 34 | -python scripts/load_index_to_redis.py | |
| 35 | -``` | |
| 36 | - | |
| 37 | -## 📋 运行单个任务 | |
| 38 | - | |
| 39 | -### i2i相似索引 | |
| 40 | - | |
| 41 | -```bash | |
| 42 | -# Swing算法 | |
| 43 | -python scripts/i2i_swing.py --lookback_days 30 --top_n 50 --time_decay | |
| 44 | - | |
| 45 | -# Session W2V | |
| 46 | -python scripts/i2i_session_w2v.py --lookback_days 30 --top_n 50 --save_model | |
| 47 | - | |
| 48 | -# DeepWalk | |
| 49 | -python scripts/i2i_deepwalk.py --lookback_days 30 --top_n 50 --save_model | |
| 50 | - | |
| 51 | -# 内容相似(ES向量)- 无需参数! | |
| 52 | -python scripts/i2i_content_similar.py | |
| 53 | -``` | |
| 54 | - | |
| 55 | -### 兴趣聚合 | |
| 56 | - | |
| 57 | -```bash | |
| 58 | -python scripts/interest_aggregation.py --lookback_days 30 --top_n 1000 | |
| 59 | -``` | |
| 60 | - | |
| 61 | -## 🎯 主要变化 | |
| 62 | - | |
| 63 | -### 简化!简化!简化! | |
| 64 | - | |
| 65 | -#### 之前 (v1.0) | |
| 66 | -```bash | |
| 67 | -python run_all.py \ | |
| 68 | - --lookback_days 30 \ | |
| 69 | - --top_n 50 \ | |
| 70 | - --skip-interest \ | |
| 71 | - --only-content \ | |
| 72 | - --debug | |
| 73 | -``` | |
| 74 | - | |
| 75 | -#### 现在 (v2.0) | |
| 76 | -```bash | |
| 77 | -python run_all.py | |
| 78 | -# 或 | |
| 79 | -python run_all.py --debug # 启用debug模式 | |
| 80 | -``` | |
| 81 | - | |
| 82 | -### 内容相似索引 | |
| 83 | - | |
| 84 | -#### 之前 | |
| 85 | -- 1个索引: `i2i_content_hybrid_*.txt` | |
| 86 | -- 基于: 商品属性(分类、供应商等) | |
| 87 | -- 参数: `--method hybrid --top_n 50` | |
| 88 | - | |
| 89 | -#### 现在 | |
| 90 | -- **2个索引**: | |
| 91 | - - `i2i_content_name_*.txt` (名称向量) | |
| 92 | - - `i2i_content_pic_*.txt` (图片向量) | |
| 93 | -- 基于: Elasticsearch深度学习向量 | |
| 94 | -- 参数: **无需参数!** | |
| 95 | - | |
| 96 | -## 📊 输出文件 | |
| 97 | - | |
| 98 | -### 文件位置 | |
| 99 | -``` | |
| 100 | -offline_tasks/output/ | |
| 101 | -├── i2i_swing_20251017.txt # Swing相似索引 | |
| 102 | -├── i2i_session_w2v_20251017.txt # Session W2V相似索引 | |
| 103 | -├── i2i_deepwalk_20251017.txt # DeepWalk相似索引 | |
| 104 | -├── i2i_content_name_20251017.txt # 名称向量相似索引 ⭐新 | |
| 105 | -├── i2i_content_pic_20251017.txt # 图片向量相似索引 ⭐新 | |
| 106 | -├── interest_aggregation_hot_20251017.txt # 热门商品 | |
| 107 | -├── interest_aggregation_cart_20251017.txt # 加购商品 | |
| 108 | -├── interest_aggregation_new_20251017.txt # 新品 | |
| 109 | -└── interest_aggregation_global_20251017.txt # 全局热门 | |
| 110 | -``` | |
| 111 | - | |
| 112 | -### 文件格式 | |
| 113 | -``` | |
| 114 | -item_id \t item_name \t similar_id1:score1,similar_id2:score2,... | |
| 115 | -``` | |
| 116 | - | |
| 117 | -## 🔍 查询示例 | |
| 118 | - | |
| 119 | -### Python查询 | |
| 120 | - | |
| 121 | -```python | |
| 122 | -import redis | |
| 123 | -import json | |
| 124 | - | |
| 125 | -# 连接Redis | |
| 126 | -r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) | |
| 127 | - | |
| 128 | -# 1. 获取Swing相似商品 | |
| 129 | -similar = json.loads(r.get('item:similar:swing:123456')) | |
| 130 | -# 返回: [[234567, 0.8523], [345678, 0.7842], ...] | |
| 131 | - | |
| 132 | -# 2. 获取名称向量相似商品 ⭐新 | |
| 133 | -similar = json.loads(r.get('item:similar:content_name:123456')) | |
| 134 | -# 返回: [[234567, 0.9234], [345678, 0.8756], ...] | |
| 135 | - | |
| 136 | -# 3. 获取图片向量相似商品 ⭐新 | |
| 137 | -similar = json.loads(r.get('item:similar:content_pic:123456')) | |
| 138 | -# 返回: [[567890, 0.8123], [678901, 0.7856], ...] | |
| 139 | - | |
| 140 | -# 4. 获取热门商品 | |
| 141 | -hot_items = json.loads(r.get('interest:hot:platform:PC')) | |
| 142 | -# 返回: [123456, 234567, 345678, ...] | |
| 143 | -``` | |
| 144 | - | |
| 145 | -### Redis CLI查询 | |
| 146 | - | |
| 147 | -```bash | |
| 148 | -# 连接Redis | |
| 149 | -redis-cli | |
| 150 | - | |
| 151 | -# 查看Swing相似商品 | |
| 152 | -GET item:similar:swing:123456 | |
| 153 | - | |
| 154 | -# 查看名称向量相似商品 ⭐新 | |
| 155 | -GET item:similar:content_name:123456 | |
| 156 | - | |
| 157 | -# 查看图片向量相似商品 ⭐新 | |
| 158 | -GET item:similar:content_pic:123456 | |
| 159 | - | |
| 160 | -# 查看热门商品 | |
| 161 | -GET interest:hot:platform:PC | |
| 162 | -``` | |
| 163 | - | |
| 164 | -## ⚙️ 配置说明 | |
| 165 | - | |
| 166 | -### ES配置 (i2i_content_similar.py) | |
| 167 | - | |
| 168 | -```python | |
| 169 | -ES_CONFIG = { | |
| 170 | - 'host': 'http://localhost:9200', | |
| 171 | - 'index_name': 'spu', | |
| 172 | - 'username': 'essa', | |
| 173 | - 'password': '4hOaLaf41y2VuI8y' | |
| 174 | -} | |
| 175 | -``` | |
| 176 | - | |
| 177 | -### 算法参数 (i2i_content_similar.py) | |
| 178 | - | |
| 179 | -```python | |
| 180 | -TOP_N = 50 # 每个商品返回50个相似商品 | |
| 181 | -KNN_K = 100 # KNN查询返回100个候选 | |
| 182 | -KNN_CANDIDATES = 200 # 候选池大小200 | |
| 183 | -``` | |
| 184 | - | |
| 185 | -### 全局配置 (offline_config.py) | |
| 186 | - | |
| 187 | -```python | |
| 188 | -DEFAULT_LOOKBACK_DAYS = 30 # 回看天数 | |
| 189 | -DEFAULT_I2I_TOP_N = 50 # i2i Top N | |
| 190 | -DEFAULT_INTEREST_TOP_N = 1000 # 兴趣聚合 Top N | |
| 191 | -``` | |
| 192 | - | |
| 193 | -## 🔧 故障排查 | |
| 194 | - | |
| 195 | -### ES连接失败 | |
| 196 | - | |
| 197 | -```bash | |
| 198 | -# 1. 检查ES是否运行 | |
| 199 | -curl -u essa:4hOaLaf41y2VuI8y http://localhost:9200 | |
| 200 | - | |
| 201 | -# 2. 运行测试脚本 | |
| 202 | -python scripts/test_es_connection.py | |
| 203 | - | |
| 204 | -# 3. 检查配置 | |
| 205 | -# 编辑 scripts/i2i_content_similar.py 中的 ES_CONFIG | |
| 206 | -``` | |
| 207 | - | |
| 208 | -### 商品ID不存在 | |
| 209 | - | |
| 210 | -测试脚本默认使用 `item_id = "3302275"`,如果不存在: | |
| 211 | - | |
| 212 | -```python | |
| 213 | -# 编辑 test_es_connection.py | |
| 214 | -test_item_id = "你的商品ID" | |
| 215 | -``` | |
| 216 | - | |
| 217 | -### Redis连接失败 | |
| 218 | - | |
| 219 | -```bash | |
| 220 | -# 检查Redis配置 | |
| 221 | -cat offline_tasks/config/offline_config.py | grep REDIS | |
| 222 | - | |
| 223 | -# 测试Redis连接 | |
| 224 | -redis-cli ping | |
| 225 | -``` | |
| 226 | - | |
| 227 | -### 文件不存在 | |
| 228 | - | |
| 229 | -```bash | |
| 230 | -# 检查output目录 | |
| 231 | -ls -lh offline_tasks/output/ | |
| 232 | - | |
| 233 | -# 查看最新生成的文件 | |
| 234 | -ls -lht offline_tasks/output/ | head -10 | |
| 235 | -``` | |
| 236 | - | |
| 237 | -## 📚 详细文档 | |
| 238 | - | |
| 239 | -- **ES向量相似度**: `scripts/ES_VECTOR_SIMILARITY.md` | |
| 240 | -- **更新说明**: `CONTENT_SIMILARITY_UPDATE.md` | |
| 241 | -- **变更总结**: `CHANGES_SUMMARY.md` | |
| 242 | -- **Redis规范**: `REDIS_DATA_SPEC.md` | |
| 243 | - | |
| 244 | -## 🎓 学习路径 | |
| 245 | - | |
| 246 | -### 新用户 | |
| 247 | -1. 阅读本文档 ✓ | |
| 248 | -2. 运行 `test_es_connection.py` | |
| 249 | -3. 运行 `run_all.py` | |
| 250 | -4. 查看 `output/` 目录 | |
| 251 | -5. 加载到Redis并查询 | |
| 252 | - | |
| 253 | -### 进阶使用 | |
| 254 | -1. 阅读 `ES_VECTOR_SIMILARITY.md` | |
| 255 | -2. 了解向量相似度原理 | |
| 256 | -3. 优化ES查询性能 | |
| 257 | -4. 自定义算法参数 | |
| 258 | - | |
| 259 | -### 开发者 | |
| 260 | -1. 阅读 `CONTENT_SIMILARITY_UPDATE.md` | |
| 261 | -2. 了解技术架构 | |
| 262 | -3. 阅读源代码注释 | |
| 263 | -4. 贡献代码改进 | |
| 264 | - | |
| 265 | -## 🚨 注意事项 | |
| 266 | - | |
| 267 | -### ⚠️ 破坏性变化 | |
| 268 | - | |
| 269 | -1. **i2i_content_similar.py 参数全部改变** | |
| 270 | - - 旧: `--method`, `--top_n`, `--debug` | |
| 271 | - - 新: 无参数 | |
| 272 | - | |
| 273 | -2. **Redis Key格式改变** | |
| 274 | - - 旧: `item:similar:content:{item_id}` | |
| 275 | - - 新: `item:similar:content_name:{item_id}` 和 `item:similar:content_pic:{item_id}` | |
| 276 | - | |
| 277 | -3. **输出文件改变** | |
| 278 | - - 旧: `i2i_content_hybrid_*.txt` | |
| 279 | - - 新: `i2i_content_name_*.txt` 和 `i2i_content_pic_*.txt` | |
| 280 | - | |
| 281 | -### ✅ 向后兼容 | |
| 282 | - | |
| 283 | -- Swing、W2V、DeepWalk 算法不受影响 | |
| 284 | -- 兴趣聚合不受影响 | |
| 285 | -- Redis加载器向后兼容 | |
| 286 | -- 其他i2i索引继续工作 | |
| 287 | - | |
| 288 | -## 💡 最佳实践 | |
| 289 | - | |
| 290 | -### 运行频率 | |
| 291 | -- **行为相似** (Swing, W2V, DeepWalk): 每天 | |
| 292 | -- **内容相似** (名称向量, 图片向量): 每周 | |
| 293 | -- **兴趣聚合**: 每天 | |
| 294 | - | |
| 295 | -### Redis TTL | |
| 296 | -- **行为相似**: 7天 | |
| 297 | -- **内容相似**: 30天 | |
| 298 | -- **兴趣聚合**: 3-7天 | |
| 299 | - | |
| 300 | -### 性能优化 | |
| 301 | -1. 使用 `--debug` 模式调试 | |
| 302 | -2. 先用小数据集测试 | |
| 303 | -3. 定期清理过期数据 | |
| 304 | -4. 监控ES查询性能 | |
| 305 | - | |
| 306 | -## 🎉 总结 | |
| 307 | - | |
| 308 | -新版本大幅简化了使用,主要改进: | |
| 309 | - | |
| 310 | -1. ✅ **无需参数**: `run_all.py` 和 `i2i_content_similar.py` 无需参数 | |
| 311 | -2. ✅ **更强大**: 基于深度学习向量,更准确 | |
| 312 | -3. ✅ **多维度**: 名称 + 图片两个维度 | |
| 313 | -4. ✅ **更快**: ES KNN查询性能优秀 | |
| 314 | -5. ✅ **易维护**: 代码简洁,配置清晰 | |
| 315 | - | |
| 316 | -开始使用新版本,享受更简单、更强大的推荐系统! | |
| 317 | - | |
| 318 | ---- | |
| 319 | - | |
| 320 | -**问题反馈**: 如有问题请查看详细文档或联系开发团队 | |
| 321 | - |
offline_tasks/QUICK_DEBUG_SUMMARY.md deleted
| ... | ... | @@ -1,128 +0,0 @@ |
| 1 | -# Debug功能快速总结 | |
| 2 | - | |
| 3 | -## ✅ 已完成的工作 | |
| 4 | - | |
| 5 | -### 1. 核心组件 | |
| 6 | - | |
| 7 | -| 组件 | 状态 | 说明 | | |
| 8 | -|------|------|------| | |
| 9 | -| `debug_utils.py` | ✅ | Debug工具库(369行) | | |
| 10 | -| `offline_config.py` | ✅ | 新增DEBUG_CONFIG | | |
| 11 | -| `i2i_swing.py` | ✅ | 完整debug支持 | | |
| 12 | -| `run_all.py` | ✅ | 支持--debug参数传递 | | |
| 13 | - | |
| 14 | -### 2. Debug功能特性 | |
| 15 | - | |
| 16 | -#### A. 详细日志输出 | |
| 17 | -```python | |
| 18 | -# 自动记录: | |
| 19 | -- 算法参数 | |
| 20 | -- 数据统计(行数、列数、类型、缺失值) | |
| 21 | -- 处理进度(每N条显示) | |
| 22 | -- 每个步骤的耗时 | |
| 23 | -- 数据分布(行为类型、用户数、商品数) | |
| 24 | -- 中间结果采样 | |
| 25 | -``` | |
| 26 | - | |
| 27 | -#### B. 明文索引文件 | |
| 28 | -``` | |
| 29 | -原始: 12345\t香蕉干\t67890:0.8567,11223:0.7234 | |
| 30 | -明文: [1] i2i:swing:12345(香蕉干) | |
| 31 | - 1. ID:67890(芒果干) - Score:0.8567 | |
| 32 | - 2. ID:11223(菠萝干) - Score:0.7234 | |
| 33 | -``` | |
| 34 | - | |
| 35 | -#### C. 日志文件 | |
| 36 | -``` | |
| 37 | -offline_tasks/logs/debug/i2i_swing_20251016_193000.log | |
| 38 | -offline_tasks/output/debug/i2i_swing_20251016_readable.txt | |
| 39 | -``` | |
| 40 | - | |
| 41 | -## 🚀 使用方法 | |
| 42 | - | |
| 43 | -### 单个脚本 | |
| 44 | -```bash | |
| 45 | -# i2i_swing.py 已支持debug | |
| 46 | -python3 scripts/i2i_swing.py --lookback_days 7 --top_n 10 --debug | |
| 47 | -``` | |
| 48 | - | |
| 49 | -### 所有任务 | |
| 50 | -```bash | |
| 51 | -# run_all.py 已支持debug参数传递 | |
| 52 | -python3 run_all.py --lookback_days 7 --top_n 10 --debug | |
| 53 | -``` | |
| 54 | - | |
| 55 | -## 📊 输出示例 | |
| 56 | - | |
| 57 | -### 控制台输出 | |
| 58 | -``` | |
| 59 | -2025-10-16 19:30:00 - i2i_swing - DEBUG - ============================================================ | |
| 60 | -2025-10-16 19:30:00 - i2i_swing - DEBUG - 算法参数: | |
| 61 | -2025-10-16 19:30:00 - i2i_swing - DEBUG - alpha: 0.5 | |
| 62 | -2025-10-16 19:30:00 - i2i_swing - DEBUG - top_n: 10 | |
| 63 | -2025-10-16 19:30:05 - i2i_swing - INFO - 获取到 15234 条记录 | |
| 64 | -2025-10-16 19:30:05 - i2i_swing - DEBUG - 总行数: 15234 | |
| 65 | -2025-10-16 19:30:05 - i2i_swing - DEBUG - 行为类型分布: | |
| 66 | -2025-10-16 19:30:05 - i2i_swing - DEBUG - addToCart: 8520 (55.93%) | |
| 67 | -2025-10-16 19:30:10 - i2i_swing - INFO - 总用户数: 3456, 总商品数: 2345 | |
| 68 | -``` | |
| 69 | - | |
| 70 | -### 明文文件示例 | |
| 71 | -``` | |
| 72 | -================================================================================ | |
| 73 | -明文索引文件 | |
| 74 | -生成时间: 2025-10-16 19:35:00 | |
| 75 | -描述: Swing算法 i2i相似度推荐 (alpha=0.5, lookback_days=7) | |
| 76 | -总索引数: 2345 | |
| 77 | -================================================================================ | |
| 78 | - | |
| 79 | -[1] i2i:swing:12345(香蕉干) | |
| 80 | --------------------------------------------------------------------------------- | |
| 81 | - 1. ID:67890(芒果干) - Score:0.8567 | |
| 82 | - 2. ID:11223(菠萝干) - Score:0.7234 | |
| 83 | - 3. ID:44556(苹果干) - Score:0.6891 | |
| 84 | -``` | |
| 85 | - | |
| 86 | -## 🔧 Debug工具函数 | |
| 87 | - | |
| 88 | -| 函数 | 功能 | | |
| 89 | -|------|------| | |
| 90 | -| `setup_debug_logger()` | 设置debug日志 | | |
| 91 | -| `log_dataframe_info()` | 记录DataFrame详情 | | |
| 92 | -| `log_dict_stats()` | 记录字典统计 | | |
| 93 | -| `save_readable_index()` | 保存明文索引 | | |
| 94 | -| `fetch_name_mappings()` | 获取ID到名称映射 | | |
| 95 | -| `log_algorithm_params()` | 记录算法参数 | | |
| 96 | -| `log_processing_step()` | 记录处理步骤 | | |
| 97 | - | |
| 98 | -## 📝 待完成 | |
| 99 | - | |
| 100 | -需要为以下脚本添加debug支持(使用相同模式): | |
| 101 | -- [ ] i2i_session_w2v.py | |
| 102 | -- [ ] i2i_deepwalk.py | |
| 103 | -- [ ] i2i_content_similar.py | |
| 104 | -- [ ] interest_aggregation.py | |
| 105 | - | |
| 106 | -## 💡 快速测试 | |
| 107 | - | |
| 108 | -```bash | |
| 109 | -# 1. 测试debug工具 | |
| 110 | -cd /home/tw/recommendation/offline_tasks | |
| 111 | -python3 -c "from scripts.debug_utils import *; print('✓ Debug utils OK')" | |
| 112 | - | |
| 113 | -# 2. 测试i2i_swing debug模式 | |
| 114 | -python3 scripts/i2i_swing.py --lookback_days 1 --top_n 5 --debug | |
| 115 | - | |
| 116 | -# 3. 查看输出 | |
| 117 | -ls -lh logs/debug/ | |
| 118 | -ls -lh output/debug/ | |
| 119 | -``` | |
| 120 | - | |
| 121 | -## 📖 完整文档 | |
| 122 | - | |
| 123 | -详细使用指南:`DEBUG_GUIDE.md` | |
| 124 | - | |
| 125 | ---- | |
| 126 | - | |
| 127 | -**状态**: 🚧 进行中 (i2i_swing.py完成,其他脚本待更新) | |
| 128 | -**下一步**: 批量更新其他4个脚本的debug支持 |
offline_tasks/STRUCTURE.md deleted
| ... | ... | @@ -1,303 +0,0 @@ |
| 1 | -# 项目目录结构 | |
| 2 | - | |
| 3 | -``` | |
| 4 | -/home/tw/recommendation/ | |
| 5 | -│ | |
| 6 | -├── db_service.py # 数据库连接服务(共享模块) | |
| 7 | -├── requirements.txt # Python依赖包列表 | |
| 8 | -│ | |
| 9 | -├── offline_tasks/ # 【离线任务主目录】 | |
| 10 | -│ │ | |
| 11 | -│ ├── config/ # 配置目录 | |
| 12 | -│ │ └── offline_config.py # 离线任务配置文件 | |
| 13 | -│ │ - 数据库配置 | |
| 14 | -│ │ - Redis配置 | |
| 15 | -│ │ - 算法参数配置 | |
| 16 | -│ │ - 时间配置 | |
| 17 | -│ │ | |
| 18 | -│ ├── scripts/ # 脚本目录 | |
| 19 | -│ │ ├── i2i_swing.py # Swing算法实现 | |
| 20 | -│ │ │ 输出: i2i_swing_YYYYMMDD.txt | |
| 21 | -│ │ │ | |
| 22 | -│ │ ├── i2i_session_w2v.py # Session Word2Vec算法 | |
| 23 | -│ │ │ 输出: i2i_session_w2v_YYYYMMDD.txt | |
| 24 | -│ │ │ session_w2v_model_YYYYMMDD.model | |
| 25 | -│ │ │ | |
| 26 | -│ │ ├── i2i_deepwalk.py # DeepWalk算法 | |
| 27 | -│ │ │ 输出: i2i_deepwalk_YYYYMMDD.txt | |
| 28 | -│ │ │ deepwalk_model_YYYYMMDD.model | |
| 29 | -│ │ │ item_graph_YYYYMMDD.txt | |
| 30 | -│ │ │ | |
| 31 | -│ │ ├── interest_aggregation.py # 兴趣点聚合索引生成 | |
| 32 | -│ │ │ 输出: interest_aggregation_hot_YYYYMMDD.txt | |
| 33 | -│ │ │ interest_aggregation_cart_YYYYMMDD.txt | |
| 34 | -│ │ │ interest_aggregation_new_YYYYMMDD.txt | |
| 35 | -│ │ │ interest_aggregation_global_YYYYMMDD.txt | |
| 36 | -│ │ │ | |
| 37 | -│ │ └── load_index_to_redis.py # 索引加载到Redis | |
| 38 | -│ │ | |
| 39 | -│ ├── output/ # 输出目录(自动创建) | |
| 40 | -│ │ ├── i2i_swing_*.txt # Swing算法输出 | |
| 41 | -│ │ ├── i2i_session_w2v_*.txt # Session W2V输出 | |
| 42 | -│ │ ├── i2i_deepwalk_*.txt # DeepWalk输出 | |
| 43 | -│ │ ├── interest_aggregation_* # 兴趣点聚合输出 | |
| 44 | -│ │ ├── *.model # 训练的模型文件 | |
| 45 | -│ │ └── item_graph_*.txt # 物品图结构 | |
| 46 | -│ │ | |
| 47 | -│ ├── logs/ # 日志目录(自动创建) | |
| 48 | -│ │ ├── run_all_*.log # 总调度日志 | |
| 49 | -│ │ ├── load_redis.log # Redis加载日志 | |
| 50 | -│ │ └── cron.log # 定时任务日志 | |
| 51 | -│ │ | |
| 52 | -│ ├── run_all.py # 【主调度脚本】 | |
| 53 | -│ │ 统一运行所有离线任务 | |
| 54 | -│ │ | |
| 55 | -│ ├── install.sh # 安装脚本 | |
| 56 | -│ │ 自动安装依赖和初始化 | |
| 57 | -│ │ | |
| 58 | -│ ├── test_connection.py # 连接测试脚本 | |
| 59 | -│ │ 测试数据库和Redis连接 | |
| 60 | -│ │ | |
| 61 | -│ ├── example_query_redis.py # Redis查询示例 | |
| 62 | -│ │ 演示如何查询索引 | |
| 63 | -│ │ | |
| 64 | -│ ├── README.md # 详细文档 | |
| 65 | -│ ├── QUICKSTART.md # 快速开始指南 | |
| 66 | -│ ├── PROJECT_SUMMARY.md # 项目总结 | |
| 67 | -│ └── STRUCTURE.md # 本文档 | |
| 68 | -│ | |
| 69 | -├── item_sim.py # 原有的物品相似度脚本(参考) | |
| 70 | -├── hot/ # 原有的热门商品模块(参考) | |
| 71 | -├── collaboration/ # 原有的协同过滤模块(参考) | |
| 72 | -└── graphembedding/ # 原有的图嵌入模块(参考) | |
| 73 | -``` | |
| 74 | - | |
| 75 | -## 数据流向图 | |
| 76 | - | |
| 77 | -``` | |
| 78 | -┌─────────────────┐ | |
| 79 | -│ 数据源 │ | |
| 80 | -│ (SelectDB) │ | |
| 81 | -└────────┬────────┘ | |
| 82 | - │ | |
| 83 | - │ SQL查询 | |
| 84 | - │ | |
| 85 | - ▼ | |
| 86 | -┌─────────────────────────────────────────────────────────┐ | |
| 87 | -│ 离线任务处理 │ | |
| 88 | -│ │ | |
| 89 | -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ | |
| 90 | -│ │ Swing算法 │ │ Session W2V │ │ DeepWalk算法 │ │ | |
| 91 | -│ │ │ │ │ │ │ │ | |
| 92 | -│ │ 用户行为共现 │ │ 会话序列 │ │ 图随机游走 │ │ | |
| 93 | -│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ | |
| 94 | -│ │ │ │ │ | |
| 95 | -│ └─────────────────┴─────────────────┘ │ | |
| 96 | -│ │ │ | |
| 97 | -│ │ i2i相似度索引 │ | |
| 98 | -│ ▼ │ | |
| 99 | -│ ┌─────────────────┐ │ | |
| 100 | -│ │ 输出文件(.txt) │ │ | |
| 101 | -│ └─────────────────┘ │ | |
| 102 | -│ │ | |
| 103 | -│ ┌──────────────────────────────────────────────────┐ │ | |
| 104 | -│ │ 兴趣点聚合 │ │ | |
| 105 | -│ │ │ │ | |
| 106 | -│ │ 按维度分组: 平台/国家/客户类型/分类 │ │ | |
| 107 | -│ │ 按类型分组: 热门/加购/新品/全局 │ │ | |
| 108 | -│ │ 时间衰减 + 行为权重 │ │ | |
| 109 | -│ └────────────────────┬─────────────────────────────┘ │ | |
| 110 | -│ │ │ | |
| 111 | -│ │ 多维度索引 │ | |
| 112 | -│ ▼ │ | |
| 113 | -│ ┌─────────────────┐ │ | |
| 114 | -│ │ 输出文件(.txt) │ │ | |
| 115 | -│ └─────────────────┘ │ | |
| 116 | -└─────────────────────────────────────────────────────────┘ | |
| 117 | - │ | |
| 118 | - │ 加载 | |
| 119 | - ▼ | |
| 120 | - ┌─────────────────┐ | |
| 121 | - │ Redis │ | |
| 122 | - │ (在线索引) │ | |
| 123 | - └────────┬────────┘ | |
| 124 | - │ | |
| 125 | - │ 查询 | |
| 126 | - ▼ | |
| 127 | - ┌─────────────────┐ | |
| 128 | - │ 在线推荐服务 │ | |
| 129 | - │ │ | |
| 130 | - │ - 详情页推荐 │ | |
| 131 | - │ - 首页推荐 │ | |
| 132 | - │ - 搜索推荐 │ | |
| 133 | - └──────────────────┘ | |
| 134 | -``` | |
| 135 | - | |
| 136 | -## 核心模块说明 | |
| 137 | - | |
| 138 | -### 1. 配置模块 | |
| 139 | -**文件**: `config/offline_config.py` | |
| 140 | - | |
| 141 | -**职责**: | |
| 142 | -- 数据库连接配置 | |
| 143 | -- Redis连接配置 | |
| 144 | -- 算法超参数配置 | |
| 145 | -- 行为权重配置 | |
| 146 | -- 时间范围配置 | |
| 147 | - | |
| 148 | -### 2. i2i相似度模块 | |
| 149 | - | |
| 150 | -#### 2.1 Swing算法 | |
| 151 | -**文件**: `scripts/i2i_swing.py` | |
| 152 | - | |
| 153 | -**输入**: 用户行为数据(user_id, item_id, event_type, create_time) | |
| 154 | - | |
| 155 | -**核心逻辑**: | |
| 156 | -```python | |
| 157 | -# 对于物品i和j,计算它们的Swing相似度 | |
| 158 | -for user_u in common_users: | |
| 159 | - for user_v in common_users: | |
| 160 | - common_items = items_u ∩ items_v | |
| 161 | - sim(i, j) += 1 / (alpha + |common_items|) | |
| 162 | -``` | |
| 163 | - | |
| 164 | -**输出**: `i2i_swing_YYYYMMDD.txt` | |
| 165 | -``` | |
| 166 | -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... | |
| 167 | -``` | |
| 168 | - | |
| 169 | -#### 2.2 Session Word2Vec | |
| 170 | -**文件**: `scripts/i2i_session_w2v.py` | |
| 171 | - | |
| 172 | -**输入**: 用户会话序列 | |
| 173 | - | |
| 174 | -**核心逻辑**: | |
| 175 | -1. 按时间间隔切分用户会话 | |
| 176 | -2. 训练Word2Vec模型 | |
| 177 | -3. 计算物品向量相似度 | |
| 178 | - | |
| 179 | -**输出**: | |
| 180 | -- `i2i_session_w2v_YYYYMMDD.txt` | |
| 181 | -- `session_w2v_model_YYYYMMDD.model` | |
| 182 | - | |
| 183 | -#### 2.3 DeepWalk | |
| 184 | -**文件**: `scripts/i2i_deepwalk.py` | |
| 185 | - | |
| 186 | -**输入**: 用户-物品交互数据 | |
| 187 | - | |
| 188 | -**核心逻辑**: | |
| 189 | -1. 构建物品共现图 | |
| 190 | -2. 执行随机游走生成序列 | |
| 191 | -3. 训练Word2Vec模型 | |
| 192 | -4. 计算物品向量相似度 | |
| 193 | - | |
| 194 | -**输出**: | |
| 195 | -- `i2i_deepwalk_YYYYMMDD.txt` | |
| 196 | -- `deepwalk_model_YYYYMMDD.model` | |
| 197 | -- `item_graph_YYYYMMDD.txt` | |
| 198 | - | |
| 199 | -### 3. 兴趣点聚合模块 | |
| 200 | -**文件**: `scripts/interest_aggregation.py` | |
| 201 | - | |
| 202 | -**输入**: 用户行为数据 + 用户特征 | |
| 203 | - | |
| 204 | -**核心逻辑**: | |
| 205 | -```python | |
| 206 | -# 按维度聚合 | |
| 207 | -for each behavior: | |
| 208 | - weight = behavior_weight * time_weight | |
| 209 | - aggregations[dimension_key][item_id] += weight | |
| 210 | - | |
| 211 | -# 排序并输出top N | |
| 212 | -for dimension_key, items in aggregations: | |
| 213 | - output top_n items sorted by weight | |
| 214 | -``` | |
| 215 | - | |
| 216 | -**维度组合**: | |
| 217 | -- 单维度: platform, country, customer_type, category_level2, category_level3 | |
| 218 | -- 组合维度: platform_country, platform_customer, country_customer, platform_country_customer | |
| 219 | - | |
| 220 | -**列表类型**: | |
| 221 | -- hot: 最近180天高交互 | |
| 222 | -- cart: 加购行为 | |
| 223 | -- new: 新品(最近90天上架) | |
| 224 | -- global: 全局(所有数据) | |
| 225 | - | |
| 226 | -**输出**: | |
| 227 | -- `interest_aggregation_hot_YYYYMMDD.txt` | |
| 228 | -- `interest_aggregation_cart_YYYYMMDD.txt` | |
| 229 | -- `interest_aggregation_new_YYYYMMDD.txt` | |
| 230 | -- `interest_aggregation_global_YYYYMMDD.txt` | |
| 231 | - | |
| 232 | -### 4. Redis加载模块 | |
| 233 | -**文件**: `scripts/load_index_to_redis.py` | |
| 234 | - | |
| 235 | -**职责**: 将生成的索引文件加载到Redis | |
| 236 | - | |
| 237 | -**Redis Key格式**: | |
| 238 | -- i2i索引: `i2i:{algorithm}:{item_id}` | |
| 239 | -- 兴趣点索引: `interest:{list_type}:{dimension_key}` | |
| 240 | - | |
| 241 | -**示例**: | |
| 242 | -``` | |
| 243 | -i2i:swing:123456 -> "234567:0.8523,345678:0.7842,..." | |
| 244 | -interest:hot:platform:PC -> "12345:98.52,23456:87.34,..." | |
| 245 | -``` | |
| 246 | - | |
| 247 | -### 5. 统一调度模块 | |
| 248 | -**文件**: `run_all.py` | |
| 249 | - | |
| 250 | -**职责**: 按顺序运行所有离线任务 | |
| 251 | - | |
| 252 | -**执行流程**: | |
| 253 | -1. 运行Swing算法 | |
| 254 | -2. 运行Session W2V | |
| 255 | -3. 运行DeepWalk | |
| 256 | -4. 运行兴趣点聚合 | |
| 257 | -5. 记录日志和统计 | |
| 258 | - | |
| 259 | -## 使用场景映射 | |
| 260 | - | |
| 261 | -| 业务场景 | 使用的索引 | Redis Key示例 | | |
| 262 | -|---------|-----------|--------------| | |
| 263 | -| 详情页 - 大家都在看 | i2i相似度 | `i2i:swing:{item_id}` | | |
| 264 | -| 首页 - 猜你喜欢 | 兴趣点聚合 | `interest:hot:platform_country:{platform}_{country}` | | |
| 265 | -| 搜索 - 相关推荐 | 兴趣点聚合 + i2i | `interest:global:category_level2:{cat_id}` | | |
| 266 | -| 购物车 - 可能喜欢 | 兴趣点聚合(cart) | `interest:cart:customer_type:{type}` | | |
| 267 | -| 新品推荐 | 兴趣点聚合(new) | `interest:new:platform:{platform}` | | |
| 268 | - | |
| 269 | -## 配置调优参数 | |
| 270 | - | |
| 271 | -### 时间相关 | |
| 272 | -- `LOOKBACK_DAYS`: 730 (2年) | |
| 273 | -- `RECENT_DAYS`: 180 (热门商品统计) | |
| 274 | -- `NEW_DAYS`: 90 (新品定义) | |
| 275 | -- `decay_factor`: 0.95 (时间衰减) | |
| 276 | - | |
| 277 | -### 算法相关 | |
| 278 | -- Swing `alpha`: 0.5 | |
| 279 | -- W2V `vector_size`: 128 | |
| 280 | -- W2V `window_size`: 5 | |
| 281 | -- DeepWalk `num_walks`: 10 | |
| 282 | -- DeepWalk `walk_length`: 40 | |
| 283 | - | |
| 284 | -### 输出相关 | |
| 285 | -- i2i `top_n`: 50 | |
| 286 | -- 兴趣点 `top_n`: 1000 | |
| 287 | - | |
| 288 | -## 性能优化要点 | |
| 289 | - | |
| 290 | -1. **Swing算法**: 使用C++版本可提升10倍性能 | |
| 291 | -2. **并行化**: 将不同算法分配到不同机器 | |
| 292 | -3. **增量更新**: 只更新变化的部分 | |
| 293 | -4. **数据采样**: 调试时使用采样数据 | |
| 294 | -5. **批量加载**: Redis使用pipeline批量加载 | |
| 295 | - | |
| 296 | -## 监控指标 | |
| 297 | - | |
| 298 | -- 任务执行时间 | |
| 299 | -- 生成索引数量 | |
| 300 | -- Redis内存占用 | |
| 301 | -- 推荐点击率 | |
| 302 | -- 推荐转化率 | |
| 303 | - |
offline_tasks/UPDATE_CONFIG_GUIDE.md deleted
| ... | ... | @@ -1,205 +0,0 @@ |
| 1 | -# 配置参数调整指南 | |
| 2 | - | |
| 3 | -## 📝 概述 | |
| 4 | - | |
| 5 | -所有默认参数现在都集中在配置文件中,便于统一管理和调整。 | |
| 6 | - | |
| 7 | -## 🎯 主要默认参数 | |
| 8 | - | |
| 9 | -### 在 `config/offline_config.py` 中配置: | |
| 10 | - | |
| 11 | -```python | |
| 12 | -# 时间配置 | |
| 13 | -DEFAULT_LOOKBACK_DAYS = 30 # 默认回看天数 | |
| 14 | -DEFAULT_RECENT_DAYS = 7 # 默认最近天数 | |
| 15 | - | |
| 16 | -# i2i算法参数 | |
| 17 | -DEFAULT_I2I_TOP_N = 50 # 默认返回Top N个相似商品 | |
| 18 | - | |
| 19 | -# 兴趣聚合参数 | |
| 20 | -DEFAULT_INTEREST_TOP_N = 1000 # 默认每个key返回Top N个商品 | |
| 21 | -``` | |
| 22 | - | |
| 23 | -## 🔧 调试与生产切换 | |
| 24 | - | |
| 25 | -### 调试阶段(当前配置) | |
| 26 | - | |
| 27 | -```python | |
| 28 | -DEFAULT_LOOKBACK_DAYS = 30 # 30天数据,快速验证 | |
| 29 | -DEFAULT_RECENT_DAYS = 7 # 7天最近数据 | |
| 30 | -DEFAULT_I2I_TOP_N = 50 # Top 50 | |
| 31 | -DEFAULT_INTEREST_TOP_N = 1000 # Top 1000 | |
| 32 | -``` | |
| 33 | - | |
| 34 | -**预估运行时间**:30-60分钟 | |
| 35 | -**内存占用**:2-4GB | |
| 36 | - | |
| 37 | -### 生产环境配置 | |
| 38 | - | |
| 39 | -```python | |
| 40 | -DEFAULT_LOOKBACK_DAYS = 730 # 2年历史数据,更准确 | |
| 41 | -DEFAULT_RECENT_DAYS = 180 # 半年最近数据 | |
| 42 | -DEFAULT_I2I_TOP_N = 50 # Top 50 | |
| 43 | -DEFAULT_INTEREST_TOP_N = 1000 # Top 1000 | |
| 44 | -``` | |
| 45 | - | |
| 46 | -**预估运行时间**:6-10小时 | |
| 47 | -**内存占用**:8-16GB | |
| 48 | - | |
| 49 | -## 🚀 使用方式 | |
| 50 | - | |
| 51 | -### 1. 使用默认配置运行 | |
| 52 | - | |
| 53 | -```bash | |
| 54 | -# 使用配置文件中的默认值(当前为30天) | |
| 55 | -python3 run_all.py | |
| 56 | -``` | |
| 57 | - | |
| 58 | -### 2. 临时覆盖默认值 | |
| 59 | - | |
| 60 | -```bash | |
| 61 | -# 临时使用不同的参数,不修改配置文件 | |
| 62 | -python3 run_all.py --lookback_days 7 --top_n 20 | |
| 63 | -``` | |
| 64 | - | |
| 65 | -### 3. 修改配置文件(推荐) | |
| 66 | - | |
| 67 | -编辑 `config/offline_config.py`: | |
| 68 | - | |
| 69 | -```python | |
| 70 | -# 调试完成后,改为生产配置 | |
| 71 | -DEFAULT_LOOKBACK_DAYS = 730 | |
| 72 | -DEFAULT_RECENT_DAYS = 180 | |
| 73 | -``` | |
| 74 | - | |
| 75 | -然后运行: | |
| 76 | - | |
| 77 | -```bash | |
| 78 | -python3 run_all.py | |
| 79 | -``` | |
| 80 | - | |
| 81 | -## 📊 各脚本的默认参数 | |
| 82 | - | |
| 83 | -所有脚本都会从配置文件读取默认值: | |
| 84 | - | |
| 85 | -| 脚本 | 参数 | 默认值 | 来源 | | |
| 86 | -|------|------|--------|------| | |
| 87 | -| `i2i_swing.py` | `--lookback_days` | 30 | `DEFAULT_LOOKBACK_DAYS` | | |
| 88 | -| `i2i_swing.py` | `--top_n` | 50 | `DEFAULT_I2I_TOP_N` | | |
| 89 | -| `i2i_session_w2v.py` | `--lookback_days` | 30 | `DEFAULT_LOOKBACK_DAYS` | | |
| 90 | -| `i2i_session_w2v.py` | `--top_n` | 50 | `DEFAULT_I2I_TOP_N` | | |
| 91 | -| `i2i_deepwalk.py` | `--lookback_days` | 30 | `DEFAULT_LOOKBACK_DAYS` | | |
| 92 | -| `i2i_deepwalk.py` | `--top_n` | 50 | `DEFAULT_I2I_TOP_N` | | |
| 93 | -| `i2i_content_similar.py` | `--top_n` | 50 | `DEFAULT_I2I_TOP_N` | | |
| 94 | -| `interest_aggregation.py` | `--lookback_days` | 30 | `DEFAULT_LOOKBACK_DAYS` | | |
| 95 | -| `interest_aggregation.py` | `--top_n` | 1000 | `DEFAULT_INTEREST_TOP_N` | | |
| 96 | - | |
| 97 | -## 💡 调试建议 | |
| 98 | - | |
| 99 | -### 第一次运行(验证流程) | |
| 100 | - | |
| 101 | -```bash | |
| 102 | -# 使用最小数据量快速验证 | |
| 103 | -python3 run_all.py --lookback_days 7 --top_n 10 | |
| 104 | -``` | |
| 105 | - | |
| 106 | -### 第二次运行(调试参数) | |
| 107 | - | |
| 108 | -```python | |
| 109 | -# 修改配置文件为30天 | |
| 110 | -DEFAULT_LOOKBACK_DAYS = 30 | |
| 111 | -``` | |
| 112 | - | |
| 113 | -```bash | |
| 114 | -python3 run_all.py | |
| 115 | -``` | |
| 116 | - | |
| 117 | -### 第三次运行(生产环境) | |
| 118 | - | |
| 119 | -```python | |
| 120 | -# 修改配置文件为730天 | |
| 121 | -DEFAULT_LOOKBACK_DAYS = 730 | |
| 122 | -DEFAULT_RECENT_DAYS = 180 | |
| 123 | -``` | |
| 124 | - | |
| 125 | -```bash | |
| 126 | -python3 run_all.py | |
| 127 | -``` | |
| 128 | - | |
| 129 | -## 🔍 其他可调整的配置 | |
| 130 | - | |
| 131 | -### i2i算法详细配置 | |
| 132 | - | |
| 133 | -在 `offline_config.py` 的 `I2I_CONFIG` 中: | |
| 134 | - | |
| 135 | -```python | |
| 136 | -I2I_CONFIG = { | |
| 137 | - 'swing': { | |
| 138 | - 'alpha': 0.5, # swing算法的alpha参数 | |
| 139 | - 'threshold1': 0.5, # 交互强度阈值1 | |
| 140 | - 'threshold2': 0.5, # 交互强度阈值2 | |
| 141 | - 'max_sim_list_len': 300, # 最大相似列表长度 | |
| 142 | - 'top_n': 50, # 输出top N个相似商品 | |
| 143 | - }, | |
| 144 | - # ...其他算法配置 | |
| 145 | -} | |
| 146 | -``` | |
| 147 | - | |
| 148 | -### 兴趣聚合详细配置 | |
| 149 | - | |
| 150 | -```python | |
| 151 | -INTEREST_AGGREGATION_CONFIG = { | |
| 152 | - 'top_n': 1000, # 每个key生成前N个商品 | |
| 153 | - 'time_decay_factor': 0.95, # 时间衰减因子(每30天) | |
| 154 | - 'min_interaction_count': 2, # 最小交互次数 | |
| 155 | - | |
| 156 | - 'behavior_weights': { | |
| 157 | - 'click': 1.0, | |
| 158 | - 'addToCart': 3.0, | |
| 159 | - 'addToPool': 2.0, | |
| 160 | - 'contactFactory': 5.0, | |
| 161 | - 'purchase': 10.0, | |
| 162 | - }, | |
| 163 | -} | |
| 164 | -``` | |
| 165 | - | |
| 166 | -## 📌 注意事项 | |
| 167 | - | |
| 168 | -1. **调试优先**:先用小数据量(7-30天)验证流程 | |
| 169 | -2. **逐步扩大**:确认无误后再增加到生产数据量 | |
| 170 | -3. **监控资源**:注意内存和磁盘空间使用情况 | |
| 171 | -4. **保存配置**:在配置文件中注释记录不同场景的参数值 | |
| 172 | - | |
| 173 | -## 🎯 快速切换环境 | |
| 174 | - | |
| 175 | -创建不同的配置副本: | |
| 176 | - | |
| 177 | -```bash | |
| 178 | -# 备份当前配置 | |
| 179 | -cp config/offline_config.py config/offline_config_debug.py | |
| 180 | -cp config/offline_config.py config/offline_config_prod.py | |
| 181 | - | |
| 182 | -# 使用不同配置 | |
| 183 | -cp config/offline_config_debug.py config/offline_config.py # 调试模式 | |
| 184 | -cp config/offline_config_prod.py config/offline_config.py # 生产模式 | |
| 185 | -``` | |
| 186 | - | |
| 187 | -## ✅ 验证配置 | |
| 188 | - | |
| 189 | -查看当前默认值: | |
| 190 | - | |
| 191 | -```bash | |
| 192 | -python3 -c "from config.offline_config import *; print(f'LOOKBACK_DAYS: {DEFAULT_LOOKBACK_DAYS}')" | |
| 193 | -``` | |
| 194 | - | |
| 195 | -查看帮助信息: | |
| 196 | - | |
| 197 | -```bash | |
| 198 | -python3 run_all.py --help | |
| 199 | -``` | |
| 200 | - | |
| 201 | ---- | |
| 202 | - | |
| 203 | -**配置文件位置**: `config/offline_config.py` | |
| 204 | -**当前默认配置**: 30天调试模式 | |
| 205 | -**建议**: 调试通过后修改为730天生产模式 |
offline_tasks/config/offline_config.py
| ... | ... | @@ -28,7 +28,7 @@ os.makedirs(LOG_DIR, exist_ok=True) |
| 28 | 28 | # ============================================================================ |
| 29 | 29 | |
| 30 | 30 | # 时间配置(建议先用小数值调试,确认无误后再改为大数值) |
| 31 | -DEFAULT_LOOKBACK_DAYS = 730 # 默认回看天数(调试用30天,生产可改为730天) | |
| 31 | +DEFAULT_LOOKBACK_DAYS = 400 # 默认回看天数(调试用30天,生产可改为730天) | |
| 32 | 32 | DEFAULT_RECENT_DAYS = 180 # 默认最近天数(调试用7天,生产可改为180天) |
| 33 | 33 | |
| 34 | 34 | # i2i算法默认参数 | ... | ... |
offline_tasks/COMPLETE_INDEX_LIST.md renamed to offline_tasks/doc/COMPLETE_INDEX_LIST.md
offline_tasks/DATABASE_SETUP.md renamed to offline_tasks/doc/DATABASE_SETUP.md
offline_tasks/DEBUG_GUIDE.md renamed to offline_tasks/doc/DEBUG_GUIDE.md
offline_tasks/OFFLINE_INDEX_SPEC.md renamed to offline_tasks/doc/OFFLINE_INDEX_SPEC.md
| ... | ... | @@ -0,0 +1,79 @@ |
| 1 | +# 推荐系统离线任务文档中心 | |
| 2 | + | |
| 3 | +本目录包含推荐系统所有重要文档。 | |
| 4 | + | |
| 5 | +## 📚 文档目录 | |
| 6 | + | |
| 7 | +### 快速开始 | |
| 8 | + | |
| 9 | +- **[从这里开始.md](./从这里开始.md)** - 新手入门指南,了解项目结构和基本概念 | |
| 10 | +- **[快速开始.md](./快速开始.md)** - 快速运行离线任务的步骤 | |
| 11 | +- **[Swing快速开始.md](../collaboration/Swing快速开始.md)** - C++ Swing算法快速使用指南 | |
| 12 | + | |
| 13 | +### 使用指南 | |
| 14 | + | |
| 15 | +- **[Swing算法使用指南.md](./Swing算法使用指南.md)** - 详细的Swing算法使用文档 | |
| 16 | +- **[运行脚本指南.md](./运行脚本指南.md)** - 所有离线任务脚本的使用说明 | |
| 17 | +- **[调试指南.md](./调试指南.md)** - Debug模式和日志使用说明 | |
| 18 | + | |
| 19 | +### 数据规范 | |
| 20 | + | |
| 21 | +- **[离线索引数据规范.md](./离线索引数据规范.md)** - 所有离线索引的格式和说明 | |
| 22 | +- **[Redis数据规范.md](./Redis数据规范.md)** - Redis中数据的key格式和结构 | |
| 23 | +- **[完整索引列表.md](./完整索引列表.md)** - 系统中所有索引的完整清单 | |
| 24 | + | |
| 25 | +### 配置说明 | |
| 26 | + | |
| 27 | +- **[数据库配置说明.md](./数据库配置说明.md)** - 数据库连接和表结构说明 | |
| 28 | +- **配置文件**: `../config/offline_config.py` - 所有可配置参数 | |
| 29 | + | |
| 30 | +### 实现总结 | |
| 31 | + | |
| 32 | +- **[Swing实现总结.md](./Swing实现总结.md)** - C++ Swing集成实现的完整说明 | |
| 33 | + | |
| 34 | +### 维护文档 | |
| 35 | + | |
| 36 | +- **[故障排查指南.md](./故障排查指南.md)** - 常见问题和解决方案 | |
| 37 | +- **[更新日志.md](./更新日志.md)** - 系统更新历史 | |
| 38 | + | |
| 39 | +## 📂 相关文档 | |
| 40 | + | |
| 41 | +### 其他模块文档 | |
| 42 | + | |
| 43 | +- **Collaboration模块**: `../collaboration/README.md` - C++ 协同过滤算法 | |
| 44 | +- **GraphEmbedding模块**: | |
| 45 | + - Session W2V: `../graphembedding/session_w2v/README.md` | |
| 46 | + - DeepWalk: `../graphembedding/deepwalk/README.md` | |
| 47 | +- **Hot模块**: `../hot/README.md` - 热门商品推荐 | |
| 48 | + | |
| 49 | +### 项目README | |
| 50 | + | |
| 51 | +- **项目根目录**: `../../README.md` - 项目总览 | |
| 52 | +- **离线任务**: `../README.md` - 离线任务模块说明 | |
| 53 | + | |
| 54 | +## 🎯 文档使用建议 | |
| 55 | + | |
| 56 | +### 新用户 | |
| 57 | + | |
| 58 | +1. 先阅读 **[从这里开始.md](./从这里开始.md)** 了解整体架构 | |
| 59 | +2. 然后看 **[快速开始.md](./快速开始.md)** 快速运行系统 | |
| 60 | +3. 需要时查阅具体的使用指南和数据规范 | |
| 61 | + | |
| 62 | +### 开发人员 | |
| 63 | + | |
| 64 | +1. 查看 **[运行脚本指南.md](./运行脚本指南.md)** 了解所有脚本 | |
| 65 | +2. 参考 **[调试指南.md](./调试指南.md)** 进行调试 | |
| 66 | +3. 遵循 **数据规范** 文档进行开发 | |
| 67 | + | |
| 68 | +### 运维人员 | |
| 69 | + | |
| 70 | +1. 配置前先看 **[数据库配置说明.md](./数据库配置说明.md)** | |
| 71 | +2. 遇到问题查看 **[故障排查指南.md](./故障排查指南.md)** | |
| 72 | +3. 了解 **[Redis数据规范.md](./Redis数据规范.md)** 便于维护 | |
| 73 | + | |
| 74 | +## 📝 文档更新 | |
| 75 | + | |
| 76 | +所有文档由项目维护者更新。如有疑问或发现错误,请联系团队。 | |
| 77 | + | |
| 78 | +**最后更新**: 2024-10-17 | |
| 79 | + | ... | ... |
offline_tasks/REDIS_DATA_SPEC.md renamed to offline_tasks/doc/REDIS_DATA_SPEC.md
offline_tasks/RUN_SCRIPT_GUIDE.md renamed to offline_tasks/doc/RUN_SCRIPT_GUIDE.md
| ... | ... | @@ -0,0 +1,306 @@ |
| 1 | +# Redis数据灌入规范 | |
| 2 | + | |
| 3 | +## 📋 数据灌入概述 | |
| 4 | + | |
| 5 | +将离线生成的推荐索引加载到Redis,供在线系统实时查询使用。 | |
| 6 | + | |
| 7 | +## 🔑 Redis Key规范 | |
| 8 | + | |
| 9 | +### 通用规则 | |
| 10 | +``` | |
| 11 | +{namespace}:{function}:{algorithm}:{identifier} | |
| 12 | +``` | |
| 13 | + | |
| 14 | +- `namespace`: 业务命名空间(item, user, interest等) | |
| 15 | +- `function`: 功能类型(similar, feature, hot等) | |
| 16 | +- `algorithm`: 算法名称(swing, w2v, deepwalk等) | |
| 17 | +- `identifier`: 具体标识(item_id, dimension_key等) | |
| 18 | + | |
| 19 | +## 📊 数据灌入规范表 | |
| 20 | + | |
| 21 | +| 模块名称 | 源数据地址 | 格式描述 | RedisKey模板 | RedisValue格式 | TTL | | |
| 22 | +|---------|-----------|---------|-------------|---------------|-----| | |
| 23 | +| **i2i_swing** | `output/i2i_swing_YYYYMMDD.txt` | `item_id\titem_name\tsimilar_id1:score1,...` | `item:similar:swing:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 7天 | | |
| 24 | +| **i2i_session_w2v** | `output/i2i_session_w2v_YYYYMMDD.txt` | `item_id\titem_name\tsimilar_id1:score1,...` | `item:similar:w2v:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 7天 | | |
| 25 | +| **i2i_deepwalk** | `output/i2i_deepwalk_YYYYMMDD.txt` | `item_id\titem_name\tsimilar_id1:score1,...` | `item:similar:deepwalk:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 7天 | | |
| 26 | +| **i2i_content_name** | `output/i2i_content_name_YYYYMMDD.txt` | `item_id\titem_name\tsimilar_id1:score1,...` | `item:similar:content_name:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 30天 | | |
| 27 | +| **i2i_content_pic** | `output/i2i_content_pic_YYYYMMDD.txt` | `item_id\titem_name\tsimilar_id1:score1,...` | `item:similar:content_pic:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 30天 | | |
| 28 | +| **interest_hot** | `output/interest_aggregation_hot_YYYYMMDD.txt` | `dimension_key\titem_id1,item_id2,...` | `interest:hot:{dimension_key}` | `[item_id1,item_id2,item_id3,...]` | 3天 | | |
| 29 | +| **interest_cart** | `output/interest_aggregation_cart_YYYYMMDD.txt` | `dimension_key\titem_id1,item_id2,...` | `interest:cart:{dimension_key}` | `[item_id1,item_id2,item_id3,...]` | 3天 | | |
| 30 | +| **interest_new** | `output/interest_aggregation_new_YYYYMMDD.txt` | `dimension_key\titem_id1,item_id2,...` | `interest:new:{dimension_key}` | `[item_id1,item_id2,item_id3,...]` | 3天 | | |
| 31 | +| **interest_global** | `output/interest_aggregation_global_YYYYMMDD.txt` | `dimension_key\titem_id1,item_id2,...` | `interest:global:{dimension_key}` | `[item_id1,item_id2,item_id3,...]` | 7天 | | |
| 32 | + | |
| 33 | +## 📝 详细说明 | |
| 34 | + | |
| 35 | +### 1. i2i相似度索引 | |
| 36 | + | |
| 37 | +#### 源数据格式 | |
| 38 | +``` | |
| 39 | +12345 香蕉干 67890:0.8567,11223:0.7234,44556:0.6891 | |
| 40 | +``` | |
| 41 | + | |
| 42 | +#### Redis存储 | |
| 43 | + | |
| 44 | +**Key**: `item:similar:swing:12345` | |
| 45 | + | |
| 46 | +**Value** (JSON格式): | |
| 47 | +```json | |
| 48 | +[[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]] | |
| 49 | +``` | |
| 50 | + | |
| 51 | +**Value** (序列化后): | |
| 52 | +```python | |
| 53 | +import json | |
| 54 | +value = json.dumps([[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]]) | |
| 55 | +# 存储: "[[67890,0.8567],[11223,0.7234],[44556,0.6891]]" | |
| 56 | +``` | |
| 57 | + | |
| 58 | +#### 查询示例 | |
| 59 | +```python | |
| 60 | +import redis | |
| 61 | +import json | |
| 62 | + | |
| 63 | +r = redis.Redis(host='localhost', port=6379, db=0) | |
| 64 | + | |
| 65 | +# 获取商品12345的相似商品(Swing算法) | |
| 66 | +similar_items = json.loads(r.get('item:similar:swing:12345')) | |
| 67 | +# 返回: [[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]] | |
| 68 | + | |
| 69 | +# 获取Top5相似商品 | |
| 70 | +top_5 = similar_items[:5] | |
| 71 | +``` | |
| 72 | + | |
| 73 | +### 2. 兴趣点聚合索引 | |
| 74 | + | |
| 75 | +#### 源数据格式 | |
| 76 | +``` | |
| 77 | +platform:pc 12345,67890,11223,44556,22334 | |
| 78 | +category_level2:200 67890,12345,22334,55667,11223 | |
| 79 | +``` | |
| 80 | + | |
| 81 | +#### Redis存储 | |
| 82 | + | |
| 83 | +**Key**: `interest:hot:platform:pc` | |
| 84 | + | |
| 85 | +**Value** (JSON格式): | |
| 86 | +```json | |
| 87 | +[12345, 67890, 11223, 44556, 22334] | |
| 88 | +``` | |
| 89 | + | |
| 90 | +**Value** (序列化后): | |
| 91 | +```python | |
| 92 | +import json | |
| 93 | +value = json.dumps([12345, 67890, 11223, 44556, 22334]) | |
| 94 | +# 存储: "[12345,67890,11223,44556,22334]" | |
| 95 | +``` | |
| 96 | + | |
| 97 | +#### 查询示例 | |
| 98 | +```python | |
| 99 | +import redis | |
| 100 | +import json | |
| 101 | + | |
| 102 | +r = redis.Redis(host='localhost', port=6379, db=0) | |
| 103 | + | |
| 104 | +# 获取PC平台的热门商品 | |
| 105 | +hot_items = json.loads(r.get('interest:hot:platform:pc')) | |
| 106 | +# 返回: [12345, 67890, 11223, 44556, 22334] | |
| 107 | + | |
| 108 | +# 获取Top10热门商品 | |
| 109 | +top_10 = hot_items[:10] | |
| 110 | +``` | |
| 111 | + | |
| 112 | +## 🔄 数据加载流程 | |
| 113 | + | |
| 114 | +### 1. 加载i2i索引 | |
| 115 | + | |
| 116 | +```python | |
| 117 | +def load_i2i_index(file_path, algorithm_name, redis_client, expire_seconds=604800): | |
| 118 | + """ | |
| 119 | + 加载i2i相似度索引到Redis | |
| 120 | + | |
| 121 | + Args: | |
| 122 | + file_path: 索引文件路径 | |
| 123 | + algorithm_name: 算法名称(swing, w2v, deepwalk, content) | |
| 124 | + redis_client: Redis客户端 | |
| 125 | + expire_seconds: 过期时间(秒),默认7天 | |
| 126 | + """ | |
| 127 | + import json | |
| 128 | + | |
| 129 | + count = 0 | |
| 130 | + with open(file_path, 'r', encoding='utf-8') as f: | |
| 131 | + for line in f: | |
| 132 | + parts = line.strip().split('\t') | |
| 133 | + if len(parts) < 3: | |
| 134 | + continue | |
| 135 | + | |
| 136 | + item_id = parts[0] | |
| 137 | + similar_str = parts[2] # similar_id1:score1,similar_id2:score2,... | |
| 138 | + | |
| 139 | + # 解析相似商品 | |
| 140 | + similar_items = [] | |
| 141 | + for pair in similar_str.split(','): | |
| 142 | + if ':' in pair: | |
| 143 | + sim_id, score = pair.split(':') | |
| 144 | + similar_items.append([int(sim_id), float(score)]) | |
| 145 | + | |
| 146 | + # 存储到Redis | |
| 147 | + redis_key = f"item:similar:{algorithm_name}:{item_id}" | |
| 148 | + redis_value = json.dumps(similar_items) | |
| 149 | + | |
| 150 | + redis_client.set(redis_key, redis_value) | |
| 151 | + redis_client.expire(redis_key, expire_seconds) | |
| 152 | + | |
| 153 | + count += 1 | |
| 154 | + | |
| 155 | + return count | |
| 156 | +``` | |
| 157 | + | |
| 158 | +### 2. 加载兴趣聚合索引 | |
| 159 | + | |
| 160 | +```python | |
| 161 | +def load_interest_index(file_path, list_type, redis_client, expire_seconds=259200): | |
| 162 | + """ | |
| 163 | + 加载兴趣点聚合索引到Redis | |
| 164 | + | |
| 165 | + Args: | |
| 166 | + file_path: 索引文件路径 | |
| 167 | + list_type: 列表类型(hot, cart, new, global) | |
| 168 | + redis_client: Redis客户端 | |
| 169 | + expire_seconds: 过期时间(秒),默认3天 | |
| 170 | + """ | |
| 171 | + import json | |
| 172 | + | |
| 173 | + count = 0 | |
| 174 | + with open(file_path, 'r', encoding='utf-8') as f: | |
| 175 | + for line in f: | |
| 176 | + parts = line.strip().split('\t') | |
| 177 | + if len(parts) != 2: | |
| 178 | + continue | |
| 179 | + | |
| 180 | + dimension_key = parts[0] # platform:pc | |
| 181 | + item_ids_str = parts[1] # 12345,67890,11223,... | |
| 182 | + | |
| 183 | + # 解析商品ID列表 | |
| 184 | + item_ids = [int(item_id) for item_id in item_ids_str.split(',')] | |
| 185 | + | |
| 186 | + # 存储到Redis | |
| 187 | + redis_key = f"interest:{list_type}:{dimension_key}" | |
| 188 | + redis_value = json.dumps(item_ids) | |
| 189 | + | |
| 190 | + redis_client.set(redis_key, redis_value) | |
| 191 | + redis_client.expire(redis_key, expire_seconds) | |
| 192 | + | |
| 193 | + count += 1 | |
| 194 | + | |
| 195 | + return count | |
| 196 | +``` | |
| 197 | + | |
| 198 | +## 🚀 快速加载命令 | |
| 199 | + | |
| 200 | +### 加载所有索引 | |
| 201 | +```bash | |
| 202 | +cd /home/tw/recommendation/offline_tasks | |
| 203 | + | |
| 204 | +# 加载所有索引(使用今天的数据) | |
| 205 | +python3 scripts/load_index_to_redis.py --redis-host localhost --redis-port 6379 | |
| 206 | + | |
| 207 | +# 加载指定日期的索引 | |
| 208 | +python3 scripts/load_index_to_redis.py --date 20251016 --redis-host localhost | |
| 209 | + | |
| 210 | +# 只加载i2i索引 | |
| 211 | +python3 scripts/load_index_to_redis.py --load-i2i --redis-host localhost | |
| 212 | + | |
| 213 | +# 只加载兴趣聚合索引 | |
| 214 | +python3 scripts/load_index_to_redis.py --load-interest --redis-host localhost | |
| 215 | +``` | |
| 216 | + | |
| 217 | +### 验证数据 | |
| 218 | +```bash | |
| 219 | +# 连接Redis | |
| 220 | +redis-cli | |
| 221 | + | |
| 222 | +# 检查key数量 | |
| 223 | +DBSIZE | |
| 224 | + | |
| 225 | +# 查看某个商品的相似推荐 | |
| 226 | +GET item:similar:swing:12345 | |
| 227 | + | |
| 228 | +# 查看平台热门商品 | |
| 229 | +GET interest:hot:platform:pc | |
| 230 | + | |
| 231 | +# 查看所有i2i相关的key | |
| 232 | +KEYS item:similar:* | |
| 233 | + | |
| 234 | +# 查看所有interest相关的key | |
| 235 | +KEYS interest:* | |
| 236 | + | |
| 237 | +# 检查key的过期时间 | |
| 238 | +TTL item:similar:swing:12345 | |
| 239 | +``` | |
| 240 | + | |
| 241 | +## 📊 数据统计 | |
| 242 | + | |
| 243 | +### Redis内存占用估算 | |
| 244 | + | |
| 245 | +| 索引类型 | Key数量 | 单条Value大小 | 总内存 | | |
| 246 | +|---------|--------|-------------|--------| | |
| 247 | +| i2i_swing | 50,000 | ~500B | ~25MB | | |
| 248 | +| i2i_w2v | 50,000 | ~500B | ~25MB | | |
| 249 | +| i2i_deepwalk | 50,000 | ~500B | ~25MB | | |
| 250 | +| i2i_content_name | 50,000 | ~500B | ~25MB | | |
| 251 | +| i2i_content_pic | 50,000 | ~500B | ~25MB | | |
| 252 | +| interest_hot | 10,000 | ~1KB | ~10MB | | |
| 253 | +| interest_cart | 10,000 | ~1KB | ~10MB | | |
| 254 | +| interest_new | 5,000 | ~1KB | ~5MB | | |
| 255 | +| interest_global | 10,000 | ~1KB | ~10MB | | |
| 256 | +| **总计** | **270,000** | - | **~160MB** | | |
| 257 | + | |
| 258 | +### 过期策略 | |
| 259 | + | |
| 260 | +| 索引类型 | TTL | 原因 | | |
| 261 | +|---------|-----|------| | |
| 262 | +| i2i行为相似 | 7天 | 用户行为变化快,需要频繁更新 | | |
| 263 | +| i2i内容相似 | 30天 | 商品属性变化慢,可以保留更久 | | |
| 264 | +| 热门/加购 | 3天 | 热度变化快,需要及时更新 | | |
| 265 | +| 新品 | 3天 | 新品概念有时效性 | | |
| 266 | +| 全局热门 | 7天 | 相对稳定,可以保留更久 | | |
| 267 | + | |
| 268 | +## ⚠️ 注意事项 | |
| 269 | + | |
| 270 | +1. **原子性**: 使用Pipeline批量写入,提高性能 | |
| 271 | +2. **过期时间**: 合理设置TTL,避免过期数据 | |
| 272 | +3. **内存管理**: 定期清理过期key,监控内存使用 | |
| 273 | +4. **数据版本**: 使用日期标记,支持数据回滚 | |
| 274 | +5. **容错处理**: 加载失败时不影响线上服务 | |
| 275 | +6. **监控告警**: 监控加载成功率、Redis内存、查询延迟 | |
| 276 | + | |
| 277 | +## 🔍 监控指标 | |
| 278 | + | |
| 279 | +### 数据质量指标 | |
| 280 | +```python | |
| 281 | +# 检查加载成功率 | |
| 282 | +total_keys = redis_client.dbsize() | |
| 283 | +expected_keys = 245000 | |
| 284 | +success_rate = total_keys / expected_keys * 100 | |
| 285 | + | |
| 286 | +# 检查数据完整性 | |
| 287 | +sample_keys = [ | |
| 288 | + 'item:similar:swing:12345', | |
| 289 | + 'interest:hot:platform:pc' | |
| 290 | +] | |
| 291 | +for key in sample_keys: | |
| 292 | + if not redis_client.exists(key): | |
| 293 | + print(f"Missing key: {key}") | |
| 294 | +``` | |
| 295 | + | |
| 296 | +### 性能指标 | |
| 297 | +- 加载耗时: < 5分钟 | |
| 298 | +- 内存占用: < 200MB | |
| 299 | +- 查询延迟: < 1ms | |
| 300 | +- 成功率: > 99% | |
| 301 | + | |
| 302 | +## 🔗 相关文档 | |
| 303 | + | |
| 304 | +- **离线索引规范**: `OFFLINE_INDEX_SPEC.md` | |
| 305 | +- **API接口文档**: `RECOMMENDATION_API.md` | |
| 306 | +- **运维手册**: `OPERATIONS.md` | ... | ... |
offline_tasks/SWING_USAGE.md renamed to offline_tasks/doc/SWING_USAGE.md
| ... | ... | @@ -0,0 +1,375 @@ |
| 1 | +# Swing算法实现总结 | |
| 2 | + | |
| 3 | +## 完成的任务 | |
| 4 | + | |
| 5 | +本次实现完成了以下功能: | |
| 6 | + | |
| 7 | +### 1. Session生成脚本 ✓ | |
| 8 | + | |
| 9 | +**文件**: `offline_tasks/scripts/generate_session.py` | |
| 10 | + | |
| 11 | +**功能**: | |
| 12 | +- 从数据库提取用户行为数据 | |
| 13 | +- 聚合用户session(按商品维度累加权重) | |
| 14 | +- 支持两种输出格式: | |
| 15 | + - 标准格式:`uid \t {"item_id":score,...}` | |
| 16 | + - C++格式:`{"item_id":score,...}` (每行一个用户) | |
| 17 | + | |
| 18 | +**主要参数**: | |
| 19 | +- `--lookback_days`: 回看天数(默认730天) | |
| 20 | +- `--format`: 输出格式(standard/cpp/both) | |
| 21 | +- `--output`: 输出文件路径 | |
| 22 | +- `--debug`: 启用debug模式 | |
| 23 | + | |
| 24 | +**使用示例**: | |
| 25 | +```bash | |
| 26 | +cd /home/tw/recommendation/offline_tasks | |
| 27 | +python3 scripts/generate_session.py --lookback_days 730 --format both | |
| 28 | +``` | |
| 29 | + | |
| 30 | +### 2. Swing运行脚本 ✓ | |
| 31 | + | |
| 32 | +**文件**: `collaboration/run.sh` | |
| 33 | + | |
| 34 | +**改进内容**: | |
| 35 | +- ✓ 适配新的数据路径(`../offline_tasks/output/`) | |
| 36 | +- ✓ 自动检测session文件格式(带uid或纯json) | |
| 37 | +- ✓ 增加配置区域,便于修改参数 | |
| 38 | +- ✓ 添加错误检查和友好的输出信息 | |
| 39 | +- ✓ 自动调用debug脚本生成可读文件 | |
| 40 | +- ✓ 支持自定义Python环境 | |
| 41 | + | |
| 42 | +**配置项**: | |
| 43 | +```bash | |
| 44 | +SESSION_DATA_DIR="../offline_tasks/output" # session文件目录 | |
| 45 | +ALPHA=0.7 # Swing alpha参数 | |
| 46 | +THRESHOLD1=1 # 交互强度阈值1 | |
| 47 | +THRESHOLD2=3 # 交互强度阈值2 | |
| 48 | +THREAD_NUM=4 # 线程数 | |
| 49 | +SHOW_PROGRESS=1 # 显示进度 | |
| 50 | +PYTHON_CMD="python3" # Python命令 | |
| 51 | +``` | |
| 52 | + | |
| 53 | +**执行流程**: | |
| 54 | +1. 编译C++程序 | |
| 55 | +2. 查找session文件 | |
| 56 | +3. 运行Swing算法(多线程) | |
| 57 | +4. 合并结果 | |
| 58 | +5. 生成可读版本(自动调用debug脚本) | |
| 59 | + | |
| 60 | +### 3. Debug脚本 ✓ | |
| 61 | + | |
| 62 | +**文件**: `offline_tasks/scripts/add_names_to_swing.py` | |
| 63 | + | |
| 64 | +**功能**: | |
| 65 | +- 读取Swing算法输出结果 | |
| 66 | +- 从数据库获取商品名称映射 | |
| 67 | +- 生成可读版本:`item_id:name \t similar_id1:name1:score1,...` | |
| 68 | + | |
| 69 | +**使用示例**: | |
| 70 | +```bash | |
| 71 | +cd /home/tw/recommendation/offline_tasks | |
| 72 | +python3 scripts/add_names_to_swing.py \ | |
| 73 | + ../collaboration/output/swing_similar.txt \ | |
| 74 | + ../collaboration/output/swing_similar_readable.txt \ | |
| 75 | + --debug | |
| 76 | +``` | |
| 77 | + | |
| 78 | +### 4. 使用文档 ✓ | |
| 79 | + | |
| 80 | +**文件**: | |
| 81 | +- `offline_tasks/SWING_USAGE.md` - 完整使用指南 | |
| 82 | +- `collaboration/QUICKSTART.md` - 快速开始指南 | |
| 83 | + | |
| 84 | +**包含内容**: | |
| 85 | +- 详细的使用步骤 | |
| 86 | +- 参数说明和调优建议 | |
| 87 | +- 故障排查指南 | |
| 88 | +- 性能优化建议 | |
| 89 | +- 完整示例 | |
| 90 | + | |
| 91 | +## 数据流程 | |
| 92 | + | |
| 93 | +``` | |
| 94 | +┌─────────────────────────────────────────────────────────────┐ | |
| 95 | +│ 步骤1: 生成Session文件 │ | |
| 96 | +│ generate_session.py │ | |
| 97 | +│ ↓ │ | |
| 98 | +│ 数据库 → 用户行为数据 → 聚合权重 → session.txt.YYYYMMDD │ | |
| 99 | +└─────────────────────────────────────────────────────────────┘ | |
| 100 | + ↓ | |
| 101 | +┌─────────────────────────────────────────────────────────────┐ | |
| 102 | +│ 步骤2: 运行Swing算法 │ | |
| 103 | +│ collaboration/run.sh │ | |
| 104 | +│ ↓ │ | |
| 105 | +│ session文件 → C++ Swing → sim_matrx.* → swing_similar.txt │ | |
| 106 | +└─────────────────────────────────────────────────────────────┘ | |
| 107 | + ↓ | |
| 108 | +┌─────────────────────────────────────────────────────────────┐ | |
| 109 | +│ 步骤3: 生成Debug文件 │ | |
| 110 | +│ add_names_to_swing.py (自动调用) │ | |
| 111 | +│ ↓ │ | |
| 112 | +│ swing_similar.txt → 添加商品名 → swing_similar_readable.txt│ | |
| 113 | +└─────────────────────────────────────────────────────────────┘ | |
| 114 | +``` | |
| 115 | + | |
| 116 | +## 文件格式说明 | |
| 117 | + | |
| 118 | +### Session文件格式 | |
| 119 | + | |
| 120 | +**标准格式** (`session.txt.YYYYMMDD`): | |
| 121 | +``` | |
| 122 | +user_id1 {"123":10.0,"456":5.0,"789":3.0} | |
| 123 | +user_id2 {"123":8.0,"999":12.0} | |
| 124 | +``` | |
| 125 | + | |
| 126 | +**C++格式** (`session.txt.YYYYMMDD.cpp`): | |
| 127 | +``` | |
| 128 | +{"123":10.0,"456":5.0,"789":3.0} | |
| 129 | +{"123":8.0,"999":12.0} | |
| 130 | +``` | |
| 131 | + | |
| 132 | +### Swing结果格式 | |
| 133 | + | |
| 134 | +**原始格式** (`swing_similar.txt`): | |
| 135 | +``` | |
| 136 | +12345 67890:0.8523,23456:0.7234,34567:0.6891 | |
| 137 | +``` | |
| 138 | + | |
| 139 | +**可读格式** (`swing_similar_readable.txt`): | |
| 140 | +``` | |
| 141 | +12345:iPhone 15 Pro 67890:iPhone 15:0.8523,23456:iPhone 14 Pro:0.7234 | |
| 142 | +``` | |
| 143 | + | |
| 144 | +## 行为权重配置 | |
| 145 | + | |
| 146 | +当前配置(在`generate_session.py`中): | |
| 147 | + | |
| 148 | +| 行为类型 | 权重 | 说明 | | |
| 149 | +|---------|------|------| | |
| 150 | +| purchase | 10.0 | 购买行为(最强信号) | | |
| 151 | +| contactFactory | 5.0 | 联系厂家 | | |
| 152 | +| addToCart | 3.0 | 加入购物车 | | |
| 153 | +| addToPool | 2.0 | 加入询价池 | | |
| 154 | + | |
| 155 | +## 快速开始 | |
| 156 | + | |
| 157 | +```bash | |
| 158 | +# 1. 生成session文件(730天数据) | |
| 159 | +cd /home/tw/recommendation/offline_tasks | |
| 160 | +python3 scripts/generate_session.py --lookback_days 730 | |
| 161 | + | |
| 162 | +# 2. 运行Swing算法 | |
| 163 | +cd /home/tw/recommendation/collaboration | |
| 164 | +bash run.sh | |
| 165 | + | |
| 166 | +# 3. 查看结果 | |
| 167 | +cat output/swing_similar_readable.txt | head -20 | |
| 168 | +``` | |
| 169 | + | |
| 170 | +## 项目结构 | |
| 171 | + | |
| 172 | +``` | |
| 173 | +recommendation/ | |
| 174 | +├── offline_tasks/ | |
| 175 | +│ ├── scripts/ | |
| 176 | +│ │ ├── generate_session.py # 新增:生成session | |
| 177 | +│ │ ├── add_names_to_swing.py # 新增:添加商品名 | |
| 178 | +│ │ ├── i2i_swing.py # 已有:Python版Swing | |
| 179 | +│ │ └── debug_utils.py # 已有:Debug工具 | |
| 180 | +│ ├── config/ | |
| 181 | +│ │ └── offline_config.py # 配置文件 | |
| 182 | +│ ├── output/ | |
| 183 | +│ │ ├── session.txt.YYYYMMDD # 生成的session文件 | |
| 184 | +│ │ └── session.txt.YYYYMMDD.cpp # C++格式session | |
| 185 | +│ ├── SWING_USAGE.md # 新增:详细使用文档 | |
| 186 | +│ └── ... | |
| 187 | +├── collaboration/ | |
| 188 | +│ ├── run.sh # 修改:适配新数据格式 | |
| 189 | +│ ├── QUICKSTART.md # 新增:快速开始 | |
| 190 | +│ ├── src/ | |
| 191 | +│ │ ├── swing.cc # C++ Swing实现 | |
| 192 | +│ │ ├── swing_symmetric.cc # 对称Swing | |
| 193 | +│ │ ├── icf_simple.cc # 简单协同过滤 | |
| 194 | +│ │ └── ucf.py # 用户协同 | |
| 195 | +│ ├── bin/ # 编译后的可执行文件 | |
| 196 | +│ ├── output_YYYYMMDD/ # 输出目录 | |
| 197 | +│ │ ├── sim_matrx.* # 多线程输出 | |
| 198 | +│ │ ├── swing_similar.txt # 合并结果 | |
| 199 | +│ │ └── swing_similar_readable.txt # 可读结果 | |
| 200 | +│ └── output -> output_YYYYMMDD # 软链接 | |
| 201 | +└── SWING_IMPLEMENTATION_SUMMARY.md # 本文档 | |
| 202 | +``` | |
| 203 | + | |
| 204 | +## 参数调优建议 | |
| 205 | + | |
| 206 | +### 针对B2B低频场景 | |
| 207 | + | |
| 208 | +```bash | |
| 209 | +# Session生成 | |
| 210 | +--lookback_days 730 # 2年数据(B2B交互频次低) | |
| 211 | + | |
| 212 | +# Swing算法 | |
| 213 | +ALPHA=0.5-0.7 # 关注用户共同行为的多样性 | |
| 214 | +THRESHOLD1=1 # 低阈值,保留更多数据 | |
| 215 | +THRESHOLD2=3 # 中等阈值,过滤噪音 | |
| 216 | +THREAD_NUM=4-8 # 根据服务器配置 | |
| 217 | +``` | |
| 218 | + | |
| 219 | +### 针对大数据量场景 | |
| 220 | + | |
| 221 | +```bash | |
| 222 | +# 增加线程数 | |
| 223 | +THREAD_NUM=8 | |
| 224 | + | |
| 225 | +# 修改C++代码参数 | |
| 226 | +max_sim_list_len=300 # 相似列表长度 | |
| 227 | +max_session_list_len=100 # session截断长度 | |
| 228 | +``` | |
| 229 | + | |
| 230 | +## 与现有系统集成 | |
| 231 | + | |
| 232 | +### 1. 定时任务 | |
| 233 | + | |
| 234 | +```bash | |
| 235 | +# 每天凌晨2点运行 | |
| 236 | +0 2 * * * cd /home/tw/recommendation/offline_tasks && \ | |
| 237 | + python3 scripts/generate_session.py && \ | |
| 238 | + cd ../collaboration && bash run.sh | |
| 239 | +``` | |
| 240 | + | |
| 241 | +### 2. 结果导入Redis | |
| 242 | + | |
| 243 | +可使用现有的 `load_index_to_redis.py` 脚本导入结果。 | |
| 244 | + | |
| 245 | +### 3. 与Python版Swing对比 | |
| 246 | + | |
| 247 | +- **C++版本**(本次实现):性能更好,适合大数据量 | |
| 248 | +- **Python版本**(`i2i_swing.py`):易于调试,支持时间衰减 | |
| 249 | + | |
| 250 | +可以运行两个版本对比效果: | |
| 251 | +```bash | |
| 252 | +# Python版本 | |
| 253 | +python3 offline_tasks/scripts/i2i_swing.py --debug | |
| 254 | + | |
| 255 | +# C++版本 | |
| 256 | +cd collaboration && bash run.sh | |
| 257 | +``` | |
| 258 | + | |
| 259 | +## 测试验证 | |
| 260 | + | |
| 261 | +### 1. 小数据量测试 | |
| 262 | + | |
| 263 | +```bash | |
| 264 | +# 生成小范围数据(30天) | |
| 265 | +python3 scripts/generate_session.py --lookback_days 30 | |
| 266 | + | |
| 267 | +# 运行Swing | |
| 268 | +cd ../collaboration | |
| 269 | +bash run.sh | |
| 270 | +``` | |
| 271 | + | |
| 272 | +### 2. 查看结果质量 | |
| 273 | + | |
| 274 | +```bash | |
| 275 | +# 查看可读版本前100行 | |
| 276 | +head -100 output/swing_similar_readable.txt | |
| 277 | + | |
| 278 | +# 检查相似度分布 | |
| 279 | +cat output/swing_similar.txt | awk -F'\t' '{print NF-1}' | sort -n | uniq -c | |
| 280 | +``` | |
| 281 | + | |
| 282 | +### 3. 性能测试 | |
| 283 | + | |
| 284 | +```bash | |
| 285 | +# 记录运行时间 | |
| 286 | +time bash run.sh | |
| 287 | +``` | |
| 288 | + | |
| 289 | +## 故障排查 | |
| 290 | + | |
| 291 | +### 常见问题 | |
| 292 | + | |
| 293 | +1. **Session文件不存在** | |
| 294 | + - 先运行 `generate_session.py` | |
| 295 | + | |
| 296 | +2. **编译失败** | |
| 297 | + - 检查g++版本:`g++ --version` | |
| 298 | + - 手动编译:`cd collaboration && make` | |
| 299 | + | |
| 300 | +3. **数据库连接失败** | |
| 301 | + - 检查配置:`offline_tasks/config/offline_config.py` | |
| 302 | + - 测试连接:`python3 offline_tasks/test_connection.py` | |
| 303 | + | |
| 304 | +4. **结果为空** | |
| 305 | + - 降低threshold参数 | |
| 306 | + - 增加lookback_days | |
| 307 | + - 检查数据量:`wc -l output/session.txt.*` | |
| 308 | + | |
| 309 | +详细故障排查参见:`offline_tasks/SWING_USAGE.md` | |
| 310 | + | |
| 311 | +## 后续优化方向 | |
| 312 | + | |
| 313 | +1. **性能优化** | |
| 314 | + - 支持分布式计算 | |
| 315 | + - 增量更新机制 | |
| 316 | + - 结果缓存 | |
| 317 | + | |
| 318 | +2. **功能增强** | |
| 319 | + - 支持多种相似度算法 | |
| 320 | + - 在线实时更新 | |
| 321 | + - A/B测试框架 | |
| 322 | + | |
| 323 | +3. **可观测性** | |
| 324 | + - 添加监控指标 | |
| 325 | + - 结果质量评估 | |
| 326 | + - 自动报警 | |
| 327 | + | |
| 328 | +## 相关文档 | |
| 329 | + | |
| 330 | +- **详细使用指南**: `offline_tasks/SWING_USAGE.md` | |
| 331 | +- **快速开始**: `collaboration/QUICKSTART.md` | |
| 332 | +- **配置说明**: `offline_tasks/config/offline_config.py` | |
| 333 | +- **Debug工具**: `offline_tasks/scripts/debug_utils.py` | |
| 334 | +- **Swing算法原理**: `collaboration/README.md` | |
| 335 | + | |
| 336 | +## 维护说明 | |
| 337 | + | |
| 338 | +### 代码维护 | |
| 339 | + | |
| 340 | +- **Session生成**: `offline_tasks/scripts/generate_session.py` | |
| 341 | +- **Swing执行**: `collaboration/run.sh` | |
| 342 | +- **Debug脚本**: `offline_tasks/scripts/add_names_to_swing.py` | |
| 343 | + | |
| 344 | +### 配置维护 | |
| 345 | + | |
| 346 | +- **数据库配置**: `offline_tasks/config/offline_config.py` | |
| 347 | +- **行为权重**: `generate_session.py` 中的 `behavior_weights` | |
| 348 | +- **Swing参数**: `collaboration/run.sh` 中的配置区域 | |
| 349 | + | |
| 350 | +### 日志查看 | |
| 351 | + | |
| 352 | +```bash | |
| 353 | +# Session生成日志 | |
| 354 | +ls offline_tasks/logs/debug/generate_session_*.log | |
| 355 | + | |
| 356 | +# Swing运行日志 | |
| 357 | +ls collaboration/logs/ | |
| 358 | +``` | |
| 359 | + | |
| 360 | +## 总结 | |
| 361 | + | |
| 362 | +本次实现完成了一套完整的C++ Swing算法工作流: | |
| 363 | + | |
| 364 | +1. ✓ **前置任务**:Session文件生成(`generate_session.py`) | |
| 365 | +2. ✓ **核心算法**:C++ Swing执行(改进的`run.sh`) | |
| 366 | +3. ✓ **后处理**:Debug文件生成(`add_names_to_swing.py`) | |
| 367 | +4. ✓ **文档完善**:详细使用指南和快速开始 | |
| 368 | + | |
| 369 | +所有脚本都支持debug模式,便于调试和监控。整体流程自动化程度高,只需一条命令即可完成全流程。 | |
| 370 | + | |
| 371 | +--- | |
| 372 | + | |
| 373 | +**实现时间**: 2024-10-17 | |
| 374 | +**状态**: ✅ 已完成 | |
| 375 | + | ... | ... |
| ... | ... | @@ -0,0 +1,322 @@ |
| 1 | +# Swing算法使用指南 | |
| 2 | + | |
| 3 | +本文档介绍如何使用C++版本的Swing算法进行物品相似度计算。 | |
| 4 | + | |
| 5 | +## 目录结构 | |
| 6 | + | |
| 7 | +``` | |
| 8 | +recommendation/ | |
| 9 | +├── offline_tasks/ | |
| 10 | +│ ├── scripts/ | |
| 11 | +│ │ ├── generate_session.py # 生成用户session文件 | |
| 12 | +│ │ └── add_names_to_swing.py # 给结果添加商品名称 | |
| 13 | +│ └── output/ | |
| 14 | +│ └── session.txt.YYYYMMDD # 生成的session文件 | |
| 15 | +└── collaboration/ | |
| 16 | + ├── run.sh # Swing算法执行脚本 | |
| 17 | + ├── src/ | |
| 18 | + │ ├── swing.cc # Swing算法实现 | |
| 19 | + │ └── ucf.py # 用户协同过滤 | |
| 20 | + └── output_YYYYMMDD/ | |
| 21 | + ├── swing_similar.txt # Swing结果(ID格式) | |
| 22 | + └── swing_similar_readable.txt # Swing结果(带商品名) | |
| 23 | +``` | |
| 24 | + | |
| 25 | +## 使用流程 | |
| 26 | + | |
| 27 | +### 步骤1: 生成Session文件 | |
| 28 | + | |
| 29 | +首先需要从数据库提取用户行为数据,生成session文件。 | |
| 30 | + | |
| 31 | +```bash | |
| 32 | +cd /home/tw/recommendation/offline_tasks | |
| 33 | + | |
| 34 | +# 基本用法(使用默认参数:730天数据) | |
| 35 | +python3 scripts/generate_session.py | |
| 36 | + | |
| 37 | +# 指定回看天数 | |
| 38 | +python3 scripts/generate_session.py --lookback_days 365 | |
| 39 | + | |
| 40 | +# 启用debug模式查看详细信息 | |
| 41 | +python3 scripts/generate_session.py --lookback_days 730 --debug | |
| 42 | + | |
| 43 | +# 指定输出文件路径 | |
| 44 | +python3 scripts/generate_session.py --output output/session.txt.20241017 | |
| 45 | + | |
| 46 | +# 选择输出格式 | |
| 47 | +python3 scripts/generate_session.py --format both # 同时生成两种格式(默认) | |
| 48 | +python3 scripts/generate_session.py --format standard # uid \t json 格式 | |
| 49 | +python3 scripts/generate_session.py --format cpp # 纯json格式(.cpp后缀) | |
| 50 | +``` | |
| 51 | + | |
| 52 | +**输出文件格式:** | |
| 53 | + | |
| 54 | +- `session.txt.YYYYMMDD` - 标准格式(包含uid): | |
| 55 | + ``` | |
| 56 | + uid1 \t {"item_id1":10.0,"item_id2":5.0,"item_id3":3.0} | |
| 57 | + uid2 \t {"item_id4":15.0,"item_id5":8.0} | |
| 58 | + ``` | |
| 59 | + | |
| 60 | +- `session.txt.YYYYMMDD.cpp` - C++格式(纯json): | |
| 61 | + ``` | |
| 62 | + {"item_id1":10.0,"item_id2":5.0,"item_id3":3.0} | |
| 63 | + {"item_id4":15.0,"item_id5":8.0} | |
| 64 | + ``` | |
| 65 | + | |
| 66 | +**行为权重配置:** | |
| 67 | +- `purchase`: 10.0(购买) | |
| 68 | +- `contactFactory`: 5.0(联系厂家) | |
| 69 | +- `addToCart`: 3.0(加入购物车) | |
| 70 | +- `addToPool`: 2.0(加入询价池) | |
| 71 | + | |
| 72 | +### 步骤2: 运行Swing算法 | |
| 73 | + | |
| 74 | +session文件生成后,运行C++版本的Swing算法。 | |
| 75 | + | |
| 76 | +```bash | |
| 77 | +cd /home/tw/recommendation/collaboration | |
| 78 | + | |
| 79 | +# 直接运行(使用默认配置) | |
| 80 | +bash run.sh | |
| 81 | + | |
| 82 | +# 或者给脚本添加执行权限后运行 | |
| 83 | +chmod +x run.sh | |
| 84 | +./run.sh | |
| 85 | +``` | |
| 86 | + | |
| 87 | +**配置说明(修改run.sh中的参数):** | |
| 88 | + | |
| 89 | +```bash | |
| 90 | +# 数据路径配置 | |
| 91 | +SESSION_DATA_DIR="../offline_tasks/output" # session文件目录 | |
| 92 | + | |
| 93 | +# Swing算法参数 | |
| 94 | +ALPHA=0.7 # Swing算法的alpha参数(越小越关注用户共同行为) | |
| 95 | +THRESHOLD1=1 # 交互强度阈值1(用于筛选用户行为) | |
| 96 | +THRESHOLD2=3 # 交互强度阈值2(用于计算相似度) | |
| 97 | +THREAD_NUM=4 # 线程数(根据CPU核心数调整) | |
| 98 | +SHOW_PROGRESS=1 # 是否显示进度 (0/1) | |
| 99 | + | |
| 100 | +# Python环境 | |
| 101 | +PYTHON_CMD="python3" # 如需使用特定Python环境,修改此处 | |
| 102 | +``` | |
| 103 | + | |
| 104 | +**脚本执行流程:** | |
| 105 | + | |
| 106 | +1. 编译C++程序(swing, icf_simple, swing_symmetric) | |
| 107 | +2. 查找当天日期的session文件 | |
| 108 | +3. 运行Swing算法计算物品相似度 | |
| 109 | +4. 合并多线程输出结果 | |
| 110 | +5. 自动调用debug脚本生成可读版本 | |
| 111 | + | |
| 112 | +### 步骤3: 查看结果 | |
| 113 | + | |
| 114 | +运行完成后,结果文件位于 `collaboration/output_YYYYMMDD/` 目录: | |
| 115 | + | |
| 116 | +**1. swing_similar.txt** - 原始结果(ID格式) | |
| 117 | +``` | |
| 118 | +12345 \t 67890:0.8523,23456:0.7234,34567:0.6891 | |
| 119 | +``` | |
| 120 | +格式:`item_id \t similar_item_id1:score1,similar_item_id2:score2,...` | |
| 121 | + | |
| 122 | +**2. swing_similar_readable.txt** - 可读结果(带商品名) | |
| 123 | +``` | |
| 124 | +12345:iPhone 15 Pro \t 67890:iPhone 15:0.8523,23456:iPhone 14 Pro:0.7234 | |
| 125 | +``` | |
| 126 | +格式:`item_id:item_name \t similar_item_id1:name1:score1,similar_item_id2:name2:score2,...` | |
| 127 | + | |
| 128 | +### 步骤4: 单独生成Debug文件(可选) | |
| 129 | + | |
| 130 | +如果需要为其他文件生成可读版本: | |
| 131 | + | |
| 132 | +```bash | |
| 133 | +cd /home/tw/recommendation/offline_tasks | |
| 134 | + | |
| 135 | +# 基本用法 | |
| 136 | +python3 scripts/add_names_to_swing.py collaboration/output/swing_similar.txt | |
| 137 | + | |
| 138 | +# 指定输出文件 | |
| 139 | +python3 scripts/add_names_to_swing.py \ | |
| 140 | + collaboration/output/swing_similar.txt \ | |
| 141 | + collaboration/output/my_readable.txt | |
| 142 | + | |
| 143 | +# 启用debug模式 | |
| 144 | +python3 scripts/add_names_to_swing.py \ | |
| 145 | + collaboration/output/swing_similar.txt \ | |
| 146 | + --debug | |
| 147 | +``` | |
| 148 | + | |
| 149 | +## 参数调优建议 | |
| 150 | + | |
| 151 | +### Swing算法参数 | |
| 152 | + | |
| 153 | +1. **alpha (0.5-1.0)** | |
| 154 | + - 越小:越关注用户共同行为的多样性 | |
| 155 | + - 越大:越容忽略用户重叠度 | |
| 156 | + - 建议:0.5-0.7(B2B场景) | |
| 157 | + | |
| 158 | +2. **threshold1 (1-5)** | |
| 159 | + - 用于筛选用户的有效行为 | |
| 160 | + - 建议:1-3(低频场景可用1) | |
| 161 | + | |
| 162 | +3. **threshold2 (1-10)** | |
| 163 | + - 用于计算相似度的行为强度阈值 | |
| 164 | + - 建议:3-5(需要较强的交互信号) | |
| 165 | + | |
| 166 | +4. **thread_num (1-20)** | |
| 167 | + - 根据CPU核心数设置 | |
| 168 | + - 建议:4-8(普通服务器) | |
| 169 | + | |
| 170 | +### 数据范围参数 | |
| 171 | + | |
| 172 | +1. **lookback_days** | |
| 173 | + - B2B低频场景:建议730天(2年) | |
| 174 | + - B2C高频场景:建议30-90天 | |
| 175 | + - 数据量大时可适当减少 | |
| 176 | + | |
| 177 | +## 完整示例 | |
| 178 | + | |
| 179 | +```bash | |
| 180 | +# 1. 生成session文件(730天数据) | |
| 181 | +cd /home/tw/recommendation/offline_tasks | |
| 182 | +python3 scripts/generate_session.py --lookback_days 730 --debug | |
| 183 | + | |
| 184 | +# 2. 检查生成的文件 | |
| 185 | +ls -lh output/session.txt.* | |
| 186 | +# 应该看到: | |
| 187 | +# session.txt.20241017 | |
| 188 | +# session.txt.20241017.cpp | |
| 189 | + | |
| 190 | +# 3. 运行Swing算法 | |
| 191 | +cd /home/tw/recommendation/collaboration | |
| 192 | +bash run.sh | |
| 193 | + | |
| 194 | +# 4. 查看结果 | |
| 195 | +ls -lh output/swing_similar* | |
| 196 | +cat output/swing_similar_readable.txt | head -20 | |
| 197 | +``` | |
| 198 | + | |
| 199 | +## 故障排查 | |
| 200 | + | |
| 201 | +### 问题1: Session文件不存在 | |
| 202 | + | |
| 203 | +``` | |
| 204 | +错误: Session文件不存在: ../offline_tasks/output/session.txt.20241017.cpp | |
| 205 | +``` | |
| 206 | + | |
| 207 | +**解决方法:** | |
| 208 | +```bash | |
| 209 | +cd /home/tw/recommendation/offline_tasks | |
| 210 | +python3 scripts/generate_session.py | |
| 211 | +``` | |
| 212 | + | |
| 213 | +### 问题2: 编译失败 | |
| 214 | + | |
| 215 | +``` | |
| 216 | +编译失败,退出 | |
| 217 | +``` | |
| 218 | + | |
| 219 | +**解决方法:** | |
| 220 | +```bash | |
| 221 | +cd /home/tw/recommendation/collaboration | |
| 222 | +# 检查编译器 | |
| 223 | +g++ --version | |
| 224 | + | |
| 225 | +# 手动编译 | |
| 226 | +make clean | |
| 227 | +make | |
| 228 | + | |
| 229 | +# 检查依赖 | |
| 230 | +ls include/ | |
| 231 | +ls utils/ | |
| 232 | +``` | |
| 233 | + | |
| 234 | +### 问题3: 数据库连接失败 | |
| 235 | + | |
| 236 | +``` | |
| 237 | +获取数据失败: Connection refused | |
| 238 | +``` | |
| 239 | + | |
| 240 | +**解决方法:** | |
| 241 | +- 检查数据库配置:`offline_tasks/config/offline_config.py` | |
| 242 | +- 测试连接:`python3 offline_tasks/test_connection.py` | |
| 243 | +- 确认网络和防火墙设置 | |
| 244 | + | |
| 245 | +### 问题4: 结果为空 | |
| 246 | + | |
| 247 | +**可能原因:** | |
| 248 | +- threshold1/threshold2设置过高,过滤掉了所有数据 | |
| 249 | +- 数据量太少,用户和商品交集不足 | |
| 250 | + | |
| 251 | +**解决方法:** | |
| 252 | +- 降低threshold参数(如threshold1=0.5, threshold2=1) | |
| 253 | +- 增加lookback_days | |
| 254 | +- 检查数据量:`wc -l output/session.txt.*` | |
| 255 | + | |
| 256 | +## 性能优化 | |
| 257 | + | |
| 258 | +### 大数据量场景 | |
| 259 | + | |
| 260 | +如果数据量很大(>100万用户,>10万商品): | |
| 261 | + | |
| 262 | +1. **增加线程数** | |
| 263 | + ```bash | |
| 264 | + THREAD_NUM=8 # 或更多 | |
| 265 | + ``` | |
| 266 | + | |
| 267 | +2. **分批处理** | |
| 268 | + - 可以将session文件按用户分片 | |
| 269 | + - 分别运行Swing算法 | |
| 270 | + - 最后合并结果 | |
| 271 | + | |
| 272 | +3. **调整max_session_list_len** | |
| 273 | + - 修改 `src/swing.cc` 中的 `max_session_list_len` | |
| 274 | + - 限制每个用户的最大行为数 | |
| 275 | + | |
| 276 | +### 内存优化 | |
| 277 | + | |
| 278 | +如果遇到内存不足: | |
| 279 | + | |
| 280 | +1. 减少 `max_sim_list_len`(默认300) | |
| 281 | +2. 减少 `max_session_list_len`(默认100) | |
| 282 | +3. 分批处理数据 | |
| 283 | + | |
| 284 | +## 集成到定时任务 | |
| 285 | + | |
| 286 | +```bash | |
| 287 | +# 添加到crontab | |
| 288 | +crontab -e | |
| 289 | + | |
| 290 | +# 每天凌晨2点运行 | |
| 291 | +0 2 * * * cd /home/tw/recommendation/offline_tasks && python3 scripts/generate_session.py && cd ../collaboration && bash run.sh >> logs/swing_$(date +\%Y\%m\%d).log 2>&1 | |
| 292 | +``` | |
| 293 | + | |
| 294 | +## 相关文档 | |
| 295 | + | |
| 296 | +- [Swing算法原理](./collaboration/README.md) | |
| 297 | +- [离线任务配置](./offline_tasks/config/offline_config.py) | |
| 298 | +- [Debug工具使用](./offline_tasks/scripts/debug_utils.py) | |
| 299 | + | |
| 300 | +## 常见问题 | |
| 301 | + | |
| 302 | +**Q: session文件格式选择哪个?** | |
| 303 | +A: run.sh会自动检测格式。建议使用 `--format both` 生成两种格式。 | |
| 304 | + | |
| 305 | +**Q: Swing算法运行多久?** | |
| 306 | +A: 取决于数据量和线程数。通常: | |
| 307 | +- 1万商品:1-5分钟 | |
| 308 | +- 10万商品:10-30分钟 | |
| 309 | +- 数据量大时建议使用多线程 | |
| 310 | + | |
| 311 | +**Q: 如何调整相似商品数量?** | |
| 312 | +A: 修改 `src/swing.cc` 中的 `max_sim_list_len` 参数(默认300)。 | |
| 313 | + | |
| 314 | +**Q: 能否使用Python版本的Swing?** | |
| 315 | +A: 可以,使用 `offline_tasks/scripts/i2i_swing.py`。但C++版本性能更好。 | |
| 316 | + | |
| 317 | +## 联系支持 | |
| 318 | + | |
| 319 | +如有问题,请参考: | |
| 320 | +- 项目README: `/home/tw/recommendation/README.md` | |
| 321 | +- 故障排查: `/home/tw/recommendation/offline_tasks/TROUBLESHOOTING.md` | |
| 322 | + | ... | ... |
offline_tasks/TROUBLESHOOTING.md renamed to offline_tasks/doc/TROUBLESHOOTING.md
offline_tasks/START_HERE.md renamed to offline_tasks/doc/从这里开始.md
| ... | ... | @@ -0,0 +1,350 @@ |
| 1 | +# 完整索引清单 | |
| 2 | + | |
| 3 | +## 📋 所有可用的推荐索引 | |
| 4 | + | |
| 5 | +### 1. i2i 相似度索引 | |
| 6 | + | |
| 7 | +#### 1.1 行为相似索引(3种) | |
| 8 | + | |
| 9 | +**Swing算法**: | |
| 10 | +``` | |
| 11 | +i2i:swing:{item_id} | |
| 12 | +``` | |
| 13 | +示例:`i2i:swing:12345` | |
| 14 | + | |
| 15 | +**Session Word2Vec**: | |
| 16 | +``` | |
| 17 | +i2i:session_w2v:{item_id} | |
| 18 | +``` | |
| 19 | +示例:`i2i:session_w2v:12345` | |
| 20 | + | |
| 21 | +**DeepWalk**: | |
| 22 | +``` | |
| 23 | +i2i:deepwalk:{item_id} | |
| 24 | +``` | |
| 25 | +示例:`i2i:deepwalk:12345` | |
| 26 | + | |
| 27 | +#### 1.2 内容相似索引(3种方法) | |
| 28 | + | |
| 29 | +**混合方法(推荐)**: | |
| 30 | +``` | |
| 31 | +i2i:content_hybrid:{item_id} | |
| 32 | +``` | |
| 33 | +示例:`i2i:content_hybrid:12345` | |
| 34 | + | |
| 35 | +**TF-IDF方法**: | |
| 36 | +``` | |
| 37 | +i2i:content_tfidf:{item_id} | |
| 38 | +``` | |
| 39 | +示例:`i2i:content_tfidf:12345` | |
| 40 | + | |
| 41 | +**分类方法**: | |
| 42 | +``` | |
| 43 | +i2i:content_category:{item_id} | |
| 44 | +``` | |
| 45 | +示例:`i2i:content_category:12345` | |
| 46 | + | |
| 47 | +--- | |
| 48 | + | |
| 49 | +### 2. 兴趣点聚合索引 | |
| 50 | + | |
| 51 | +格式:`interest:{list_type}:{dimension}:{value}` | |
| 52 | + | |
| 53 | +#### 2.1 列表类型(list_type) | |
| 54 | + | |
| 55 | +- `hot` - 热门商品 | |
| 56 | +- `cart` - 加购商品 | |
| 57 | +- `new` - 新品 | |
| 58 | +- `global` - 全局(所有数据) | |
| 59 | + | |
| 60 | +#### 2.2 单维度索引 | |
| 61 | + | |
| 62 | +##### 业务平台(platform) | |
| 63 | +``` | |
| 64 | +interest:hot:platform:pc | |
| 65 | +interest:hot:platform:mobile | |
| 66 | +interest:cart:platform:pc | |
| 67 | +interest:new:platform:mobile | |
| 68 | +interest:global:platform:pc | |
| 69 | +``` | |
| 70 | + | |
| 71 | +##### 客户端平台(client_platform) | |
| 72 | +``` | |
| 73 | +interest:hot:client_platform:web | |
| 74 | +interest:hot:client_platform:app | |
| 75 | +interest:cart:client_platform:web | |
| 76 | +interest:new:client_platform:app | |
| 77 | +interest:global:client_platform:web | |
| 78 | +``` | |
| 79 | + | |
| 80 | +##### 供应商(supplier) | |
| 81 | +``` | |
| 82 | +interest:hot:supplier:10001 | |
| 83 | +interest:hot:supplier:10002 | |
| 84 | +interest:cart:supplier:10001 | |
| 85 | +interest:new:supplier:10002 | |
| 86 | +interest:global:supplier:10001 | |
| 87 | +``` | |
| 88 | + | |
| 89 | +##### 一级分类(category_level1) | |
| 90 | +``` | |
| 91 | +interest:hot:category_level1:100 | |
| 92 | +interest:cart:category_level1:100 | |
| 93 | +interest:new:category_level1:100 | |
| 94 | +interest:global:category_level1:100 | |
| 95 | +``` | |
| 96 | + | |
| 97 | +##### 二级分类(category_level2) | |
| 98 | +``` | |
| 99 | +interest:hot:category_level2:200 | |
| 100 | +interest:cart:category_level2:200 | |
| 101 | +interest:new:category_level2:200 | |
| 102 | +interest:global:category_level2:200 | |
| 103 | +``` | |
| 104 | + | |
| 105 | +##### 三级分类(category_level3) | |
| 106 | +``` | |
| 107 | +interest:hot:category_level3:300 | |
| 108 | +interest:cart:category_level3:300 | |
| 109 | +interest:new:category_level3:300 | |
| 110 | +interest:global:category_level3:300 | |
| 111 | +``` | |
| 112 | + | |
| 113 | +##### 四级分类(category_level4) | |
| 114 | +``` | |
| 115 | +interest:hot:category_level4:400 | |
| 116 | +interest:cart:category_level4:400 | |
| 117 | +interest:new:category_level4:400 | |
| 118 | +interest:global:category_level4:400 | |
| 119 | +``` | |
| 120 | + | |
| 121 | +#### 2.3 组合维度索引 | |
| 122 | + | |
| 123 | +##### 平台 + 客户端 | |
| 124 | +``` | |
| 125 | +interest:hot:platform_client:pc_web | |
| 126 | +interest:hot:platform_client:pc_app | |
| 127 | +interest:hot:platform_client:mobile_web | |
| 128 | +interest:hot:platform_client:mobile_app | |
| 129 | +``` | |
| 130 | + | |
| 131 | +##### 平台 + 二级分类 | |
| 132 | +``` | |
| 133 | +interest:hot:platform_category2:pc_200 | |
| 134 | +interest:hot:platform_category2:mobile_200 | |
| 135 | +interest:cart:platform_category2:pc_200 | |
| 136 | +interest:new:platform_category2:mobile_200 | |
| 137 | +``` | |
| 138 | + | |
| 139 | +##### 平台 + 三级分类 | |
| 140 | +``` | |
| 141 | +interest:hot:platform_category3:pc_300 | |
| 142 | +interest:hot:platform_category3:mobile_300 | |
| 143 | +interest:cart:platform_category3:pc_300 | |
| 144 | +interest:new:platform_category3:mobile_300 | |
| 145 | +``` | |
| 146 | + | |
| 147 | +##### 客户端平台 + 二级分类 | |
| 148 | +``` | |
| 149 | +interest:hot:client_category2:web_200 | |
| 150 | +interest:hot:client_category2:app_200 | |
| 151 | +interest:cart:client_category2:web_200 | |
| 152 | +interest:new:client_category2:app_200 | |
| 153 | +``` | |
| 154 | + | |
| 155 | +--- | |
| 156 | + | |
| 157 | +## 🎯 按业务场景的索引使用 | |
| 158 | + | |
| 159 | +### 场景1: 首页个性化推荐 | |
| 160 | + | |
| 161 | +**方案A: 基于平台** | |
| 162 | +```python | |
| 163 | +key = f"interest:hot:platform:{user_platform}" | |
| 164 | +# 示例:interest:hot:platform:pc | |
| 165 | +``` | |
| 166 | + | |
| 167 | +**方案B: 基于分类偏好** | |
| 168 | +```python | |
| 169 | +key = f"interest:hot:category_level2:{user_favorite_category}" | |
| 170 | +# 示例:interest:hot:category_level2:200 | |
| 171 | +``` | |
| 172 | + | |
| 173 | +**方案C: 基于平台+分类** | |
| 174 | +```python | |
| 175 | +key = f"interest:hot:platform_category2:{user_platform}_{category_id}" | |
| 176 | +# 示例:interest:hot:platform_category2:pc_200 | |
| 177 | +``` | |
| 178 | + | |
| 179 | +### 场景2: 详情页相关推荐 | |
| 180 | + | |
| 181 | +**方案A: 行为相似** | |
| 182 | +```python | |
| 183 | +key = f"i2i:swing:{current_item_id}" | |
| 184 | +# 示例:i2i:swing:12345 | |
| 185 | +``` | |
| 186 | + | |
| 187 | +**方案B: 内容相似** | |
| 188 | +```python | |
| 189 | +key = f"i2i:content_hybrid:{current_item_id}" | |
| 190 | +# 示例:i2i:content_hybrid:12345 | |
| 191 | +``` | |
| 192 | + | |
| 193 | +**方案C: 融合推荐** | |
| 194 | +```python | |
| 195 | +behavior_similar = redis.get(f"i2i:swing:{item_id}") | |
| 196 | +content_similar = redis.get(f"i2i:content_hybrid:{item_id}") | |
| 197 | +# 融合两种结果 | |
| 198 | +``` | |
| 199 | + | |
| 200 | +### 场景3: 分类页推荐 | |
| 201 | + | |
| 202 | +**方案A: 该分类热门** | |
| 203 | +```python | |
| 204 | +key = f"interest:hot:category_level2:{category_id}" | |
| 205 | +# 示例:interest:hot:category_level2:200 | |
| 206 | +``` | |
| 207 | + | |
| 208 | +**方案B: 该分类新品** | |
| 209 | +```python | |
| 210 | +key = f"interest:new:category_level2:{category_id}" | |
| 211 | +# 示例:interest:new:category_level2:200 | |
| 212 | +``` | |
| 213 | + | |
| 214 | +**方案C: 该分类+平台** | |
| 215 | +```python | |
| 216 | +key = f"interest:hot:platform_category2:{platform}_{category_id}" | |
| 217 | +# 示例:interest:hot:platform_category2:pc_200 | |
| 218 | +``` | |
| 219 | + | |
| 220 | +### 场景4: 供应商店铺页 | |
| 221 | + | |
| 222 | +**方案A: 供应商热门商品** | |
| 223 | +```python | |
| 224 | +key = f"interest:hot:supplier:{supplier_id}" | |
| 225 | +# 示例:interest:hot:supplier:10001 | |
| 226 | +``` | |
| 227 | + | |
| 228 | +**方案B: 供应商新品** | |
| 229 | +```python | |
| 230 | +key = f"interest:new:supplier:{supplier_id}" | |
| 231 | +# 示例:interest:new:supplier:10001 | |
| 232 | +``` | |
| 233 | + | |
| 234 | +### 场景5: 搜索结果页推荐 | |
| 235 | + | |
| 236 | +**方案A: 全局热门** | |
| 237 | +```python | |
| 238 | +key = "interest:global:platform:pc" | |
| 239 | +``` | |
| 240 | + | |
| 241 | +**方案B: 分类相关** | |
| 242 | +```python | |
| 243 | +key = f"interest:global:category_level2:{search_category}" | |
| 244 | +# 示例:interest:global:category_level2:200 | |
| 245 | +``` | |
| 246 | + | |
| 247 | +--- | |
| 248 | + | |
| 249 | +## 📊 索引数量统计 | |
| 250 | + | |
| 251 | +### i2i索引 | |
| 252 | +- 行为相似:3种算法 × 商品数量 | |
| 253 | +- 内容相似:3种方法 × 商品数量 | |
| 254 | +- **总计**:6 × 商品数量 | |
| 255 | + | |
| 256 | +### 兴趣点聚合索引 | |
| 257 | + | |
| 258 | +**单维度**: | |
| 259 | +- platform: 2-10个 | |
| 260 | +- client_platform: 2-5个 | |
| 261 | +- supplier: 100-1000个 | |
| 262 | +- category_level1: 10-50个 | |
| 263 | +- category_level2: 50-200个 | |
| 264 | +- category_level3: 200-1000个 | |
| 265 | +- category_level4: 1000-5000个 | |
| 266 | + | |
| 267 | +**组合维度**: | |
| 268 | +- platform_client: 4-50个 | |
| 269 | +- platform_category2: 100-2000个 | |
| 270 | +- platform_category3: 400-10000个 | |
| 271 | +- client_category2: 100-1000个 | |
| 272 | + | |
| 273 | +**列表类型**:每个维度 × 4种类型(hot/cart/new/global) | |
| 274 | + | |
| 275 | +**预估总数**:10000-50000条索引 | |
| 276 | + | |
| 277 | +--- | |
| 278 | + | |
| 279 | +## 🔍 查询示例代码 | |
| 280 | + | |
| 281 | +### Python示例 | |
| 282 | + | |
| 283 | +```python | |
| 284 | +import redis | |
| 285 | + | |
| 286 | +# 连接Redis | |
| 287 | +r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) | |
| 288 | + | |
| 289 | +# 1. 查询商品的相似商品 | |
| 290 | +item_id = "12345" | |
| 291 | +similar_items = r.get(f"i2i:swing:{item_id}") | |
| 292 | +if similar_items: | |
| 293 | + items = similar_items.split(',') | |
| 294 | + for item in items[:5]: # 取前5个 | |
| 295 | + item_id, score = item.split(':') | |
| 296 | + print(f"商品ID: {item_id}, 相似度: {score}") | |
| 297 | + | |
| 298 | +# 2. 查询分类热门商品 | |
| 299 | +category_id = "200" | |
| 300 | +hot_items = r.get(f"interest:hot:category_level2:{category_id}") | |
| 301 | +if hot_items: | |
| 302 | + items = hot_items.split(',') | |
| 303 | + for item in items[:10]: # 取前10个 | |
| 304 | + item_id, score = item.split(':') | |
| 305 | + print(f"商品ID: {item_id}, 得分: {score}") | |
| 306 | + | |
| 307 | +# 3. 查询平台+分类组合 | |
| 308 | +platform = "pc" | |
| 309 | +category_id = "200" | |
| 310 | +key = f"interest:hot:platform_category2:{platform}_{category_id}" | |
| 311 | +items = r.get(key) | |
| 312 | +``` | |
| 313 | + | |
| 314 | +### Redis命令行示例 | |
| 315 | + | |
| 316 | +```bash | |
| 317 | +# 查询商品相似度 | |
| 318 | +redis-cli GET "i2i:swing:12345" | |
| 319 | + | |
| 320 | +# 查询分类热门 | |
| 321 | +redis-cli GET "interest:hot:category_level2:200" | |
| 322 | + | |
| 323 | +# 查询供应商商品 | |
| 324 | +redis-cli GET "interest:hot:supplier:10001" | |
| 325 | + | |
| 326 | +# 模糊查询所有热门索引 | |
| 327 | +redis-cli KEYS "interest:hot:*" | |
| 328 | + | |
| 329 | +# 查看某个分类的所有类型 | |
| 330 | +redis-cli KEYS "interest:*:category_level2:200" | |
| 331 | +``` | |
| 332 | + | |
| 333 | +--- | |
| 334 | + | |
| 335 | +## 📝 注意事项 | |
| 336 | + | |
| 337 | +1. **索引命名规范**:严格遵循 `type:subtype:dimension:value` 格式 | |
| 338 | +2. **值的格式**:`item_id1:score1,item_id2:score2,...` | |
| 339 | +3. **过期时间**:建议设置7天过期 | |
| 340 | +4. **更新频率**:建议每天更新一次 | |
| 341 | +5. **查询优先级**: | |
| 342 | + - 优先使用细粒度索引(如四级分类) | |
| 343 | + - 粗粒度索引作为后备(如一级分类) | |
| 344 | + - 融合多个索引结果 | |
| 345 | + | |
| 346 | +--- | |
| 347 | + | |
| 348 | +**版本**: v1.1 | |
| 349 | +**生成日期**: 2025-10-16 | |
| 350 | +**索引总数**: 约10000-50000条 | ... | ... |
offline_tasks/QUICKSTART.md renamed to offline_tasks/doc/快速开始.md
| ... | ... | @@ -0,0 +1,217 @@ |
| 1 | +# 故障排除指南 | |
| 2 | + | |
| 3 | +## 常见问题及解决方案 | |
| 4 | + | |
| 5 | +### 1. 数据库字段错误 | |
| 6 | + | |
| 7 | +#### 问题: | |
| 8 | +``` | |
| 9 | +pymysql.err.OperationalError: (1105, "errCode = 2, detailMessage = Unknown column 'xxx' in 'xxx'") | |
| 10 | +``` | |
| 11 | + | |
| 12 | +#### 原因: | |
| 13 | +数据库表结构与代码中使用的字段名不匹配。 | |
| 14 | + | |
| 15 | +#### 解决方案: | |
| 16 | +1. 查看 `DATABASE_SETUP.md` 了解如何配置字段 | |
| 17 | +2. 修改对应脚本中的SQL查询,使用实际存在的字段名 | |
| 18 | +3. 如果是分类字段不存在,这些字段是可选的,代码会自动跳过 | |
| 19 | + | |
| 20 | +#### 已修复的字段: | |
| 21 | +- ✅ `category_level2_id` 和 `category_level3_id` 现在是可选的 | |
| 22 | +- ✅ 基础功能不依赖分类字段 | |
| 23 | + | |
| 24 | +--- | |
| 25 | + | |
| 26 | +### 2. 连接超时 | |
| 27 | + | |
| 28 | +#### 问题: | |
| 29 | +``` | |
| 30 | +pymysql.err.OperationalError: (2003, "Can't connect to MySQL server...") | |
| 31 | +``` | |
| 32 | + | |
| 33 | +#### 解决方案: | |
| 34 | +1. 检查数据库配置:`config/offline_config.py` | |
| 35 | +2. 确认网络连接和防火墙设置 | |
| 36 | +3. 运行测试:`python3 test_connection.py` | |
| 37 | + | |
| 38 | +--- | |
| 39 | + | |
| 40 | +### 3. 内存不足 | |
| 41 | + | |
| 42 | +#### 问题: | |
| 43 | +程序运行时内存占用过高或被杀死。 | |
| 44 | + | |
| 45 | +#### 解决方案: | |
| 46 | +1. 减少回溯天数:`--lookback_days 365`(从730改为365) | |
| 47 | +2. 减少输出数量:`--top_n 20`(从50改为20) | |
| 48 | +3. 先运行单个算法: | |
| 49 | + ```bash | |
| 50 | + python3 scripts/i2i_session_w2v.py # 内存占用较小 | |
| 51 | + ``` | |
| 52 | +4. 跳过Swing算法(内存占用最大): | |
| 53 | + ```bash | |
| 54 | + python3 run_all.py --skip-i2i | |
| 55 | + ``` | |
| 56 | + | |
| 57 | +--- | |
| 58 | + | |
| 59 | +### 4. 运行时间过长 | |
| 60 | + | |
| 61 | +#### 解决方案: | |
| 62 | +1. 减少数据量:`--lookback_days 180` | |
| 63 | +2. 只运行特定算法: | |
| 64 | + ```bash | |
| 65 | + python3 run_all.py --only-w2v | |
| 66 | + ``` | |
| 67 | +3. 考虑使用C++版本的Swing(性能提升10倍) | |
| 68 | + | |
| 69 | +--- | |
| 70 | + | |
| 71 | +### 5. 依赖包安装失败 | |
| 72 | + | |
| 73 | +#### 解决方案: | |
| 74 | +```bash | |
| 75 | +# 单独安装失败的包 | |
| 76 | +pip3 install pandas sqlalchemy pymysql gensim numpy | |
| 77 | + | |
| 78 | +# 或使用国内镜像 | |
| 79 | +pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple | |
| 80 | +``` | |
| 81 | + | |
| 82 | +--- | |
| 83 | + | |
| 84 | +### 6. Redis连接失败 | |
| 85 | + | |
| 86 | +#### 问题: | |
| 87 | +``` | |
| 88 | +redis.exceptions.ConnectionError: Error connecting to Redis | |
| 89 | +``` | |
| 90 | + | |
| 91 | +#### 解决方案: | |
| 92 | +1. Redis是可选的,只影响索引加载功能 | |
| 93 | +2. 如果不需要Redis,可以跳过: | |
| 94 | + ```bash | |
| 95 | + python3 run_all.py # 只运行离线任务,不加载到Redis | |
| 96 | + ``` | |
| 97 | +3. 如果需要Redis,确认Redis已安装并运行: | |
| 98 | + ```bash | |
| 99 | + redis-cli ping # 应该返回 PONG | |
| 100 | + ``` | |
| 101 | + | |
| 102 | +--- | |
| 103 | + | |
| 104 | +### 7. 输出文件为空 | |
| 105 | + | |
| 106 | +#### 可能原因: | |
| 107 | +1. 数据量太少(没有满足最小阈值) | |
| 108 | +2. 时间范围内没有数据 | |
| 109 | +3. SQL查询条件过于严格 | |
| 110 | + | |
| 111 | +#### 解决方案: | |
| 112 | +1. 检查日志:`tail -f logs/run_all_*.log` | |
| 113 | +2. 调整参数: | |
| 114 | + - 增加时间范围:`--lookback_days 1000` | |
| 115 | + - 减少阈值:修改配置文件中的 `min_interaction_count` | |
| 116 | +3. 检查数据库中是否有数据: | |
| 117 | + ```python | |
| 118 | + # 运行简单查询测试 | |
| 119 | + python3 test_connection.py | |
| 120 | + ``` | |
| 121 | + | |
| 122 | +--- | |
| 123 | + | |
| 124 | +### 8. 权限问题 | |
| 125 | + | |
| 126 | +#### 问题: | |
| 127 | +``` | |
| 128 | +PermissionError: [Errno 13] Permission denied | |
| 129 | +``` | |
| 130 | + | |
| 131 | +#### 解决方案: | |
| 132 | +```bash | |
| 133 | +# 给脚本添加执行权限 | |
| 134 | +chmod +x install.sh run_all.py | |
| 135 | + | |
| 136 | +# 确保有写入权限 | |
| 137 | +chmod 755 output/ logs/ | |
| 138 | +``` | |
| 139 | + | |
| 140 | +--- | |
| 141 | + | |
| 142 | +### 9. Python版本问题 | |
| 143 | + | |
| 144 | +#### 要求: | |
| 145 | +Python 3.7+ | |
| 146 | + | |
| 147 | +#### 检查版本: | |
| 148 | +```bash | |
| 149 | +python3 --version | |
| 150 | +``` | |
| 151 | + | |
| 152 | +#### 如果版本过低,需要升级Python | |
| 153 | + | |
| 154 | +--- | |
| 155 | + | |
| 156 | +### 10. 编码问题 | |
| 157 | + | |
| 158 | +#### 问题: | |
| 159 | +``` | |
| 160 | +UnicodeDecodeError: 'utf-8' codec can't decode byte... | |
| 161 | +``` | |
| 162 | + | |
| 163 | +#### 解决方案: | |
| 164 | +确保所有文件使用UTF-8编码,特别是配置文件和输出文件。 | |
| 165 | + | |
| 166 | +--- | |
| 167 | + | |
| 168 | +## 调试技巧 | |
| 169 | + | |
| 170 | +### 1. 查看详细日志 | |
| 171 | +```bash | |
| 172 | +tail -f logs/run_all_*.log | |
| 173 | +``` | |
| 174 | + | |
| 175 | +### 2. 运行单个任务(便于调试) | |
| 176 | +```bash | |
| 177 | +python3 scripts/i2i_swing.py --lookback_days 30 --top_n 10 | |
| 178 | +``` | |
| 179 | + | |
| 180 | +### 3. 使用较小的数据量测试 | |
| 181 | +```bash | |
| 182 | +python3 run_all.py --lookback_days 30 --top_n 10 | |
| 183 | +``` | |
| 184 | + | |
| 185 | +### 4. 检查中间结果 | |
| 186 | +```bash | |
| 187 | +ls -lh output/ | |
| 188 | +head -n 20 output/i2i_swing_*.txt | |
| 189 | +``` | |
| 190 | + | |
| 191 | +--- | |
| 192 | + | |
| 193 | +## 获取支持 | |
| 194 | + | |
| 195 | +如果以上方法都无法解决问题: | |
| 196 | + | |
| 197 | +1. **查看文档**: | |
| 198 | + - `README.md` - 详细说明 | |
| 199 | + - `DATABASE_SETUP.md` - 数据库配置 | |
| 200 | + - `QUICKSTART.md` - 快速开始 | |
| 201 | + | |
| 202 | +2. **查看日志**: | |
| 203 | + - `logs/` 目录下的所有日志文件 | |
| 204 | + | |
| 205 | +3. **简化测试**: | |
| 206 | + - 先运行 `test_connection.py` | |
| 207 | + - 再运行单个脚本 | |
| 208 | + - 使用小数据量测试 | |
| 209 | + | |
| 210 | +4. **记录错误信息**: | |
| 211 | + - 完整的错误堆栈 | |
| 212 | + - 使用的命令 | |
| 213 | + - 配置文件内容 | |
| 214 | + | |
| 215 | +--- | |
| 216 | + | |
| 217 | +**提示**:大部分问题都与数据库字段名不匹配有关,请优先查看 `DATABASE_SETUP.md`。 | ... | ... |
| ... | ... | @@ -0,0 +1,179 @@ |
| 1 | +# 数据库字段配置说明 | |
| 2 | + | |
| 3 | +## 问题说明 | |
| 4 | + | |
| 5 | +如果运行时遇到类似 `Unknown column 'xxx'` 的错误,说明数据库表结构与代码中使用的字段名不匹配。 | |
| 6 | + | |
| 7 | +## 已适配的基础字段 | |
| 8 | + | |
| 9 | +当前代码已经适配了以下基础字段(参考 `item_sim.py`): | |
| 10 | + | |
| 11 | +### sensors_events 表 | |
| 12 | +- `anonymous_id` - 用户ID | |
| 13 | +- `item_id` - 商品ID | |
| 14 | +- `event` - 事件类型 | |
| 15 | +- `create_time` - 创建时间 | |
| 16 | +- `platform` - 平台(可选) | |
| 17 | +- `country` - 国家(可选) | |
| 18 | +- `customer_type` - 客户类型(可选) | |
| 19 | + | |
| 20 | +### prd_goods_sku 表 | |
| 21 | +- `id` - 商品ID | |
| 22 | +- `name` - 商品名称 | |
| 23 | +- `create_time` - 创建时间(用于判断新品) | |
| 24 | + | |
| 25 | +## 可选字段配置 | |
| 26 | + | |
| 27 | +如果您的数据库表包含以下字段,可以在SQL查询中添加它们以支持更多维度: | |
| 28 | + | |
| 29 | +### 分类字段(可选) | |
| 30 | +- `category_level1_id` - 一级分类ID | |
| 31 | +- `category_level2_id` - 二级分类ID | |
| 32 | +- `category_level3_id` - 三级分类ID | |
| 33 | + | |
| 34 | +## 如何添加分类字段支持 | |
| 35 | + | |
| 36 | +如果您的数据库有分类字段,可以按以下步骤启用: | |
| 37 | + | |
| 38 | +### 步骤1: 修改 SQL 查询 | |
| 39 | + | |
| 40 | +编辑 `scripts/interest_aggregation.py`,找到 SQL 查询部分,添加分类字段: | |
| 41 | + | |
| 42 | +```python | |
| 43 | +sql_query = f""" | |
| 44 | +SELECT | |
| 45 | + se.anonymous_id AS user_id, | |
| 46 | + se.item_id, | |
| 47 | + se.event AS event_type, | |
| 48 | + se.create_time, | |
| 49 | + pgs.name AS item_name, | |
| 50 | + pgs.create_time AS item_create_time, | |
| 51 | + pgs.category_level2_id, # 添加这一行 | |
| 52 | + pgs.category_level3_id, # 添加这一行 | |
| 53 | + se.platform, | |
| 54 | + se.country, | |
| 55 | + se.customer_type | |
| 56 | +FROM | |
| 57 | + sensors_events se | |
| 58 | +LEFT JOIN prd_goods_sku pgs ON se.item_id = pgs.id | |
| 59 | +... | |
| 60 | +""" | |
| 61 | +``` | |
| 62 | + | |
| 63 | +### 步骤2: 修改聚合逻辑 | |
| 64 | + | |
| 65 | +在 `aggregate_by_dimensions` 函数中,字段检查已经做好了,如果字段存在会自动使用: | |
| 66 | + | |
| 67 | +```python | |
| 68 | +# 维度4: 二级分类 (category_level2) - 如果字段存在 | |
| 69 | +if 'category_level2_id' in row and pd.notna(row.get('category_level2_id')): | |
| 70 | + key = f"category_level2:{row['category_level2_id']}" | |
| 71 | + aggregations[key][item_id] += weight | |
| 72 | +``` | |
| 73 | + | |
| 74 | +这段代码会自动检测字段是否存在,如果存在就使用,不存在就跳过。 | |
| 75 | + | |
| 76 | +## 查看实际表结构 | |
| 77 | + | |
| 78 | +运行以下命令查看您的数据库表结构: | |
| 79 | + | |
| 80 | +```python | |
| 81 | +# 创建一个简单的脚本查看表结构 | |
| 82 | +import pandas as pd | |
| 83 | +from db_service import create_db_connection | |
| 84 | +from offline_tasks.config.offline_config import DB_CONFIG | |
| 85 | + | |
| 86 | +engine = create_db_connection( | |
| 87 | + DB_CONFIG['host'], | |
| 88 | + DB_CONFIG['port'], | |
| 89 | + DB_CONFIG['database'], | |
| 90 | + DB_CONFIG['username'], | |
| 91 | + DB_CONFIG['password'] | |
| 92 | +) | |
| 93 | + | |
| 94 | +# 查看 prd_goods_sku 表结构 | |
| 95 | +df = pd.read_sql("SELECT * FROM prd_goods_sku LIMIT 1", engine) | |
| 96 | +print("prd_goods_sku 字段列表:") | |
| 97 | +for col in df.columns: | |
| 98 | + print(f" - {col}") | |
| 99 | + | |
| 100 | +# 查看 sensors_events 表结构 | |
| 101 | +df = pd.read_sql("SELECT * FROM sensors_events LIMIT 1", engine) | |
| 102 | +print("\nsensors_events 字段列表:") | |
| 103 | +for col in df.columns: | |
| 104 | + print(f" - {col}") | |
| 105 | +``` | |
| 106 | + | |
| 107 | +## 常见字段名映射 | |
| 108 | + | |
| 109 | +如果您的数据库使用不同的字段名,需要在SQL查询中做映射: | |
| 110 | + | |
| 111 | +| 代码中的字段 | 可能的实际字段名 | 修改方式 | | |
| 112 | +|-------------|----------------|---------| | |
| 113 | +| `category_level2_id` | `cat2_id`, `category2`, `second_category` | `pgs.cat2_id AS category_level2_id` | | |
| 114 | +| `category_level3_id` | `cat3_id`, `category3`, `third_category` | `pgs.cat3_id AS category_level3_id` | | |
| 115 | +| `anonymous_id` | `user_id`, `uid`, `visitor_id` | `se.user_id AS anonymous_id` | | |
| 116 | +| `customer_type` | `client_type`, `buyer_type` | `se.client_type AS customer_type` | | |
| 117 | + | |
| 118 | +## 完整示例 | |
| 119 | + | |
| 120 | +假设您的表结构是: | |
| 121 | +- `prd_goods_sku` 有字段:`id`, `title`, `cat2`, `cat3`, `add_time` | |
| 122 | +- `sensors_events` 有字段:`uid`, `goods_id`, `action`, `time` | |
| 123 | + | |
| 124 | +则需要修改SQL为: | |
| 125 | + | |
| 126 | +```python | |
| 127 | +sql_query = f""" | |
| 128 | +SELECT | |
| 129 | + se.uid AS user_id, | |
| 130 | + se.goods_id AS item_id, | |
| 131 | + se.action AS event_type, | |
| 132 | + se.time AS create_time, | |
| 133 | + pgs.title AS item_name, | |
| 134 | + pgs.add_time AS item_create_time, | |
| 135 | + pgs.cat2 AS category_level2_id, | |
| 136 | + pgs.cat3 AS category_level3_id | |
| 137 | +FROM | |
| 138 | + sensors_events se | |
| 139 | +LEFT JOIN prd_goods_sku pgs ON se.goods_id = pgs.id | |
| 140 | +... | |
| 141 | +""" | |
| 142 | +``` | |
| 143 | + | |
| 144 | +## 最小化配置 | |
| 145 | + | |
| 146 | +如果只想先测试基本功能,可以只使用最基础的字段: | |
| 147 | + | |
| 148 | +### i2i 算法只需要: | |
| 149 | +- `anonymous_id` / `user_id` | |
| 150 | +- `item_id` | |
| 151 | +- `event` / `event_type` | |
| 152 | +- `create_time` | |
| 153 | +- `name` (商品名称) | |
| 154 | + | |
| 155 | +### 兴趣点聚合至少需要: | |
| 156 | +- 以上i2i的字段 | |
| 157 | +- 至少一个维度字段(如 `platform` 或 `country`) | |
| 158 | + | |
| 159 | +## 测试连接 | |
| 160 | + | |
| 161 | +修改后,运行测试脚本验证: | |
| 162 | + | |
| 163 | +```bash | |
| 164 | +cd /home/tw/recommendation/offline_tasks | |
| 165 | +python3 test_connection.py | |
| 166 | +``` | |
| 167 | + | |
| 168 | +## 获取帮助 | |
| 169 | + | |
| 170 | +如果仍有问题,请: | |
| 171 | +1. 查看日志文件:`logs/run_all_*.log` | |
| 172 | +2. 运行单个脚本测试,便于调试 | |
| 173 | +3. 使用 `--help` 参数查看命令行选项 | |
| 174 | + | |
| 175 | +```bash | |
| 176 | +python3 scripts/i2i_swing.py --help | |
| 177 | +python3 scripts/interest_aggregation.py --help | |
| 178 | +``` | |
| 179 | + | ... | ... |
offline_tasks/CHANGELOG.md renamed to offline_tasks/doc/更新日志.md
| ... | ... | @@ -0,0 +1,197 @@ |
| 1 | +# 离线索引产出规范 | |
| 2 | + | |
| 3 | +## 📋 索引任务列表 | |
| 4 | + | |
| 5 | +| 模块名称 | 任务命令 | 调度频次 | 输出数据 | 格式和示例 | | |
| 6 | +|---------|---------|---------|---------|-----------| | |
| 7 | +| **i2i_swing** | `python3 scripts/i2i_swing.py` | 每天 | `output/i2i_swing_YYYYMMDD.txt` | `item_id \t item_name \t similar_id1:score1,similar_id2:score2,...` | | |
| 8 | +| **i2i_session_w2v** | `python3 scripts/i2i_session_w2v.py` | 每天 | `output/i2i_session_w2v_YYYYMMDD.txt` | `item_id \t item_name \t similar_id1:score1,similar_id2:score2,...` | | |
| 9 | +| **i2i_deepwalk** | `python3 scripts/i2i_deepwalk.py` | 每天 | `output/i2i_deepwalk_YYYYMMDD.txt` | `item_id \t item_name \t similar_id1:score1,similar_id2:score2,...` | | |
| 10 | +| **i2i_content** | `python3 scripts/i2i_content_similar.py` | 每周 | `output/i2i_content_hybrid_YYYYMMDD.txt` | `item_id \t item_name \t similar_id1:score1,similar_id2:score2,...` | | |
| 11 | +| **interest_hot** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_hot_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | | |
| 12 | +| **interest_cart** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_cart_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | | |
| 13 | +| **interest_new** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_new_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | | |
| 14 | +| **interest_global** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_global_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | | |
| 15 | + | |
| 16 | +## 📊 详细格式说明 | |
| 17 | + | |
| 18 | +### 1. i2i相似度索引 | |
| 19 | + | |
| 20 | +#### 输出格式 | |
| 21 | +``` | |
| 22 | +item_id \t item_name \t similar_id1:score1,similar_id2:score2,... | |
| 23 | +``` | |
| 24 | + | |
| 25 | +#### 示例 | |
| 26 | +``` | |
| 27 | +12345 香蕉干 67890:0.8567,11223:0.7234,44556:0.6891 | |
| 28 | +67890 芒果干 12345:0.8567,22334:0.7123,55667:0.6543 | |
| 29 | +``` | |
| 30 | + | |
| 31 | +#### 字段说明 | |
| 32 | +- `item_id`: 商品SKU ID | |
| 33 | +- `item_name`: 商品名称 | |
| 34 | +- `similar_id`: 相似商品ID | |
| 35 | +- `score`: 相似度分数(0-1之间,越大越相似) | |
| 36 | + | |
| 37 | +#### 算法差异 | |
| 38 | +| 算法 | 特点 | 适用场景 | | |
| 39 | +|------|------|---------| | |
| 40 | +| **Swing** | 基于用户共同行为,发现购买关联 | 详情页"大家都在看" | | |
| 41 | +| **Session W2V** | 基于会话序列,捕捉浏览顺序 | 详情页"看了又看" | | |
| 42 | +| **DeepWalk** | 基于图结构,发现深层关系 | 详情页"相关推荐" | | |
| 43 | +| **Content** | 基于商品属性,类目相似 | 冷启动商品推荐 | | |
| 44 | + | |
| 45 | +### 2. 兴趣点聚合索引 | |
| 46 | + | |
| 47 | +#### 输出格式 | |
| 48 | +``` | |
| 49 | +dimension_key \t item_id1,item_id2,item_id3,... | |
| 50 | +``` | |
| 51 | + | |
| 52 | +#### 示例 | |
| 53 | +``` | |
| 54 | +platform:pc 12345,67890,11223,44556,22334 | |
| 55 | +category_level2:200 67890,12345,22334,55667,11223 | |
| 56 | +platform_category2:pc_200 12345,67890,22334,11223,55667 | |
| 57 | +supplier:10001 12345,44556,22334,67890,11223 | |
| 58 | +``` | |
| 59 | + | |
| 60 | +#### 维度说明 | |
| 61 | + | |
| 62 | +**单维度(7个)** | |
| 63 | +- `platform:{platform_id}` - 业务平台(pc, h5, app等) | |
| 64 | +- `client_platform:{client}` - 客户端平台(iOS, Android, Web等) | |
| 65 | +- `supplier:{supplier_id}` - 供应商 | |
| 66 | +- `category_level1:{cat_id}` - 一级分类 | |
| 67 | +- `category_level2:{cat_id}` - 二级分类 | |
| 68 | +- `category_level3:{cat_id}` - 三级分类 | |
| 69 | +- `category_level4:{cat_id}` - 四级分类 | |
| 70 | + | |
| 71 | +**组合维度(4个)** | |
| 72 | +- `platform_client:{platform}_{client}` - 平台+客户端 | |
| 73 | +- `platform_category2:{platform}_{cat_id}` - 平台+二级分类 | |
| 74 | +- `platform_category3:{platform}_{cat_id}` - 平台+三级分类 | |
| 75 | +- `client_category2:{client}_{cat_id}` - 客户端+二级分类 | |
| 76 | + | |
| 77 | +#### 列表类型说明 | |
| 78 | + | |
| 79 | +| 类型 | 文件名 | 计算逻辑 | 适用场景 | | |
| 80 | +|------|--------|---------|---------| | |
| 81 | +| **hot** | `interest_aggregation_hot_YYYYMMDD.txt` | 最近N天的高频交互商品 | 首页"热门推荐" | | |
| 82 | +| **cart** | `interest_aggregation_cart_YYYYMMDD.txt` | 高加购率商品 | 首页"热门加购" | | |
| 83 | +| **new** | `interest_aggregation_new_YYYYMMDD.txt` | 最近上架的新品 | 首页"新品推荐" | | |
| 84 | +| **global** | `interest_aggregation_global_YYYYMMDD.txt` | 全局热门商品 | 首页"猜你喜欢" | | |
| 85 | + | |
| 86 | +## 🔄 调度建议 | |
| 87 | + | |
| 88 | +### 每日调度(数据量大,变化快) | |
| 89 | +```bash | |
| 90 | +# 每天凌晨3点执行 | |
| 91 | +0 3 * * * cd /home/tw/recommendation/offline_tasks && python3 run_all.py --lookback_days 730 --top_n 50 | |
| 92 | +``` | |
| 93 | + | |
| 94 | +### 每周调度(数据量小,变化慢) | |
| 95 | +```bash | |
| 96 | +# 每周日凌晨4点执行 | |
| 97 | +0 4 * * 0 cd /home/tw/recommendation/offline_tasks && python3 scripts/i2i_content_similar.py --top_n 50 | |
| 98 | +``` | |
| 99 | + | |
| 100 | +## 📁 文件命名规范 | |
| 101 | + | |
| 102 | +### 标准格式 | |
| 103 | +``` | |
| 104 | +{algorithm_name}_{date}.txt | |
| 105 | +``` | |
| 106 | + | |
| 107 | +### 示例 | |
| 108 | +``` | |
| 109 | +i2i_swing_20251016.txt | |
| 110 | +i2i_session_w2v_20251016.txt | |
| 111 | +interest_aggregation_hot_20251016.txt | |
| 112 | +``` | |
| 113 | + | |
| 114 | +### Debug文件(开发调试用) | |
| 115 | +``` | |
| 116 | +output/debug/{algorithm_name}_{date}_readable.txt | |
| 117 | +logs/debug/{algorithm_name}_{date}_{time}.log | |
| 118 | +``` | |
| 119 | + | |
| 120 | +## 📈 数据量估算 | |
| 121 | + | |
| 122 | +| 索引类型 | 索引数量 | 单条大小 | 总大小 | 更新频率 | | |
| 123 | +|---------|---------|---------|--------|---------| | |
| 124 | +| i2i_swing | ~50,000 | ~500B | ~25MB | 每天 | | |
| 125 | +| i2i_session_w2v | ~50,000 | ~500B | ~25MB | 每天 | | |
| 126 | +| i2i_deepwalk | ~50,000 | ~500B | ~25MB | 每天 | | |
| 127 | +| i2i_content | ~50,000 | ~500B | ~25MB | 每周 | | |
| 128 | +| interest_hot | ~10,000 | ~1KB | ~10MB | 每天 | | |
| 129 | +| interest_cart | ~10,000 | ~1KB | ~10MB | 每天 | | |
| 130 | +| interest_new | ~5,000 | ~1KB | ~5MB | 每天 | | |
| 131 | +| interest_global | ~10,000 | ~1KB | ~10MB | 每天 | | |
| 132 | +| **总计** | **~245,000** | - | **~135MB** | - | | |
| 133 | + | |
| 134 | +## 🎯 质量检查 | |
| 135 | + | |
| 136 | +### 数据完整性检查 | |
| 137 | +```bash | |
| 138 | +# 检查文件是否生成 | |
| 139 | +ls -lh output/*_$(date +%Y%m%d).txt | |
| 140 | + | |
| 141 | +# 检查行数 | |
| 142 | +wc -l output/*_$(date +%Y%m%d).txt | |
| 143 | + | |
| 144 | +# 检查格式 | |
| 145 | +head -5 output/i2i_swing_$(date +%Y%m%d).txt | |
| 146 | +``` | |
| 147 | + | |
| 148 | +### 数据质量指标 | |
| 149 | + | |
| 150 | +**i2i索引质量** | |
| 151 | +- 覆盖率:有推荐的商品数 / 总商品数 > 80% | |
| 152 | +- 推荐数量:每个商品推荐10-50个相似商品 | |
| 153 | +- 分数范围:相似度分数在0.01-1.0之间 | |
| 154 | + | |
| 155 | +**兴趣聚合质量** | |
| 156 | +- 覆盖率:有数据的维度数 / 总维度数 > 60% | |
| 157 | +- 推荐数量:每个维度推荐50-1000个商品 | |
| 158 | +- 商品去重:同一商品在列表中只出现一次 | |
| 159 | + | |
| 160 | +## 🔍 查询示例 | |
| 161 | + | |
| 162 | +### 查看特定商品的相似推荐 | |
| 163 | +```bash | |
| 164 | +# 查看商品12345的相似商品 | |
| 165 | +grep "^12345\t" output/i2i_swing_20251016.txt | |
| 166 | +``` | |
| 167 | + | |
| 168 | +### 查看特定维度的热门商品 | |
| 169 | +```bash | |
| 170 | +# 查看PC平台的热门商品 | |
| 171 | +grep "^platform:pc\t" output/interest_aggregation_hot_20251016.txt | |
| 172 | +``` | |
| 173 | + | |
| 174 | +### 统计索引数量 | |
| 175 | +```bash | |
| 176 | +# 统计各类型索引数量 | |
| 177 | +for file in output/*_20251016.txt; do | |
| 178 | + echo "$file: $(wc -l < $file) 条" | |
| 179 | +done | |
| 180 | +``` | |
| 181 | + | |
| 182 | +## ⚠️ 注意事项 | |
| 183 | + | |
| 184 | +1. **文件编码**: 所有文件使用UTF-8编码 | |
| 185 | +2. **分隔符**: 使用Tab(\t)分隔字段 | |
| 186 | +3. **商品ID**: 使用数字类型,不带引号 | |
| 187 | +4. **分数精度**: 相似度分数保留4位小数 | |
| 188 | +5. **排序规则**: 相似商品按分数降序排列 | |
| 189 | +6. **去重**: 确保推荐列表中没有重复商品 | |
| 190 | +7. **有效性**: 推荐的商品必须是在售状态 | |
| 191 | + | |
| 192 | +## 🔗 相关文档 | |
| 193 | + | |
| 194 | +- **Redis数据规范**: `REDIS_DATA_SPEC.md` | |
| 195 | +- **API接口文档**: `RECOMMENDATION_API.md` | |
| 196 | +- **Debug指南**: `DEBUG_GUIDE.md` | |
| 197 | +- **配置说明**: `UPDATE_CONFIG_GUIDE.md` | ... | ... |
| ... | ... | @@ -0,0 +1,366 @@ |
| 1 | +# 推荐系统离线任务改进总结 | |
| 2 | + | |
| 3 | +**日期**: 2024-10-17 | |
| 4 | +**改进内容**: 前置任务优化、文档整理、算法增强 | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## ✅ 完成的改进 | |
| 9 | + | |
| 10 | +### 1. 前置任务:商品属性获取 | |
| 11 | + | |
| 12 | +**问题**: 多个脚本都需要获取ID->名称映射,导致重复查询数据库,效率低下。 | |
| 13 | + | |
| 14 | +**解决方案**: | |
| 15 | +- ✅ 创建 `fetch_item_attributes.py` 作为前置任务 | |
| 16 | +- ✅ 一次性从数据库获取所有ID->名称映射 | |
| 17 | +- ✅ 保存到本地JSON文件 (`item_attributes_mappings.json`) | |
| 18 | +- ✅ 后续任务直接加载本地文件,避免重复查询 | |
| 19 | + | |
| 20 | +**新增文件**: | |
| 21 | +``` | |
| 22 | +offline_tasks/scripts/fetch_item_attributes.py | |
| 23 | +offline_tasks/output/item_attributes_mappings.json | |
| 24 | +offline_tasks/output/item_attributes_stats.txt | |
| 25 | +``` | |
| 26 | + | |
| 27 | +**修改文件**: | |
| 28 | +``` | |
| 29 | +offline_tasks/scripts/debug_utils.py | |
| 30 | + - 新增 load_name_mappings_from_file() 函数 | |
| 31 | + - fetch_name_mappings() 标记为已弃用 | |
| 32 | + | |
| 33 | +offline_tasks/scripts/add_names_to_swing.py | |
| 34 | + - 改用 load_name_mappings_from_file() | |
| 35 | + - 移除数据库连接代码 | |
| 36 | + | |
| 37 | +offline_tasks/scripts/i2i_swing.py | |
| 38 | + - 改用 load_name_mappings_from_file() | |
| 39 | + - Debug模式加载本地映射 | |
| 40 | + | |
| 41 | +offline_tasks/run_all.py | |
| 42 | + - 添加前置任务1: fetch_item_attributes | |
| 43 | + - 添加前置任务2: generate_session | |
| 44 | + - 调整任务执行顺序 | |
| 45 | +``` | |
| 46 | + | |
| 47 | +**效果**: | |
| 48 | +- ⚡ 大幅减少数据库查询次数 | |
| 49 | +- 🚀 提升整体任务执行速度 | |
| 50 | +- 💾 统一的映射数据源,避免不一致 | |
| 51 | + | |
| 52 | +--- | |
| 53 | + | |
| 54 | +### 2. Session文件生成前置化 | |
| 55 | + | |
| 56 | +**改进**: 将session文件生成作为独立的前置任务 | |
| 57 | + | |
| 58 | +**新增任务流程**: | |
| 59 | +``` | |
| 60 | +run_all.py 执行顺序: | |
| 61 | +1. fetch_item_attributes.py → 获取商品属性 | |
| 62 | +2. generate_session.py → 生成用户session文件 | |
| 63 | +3. i2i_swing.py → Swing算法 | |
| 64 | +4. i2i_session_w2v.py → Session W2V | |
| 65 | +5. i2i_deepwalk.py → DeepWalk | |
| 66 | +6. i2i_content_similar.py → 内容相似度 | |
| 67 | +7. interest_aggregation.py → 兴趣聚合 | |
| 68 | +``` | |
| 69 | + | |
| 70 | +**好处**: | |
| 71 | +- 📦 Session文件可复用于多个任务 | |
| 72 | +- 🔄 C++版Swing算法可直接使用session文件 | |
| 73 | +- 🎯 数据准备与算法执行解耦 | |
| 74 | + | |
| 75 | +--- | |
| 76 | + | |
| 77 | +### 3. Swing算法增强:支持日期维度 | |
| 78 | + | |
| 79 | +**功能**: i2i_swing.py 现在支持 uid+日期 作为额外的session维度 | |
| 80 | + | |
| 81 | +**实现方式**: | |
| 82 | +```python | |
| 83 | +# 数据duplicate: | |
| 84 | +# 1. 原始数据:uid -> items | |
| 85 | +# 2. 日期数据:uid_YYYYMMDD -> items | |
| 86 | +# 3. 合并后输入Swing算法 | |
| 87 | + | |
| 88 | +# 新增参数 | |
| 89 | +--use_daily_session # 启用日期维度(默认开启) | |
| 90 | +--no_daily_session # 禁用日期维度 | |
| 91 | +``` | |
| 92 | + | |
| 93 | +**示例**: | |
| 94 | +```bash | |
| 95 | +# 使用日期维度(默认) | |
| 96 | +python3 scripts/i2i_swing.py --lookback_days 730 --debug | |
| 97 | + | |
| 98 | +# 禁用日期维度 | |
| 99 | +python3 scripts/i2i_swing.py --lookback_days 730 --no_daily_session | |
| 100 | +``` | |
| 101 | + | |
| 102 | +**效果**: | |
| 103 | +- 📈 同时考虑用户整体行为和单日行为 | |
| 104 | +- 🎯 更精准的物品相似度计算 | |
| 105 | +- 🔍 捕获用户短期和长期兴趣 | |
| 106 | + | |
| 107 | +**原理**: | |
| 108 | +``` | |
| 109 | +原始数据: | |
| 110 | + user_A -> [item1, item2, item3] (跨多天的行为) | |
| 111 | + | |
| 112 | +Duplicate后: | |
| 113 | + user_A -> [item1, item2, item3] # 原始session | |
| 114 | + user_A_20241015 -> [item1] # 10月15日session | |
| 115 | + user_A_20241016 -> [item2, item3] # 10月16日session | |
| 116 | + | |
| 117 | +算法同时考虑: | |
| 118 | +- 用户整体偏好(跨时间) | |
| 119 | +- 用户单日集中行为(同一天购买的商品更相关) | |
| 120 | +``` | |
| 121 | + | |
| 122 | +--- | |
| 123 | + | |
| 124 | +### 4. 文档整理与规范化 | |
| 125 | + | |
| 126 | +**问题**: 文档散乱,临时记录太多,命名不规范。 | |
| 127 | + | |
| 128 | +**解决方案**: | |
| 129 | + | |
| 130 | +#### 4.1 创建统一文档中心 | |
| 131 | +``` | |
| 132 | +offline_tasks/doc/ | |
| 133 | +├── README.md # 文档索引 | |
| 134 | +├── 从这里开始.md | |
| 135 | +├── 快速开始.md | |
| 136 | +├── Swing算法使用指南.md | |
| 137 | +├── 运行脚本指南.md | |
| 138 | +├── 调试指南.md | |
| 139 | +├── 离线索引数据规范.md | |
| 140 | +├── Redis数据规范.md | |
| 141 | +├── 完整索引列表.md | |
| 142 | +├── 数据库配置说明.md | |
| 143 | +├── 故障排查指南.md | |
| 144 | +├── 更新日志.md | |
| 145 | +└── Swing实现总结.md | |
| 146 | +``` | |
| 147 | + | |
| 148 | +#### 4.2 删除临时文档 | |
| 149 | +``` | |
| 150 | +已删除的临时文档(offline_tasks/): | |
| 151 | +- MEMORY_MONITORING_UPDATE.md | |
| 152 | +- LATEST_UPDATES.md | |
| 153 | +- CONTENT_SIMILARITY_UPDATE.md | |
| 154 | +- QUICKSTART_NEW.md | |
| 155 | +- CHANGES_SUMMARY.md | |
| 156 | +- B2B_LOW_FREQUENCY_OPTIMIZATION.md | |
| 157 | +- FIX_NAME_MAPPING.md | |
| 158 | +- QUICK_DEBUG_SUMMARY.md | |
| 159 | +- UPDATE_CONFIG_GUIDE.md | |
| 160 | +- FINAL_UPDATE.md | |
| 161 | +- FINAL_SUMMARY.md | |
| 162 | +- CURRENT_STATUS.md | |
| 163 | +- DELIVERY.md | |
| 164 | +- PROJECT_SUMMARY.md | |
| 165 | +- STRUCTURE.md | |
| 166 | +- FIELD_MAPPING.md | |
| 167 | + | |
| 168 | +已删除的临时文档(根目录): | |
| 169 | +- DEBUG_IMPLEMENTATION_SUMMARY.md | |
| 170 | +- CONFIG_CHANGES_SUMMARY.md | |
| 171 | +- OFFLINE_TASKS_SUMMARY.md | |
| 172 | +- OFFLINE_TASKS_README.md | |
| 173 | +- tables_structure.md | |
| 174 | +``` | |
| 175 | + | |
| 176 | +#### 4.3 文档命名规范 | |
| 177 | +- ✅ 所有重要文档使用中文名 | |
| 178 | +- ✅ 统一放在 `doc/` 目录 | |
| 179 | +- ✅ 创建 `doc/README.md` 索引 | |
| 180 | +- ✅ collaboration目录下的文档同步规范 | |
| 181 | + | |
| 182 | +--- | |
| 183 | + | |
| 184 | +## 📊 改进效果对比 | |
| 185 | + | |
| 186 | +### 数据库查询优化 | |
| 187 | + | |
| 188 | +**改进前**: | |
| 189 | +``` | |
| 190 | +每次运行任务: | |
| 191 | + 1. i2i_swing.py → 查询商品名称 | |
| 192 | + 2. add_names_to_swing → 查询商品名称 | |
| 193 | + 3. 其他debug任务 → 重复查询 | |
| 194 | + | |
| 195 | +总查询次数: 5-10次(取决于任务数) | |
| 196 | +``` | |
| 197 | + | |
| 198 | +**改进后**: | |
| 199 | +``` | |
| 200 | +每次运行任务: | |
| 201 | + 1. fetch_item_attributes.py → 查询1次,保存文件 | |
| 202 | + 2. 所有后续任务 → 加载本地文件 | |
| 203 | + | |
| 204 | +总查询次数: 1次 | |
| 205 | +``` | |
| 206 | + | |
| 207 | +**性能提升**: 减少 80-90% 的数据库查询 | |
| 208 | + | |
| 209 | +### Session文件生成 | |
| 210 | + | |
| 211 | +**改进前**: | |
| 212 | +``` | |
| 213 | +- i2i_swing.py 内部查询数据库 | |
| 214 | +- C++ Swing需要手动准备数据 | |
| 215 | +``` | |
| 216 | + | |
| 217 | +**改进后**: | |
| 218 | +``` | |
| 219 | +- generate_session.py 统一生成 | |
| 220 | +- 支持两种格式(标准/C++) | |
| 221 | +- 所有算法可复用 | |
| 222 | +``` | |
| 223 | + | |
| 224 | +### 文档管理 | |
| 225 | + | |
| 226 | +**改进前**: | |
| 227 | +- 30+ 散乱的Markdown文件 | |
| 228 | +- 无明确分类和索引 | |
| 229 | +- 大量临时记录 | |
| 230 | + | |
| 231 | +**改进后**: | |
| 232 | +- 13个核心文档 | |
| 233 | +- 统一在 doc/ 目录 | |
| 234 | +- 清晰的索引和分类 | |
| 235 | + | |
| 236 | +--- | |
| 237 | + | |
| 238 | +## 🚀 使用指南 | |
| 239 | + | |
| 240 | +### 运行完整流程 | |
| 241 | + | |
| 242 | +```bash | |
| 243 | +cd /home/tw/recommendation/offline_tasks | |
| 244 | + | |
| 245 | +# 方式1: 运行全部任务(推荐) | |
| 246 | +python3 run_all.py --debug | |
| 247 | + | |
| 248 | +# 方式2: 分步运行 | |
| 249 | +# 步骤1: 获取商品属性 | |
| 250 | +python3 scripts/fetch_item_attributes.py | |
| 251 | + | |
| 252 | +# 步骤2: 生成session文件 | |
| 253 | +python3 scripts/generate_session.py --lookback_days 730 | |
| 254 | + | |
| 255 | +# 步骤3: 运行Swing算法(启用日期维度) | |
| 256 | +python3 scripts/i2i_swing.py --lookback_days 730 --use_daily_session --debug | |
| 257 | +``` | |
| 258 | + | |
| 259 | +### C++ Swing算法 | |
| 260 | + | |
| 261 | +```bash | |
| 262 | +cd /home/tw/recommendation/collaboration | |
| 263 | + | |
| 264 | +# session文件自动生成后,运行Swing | |
| 265 | +bash run.sh | |
| 266 | +``` | |
| 267 | + | |
| 268 | +### 查看文档 | |
| 269 | + | |
| 270 | +```bash | |
| 271 | +cd /home/tw/recommendation/offline_tasks/doc | |
| 272 | + | |
| 273 | +# 查看文档索引 | |
| 274 | +cat README.md | |
| 275 | + | |
| 276 | +# 快速开始 | |
| 277 | +cat 快速开始.md | |
| 278 | + | |
| 279 | +# 详细使用指南 | |
| 280 | +cat Swing算法使用指南.md | |
| 281 | +``` | |
| 282 | + | |
| 283 | +--- | |
| 284 | + | |
| 285 | +## 📁 文件结构 | |
| 286 | + | |
| 287 | +``` | |
| 288 | +recommendation/ | |
| 289 | +├── offline_tasks/ | |
| 290 | +│ ├── scripts/ | |
| 291 | +│ │ ├── fetch_item_attributes.py # 新增:前置任务 | |
| 292 | +│ │ ├── generate_session.py # 已有:session生成 | |
| 293 | +│ │ ├── add_names_to_swing.py # 修改:使用本地映射 | |
| 294 | +│ │ ├── i2i_swing.py # 修改:支持日期维度 | |
| 295 | +│ │ └── debug_utils.py # 修改:添加加载函数 | |
| 296 | +│ ├── doc/ # 新增:文档中心 | |
| 297 | +│ │ ├── README.md | |
| 298 | +│ │ ├── 快速开始.md | |
| 299 | +│ │ ├── Swing算法使用指南.md | |
| 300 | +│ │ └── ... | |
| 301 | +│ ├── output/ | |
| 302 | +│ │ ├── item_attributes_mappings.json # 新增:映射文件 | |
| 303 | +│ │ ├── item_attributes_stats.txt # 新增:统计信息 | |
| 304 | +│ │ └── session.txt.YYYYMMDD # session文件 | |
| 305 | +│ ├── run_all.py # 修改:添加前置任务 | |
| 306 | +│ └── README.md | |
| 307 | +└── collaboration/ | |
| 308 | + ├── run.sh # 已修改:适配session | |
| 309 | + ├── Swing快速开始.md # 重命名 | |
| 310 | + └── ... | |
| 311 | +``` | |
| 312 | + | |
| 313 | +--- | |
| 314 | + | |
| 315 | +## 🎯 核心改进点总结 | |
| 316 | + | |
| 317 | +1. **✅ 性能优化**: 减少80-90%的数据库查询 | |
| 318 | +2. **✅ 架构优化**: 前置任务解耦,数据准备与算法分离 | |
| 319 | +3. **✅ 功能增强**: Swing算法支持日期维度 | |
| 320 | +4. **✅ 文档规范**: 统一管理,中文命名,清晰索引 | |
| 321 | +5. **✅ 代码质量**: 无Linter错误,统一编码规范 | |
| 322 | + | |
| 323 | +--- | |
| 324 | + | |
| 325 | +## 📝 后续建议 | |
| 326 | + | |
| 327 | +### 短期优化 | |
| 328 | + | |
| 329 | +1. **监控与告警** | |
| 330 | + - 添加任务执行时间监控 | |
| 331 | + - 映射文件过期检测 | |
| 332 | + - 自动重新获取机制 | |
| 333 | + | |
| 334 | +2. **性能优化** | |
| 335 | + - Session文件增量更新 | |
| 336 | + - 映射文件分批加载 | |
| 337 | + - 并行任务执行 | |
| 338 | + | |
| 339 | +### 长期规划 | |
| 340 | + | |
| 341 | +1. **分布式支持** | |
| 342 | + - 多机协同计算 | |
| 343 | + - 数据分片处理 | |
| 344 | + | |
| 345 | +2. **实时更新** | |
| 346 | + - 在线学习机制 | |
| 347 | + - 增量索引更新 | |
| 348 | + | |
| 349 | +3. **A/B测试框架** | |
| 350 | + - 多版本算法对比 | |
| 351 | + - 效果评估系统 | |
| 352 | + | |
| 353 | +--- | |
| 354 | + | |
| 355 | +## 📞 相关文档 | |
| 356 | + | |
| 357 | +- **快速开始**: [doc/快速开始.md](./快速开始.md) | |
| 358 | +- **详细指南**: [doc/Swing算法使用指南.md](./Swing算法使用指南.md) | |
| 359 | +- **故障排查**: [doc/故障排查指南.md](./故障排查指南.md) | |
| 360 | +- **文档索引**: [doc/README.md](./README.md) | |
| 361 | + | |
| 362 | +--- | |
| 363 | + | |
| 364 | +**改进完成时间**: 2024-10-17 | |
| 365 | +**状态**: ✅ 已完成并测试通过 | |
| 366 | + | ... | ... |
| ... | ... | @@ -0,0 +1,332 @@ |
| 1 | +# Debug模式使用指南 | |
| 2 | + | |
| 3 | +## 🐛 Debug功能概述 | |
| 4 | + | |
| 5 | +Debug模式为所有离线任务提供: | |
| 6 | +1. **详细的DEBUG级别日志** - 显示数据流向、统计信息、处理进度 | |
| 7 | +2. **明文索引文件** - ID后面带上对应的名称,方便检查效果 | |
| 8 | +3. **数据采样展示** - 关键步骤的示例数据 | |
| 9 | +4. **性能统计** - 每个步骤的耗时和资源使用 | |
| 10 | + | |
| 11 | +## 🚀 快速开始 | |
| 12 | + | |
| 13 | +### 1. 运行单个脚本(Debug模式) | |
| 14 | + | |
| 15 | +```bash | |
| 16 | +cd /home/tw/recommendation/offline_tasks | |
| 17 | + | |
| 18 | +# Swing算法 - Debug模式 | |
| 19 | +python3 scripts/i2i_swing.py --lookback_days 7 --top_n 10 --debug | |
| 20 | + | |
| 21 | +# 兴趣聚合 - Debug模式 | |
| 22 | +python3 scripts/interest_aggregation.py --lookback_days 7 --top_n 100 --debug | |
| 23 | + | |
| 24 | +# 内容相似 - Debug模式 | |
| 25 | +python3 scripts/i2i_content_similar.py --top_n 10 --debug | |
| 26 | +``` | |
| 27 | + | |
| 28 | +### 2. 运行所有任务(Debug模式) | |
| 29 | + | |
| 30 | +```bash | |
| 31 | +# 使用debug参数运行所有任务 | |
| 32 | +python3 run_all.py --lookback_days 7 --top_n 10 --debug | |
| 33 | +``` | |
| 34 | + | |
| 35 | +## 📊 Debug输出说明 | |
| 36 | + | |
| 37 | +### A. 日志输出 | |
| 38 | + | |
| 39 | +Debug模式下,日志会输出到两个地方: | |
| 40 | +1. **控制台** - 实时查看进度 | |
| 41 | +2. **Debug日志文件** - 完整保存 | |
| 42 | + | |
| 43 | +日志文件位置: | |
| 44 | +``` | |
| 45 | +offline_tasks/logs/debug/i2i_swing_20251016_193000.log | |
| 46 | +offline_tasks/logs/debug/interest_aggregation_20251016_193500.log | |
| 47 | +... | |
| 48 | +``` | |
| 49 | + | |
| 50 | +### B. 日志内容示例 | |
| 51 | + | |
| 52 | +``` | |
| 53 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - ============================================================ | |
| 54 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - 算法参数: | |
| 55 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - ============================================================ | |
| 56 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - alpha: 0.5 | |
| 57 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - top_n: 10 | |
| 58 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - lookback_days: 7 | |
| 59 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - debug: True | |
| 60 | +2025-10-16 19:30:00 - i2i_swing - DEBUG - ============================================================ | |
| 61 | + | |
| 62 | +2025-10-16 19:30:05 - i2i_swing - INFO - 获取到 15234 条记录 | |
| 63 | + | |
| 64 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - ============================================================ | |
| 65 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - 用户行为数据 信息: | |
| 66 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - ============================================================ | |
| 67 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - 总行数: 15234 | |
| 68 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - 总列数: 5 | |
| 69 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - 列名: ['user_id', 'item_id', 'event_type', 'create_time', 'item_name'] | |
| 70 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - | |
| 71 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - 数据类型: | |
| 72 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - user_id: object | |
| 73 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - item_id: int64 | |
| 74 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - event_type: object | |
| 75 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - create_time: datetime64[ns] | |
| 76 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - item_name: object | |
| 77 | + | |
| 78 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - 行为类型分布: | |
| 79 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - addToCart: 8520 (55.93%) | |
| 80 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - contactFactory: 3456 (22.68%) | |
| 81 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - purchase: 2134 (14.01%) | |
| 82 | +2025-10-16 19:30:05 - i2i_swing - DEBUG - addToPool: 1124 (7.38%) | |
| 83 | + | |
| 84 | +2025-10-16 19:30:10 - i2i_swing - INFO - 总用户数: 3456, 总商品数: 2345 | |
| 85 | +2025-10-16 19:30:15 - i2i_swing - DEBUG - 已处理 50/2345 个商品 (2.1%) | |
| 86 | +2025-10-16 19:30:20 - i2i_swing - DEBUG - 已处理 100/2345 个商品 (4.3%) | |
| 87 | +... | |
| 88 | +``` | |
| 89 | + | |
| 90 | +### C. 明文索引文件 | |
| 91 | + | |
| 92 | +Debug模式下,每个索引文件都会生成对应的明文文件: | |
| 93 | + | |
| 94 | +**原始索引文件** (`output/i2i_swing_20251016.txt`): | |
| 95 | +``` | |
| 96 | +12345 香蕉干 67890:0.8567,11223:0.7234,44556:0.6891 | |
| 97 | +67890 芒果干 12345:0.8567,22334:0.7123,55667:0.6543 | |
| 98 | +``` | |
| 99 | + | |
| 100 | +**明文索引文件** (`output/debug/i2i_swing_20251016_readable.txt`): | |
| 101 | +``` | |
| 102 | +================================================================================ | |
| 103 | +明文索引文件 | |
| 104 | +生成时间: 2025-10-16 19:35:00 | |
| 105 | +描述: Swing算法 i2i相似度推荐 (alpha=0.5, lookback_days=7) | |
| 106 | +总索引数: 2345 | |
| 107 | +================================================================================ | |
| 108 | + | |
| 109 | +[1] i2i:swing:12345(香蕉干) | |
| 110 | +-------------------------------------------------------------------------------- | |
| 111 | + 1. ID:67890(芒果干) - Score:0.8567 | |
| 112 | + 2. ID:11223(菠萝干) - Score:0.7234 | |
| 113 | + 3. ID:44556(苹果干) - Score:0.6891 | |
| 114 | + 4. ID:22334(木瓜干) - Score:0.6234 | |
| 115 | + 5. ID:55667(草莓干) - Score:0.5891 | |
| 116 | + | |
| 117 | +[2] i2i:swing:67890(芒果干) | |
| 118 | +-------------------------------------------------------------------------------- | |
| 119 | + 1. ID:12345(香蕉干) - Score:0.8567 | |
| 120 | + 2. ID:22334(木瓜干) - Score:0.7123 | |
| 121 | + 3. ID:55667(草莓干) - Score:0.6543 | |
| 122 | + 4. ID:11223(菠萝干) - Score:0.6234 | |
| 123 | + 5. ID:44556(苹果干) - Score:0.5891 | |
| 124 | + | |
| 125 | +... | |
| 126 | + | |
| 127 | +================================================================================ | |
| 128 | +已输出 50/2345 个索引 | |
| 129 | +================================================================================ | |
| 130 | +``` | |
| 131 | + | |
| 132 | +## 📁 文件结构 | |
| 133 | + | |
| 134 | +Debug模式下的文件组织: | |
| 135 | + | |
| 136 | +``` | |
| 137 | +offline_tasks/ | |
| 138 | +├── output/ | |
| 139 | +│ ├── i2i_swing_20251016.txt # 原始索引文件 | |
| 140 | +│ ├── interest_aggregation_hot_20251016.txt | |
| 141 | +│ └── debug/ # Debug明文文件目录 | |
| 142 | +│ ├── i2i_swing_20251016_readable.txt # 明文索引 | |
| 143 | +│ ├── interest_aggregation_hot_20251016_readable.txt | |
| 144 | +│ └── ... | |
| 145 | +└── logs/ | |
| 146 | + ├── run_all_20251016.log # 主日志 | |
| 147 | + └── debug/ # Debug详细日志目录 | |
| 148 | + ├── i2i_swing_20251016_193000.log | |
| 149 | + ├── interest_aggregation_20251016_193500.log | |
| 150 | + └── ... | |
| 151 | +``` | |
| 152 | + | |
| 153 | +## 🔍 使用场景 | |
| 154 | + | |
| 155 | +### 场景1:调试数据流程 | |
| 156 | + | |
| 157 | +```bash | |
| 158 | +# 使用小数据量+debug模式快速验证 | |
| 159 | +python3 scripts/i2i_swing.py --lookback_days 1 --top_n 5 --debug | |
| 160 | + | |
| 161 | +# 查看日志,检查: | |
| 162 | +# - 数据加载是否正确 | |
| 163 | +# - 行为类型分布是否合理 | |
| 164 | +# - 用户/商品数量是否符合预期 | |
| 165 | +``` | |
| 166 | + | |
| 167 | +### 场景2:检查推荐效果 | |
| 168 | + | |
| 169 | +```bash | |
| 170 | +# 生成明文索引文件 | |
| 171 | +python3 scripts/i2i_swing.py --lookback_days 7 --top_n 20 --debug | |
| 172 | + | |
| 173 | +# 打开明文文件查看: | |
| 174 | +cat output/debug/i2i_swing_20251016_readable.txt | less | |
| 175 | + | |
| 176 | +# 检查推荐是否合理,例如: | |
| 177 | +# - 香蕉干 -> 芒果干、菠萝干 ✓ 合理 | |
| 178 | +# - 电脑 -> 香蕉干 ✗ 不合理,需要调整参数 | |
| 179 | +``` | |
| 180 | + | |
| 181 | +### 场景3:性能调优 | |
| 182 | + | |
| 183 | +```bash | |
| 184 | +# Debug模式查看各步骤耗时 | |
| 185 | +python3 scripts/i2i_swing.py --debug 2>&1 | grep "耗时" | |
| 186 | + | |
| 187 | +# 输出示例: | |
| 188 | +# 步骤1耗时: 2.34秒 | |
| 189 | +# 步骤2耗时: 15.67秒 <- 瓶颈在这里 | |
| 190 | +# 步骤3耗时: 1.23秒 | |
| 191 | +# 总耗时: 19.24秒 | |
| 192 | +``` | |
| 193 | + | |
| 194 | +### 场景4:参数调整 | |
| 195 | + | |
| 196 | +```bash | |
| 197 | +# 测试不同alpha值的效果 | |
| 198 | +python3 scripts/i2i_swing.py --alpha 0.3 --debug > alpha_0.3.log 2>&1 | |
| 199 | +python3 scripts/i2i_swing.py --alpha 0.5 --debug > alpha_0.5.log 2>&1 | |
| 200 | +python3 scripts/i2i_swing.py --alpha 0.7 --debug > alpha_0.7.log 2>&1 | |
| 201 | + | |
| 202 | +# 对比明文文件,选择最佳参数 | |
| 203 | +diff output/debug/i2i_swing_*_readable.txt | |
| 204 | +``` | |
| 205 | + | |
| 206 | +## 💡 最佳实践 | |
| 207 | + | |
| 208 | +### 1. 开发调试阶段 | |
| 209 | + | |
| 210 | +```bash | |
| 211 | +# 使用小数据量 + Debug模式 | |
| 212 | +python3 run_all.py --lookback_days 3 --top_n 10 --debug | |
| 213 | +``` | |
| 214 | + | |
| 215 | +- ✅ 快速验证流程 | |
| 216 | +- ✅ 详细日志便于排错 | |
| 217 | +- ✅ 明文文件检查效果 | |
| 218 | + | |
| 219 | +### 2. 参数调优阶段 | |
| 220 | + | |
| 221 | +```bash | |
| 222 | +# 中等数据量 + Debug模式 | |
| 223 | +python3 scripts/i2i_swing.py --lookback_days 30 --top_n 50 --debug | |
| 224 | +``` | |
| 225 | + | |
| 226 | +- ✅ 查看数据分布 | |
| 227 | +- ✅ 评估推荐质量 | |
| 228 | +- ✅ 调整算法参数 | |
| 229 | + | |
| 230 | +### 3. 生产运行阶段 | |
| 231 | + | |
| 232 | +```bash | |
| 233 | +# 大数据量 + 正常模式(不加--debug) | |
| 234 | +python3 run_all.py --lookback_days 730 --top_n 50 | |
| 235 | +``` | |
| 236 | + | |
| 237 | +- ✅ 高效运行 | |
| 238 | +- ✅ 只输出必要日志 | |
| 239 | +- ✅ 节省磁盘空间 | |
| 240 | + | |
| 241 | +## 🛠️ Debug工具 | |
| 242 | + | |
| 243 | +### 查看实时日志 | |
| 244 | + | |
| 245 | +```bash | |
| 246 | +# 实时查看debug日志 | |
| 247 | +tail -f logs/debug/i2i_swing_*.log | |
| 248 | + | |
| 249 | +# 只看DEBUG级别 | |
| 250 | +tail -f logs/debug/i2i_swing_*.log | grep "DEBUG" | |
| 251 | + | |
| 252 | +# 只看错误 | |
| 253 | +tail -f logs/debug/i2i_swing_*.log | grep "ERROR" | |
| 254 | +``` | |
| 255 | + | |
| 256 | +### 统计分析 | |
| 257 | + | |
| 258 | +```bash | |
| 259 | +# 统计处理的数据量 | |
| 260 | +grep "总行数" logs/debug/*.log | |
| 261 | + | |
| 262 | +# 统计生成的索引数 | |
| 263 | +grep "总索引数" output/debug/*_readable.txt | |
| 264 | + | |
| 265 | +# 查看性能统计 | |
| 266 | +grep "耗时" logs/debug/*.log | |
| 267 | +``` | |
| 268 | + | |
| 269 | +### 快速检查 | |
| 270 | + | |
| 271 | +```bash | |
| 272 | +# 检查前10个推荐 | |
| 273 | +head -50 output/debug/i2i_swing_*_readable.txt | |
| 274 | + | |
| 275 | +# 搜索特定商品的推荐 | |
| 276 | +grep "香蕉干" output/debug/i2i_swing_*_readable.txt -A 10 | |
| 277 | + | |
| 278 | +# 统计推荐数量分布 | |
| 279 | +grep "Score:" output/debug/i2i_swing_*_readable.txt | wc -l | |
| 280 | +``` | |
| 281 | + | |
| 282 | +## ⚠️ 注意事项 | |
| 283 | + | |
| 284 | +1. **磁盘空间** | |
| 285 | + - Debug日志和明文文件会占用较多空间 | |
| 286 | + - 建议定期清理:`rm -rf logs/debug/* output/debug/*` | |
| 287 | + | |
| 288 | +2. **运行时间** | |
| 289 | + - Debug模式会增加10-20%的运行时间 | |
| 290 | + - 生产环境建议关闭debug | |
| 291 | + | |
| 292 | +3. **敏感信息** | |
| 293 | + - 明文文件包含商品名称等信息 | |
| 294 | + - 注意数据安全和隐私保护 | |
| 295 | + | |
| 296 | +4. **文件编码** | |
| 297 | + - 明文文件使用UTF-8编码 | |
| 298 | + - 确保查看工具支持中文显示 | |
| 299 | + | |
| 300 | +## 📖 相关命令 | |
| 301 | + | |
| 302 | +```bash | |
| 303 | +# 查看帮助 | |
| 304 | +python3 scripts/i2i_swing.py --help | |
| 305 | +python3 run_all.py --help | |
| 306 | + | |
| 307 | +# 验证配置 | |
| 308 | +python3 -c "from config.offline_config import DEBUG_CONFIG; print(DEBUG_CONFIG)" | |
| 309 | + | |
| 310 | +# 测试debug工具 | |
| 311 | +python3 -c "from scripts.debug_utils import *; print('Debug utils loaded OK')" | |
| 312 | +``` | |
| 313 | + | |
| 314 | +## ✅ 验证Debug功能 | |
| 315 | + | |
| 316 | +```bash | |
| 317 | +# 快速测试 | |
| 318 | +cd /home/tw/recommendation/offline_tasks | |
| 319 | +python3 scripts/i2i_swing.py --lookback_days 1 --top_n 5 --debug | |
| 320 | + | |
| 321 | +# 应该看到: | |
| 322 | +# ✓ DEBUG级别日志输出 | |
| 323 | +# ✓ 创建debug日志文件 | |
| 324 | +# ✓ 生成明文索引文件 | |
| 325 | +# ✓ 显示数据统计信息 | |
| 326 | +``` | |
| 327 | + | |
| 328 | +--- | |
| 329 | + | |
| 330 | +**Debug模式**: 开发和调试的利器 | |
| 331 | +**正常模式**: 生产环境的选择 | |
| 332 | +**灵活切换**: 一个参数的事情 | ... | ... |
| ... | ... | @@ -0,0 +1,304 @@ |
| 1 | +# run.sh 运行脚本使用指南 | |
| 2 | + | |
| 3 | +## 概述 | |
| 4 | + | |
| 5 | +`run.sh` 是一个自动化运行脚本,集成了内存监控功能,确保离线任务安全高效运行。 | |
| 6 | + | |
| 7 | +## 主要特性 | |
| 8 | + | |
| 9 | +### 1. 内存监控 | |
| 10 | +- **警告阈值**: 25GB - 打印警告日志 | |
| 11 | +- **强制终止阈值**: 30GB - 自动kill进程 | |
| 12 | +- **检查频率**: 每10秒检查一次 | |
| 13 | +- **日志文件**: `logs/memory_monitor.log` | |
| 14 | + | |
| 15 | +### 2. 任务流程 | |
| 16 | +1. 清理旧进程和输出 | |
| 17 | +2. 运行调试模式(小数据量测试) | |
| 18 | +3. 运行生产模式(大数据量) | |
| 19 | +4. 加载索引到Redis | |
| 20 | + | |
| 21 | +### 3. 错误处理 | |
| 22 | +- 任何步骤失败会立即退出 | |
| 23 | +- 显示详细的退出码 | |
| 24 | +- 保存完整日志 | |
| 25 | + | |
| 26 | +## 使用方法 | |
| 27 | + | |
| 28 | +### 基本运行 | |
| 29 | +```bash | |
| 30 | +cd /home/tw/recommendation/offline_tasks | |
| 31 | +./run.sh | |
| 32 | +``` | |
| 33 | + | |
| 34 | +### 查看实时输出 | |
| 35 | +```bash | |
| 36 | +./run.sh | tee logs/run_$(date +%Y%m%d_%H%M%S).log | |
| 37 | +``` | |
| 38 | + | |
| 39 | +### 后台运行 | |
| 40 | +```bash | |
| 41 | +nohup ./run.sh > logs/run_$(date +%Y%m%d_%H%M%S).log 2>&1 & | |
| 42 | +``` | |
| 43 | + | |
| 44 | +## 内存监控说明 | |
| 45 | + | |
| 46 | +### 监控逻辑 | |
| 47 | + | |
| 48 | +```bash | |
| 49 | +check_memory() { | |
| 50 | + local pid=$1 | |
| 51 | + local threshold_warn=25 # 25GB警告 | |
| 52 | + local threshold_kill=30 # 30GB强制kill | |
| 53 | + | |
| 54 | + while 进程运行中; do | |
| 55 | + 获取内存使用 | |
| 56 | + | |
| 57 | + if 内存 >= 30GB: | |
| 58 | + 打印错误日志 | |
| 59 | + 强制终止进程 | |
| 60 | + break | |
| 61 | + elif 内存 >= 25GB: | |
| 62 | + 打印警告日志 | |
| 63 | + | |
| 64 | + sleep 10秒 | |
| 65 | + done | |
| 66 | +} | |
| 67 | +``` | |
| 68 | + | |
| 69 | +### 日志格式 | |
| 70 | + | |
| 71 | +**警告日志**: | |
| 72 | +``` | |
| 73 | +[2025-10-17 14:30:25] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB), PID=12345 | |
| 74 | +``` | |
| 75 | + | |
| 76 | +**强制终止日志**: | |
| 77 | +``` | |
| 78 | +[2025-10-17 14:35:30] ❌ 内存超限!当前使用: 31.20GB (>= 30GB), 强制终止进程 PID=12345 | |
| 79 | +``` | |
| 80 | + | |
| 81 | +### 查看监控日志 | |
| 82 | +```bash | |
| 83 | +# 实时查看 | |
| 84 | +tail -f logs/memory_monitor.log | |
| 85 | + | |
| 86 | +# 查看历史 | |
| 87 | +cat logs/memory_monitor.log | |
| 88 | +``` | |
| 89 | + | |
| 90 | +## 运行流程 | |
| 91 | + | |
| 92 | +### 步骤1: 调试模式 | |
| 93 | +```bash | |
| 94 | +python3 run_all.py --debug | |
| 95 | +``` | |
| 96 | +- 使用默认参数(见 `offline_config.py`) | |
| 97 | +- 输出保存到 `output_debug/` | |
| 98 | +- 启动内存监控 | |
| 99 | + | |
| 100 | +### 步骤2: 生产模式 | |
| 101 | +```bash | |
| 102 | +python3 run_all.py --debug | |
| 103 | +``` | |
| 104 | +- 生成完整索引 | |
| 105 | +- 输出保存到 `output/` | |
| 106 | +- 启动内存监控 | |
| 107 | + | |
| 108 | +### 步骤3: 加载Redis | |
| 109 | +```bash | |
| 110 | +python3 scripts/load_index_to_redis.py --redis-host localhost | |
| 111 | +``` | |
| 112 | +- 加载所有生成的索引 | |
| 113 | +- 包括新增的内容相似索引 | |
| 114 | + | |
| 115 | +## 输出示例 | |
| 116 | + | |
| 117 | +``` | |
| 118 | +====================================================================== | |
| 119 | +开始运行离线任务 - 2025-10-17 14:00:00 | |
| 120 | +内存监控: 警告阈值=25GB, 强制终止阈值=30GB | |
| 121 | +====================================================================== | |
| 122 | + | |
| 123 | +>>> 步骤1: 调试模式运行(小数据量) | |
| 124 | +调试任务 PID: 12345 | |
| 125 | +✓ 调试模式完成 | |
| 126 | + | |
| 127 | +>>> 步骤2: 生产模式运行(大数据量) | |
| 128 | +生产任务 PID: 12346 | |
| 129 | +[2025-10-17 14:30:25] ⚠️ 内存警告!当前使用: 26.45GB (>= 25GB), PID=12346 | |
| 130 | +✓ 生产模式完成 | |
| 131 | + | |
| 132 | +>>> 步骤3: 加载到Redis | |
| 133 | +✓ Redis加载完成 | |
| 134 | + | |
| 135 | +====================================================================== | |
| 136 | +所有任务完成 - 2025-10-17 16:30:00 | |
| 137 | +====================================================================== | |
| 138 | +``` | |
| 139 | + | |
| 140 | +## 自定义配置 | |
| 141 | + | |
| 142 | +### 修改内存阈值 | |
| 143 | + | |
| 144 | +编辑 `run.sh` 中的以下行: | |
| 145 | +```bash | |
| 146 | +local threshold_warn=25 # 修改警告阈值 | |
| 147 | +local threshold_kill=30 # 修改强制kill阈值 | |
| 148 | +``` | |
| 149 | + | |
| 150 | +### 修改检查频率 | |
| 151 | + | |
| 152 | +编辑 `run.sh` 中的以下行: | |
| 153 | +```bash | |
| 154 | +sleep 10 # 修改为其他秒数 | |
| 155 | +``` | |
| 156 | + | |
| 157 | +### 跳过调试模式 | |
| 158 | + | |
| 159 | +注释掉步骤1相关代码: | |
| 160 | +```bash | |
| 161 | +# # 3. 调试模式运行(小数据量) | |
| 162 | +# echo "" | |
| 163 | +# echo ">>> 步骤1: 调试模式运行(小数据量)" | |
| 164 | +# python3 run_all.py --debug & | |
| 165 | +# ... | |
| 166 | +``` | |
| 167 | + | |
| 168 | +### 修改Redis配置 | |
| 169 | + | |
| 170 | +修改步骤3的参数: | |
| 171 | +```bash | |
| 172 | +python3 scripts/load_index_to_redis.py \ | |
| 173 | + --redis-host your-redis-host \ | |
| 174 | + --redis-port 6379 \ | |
| 175 | + --redis-db 0 | |
| 176 | +``` | |
| 177 | + | |
| 178 | +## 故障排查 | |
| 179 | + | |
| 180 | +### 内存持续超限 | |
| 181 | + | |
| 182 | +**原因**: | |
| 183 | +- 数据量太大 | |
| 184 | +- 内存泄漏 | |
| 185 | +- 并发任务过多 | |
| 186 | + | |
| 187 | +**解决方案**: | |
| 188 | +1. 增加内存阈值(临时方案) | |
| 189 | +2. 优化代码减少内存占用 | |
| 190 | +3. 分批处理数据 | |
| 191 | +4. 使用增量更新 | |
| 192 | + | |
| 193 | +### 进程被意外终止 | |
| 194 | + | |
| 195 | +**检查日志**: | |
| 196 | +```bash | |
| 197 | +# 查看监控日志 | |
| 198 | +cat logs/memory_monitor.log | |
| 199 | + | |
| 200 | +# 查看任务日志 | |
| 201 | +ls -lht logs/ | |
| 202 | +cat logs/run_all_*.log | |
| 203 | +``` | |
| 204 | + | |
| 205 | +### 任务失败 | |
| 206 | + | |
| 207 | +**查看退出码**: | |
| 208 | +- 0: 成功 | |
| 209 | +- 1: 一般错误 | |
| 210 | +- 137: 被kill信号终止(可能是内存超限) | |
| 211 | +- 其他: 查看具体错误信息 | |
| 212 | + | |
| 213 | +## 监控建议 | |
| 214 | + | |
| 215 | +### 1. 添加系统监控 | |
| 216 | +```bash | |
| 217 | +# 安装监控工具 | |
| 218 | +apt install htop iotop | |
| 219 | + | |
| 220 | +# 实时监控 | |
| 221 | +htop -p $(pgrep -f run_all.py) | |
| 222 | +``` | |
| 223 | + | |
| 224 | +### 2. 设置告警 | |
| 225 | +```bash | |
| 226 | +# 配置邮件告警 | |
| 227 | +if [ $PROD_EXIT_CODE -ne 0 ]; then | |
| 228 | + echo "任务失败" | mail -s "离线任务告警" admin@example.com | |
| 229 | +fi | |
| 230 | +``` | |
| 231 | + | |
| 232 | +### 3. 定时任务 | |
| 233 | +```bash | |
| 234 | +# 添加到crontab | |
| 235 | +0 2 * * * /home/tw/recommendation/offline_tasks/run.sh >> /var/log/offline_tasks.log 2>&1 | |
| 236 | +``` | |
| 237 | + | |
| 238 | +## 性能优化 | |
| 239 | + | |
| 240 | +### 内存优化建议 | |
| 241 | + | |
| 242 | +1. **分批处理** | |
| 243 | + ```python | |
| 244 | + # 在代码中使用batch处理 | |
| 245 | + batch_size = 1000 | |
| 246 | + for i in range(0, len(items), batch_size): | |
| 247 | + batch = items[i:i+batch_size] | |
| 248 | + process(batch) | |
| 249 | + ``` | |
| 250 | + | |
| 251 | +2. **及时释放** | |
| 252 | + ```python | |
| 253 | + import gc | |
| 254 | + del large_object | |
| 255 | + gc.collect() | |
| 256 | + ``` | |
| 257 | + | |
| 258 | +3. **使用生成器** | |
| 259 | + ```python | |
| 260 | + def process_items(): | |
| 261 | + for item in items: | |
| 262 | + yield process(item) | |
| 263 | + ``` | |
| 264 | + | |
| 265 | +## 日志管理 | |
| 266 | + | |
| 267 | +### 日志文件 | |
| 268 | +- `logs/memory_monitor.log` - 内存监控日志 | |
| 269 | +- `logs/run_all_YYYYMMDD.log` - 任务运行日志 | |
| 270 | +- `output/` - 生成的索引文件 | |
| 271 | +- `output_debug/` - 调试模式输出 | |
| 272 | + | |
| 273 | +### 清理旧日志 | |
| 274 | +```bash | |
| 275 | +# 保留最近7天 | |
| 276 | +find logs/ -name "*.log" -mtime +7 -delete | |
| 277 | + | |
| 278 | +# 压缩旧日志 | |
| 279 | +find logs/ -name "*.log" -mtime +3 -exec gzip {} \; | |
| 280 | +``` | |
| 281 | + | |
| 282 | +## 安全注意事项 | |
| 283 | + | |
| 284 | +1. **权限**: 确保脚本有执行权限 `chmod +x run.sh` | |
| 285 | +2. **路径**: 使用绝对路径避免混淆 | |
| 286 | +3. **清理**: 脚本会清理旧进程,确保没有重要进程被误杀 | |
| 287 | +4. **备份**: 脚本会删除output目录,请提前备份重要数据 | |
| 288 | + | |
| 289 | +## 总结 | |
| 290 | + | |
| 291 | +`run.sh` 提供了: | |
| 292 | +- ✅ 自动化运行流程 | |
| 293 | +- ✅ 内存监控保护 | |
| 294 | +- ✅ 详细日志记录 | |
| 295 | +- ✅ 错误处理机制 | |
| 296 | +- ✅ 进度显示 | |
| 297 | + | |
| 298 | +建议在生产环境使用前先在测试环境验证。 | |
| 299 | + | |
| 300 | +--- | |
| 301 | + | |
| 302 | +**更新时间**: 2025-10-17 | |
| 303 | +**版本**: v2.0 | |
| 304 | + | ... | ... |
offline_tasks/run.sh
| ... | ... | @@ -29,12 +29,6 @@ check_memory() { |
| 29 | 29 | done |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | -# # 查看配置指南 | |
| 33 | -# cat UPDATE_CONFIG_GUIDE.md | |
| 34 | - | |
| 35 | -# 2. 测试连接 | |
| 36 | -# python3 test_connection.py | |
| 37 | - | |
| 38 | 32 | # 清理旧进程 |
| 39 | 33 | ps -ef|grep run_all.py | awk '{print $2}' | xargs kill -9 2>/dev/null |
| 40 | 34 | ps -ef|grep recommendation | awk '{print $2}' | xargs kill -9 2>/dev/null |
| ... | ... | @@ -50,7 +44,8 @@ echo "======================================================================" |
| 50 | 44 | |
| 51 | 45 | echo "" |
| 52 | 46 | echo ">>> run_all.py" |
| 53 | -python3 run_all.py --debug | |
| 47 | +# python3 run_all.py --lookback_days 400 --top_n 50 --debug & | |
| 48 | +python3 run_all.py --debug & | |
| 54 | 49 | PID_PROD=$! |
| 55 | 50 | echo "生产任务 PID: $PID_PROD" |
| 56 | 51 | ... | ... |
offline_tasks/run_all.py
| ... | ... | @@ -95,6 +95,35 @@ def main(): |
| 95 | 95 | success_count = 0 |
| 96 | 96 | total_count = 0 |
| 97 | 97 | |
| 98 | + # 前置任务1: 获取item基础属性 | |
| 99 | + logger.info("\n" + "="*80) | |
| 100 | + logger.info("前置任务1: 获取商品基础属性(ID->名称映射)") | |
| 101 | + logger.info("="*80) | |
| 102 | + total_count += 1 | |
| 103 | + script_args = [] | |
| 104 | + if args.debug: | |
| 105 | + script_args.append('--debug') | |
| 106 | + if run_script('fetch_item_attributes.py', script_args): | |
| 107 | + success_count += 1 | |
| 108 | + else: | |
| 109 | + logger.error("获取商品属性失败,后续任务可能无法生成可读文件") | |
| 110 | + | |
| 111 | + # 前置任务2: 生成session文件 | |
| 112 | + logger.info("\n" + "="*80) | |
| 113 | + logger.info("前置任务2: 生成用户行为Session文件") | |
| 114 | + logger.info("="*80) | |
| 115 | + total_count += 1 | |
| 116 | + script_args = [ | |
| 117 | + '--lookback_days', str(DEFAULT_LOOKBACK_DAYS), | |
| 118 | + '--format', 'both' | |
| 119 | + ] | |
| 120 | + if args.debug: | |
| 121 | + script_args.append('--debug') | |
| 122 | + if run_script('generate_session.py', script_args): | |
| 123 | + success_count += 1 | |
| 124 | + else: | |
| 125 | + logger.error("生成session文件失败") | |
| 126 | + | |
| 98 | 127 | # i2i 行为相似任务 |
| 99 | 128 | logger.info("\n" + "="*80) |
| 100 | 129 | logger.info("Task 1: Running Swing algorithm for i2i similarity") | ... | ... |
offline_tasks/scripts/add_names_to_swing.py
| ... | ... | @@ -9,9 +9,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath( |
| 9 | 9 | |
| 10 | 10 | import argparse |
| 11 | 11 | from datetime import datetime |
| 12 | -from db_service import create_db_connection | |
| 13 | -from offline_tasks.config.offline_config import DB_CONFIG | |
| 14 | -from offline_tasks.scripts.debug_utils import setup_debug_logger, fetch_name_mappings | |
| 12 | +from offline_tasks.scripts.debug_utils import setup_debug_logger, load_name_mappings_from_file | |
| 15 | 13 | |
| 16 | 14 | |
| 17 | 15 | def add_names_to_swing_result(input_file, output_file, name_mappings, logger=None, debug=False): |
| ... | ... | @@ -110,20 +108,16 @@ def main(): |
| 110 | 108 | logger.error(f"输入文件不存在: {args.input_file}") |
| 111 | 109 | return |
| 112 | 110 | |
| 113 | - # 创建数据库连接 | |
| 114 | - logger.info("连接数据库...") | |
| 115 | - engine = create_db_connection( | |
| 116 | - DB_CONFIG['host'], | |
| 117 | - DB_CONFIG['port'], | |
| 118 | - DB_CONFIG['database'], | |
| 119 | - DB_CONFIG['username'], | |
| 120 | - DB_CONFIG['password'] | |
| 121 | - ) | |
| 111 | + # 从本地文件加载名称映射 | |
| 112 | + logger.info("加载ID到名称的映射...") | |
| 113 | + name_mappings = load_name_mappings_from_file(debug=args.debug) | |
| 114 | + | |
| 115 | + if not name_mappings or not name_mappings.get('item'): | |
| 116 | + logger.error("映射文件为空或加载失败") | |
| 117 | + logger.error("请先运行: python3 scripts/fetch_item_attributes.py") | |
| 118 | + return | |
| 122 | 119 | |
| 123 | - # 获取名称映射 | |
| 124 | - logger.info("获取ID到名称的映射...") | |
| 125 | - name_mappings = fetch_name_mappings(engine, debug=args.debug) | |
| 126 | - logger.info(f"获取到 {len(name_mappings['item'])} 个商品名称") | |
| 120 | + logger.info(f"加载了 {len(name_mappings['item'])} 个商品名称") | |
| 127 | 121 | |
| 128 | 122 | # 处理文件 |
| 129 | 123 | add_names_to_swing_result( | ... | ... |
offline_tasks/scripts/debug_utils.py
| ... | ... | @@ -255,9 +255,60 @@ def format_key_with_name(key, name_mappings): |
| 255 | 255 | return ':'.join(formatted_parts) |
| 256 | 256 | |
| 257 | 257 | |
| 258 | +def load_name_mappings_from_file(mappings_file=None, debug=False): | |
| 259 | + """ | |
| 260 | + 从本地文件加载ID到名称的映射(推荐使用) | |
| 261 | + 避免重复查询数据库,提高性能 | |
| 262 | + | |
| 263 | + Args: | |
| 264 | + mappings_file: 映射文件路径(如果为None,使用默认路径) | |
| 265 | + debug: 是否输出debug信息 | |
| 266 | + | |
| 267 | + Returns: | |
| 268 | + name_mappings字典 | |
| 269 | + """ | |
| 270 | + if mappings_file is None: | |
| 271 | + # 默认路径 | |
| 272 | + base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| 273 | + mappings_file = os.path.join(base_dir, 'output', 'item_attributes_mappings.json') | |
| 274 | + | |
| 275 | + if not os.path.exists(mappings_file): | |
| 276 | + if debug: | |
| 277 | + print(f"✗ 映射文件不存在: {mappings_file}") | |
| 278 | + print(f" 请先运行 fetch_item_attributes.py 生成映射文件") | |
| 279 | + return { | |
| 280 | + 'item': {}, | |
| 281 | + 'category': {}, | |
| 282 | + 'platform': {}, | |
| 283 | + 'supplier': {}, | |
| 284 | + 'client_platform': {} | |
| 285 | + } | |
| 286 | + | |
| 287 | + try: | |
| 288 | + with open(mappings_file, 'r', encoding='utf-8') as f: | |
| 289 | + mappings = json.load(f) | |
| 290 | + | |
| 291 | + if debug: | |
| 292 | + print(f"✓ 从文件加载映射: {mappings_file}") | |
| 293 | + for key, value in mappings.items(): | |
| 294 | + print(f" {key}: {len(value)} 个映射") | |
| 295 | + | |
| 296 | + return mappings | |
| 297 | + except Exception as e: | |
| 298 | + if debug: | |
| 299 | + print(f"✗ 加载映射文件失败: {e}") | |
| 300 | + return { | |
| 301 | + 'item': {}, | |
| 302 | + 'category': {}, | |
| 303 | + 'platform': {}, | |
| 304 | + 'supplier': {}, | |
| 305 | + 'client_platform': {} | |
| 306 | + } | |
| 307 | + | |
| 308 | + | |
| 258 | 309 | def fetch_name_mappings(engine, debug=False): |
| 259 | 310 | """ |
| 260 | - 从数据库获取ID到名称的映射 | |
| 311 | + 从数据库获取ID到名称的映射(已弃用,建议使用load_name_mappings_from_file) | |
| 261 | 312 | |
| 262 | 313 | Args: |
| 263 | 314 | engine: 数据库连接 |
| ... | ... | @@ -268,6 +319,10 @@ def fetch_name_mappings(engine, debug=False): |
| 268 | 319 | """ |
| 269 | 320 | import pandas as pd |
| 270 | 321 | |
| 322 | + if debug: | |
| 323 | + print("⚠️ 警告: 使用fetch_name_mappings直接查询数据库") | |
| 324 | + print(" 建议使用load_name_mappings_from_file加载本地映射文件") | |
| 325 | + | |
| 271 | 326 | mappings = { |
| 272 | 327 | 'item': {}, |
| 273 | 328 | 'category': {}, | ... | ... |
| ... | ... | @@ -0,0 +1,179 @@ |
| 1 | +""" | |
| 2 | +获取商品基础属性(前置任务) | |
| 3 | +从数据库获取ID->名称的映射,保存到本地文件供其他任务使用 | |
| 4 | +避免每个任务重复查询数据库 | |
| 5 | +""" | |
| 6 | +import sys | |
| 7 | +import os | |
| 8 | +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | |
| 9 | + | |
| 10 | +import pandas as pd | |
| 11 | +import json | |
| 12 | +import argparse | |
| 13 | +from datetime import datetime | |
| 14 | +from db_service import create_db_connection | |
| 15 | +from offline_tasks.config.offline_config import DB_CONFIG, OUTPUT_DIR | |
| 16 | +from offline_tasks.scripts.debug_utils import setup_debug_logger | |
| 17 | + | |
| 18 | + | |
| 19 | +def fetch_and_save_mappings(engine, output_dir, logger=None, debug=False): | |
| 20 | + """ | |
| 21 | + 从数据库获取各种ID->名称映射并保存 | |
| 22 | + | |
| 23 | + Args: | |
| 24 | + engine: 数据库连接 | |
| 25 | + output_dir: 输出目录 | |
| 26 | + logger: 日志记录器 | |
| 27 | + debug: 是否开启debug模式 | |
| 28 | + | |
| 29 | + Returns: | |
| 30 | + mappings字典和输出文件路径 | |
| 31 | + """ | |
| 32 | + if logger: | |
| 33 | + logger.info("开始获取ID到名称的映射...") | |
| 34 | + | |
| 35 | + mappings = { | |
| 36 | + 'item': {}, | |
| 37 | + 'category': {}, | |
| 38 | + 'platform': {}, | |
| 39 | + 'supplier': {}, | |
| 40 | + 'client_platform': {} | |
| 41 | + } | |
| 42 | + | |
| 43 | + stats = {} | |
| 44 | + | |
| 45 | + # 1. 获取商品名称 | |
| 46 | + try: | |
| 47 | + if logger: | |
| 48 | + logger.info("获取商品名称...") | |
| 49 | + query = "SELECT id, name FROM prd_goods_sku WHERE status IN (2,4,5) LIMIT 5000000" | |
| 50 | + df = pd.read_sql(query, engine) | |
| 51 | + mappings['item'] = dict(zip(df['id'].astype(str), df['name'])) | |
| 52 | + stats['item'] = len(mappings['item']) | |
| 53 | + if logger: | |
| 54 | + logger.info(f"✓ 获取到 {stats['item']} 个商品名称") | |
| 55 | + except Exception as e: | |
| 56 | + if logger: | |
| 57 | + logger.error(f"✗ 获取商品名称失败: {e}") | |
| 58 | + stats['item'] = 0 | |
| 59 | + | |
| 60 | + # 2. 获取分类名称 | |
| 61 | + try: | |
| 62 | + if logger: | |
| 63 | + logger.info("获取分类名称...") | |
| 64 | + query = "SELECT id, name FROM prd_category LIMIT 100000" | |
| 65 | + df = pd.read_sql(query, engine) | |
| 66 | + mappings['category'] = dict(zip(df['id'].astype(str), df['name'])) | |
| 67 | + stats['category'] = len(mappings['category']) | |
| 68 | + if logger: | |
| 69 | + logger.info(f"✓ 获取到 {stats['category']} 个分类名称") | |
| 70 | + except Exception as e: | |
| 71 | + if logger: | |
| 72 | + logger.error(f"✗ 获取分类名称失败: {e}") | |
| 73 | + stats['category'] = 0 | |
| 74 | + | |
| 75 | + # 3. 获取供应商名称 | |
| 76 | + try: | |
| 77 | + if logger: | |
| 78 | + logger.info("获取供应商名称...") | |
| 79 | + query = "SELECT id, name FROM sup_supplier LIMIT 100000" | |
| 80 | + df = pd.read_sql(query, engine) | |
| 81 | + mappings['supplier'] = dict(zip(df['id'].astype(str), df['name'])) | |
| 82 | + stats['supplier'] = len(mappings['supplier']) | |
| 83 | + if logger: | |
| 84 | + logger.info(f"✓ 获取到 {stats['supplier']} 个供应商名称") | |
| 85 | + except Exception as e: | |
| 86 | + if logger: | |
| 87 | + logger.error(f"✗ 获取供应商名称失败: {e}") | |
| 88 | + stats['supplier'] = 0 | |
| 89 | + | |
| 90 | + # 4. 平台名称(硬编码) | |
| 91 | + mappings['platform'] = { | |
| 92 | + 'pc': 'PC端', | |
| 93 | + 'h5': 'H5移动端', | |
| 94 | + 'app': 'APP', | |
| 95 | + 'miniprogram': '小程序', | |
| 96 | + 'wechat': '微信' | |
| 97 | + } | |
| 98 | + stats['platform'] = len(mappings['platform']) | |
| 99 | + | |
| 100 | + # 5. 客户端平台(硬编码) | |
| 101 | + mappings['client_platform'] = { | |
| 102 | + 'iOS': 'iOS', | |
| 103 | + 'Android': 'Android', | |
| 104 | + 'Web': 'Web', | |
| 105 | + 'H5': 'H5' | |
| 106 | + } | |
| 107 | + stats['client_platform'] = len(mappings['client_platform']) | |
| 108 | + | |
| 109 | + # 保存到文件 | |
| 110 | + os.makedirs(output_dir, exist_ok=True) | |
| 111 | + output_file = os.path.join(output_dir, 'item_attributes_mappings.json') | |
| 112 | + | |
| 113 | + if logger: | |
| 114 | + logger.info(f"保存映射到: {output_file}") | |
| 115 | + | |
| 116 | + with open(output_file, 'w', encoding='utf-8') as f: | |
| 117 | + json.dump(mappings, f, ensure_ascii=False, indent=2) | |
| 118 | + | |
| 119 | + # 保存统计信息 | |
| 120 | + stats_file = os.path.join(output_dir, 'item_attributes_stats.txt') | |
| 121 | + with open(stats_file, 'w', encoding='utf-8') as f: | |
| 122 | + f.write(f"商品属性映射统计信息\n") | |
| 123 | + f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") | |
| 124 | + f.write(f"=" * 60 + "\n") | |
| 125 | + for key, count in stats.items(): | |
| 126 | + f.write(f"{key}: {count}\n") | |
| 127 | + f.write(f"=" * 60 + "\n") | |
| 128 | + f.write(f"总计: {sum(stats.values())}\n") | |
| 129 | + | |
| 130 | + if logger: | |
| 131 | + logger.info(f"统计信息已保存到: {stats_file}") | |
| 132 | + logger.info(f"总计获取 {sum(stats.values())} 个映射") | |
| 133 | + | |
| 134 | + return mappings, output_file | |
| 135 | + | |
| 136 | + | |
| 137 | +def main(): | |
| 138 | + parser = argparse.ArgumentParser(description='Fetch item attributes and save mappings') | |
| 139 | + parser.add_argument('--output_dir', type=str, default=OUTPUT_DIR, | |
| 140 | + help='Output directory for mappings file') | |
| 141 | + parser.add_argument('--debug', action='store_true', | |
| 142 | + help='Enable debug mode with detailed logging') | |
| 143 | + | |
| 144 | + args = parser.parse_args() | |
| 145 | + | |
| 146 | + # 设置日志 | |
| 147 | + logger = setup_debug_logger('fetch_item_attributes', debug=args.debug) | |
| 148 | + | |
| 149 | + logger.info("="*60) | |
| 150 | + logger.info("商品属性获取任务(前置任务)") | |
| 151 | + logger.info("="*60) | |
| 152 | + | |
| 153 | + # 创建数据库连接 | |
| 154 | + logger.info("连接数据库...") | |
| 155 | + engine = create_db_connection( | |
| 156 | + DB_CONFIG['host'], | |
| 157 | + DB_CONFIG['port'], | |
| 158 | + DB_CONFIG['database'], | |
| 159 | + DB_CONFIG['username'], | |
| 160 | + DB_CONFIG['password'] | |
| 161 | + ) | |
| 162 | + | |
| 163 | + # 获取并保存映射 | |
| 164 | + mappings, output_file = fetch_and_save_mappings( | |
| 165 | + engine, | |
| 166 | + args.output_dir, | |
| 167 | + logger=logger, | |
| 168 | + debug=args.debug | |
| 169 | + ) | |
| 170 | + | |
| 171 | + logger.info("="*60) | |
| 172 | + logger.info("✓ 商品属性获取完成!") | |
| 173 | + logger.info(f"映射文件: {output_file}") | |
| 174 | + logger.info("="*60) | |
| 175 | + | |
| 176 | + | |
| 177 | +if __name__ == '__main__': | |
| 178 | + main() | |
| 179 | + | ... | ... |
offline_tasks/scripts/i2i_swing.py
| ... | ... | @@ -20,7 +20,7 @@ from offline_tasks.config.offline_config import ( |
| 20 | 20 | ) |
| 21 | 21 | from offline_tasks.scripts.debug_utils import ( |
| 22 | 22 | setup_debug_logger, log_dataframe_info, log_dict_stats, |
| 23 | - save_readable_index, fetch_name_mappings, log_algorithm_params, | |
| 23 | + save_readable_index, load_name_mappings_from_file, log_algorithm_params, | |
| 24 | 24 | log_processing_step |
| 25 | 25 | ) |
| 26 | 26 | |
| ... | ... | @@ -51,7 +51,7 @@ def calculate_time_weight(event_time, reference_time, decay_factor=0.95, days_un |
| 51 | 51 | return weight |
| 52 | 52 | |
| 53 | 53 | |
| 54 | -def swing_algorithm(df, alpha=0.5, time_decay=True, decay_factor=0.95, logger=None, debug=False): | |
| 54 | +def swing_algorithm(df, alpha=0.5, time_decay=True, decay_factor=0.95, use_daily_session=True, logger=None, debug=False): | |
| 55 | 55 | """ |
| 56 | 56 | Swing算法实现 |
| 57 | 57 | |
| ... | ... | @@ -60,6 +60,7 @@ def swing_algorithm(df, alpha=0.5, time_decay=True, decay_factor=0.95, logger=No |
| 60 | 60 | alpha: Swing算法的alpha参数 |
| 61 | 61 | time_decay: 是否使用时间衰减 |
| 62 | 62 | decay_factor: 时间衰减因子 |
| 63 | + use_daily_session: 是否同时使用uid+日期作为session维度 | |
| 63 | 64 | logger: 日志记录器 |
| 64 | 65 | debug: 是否开启debug模式 |
| 65 | 66 | |
| ... | ... | @@ -68,7 +69,7 @@ def swing_algorithm(df, alpha=0.5, time_decay=True, decay_factor=0.95, logger=No |
| 68 | 69 | """ |
| 69 | 70 | start_time = datetime.now() |
| 70 | 71 | if logger: |
| 71 | - logger.debug(f"开始Swing算法计算,参数: alpha={alpha}, time_decay={time_decay}") | |
| 72 | + logger.debug(f"开始Swing算法计算,参数: alpha={alpha}, time_decay={time_decay}, use_daily_session={use_daily_session}") | |
| 72 | 73 | |
| 73 | 74 | # 如果使用时间衰减,计算时间权重 |
| 74 | 75 | reference_time = datetime.now() |
| ... | ... | @@ -82,6 +83,27 @@ def swing_algorithm(df, alpha=0.5, time_decay=True, decay_factor=0.95, logger=No |
| 82 | 83 | if logger and debug: |
| 83 | 84 | logger.debug(f"时间权重统计: min={df['time_weight'].min():.4f}, max={df['time_weight'].max():.4f}, avg={df['time_weight'].mean():.4f}") |
| 84 | 85 | |
| 86 | + # 如果启用daily_session,duplicate数据:添加uid+date作为新的uid | |
| 87 | + if use_daily_session and 'create_time' in df.columns: | |
| 88 | + if logger: | |
| 89 | + logger.info("启用日期维度:duplicate数据,添加uid+日期作为新的session") | |
| 90 | + | |
| 91 | + # 创建原始数据副本 | |
| 92 | + df_original = df.copy() | |
| 93 | + | |
| 94 | + # 创建uid+date版本 | |
| 95 | + df_daily = df.copy() | |
| 96 | + df_daily['date'] = pd.to_datetime(df_daily['create_time']).dt.strftime('%Y%m%d') | |
| 97 | + df_daily['user_id'] = df_daily['user_id'].astype(str) + '_' + df_daily['date'] | |
| 98 | + | |
| 99 | + # 合并两份数据 | |
| 100 | + df = pd.concat([df_original, df_daily], ignore_index=True) | |
| 101 | + | |
| 102 | + if logger: | |
| 103 | + logger.info(f"原始数据: {len(df_original)} 条") | |
| 104 | + logger.info(f"日期维度数据: {len(df_daily)} 条") | |
| 105 | + logger.info(f"合并后总数据: {len(df)} 条") | |
| 106 | + | |
| 85 | 107 | # 构建用户-物品倒排索引 |
| 86 | 108 | if logger: |
| 87 | 109 | log_processing_step(logger, "步骤1: 构建用户-物品倒排索引") |
| ... | ... | @@ -201,6 +223,10 @@ def main(): |
| 201 | 223 | help='Use time decay for behavior weights (default: False for B2B low-frequency scenarios)') |
| 202 | 224 | parser.add_argument('--decay_factor', type=float, default=0.95, |
| 203 | 225 | help='Time decay factor') |
| 226 | + parser.add_argument('--use_daily_session', action='store_true', default=True, | |
| 227 | + help='Use uid+date as additional session dimension (default: True)') | |
| 228 | + parser.add_argument('--no_daily_session', action='store_false', dest='use_daily_session', | |
| 229 | + help='Disable uid+date session dimension') | |
| 204 | 230 | parser.add_argument('--output', type=str, default=None, |
| 205 | 231 | help='Output file path') |
| 206 | 232 | parser.add_argument('--debug', action='store_true', |
| ... | ... | @@ -218,6 +244,7 @@ def main(): |
| 218 | 244 | 'lookback_days': args.lookback_days, |
| 219 | 245 | 'time_decay': args.time_decay, |
| 220 | 246 | 'decay_factor': args.decay_factor, |
| 247 | + 'use_daily_session': args.use_daily_session, | |
| 221 | 248 | 'debug': args.debug |
| 222 | 249 | }) |
| 223 | 250 | |
| ... | ... | @@ -299,6 +326,7 @@ def main(): |
| 299 | 326 | alpha=args.alpha, |
| 300 | 327 | time_decay=args.time_decay, |
| 301 | 328 | decay_factor=args.decay_factor, |
| 329 | + use_daily_session=args.use_daily_session, | |
| 302 | 330 | logger=logger, |
| 303 | 331 | debug=args.debug |
| 304 | 332 | ) |
| ... | ... | @@ -333,9 +361,9 @@ def main(): |
| 333 | 361 | if args.debug: |
| 334 | 362 | logger.info("Debug模式:生成明文索引文件...") |
| 335 | 363 | try: |
| 336 | - # 获取名称映射 | |
| 337 | - logger.debug("获取ID到名称的映射...") | |
| 338 | - name_mappings = fetch_name_mappings(engine, debug=True) | |
| 364 | + # 从本地文件加载名称映射 | |
| 365 | + logger.debug("加载ID到名称的映射...") | |
| 366 | + name_mappings = load_name_mappings_from_file(debug=True) | |
| 339 | 367 | |
| 340 | 368 | # 准备索引数据(合并已有的item_name_map) |
| 341 | 369 | # item_name_map的key已经是str类型,可以直接更新 | ... | ... |