
简介这是一套面向计算机相关专业学生与开发者的电商行业知识图谱实战项目围绕实体关系构建展开可落地于商品推荐、商品搭配与智能问答等场景适合作为毕业设计、课程设计或知识图谱入门进阶的学习素材。压缩包共59个文件、约15.49MB以21个Python源码为核心辅以10个CSV数据、10个TXT词典、5个JSON配置及3个Markdown说明另含XMind思维导图、产品文档与前端页面覆盖数据处理、模型、控制器、服务端等模块结构清晰便于按需查阅。目前已有130人学习。项目代码均经测试运行成功答辩评审平均分达96分读者可据此理解图谱搭建全流程、实体关系抽取与推荐问答的实现思路并在此基础上修改扩展功能。1. 电商知识图谱到底解决什么问题从「猜你喜欢」到「搭配购」的底层逻辑做电商推荐的同学大概率遇到过这种场景用户刚买了一个手机壳首页立刻推手机壳还是同款不同色。协同过滤在这个case上翻车得很彻底因为它只知道「买A的人还买B」却不知道「手机壳」和「钢化膜」之间存在配件搭配关系更不知道「iPhone 16」和「iPhone 16 Pro」的壳不通用。Python 电商行业知识图谱要干的事情就是把这些散落在商品标题、类目、属性表里的关系抽出来建成一张机器能推理的图让推荐、搭配、问答三个场景共享同一套语义底座。这篇文章面向的是已经会用 Python 写脚本、但对知识图谱还停留在「听说过 Neo4j」阶段的工程师。我会从实体关系怎么定义、数据从哪来、图怎么建、怎么查、怎么接到推荐和问答上一步步拆开讲。不涉及任何平台绑定所有代码在本地跑得通。读完你应该能判断自己的电商数据值不值得做图谱以及最小可跑通的版本长什么样。2. 电商图谱的本体设计实体、关系、属性怎么定才不返工2.1 先想清楚要回答哪些问题再定实体类型本体建模是知识图谱里最容易返工的一步。我见过太多团队上来就画一张巨大的ER图结果建到一半发现「品牌」和「店铺」的关系根本用不上。正确的顺序是反过来的先列出业务侧要回答的问题再倒推需要哪些实体和关系。电商场景下典型的问题无非这几类推荐类「买了这个的人还买了什么」「这个商品的同类替代品有哪些」搭配类「这个裙子配什么鞋」「这套家具还缺什么」问答类「XX品牌的旗舰店有哪些」「500元以内的蓝牙耳机推荐」从这三类问题倒推最小可用的实体集合是商品Product、类目Category、品牌Brand、店铺Shop、用户User、属性值AttrValue。关系则是商品属于类目、商品属于品牌、商品在店铺售卖、商品拥有属性、商品与商品搭配、用户购买商品、用户浏览商品。这里有个关键决策属性到底建成实体还是建成商品节点的property我的经验是凡是需要跨商品做聚合或推理的属性必须建成独立节点。比如「颜色」如果只是存在商品节点上你没法回答「所有红色系的连衣裙有哪些」建成AttrValue节点后一次图查询就能拉出来。而像「重量」「尺寸」这种只用于展示、不做推理的直接放property就行别给自己找麻烦。2.2 用 Python 定义本体一份可执行的 schema本体不该只停留在文档里最好用代码固化下来这样后续抽取、校验、入库都能复用同一份定义。下面这份schema用纯Python字典描述不依赖任何图数据库驱动方便你替换成自己的类目体系。# ontology.py # 电商知识图谱本体定义实体类型 关系类型 属性约束 ENTITY_TYPES { Product: { properties: [product_id, title, price, sales, rating], required: [product_id, title, price] }, Category: { properties: [category_id, name, level, parent_id], required: [category_id, name] }, Brand: { properties: [brand_id, name, country], required: [brand_id, name] }, Shop: { properties: [shop_id, name, score, location], required: [shop_id, name] }, AttrValue: { properties: [attr_id, attr_name, attr_value], required: [attr_id, attr_name, attr_value] }, User: { properties: [user_id, age_range, gender], required: [user_id] } } RELATION_TYPES { BELONGS_TO: (Product, Category), # 商品属于类目 MADE_BY: (Product, Brand), # 商品属于品牌 SOLD_IN: (Product, Shop), # 商品在店铺售卖 HAS_ATTR: (Product, AttrValue), # 商品拥有属性 MATCHES_WITH: (Product, Product), # 商品搭配关系 BOUGHT: (User, Product), # 用户购买 VIEWED: (User, Product), # 用户浏览 SIMILAR_TO: (Product, Product) # 商品相似 } def validate_node(node_type, node_data): 校验节点是否符合本体定义返回 (bool, msg) if node_type not in ENTITY_TYPES: return False, f未知实体类型: {node_type} schema ENTITY_TYPES[node_type] for field in schema[required]: if field not in node_data or node_data[field] in (None, ): return False, f{node_type} 缺少必填字段: {field} return True, ok这段代码的价值在于它把「本体」从一张图变成了可调用的校验函数。后续无论你是从CSV导入还是从爬虫写入都先过一遍validate_node脏数据在入口就被拦住不会污染整张图。参数上required字段是硬约束properties是软约束可以有缺失这个区分很重要——电商数据里价格缺失很常见但product_id缺失就是致命错误。2.3 类目体系怎么处理树形结构的两种建法电商类目天然是树形的一级类目「女装」下面有「连衣裙」再下面有「长袖连衣裙」。建图时有两种做法第一种是只建叶子类目节点商品直接挂到叶子类目上父级关系通过parent_id属性维护。优点是节点少、查询快缺点是查「所有女装」时要递归。第二种是每个层级都建节点用PARENT_OF关系连起来。优点是层级查询直观缺点是节点数量膨胀而且类目调整时维护成本高。我一般选第一种因为电商类目树通常不超过5层递归查询在Neo4j里用*1..5就能搞定性能完全够用。只有当你的类目体系超过7层、或者需要频繁做跨层级聚合时才值得上第二种。3. 数据从哪来商品标题、属性表、订单日志的抽取流水线3.1 商品标题里的实体识别规则词典比大模型更稳电商知识图谱的原始数据80%来自商品标题和属性表。标题像「2024新款女士真皮手提包大容量通勤单肩包」这种里面藏着类目、材质、风格、适用人群。用大模型抽取当然可以但成本和稳定性对中小团队不友好。我的做法是词典匹配规则模板覆盖80%的常见模式剩下的长尾再考虑模型。先建三本词典品牌词典、类目词典、属性词典。品牌词典可以从店铺资质或商品详情页的品牌字段拿类目词典直接用平台的类目树属性词典需要自己攒比如颜色词、材质词、风格词。# extractor.py # 基于词典和规则的标题实体抽取 import re BRAND_DICT {华为, 小米, 苹果, 耐克, 阿迪达斯, 优衣库} CATEGORY_DICT {连衣裙, 手提包, 单肩包, 蓝牙耳机, 运动鞋, 手机壳} ATTR_PATTERNS { color: re.compile(r(红色|黑色|白色|蓝色|粉色|灰色|米色)), material: re.compile(r(真皮|PU|帆布|棉麻|羊毛|涤纶)), style: re.compile(r(通勤|休闲|复古|简约|韩版|欧美)), audience: re.compile(r(女士|男士|学生|儿童|中年)), } def extract_from_title(title): 从商品标题抽取实体和属性返回结构化字典 result {brand: None, category: None, attrs: {}} # 品牌匹配取最长匹配避免小米被小误伤 for brand in sorted(BRAND_DICT, keylen, reverseTrue): if brand in title: result[brand] brand break # 类目匹配同样取最长 for cat in sorted(CATEGORY_DICT, keylen, reverseTrue): if cat in title: result[category] cat break # 属性匹配每个维度取第一个命中 for attr_name, pattern in ATTR_PATTERNS.items(): match pattern.search(title) if match: result[attrs][attr_name] match.group(1) return result # 测试 if __name__ __main__: t 2024新款女士真皮手提包大容量通勤单肩包 print(extract_from_title(t)) # {brand: None, category: 手提包, attrs: {material: 真皮, style: 通勤, audience: 女士}}逻辑说明品牌和类目用最长优先匹配因为「小米」和「小」如果都命中短的会误伤。属性用正则逐个维度扫每个维度只取第一个命中避免「红色」和「粉色」同时出现时冲突。参数上ATTR_PATTERNS里的正则可以根据你的品类扩充比如做家具的加「实木|板材|金属」做美妆的加「保湿|美白|控油」。这套规则跑下来标题抽取的准确率在85%左右召回率看词典覆盖度。漏掉的主要是「ins风」「法式」这类新兴风格词需要定期人工补充词典。别指望一次建全词典是养出来的。3.2 属性表的对齐把「红色」和「红」合并成一个节点属性表里最头疼的是同义词。「红色」和「红」、「真皮」和「牛皮」、「通勤」和「上班」——如果不做归一化图里会散落一堆同义节点查询时漏掉一半结果。归一化的做法是建一张同义词映射表在写入图之前先做替换。这张表可以人工维护核心词长尾词用编辑距离或词向量相似度自动归并。# normalizer.py # 属性值归一化同义词合并 SYNONYM_MAP { 红: 红色, 大红: 红色, 酒红: 红色, 牛皮: 真皮, 羊皮: 真皮, 头层皮: 真皮, 上班: 通勤, 职场: 通勤, 休闲风: 休闲, casual: 休闲, } def normalize_attr(value): 归一化属性值未命中则返回原值 return SYNONYM_MAP.get(value.strip(), value.strip()) # 批量归一化 def normalize_batch(values): return [normalize_attr(v) for v in values]参数说明SYNONYM_MAP的key是原始值value是标准值。维护时遵循「少数服从多数」原则——哪个说法在数据里出现最多就用哪个当标准值。比如「红色」出现10万次、「红」出现2万次标准值就是「红色」。注意归一化只对属性值做不要对商品标题做。标题是原始文本改了就没法回溯了。3.3 搭配关系的挖掘从订单共现到图上的 MATCHES_WITH搭配关系是电商图谱区别于通用图谱的核心。它的来源有两个一是运营人工配置的搭配套餐二是从订单共现里挖出来的统计关系。订单共现的逻辑很简单同一个订单里出现的商品两两之间算一次共现。统计所有订单后用支持度和置信度筛出强关联。# cooccurrence.py # 从订单数据挖掘商品搭配关系 from collections import defaultdict from itertools import combinations def mine_cooccurrence(orders, min_support5, min_confidence0.1): orders: list of list, 每个元素是一个订单的商品ID列表 min_support: 最小共现次数 min_confidence: 最小置信度 P(B|A) count(A,B)/count(A) 返回: [(item_a, item_b, support, confidence), ...] pair_count defaultdict(int) item_count defaultdict(int) for order in orders: items list(set(order)) # 同一订单内去重 for item in items: item_count[item] 1 for a, b in combinations(sorted(items), 2): pair_count[(a, b)] 1 results [] for (a, b), cnt in pair_count.items(): if cnt min_support: continue conf_ab cnt / item_count[a] conf_ba cnt / item_count[b] # 取两个方向的较大值作为搭配强度 confidence max(conf_ab, conf_ba) if confidence min_confidence: results.append((a, b, cnt, round(confidence, 4))) return sorted(results, keylambda x: -x[3])逻辑说明min_support过滤掉偶然共现min_confidence过滤掉「A出现时B不一定出现」的弱关系。置信度取双向最大值是因为搭配关系通常不对称——买手机的人大概率买壳但买壳的人不一定买手机。参数上min_support建议设为订单总量的0.1%min_confidence从0.05开始试太高会漏掉长尾搭配。挖出来的搭配对写入图时建成MATCHES_WITH关系关系属性带上support和confidence后续推荐排序时可以直接用。4. 用 Neo4j 建图从 CSV 到 Cypher 的完整导入流程4.1 环境准备与约束建立Neo4j 是电商知识图谱最常见的落地选择社区版免费且 Cypher 查询语言上手快。安装方式不展开假设你已经有一个跑起来的 Neo4j 实例本地或服务器都行。第一步不是导数据而是建约束和索引否则数据量上去后查询会慢到怀疑人生。// 1. 为每个实体类型的业务主键建唯一约束 CREATE CONSTRAINT product_id_unique IF NOT EXISTS FOR (p:Product) REQUIRE p.product_id IS UNIQUE; CREATE CONSTRAINT category_id_unique IF NOT EXISTS FOR (c:Category) REQUIRE c.category_id IS UNIQUE; CREATE CONSTRAINT brand_id_unique IF NOT EXISTS FOR (b:Brand) REQUIRE b.brand_id IS UNIQUE; CREATE CONSTRAINT shop_id_unique IF NOT EXISTS FOR (s:Shop) REQUIRE s.shop_id IS UNIQUE; CREATE CONSTRAINT attr_id_unique IF NOT EXISTS FOR (a:AttrValue) REQUIRE a.attr_id IS UNIQUE; // 2. 为常用查询字段建索引 CREATE INDEX product_title_idx IF NOT EXISTS FOR (p:Product) ON (p.title); CREATE INDEX product_price_idx IF NOT EXISTS FOR (p:Product) ON (p.price);约束的作用不只是防重复更重要的是Neo4j 会为唯一约束自动建索引后续用MERGE导入时性能差好几倍。索引则针对WHERE条件里的字段比如按价格区间筛商品。4.2 用 Python 批量导入节点和关系数据量小的时候可以用LOAD CSV但电商数据动辄几十万商品用 Python 驱动批量提交更可控。核心是用 MERGE 而不是 CREATE保证重复导入不会产生重复节点。# neo4j_import.py # 用 Python 驱动批量导入电商图谱 from neo4j import GraphDatabase import csv driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, your_password)) def import_products(tx, batch): 批量 MERGE 商品节点 tx.run( UNWIND $batch AS row MERGE (p:Product {product_id: row.product_id}) SET p.title row.title, p.price toFloat(row.price), p.sales toInteger(row.sales), p.rating toFloat(row.rating) , batchbatch) def import_relations(tx, batch): 批量建立商品-类目-品牌关系 tx.run( UNWIND $batch AS row MATCH (p:Product {product_id: row.product_id}) MERGE (c:Category {category_id: row.category_id}) SET c.name row.category_name MERGE (p)-[:BELONGS_TO]-(c) MERGE (b:Brand {brand_id: row.brand_id}) SET b.name row.brand_name MERGE (p)-[:MADE_BY]-(b) , batchbatch) def load_csv(path): with open(path, encodingutf-8) as f: return list(csv.DictReader(f)) if __name__ __main__: rows load_csv(products.csv) batch_size 1000 with driver.session() as session: for i in range(0, len(rows), batch_size): batch rows[i:ibatch_size] session.execute_write(import_products, batch) session.execute_write(import_relations, batch) print(f已导入 {min(ibatch_size, len(rows))}/{len(rows)}) driver.close()逻辑说明UNWIND $batch把一批数据展开成多行一次网络往返处理1000条比逐条提交快两个数量级。MERGE保证幂等重复跑不会炸。参数上batch_size建议1000到5000太小网络开销大太大事务内存吃紧。toFloat和toInteger是显式类型转换CSV读进来全是字符串不转的话价格排序会按字典序排10排在2前面。4.3 搭配关系和相似关系的写入搭配关系来自第3章的共现挖掘结果相似关系可以用商品标题的文本相似度或属性重合度算。写入时注意关系是有方向的但搭配语义上是对称的所以两个方向都建或者查询时用无向匹配。# import_match.py # 导入商品搭配关系 def import_matches(tx, batch): tx.run( UNWIND $batch AS row MATCH (a:Product {product_id: row.item_a}) MATCH (b:Product {product_id: row.item_b}) MERGE (a)-[r:MATCHES_WITH]-(b) SET r.support toInteger(row.support), r.confidence toFloat(row.confidence) , batchbatch) # 调用时传入 mine_cooccurrence 的结果 # batch [{item_a: a, item_b: b, support: s, confidence: c} ...]参数说明support和confidence存成关系属性推荐排序时可以用ORDER BY r.confidence DESC。如果搭配关系量很大百万级建议只保留单向关系查询时用MATCH (a)-[:MATCHES_WITH]-(b)无向匹配省一半存储。5. 图谱接推荐、搭配、问答三个场景的查询与排序5.1 商品推荐基于图路径的召回与排序传统协同过滤的召回是「买A的人还买B」图谱推荐的召回可以更丰富同类目高销量、同品牌替代、搭配商品、相似商品。用一条Cypher就能把多路召回合并。// 给定商品ID召回推荐候选 MATCH (p:Product {product_id: $pid}) // 路径1同类目下的其他商品 OPTIONAL MATCH (p)-[:BELONGS_TO]-(c:Category)-[:BELONGS_TO]-(rec1:Product) WHERE rec1.product_id $pid // 路径2搭配商品 OPTIONAL MATCH (p)-[:MATCHES_WITH]-(rec2:Product) // 路径3同品牌商品 OPTIONAL MATCH (p)-[:MADE_BY]-(b:Brand)-[:MADE_BY]-(rec3:Product) WHERE rec3.product_id $pid WITH p, collect(DISTINCT rec1)[..20] AS same_cat, collect(DISTINCT rec2)[..10] AS match_items, collect(DISTINCT rec3)[..10] AS same_brand RETURN same_cat, match_items, same_brand逻辑说明OPTIONAL MATCH保证某条路径没有结果时不影响其他路径。collect(DISTINCT ...)去重后截断避免候选爆炸。参数上[..20]这种截断值根据你的排序策略定——如果后续有精排模型召回可以多给一些如果没有就少给一些直接展示。拿到候选后排序可以用简单的加权分score 0.4 * 同类目销量归一化 0.3 * 搭配置信度 0.2 * 品牌匹配 0.1 * 价格接近度。权重根据业务调没有标准答案。5.2 商品搭配从 MATCHES_WITH 到搭配套餐生成搭配场景比推荐更强调互补性而非相似性。用户看了一条裙子推相似裙子是推荐推搭配的鞋和包才是搭配。图谱里的MATCHES_WITH关系直接支撑这个场景。// 查询与给定商品搭配的商品按置信度排序 MATCH (p:Product {product_id: $pid})-[r:MATCHES_WITH]-(m:Product) WHERE m.price $max_price RETURN m.product_id, m.title, m.price, r.confidence ORDER BY r.confidence DESC LIMIT 10如果要生成「整套搭配」可以沿着搭配关系走两跳但要注意控制路径爆炸。两跳搭配在稠密图上可能召回几千个组合实际业务里通常限制每跳的候选数。// 两跳搭配裙子 - 鞋 - 包 MATCH (p:Product {product_id: $pid})-[r1:MATCHES_WITH]-(m1:Product) WHERE r1.confidence 0.1 MATCH (m1)-[r2:MATCHES_WITH]-(m2:Product) WHERE m2.product_id $pid AND r2.confidence 0.1 RETURN p.title AS base, m1.title AS mid, m2.title AS outer, r1.confidence * r2.confidence AS combo_score ORDER BY combo_score DESC LIMIT 5参数说明r1.confidence 0.1是剪枝阈值低于这个值的搭配关系不可靠带着走只会放大噪声。combo_score用置信度乘积反映整条搭配链的强度。5.3 问答系统把自然语言转成 Cypher问答是图谱最直观的出口。用户问「500元以内的蓝牙耳机有哪些」系统要能解析出「蓝牙耳机」是类目、「500元以内」是价格约束然后生成Cypher查询。完整NL2Cypher需要模型但电商场景的问答句式相对固定用模板匹配槽位填充就能覆盖大部分。下面是一个最小实现# qa_engine.py # 基于模板的电商图谱问答 import re TEMPLATES [ { pattern: re.compile(r(\d)元以内的(.)), cypher: MATCH (p:Product)-[:BELONGS_TO]-(c:Category) WHERE c.name CONTAINS $category AND p.price $price RETURN p.title, p.price ORDER BY p.sales DESC LIMIT 10 , slots: {price: 1, category: 2} }, { pattern: re.compile(r(.)品牌的(.)), cypher: MATCH (p:Product)-[:MADE_BY]-(b:Brand) MATCH (p)-[:BELONGS_TO]-(c:Category) WHERE b.name CONTAINS $brand AND c.name CONTAINS $category RETURN p.title, p.price ORDER BY p.rating DESC LIMIT 10 , slots: {brand: 1, category: 2} }, ] def answer(question, session): for tpl in TEMPLATES: m tpl[pattern].search(question) if not m: continue params {} for slot, group_idx in tpl[slots].items(): val m.group(group_idx) params[slot] int(val) if slot price else val result session.run(tpl[cypher], **params) return [dict(r) for r in result] return None逻辑说明每个模板包含正则、Cypher和槽位映射。匹配成功后把正则捕获组的值填进Cypher参数。参数上price需要转int其他保持字符串。这套方案覆盖不了复杂问句但电商问答的80%就是「XX元以内的XX」「XX品牌的XX」这几种句式先把高频搞定长尾再上模型。注意Cypher里的CONTAINS是模糊匹配如果类目名有歧义比如「耳机」既匹配「蓝牙耳机」也匹配「有线耳机」需要在模板里加更精确的类目词典约束。6. 避坑与排查建图过程中最容易翻车的五个地方6.1 坑一MERGE 用错导致节点重复现象导入两次数据后商品节点数量翻倍查询结果出现重复。原因MERGE (p:Product {product_id: row.id})只在product_id有唯一约束时才幂等。如果约束没建或者MERGE的字段和约束字段不一致就会重复创建。解决导入前先执行SHOW CONSTRAINTS确认约束存在。MERGE的字段必须和约束字段完全一致大小写敏感。6.2 坑二批量导入时事务内存溢出现象导入到一半报OutOfMemoryError或者Neo4j服务直接挂掉。原因batch_size设得太大或者单个事务里同时MERGE节点和关系内存占用翻倍。解决把batch_size降到1000节点和关系分两个事务提交。如果还不行检查Neo4j的dbms.memory.heap.max_size配置默认值通常偏小。6.3 坑三属性值没归一化导致查询漏结果现象查「红色连衣裙」只返回一半另一半存的是「红」。原因写入时没做同义词归一化图里同时存在「红色」和「红」两个AttrValue节点。解决在写入前统一过一遍normalize_attr。已经导入的数据用Cypher批量更新MATCH (a:AttrValue {attr_value: 红}) SET a.attr_value 红色。6.4 坑四搭配关系方向搞反导致推荐缺失现象A搭配B但查B的搭配时查不到A。原因只建了(A)-[:MATCHES_WITH]-(B)查询时用了有向匹配。解决查询时用无向匹配MATCH (a)-[:MATCHES_WITH]-(b)或者在写入时两个方向都建。前者省存储后者查询快看你的读写比。6.5 坑五Cypher 查询没走索引导致全图扫描现象数据量到十万级后按标题查商品要好几秒。原因WHERE p.title CONTAINS xxx这种模糊匹配走不了索引Neo4j会全表扫描。解决精确匹配用走索引模糊匹配考虑上全文索引Neo4j的full-text index或者把搜索需求交给外部搜索引擎图谱只负责关系查询。7. 进阶技巧用图嵌入给商品向量化接上向量检索图谱建好之后除了直接查Cypher还有一个进阶玩法用图嵌入算法把每个商品映射成一个低维向量然后接向量数据库做语义检索。这样既能保留图的结构信息又能支持「类似这个商品」的模糊查询。我常用的是Node2Vec它在电商图上跑出来的向量同类目商品会聚在一起搭配商品的距离也会比随机商品近。下面是用node2vec库跑嵌入的最小示例# graph_embedding.py # 用 Node2Vec 给商品节点生成向量 import networkx as nx from node2vec import Node2Vec from neo4j import GraphDatabase driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, your_password)) def load_graph(): 从 Neo4j 拉取商品-商品关系构建 NetworkX 图 G nx.Graph() with driver.session() as session: result session.run( MATCH (a:Product)-[r:MATCHES_WITH|SIMILAR_TO]-(b:Product) RETURN a.product_id AS a, b.product_id AS b, type(r) AS rel, r.confidence AS w ) for row in result: G.add_edge(row[a], row[b], weightrow[w] or 0.5) return G def train_embedding(G, dim64, walk_length30, num_walks200): 训练 Node2Vec 嵌入 node2vec Node2Vec( G, dimensionsdim, walk_lengthwalk_length, num_walksnum_walks, weight_keyweight, workers4 ) model node2vec.fit(window10, min_count1, batch_words4) return model if __name__ __main__: G load_graph() print(f图规模: {G.number_of_nodes()} 节点, {G.number_of_edges()} 边) model train_embedding(G) # 保存向量 model.wv.save_word2vec_format(product_vectors.txt) # 查相似商品 similar model.wv.most_similar(P12345, topn10) print(similar)逻辑说明load_graph只拉商品之间的关系边不拉商品-类目边因为Node2Vec学的是节点共现类目节点会把商品向量拉向类目中心损失个体差异。dim64是经验值电商商品向量通常64到128维够用再高收益递减。walk_length30和num_walks200控制随机游走的深度和广度图越稠密这两个值要越大。参数说明weight_keyweight让游走时按关系权重采样搭配置信度高的边被走到的概率大学出来的向量更能反映强关系。window10是Word2Vec的上下文窗口对应图上就是几跳邻居。生成的向量存进Milvus或Faiss后就能做「以图搜图」式的商品检索。我一般会把这个向量检索作为召回的一路和Cypher的结构化召回合并再统一排序。图谱负责精确的关系推理向量负责模糊的语义匹配两者互补。最后说个血泪教训图嵌入的向量质量高度依赖图的质量。如果搭配关系里混了大量噪声比如用min_support1挖出来的偶然共现学出来的向量会把不相关的商品聚在一起反而拉低效果。所以先把图建干净再谈嵌入顺序不能反。希望帮到你。本文还有配套的精品资源点击获取