知识图谱实战用NetworkX构建电影推荐系统的完整指南当你在深夜打开流媒体平台面对成千上万的电影选项却不知从何看起时一个智能的推荐系统能成为你的私人观影顾问。本文将带你从零开始使用Python的NetworkX库构建一个基于知识图谱的电影推荐系统。不同于传统的协同过滤算法知识图谱能捕捉电影、演员、导演之间复杂的语义关系让推荐结果更具解释性和多样性。1. 知识图谱基础与电影领域建模知识图谱本质上是一种语义网络它通过三元组头实体-关系-尾实体的形式组织数据。在电影领域我们可以识别出几类核心实体电影实体包含标题、上映年份、评分等属性人物实体包括导演、演员带有职业、国籍等属性类型实体如科幻、悬疑、喜剧等电影分类奖项实体如奥斯卡、金球奖等这些实体间的关系网络构成了推荐系统的知识基础。下面是一个典型电影知识图谱的三元组示例# 电影知识图谱的三元组示例 triplets [ (克里斯托弗·诺兰, 导演, 《星际穿越》), (《星际穿越》, 类型, 科幻), (马修·麦康纳, 主演, 《星际穿越》), (《盗梦空间》, 获奖, 奥斯卡最佳视觉效果) ]构建知识图谱前需要明确领域范围和实体关系模型。下表展示了电影知识图谱的典型实体关系模型实体类型可能属性关联关系电影标题、年份、评分、时长属于类型、由导演执导、有演员出演导演姓名、国籍、出生年份执导电影、合作演员演员姓名、国籍、出生年份出演电影、合作导演电影类型类型名称、描述被电影归类2. 数据准备与知识获取构建高质量的知识图谱需要可靠的数据源。以下是几种常见的数据获取方式2.1 公开数据集利用IMDb数据集包含电影、演员、评分等基础信息豆瓣API提供中文电影数据及用户评分WikiData结构化维基百科数据包含丰富的实体关系import pandas as pd # 示例从CSV加载电影基础数据 movies_df pd.read_csv(movies.csv) actors_df pd.read_csv(actors.csv) relations_df pd.read_csv(movie_actor_relations.csv) print(f加载了{len(movies_df)}部电影数据) print(f加载了{len(actors_df)}位演员数据)2.2 数据清洗与转换原始数据往往需要经过以下处理步骤实体解析合并指代同一实体的不同表述如诺兰和克里斯托弗·诺兰关系抽取从非结构化文本中识别实体关系数据补全填充缺失的重要属性值格式统一确保时间、评分等字段格式一致# 数据清洗示例统一导演姓名格式 def normalize_director_name(name): name name.replace(导演 , ).strip() if · in name: parts name.split(·) return f{parts[0]}·{parts[1].capitalize()} return name movies_df[director] movies_df[director].apply(normalize_director_name)3. NetworkX图谱构建实战NetworkX是Python中最常用的图计算库之一特别适合构建和操作知识图谱。3.1 创建知识图谱基础结构import networkx as nx class MovieKnowledgeGraph: def __init__(self): self.graph nx.DiGraph() self.entity_index {} # 用于快速查找实体 def add_entity(self, entity_id, entity_type, attributesNone): 添加实体节点 if attributes is None: attributes {} attributes[type] entity_type self.graph.add_node(entity_id, **attributes) self.entity_index[entity_id] entity_type def add_relation(self, source_id, target_id, relation_type, attributesNone): 添加实体关系 if attributes is None: attributes {} self.graph.add_edge(source_id, target_id, relationrelation_type, **attributes) def get_entity_relations(self, entity_id): 获取实体的所有关联关系 relations [] # 获取该实体作为源节点的关系 for neighbor in self.graph.successors(entity_id): edge_data self.graph.get_edge_data(entity_id, neighbor) relations.append((entity_id, edge_data[relation], neighbor)) # 获取该实体作为目标节点的关系 for neighbor in self.graph.predecessors(entity_id): edge_data self.graph.get_edge_data(neighbor, entity_id) relations.append((neighbor, edge_data[relation], entity_id)) return relations3.2 批量导入电影数据def build_movie_kg_from_dataframes(kg, movies_df, actors_df, relations_df): # 添加电影节点 for _, row in movies_df.iterrows(): kg.add_entity( entity_idrow[movie_id], entity_typemovie, attributes{ title: row[title], year: row[year], rating: row[rating] } ) # 添加演员节点 for _, row in actors_df.iterrows(): kg.add_entity( entity_idrow[actor_id], entity_typeperson, attributes{ name: row[name], birth_year: row[birth_year], nationality: row[nationality] } ) # 添加关系 for _, row in relations_df.iterrows(): kg.add_relation( source_idrow[source_id], target_idrow[target_id], relation_typerow[relation_type] ) return kg # 初始化知识图谱 movie_kg MovieKnowledgeGraph() movie_kg build_movie_kg_from_dataframes(movie_kg, movies_df, actors_df, relations_df)3.3 知识图谱可视化NetworkX集成了Matplotlib的绘图功能可以直观展示知识图谱结构import matplotlib.pyplot as plt def visualize_kg(kg, layoutspring, figsize(15, 12)): plt.figure(figsizefigsize) # 根据实体类型设置不同颜色 node_colors [] for node in kg.graph.nodes(): if kg.graph.nodes[node][type] movie: node_colors.append(lightblue) else: node_colors.append(lightgreen) # 选择布局算法 if layout spring: pos nx.spring_layout(kg.graph, k0.5, iterations50) elif layout circular: pos nx.circular_layout(kg.graph) else: pos nx.random_layout(kg.graph) # 绘制节点 nx.draw_networkx_nodes( kg.graph, pos, node_size800, node_colornode_colors, alpha0.9 ) # 绘制边 nx.draw_networkx_edges( kg.graph, pos, edge_colorgray, arrowsTrue, arrowstyle-|, arrowsize15 ) # 绘制节点标签 node_labels {node: kg.graph.nodes[node].get(title, node) for node in kg.graph.nodes()} nx.draw_networkx_labels( kg.graph, pos, labelsnode_labels, font_size10 ) # 绘制边标签关系 edge_labels {(u, v): d[relation] for u, v, d in kg.graph.edges(dataTrue)} nx.draw_networkx_edge_labels( kg.graph, pos, edge_labelsedge_labels, font_size8 ) plt.axis(off) plt.show() # 可视化知识图谱只展示部分节点避免过于密集 subgraph movie_kg.graph.subgraph(list(movie_kg.graph.nodes)[:20]) partial_kg MovieKnowledgeGraph() partial_kg.graph subgraph visualize_kg(partial_kg)4. 基于知识图谱的推荐算法实现知识图谱推荐的核心思想是利用图中的语义关系和路径模式发现潜在的关联。4.1 基于元路径的推荐元路径是连接两个实体的一系列关系序列例如用户-喜欢-电影-由-导演-执导-电影就是一个元路径。我们可以基于这些路径计算实体间的相关性。def meta_path_recommendation(kg, source_entity, path_pattern, top_n5): 基于元路径的推荐算法 :param kg: 知识图谱实例 :param source_entity: 源实体ID :param path_pattern: 元路径模式如[directed_by, directed] :param top_n: 返回推荐数量 :return: 推荐实体列表 recommendations {} # 获取所有符合元路径的目标实体 current_entities {source_entity} for relation in path_pattern: next_entities set() for entity in current_entities: # 根据当前关系方向查找下一跳实体 if relation.endswith(_by): # 反向关系如directed_by是directed的反向 actual_relation relation.replace(_by, ) predecessors list(kg.graph.predecessors(entity)) for pred in predecessors: edge_data kg.graph.get_edge_data(pred, entity) if edge_data and edge_data.get(relation) actual_relation: next_entities.add(pred) else: # 正向关系 successors list(kg.graph.successors(entity)) for succ in successors: edge_data kg.graph.get_edge_data(entity, succ) if edge_data and edge_data.get(relation) relation: next_entities.add(succ) current_entities next_entities if not current_entities: break # 统计实体出现频率作为推荐权重 for entity in current_entities: if entity ! source_entity: recommendations[entity] recommendations.get(entity, 0) 1 # 按权重排序返回推荐结果 sorted_recommendations sorted( recommendations.items(), keylambda x: x[1], reverseTrue ) return [entity for entity, _ in sorted_recommendations[:top_n]] # 示例推荐与诺兰风格相似的导演的电影 nolan_movies [n for n in movie_kg.graph.successors(克里斯托弗·诺兰) if movie_kg.graph.get_edge_data(克里斯托弗·诺兰, n)[relation] directed] for movie in nolan_movies: print(f为喜欢 {movie} 的用户推荐:) recs meta_path_recommendation( movie_kg, movie, [directed_by, directed], top_n3 ) for rec in recs: print(f- {rec} (同导演其他作品))4.2 基于图嵌入的推荐图嵌入算法可以将知识图谱中的实体和关系映射到低维向量空间从而计算实体间的相似度。from node2vec import Node2Vec def train_graph_embeddings(kg, dimensions64, walk_length30, num_walks200): 使用node2vec学习图嵌入表示 # 创建node2vec实例 node2vec Node2Vec( kg.graph, dimensionsdimensions, walk_lengthwalk_length, num_walksnum_walks, workers4 ) # 训练模型 model node2vec.fit(window10, min_count1, batch_words4) return model def embedding_based_recommendation(model, kg, source_entity, top_n5): 基于图嵌入的推荐 # 获取所有电影实体 all_movies [ node for node in kg.graph.nodes() if kg.graph.nodes[node][type] movie ] # 计算与源实体的相似度 similarities [] for movie in all_movies: if movie ! source_entity: try: sim model.wv.similarity(source_entity, movie) similarities.append((movie, sim)) except KeyError: continue # 返回最相似的前top_n个电影 similarities.sort(keylambda x: x[1], reverseTrue) return [movie for movie, _ in similarities[:top_n]] # 训练图嵌入模型实际应用中应预先训练保存 # embedding_model train_graph_embeddings(movie_kg) # 示例推荐假设模型已训练 # print(基于图嵌入的推荐:) # for movie in nolan_movies: # recs embedding_based_recommendation(embedding_model, movie_kg, movie) # print(f喜欢 {movie} 的用户可能也喜欢:) # for rec in recs: # print(f- {rec})4.3 混合推荐策略结合多种推荐策略可以得到更全面的推荐结果def hybrid_recommendation(kg, source_entity, embedding_modelNone, top_n5): 混合推荐策略 # 基于元路径的推荐 meta_recs meta_path_recommendation( kg, source_entity, [directed_by, directed], top_ntop_n*2 ) # 基于图嵌入的推荐 if embedding_model: embed_recs embedding_based_recommendation( embedding_model, kg, source_entity, top_ntop_n*2 ) else: embed_recs [] # 合并推荐结果并去重 all_recs {} for i, rec in enumerate(meta_recs): all_recs[rec] all_recs.get(rec, 0) (len(meta_recs) - i) for i, rec in enumerate(embed_recs): all_recs[rec] all_recs.get(rec, 0) (len(embed_recs) - i) # 排除用户已经知道/喜欢的电影 if source_entity in all_recs: del all_recs[source_entity] # 按综合评分排序 sorted_recs sorted( all_recs.items(), keylambda x: x[1], reverseTrue ) return [rec for rec, _ in sorted_recs[:top_n]] # 示例混合推荐 # print(混合推荐结果:) # for movie in nolan_movies: # recs hybrid_recommendation(movie_kg, movie, embedding_model) # print(f综合推荐给喜欢 {movie} 的用户:) # for rec in recs: # print(f- {rec})5. 系统优化与生产部署构建完整的推荐系统还需要考虑性能优化和生产环境部署问题。5.1 知识图谱存储优化对于大规模知识图谱NetworkX的内存存储可能不够高效可以考虑以下优化方案图数据库迁移将知识图谱迁移到Neo4j、Nebula Graph等专业图数据库数据分片按电影类型、年代等维度对图谱进行分片存储缓存策略对热门实体和查询结果进行缓存# 示例将NetworkX图导出为Neo4j兼容格式 def export_to_neo4j(kg, output_dir): 导出知识图谱为Neo4j兼容的CSV文件 # 导出节点 nodes_data [] for node in kg.graph.nodes(): node_data kg.graph.nodes[node] nodes_data.append({ entity_id: node, type: node_data.get(type, ), **{k: v for k, v in node_data.items() if k ! type} }) nodes_df pd.DataFrame(nodes_data) nodes_df.to_csv(f{output_dir}/nodes.csv, indexFalse) # 导出关系 relations_data [] for u, v, data in kg.graph.edges(dataTrue): relations_data.append({ source_id: u, target_id: v, relation_type: data.get(relation, ), **{k: v for k, v in data.items() if k ! relation} }) relations_df pd.DataFrame(relations_data) relations_df.to_csv(f{output_dir}/relations.csv, indexFalse) # export_to_neo4j(movie_kg, neo4j_data)5.2 推荐服务API开发使用Flask或FastAPI可以快速构建推荐系统的Web服务接口from flask import Flask, request, jsonify app Flask(__name__) # 假设已经初始化了知识图谱和推荐模型 # movie_kg load_kg_from_file(movie_kg.pkl) # embedding_model load_embedding_model(embeddings.model) app.route(/recommend, methods[GET]) def recommend(): movie_id request.args.get(movie_id) if not movie_id or movie_id not in movie_kg.entity_index: return jsonify({error: Invalid movie_id}), 400 # 获取推荐结果 recommendations hybrid_recommendation( movie_kg, movie_id, embedding_model ) # 获取推荐电影的详细信息 results [] for rec in recommendations: node_data movie_kg.graph.nodes[rec] results.append({ movie_id: rec, title: node_data.get(title, rec), year: node_data.get(year, ), rating: node_data.get(rating, ) }) return jsonify({recommendations: results}) # if __name__ __main__: # app.run(host0.0.0.0, port5000)5.3 推荐效果评估推荐系统的评估可以从以下几个方面进行准确率评估划分训练集和测试集计算推荐命中率多样性评估检查推荐结果是否覆盖足够多的电影类型新颖性评估评估推荐结果中有多少是用户不太可能自己发现的电影响应时间确保推荐请求能在合理时间内返回def evaluate_recommendation(kg, test_users, top_n5): 推荐系统评估 hit_counts 0 total_recommendations 0 diversity set() for user in test_users: liked_movie user[liked_movie] expected_movies set(user[should_recommend]) # 获取推荐结果 recommendations set( hybrid_recommendation(kg, liked_movie, top_ntop_n) ) # 计算命中数 hits recommendations expected_movies hit_counts len(hits) total_recommendations len(recommendations) # 记录推荐结果的类型多样性 for rec in recommendations: movie_types [ v for u, v, d in kg.graph.out_edges(rec, dataTrue) if d.get(relation) belongs_to ] diversity.update(movie_types) # 计算指标 precision hit_counts / total_recommendations coverage len(diversity) / len(get_all_movie_types(kg)) return { precision: precision, coverage: coverage, diversity_count: len(diversity) } def get_all_movie_types(kg): 获取知识图谱中所有的电影类型 return [ node for node in kg.graph.nodes() if kg.graph.nodes[node].get(type) genre ] # 示例测试用户数据 test_users [ { liked_movie: 《星际穿越》, should_recommend: [《盗梦空间》, 《蝙蝠侠黑暗骑士》] } # 实际应用中应该有更多测试用例 ] # 执行评估 # evaluation_results evaluate_recommendation(movie_kg, test_users) # print(推荐系统评估结果:, evaluation_results)
知识图谱从入门到实践:手把手教你用NetworkX构建电影推荐图谱
发布时间:2026/5/22 10:35:12
知识图谱实战用NetworkX构建电影推荐系统的完整指南当你在深夜打开流媒体平台面对成千上万的电影选项却不知从何看起时一个智能的推荐系统能成为你的私人观影顾问。本文将带你从零开始使用Python的NetworkX库构建一个基于知识图谱的电影推荐系统。不同于传统的协同过滤算法知识图谱能捕捉电影、演员、导演之间复杂的语义关系让推荐结果更具解释性和多样性。1. 知识图谱基础与电影领域建模知识图谱本质上是一种语义网络它通过三元组头实体-关系-尾实体的形式组织数据。在电影领域我们可以识别出几类核心实体电影实体包含标题、上映年份、评分等属性人物实体包括导演、演员带有职业、国籍等属性类型实体如科幻、悬疑、喜剧等电影分类奖项实体如奥斯卡、金球奖等这些实体间的关系网络构成了推荐系统的知识基础。下面是一个典型电影知识图谱的三元组示例# 电影知识图谱的三元组示例 triplets [ (克里斯托弗·诺兰, 导演, 《星际穿越》), (《星际穿越》, 类型, 科幻), (马修·麦康纳, 主演, 《星际穿越》), (《盗梦空间》, 获奖, 奥斯卡最佳视觉效果) ]构建知识图谱前需要明确领域范围和实体关系模型。下表展示了电影知识图谱的典型实体关系模型实体类型可能属性关联关系电影标题、年份、评分、时长属于类型、由导演执导、有演员出演导演姓名、国籍、出生年份执导电影、合作演员演员姓名、国籍、出生年份出演电影、合作导演电影类型类型名称、描述被电影归类2. 数据准备与知识获取构建高质量的知识图谱需要可靠的数据源。以下是几种常见的数据获取方式2.1 公开数据集利用IMDb数据集包含电影、演员、评分等基础信息豆瓣API提供中文电影数据及用户评分WikiData结构化维基百科数据包含丰富的实体关系import pandas as pd # 示例从CSV加载电影基础数据 movies_df pd.read_csv(movies.csv) actors_df pd.read_csv(actors.csv) relations_df pd.read_csv(movie_actor_relations.csv) print(f加载了{len(movies_df)}部电影数据) print(f加载了{len(actors_df)}位演员数据)2.2 数据清洗与转换原始数据往往需要经过以下处理步骤实体解析合并指代同一实体的不同表述如诺兰和克里斯托弗·诺兰关系抽取从非结构化文本中识别实体关系数据补全填充缺失的重要属性值格式统一确保时间、评分等字段格式一致# 数据清洗示例统一导演姓名格式 def normalize_director_name(name): name name.replace(导演 , ).strip() if · in name: parts name.split(·) return f{parts[0]}·{parts[1].capitalize()} return name movies_df[director] movies_df[director].apply(normalize_director_name)3. NetworkX图谱构建实战NetworkX是Python中最常用的图计算库之一特别适合构建和操作知识图谱。3.1 创建知识图谱基础结构import networkx as nx class MovieKnowledgeGraph: def __init__(self): self.graph nx.DiGraph() self.entity_index {} # 用于快速查找实体 def add_entity(self, entity_id, entity_type, attributesNone): 添加实体节点 if attributes is None: attributes {} attributes[type] entity_type self.graph.add_node(entity_id, **attributes) self.entity_index[entity_id] entity_type def add_relation(self, source_id, target_id, relation_type, attributesNone): 添加实体关系 if attributes is None: attributes {} self.graph.add_edge(source_id, target_id, relationrelation_type, **attributes) def get_entity_relations(self, entity_id): 获取实体的所有关联关系 relations [] # 获取该实体作为源节点的关系 for neighbor in self.graph.successors(entity_id): edge_data self.graph.get_edge_data(entity_id, neighbor) relations.append((entity_id, edge_data[relation], neighbor)) # 获取该实体作为目标节点的关系 for neighbor in self.graph.predecessors(entity_id): edge_data self.graph.get_edge_data(neighbor, entity_id) relations.append((neighbor, edge_data[relation], entity_id)) return relations3.2 批量导入电影数据def build_movie_kg_from_dataframes(kg, movies_df, actors_df, relations_df): # 添加电影节点 for _, row in movies_df.iterrows(): kg.add_entity( entity_idrow[movie_id], entity_typemovie, attributes{ title: row[title], year: row[year], rating: row[rating] } ) # 添加演员节点 for _, row in actors_df.iterrows(): kg.add_entity( entity_idrow[actor_id], entity_typeperson, attributes{ name: row[name], birth_year: row[birth_year], nationality: row[nationality] } ) # 添加关系 for _, row in relations_df.iterrows(): kg.add_relation( source_idrow[source_id], target_idrow[target_id], relation_typerow[relation_type] ) return kg # 初始化知识图谱 movie_kg MovieKnowledgeGraph() movie_kg build_movie_kg_from_dataframes(movie_kg, movies_df, actors_df, relations_df)3.3 知识图谱可视化NetworkX集成了Matplotlib的绘图功能可以直观展示知识图谱结构import matplotlib.pyplot as plt def visualize_kg(kg, layoutspring, figsize(15, 12)): plt.figure(figsizefigsize) # 根据实体类型设置不同颜色 node_colors [] for node in kg.graph.nodes(): if kg.graph.nodes[node][type] movie: node_colors.append(lightblue) else: node_colors.append(lightgreen) # 选择布局算法 if layout spring: pos nx.spring_layout(kg.graph, k0.5, iterations50) elif layout circular: pos nx.circular_layout(kg.graph) else: pos nx.random_layout(kg.graph) # 绘制节点 nx.draw_networkx_nodes( kg.graph, pos, node_size800, node_colornode_colors, alpha0.9 ) # 绘制边 nx.draw_networkx_edges( kg.graph, pos, edge_colorgray, arrowsTrue, arrowstyle-|, arrowsize15 ) # 绘制节点标签 node_labels {node: kg.graph.nodes[node].get(title, node) for node in kg.graph.nodes()} nx.draw_networkx_labels( kg.graph, pos, labelsnode_labels, font_size10 ) # 绘制边标签关系 edge_labels {(u, v): d[relation] for u, v, d in kg.graph.edges(dataTrue)} nx.draw_networkx_edge_labels( kg.graph, pos, edge_labelsedge_labels, font_size8 ) plt.axis(off) plt.show() # 可视化知识图谱只展示部分节点避免过于密集 subgraph movie_kg.graph.subgraph(list(movie_kg.graph.nodes)[:20]) partial_kg MovieKnowledgeGraph() partial_kg.graph subgraph visualize_kg(partial_kg)4. 基于知识图谱的推荐算法实现知识图谱推荐的核心思想是利用图中的语义关系和路径模式发现潜在的关联。4.1 基于元路径的推荐元路径是连接两个实体的一系列关系序列例如用户-喜欢-电影-由-导演-执导-电影就是一个元路径。我们可以基于这些路径计算实体间的相关性。def meta_path_recommendation(kg, source_entity, path_pattern, top_n5): 基于元路径的推荐算法 :param kg: 知识图谱实例 :param source_entity: 源实体ID :param path_pattern: 元路径模式如[directed_by, directed] :param top_n: 返回推荐数量 :return: 推荐实体列表 recommendations {} # 获取所有符合元路径的目标实体 current_entities {source_entity} for relation in path_pattern: next_entities set() for entity in current_entities: # 根据当前关系方向查找下一跳实体 if relation.endswith(_by): # 反向关系如directed_by是directed的反向 actual_relation relation.replace(_by, ) predecessors list(kg.graph.predecessors(entity)) for pred in predecessors: edge_data kg.graph.get_edge_data(pred, entity) if edge_data and edge_data.get(relation) actual_relation: next_entities.add(pred) else: # 正向关系 successors list(kg.graph.successors(entity)) for succ in successors: edge_data kg.graph.get_edge_data(entity, succ) if edge_data and edge_data.get(relation) relation: next_entities.add(succ) current_entities next_entities if not current_entities: break # 统计实体出现频率作为推荐权重 for entity in current_entities: if entity ! source_entity: recommendations[entity] recommendations.get(entity, 0) 1 # 按权重排序返回推荐结果 sorted_recommendations sorted( recommendations.items(), keylambda x: x[1], reverseTrue ) return [entity for entity, _ in sorted_recommendations[:top_n]] # 示例推荐与诺兰风格相似的导演的电影 nolan_movies [n for n in movie_kg.graph.successors(克里斯托弗·诺兰) if movie_kg.graph.get_edge_data(克里斯托弗·诺兰, n)[relation] directed] for movie in nolan_movies: print(f为喜欢 {movie} 的用户推荐:) recs meta_path_recommendation( movie_kg, movie, [directed_by, directed], top_n3 ) for rec in recs: print(f- {rec} (同导演其他作品))4.2 基于图嵌入的推荐图嵌入算法可以将知识图谱中的实体和关系映射到低维向量空间从而计算实体间的相似度。from node2vec import Node2Vec def train_graph_embeddings(kg, dimensions64, walk_length30, num_walks200): 使用node2vec学习图嵌入表示 # 创建node2vec实例 node2vec Node2Vec( kg.graph, dimensionsdimensions, walk_lengthwalk_length, num_walksnum_walks, workers4 ) # 训练模型 model node2vec.fit(window10, min_count1, batch_words4) return model def embedding_based_recommendation(model, kg, source_entity, top_n5): 基于图嵌入的推荐 # 获取所有电影实体 all_movies [ node for node in kg.graph.nodes() if kg.graph.nodes[node][type] movie ] # 计算与源实体的相似度 similarities [] for movie in all_movies: if movie ! source_entity: try: sim model.wv.similarity(source_entity, movie) similarities.append((movie, sim)) except KeyError: continue # 返回最相似的前top_n个电影 similarities.sort(keylambda x: x[1], reverseTrue) return [movie for movie, _ in similarities[:top_n]] # 训练图嵌入模型实际应用中应预先训练保存 # embedding_model train_graph_embeddings(movie_kg) # 示例推荐假设模型已训练 # print(基于图嵌入的推荐:) # for movie in nolan_movies: # recs embedding_based_recommendation(embedding_model, movie_kg, movie) # print(f喜欢 {movie} 的用户可能也喜欢:) # for rec in recs: # print(f- {rec})4.3 混合推荐策略结合多种推荐策略可以得到更全面的推荐结果def hybrid_recommendation(kg, source_entity, embedding_modelNone, top_n5): 混合推荐策略 # 基于元路径的推荐 meta_recs meta_path_recommendation( kg, source_entity, [directed_by, directed], top_ntop_n*2 ) # 基于图嵌入的推荐 if embedding_model: embed_recs embedding_based_recommendation( embedding_model, kg, source_entity, top_ntop_n*2 ) else: embed_recs [] # 合并推荐结果并去重 all_recs {} for i, rec in enumerate(meta_recs): all_recs[rec] all_recs.get(rec, 0) (len(meta_recs) - i) for i, rec in enumerate(embed_recs): all_recs[rec] all_recs.get(rec, 0) (len(embed_recs) - i) # 排除用户已经知道/喜欢的电影 if source_entity in all_recs: del all_recs[source_entity] # 按综合评分排序 sorted_recs sorted( all_recs.items(), keylambda x: x[1], reverseTrue ) return [rec for rec, _ in sorted_recs[:top_n]] # 示例混合推荐 # print(混合推荐结果:) # for movie in nolan_movies: # recs hybrid_recommendation(movie_kg, movie, embedding_model) # print(f综合推荐给喜欢 {movie} 的用户:) # for rec in recs: # print(f- {rec})5. 系统优化与生产部署构建完整的推荐系统还需要考虑性能优化和生产环境部署问题。5.1 知识图谱存储优化对于大规模知识图谱NetworkX的内存存储可能不够高效可以考虑以下优化方案图数据库迁移将知识图谱迁移到Neo4j、Nebula Graph等专业图数据库数据分片按电影类型、年代等维度对图谱进行分片存储缓存策略对热门实体和查询结果进行缓存# 示例将NetworkX图导出为Neo4j兼容格式 def export_to_neo4j(kg, output_dir): 导出知识图谱为Neo4j兼容的CSV文件 # 导出节点 nodes_data [] for node in kg.graph.nodes(): node_data kg.graph.nodes[node] nodes_data.append({ entity_id: node, type: node_data.get(type, ), **{k: v for k, v in node_data.items() if k ! type} }) nodes_df pd.DataFrame(nodes_data) nodes_df.to_csv(f{output_dir}/nodes.csv, indexFalse) # 导出关系 relations_data [] for u, v, data in kg.graph.edges(dataTrue): relations_data.append({ source_id: u, target_id: v, relation_type: data.get(relation, ), **{k: v for k, v in data.items() if k ! relation} }) relations_df pd.DataFrame(relations_data) relations_df.to_csv(f{output_dir}/relations.csv, indexFalse) # export_to_neo4j(movie_kg, neo4j_data)5.2 推荐服务API开发使用Flask或FastAPI可以快速构建推荐系统的Web服务接口from flask import Flask, request, jsonify app Flask(__name__) # 假设已经初始化了知识图谱和推荐模型 # movie_kg load_kg_from_file(movie_kg.pkl) # embedding_model load_embedding_model(embeddings.model) app.route(/recommend, methods[GET]) def recommend(): movie_id request.args.get(movie_id) if not movie_id or movie_id not in movie_kg.entity_index: return jsonify({error: Invalid movie_id}), 400 # 获取推荐结果 recommendations hybrid_recommendation( movie_kg, movie_id, embedding_model ) # 获取推荐电影的详细信息 results [] for rec in recommendations: node_data movie_kg.graph.nodes[rec] results.append({ movie_id: rec, title: node_data.get(title, rec), year: node_data.get(year, ), rating: node_data.get(rating, ) }) return jsonify({recommendations: results}) # if __name__ __main__: # app.run(host0.0.0.0, port5000)5.3 推荐效果评估推荐系统的评估可以从以下几个方面进行准确率评估划分训练集和测试集计算推荐命中率多样性评估检查推荐结果是否覆盖足够多的电影类型新颖性评估评估推荐结果中有多少是用户不太可能自己发现的电影响应时间确保推荐请求能在合理时间内返回def evaluate_recommendation(kg, test_users, top_n5): 推荐系统评估 hit_counts 0 total_recommendations 0 diversity set() for user in test_users: liked_movie user[liked_movie] expected_movies set(user[should_recommend]) # 获取推荐结果 recommendations set( hybrid_recommendation(kg, liked_movie, top_ntop_n) ) # 计算命中数 hits recommendations expected_movies hit_counts len(hits) total_recommendations len(recommendations) # 记录推荐结果的类型多样性 for rec in recommendations: movie_types [ v for u, v, d in kg.graph.out_edges(rec, dataTrue) if d.get(relation) belongs_to ] diversity.update(movie_types) # 计算指标 precision hit_counts / total_recommendations coverage len(diversity) / len(get_all_movie_types(kg)) return { precision: precision, coverage: coverage, diversity_count: len(diversity) } def get_all_movie_types(kg): 获取知识图谱中所有的电影类型 return [ node for node in kg.graph.nodes() if kg.graph.nodes[node].get(type) genre ] # 示例测试用户数据 test_users [ { liked_movie: 《星际穿越》, should_recommend: [《盗梦空间》, 《蝙蝠侠黑暗骑士》] } # 实际应用中应该有更多测试用例 ] # 执行评估 # evaluation_results evaluate_recommendation(movie_kg, test_users) # print(推荐系统评估结果:, evaluation_results)