当前位置: 首页 > article >正文

从0开始学习机器学习--Day32--推荐系统作业

题目:给用户推荐电影

代码:

import numpy as np
import matplotlib.pyplot as plt
import scipy.io as sio
from scipy.optimize import minimize
def serialize(X, theta):# 序列化,因为后续优化方法对参数的要求为一维

    return np.append(X.flatten(), theta.flatten())

def deserialize(paramers, nm, nu, nf):
    X = paramers[:nm*nf].reshape(nm, nf)#reshape() 里写nm是因为X代表的是电影数据
    theta = paramers[nm*nf:].reshape(nu, nf)#reshape() 里写nu是因为theta代表的是用户数据
    return X, theta

def cost_function(paramers, Y, R, nm, nu, nf, lamda):#代价函数
    X, theta = deserialize(paramers, nm, nu, nf)
    error = 0.5*np.square((X@theta.T-Y)*R).sum()# 乘以R是因为公式里要求是对电影进行评分了的用户
    reg_1 = 0.5*lamda*np.square(X).sum()
    reg_2 = 0.5*lamda*np.square(theta).sum()
    return error + reg_1 + reg_2

def cost_gradient(paramers, Y, R, nm, nu, nf, lamda):# 梯度下降
    X, theta = deserialize(paramers, nm, nu, nf)
    X_gradient = ((X@theta.T-Y)*R)@theta + lamda*X
    theta_gradient = ((X@theta.T-Y)*R).T@X + lamda*theta #这里用转置是遵守矩阵乘法法则
    return serialize(X_gradient, theta_gradient)

def normalize_ratings(Y, R):#均值归一化
    Y_mean = (Y.sum(axis=1)/R.sum(axis=1)).reshape(-1, 1)# R的作用依然是为了定位做了评分的用户,这里reshape成二维矩阵是为了后续方便计算
    Y_norm = (Y-Y_mean)*R
    return Y_norm, Y_mean

mat = sio.loadmat('./data/ex8_movies.mat')
print(mat.keys())
Y, R = mat['Y'], mat['R']#Y存放的是用户对电影的评分,R存放的是判断用户是否做了评分
print(Y.shape, R.shape)

paramer_mat = sio.loadmat('./data/ex8_movieParams.mat')
print(paramer_mat.keys())
X = paramer_mat['X']
theta = paramer_mat['Theta']
nu, nm, nf = paramer_mat['num_users'], paramer_mat['num_movies'], paramer_mat['num_features']
print(X.shape, theta.shape)
print(nu, nm, nf)
nu = int(nu)#将矩阵转换为整数,方便后续计算
nm = int(nm)
nf = int(nf)
print(nu, nm, nf)

users = 4
movies = 5
features = 3
X_sub = X[:movies, :features]#取子集进行测试
theta_sub = theta[:users, :features]
Y_sub = Y[:movies, :users]
R_sub = R[:movies, :users]
cost1 = cost_function(serialize(X_sub, theta_sub), Y_sub, R_sub, movies, users, features, lamda=0)
print(cost1)
cost2 = cost_function(serialize(X_sub, theta_sub), Y_sub, R_sub, movies, users, features, lamda=0.5)
print(cost2)

#添加个体用户,修改部分数据测试一下算法
new_ratings = np.zeros((nm, 1))
new_ratings[9] = 5
new_ratings[66] = 5
new_ratings[96] = 5
new_ratings[121] = 4
new_ratings[148] = 4
new_ratings[285] = 3
new_ratings[490] = 4
new_ratings[599] = 4
new_ratings[643] = 4
new_ratings[958] = 5
new_ratings[1117] = 3

y = np.c_[Y, new_ratings]
r = np.c_[R, new_ratings!=0]#!=0是根据判断是否不为0返回True或False,因为这里只需要作是否评分的判断
print(y.shape)

Y_norm, Y_mean = normalize_ratings(Y, R)

#参数初始化
X = np.random.random((nm, nf))
theta =np.random.random((nu, nf))
paramers = serialize(X, theta)
lamda = 5

#模型训练
res = minimize(fun=cost_function,
         x0=paramers,
         args=(Y_norm, R, nm, nu, nf, lamda),
         method='TNC',
         jac=cost_gradient,
         options={'maxiter':100})

