# ES向量相似度索引生成 ## 概述 `i2i_content_similar.py` 脚本从Elasticsearch获取商品向量,计算并生成两种内容相似度索引: 1. **基于名称文本向量的相似度** (`i2i_content_name`) 2. **基于图片向量的相似度** (`i2i_content_pic`) ## 使用方法 ### 运行脚本 ```bash cd /home/tw/recommendation/offline_tasks python scripts/i2i_content_similar.py ``` 脚本无需任何参数,所有配置都在代码中设置好。 ### 配置说明 脚本内置配置(位于 `i2i_content_similar.py` 头部): ```python # ES配置 ES_CONFIG = { 'host': 'http://localhost:9200', 'index_name': 'spu', 'username': 'essa', 'password': '4hOaLaf41y2VuI8y' } # 算法参数 TOP_N = 50 # 每个商品返回的相似商品数量 KNN_K = 100 # knn查询返回的候选数 KNN_CANDIDATES = 200 # knn查询的候选池大小 ``` ## 工作流程 ### 1. 获取活跃商品 从数据库查询最近1年内有过行为的商品: ```sql SELECT DISTINCT item_id FROM sensors_events WHERE event IN ('click', 'contactFactory', 'addToPool', 'addToCart', 'purchase') AND create_time >= '1年前' AND item_id IS NOT NULL ``` ### 2. 从ES获取向量 对每个活跃商品,从Elasticsearch查询: ```json { "query": { "term": { "_id": "商品ID" } }, "_source": { "includes": ["_id", "name_zh", "embedding_name_zh", "embedding_pic_h14", "on_sell_days_boost"] } } ``` 返回字段: - `_id`: 商品ID - `name_zh`: 中文名称(用于debug输出) - `embedding_name_zh`: 名称文本向量 (1024维) - `embedding_pic_h14`: 图片向量列表,每个元素包含: - `vector`: 向量 (1024维) - `url`: 图片URL - `on_sell_days_boost`: 上架天数提权值 (0.9~1.1) ### 3. KNN向量相似度查询 使用商品的向量查询相似商品: **名称向量查询:** ```json { "knn": { "field": "embedding_name_zh", "query_vector": [向量值], "k": 100, "num_candidates": 200 }, "_source": ["_id", "name_zh", "on_sell_days_boost"], "size": 100 } ``` **图片向量查询:** ```json { "knn": { "field": "embedding_pic_h14.vector", "query_vector": [向量值], "k": 100, "num_candidates": 200 }, "_source": ["_id", "name_zh", "on_sell_days_boost"], "size": 100 } ``` ### 4. 应用上架天数提权 对每个查询结果,应用 `on_sell_days_boost` 提权: ```python base_score = knn_result['_score'] # KNN基础分数 boost = knn_result['_source']['on_sell_days_boost'] # 提权值 (0.9~1.1) final_score = base_score * boost # 最终分数 ``` **提权说明:** - `on_sell_days_boost` 是基于商品上架天数计算的提权因子 - 取值范围: 0.9 ~ 1.1 - > 1.0: 提权(新品或热门商品) - = 1.0: 不提权(正常商品) - < 1.0: 降权(长尾商品) - 如果字段缺失或异常,默认使用 1.0(不提权) ### 5. 生成索引文件 输出两个文件到 `output/` 目录: - `i2i_content_name_YYYYMMDD.txt`: 基于名称向量的相似索引 - `i2i_content_pic_YYYYMMDD.txt`: 基于图片向量的相似索引 **文件格式:** ``` item_id\titem_name\tsimilar_id1:score1,similar_id2:score2,... ``` **示例:** ``` 123456 香蕉干 234567:0.9234,345678:0.8756,456789:0.8432 ``` ## 输出说明 ### Redis Key 格式 #### 名称向量相似 - **Key**: `item:similar:content_name:{item_id}` - **Value**: `[[similar_id1,score1],[similar_id2,score2],...]` - **TTL**: 30天 #### 图片向量相似 - **Key**: `item:similar:content_pic:{item_id}` - **Value**: `[[similar_id1,score1],[similar_id2,score2],...]` - **TTL**: 30天 ### 使用示例 ```python import redis import json r = redis.Redis(host='localhost', port=6379, db=0) # 获取基于名称向量的相似商品 similar_items = json.loads(r.get('item:similar:content_name:123456')) # 返回: [[234567, 0.9234], [345678, 0.8756], ...] # 获取基于图片向量的相似商品 similar_items = json.loads(r.get('item:similar:content_pic:123456')) # 返回: [[567890, 0.8123], [678901, 0.7856], ...] ``` ## 性能说明 ### 运行时间估算 假设有 50,000 个活跃商品: - ES查询获取向量: ~50,000次,每次约10ms = 8-10分钟 - KNN相似度查询: ~50,000次,每次约50ms = 40-50分钟 - 总计: 约50-60分钟 ### 优化建议 如果性能不够: 1. **批量处理**: 使用ES的 `_mget` 批量获取向量 2. **并发查询**: 使用多线程/异步IO提高查询并发 3. **增量更新**: 只处理新增/更新的商品 4. **缓存结果**: 将ES向量缓存到本地,避免重复查询 ## ES向量字段说明 ### embedding_name_zh - **类型**: `dense_vector` - **维度**: 1024 - **相似度**: `dot_product` - **用途**: 基于商品名称的语义向量 ### embedding_pic_h14 - **类型**: `nested` - **结构**: ```json [ { "vector": [1024维向量], "url": "图片URL" } ] ``` - **相似度**: `dot_product` - **用途**: 基于商品图片的视觉向量 ### on_sell_days_boost - **类型**: `float` - **取值范围**: 0.9 ~ 1.1 - **默认值**: 1.0 - **用途**: 基于上架天数的提权因子 - **计算逻辑**: 最终分数 = KNN分数 × on_sell_days_boost ## 注意事项 1. **网络连接**: 确保能访问ES服务器 2. **权限**: 确保ES用户有查询权限 3. **向量缺失**: 部分商品可能没有向量,会被跳过 4. **向量格式**: 图片向量是嵌套结构,取第一个图片的向量 5. **自我排除**: KNN结果会排除商品自己 6. **提权应用**: 所有相似度分数都已应用 `on_sell_days_boost` 提权 7. **提权范围**: boost值会被限制在0.9~1.1范围内,异常值使用1.0 ## 故障排查 ### 连接ES失败 ```python # 检查ES配置 curl -u essa:4hOaLaf41y2VuI8y http://localhost:9200/_cat/indices ``` ### 查询超时 调整超时参数: ```python es = Elasticsearch( [ES_CONFIG['host']], basic_auth=(ES_CONFIG['username'], ES_CONFIG['password']), request_timeout=60 # 增加到60秒 ) ``` ### 向量字段不存在 检查ES mapping: ```bash curl -u essa:4hOaLaf41y2VuI8y http://localhost:9200/spu/_mapping ``` ## 与其他相似度算法的对比 | 算法 | 数据源 | 优势 | 适用场景 | |------|--------|------|---------| | **Swing** | 用户行为 | 捕获真实交互关系 | 行为相似推荐 | | **W2V** | 用户会话 | 捕获序列关系 | 下一个商品推荐 | | **DeepWalk** | 行为图 | 发现深层关联 | 潜在兴趣挖掘 | | **名称向量** | ES语义向量 | 语义理解强 | 文本相似推荐 | | **图片向量** | ES视觉向量 | 视觉相似性强 | 外观相似推荐 | ## 更新频率建议 - **名称向量相似**: 每周更新(商品名称变化少) - **图片向量相似**: 每周更新(商品图片变化少) - **Redis TTL**: 30天(内容相似度变化慢)