import numpy as np
import torch
from recsys_utils import *
X, W, b, num_movies, num_features, num_users = load_precalc_params_small()
Y, R = load_ratings_small()
print("Y的形状", Y.shape, "R的形状", R.shape)
print("X的形状", X.shape)
print("W的形状", W.shape)
print("b的形状", b.shape)
print("特征数量", num_features)
print("电影数量", num_movies)
print("用户数量", num_users)
tsmean = np.mean(Y[0, R[0, :].astype(bool)])
print(f"电影1的平均评分为: {tsmean:0.3f} / 5")
def cofi_cost_func(X, W, b, Y, R, lambda_):
"""
计算协同过滤的损失值,包含预测误差和正则化项
参数:
X: 电影特征矩阵 (电影数, 特征数)
W: 用户参数矩阵 (用户数, 特征数)
b: 用户偏置向量 (1, 用户数)
Y: 实际评分矩阵 (电影数, 用户数)
R: 指示矩阵 (电影数, 用户数),标记有评分的位置
lambda_: 正则化系数,控制过拟合
返回:
J: 总损失值
"""
nm, nu = Y.shape
J = 0
predictions = X @ W.T + b
errors = (predictions - Y) * R
J = 0.5 * np.sum(errors **2)
J += (lambda_ / 2) * (np.sum(X** 2) + np.sum(W ** 2))
return J
from public_tests import *
test_cofi_cost_func(cofi_cost_func)
num_users_r = 4
num_movies_r = 5
num_features_r = 3
X_r = X[:num_movies_r, :num_features_r]
W_r = W[:num_users_r, :num_features_r]
b_r = b[0, :num_users_r].reshape(1,-1)
Y_r = Y[:num_movies_r, :num_users_r]
R_r = R[:num_movies_r, :num_users_r]
J = cofi_cost_func(X_r, W_r, b_r, Y_r, R_r, 0);
print(f"成本值: {J:0.2f}")
J = cofi_cost_func(X_r, W_r, b_r, Y_r, R_r, 1.5);
print(f"带正则化的成本值: {J:0.2f}")
def cofi_cost_func_v(X, W, b, Y, R, lambda_):
"""
功能同cofi_cost_func,但使用PyTorch张量操作,支持自动求导
参数:
X: 电影特征张量 (电影数, 特征数)
W: 用户参数张量 (用户数, 特征数)
b: 用户偏置张量 (1, 用户数)
Y: 实际评分张量 (电影数, 用户数)
R: 指示张量 (电影数, 用户数)
lambda_: 正则化系数
返回:
J: 总损失张量
"""
j = (torch.matmul(X, W.t()) + b - Y) * R
J = 0.5 * torch.sum(j**2) + (lambda_/2) * (torch.sum(X**2) + torch.sum(W**2))
return J
X_r_torch = torch.tensor(X_r, dtype=torch.float64)
W_r_torch = torch.tensor(W_r, dtype=torch.float64)
b_r_torch = torch.tensor(b_r, dtype=torch.float64)
Y_r_torch = torch.tensor(Y_r, dtype=torch.float64)
R_r_torch = torch.tensor(R_r, dtype=torch.float64)
J = cofi_cost_func_v(X_r_torch, W_r_torch, b_r_torch, Y_r_torch, R_r_torch, 0);
print(f"向量化成本值: {J.item():0.2f}")
J = cofi_cost_func_v(X_r_torch, W_r_torch, b_r_torch, Y_r_torch, R_r_torch, 1.5);
print(f"带正则化的向量化成本值: {J.item():0.2f}")
movieList, movieList_df = load_Movie_List_pd()
my_ratings = np.zeros(num_movies)
my_ratings[2700] = 5
my_ratings[2609] = 2
my_ratings[929] = 5
my_ratings[246] = 5
my_ratings[2716] = 3
my_ratings[1150] = 5
my_ratings[382] = 2
my_ratings[366] = 5
my_ratings[622] = 5
my_ratings[988] = 3
my_ratings[2925] = 1
my_ratings[2937] = 1
my_ratings[793] = 5
my_rated = [i for i in range(len(my_ratings)) if my_ratings[i] > 0]
print('\n新用户评分:\n')
for i in range(len(my_ratings)):
if my_ratings[i] > 0 :
print(f'为 {movieList_df.loc[i,"title"]} 打了 {my_ratings[i]} 分');
Y, R = load_ratings_small()
Y = np.c_[my_ratings, Y]
R = np.c_[(my_ratings != 0).astype(int), R]
Ynorm, Ymean = normalizeRatings(Y, R)
num_movies, num_users = Y.shape
num_features = 100
torch.manual_seed(1234)
W = torch.nn.Parameter(torch.randn((num_users, num_features), dtype=torch.float64))
X = torch.nn.Parameter(torch.randn((num_movies, num_features), dtype=torch.float64))
b = torch.nn.Parameter(torch.randn((1, num_users), dtype=torch.float64))
optimizer = torch.optim.Adam([X, W, b], lr=1e-1)
Ynorm_tensor = torch.tensor(Ynorm, dtype=torch.float64)
R_tensor = torch.tensor(R, dtype=torch.float64)
iterations = 200
lambda_ = 1
for iter in range(iterations):
optimizer.zero_grad()
cost_value = cofi_cost_func_v(X, W, b, Ynorm_tensor, R_tensor, lambda_)
cost_value.backward()
optimizer.step()
if iter % 20 == 0:
print(f"第 {iter} 次迭代的训练损失: {cost_value.item():0.1f}")
with torch.no_grad():
p = torch.matmul(X, W.t()) + b
pm = p.numpy() + Ymean
my_predictions = pm[:, 0]
ix = np.argsort(my_predictions)[::-1]
print("\n推荐电影:")
count = 0
for i in range(len(ix)):
j = ix[i]
if j not in my_rated:
print(f'预测评分为 {my_predictions[j]:0.2f} 的电影:{movieList[j]}')
count += 1
if count >= 17:
break
print('\n\n原始评分与预测评分对比:\n')
for i in range(len(my_ratings)):
if my_ratings[i] > 0:
print(f'原始评分 {my_ratings[i]}, 预测评分 {my_predictions[i]:0.2f} 的电影:{movieList[i]}')
filter_mask = (movieList_df["number of ratings"] > 20)
movieList_df["pred"] = my_predictions
movieList_df = movieList_df.reindex(columns=["pred", "mean rating", "number of ratings", "title"])
print("\n推荐电影列表(按平均评分排序):")
print(movieList_df.loc[ix[:300]].loc[filter_mask].sort_values("mean rating", ascending=False))