paramers_fit = res.x#训练好的参数
fit_X, fit_theta = deserialize(paramers_fit, nm, nu, nf)

#预测
y_pred =fit_X@fit_theta.T
y_pred = y_pred[:, -1] - Y_mean.flatten()
index = np.argsort(y_pred)#按照从小到大顺序排列
print(index[:10])#输出排名靠前的,看看模型训练后认为预测样本会喜欢什么样的电影

movie = []
with open('./data/movie_ids.txt', 'r', encoding='latin 1') as f:
    for line in f:
        tokens = line.strip().split(' ')#用空格来区分要分开的元素
        movie.append(' '.join(tokens[1:]))#不要第一个序号

print(len(movie))

for i in range(10):
    print(index[i], movie[index[i]], y_pred[index[i]])

输出:

dict_keys(['__header__', '__version__', '__globals__', 'Y', 'R'])
(1682, 943) (1682, 943)
dict_keys(['__header__', '__version__', '__globals__', 'X', 'Theta', 'num_users', 'num_movies', 'num_features'])
(1682, 10) (943, 10)
[[943]] [[1682]] [[10]]
943 1682 10
22.224603725685675
25.264421231881858
(1682, 944)
[407 123 646  58 693 960 284 653 515  19]
1682
407 Close Shave, A (1995) -5.662341746658825
123 Lone Star (1996) -5.584866704894469
646 Ran (1985) -5.439118447769447
58 Three Colors: Red (1994) -5.3926736164854745
693 Persuasion (1995) -5.327031454117134
960 Orlando (1993) -5.273157229690771
284 Secrets & Lies (1996) -5.272872567424451
653 Chinatown (1974) -5.272511152020712
515 Local Hero (1983) -5.261092245742169
19 Angels and Insects (1995) -5.2378273984105785

小结:均值归一化的必要性:预测用户的评分在结果出来前是不知道的,不作归一化会使某些数据过大是结果偏移;在做最终预测前可以人造一个小数据用于检测算法可用性,避免每次都要运行整哥算法,方便进行优化。

作业订正:https://blog.csdn.net/weixin_43490087/article/details/139842732


http://www.kler.cn/a/404535.html

相关文章:

  • 网络原理(一):应用层自定义协议的信息组织格式 初始 HTTP
  • LabVIEW实现TCP/IP通信
  • Windows 软件之 FFmpeg
  • 深入理解下oracle 11g block组成
  • 视觉经典神经网络与复现:深入解析与实践指南
  • 搜索插入位置
  • 统计班级中的说谎者(字节青训)
  • LLM2CLIP:使用大语言模型提升CLIP的文本处理,提高长文本理解和跨语言能力
  • 算法学习笔记(一):滑动窗口和双指针
  • RT_Thread内核源码分析(三)——线程
  • 分布式专题-Redis核心数据结构精讲
  • 《智能指针:明晰资源所有权的 C++利器》
  • 最新Kali安装详细版教程(附安装包,傻瓜式安装教程)
  • String、StringBuilder 和 StringBuffer 的区别
  • shell 接收长参数
  • ROSSERIAL与Arduino IDE交叉开发(UBUNTU环境,包含ESP32、arduino nano)
  • 深入JMeter核心引擎:揭秘JmeterEngine、StandardJmeterEngine、ClientJmeterEngine与Remote的奥秘
  • 基于Matlab的变压器仿真模型的建模方法(3):单相双绕组变压器的拉氏变换象函数模型及其仿真模型
  • DockerFile与容器构建技术
  • Redis的String类型和Java中的String类在底层数据结构上有一些异同点
  • 大数据面试题每日练习--Hadoop是什么?它由哪些核心组件组成?
  • reactflow 中 useNodesState 模块作用
  • 如何在 RK3568 Android 11 系统上排查以太网问题
  • ESP8266 STA模式TCP服务器 电脑手机网络调试助手
  • Ubuntu问题 -- 允许ssh使用root用户登陆
  • 界面控件DevExpress Blazor UI v24.1新版亮点:发布全新文件输入等组件