Commit abb122be504f2bf7a95a37abf65ffc2ba5a888eb
1 parent
0d5f0a82
fix
Showing
3 changed files
with
164 additions
and
21 deletions
Show diff stats
| @@ -0,0 +1,102 @@ | @@ -0,0 +1,102 @@ | ||
| 1 | +# 更新总结 - 2025-10-22 | ||
| 2 | + | ||
| 3 | +## 1. 修改 tag_category_similar.py 脚本 | ||
| 4 | + | ||
| 5 | +### 主要变更 | ||
| 6 | +- **SQL查询修改**:从只查询分类名称改为同时查询分类ID和名称 | ||
| 7 | + - 修改前:`GROUP_CONCAT(pc_1.name) AS '商品信息'` | ||
| 8 | + - 修改后:`GROUP_CONCAT(DISTINCT CONCAT(pc_1.id, ':', pc_1.name)) AS '商品信息'` | ||
| 9 | + | ||
| 10 | +- **数据处理修改**:解析ID和名称对,建立ID到名称的映射 | ||
| 11 | + ```python | ||
| 12 | + cat_id_to_name = {} | ||
| 13 | + for cat_pair in categories: | ||
| 14 | + if ':' in cat_pair: | ||
| 15 | + cat_id, cat_name = cat_pair.split(':', 1) | ||
| 16 | + cat_id_to_name[cat_id] = cat_name | ||
| 17 | + unique_cats.add(cat_id) | ||
| 18 | + ``` | ||
| 19 | + | ||
| 20 | +- **输出格式修改**: | ||
| 21 | + - **主输出文件**(`output/tag_category_similar_YYYYMMDD.txt`):只包含ID | ||
| 22 | + - 格式:`category_id \t similar_id1:score1,similar_id2:score2,...` | ||
| 23 | + - **Debug文件**(`output/debug/tag_category_similar_YYYYMMDD_readable.txt`):包含ID+名称 | ||
| 24 | + - 格式:`category_id:category_name \t similar_id1:similar_name1:score1,...` | ||
| 25 | + | ||
| 26 | +### 输出示例 | ||
| 27 | + | ||
| 28 | +**主文件(用于Redis加载):** | ||
| 29 | +``` | ||
| 30 | +123 456:0.8123,789:0.7654,234:0.6543 | ||
| 31 | +``` | ||
| 32 | + | ||
| 33 | +**Debug文件(便于人工查看):** | ||
| 34 | +``` | ||
| 35 | +================================================================================ | ||
| 36 | +明文索引文件 | ||
| 37 | +生成时间: 2025-10-22 HH:MM:SS | ||
| 38 | +描述: tag_category_similar (分类相似度) | ||
| 39 | +总索引数: 708 | ||
| 40 | +================================================================================ | ||
| 41 | + | ||
| 42 | +123:BB夹/一字夹 456:横夹:0.8123,789:橡皮筋/发圈:0.7654,234:抓夹:0.6543 | ||
| 43 | +``` | ||
| 44 | + | ||
| 45 | +## 2. 更新详细设计文档 | ||
| 46 | + | ||
| 47 | +### 修改位置 | ||
| 48 | +文件:`offline_tasks/doc/详细设计文档.md` | ||
| 49 | +章节:📊 数据量统计 | ||
| 50 | + | ||
| 51 | +### 变更内容 | ||
| 52 | +- 基于真实输出文件统计了实际数据量 | ||
| 53 | +- 更新了所有索引类型的准确数量和大小 | ||
| 54 | +- 添加了 `tag_category_similar` 索引统计 | ||
| 55 | +- 更新了Redis内存占用预估(从180MB更新为400MB) | ||
| 56 | + | ||
| 57 | +### 实际数据统计(2025-10-22) | ||
| 58 | + | ||
| 59 | +| 索引类型 | 索引数量 | 单条平均大小 | 总大小 | 更新频率 | | ||
| 60 | +|---------|---------|---------|--------|---------| | ||
| 61 | +| i2i_deepwalk | 48,376 | ~780B | 36MB | 每天 | | ||
| 62 | +| i2i_session_w2v | 50,990 | ~840B | 41MB | 每天 | | ||
| 63 | +| i2i_content_name | 127,720 | ~830B | 101MB | 每周 | | ||
| 64 | +| i2i_content_pic | 0 | - | 0 | 每周 | | ||
| 65 | +| i2i_item_behavior | 178,775 | ~750B | 128MB | 每天 | | ||
| 66 | +| interest_hot | 14,001 | ~520B | 6.9MB | 每天 | | ||
| 67 | +| interest_cart | 15,563 | ~670B | 10MB | 每天 | | ||
| 68 | +| interest_new | 6,463 | ~500B | 3.1MB | 每天 | | ||
| 69 | +| interest_global | 17,533 | ~660B | 11MB | 每天 | | ||
| 70 | +| tag_category_similar | 708 | ~930B | 630KB | 每周 | | ||
| 71 | +| **总计** | **~460,000** | - | **~338MB** | - | | ||
| 72 | + | ||
| 73 | +## 3. 需要执行的操作 | ||
| 74 | + | ||
| 75 | +要应用这些更改,需要重新运行脚本: | ||
| 76 | + | ||
| 77 | +```bash | ||
| 78 | +cd /home/tw/recommendation/offline_tasks | ||
| 79 | +python scripts/tag_category_similar.py --debug | ||
| 80 | +``` | ||
| 81 | + | ||
| 82 | +这将生成: | ||
| 83 | +- `output/tag_category_similar_20251022.txt` - ID格式的主文件 | ||
| 84 | +- `output/debug/tag_category_similar_20251022_readable.txt` - 可读格式的调试文件 | ||
| 85 | + | ||
| 86 | +## 4. 影响分析 | ||
| 87 | + | ||
| 88 | +### 向后兼容性 | ||
| 89 | +- ⚠️ **破坏性变更**:主输出文件格式从名称改为ID | ||
| 90 | +- 需要更新所有使用该文件的下游系统(特别是Redis加载脚本) | ||
| 91 | + | ||
| 92 | +### 优势 | ||
| 93 | +1. ✅ ID格式更稳定,不受名称变更影响 | ||
| 94 | +2. ✅ 数据更准确,避免名称重复问题 | ||
| 95 | +3. ✅ 保留可读版本便于调试和验证 | ||
| 96 | +4. ✅ 与其他索引文件格式保持一致 | ||
| 97 | + | ||
| 98 | +### 建议 | ||
| 99 | +- 更新Redis加载脚本,使其能够处理category ID | ||
| 100 | +- 在API层做ID到名称的转换(如需要) | ||
| 101 | +- 保持debug文件生成,便于问题排查 | ||
| 102 | + |
offline_tasks/doc/详细设计文档.md
| @@ -615,24 +615,27 @@ crontab -e | @@ -615,24 +615,27 @@ crontab -e | ||
| 615 | 615 | ||
| 616 | ## 📊 数据量统计 | 616 | ## 📊 数据量统计 |
| 617 | 617 | ||
| 618 | -### 索引数量估算 | 618 | +### 索引数量统计(基于真实数据) |
| 619 | 619 | ||
| 620 | -| 索引类型 | 索引数量 | 单条大小 | 总大小 | 更新频率 | | 620 | +| 索引类型 | 索引数量 | 单条平均大小 | 总大小 | 更新频率 | |
| 621 | |---------|---------|---------|--------|---------| | 621 | |---------|---------|---------|--------|---------| |
| 622 | -| i2i_swing_cpp | ~50,000 | ~400B | ~20MB | 每天 | | ||
| 623 | -| i2i_swing | ~50,000 | ~500B | ~25MB | 每天 | | ||
| 624 | -| i2i_session_w2v | ~50,000 | ~500B | ~25MB | 每天 | | ||
| 625 | -| i2i_deepwalk | ~50,000 | ~500B | ~25MB | 每天 | | ||
| 626 | -| i2i_content | ~50,000 | ~500B | ~25MB | 每周 | | ||
| 627 | -| interest_hot | ~10,000 | ~1KB | ~10MB | 每天 | | ||
| 628 | -| interest_cart | ~10,000 | ~1KB | ~10MB | 每天 | | ||
| 629 | -| interest_new | ~5,000 | ~1KB | ~5MB | 每天 | | ||
| 630 | -| interest_global | ~10,000 | ~1KB | ~10MB | 每天 | | ||
| 631 | -| **总计** | **~295,000** | - | **~155MB** | - | | 622 | +| i2i_deepwalk | 48,376 | ~780B | 36MB | 每天 | |
| 623 | +| i2i_session_w2v | 50,990 | ~840B | 41MB | 每天 | | ||
| 624 | +| i2i_content_name | 127,720 | ~830B | 101MB | 每周 | | ||
| 625 | +| i2i_content_pic | 0 | - | 0 | 每周 | | ||
| 626 | +| i2i_item_behavior | 178,775 | ~750B | 128MB | 每天 | | ||
| 627 | +| interest_hot | 14,001 | ~520B | 6.9MB | 每天 | | ||
| 628 | +| interest_cart | 15,563 | ~670B | 10MB | 每天 | | ||
| 629 | +| interest_new | 6,463 | ~500B | 3.1MB | 每天 | | ||
| 630 | +| interest_global | 17,533 | ~660B | 11MB | 每天 | | ||
| 631 | +| tag_category_similar | 708 | ~930B | 630KB | 每周 | | ||
| 632 | +| **总计** | **~460,000** | - | **~338MB** | - | | ||
| 633 | + | ||
| 634 | +> 注:统计数据基于 2025-10-22 的实际输出文件 | ||
| 632 | 635 | ||
| 633 | ### Redis内存占用 | 636 | ### Redis内存占用 |
| 634 | 637 | ||
| 635 | -加载到Redis后的内存占用约 **180MB**(包含key开销) | 638 | +加载到Redis后的内存占用约 **400MB**(包含key开销和Redis数据结构开销) |
| 636 | 639 | ||
| 637 | --- | 640 | --- |
| 638 | 641 |
offline_tasks/scripts/tag_category_similar.py
| @@ -35,7 +35,7 @@ SELECT | @@ -35,7 +35,7 @@ SELECT | ||
| 35 | sp.code AS `PO单号`, | 35 | sp.code AS `PO单号`, |
| 36 | psm.name AS `区域`, | 36 | psm.name AS `区域`, |
| 37 | bb.code AS `客户编码`, | 37 | bb.code AS `客户编码`, |
| 38 | - GROUP_CONCAT(pc_1.name) AS `商品信息`, | 38 | + GROUP_CONCAT(DISTINCT CONCAT(pc_1.id, ':', pc_1.name)) AS `商品信息`, |
| 39 | MIN(spi.order_time) AS `下单货时间` | 39 | MIN(spi.order_time) AS `下单货时间` |
| 40 | FROM sale_po sp | 40 | FROM sale_po sp |
| 41 | INNER JOIN sale_po_item spi ON sp.id = spi.po_id | 41 | INNER JOIN sale_po_item spi ON sp.id = spi.po_id |
| @@ -62,6 +62,8 @@ if args.debug: | @@ -62,6 +62,8 @@ if args.debug: | ||
| 62 | print(f"[DEBUG] 查询完成,共 {len(df)} 条订单记录") | 62 | print(f"[DEBUG] 查询完成,共 {len(df)} 条订单记录") |
| 63 | 63 | ||
| 64 | # 处理商品信息,分割并去重 | 64 | # 处理商品信息,分割并去重 |
| 65 | +# 构建ID到名称的映射 | ||
| 66 | +cat_id_to_name = {} | ||
| 65 | cooccur = defaultdict(lambda: defaultdict(int)) | 67 | cooccur = defaultdict(lambda: defaultdict(int)) |
| 66 | freq = defaultdict(int) | 68 | freq = defaultdict(int) |
| 67 | 69 | ||
| @@ -70,7 +72,15 @@ for _, row in df.iterrows(): | @@ -70,7 +72,15 @@ for _, row in df.iterrows(): | ||
| 70 | if pd.isna(row['商品信息']): | 72 | if pd.isna(row['商品信息']): |
| 71 | continue | 73 | continue |
| 72 | categories = [cat.strip() for cat in str(row['商品信息']).split(',') if cat.strip()] | 74 | categories = [cat.strip() for cat in str(row['商品信息']).split(',') if cat.strip()] |
| 73 | - unique_cats = set(categories) | 75 | + unique_cats = set() |
| 76 | + for cat_pair in categories: | ||
| 77 | + if ':' in cat_pair: | ||
| 78 | + cat_id, cat_name = cat_pair.split(':', 1) | ||
| 79 | + cat_id = cat_id.strip() | ||
| 80 | + cat_name = cat_name.strip() | ||
| 81 | + cat_id_to_name[cat_id] = cat_name | ||
| 82 | + unique_cats.add(cat_id) | ||
| 83 | + | ||
| 74 | for c1 in unique_cats: | 84 | for c1 in unique_cats: |
| 75 | freq[c1] += 1 | 85 | freq[c1] += 1 |
| 76 | for c2 in unique_cats: | 86 | for c2 in unique_cats: |
| @@ -105,22 +115,50 @@ if args.debug: | @@ -105,22 +115,50 @@ if args.debug: | ||
| 105 | # 准备输出 | 115 | # 准备输出 |
| 106 | date_str = datetime.now().strftime('%Y%m%d') | 116 | date_str = datetime.now().strftime('%Y%m%d') |
| 107 | output_dir = 'output' | 117 | output_dir = 'output' |
| 118 | +debug_dir = os.path.join(output_dir, 'debug') | ||
| 108 | os.makedirs(output_dir, exist_ok=True) | 119 | os.makedirs(output_dir, exist_ok=True) |
| 120 | +os.makedirs(debug_dir, exist_ok=True) | ||
| 109 | output_file = os.path.join(output_dir, f'tag_category_similar_{date_str}.txt') | 121 | output_file = os.path.join(output_dir, f'tag_category_similar_{date_str}.txt') |
| 122 | +debug_file = os.path.join(debug_dir, f'tag_category_similar_{date_str}_readable.txt') | ||
| 110 | 123 | ||
| 111 | -# 输出相似分类到文件 | 124 | +# 输出相似分类到文件(ID格式) |
| 112 | if args.debug: | 125 | if args.debug: |
| 113 | print(f"[DEBUG] 开始写入文件: {output_file}") | 126 | print(f"[DEBUG] 开始写入文件: {output_file}") |
| 114 | 127 | ||
| 115 | with open(output_file, 'w', encoding='utf-8') as f: | 128 | with open(output_file, 'w', encoding='utf-8') as f: |
| 116 | - for cat, sims in sorted(result.items()): | ||
| 117 | - cat_clean = clean_text_field(cat) | ||
| 118 | - # 格式: category_name \t similar_cat1:score1,similar_cat2:score2,... | ||
| 119 | - sim_str = ','.join([f'{clean_text_field(sim_cat)}:{score:.4f}' for sim_cat, score in sims]) | ||
| 120 | - f.write(f'{cat_clean}\t{sim_str}\n') | 129 | + for cat_id, sims in sorted(result.items()): |
| 130 | + # 格式: category_id \t similar_id1:score1,similar_id2:score2,... | ||
| 131 | + sim_str = ','.join([f'{sim_id}:{score:.4f}' for sim_id, score in sims]) | ||
| 132 | + f.write(f'{cat_id}\t{sim_str}\n') | ||
| 133 | + | ||
| 134 | +# 输出可读版本到debug目录(ID+名称格式) | ||
| 135 | +if args.debug: | ||
| 136 | + print(f"[DEBUG] 开始写入可读文件: {debug_file}") | ||
| 137 | + | ||
| 138 | +with open(debug_file, 'w', encoding='utf-8') as f: | ||
| 139 | + # 写入文件头信息 | ||
| 140 | + f.write('='*80 + '\n') | ||
| 141 | + f.write('明文索引文件\n') | ||
| 142 | + f.write(f'生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n') | ||
| 143 | + f.write(f'描述: tag_category_similar (分类相似度)\n') | ||
| 144 | + f.write(f'总索引数: {len(result)}\n') | ||
| 145 | + f.write('='*80 + '\n\n') | ||
| 146 | + | ||
| 147 | + for cat_id, sims in sorted(result.items()): | ||
| 148 | + cat_name = cat_id_to_name.get(cat_id, 'Unknown') | ||
| 149 | + cat_clean = clean_text_field(cat_name) | ||
| 150 | + # 格式: category_id:category_name \t similar_id1:similar_name1:score1,... | ||
| 151 | + sim_parts = [] | ||
| 152 | + for sim_id, score in sims: | ||
| 153 | + sim_name = cat_id_to_name.get(sim_id, 'Unknown') | ||
| 154 | + sim_clean = clean_text_field(sim_name) | ||
| 155 | + sim_parts.append(f'{sim_id}:{sim_clean}:{score:.4f}') | ||
| 156 | + sim_str = ','.join(sim_parts) | ||
| 157 | + f.write(f'{cat_id}:{cat_clean}\t{sim_str}\n') | ||
| 121 | 158 | ||
| 122 | print(f"✓ Tag相似度计算完成") | 159 | print(f"✓ Tag相似度计算完成") |
| 123 | print(f" - 输出文件: {output_file}") | 160 | print(f" - 输出文件: {output_file}") |
| 161 | +print(f" - 可读文件: {debug_file}") | ||
| 124 | print(f" - 分类数: {len(result)}") | 162 | print(f" - 分类数: {len(result)}") |
| 125 | if result: | 163 | if result: |
| 126 | avg_sims = sum(len(sims) for sims in result.values()) / len(result) | 164 | avg_sims = sum(len(sims) for sims in result.values()) / len(result) |