第 5 章 构建推荐引擎

在这一章,我们将介绍以下主题:
为数据处理构建函数组合
构建机器学习流水线(pipeline)
寻找最近邻
构建一个KNN分类器
构建一个KNN回归器
计算欧氏距离分数(Euclidean distance score)
计算皮尔逊相关系数(Pearson correlation score)
寻找数据集中的相似用户
生成电影推荐
5.1 简介
推荐引擎是一个能预测用户兴趣点的模型。将推荐引擎应用于电影语
境时,便成了一个电影推荐引擎。我们通过预测当前用户可能会喜欢
的内容,将相应的东西从数据库中筛选出来,这样的推荐引擎可以有
助于将用户和数据集中的合适内容连接起来。为什么推荐引擎这么重
要?设想你有一个很庞大的商品目录,而用户可能或者不可能查找所
有的相关内容。通过推荐合适的内容,可以增加用户消费。有些公司
(如Netflix)严重地依赖推荐系统来保持用户参与度。
推荐引擎通常用协同过滤(collaborative filtering)或基于内容的
过滤(content-based filtering)来产生一组推荐。两种过滤方法的
不同之处在于挖掘推荐的方式。协同过滤从当前用户过去的行为和其
他用户对当前用户的评分来构建模型,然后使用这个模型来预测这个
用户可能感兴趣的内容;而基于内容的过滤用商品本身的特征来给用
户推荐更多的商品,商品间的相似度是模型主要的关注点。本章将重
点介绍协同过滤。
5.2 为数据处理构建函数组合
机器学习系统中的主要组成部分是数据处理流水线。在数据被输入到
机器学习算法中进行训练之前,需要对数据做各种方式的处理,使得
该数据可以被算法利用。在构建一个准确的、可扩展的机器学习系统
的过程中,拥有一个健壮的数据处理流水线非常重要。有很多基本的
函数功能可以使用,通常数据处理流水线就是这些基本函数的组合。
不推荐使用嵌套或循环的方式调用这些函数,而是用函数式编程的方
式构建函数组合。接下来介绍如何组合这些函数来形成一个可重用的
函数组合,本节将创建3个基本函数,并介绍如何将其组合成一个流水
线。
详细步骤
(1) 创建一个Python文件,导入以下程序包:
import numpy as np
(2) 定义第一个函数,将数组的每一个元素加3:
def add3(input_array):
return map(lambda x: x+3, input_array)
(3) 定义第二个函数,将数组的每一个元素乘以2:
def mul2(input_array):
return map(lambda x: x*2, input_array)
(4) 定义第三个函数,将数组的每一个元素减去5:
def sub5(input_array):
return map(lambda x: x-5, input_array)
(5) 定义一个函数组合器,将这些函数作为输入参数,返回一个组合
函数。这个组合函数基本上是输入函数按序执行的一个函数:
def function_composer(*args):
return reduce(lambda f, g: lambda x: f(g(x)), args)
我们用reduce函数依次执行所有函数,也就是将所有的输入函数合
并。
(6) 接下来可以做函数组合了。首先定义一些数据和一组操作:
if __name__=='__main__':
arr = np.array([2,5,4,7])
print "\nOperation: add3(mul2(sub5(arr)))"
(7) 如果用常规的方法,我们依次执行函数,代码如下:
arr1 = add3(arr)
arr2 = mul2(arr1)
arr3 = sub5(arr2)
print "Output using the lengthy way:", arr3
(8) 下面用单行代码的函数组合器实现同样的功能:
func_composed = function_composer(sub5, mul2, add3)
print "Output using function composition:", func_composed(arr)
(9) 可以通过上面的方法用单行代码实现函数组合,但是其表示方式
是嵌套的和不可读的,而且也是不可重用的。当需要再次用到这组操
作时,需要重新编写:
print "\nOperation:
sub5(add3(mul2(sub5(mul2(arr)))))\nOutput:", \
function_composer(mul2, sub5, mul2, add3, sub5)(arr)
(10) 运行代码,可以在命令行工具中看到如图5-1所示的输出结果。
图 5-1
5.3 构建机器学习流水线
scikit-learn库中包含了构建机器学习流水线的方法。只需要指定函
数,它就会构建一个组合对象,使数据通过整个流水线。这个流水线
可以包括诸如预处理、特征选择、监督式学习、非监督式学习等函
数。这一节将构建一个流水线,以便输入特征向量、选择最好的k 个
特征、用随机森林分类器进行分类等。
5.3.1 详细步骤
(1) 创建一个Python文件,导入以下程序包:
from sklearn.datasets import samples_generator
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.pipeline import Pipeline
(2) 生成一些示例数据:
# 生成样本数据
X, y = samples_generator.make_classification(
n_informative=4, n_features=20, n_redundant=0,
random_state=5)
这一行代码生成了一个20维的特征向量,因为这是一个默认值。在这
里,你可以通过修改n_features参数来修改特征向量的维数。
(3) 建立流水线的第一步是在数据点进一步操作之前选择k个最好的特
征。这里设置k的值为10:
# 特征选择器
selector_k_best = SelectKBest(f_regression, k=10)
(4) 建立流水线的第二步是用随机森林分类器分类数据:
# 随机森林分类器
classifier = RandomForestClassifier(n_estimators=50, max_depth=4)
(5) 接下来可以创建流水线了。pipeline方法允许我们用预定义的
对象来创建流水线:
# 构建机器学习流水线
pipeline_classifier = Pipeline([('selector', selector_k_best),
('rf', classifier)])
还可以在流水线中为模块指定名称。上一行代码中将特征选择器命名
为selector,将随机森林分类器命名为rf。你也可以任意选用其他
名称。
(6) 也可以更新这些参数,用上一步骤中命名的名称设置这些参数。
例如,如果希望在特征选择器中将k值设置为6,在随机森林分类器中
将n_estimators的值设置为25,可以用下面的代码实现。注意,这
些变量名称已在上一步骤中给出。
pipeline_classifier.set_params(selector__k=6,
rf__n_estimators=25)
(7) 接下来训练分类器:
# 训练分类器
pipeline_classifier.fit(X, y)
(8) 为训练数据预测输出结果:
# 预测输出结果
prediction = pipeline_classifier.predict(X)
print "\nPredictions:\n", prediction
(9) 评价分类器的性能:
# 打印分类器得分
print "\nScore:", pipeline_classifier.score(X, y)
(10) 还可以查看哪些特征被选中,并将其打印出:
# 打印被分类器选中的特征
features_status =
pipeline_classifier.named_steps['selector'].get_support()
selected_features = []
for count, item in enumerate(features_status):
if item:
selected_features.append(count)
print "\nSelected features (0-indexed):", ', '.join([str(x) for x
in selected_features])
(11) 运行代码,可以在命令行工具中看到如图5-2所示的输出结果。
图 5-2
5.3.2 工作原理
选择k 个最好的特征,其好处在于可以处理较小维度的数据,这对减
小计算复杂度来说非常有用。选择k 个最佳特征的方式是基于单变量
的特征选择,选择过程是先进行单变量统计测试,然后从特征向量中
抽取最优秀的特征。单变量统计测试是指只涉及一个变量的分析技
术。
做了这些测试后,向量空间中的每个特征将有一个评价分数。基于这
些评价分数,选择最好的k 个特征。我们在分类器流水线中执行这个
预处理步骤。一旦抽取出k 个特征,一个k 维的特征向量就形成了,
可以将这个特征向量用于随机森林分类器的输入训练数据。
5.4 寻找最近邻
最近邻模型是指一个通用算法类,其目的是根据训练数据集中的最近
邻数量来做决策。接下来学习如何寻找最近邻。
详细步骤
(1) 创建一个Python文件,导入以下程序包:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import NearestNeighbors
(2) 创建一些示例的二维数据:
# 输入数据
X = np.array([[1, 1], [1, 3], [2, 2], [2.5, 5], [3, 1],
[4, 2], [2, 3.5], [3, 3], [3.5, 4]])
(3) 我们的目标是对于任意给定点找到其3个最近邻。定义该参数:
# 寻找最近邻的数量
num_neighbors = 3
(4) 定义一个随机数据点,这个数据点不包括在输入数据中:
# 输入数据点
input_point = [2.6, 1.7]
(5) 看看数据的分布,并将其画出:
# 画出数据点
plt.figure()
plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k')
(6) 为了寻找最近邻,用适合的参数定义一个NearestNeighbors
对象,并用输入数据训练该对象:
# 建立最近邻模型
knn = NearestNeighbors(n_neighbors=num_neighbors,
algorithm='ball_tree').fit(X)
(7) 计算输入点与输入数据中所有点的距离:
distances, indices = knn.kneighbors(input_point)
(8) 打印出k 个最近邻:
# 打印k个最近邻点
print "\nk nearest neighbors"
for rank, index in enumerate(indices[0][:num_neighbors]):
print str(rank+1) + " -->", X[index]
indices数组是一个已排序的数组,因此仅需要解析它并打印出数据
点。
(9) 画出输入数据点,并突出显示k 个最近邻:
# 画出最近邻点
plt.figure()
plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k')
plt.scatter(X[indices][0][:][:,0], X[indices][0][:][:,1],
marker='o', s=150, color='k', facecolors='none')
plt.scatter(input_point[0], input_point[1],
marker='x', s=150, color='k', facecolors='none')
plt.show()
(10) 执行代码,可以在命令行工具中看到如图5-3所示的输出。
图 5-3
(11) 输入数据点的绘制情况如图5-4所示。
图 5-4
(12) 如图5-5所示的输出图像展示了测试数据点和3个最近邻的位置。
图 5-5
5.5 构建一个KNN分类器
KNN(k-nearest neighbors)是用k 个最近邻的训练数据集来寻找未
知对象分类的一种算法。如果希望找到未知数据点属于哪个类,可以
找到KNN并做一个多数表决。接下来学习如何构建这样的分类器。
5.5.1 详细步骤
(1) 创建一个Python文件,导入以下程序包:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from sklearn import neighbors, datasets
from utilities import load_data
(2) 我们将用到data_nn_classifier.txt文件作为输入数据。加载该
输入数据:
# 加载输入数据
input_file = 'data_nn_classifier.txt'
data = load_data(input_file)
X, y = data[:,:-1], data[:,-1].astype(np.int)
前两列包含输入数据,最后一列包含标签,因此分别将其用X和y两个
变量表示。
(3) 将输入数据可视化:
# 画出输入数据
plt.figure()
plt.title('Input datapoints')
markers = '^sov<>hp'
mapper = np.array([markers[i] for i in y])
for i in range(X.shape[0]):
plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],
s=50, edgecolors='black', facecolors='none')
迭代所有的数据点,并用合适的标记区分不同的类。
(4) 为了构建分类器,需要指定我们考虑的最近邻的个数。定义该参
数如下:
# 设置最近邻的个数
num_neighbors = 10
(5) 为了将边界可视化,需要定义一个网格,用这个网格评价该分类
器。定义网格步长如下:
# 定义网格步长
h = 0.01
(6) 接下来创建KNN分类器并进行训练:
# 创建KNN分类器模型并进行训练
classifier = neighbors.KNeighborsClassifier(num_neighbors,
weights='distance')
classifier.fit(X, y)
(7) 生成一个网格来画出边界。对网格做如下定义:
# 建立网格来画出边界
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
x_grid, y_grid = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
(8) 评价分类器对所有点的输出:
# 计算网格中所有点的输出
predicted_values = classifier.predict(np.c_[x_grid.ravel(),
y_grid.ravel()])
(9) 将其画出:
# 在图中画出计算结果
predicted_values = predicted_values.reshape(x_grid.shape)
plt.figure()
plt.pcolormesh(x_grid, y_grid, predicted_values, cmap=cm.Pastel1)
(10) 我们画出了彩色网格,现在要将训练数据点覆盖在其上,查看这
些点与边界的关系在哪里:
# 在图中画出训练数据点
for i in range(X.shape[0]):
plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],
s=50, edgecolors='black', facecolors='none')
plt.xlim(x_grid.min(), x_grid.max())
plt.ylim(y_grid.min(), y_grid.max())
plt.title('k nearest neighbors classifier boundaries')
(11) 接下来测试数据点,查看分类器能否准确分类。定义并画出它:
# 测试输入数据点
test_datapoint = [4.5, 3.6]
plt.figure()
plt.title('Test datapoint')
for i in range(X.shape[0]):
plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],
s=50, edgecolors='black', facecolors='none')
plt.scatter(test_datapoint[0], test_datapoint[1], marker='x',
linewidth=3, s=200, facecolors='black')
(12) 用以下模型提取KNN:
# 提取KNN分类结果
dist, indices = classifier.kneighbors(test_datapoint)
(13) 画出KNN并突出显示:
# 画出KNN分类结果
plt.figure()
plt.title('k nearest neighbors')
for i in indices:
plt.scatter(X[i, 0], X[i, 1], marker='o',
linewidth=3, s=100, facecolors='black')
plt.scatter(test_datapoint[0], test_datapoint[1], marker='x',
linewidth=3, s=200, facecolors='black')
for i in range(X.shape[0]):
plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],
s=50, edgecolors='black', facecolors='none')
plt.show()
(14) 在命令行工具中打印分类器输出结果:
print "Predicted output:", classifier.predict(test_datapoint)[0]
(15) 执行代码,第一幅输出图像展示了输入数据的分布,如图5-6所
示。
图 5-6
(16) 第二幅输出图像展示了用KNN分类器获得的边界,如图5-7所示。
图 5-7
(17) 第三幅输出图像展示了测试数据点的位置,如图5-8所示。
图 5-8
(18) 第四幅输出图像展示了10个最近邻的位置,如图5-9所示。
图 5-9
5.5.2 工作原理
KNN分类器存储了所有可用的数据点,并根据相似度指标来对新的数据
点进行分类。这个相似度指标通常以距离函数的形式度量。该算法是
一个非参数化技术,也就是说它在进行计算前并不需要找出任何隐含
的参数。我们只需要选择k的值。
一旦找出KNN,就会做一个多数表决。一个新数据点通过KNN的多数表
决来进行分类。这个数据点会被分到和KNN最常见的类中。如果将k的
值设置为1,那么这就变成了一个最近邻分类器,在该分类器中,将
数据点分类到训练数据集中其最近邻所属的哪一类。更多详细内容可
查看
http://www.fon.hum.uva.nl/praat/manual/kNN_classifiers_1__Wha
t_is_a_kNN_classifier_.html。
5.6 构建一个KNN回归器
我们已经知道如何用KNN算法构建分类器了,下面用KNN算法构建回归
器。接下来学习如何构建KNN回归器。
5.6.1 详细步骤
(1) 创建一个Python文件,导入以下程序包:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import neighbors
(2) 生成一些服从正态分布的样本数据:
# 生成样本数据
amplitude = 10
num_points = 100
X = amplitude * np.random.rand(num_points, 1) - 0.5 * amplitude
(3) 下面在数据中加入一些噪声来引入一些随机性。加入噪声的目的
在于测试算法是否能忽略噪声,并仍然很健壮地运行函数:
# 计算目标并添加噪声
y = np.sinc(X).ravel()
y += 0.2 * (0.5 - np.random.rand(y.size))
(4) 将数据可视化:
# 画出输入数据图形
plt.figure()
plt.scatter(X, y, s=40, c='k', facecolors='none')
plt.title('Input data')
(5) 我们在上面生成了一些数据,并针对这些点做了连续函数的评
价。接下来定义更密集的网格点:
# 用输入数据10倍的密度创建一维网格
x_values = np.linspace(-0.5*amplitude, 0.5*amplitude,
10*num_points)[:, np.newaxis]
定义这个更密集的网格点,因为我们希望针对这些点评价回归器,并
查看它和函数的相似程度。
(6) 定义最近邻的个数:
# 定义最近邻的个数
n_neighbors = 8
(7) 用之前定义的参数初始化并训练KNN回归器:
# 定义并训练回归器
knn_regressor = neighbors.KNeighborsRegressor(n_neighbors,
weights='distance')
y_values = knn_regressor.fit(X, y).predict(x_values)
(8) 将输入数据和输出数据交叠在一起,以查看回归器的性能表现:
plt.figure()
plt.scatter(X, y, s=40, c='k', facecolors='none', label='input
data')
plt.plot(x_values, y_values, c='k', linestyle='--',
label='predicted values')
plt.xlim(X.min() - 1, X.max() + 1)
plt.ylim(y.min() - 0.2, y.max() + 0.2)
plt.aopxis('tight')
plt.legend()
plt.title('K Nearest Neighbors Regressor')
plt.show()
(9) 执行代码,第一幅输出图像展示了输入数据的分布,如图5-10所
示。
图 5-10
(10) 第二幅图像展示了用KNN回归器预测到的值,如图5-11所示。
图 5-11
5.6.2 工作原理
回归器的目标是预测连续值的输出。这个例子中并没有固定数量的输
出类别,仅有一组实际输出值,我们希望回归器可以预测未知数据点
的输出值。这个例子中用到一个sinc函数来演示KNN回归器,该函数
也被称为基本正弦函数。sinc函数的定义如下:
  sinc(x) = sin(x)/x  (x != 0)
= 1 (x = 0)
当 x 为0时,sin(x)/x 变成0/0不定式,因此需要计算该函数在 x 无
限趋近于0时函数的极限。我们用到了一组值做训练,并且定义了一个
更密集的网格点来进行测试。正如在之前的图中看到的,输出曲线接
近于训练输出。
5.7 计算欧氏距离分数
现在已经有了充足的机器学习流水线和最近邻分类器的背景知识,下
面可以开始推荐引擎的探讨了。为了构建一个推荐引擎,需要定义相
似度指标,以便找到与数据库中特定用户相似的用户。欧氏距离分数
就是一个这样的指标,可以计算两个数据点之间的欧几里得距离。下
面将重点讨论电影推荐引擎。接下来学习如何计算两个用户间的欧几
里得分数。
详细步骤
(1) 创建一个Python文件,导入以下程序包:
import json import numpy as np
(2) 定义一个用于计算两个用户之间的欧几里得分数的函数。第一步
先判断用户是否在数据库中出现:
# 计算user1和user2的欧氏距离分数
def euclidean_score(dataset, user1, user2):
if user1 not in dataset:
raise TypeError('User ' + user1 + ' not present in the
dataset')
if user2 not in dataset:
raise TypeError('User ' + user2 + ' not present in the
dataset')
(3) 为了计算分数,需要提取两个用户均评过分的电影:
# 提取两个用户均评过分的电影
rated_by_both = {}
for item in dataset[user1]:
if item in dataset[user2]:
rated_by_both[item] = 1
(4) 如果没有两个用户共同评过分的电影,则说明这两个用户之间没
有相似度(至少根据数据库中的评分信息无法计算出来):
# 如果两个用户都没评分过,得分为0
if len(rated_by_both) == 0:
return 0
(5) 对于每个共同评分,只计算平方和的平方根,并将该值归一化,
使得评分取值在0到1之间:
squared_differences = []
for item in dataset[user1]:
if item in dataset[user2]:
squared_differences.append(np.square(dataset[user1]
[item] - dataset[user2][item]))
return 1 / (1 + np.sqrt(np.sum(squared_differences)))
如果评分相似,那么平方和的差别就会很小,因此评分就会变得很
高,这也是我们希望指标达到的效果。
(6) 加载数据文件中的movie_ratings.json文件:
if __name__=='__main__':
data_file = 'movie_ratings.json'
with open(data_file, 'r') as f:
data = json.loads(f.read())
(7) 假定两个随机用户,计算其欧氏距离分数:
user1 = 'John Carson'
user2 = 'Michelle Peterson'
print "\nEuclidean score:"
print euclidean_score(data, user1, user2)
(8) 运行该代码,可以看到欧氏距离分数显示在命令行工具中。
5.8 计算皮尔逊相关系数
欧氏距离分数是一个非常好的指标,但它也有一些缺点。因此,皮尔
逊相关系数常用于推荐引擎。接下来学习如何计算皮尔逊相关系数。
详细步骤
(1) 创建一个Python文件,导入以下程序包:
import json
import numpy as np
(2) 接下来定义一个用于计算两个用户之间的皮尔逊相关度系数的函
数。第一步先判断用户是否在数据库中出现:
# 计算user1和user2的皮尔逊相关系数
def pearson_score(dataset, user1, user2):
if user1 not in dataset:
raise TypeError('User ' + user1 + ' not present in the
dataset')
if user2 not in dataset:
raise TypeError('User ' + user2 + ' not present in the
dataset')
(3) 提取两个用户均评过分的电影:
# 提取两个用户均评过分的电影
rated_by_both = {}
for item in dataset[user1]:
if item in dataset[user2]:
rated_by_both[item] = 1
num_ratings = len(rated_by_both)
(4) 如果没有两个用户共同评过分的电影,则说明这两个用户之间没
有相似度,此时返回0:
# 如果两个用户都没有评分,得分为0
if num_ratings == 0:
return 0
(5) 计算相同评分电影的平方值之和:
# 计算相同评分电影的平方值之和
user1_sum = np.sum([dataset[user1][item] for item in
rated_by_both])
user2_sum = np.sum([dataset[user2][item] for item in
rated_by_both])
(6) 计算所有相同评分电影的评分的平方和:
# 计算所有相同评分电影的评分的平方和
user1_squared_sum = np.sum([np.square(dataset[user1][item])
for item in rated_by_both])
user2_squared_sum = np.sum([np.square(dataset[user2][item])
for item in rated_by_both])
(7) 计算数据集的乘积之和:
# 计算数据集的乘积之和
product_sum = np.sum([dataset[user1][item] * dataset[user2]
[item] for item in rated_by_both])
(8) 计算皮尔逊相关系数需要的各种元素:
# 计算皮尔逊相关度
Sxy = product_sum - (user1_sum * user2_sum / num_ratings)
Sxx = user1_squared_sum - np.square(user1_sum) / num_ratings
Syy = user2_squared_sum - np.square(user2_sum) / num_ratings
(9) 考虑分母为0的情况:
if Sxx * Syy == 0:
return 0
(10) 如果一切正常,返回皮尔逊相关系数:
return Sxy / np.sqrt(Sxx * Syy)
(11) 定义main函数并计算两个用户之间的皮尔逊相关系数:
if __name__=='__main__':
data_file = 'movie_ratings.json'
with open(data_file, 'r') as f:
data = json.loads(f.read())
user1 = 'John Carson'
user2 = 'Michelle Peterson'
print "\nPearson score:"
print pearson_score(data, user1, user2)
(12) 运行该代码,可以看到皮尔逊相关系数显示在命令行工具中。
5.9 寻找数据集中的相似用户
构建推荐引擎中一个非常重要的任务是寻找相似的用户,也就是说,
为某位用户生成的推荐信息可以同时推荐给与其相似的用户。接下来
学习如何寻找相似用户。
详细步骤
(1) 创建一个Python文件,导入以下程序包:
import json
import numpy as np
from pearson_score import pearson_score
(2) 定义一个函数,用于寻找与输入用户相似的用户。该函数有3个输
入参数:数据库、输入用户和寻找的相似用户个数。第一步是查看该
用户是否包含在数据库中。如果用户已经存在,则需要计算该用户与
数据库中其他所有用户的皮尔逊相关系数:
# 寻找特定数量的与输入用户相似的用户
def find_similar_users(dataset, user, num_users):
if user not in dataset:
raise TypeError('User ' + user + ' not present in the
dataset')
# 计算所有用户的皮尔逊相关度
scores = np.array([[x, pearson_score(dataset, user, x)] for x
in dataset if user != x])
(3) 将这些得分按照降序排列:
# 评分按照第二列排序
scores_sorted = np.argsort(scores[:, 1])
# 评分按照降序排列
scored_sorted_dec = scores_sorted[::-1]
(4) 提取出 k 个最高分并返回:
# 提取出k个最高分
top_k = scored_sorted_dec[0:num_users]
return scores[top_k]
(5) 定义main函数,加载输入数据库:
if __name__=='__main__':
data_file = 'movie_ratings.json'
with open(data_file, 'r') as f:
data = json.loads(f.read())
(6) 我们希望查找3个与John Carson相似的用户。用以下步骤实
现:
user = 'John Carson'
print "\nUsers similar to " + user + ":\n"
similar_users = find_similar_users(data, user, 3)
print "User\t\t\tSimilarity score\n"
for item in similar_users:
print item[0], '\t\t', round(float(item[1]), 2)
(7) 运行该代码,可以看到命令行工具中显示了3个用户,如图5-12所
示。
图 5-12
5.10 生成电影推荐
至此,我们已经创建了一个推荐引擎的各个不同组成部分,接下来生
成一个实际的电影推荐系统。本节将用到前面各节中构建的函数来构
建电影推荐引擎,接下来看看具体如何实现。
详细步骤
(1) 创建一个Python文件,导入以下程序包:
import json
import numpy as np
from euclidean_score import euclidean_score
from pearson_score import pearson_score
from find_similar_users import find_similar_users
(2) 定义一个为给定用户生成电影推荐的函数。首先检查该用户是否
存在于数据库中:
# 为给定用户生成电影推荐
def generate_recommendations(dataset, user):
if user not in dataset:
raise TypeError('User ' + user + ' not present in the
dataset')
(3) 计算该用户与数据库中其他用户的皮尔逊相关系数:
total_scores = {}
similarity_sums = {}
for u in [x for x in dataset if x != user]:
similarity_score = pearson_score(dataset, user, u)
if similarity_score <= 0:
continue
(4) 找到还未被该用户评分的电影:
for item in [x for x in dataset[u] if x not in
dataset[user] or dataset[user][x] == 0]:
total_scores.update({item: dataset[u][item] *
similarity_score})
similarity_sums.update({item: similarity_score})
(5) 如果该用户看过数据库中所有的电影,那就不能为用户推荐电
影。对该条件做如下处理:
if len(total_scores) == 0:
return ['No recommendations possible']
(6) 有了皮尔逊相关系数列表,下面生成一个电影评分标准化列表:
# 生成一个电影评分标准化列表
movie_ranks = np.array([[total/similarity_sums[item], item]
for item, total in total_scores.items()])
(7) 对皮尔逊相关系数进行降序排列:
# 根据第一列对皮尔逊相关系数进行降序排列
movie_ranks = movie_ranks[np.argsort(movie_ranks[:, 0])[::-1]]
(8) 最后提取出推荐的电影:
# 提取出推荐的电影
recommendations = [movie for _, movie in movie_ranks]
return recommendations
(9) 定义main函数,加载数据集:
if __name__=='__main__':
data_file = 'movie_ratings.json'
with open(data_file, 'r') as f:
data = json.loads(f.read())
(10) 为Michael Henry生成推荐:
user = 'Michael Henry'
print "\nRecommendations for " + user + ":"
movies = generate_recommendations(data, user)
for i, movie in enumerate(movies):
print str(i+1) + '. ' + movie
(11) 用户John Carson看过所有电影,因此在为他推荐电影时,应
该显示0推荐。来看看程序运行结果是否正确:
user = 'John Carson'
print "\nRecommendations for " + user + ":"
movies = generate_recommendations(data, user)
for i, movie in enumerate(movies):
print str(i+1) + '. ' + movie
(12) 运行该代码,可以在命令行工具中看到如图5-13所示的结果。
图 5-13

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

___Y1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值