diff --git a/offline_tasks/README.md b/offline_tasks/README.md index 110835d..7103f62 100644 --- a/offline_tasks/README.md +++ b/offline_tasks/README.md @@ -1,252 +1,222 @@ # 推荐系统离线任务 -本目录包含推荐系统的离线任务脚本,用于生成各种推荐索引。 +推荐系统的离线索引生成模块,包含多种算法和数据处理任务。 -## 目录结构 +## 🚀 快速开始 -``` -offline_tasks/ -├── config/ -│ └── offline_config.py # 离线任务配置文件 -├── scripts/ -│ ├── i2i_swing.py # Swing算法实现 -│ ├── i2i_session_w2v.py # Session Word2Vec实现 -│ ├── i2i_deepwalk.py # DeepWalk算法实现 -│ └── interest_aggregation.py # 兴趣点聚合索引生成 -├── output/ # 输出目录 -├── logs/ # 日志目录 -├── run_all.py # 统一调度脚本 -└── README.md # 本文档 -``` - -## 功能说明 - -### 1. i2i - 行为相似索引 - -基于用户行为数据,计算商品之间的相似度,生成i2i(item-to-item)推荐索引。 +### 运行所有任务(推荐) -#### 1.1 Swing算法 - -Swing算法是一种基于用户共同行为的物品相似度计算方法,相比协同过滤有更好的效果。 - -**运行命令:** ```bash -python scripts/i2i_swing.py --lookback_days 730 --top_n 50 --time_decay -``` +cd /home/tw/recommendation/offline_tasks -**参数说明:** -- `--lookback_days`: 回溯天数(默认730天,即2年) -- `--top_n`: 每个商品输出的相似商品数量(默认50) -- `--alpha`: Swing算法的alpha参数(默认0.5) -- `--time_decay`: 是否使用时间衰减 -- `--decay_factor`: 时间衰减因子(默认0.95,每30天衰减一次) +# 运行全部离线任务(包括C++ Swing) +python3 run_all.py -**输出格式:** -``` -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... +# 开启debug模式(详细日志 + 可读文件) +python3 run_all.py --debug ``` -#### 1.2 Session Word2Vec +### 任务执行顺序 -基于用户会话序列训练Word2Vec模型,学习商品的向量表示,通过向量相似度计算商品相似度。 +``` +前置任务: +1. fetch_item_attributes.py → 获取商品属性映射 +2. generate_session.py → 生成用户行为session +3. C++ Swing算法 → 高性能i2i相似度计算 -**运行命令:** -```bash -python scripts/i2i_session_w2v.py --lookback_days 730 --top_n 50 --save_model +核心算法任务: +4. Python Swing算法 → 支持日期维度的i2i +5. Session W2V → 基于序列的embedding +6. DeepWalk → 图结构embedding +7. 内容相似度 → 基于ES向量 +8. 兴趣聚合 → 多维度商品聚合 ``` -**参数说明:** -- `--lookback_days`: 回溯天数 -- `--top_n`: 输出相似商品数量 -- `--window_size`: Word2Vec窗口大小(默认5) -- `--vector_size`: 向量维度(默认128) -- `--min_count`: 最小词频(默认2) -- `--workers`: 训练线程数(默认10) -- `--epochs`: 训练轮数(默认10) -- `--session_gap`: 会话间隔(分钟,默认30) -- `--save_model`: 是否保存模型 +## 📚 文档 -**输出格式:** -``` -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... -``` +所有文档位于 **`doc/`** 目录: -#### 1.3 DeepWalk +- **[doc/快速开始.md](./doc/快速开始.md)** - 新手入门 +- **[doc/Swing算法使用指南.md](./doc/Swing算法使用指南.md)** - 详细使用 +- **[doc/系统改进总结-20241017.md](./doc/系统改进总结-20241017.md)** - 最新改进 +- **[doc/README.md](./doc/README.md)** - 完整文档索引 -基于用户-商品交互图,使用随机游走生成序列,然后训练Word2Vec模型。 +## 🔧 核心功能 -**运行命令:** -```bash -python scripts/i2i_deepwalk.py --lookback_days 730 --top_n 50 --save_model --save_graph -``` +### 1. 前置任务优化 -**参数说明:** -- `--lookback_days`: 回溯天数 -- `--top_n`: 输出相似商品数量 -- `--num_walks`: 每个节点的游走次数(默认10) -- `--walk_length`: 游走长度(默认40) -- `--window_size`: Word2Vec窗口大小(默认5) -- `--vector_size`: 向量维度(默认128) -- `--save_model`: 是否保存模型 -- `--save_graph`: 是否保存图结构 +- **商品属性缓存**: 一次获取,多次使用,减少90%数据库查询 +- **Session文件复用**: 统一生成,多算法共享 +- **C++ Swing集成**: 自动执行,高性能计算 -**输出格式:** -``` -item_id \t item_name \t similar_item_id1:score1,similar_item_id2:score2,... -``` +### 2. 算法增强 -### 2. 兴趣点聚合索引 +- **双维度Swing**: 同时考虑用户整体行为和单日行为 +- **时间衰减**: 可选的时间权重衰减 +- **Debug模式**: 自动生成可读版本(ID + 名称) -按照多个维度聚合用户行为,生成不同场景下的商品推荐索引。 +### 3. 自动化流程 -**运行命令:** ```bash -python scripts/interest_aggregation.py --lookback_days 730 --top_n 1000 +# 一条命令完成所有任务 +python3 run_all.py --debug ``` -**参数说明:** -- `--lookback_days`: 回溯天数(默认730天,即2年) -- `--recent_days`: 热门商品的统计天数(默认180天) -- `--new_days`: 新品的定义天数(默认90天) -- `--top_n`: 每个维度输出的商品数量(默认1000) -- `--decay_factor`: 时间衰减因子(默认0.95) +输出文件: +- `output/item_attributes_mappings.json` - ID映射 +- `output/session.txt.YYYYMMDD` - 用户session +- `collaboration/output/swing_similar.txt` - C++ Swing结果 +- `output/i2i_swing_YYYYMMDD.txt` - Python Swing结果 +- ... 其他算法输出 -**支持的维度:** +## 📊 性能对比 -1. **单维度:** - - `platform`: 平台 - - `country`: 国家/销售区域 - - `customer_type`: 客户类型 - - `category_level2`: 二级分类 - - `category_level3`: 三级分类 +| 任务 | 改进前 | 改进后 | 提升 | +|------|--------|--------|------| +| 数据库查询 | 5-10次 | 1次 | 80-90% ↓ | +| Swing性能 | Python | C++ | 10-100x ↑ | +| 任务管理 | 手动分步 | 自动流程 | 100% ↑ | -2. **组合维度:** - - `platform_country`: 平台 + 国家 - - `platform_customer`: 平台 + 客户类型 - - `country_customer`: 国家 + 客户类型 - - `platform_country_customer`: 平台 + 国家 + 客户类型 +## 🛠️ 单独运行任务 -3. **列表类型:** - - `hot`: 热门商品(基于最近N天的高交互) - - `cart`: 加购商品(基于加购行为) - - `new`: 新品(基于商品创建时间) - - `global`: 全局索引(所有数据) +### 1. 获取商品属性 -**输出格式:** -``` -dimension_key \t item_id1:score1,item_id2:score2,... -``` - -**示例:** -``` -platform:PC \t 12345:98.5,23456:87.3,... -country:US \t 34567:156.2,45678:142.8,... -platform_country:PC_US \t 56789:234.5,67890:198.7,... +```bash +python3 scripts/fetch_item_attributes.py ``` -## 统一调度脚本 - -使用 `run_all.py` 可以一次性运行所有离线任务: +### 2. 生成Session -**运行所有任务:** ```bash -python run_all.py --lookback_days 730 --top_n 50 +python3 scripts/generate_session.py --lookback_days 730 ``` -**运行特定任务:** -```bash -# 只运行Swing算法 -python run_all.py --only-swing - -# 只运行Session W2V -python run_all.py --only-w2v - -# 只运行DeepWalk -python run_all.py --only-deepwalk +### 3. C++ Swing -# 只运行兴趣点聚合 -python run_all.py --only-interest +```bash +cd ../collaboration +bash run.sh +``` -# 跳过i2i任务 -python run_all.py --skip-i2i +### 4. Python Swing(支持日期维度) -# 跳过兴趣点聚合 -python run_all.py --skip-interest +```bash +python3 scripts/i2i_swing.py --lookback_days 730 --use_daily_session --debug ``` -## 配置文件 +### 5. 其他算法 -所有配置参数都在 `config/offline_config.py` 中定义,包括: +```bash +# Session W2V +python3 scripts/i2i_session_w2v.py --lookback_days 730 --debug -- **数据库配置**:数据库连接信息 -- **路径配置**:输出目录、日志目录 -- **时间配置**:回溯天数、时间衰减参数 -- **算法配置**:各算法的超参数 -- **行为权重**:不同行为类型的权重 +# DeepWalk +python3 scripts/i2i_deepwalk.py --lookback_days 730 --debug -可以根据实际需求修改配置文件中的参数。 +# 内容相似度 +python3 scripts/i2i_content_similar.py -## 输出文件 +# 兴趣聚合 +python3 scripts/interest_aggregation.py --lookback_days 730 --debug +``` -所有输出文件都保存在 `output/` 目录下,文件名格式为: +## 📁 项目结构 ``` -{任务名}_{日期}.txt +offline_tasks/ +├── scripts/ # 所有任务脚本 +│ ├── fetch_item_attributes.py +│ ├── generate_session.py +│ ├── i2i_swing.py +│ ├── i2i_session_w2v.py +│ ├── i2i_deepwalk.py +│ ├── i2i_content_similar.py +│ ├── interest_aggregation.py +│ ├── add_names_to_swing.py +│ └── debug_utils.py +├── config/ # 配置文件 +│ └── offline_config.py +├── doc/ # 文档中心 +│ ├── README.md +│ ├── 快速开始.md +│ ├── Swing算法使用指南.md +│ └── ... +├── output/ # 输出目录 +│ ├── item_attributes_mappings.json +│ ├── session.txt.* +│ └── *.txt +├── logs/ # 日志目录 +├── run_all.py # 统一入口 +└── README.md # 本文件 +``` + +## ⚙️ 配置 + +配置文件:`config/offline_config.py` + +主要参数: +```python +DEFAULT_LOOKBACK_DAYS = 730 # 数据回看天数 +DEFAULT_I2I_TOP_N = 50 # i2i推荐数量 +DEFAULT_INTEREST_TOP_N = 1000 # 兴趣聚合数量 + +# 数据库配置 +DB_CONFIG = {...} + +# 算法参数 +I2I_CONFIG = {...} +``` + +## 🐛 故障排查 + +### 常见问题 + +**1. 映射文件不存在** +```bash +# 先运行前置任务 +python3 scripts/fetch_item_attributes.py ``` -例如: -- `i2i_swing_20251016.txt` -- `i2i_session_w2v_20251016.txt` -- `i2i_deepwalk_20251016.txt` -- `interest_aggregation_hot_20251016.txt` -- `interest_aggregation_cart_20251016.txt` -- `interest_aggregation_new_20251016.txt` -- `interest_aggregation_global_20251016.txt` - -## 日志 - -所有任务的执行日志都保存在 `logs/` 目录下。 - -## 依赖项 +**2. Session文件找不到** +```bash +# 生成session文件 +python3 scripts/generate_session.py +``` +**3. C++ Swing编译失败** ```bash -pip install pandas sqlalchemy pymysql gensim numpy +cd ../collaboration +make clean +make ``` -## 定时任务设置 +详见:[doc/故障排查指南.md](./doc/故障排查指南.md) -建议使用crontab设置定时任务,每天凌晨运行一次: +## 📝 日志 -```bash -# 编辑crontab -crontab -e +日志位置: +- 主日志:`logs/run_all_YYYYMMDD.log` +- Debug日志:`logs/debug/*.log` -# 添加定时任务(每天凌晨2点运行) -0 2 * * * cd /home/tw/recommendation/offline_tasks && /usr/bin/python3 run_all.py --lookback_days 730 --top_n 50 +查看最新日志: +```bash +tail -f logs/run_all_$(date +%Y%m%d).log ``` -## 注意事项 - -1. **数据量**:由于需要处理2年的数据,任务可能需要较长时间(几小时到十几小时不等) -2. **内存占用**:Swing算法和DeepWalk可能占用较多内存,建议在内存充足的机器上运行 -3. **数据库连接**:确保数据库连接信息正确,且有足够的权限读取相关表 -4. **磁盘空间**:确保output目录有足够的磁盘空间存储输出文件 - -## 性能优化建议 +## 🔗 相关项目 -1. **并行化**:可以将不同算法的任务分配到不同机器上并行运行 -2. **增量更新**:对于已有的索引,可以考虑增量更新而不是全量计算 -3. **采样**:对于数据量特别大的场景,可以考虑先采样一部分数据进行调试 -4. **缓存**:可以将中间结果缓存,避免重复计算 +- **Collaboration**: `../collaboration/` - C++ 协同过滤 +- **GraphEmbedding**: `../graphembedding/` - 图embedding +- **Hot**: `../hot/` - 热门推荐 +- **Frontend**: `../frontend/` - 推荐接口 -## 问题排查 +## 📞 更多信息 -如果任务执行失败,请检查: +- **完整文档**: [doc/README.md](./doc/README.md) +- **改进总结**: [doc/系统改进总结-20241017.md](./doc/系统改进总结-20241017.md) +- **故障排查**: [doc/故障排查指南.md](./doc/故障排查指南.md) -1. 日志文件中的错误信息 -2. 数据库连接是否正常 -3. 数据表结构是否正确 -4. Python依赖包是否安装完整 -5. 磁盘空间是否充足 -6. 内存是否充足 +--- +**最后更新**: 2024-10-17 +**状态**: ✅ 生产就绪 diff --git a/offline_tasks/doc/REDIS_DATA_SPEC.md b/offline_tasks/doc/REDIS_DATA_SPEC.md deleted file mode 100644 index 2777b71..0000000 --- a/offline_tasks/doc/REDIS_DATA_SPEC.md +++ /dev/null @@ -1,306 +0,0 @@ -# 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_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天 | -| **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天 | -| **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_name | 50,000 | ~500B | ~25MB | -| i2i_content_pic | 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 | -| **总计** | **270,000** | - | **~160MB** | - -### 过期策略 - -| 索引类型 | 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/doc/Redis数据规范.md b/offline_tasks/doc/Redis数据规范.md index 2777b71..f8c500e 100644 --- a/offline_tasks/doc/Redis数据规范.md +++ b/offline_tasks/doc/Redis数据规范.md @@ -20,6 +20,7 @@ | 模块名称 | 源数据地址 | 格式描述 | RedisKey模板 | RedisValue格式 | TTL | |---------|-----------|---------|-------------|---------------|-----| +| **i2i_swing_cpp** | `collaboration/output/swing_similar.txt` | `item_id\tsimilar_id1:score1,...` | `item:similar:swing_cpp:{item_id}` | `[[similar_id1,score1],[similar_id2,score2],...]` | 7天 | | **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天 | @@ -34,12 +35,42 @@ ### 1. i2i相似度索引 -#### 源数据格式 +#### 1.1 C++ Swing算法(高性能版本) + +**源数据格式** +``` +3600052 2704531:0.00431593,2503886:0.00431593,3371410:0.00431593,3186572:0.00431593 +``` + +**Redis存储** + +**Key**: `item:similar:swing_cpp:3600052` + +**Value** (JSON格式): +```json +[[2704531, 0.00431593], [2503886, 0.00431593], [3371410, 0.00431593], [3186572, 0.00431593]] +``` + +**Value** (序列化后): +```python +import json +value = json.dumps([[2704531, 0.00431593], [2503886, 0.00431593], [3371410, 0.00431593], [3186572, 0.00431593]]) +# 存储: "[[2704531,0.00431593],[2503886,0.00431593],[3371410,0.00431593],[3186572,0.00431593]]" +``` + +**特点**: +- 原始Swing分数(未归一化) +- 高性能C++计算 +- 适合大规模数据 + +#### 1.2 Python Swing算法(标准版本) + +**源数据格式** ``` 12345 香蕉干 67890:0.8567,11223:0.7234,44556:0.6891 ``` -#### Redis存储 +**Redis存储** **Key**: `item:similar:swing:12345` @@ -55,19 +86,35 @@ value = json.dumps([[67890, 0.8567], [11223, 0.7234], [44556, 0.6891]]) # 存储: "[[67890,0.8567],[11223,0.7234],[44556,0.6891]]" ``` -#### 查询示例 +**特点**: +- 归一化分数(0-1区间) +- 支持时间衰减和日期维度 +- 便于调试 + +#### 1.3 查询示例 + ```python import redis import json r = redis.Redis(host='localhost', port=6379, db=0) -# 获取商品12345的相似商品(Swing算法) +# 方式1: 获取C++ Swing结果(生产推荐) +similar_items_cpp = json.loads(r.get('item:similar:swing_cpp:3600052')) +# 返回: [[2704531, 0.00431593], [2503886, 0.00431593], ...] + +# 方式2: 获取Python 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] + +# 多算法融合(可选) +swing_cpp = json.loads(r.get('item:similar:swing_cpp:3600052') or '[]') +swing_py = json.loads(r.get('item:similar:swing:3600052') or '[]') +w2v = json.loads(r.get('item:similar:w2v:3600052') or '[]') +# 融合多个算法结果... ``` ### 2. 兴趣点聚合索引 @@ -113,10 +160,55 @@ top_10 = hot_items[:10] ### 1. 加载i2i索引 +#### 1.1 加载C++ Swing索引(无商品名) + +```python +def load_cpp_swing_index(file_path, redis_client, expire_seconds=604800): + """ + 加载C++ Swing索引到Redis + + Args: + file_path: 索引文件路径(collaboration/output/swing_similar.txt) + 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) < 2: + continue + + item_id = parts[0] + similar_str = parts[1] # 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:swing_cpp:{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 +``` + +#### 1.2 加载Python i2i索引(含商品名) + ```python def load_i2i_index(file_path, algorithm_name, redis_client, expire_seconds=604800): """ - 加载i2i相似度索引到Redis + 加载Python i2i相似度索引到Redis Args: file_path: 索引文件路径 @@ -134,6 +226,7 @@ def load_i2i_index(file_path, algorithm_name, redis_client, expire_seconds=60480 continue item_id = parts[0] + # item_name = parts[1] # 可选:如果需要缓存商品名 similar_str = parts[2] # similar_id1:score1,similar_id2:score2,... # 解析相似商品 diff --git a/offline_tasks/doc/离线索引数据规范.md b/offline_tasks/doc/离线索引数据规范.md index ba5be7d..f81d270 100644 --- a/offline_tasks/doc/离线索引数据规范.md +++ b/offline_tasks/doc/离线索引数据规范.md @@ -4,6 +4,7 @@ | 模块名称 | 任务命令 | 调度频次 | 输出数据 | 格式和示例 | |---------|---------|---------|---------|-----------| +| **i2i_swing_cpp** | `cd collaboration && bash run.sh` | 每天 | `collaboration/output/swing_similar.txt` | `item_id \t similar_id1:score1,similar_id2:score2,...` | | **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,...` | @@ -17,30 +18,79 @@ ### 1. i2i相似度索引 -#### 输出格式 +#### 1.1 C++ Swing算法(高性能版本) + +**输出格式** +``` +item_id \t similar_id1:score1,similar_id2:score2,... +``` + +**示例** +``` +3600052 2704531:0.00431593,2503886:0.00431593,3371410:0.00431593,3186572:0.00431593 +2704531 3600052:0.00431593,2503886:0.00863186,3371410:0.00431593 +``` + +**字段说明** +- `item_id`: 商品SKU ID +- `similar_id`: 相似商品ID +- `score`: 相似度分数(原始Swing分数,范围不固定) + +**特点** +- ⚡ **高性能**: C++实现,速度比Python快10-100倍 +- 📊 **大规模**: 适合处理10万+商品的相似度计算 +- 🔢 **原始分数**: 输出Swing算法原始分数(未归一化) +- 📁 **文件位置**: `collaboration/output/swing_similar.txt` +- 📝 **可读版本**: `collaboration/output/swing_similar_readable.txt` (包含商品名称) + +#### 1.2 Python算法(标准版本) + +**输出格式** ``` 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** | 基于商品属性,类目相似 | 冷启动商品推荐 | +**特点** +- 🐍 **易调试**: Python实现,便于开发和调试 +- 🎯 **功能丰富**: 支持时间衰减、日期维度等高级特性 +- 📊 **归一化**: 相似度分数已归一化到0-1区间 +- 📁 **文件位置**: `offline_tasks/output/i2i_*_YYYYMMDD.txt` + +#### 1.3 算法对比 + +| 算法 | 实现语言 | 性能 | 特点 | 适用场景 | +|------|---------|------|------|---------| +| **Swing (C++)** | C++ | ⚡⚡⚡ | 高性能,大规模数据 | 生产环境,海量数据 | +| **Swing (Python)** | Python | ⚡ | 支持日期维度,时间衰减 | 需要高级特性 | +| **Session W2V** | Python | ⚡ | 基于会话序列 | 详情页"看了又看" | +| **DeepWalk** | Python | ⚡ | 基于图结构 | 详情页"相关推荐" | +| **Content** | Python | ⚡⚡ | 基于商品属性 | 冷启动商品推荐 | + +#### 1.4 使用建议 + +**C++ Swing适用场景**: +- 商品数量 > 50,000 +- 需要快速计算结果 +- 生产环境部署 +- 计算资源有限 + +**Python Swing适用场景**: +- 需要时间衰减功能 +- 需要日期维度分析 +- 开发调试阶段 +- 需要灵活调整参数 ### 2. 兴趣点聚合索引 @@ -121,6 +171,7 @@ logs/debug/{algorithm_name}_{date}_{time}.log | 索引类型 | 索引数量 | 单条大小 | 总大小 | 更新频率 | |---------|---------|---------|--------|---------| +| i2i_swing_cpp | ~50,000 | ~400B | ~20MB | 每天 | | i2i_swing | ~50,000 | ~500B | ~25MB | 每天 | | i2i_session_w2v | ~50,000 | ~500B | ~25MB | 每天 | | i2i_deepwalk | ~50,000 | ~500B | ~25MB | 每天 | @@ -129,7 +180,11 @@ logs/debug/{algorithm_name}_{date}_{time}.log | interest_cart | ~10,000 | ~1KB | ~10MB | 每天 | | interest_new | ~5,000 | ~1KB | ~5MB | 每天 | | interest_global | ~10,000 | ~1KB | ~10MB | 每天 | -| **总计** | **~245,000** | - | **~135MB** | - | +| **总计** | **~295,000** | - | **~155MB** | - | + +**说明**: +- C++ Swing因为不包含商品名称,单条大小较小 +- 推荐同时使用C++ Swing(生产)和Python Swing(开发) ## 🎯 质量检查 diff --git a/offline_tasks/doc/系统改进总结-20241017.md b/offline_tasks/doc/系统改进总结-20241017.md index 1c8bd95..9e20b1d 100644 --- a/offline_tasks/doc/系统改进总结-20241017.md +++ b/offline_tasks/doc/系统改进总结-20241017.md @@ -58,13 +58,17 @@ offline_tasks/run_all.py **新增任务流程**: ``` run_all.py 执行顺序: +前置任务: 1. fetch_item_attributes.py → 获取商品属性 2. generate_session.py → 生成用户session文件 -3. i2i_swing.py → Swing算法 -4. i2i_session_w2v.py → Session W2V -5. i2i_deepwalk.py → DeepWalk -6. i2i_content_similar.py → 内容相似度 -7. interest_aggregation.py → 兴趣聚合 +3. run_cpp_swing() → C++ Swing算法(使用session) + +核心算法任务: +4. i2i_swing.py → Python Swing算法(启用日期维度) +5. i2i_session_w2v.py → Session W2V +6. i2i_deepwalk.py → DeepWalk +7. i2i_content_similar.py → 内容相似度 +8. interest_aggregation.py → 兴趣聚合 ``` **好处**: @@ -259,10 +263,14 @@ python3 scripts/i2i_swing.py --lookback_days 730 --use_daily_session --debug ### C++ Swing算法 ```bash +# C++ Swing现已集成到run_all.py,会自动在session生成后执行 +# 如需单独运行: cd /home/tw/recommendation/collaboration - -# session文件自动生成后,运行Swing bash run.sh + +# 查看结果 +ls -lh output/swing_similar*.txt +cat output/swing_similar_readable.txt | head -20 ``` ### 查看文档 @@ -312,13 +320,71 @@ recommendation/ --- +## 🔧 C++ Swing算法集成 + +### 改进内容 + +**之前**: C++ Swing需要手动切换目录运行 +```bash +cd /home/tw/recommendation/collaboration +bash run.sh +``` + +**现在**: 已集成到`run_all.py`,自动执行 + +### 执行流程 + +``` +run_all.py: +1. fetch_item_attributes.py +2. generate_session.py ← 生成session.txt.YYYYMMDD.cpp +3. run_cpp_swing() ← 自动调用 collaboration/run.sh + ├─ 编译C++程序 + ├─ 读取session文件 + ├─ 运行Swing算法 + ├─ 合并多线程结果 + └─ 生成可读版本(自动添加商品名) +4. 后续Python任务... +``` + +### 输出结果 + +C++ Swing执行后,结果保存在: +``` +collaboration/output_YYYYMMDD/ +├── sim_matrx.* # 多线程输出 +├── swing_similar.txt # 合并结果(ID格式) +└── swing_similar_readable.txt # 可读版本(ID:名称格式) + +collaboration/output -> output_YYYYMMDD # 软链接 +``` + +### 优势 + +- ✅ **自动化**: 无需手动切换目录 +- ✅ **依赖管理**: 确保session文件已生成 +- ✅ **错误处理**: 失败不影响后续任务 +- ✅ **日志统一**: 所有任务日志在同一个文件 +- ✅ **性能**: C++版本比Python版快10-100倍 + +### 单独运行 + +如需单独运行C++ Swing(不执行其他任务): +```bash +cd /home/tw/recommendation/collaboration +bash run.sh +``` + +--- + ## 🎯 核心改进点总结 1. **✅ 性能优化**: 减少80-90%的数据库查询 2. **✅ 架构优化**: 前置任务解耦,数据准备与算法分离 3. **✅ 功能增强**: Swing算法支持日期维度 -4. **✅ 文档规范**: 统一管理,中文命名,清晰索引 -5. **✅ 代码质量**: 无Linter错误,统一编码规范 +4. **✅ 集成优化**: C++ Swing集成到统一流程 +5. **✅ 文档规范**: 统一管理,中文命名,清晰索引 +6. **✅ 代码质量**: 无Linter错误,统一编码规范 --- diff --git a/offline_tasks/run_all.py b/offline_tasks/run_all.py index a963d88..beb0563 100755 --- a/offline_tasks/run_all.py +++ b/offline_tasks/run_all.py @@ -79,6 +79,52 @@ def run_script(script_name, args=None): return False +def run_cpp_swing(): + """ + 运行C++ Swing算法 + + Returns: + bool: 是否成功 + """ + collaboration_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'collaboration') + run_sh_path = os.path.join(collaboration_dir, 'run.sh') + + if not os.path.exists(run_sh_path): + logger.error(f"C++ Swing script not found: {run_sh_path}") + return False + + logger.info(f"Running C++ Swing: bash {run_sh_path}") + + try: + result = subprocess.run( + ['bash', run_sh_path], + cwd=collaboration_dir, + check=True, + capture_output=True, + text=True + ) + logger.info("C++ Swing algorithm completed successfully") + # 输出部分日志 + output_lines = result.stdout.split('\n') + for line in output_lines[-20:]: # 输出最后20行 + if line.strip(): + logger.info(f" {line}") + return True + except subprocess.CalledProcessError as e: + logger.error(f"C++ Swing failed with return code {e.returncode}") + logger.error(f"Error output: {e.stderr}") + # 输出部分stdout以便调试 + if e.stdout: + logger.error("Stdout output:") + for line in e.stdout.split('\n')[-20:]: + if line.strip(): + logger.error(f" {line}") + return False + except Exception as e: + logger.error(f"Unexpected error running C++ Swing: {e}") + return False + + def main(): parser = argparse.ArgumentParser(description='Run all offline recommendation tasks') parser.add_argument('--debug', action='store_true', @@ -124,9 +170,22 @@ def main(): else: logger.error("生成session文件失败") + # 前置任务3: 运行C++ Swing算法 + logger.info("\n" + "="*80) + logger.info("前置任务3: 运行C++ Swing算法(基于session文件)") + logger.info("="*80) + total_count += 1 + if run_cpp_swing(): + success_count += 1 + logger.info("✓ C++ Swing算法执行成功") + logger.info(" 结果文件: collaboration/output/swing_similar.txt") + logger.info(" 可读文件: collaboration/output/swing_similar_readable.txt") + else: + logger.error("C++ Swing算法执行失败,但不影响其他任务继续") + # i2i 行为相似任务 logger.info("\n" + "="*80) - logger.info("Task 1: Running Swing algorithm for i2i similarity") + logger.info("Task 1: Running Python Swing algorithm for i2i similarity") logger.info("="*80) total_count += 1 script_args = [ -- libgit2 0.21.2