我的推荐系统在为某些啤酒推荐时运行得很好,但有时候会返回 KeyError。我不知道为什么会发生这种情况?
总是发生在尝试为同一项目推荐时,所以可能与 HashMap 代码或反向 HashMap 有关。
**图片的 **
代码
import osimport timeimport gcimport argparseimport pandas as pdfrom scipy.sparse import csr_matrixfrom sklearn.neighbors import NearestNeighborsfrom fuzzywuzzy import fuzzclass KnnRecommender: """ 这是一个基于项目的协同过滤推荐器,由 sklearn 实现 KNN """ def __init__(self, path_beers, path_tastingprofiles): """ 推荐器需要数据路径:啤酒数据和品尝档案数据 参数 ---------- path_movies: str, 啤酒数据文件路径 path_ratings: str, 品尝档案数据文件路径 """ self.path_beers = path_beers self.path_tastingprofiles = path_tastingprofiles self.model = NearestNeighbors() def set_model_params(self, n_neighbors, algorithm, metric, n_jobs=None): """ 为 sklearn.neighbors.NearestNeighbors 设置模型参数 参数 ---------- n_neighbors: int, 可选(默认 = 5) algorithm: {'auto', 'ball_tree', 'kd_tree', 'brute'}, 可选 metric: string or callable, 默认 'minkowski', 或以下之一 ['cityblock', 'cosine', 'euclidean', 'l1', 'l2', 'manhattan'] n_jobs: int or None, 可选(默认=None) """ if n_jobs and (n_jobs > 1 or n_jobs == -1): os.environ['JOBLIB_TEMP_FOLDER'] = '/tmp' self.model.set_params(**{ 'n_neighbors': n_neighbors, 'algorithm': algorithm, 'metric': metric, 'n_jobs': n_jobs}) def _prep_data(self): """ 为推荐器准备数据 1. 啤酒-品尝档案 scipy 稀疏矩阵 2. 啤酒到啤酒-品尝档案 scipy 稀疏矩阵行索引的 HashMap """ # 读取数据 df_beers = pd.read_csv( os.path.join(self.path_beers), usecols=['beerID', 'name', 'beertypeID'], dtype={'beerID': 'int32', 'name': 'str', 'beerID': 'int32'}) df_tastingprofiles = pd.read_csv( os.path.join(self.path_tastingprofiles), usecols=['beerID', 'malty', 'sweet', 'sour', 'hoppy', 'bitter', 'fruity'], dtype={'beerID': 'int32', 'malty': 'float32', 'sweet': 'float32', 'sour': 'float32', 'hoppy': 'float32', 'bitter': 'float32', 'fruity': 'float32'}) #过滤啤酒/移除未被评估的啤酒 df_beers_merged = pd.merge(df_tastingprofiles, df_beers, on='beerID') df_beers = df_beers_merged.drop(['malty', 'sweet', 'sour', 'hoppy', 'bitter', 'fruity'], axis=1) # 透视并创建品尝档案矩阵 df_tastingprofile_features = df_tastingprofiles.set_index('beerID') # 创建从啤酒名称到索引的映射器 hashmap = { beer: i for i, beer in enumerate(list(df_beers.set_index('beerID').loc[df_tastingprofile_features.index].name)) # noqa } #将品尝档案特征转换为 scipy 稀疏矩阵 mat_tastingprofile_features = csr_matrix(df_tastingprofile_features.values) # 清理 del df_beers, df_beers_merged del df_tastingprofiles, df_tastingprofile_features return mat_tastingprofile_features, hashmap def _fuzzy_matching(self, hashmap, fav_beer): """ 通过模糊比率返回最接近的匹配。 如果没有找到匹配,返回 None 参数 ---------- hashmap: dict, 将啤酒名称映射到数据中啤酒的索引 fav_beer: str, 用户输入的啤酒名称 返回 ------ 最接近匹配的索引 """ match_tuple = [] # 获取匹配 for name, idx in hashmap.items(): ratio = fuzz.ratio(name.lower(), fav_beer.lower()) if ratio >= 60: match_tuple.append((name, idx, ratio)) # 排序 match_tuple = sorted(match_tuple, key=lambda x: x[2])[::-1] if not match_tuple: print('Oops! 未找到匹配') else: print('在我们的数据库中找到了可能的匹配: ' '{0}\n'.format([x[0] for x in match_tuple])) return match_tuple[0][1] def _inference(self, model, data, hashmap, fav_beer, n_recommendations): """ 根据用户输入的啤酒返回前 n 个相似的啤酒推荐 参数 ---------- model: sklearn 模型, knn 模型 data: 啤酒-品尝档案矩阵 hashmap: dict, 将啤酒名称映射到数据中啤酒的索引 fav_beer: str, 用户输入的啤酒名称 n_recommendations: int, 前 n 个推荐 返回 ------ 前 n 个相似啤酒推荐的列表 """ # 拟合 model.fit(data) # 获取输入啤酒的索引 print('您输入的啤酒:', fav_beer) idx = self._fuzzy_matching(hashmap, fav_beer) # 推理 print('推荐系统开始进行推理') print('......\n') t0 = time.time() distances, indices = model.kneighbors( data[idx], n_neighbors=n_recommendations+1) # 获取推荐的原始索引列表 raw_recommends = \ sorted( list( zip( indices.squeeze().tolist(), distances.squeeze().tolist() ) ), key=lambda x: x[1] )[:0:-1] print('我的系统花了 {:.2f}s 进行推理 \n\ '.format(time.time() - t0)) # 返回推荐(movieId, distance) return raw_recommends def make_recommendations(self, fav_beer, n_recommendations): """ 进行前 n 个啤酒推荐 参数 ---------- fav_beer: str, 用户输入的啤酒名称 n_recommendations: int, 前 n 个推荐 """ # 获取数据 mat_tastingprofile_features, hashmap = self._prep_data() # 获取推荐 raw_recommends = self._inference( self.model, mat_tastingprofile_features, hashmap, fav_beer, n_recommendations) # 打印结果 reverse_hashmap = {v: k for k, v in hashmap.items()} print('为 {} 的推荐:'.format(fav_beer)) for i, (idx, dist) in enumerate(raw_recommends): #reverse_hashmap[idx] print('{0}: {1}, 距离为 {2}'.format(i+1,reverse_hashmap[idx], dist))def parse_args(): parser = argparse.ArgumentParser( prog="啤酒推荐器", description="运行 KNN 啤酒推荐器") parser.add_argument('--path', nargs='?', default='', help='输入数据路径') parser.add_argument('--beer_filename', nargs='?', default='beer.csv', help='提供啤酒文件名') parser.add_argument('--tastingprofile_filename', nargs='?', default='tastingprofile.csv', help='提供品尝档案文件名') parser.add_argument('--beer_name', nargs='?', default='', help='提供您喜欢的啤酒名称') parser.add_argument('--top_n', type=int, default=10, help='前 n 个啤酒推荐') return parser.parse_args() if __name__ == '__main__': # 获取参数 args = parse_args() data_path = args.path beer_filename = args.beer_filename tastingprofile_filename = args.tastingprofile_filename beer_name = args.beer_name top_n = args.top_n # 初始化推荐系统 recommender = KnnRecommender( os.path.join(data_path, beer_filename), os.path.join(data_path, tastingprofile_filename)) recommender.set_model_params(20, 'brute', 'cosine', -1) # 进行推荐 recommender.make_recommendations(beer_name, top_n)
回答:
我已经修复了它。我发现当我在 HashMap 中使用名称作为值时,重复项会自动被移除。所以 HashMap 比完整的数据库列表要小。我通过在推荐算法中使用数据之前从数据集中移除重复项来解决这个问题。
我将向您展示我使用 pandas Dataframes 合并和 drop_duplicates 的简单修复方法。
#从啤酒数据集中移除重复项 df_beers_noduplicates = df_beers.drop_duplicates(subset='name', keep='first', inplace=False) df_beers_merged = pd.merge(df_tastingprofiles, df_beers_noduplicates, on='beerID') df_beers = df_beers_merged.drop(['malty', 'sweet', 'sour', 'hoppy', 'bitter', 'fruity'], axis=1) df_tastingprofiles = df_beers_merged.drop(['name'], axis=1)