diff --git a/offline_tasks/FIX_NAME_MAPPING.md b/offline_tasks/FIX_NAME_MAPPING.md new file mode 100644 index 0000000..a5e8f0c --- /dev/null +++ b/offline_tasks/FIX_NAME_MAPPING.md @@ -0,0 +1,186 @@ +# 商品名称映射问题修复 + +## 问题描述 + +在Debug模式生成的明文文件中,商品名称显示为"Unknown": + +``` +[7] i2i:swing:3667845 +-------------------------------------------------------------------------------- + 1. ID:3588590(Unknown) - Score:0.2857 + 2. ID:3623446(Unknown) - Score:... +``` + +## 根本原因 + +数据类型不匹配导致名称映射失败: + +1. **fetch_name_mappings()** 从数据库获取的映射,key是**字符串类型**: + ```python + mappings['item'] = dict(zip(df['id'].astype(str), df['name'])) + # 结果: {'12345': '香蕉干', '67890': '芒果干', ...} + ``` + +2. **item_name_map** 从DataFrame创建的映射,key是**整数类型**: + ```python + item_name_map = dict(zip(df['item_id'].unique(), ...)) + # 结果: {12345: '香蕉干', 67890: '芒果干', ...} + ``` + +3. **更新操作失败**: + ```python + name_mappings['item'].update(item_name_map) + # int类型的key和str类型的key不匹配,实际上没有更新成功 + ``` + +4. **查询时找不到**: + ```python + name = name_mappings.get('item', {}).get(str(item_id), 'Unknown') + # 将int的item_id转为str查询,但dict中只有int类型的key,所以返回'Unknown' + ``` + +## 修复方案 + +### 方法1:统一转为字符串(已采用) + +```python +# 修改前 +item_name_map = dict(zip(df['item_id'].unique(), df.groupby('item_id')['item_name'].first())) + +# 修改后(key转为字符串) +item_name_map = dict(zip(df['item_id'].unique().astype(str), df.groupby('item_id')['item_name'].first())) +``` + +### 方法2:update时转换(备选) + +```python +# 转换key类型后再更新 +name_mappings['item'].update({str(k): v for k, v in item_name_map.items()}) +``` + +## 验证修复 + +修复后运行debug模式: + +```bash +cd /home/tw/recommendation/offline_tasks +python3 scripts/i2i_swing.py --lookback_days 7 --top_n 10 --debug +``` + +检查明文文件: + +```bash +more output/debug/i2i_swing_*_readable.txt +``` + +应该看到: + +``` +[7] i2i:swing:3667845(商品名称) +-------------------------------------------------------------------------------- + 1. ID:3588590(香蕉干) - Score:0.2857 + 2. ID:3623446(芒果干) - Score:0.2143 +``` + +## 数据库字段确认 + +正确的查询关系: + +```sql +SELECT + pgs.id as 'sku_id', + pgs.name as '商品名称', + ss.name as '供应商名称', + pc_1.name as '一级类目', + pc_2.name as '二级类目' +FROM prd_goods_sku pgs +LEFT JOIN sup_supplier ss ON pgs.supplier_id = ss.id +LEFT JOIN prd_category pc_1 ON ... +``` + +## 其他脚本 + +这个问题也可能存在于其他脚本,需要同样修复: +- ✅ i2i_swing.py (已修复) +- ⚠️ i2i_session_w2v.py (如果有debug功能需要检查) +- ⚠️ i2i_deepwalk.py (如果有debug功能需要检查) +- ⚠️ i2i_content_similar.py (如果有debug功能需要检查) +- ⚠️ interest_aggregation.py (如果有debug功能需要检查) + +## 预防措施 + +为避免类似问题,建议: + +1. **统一数据类型约定**: + - 所有ID映射的key统一使用字符串类型 + - 在debug_utils.py中明确文档说明 + +2. **添加类型检查**: + ```python + def safe_update_mapping(target_dict, source_dict): + """安全更新映射,自动转换key类型""" + for k, v in source_dict.items(): + target_dict[str(k)] = v + ``` + +3. **添加调试日志**: + ```python + if debug: + logger.debug(f"更新前: {len(name_mappings['item'])} 个名称") + name_mappings['item'].update(item_name_map) + logger.debug(f"更新后: {len(name_mappings['item'])} 个名称") + ``` + +--- + +**状态**: ✅ 已修复 +**影响范围**: i2i_swing.py +**修复时间**: 2025-10-16 + +## 补充修复 - 主输出文件 + +问题同样存在于主输出文件中: + +``` +1070176 Unknown 2786217:0.4000 +2786217 Unknown 1070176:0.4000 +``` + +### 原因 + +主输出代码中使用整数item_id作为key查询: + +```python +for item_id, sims in result.items(): + item_name = item_name_map.get(item_id, 'Unknown') # item_id是int,但map的key是str +``` + +### 修复 + +统一转换为字符串: + +```python +for item_id, sims in result.items(): + item_name = item_name_map.get(str(item_id), 'Unknown') # 转换为字符串查询 +``` + +### 验证 + +```bash +# 重新运行 +python3 scripts/i2i_swing.py --lookback_days 7 --top_n 10 + +# 检查输出 +more output/i2i_swing_20251016.txt +``` + +应该看到: +``` +1070176 商品名称A 2786217:0.4000 +2786217 商品名称B 1070176:0.4000 +``` + +--- + +**更新时间**: 2025-10-16 20:30 +**状态**: ✅ 完全修复 diff --git a/offline_tasks/OFFLINE_INDEX_SPEC.md b/offline_tasks/OFFLINE_INDEX_SPEC.md new file mode 100644 index 0000000..ba5be7d --- /dev/null +++ b/offline_tasks/OFFLINE_INDEX_SPEC.md @@ -0,0 +1,197 @@ +# 离线索引产出规范 + +## 📋 索引任务列表 + +| 模块名称 | 任务命令 | 调度频次 | 输出数据 | 格式和示例 | +|---------|---------|---------|---------|-----------| +| **i2i_swing** | `python3 scripts/i2i_swing.py` | 每天 | `output/i2i_swing_YYYYMMDD.txt` | `item_id \t item_name \t similar_id1:score1,similar_id2:score2,...` | +| **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,...` | +| **i2i_deepwalk** | `python3 scripts/i2i_deepwalk.py` | 每天 | `output/i2i_deepwalk_YYYYMMDD.txt` | `item_id \t item_name \t similar_id1:score1,similar_id2:score2,...` | +| **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,...` | +| **interest_hot** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_hot_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | +| **interest_cart** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_cart_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | +| **interest_new** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_new_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | +| **interest_global** | `python3 scripts/interest_aggregation.py` | 每天 | `output/interest_aggregation_global_YYYYMMDD.txt` | `dimension_key \t item_id1,item_id2,item_id3,...` | + +## 📊 详细格式说明 + +### 1. i2i相似度索引 + +#### 输出格式 +``` +item_id \t item_name \t similar_id1:score1,similar_id2:score2,... +``` + +#### 示例 +``` +12345 香蕉干 67890:0.8567,11223:0.7234,44556:0.6891 +67890 芒果干 12345:0.8567,22334:0.7123,55667:0.6543 +``` + +#### 字段说明 +- `item_id`: 商品SKU ID +- `item_name`: 商品名称 +- `similar_id`: 相似商品ID +- `score`: 相似度分数(0-1之间,越大越相似) + +#### 算法差异 +| 算法 | 特点 | 适用场景 | +|------|------|---------| +| **Swing** | 基于用户共同行为,发现购买关联 | 详情页"大家都在看" | +| **Session W2V** | 基于会话序列,捕捉浏览顺序 | 详情页"看了又看" | +| **DeepWalk** | 基于图结构,发现深层关系 | 详情页"相关推荐" | +| **Content** | 基于商品属性,类目相似 | 冷启动商品推荐 | + +### 2. 兴趣点聚合索引 + +#### 输出格式 +``` +dimension_key \t item_id1,item_id2,item_id3,... +``` + +#### 示例 +``` +platform:pc 12345,67890,11223,44556,22334 +category_level2:200 67890,12345,22334,55667,11223 +platform_category2:pc_200 12345,67890,22334,11223,55667 +supplier:10001 12345,44556,22334,67890,11223 +``` + +#### 维度说明 + +**单维度(7个)** +- `platform:{platform_id}` - 业务平台(pc, h5, app等) +- `client_platform:{client}` - 客户端平台(iOS, Android, Web等) +- `supplier:{supplier_id}` - 供应商 +- `category_level1:{cat_id}` - 一级分类 +- `category_level2:{cat_id}` - 二级分类 +- `category_level3:{cat_id}` - 三级分类 +- `category_level4:{cat_id}` - 四级分类 + +**组合维度(4个)** +- `platform_client:{platform}_{client}` - 平台+客户端 +- `platform_category2:{platform}_{cat_id}` - 平台+二级分类 +- `platform_category3:{platform}_{cat_id}` - 平台+三级分类 +- `client_category2:{client}_{cat_id}` - 客户端+二级分类 + +#### 列表类型说明 + +| 类型 | 文件名 | 计算逻辑 | 适用场景 | +|------|--------|---------|---------| +| **hot** | `interest_aggregation_hot_YYYYMMDD.txt` | 最近N天的高频交互商品 | 首页"热门推荐" | +| **cart** | `interest_aggregation_cart_YYYYMMDD.txt` | 高加购率商品 | 首页"热门加购" | +| **new** | `interest_aggregation_new_YYYYMMDD.txt` | 最近上架的新品 | 首页"新品推荐" | +| **global** | `interest_aggregation_global_YYYYMMDD.txt` | 全局热门商品 | 首页"猜你喜欢" | + +## 🔄 调度建议 + +### 每日调度(数据量大,变化快) +```bash +# 每天凌晨3点执行 +0 3 * * * cd /home/tw/recommendation/offline_tasks && python3 run_all.py --lookback_days 730 --top_n 50 +``` + +### 每周调度(数据量小,变化慢) +```bash +# 每周日凌晨4点执行 +0 4 * * 0 cd /home/tw/recommendation/offline_tasks && python3 scripts/i2i_content_similar.py --top_n 50 +``` + +## 📁 文件命名规范 + +### 标准格式 +``` +{algorithm_name}_{date}.txt +``` + +### 示例 +``` +i2i_swing_20251016.txt +i2i_session_w2v_20251016.txt +interest_aggregation_hot_20251016.txt +``` + +### Debug文件(开发调试用) +``` +output/debug/{algorithm_name}_{date}_readable.txt +logs/debug/{algorithm_name}_{date}_{time}.log +``` + +## 📈 数据量估算 + +| 索引类型 | 索引数量 | 单条大小 | 总大小 | 更新频率 | +|---------|---------|---------|--------|---------| +| i2i_swing | ~50,000 | ~500B | ~25MB | 每天 | +| i2i_session_w2v | ~50,000 | ~500B | ~25MB | 每天 | +| i2i_deepwalk | ~50,000 | ~500B | ~25MB | 每天 | +| i2i_content | ~50,000 | ~500B | ~25MB | 每周 | +| interest_hot | ~10,000 | ~1KB | ~10MB | 每天 | +| interest_cart | ~10,000 | ~1KB | ~10MB | 每天 | +| interest_new | ~5,000 | ~1KB | ~5MB | 每天 | +| interest_global | ~10,000 | ~1KB | ~10MB | 每天 | +| **总计** | **~245,000** | - | **~135MB** | - | + +## 🎯 质量检查 + +### 数据完整性检查 +```bash +# 检查文件是否生成 +ls -lh output/*_$(date +%Y%m%d).txt + +# 检查行数 +wc -l output/*_$(date +%Y%m%d).txt + +# 检查格式 +head -5 output/i2i_swing_$(date +%Y%m%d).txt +``` + +### 数据质量指标 + +**i2i索引质量** +- 覆盖率:有推荐的商品数 / 总商品数 > 80% +- 推荐数量:每个商品推荐10-50个相似商品 +- 分数范围:相似度分数在0.01-1.0之间 + +**兴趣聚合质量** +- 覆盖率:有数据的维度数 / 总维度数 > 60% +- 推荐数量:每个维度推荐50-1000个商品 +- 商品去重:同一商品在列表中只出现一次 + +## 🔍 查询示例 + +### 查看特定商品的相似推荐 +```bash +# 查看商品12345的相似商品 +grep "^12345\t" output/i2i_swing_20251016.txt +``` + +### 查看特定维度的热门商品 +```bash +# 查看PC平台的热门商品 +grep "^platform:pc\t" output/interest_aggregation_hot_20251016.txt +``` + +### 统计索引数量 +```bash +# 统计各类型索引数量 +for file in output/*_20251016.txt; do + echo "$file: $(wc -l < $file) 条" +done +``` + +## ⚠️ 注意事项 + +1. **文件编码**: 所有文件使用UTF-8编码 +2. **分隔符**: 使用Tab(\t)分隔字段 +3. **商品ID**: 使用数字类型,不带引号 +4. **分数精度**: 相似度分数保留4位小数 +5. **排序规则**: 相似商品按分数降序排列 +6. **去重**: 确保推荐列表中没有重复商品 +7. **有效性**: 推荐的商品必须是在售状态 + +## 🔗 相关文档 + +- **Redis数据规范**: `REDIS_DATA_SPEC.md` +- **API接口文档**: `RECOMMENDATION_API.md` +- **Debug指南**: `DEBUG_GUIDE.md` +- **配置说明**: `UPDATE_CONFIG_GUIDE.md` diff --git a/offline_tasks/REDIS_DATA_SPEC.md b/offline_tasks/REDIS_DATA_SPEC.md new file mode 100644 index 0000000..80a2629 --- /dev/null +++ b/offline_tasks/REDIS_DATA_SPEC.md @@ -0,0 +1,304 @@ +# Redis数据灌入规范 + +## 📋 数据灌入概述 + +将离线生成的推荐索引加载到Redis,供在线系统实时查询使用。 + +## 🔑 Redis Key规范 + +### 通用规则 +``` +{namespace}:{function}:{algorithm}:{identifier} +``` + +- `namespace`: 业务命名空间(item, user, interest等) +- `function`: 功能类型(similar, feature, hot等) +- `algorithm`: 算法名称(swing, w2v, deepwalk等) +- `identifier`: 具体标识(item_id, dimension_key等) + +## 📊 数据灌入规范表 + +| 模块名称 | 源数据地址 | 格式描述 | RedisKey模板 | RedisValue格式 | TTL | +|---------|-----------|---------|-------------|---------------|-----| +| **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天 | +| **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天 | +| **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天 | +| **i2i_content** | `output/i2i_content_hybrid_YYYYMMDD.txt` | `item_id\titem_name\tsimilar_id1:score1,...` | `item:similar:content:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 30天 | +| **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天 | +| **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天 | +| **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天 | +| **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天 | + +## 📝 详细说明 + +### 1. i2i相似度索引 + +#### 源数据格式 +``` +12345 香蕉干 67890:0.8567,11223:0.7234,44556:0.6891 +``` + +#### Redis存储 + +**Key**: `item:similar:swing:12345` + +**Value** (JSON格式): +```json +[[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]] +``` + +**Value** (序列化后): +```python +import json +value = json.dumps([[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]]) +# 存储: "[[67890,0.8567],[11223,0.7234],[44556,0.6891]]" +``` + +#### 查询示例 +```python +import redis +import json + +r = redis.Redis(host='localhost', port=6379, db=0) + +# 获取商品12345的相似商品(Swing算法) +similar_items = json.loads(r.get('item:similar:swing:12345')) +# 返回: [[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]] + +# 获取Top5相似商品 +top_5 = similar_items[:5] +``` + +### 2. 兴趣点聚合索引 + +#### 源数据格式 +``` +platform:pc 12345,67890,11223,44556,22334 +category_level2:200 67890,12345,22334,55667,11223 +``` + +#### Redis存储 + +**Key**: `interest:hot:platform:pc` + +**Value** (JSON格式): +```json +[12345, 67890, 11223, 44556, 22334] +``` + +**Value** (序列化后): +```python +import json +value = json.dumps([12345, 67890, 11223, 44556, 22334]) +# 存储: "[12345,67890,11223,44556,22334]" +``` + +#### 查询示例 +```python +import redis +import json + +r = redis.Redis(host='localhost', port=6379, db=0) + +# 获取PC平台的热门商品 +hot_items = json.loads(r.get('interest:hot:platform:pc')) +# 返回: [12345, 67890, 11223, 44556, 22334] + +# 获取Top10热门商品 +top_10 = hot_items[:10] +``` + +## 🔄 数据加载流程 + +### 1. 加载i2i索引 + +```python +def load_i2i_index(file_path, algorithm_name, redis_client, expire_seconds=604800): + """ + 加载i2i相似度索引到Redis + + Args: + file_path: 索引文件路径 + algorithm_name: 算法名称(swing, w2v, deepwalk, content) + redis_client: Redis客户端 + expire_seconds: 过期时间(秒),默认7天 + """ + import json + + count = 0 + with open(file_path, 'r', encoding='utf-8') as f: + for line in f: + parts = line.strip().split('\t') + if len(parts) < 3: + continue + + item_id = parts[0] + similar_str = parts[2] # similar_id1:score1,similar_id2:score2,... + + # 解析相似商品 + similar_items = [] + for pair in similar_str.split(','): + if ':' in pair: + sim_id, score = pair.split(':') + similar_items.append([int(sim_id), float(score)]) + + # 存储到Redis + redis_key = f"item:similar:{algorithm_name}:{item_id}" + redis_value = json.dumps(similar_items) + + redis_client.set(redis_key, redis_value) + redis_client.expire(redis_key, expire_seconds) + + count += 1 + + return count +``` + +### 2. 加载兴趣聚合索引 + +```python +def load_interest_index(file_path, list_type, redis_client, expire_seconds=259200): + """ + 加载兴趣点聚合索引到Redis + + Args: + file_path: 索引文件路径 + list_type: 列表类型(hot, cart, new, global) + redis_client: Redis客户端 + expire_seconds: 过期时间(秒),默认3天 + """ + import json + + count = 0 + with open(file_path, 'r', encoding='utf-8') as f: + for line in f: + parts = line.strip().split('\t') + if len(parts) != 2: + continue + + dimension_key = parts[0] # platform:pc + item_ids_str = parts[1] # 12345,67890,11223,... + + # 解析商品ID列表 + item_ids = [int(item_id) for item_id in item_ids_str.split(',')] + + # 存储到Redis + redis_key = f"interest:{list_type}:{dimension_key}" + redis_value = json.dumps(item_ids) + + redis_client.set(redis_key, redis_value) + redis_client.expire(redis_key, expire_seconds) + + count += 1 + + return count +``` + +## 🚀 快速加载命令 + +### 加载所有索引 +```bash +cd /home/tw/recommendation/offline_tasks + +# 加载所有索引(使用今天的数据) +python3 scripts/load_index_to_redis.py --redis-host localhost --redis-port 6379 + +# 加载指定日期的索引 +python3 scripts/load_index_to_redis.py --date 20251016 --redis-host localhost + +# 只加载i2i索引 +python3 scripts/load_index_to_redis.py --load-i2i --redis-host localhost + +# 只加载兴趣聚合索引 +python3 scripts/load_index_to_redis.py --load-interest --redis-host localhost +``` + +### 验证数据 +```bash +# 连接Redis +redis-cli + +# 检查key数量 +DBSIZE + +# 查看某个商品的相似推荐 +GET item:similar:swing:12345 + +# 查看平台热门商品 +GET interest:hot:platform:pc + +# 查看所有i2i相关的key +KEYS item:similar:* + +# 查看所有interest相关的key +KEYS interest:* + +# 检查key的过期时间 +TTL item:similar:swing:12345 +``` + +## 📊 数据统计 + +### Redis内存占用估算 + +| 索引类型 | Key数量 | 单条Value大小 | 总内存 | +|---------|--------|-------------|--------| +| i2i_swing | 50,000 | ~500B | ~25MB | +| i2i_w2v | 50,000 | ~500B | ~25MB | +| i2i_deepwalk | 50,000 | ~500B | ~25MB | +| i2i_content | 50,000 | ~500B | ~25MB | +| interest_hot | 10,000 | ~1KB | ~10MB | +| interest_cart | 10,000 | ~1KB | ~10MB | +| interest_new | 5,000 | ~1KB | ~5MB | +| interest_global | 10,000 | ~1KB | ~10MB | +| **总计** | **245,000** | - | **~135MB** | + +### 过期策略 + +| 索引类型 | TTL | 原因 | +|---------|-----|------| +| i2i行为相似 | 7天 | 用户行为变化快,需要频繁更新 | +| i2i内容相似 | 30天 | 商品属性变化慢,可以保留更久 | +| 热门/加购 | 3天 | 热度变化快,需要及时更新 | +| 新品 | 3天 | 新品概念有时效性 | +| 全局热门 | 7天 | 相对稳定,可以保留更久 | + +## ⚠️ 注意事项 + +1. **原子性**: 使用Pipeline批量写入,提高性能 +2. **过期时间**: 合理设置TTL,避免过期数据 +3. **内存管理**: 定期清理过期key,监控内存使用 +4. **数据版本**: 使用日期标记,支持数据回滚 +5. **容错处理**: 加载失败时不影响线上服务 +6. **监控告警**: 监控加载成功率、Redis内存、查询延迟 + +## 🔍 监控指标 + +### 数据质量指标 +```python +# 检查加载成功率 +total_keys = redis_client.dbsize() +expected_keys = 245000 +success_rate = total_keys / expected_keys * 100 + +# 检查数据完整性 +sample_keys = [ + 'item:similar:swing:12345', + 'interest:hot:platform:pc' +] +for key in sample_keys: + if not redis_client.exists(key): + print(f"Missing key: {key}") +``` + +### 性能指标 +- 加载耗时: < 5分钟 +- 内存占用: < 200MB +- 查询延迟: < 1ms +- 成功率: > 99% + +## 🔗 相关文档 + +- **离线索引规范**: `OFFLINE_INDEX_SPEC.md` +- **API接口文档**: `RECOMMENDATION_API.md` +- **运维手册**: `OPERATIONS.md` diff --git a/offline_tasks/run.sh b/offline_tasks/run.sh index ce22bd0..38bd1e3 100755 --- a/offline_tasks/run.sh +++ b/offline_tasks/run.sh @@ -1,9 +1,19 @@ cd /home/tw/recommendation/offline_tasks -# 查看配置指南 -cat UPDATE_CONFIG_GUIDE.md +# # 查看配置指南 +# cat UPDATE_CONFIG_GUIDE.md -# 查看优化总结 -cat ../CONFIG_CHANGES_SUMMARY.md +# 2. 测试连接 +python3 test_connection.py -python3 run_all.py --lookback_days 7 --top_n 10 --debug > log.runall \ No newline at end of file +# 3. 调试模式运行(小数据量) +python3 run_all.py --lookback_days 7 --top_n 10 --debug + +mv output output_debug +mkdir output + +# # 4. 生产模式运行(大数据量) +python3 run_all.py --lookback_days 730 --top_n 50 + +# 5. 加载到Redis +python3 scripts/load_index_to_redis.py --redis-host localhost diff --git a/offline_tasks/scripts/debug_utils.py b/offline_tasks/scripts/debug_utils.py index 979f5e4..c2c2184 100644 --- a/offline_tasks/scripts/debug_utils.py +++ b/offline_tasks/scripts/debug_utils.py @@ -278,7 +278,7 @@ def fetch_name_mappings(engine, debug=False): try: # 获取商品名称 - query = "SELECT id, name FROM prd_goods_sku WHERE status IN (2,4,5) LIMIT 100000" + query = "SELECT id, name FROM prd_goods_sku WHERE status IN (2,4,5) LIMIT 5000000" df = pd.read_sql(query, engine) mappings['item'] = dict(zip(df['id'].astype(str), df['name'])) if debug: @@ -289,7 +289,7 @@ def fetch_name_mappings(engine, debug=False): try: # 获取分类名称 - query = "SELECT id, name FROM prd_category LIMIT 10000" + query = "SELECT id, name FROM prd_category LIMIT 100000" df = pd.read_sql(query, engine) mappings['category'] = dict(zip(df['id'].astype(str), df['name'])) if debug: @@ -300,7 +300,7 @@ def fetch_name_mappings(engine, debug=False): try: # 获取供应商名称 - query = "SELECT id, name FROM sup_supplier LIMIT 10000" + query = "SELECT id, name FROM sup_supplier LIMIT 100000" df = pd.read_sql(query, engine) mappings['supplier'] = dict(zip(df['id'].astype(str), df['name'])) if debug: diff --git a/offline_tasks/scripts/i2i_swing.py b/offline_tasks/scripts/i2i_swing.py index 52ad031..dbee1c9 100644 --- a/offline_tasks/scripts/i2i_swing.py +++ b/offline_tasks/scripts/i2i_swing.py @@ -303,8 +303,8 @@ def main(): debug=args.debug ) - # 创建item_id到name的映射 - item_name_map = dict(zip(df['item_id'].unique(), df.groupby('item_id')['item_name'].first())) + # 创建item_id到name的映射(key转为字符串,与name_mappings一致) + item_name_map = dict(zip(df['item_id'].unique().astype(str), df.groupby('item_id')['item_name'].first())) # 输出结果 output_file = args.output or os.path.join(OUTPUT_DIR, f'i2i_swing_{datetime.now().strftime("%Y%m%d")}.txt') @@ -313,7 +313,8 @@ def main(): output_count = 0 with open(output_file, 'w', encoding='utf-8') as f: for item_id, sims in result.items(): - item_name = item_name_map.get(item_id, 'Unknown') + # item_name_map的key是字符串,需要转换 + item_name = item_name_map.get(str(item_id), 'Unknown') # 只取前N个最相似的商品 top_sims = sims[:args.top_n] @@ -336,9 +337,13 @@ def main(): logger.debug("获取ID到名称的映射...") name_mappings = fetch_name_mappings(engine, debug=True) - # 准备索引数据(使用已有的item_name_map) + # 准备索引数据(合并已有的item_name_map) + # item_name_map的key已经是str类型,可以直接更新 name_mappings['item'].update(item_name_map) + if debug: + logger.debug(f"name_mappings['item']共有 {len(name_mappings['item'])} 个商品名称") + index_data = {} for item_id, sims in result.items(): top_sims = sims[:args.top_n] -- libgit2 0.21.2