Commit abb122be504f2bf7a95a37abf65ffc2ba5a888eb

Authored by tangwang
1 parent 0d5f0a82

fix

offline_tasks/CHANGES_SUMMARY.md 0 → 100644
@@ -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)