吴恩达机器学习 第三课 week2 推荐算法(下)

目录

01 学习目标

02 基于内容的过滤算法

03 实现“电影推荐系统”

3.1 问题描述

3.2 算法实现

04 大项目(数据很大)的推荐方法※

4.1 方法原理

4.2 实施示例

05 总结


01 学习目标

     (1)理解基于内容的过滤算法(Content-Based Filtering Algorithm)推荐原理

     (2)利用基于内容的过滤算法构建“电影推荐系统”

     (3)了解大项目(数据集很大)的推荐方法

02 基于内容的过滤算法

      基于内容的过滤算法,是采用神经网络架构提取用户特征物品特征后向用户推荐相似物品。用户特征包括:年龄、性别、国家、喜好和评分等等,物品(比如电影)特征包括:年份、类型、影评及所有评价用户的平均年龄等等。

       Content-based filtering的原理如下图所示:

      首先(准备内容),准备两组数据,一组是用户内容Xu,一组是物品的内容Xm。(需说明,Xu数组内均为工程特征,即经过处理后的内容,而Xm数组由原始数据和工程特征组合构成)

      然后(提取特征),定义神经网络模型,其架构为两个并联的模型,将Xu和Xm作为输入,可分别输出用户特征Vu和物品特征Vm。

      接着(用户喜好预测),利用多元线性回归模型,预测用户对所有物品的评分,但Content-based filtering中的回归模型是没有偏置项b的,如下:

\textbf{Y}_j^{(i)}=\textbf{V}_u^{(j)}\cdot \textbf{V}_m^{(i)}

式中,Y为预测结果(分数),i为物品的序数,j为用户的序数。

      现在(相似度计算),根据物品的特征计算物品之间的相似度,物品相似度有很多计算方法,这里根据课程采用欧氏距离平方:

distance=||\textbf{V}_m^{(k)}-\textbf{V}_m^{(i)}||^2=\sum_{l=1}^{n}(v_{ml}^{(k)}-v_{ml}^{(i)})^2

        最后(生成推荐),有了用户对每个物品的预测分数,也有了物品相似度,就可以向用户推荐他喜爱之物的相似物品了。

03 实现“电影推荐系统”

3.1 问题描述

       这次,我们依然采用从GroupLens网站的 MovieLens 栏目下载电影数据集来构建“电影推荐系统”。我们的任务是:根据一位用户的行为,预测其喜好,并为其推荐电影。

3.2 算法实现

    (1)导入模块

# 导包
import pickle
import csv
import numpy as np
import numpy.ma as ma
from numpy import genfromtxt
from collections import defaultdict
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Model
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.model_selection import train_test_split
import tabulate
pd.set_option("display.precision", 1)
# Import Lambda layer at the beginning if not already imported
from tensorflow.keras.layers import Lambda

      (2)读取数据

       ① 由于数据较多,定义函数读取:

# 定义函数,读取数据
def load_data():item_train = genfromtxt('./data/content_item_train.csv', delimiter=',')user_train = genfromtxt('./data/content_user_train.csv', delimiter=',')y_train    = genfromtxt('./data/content_y_train.csv', delimiter=',')with open('./data/content_item_train_header.txt', newline='') as f:item_features = list(csv.reader(f))[0]with open('./data/content_user_train_header.txt', newline='') as f:user_features = list(csv.reader(f))[0]item_vecs = genfromtxt('./data/content_item_vecs.csv', delimiter=',')movie_dict = defaultdict(dict)count = 0with open('./data/content_movie_list.csv', newline='') as csvfile:reader = csv.reader(csvfile, delimiter=',', quotechar='"')for line in reader:if count == 0: count +=1  #skip headerelse:count +=1movie_id = int(line[0])  movie_dict[movie_id]["title"] = line[1]  movie_dict[movie_id]["genres"] =line[2]  with open('./data/content_user_to_genre.pickle', 'rb') as f:user_to_genre = pickle.load(f)return(item_train, user_train, y_train, item_features, user_features, item_vecs, movie_dict, user_to_genre)

       ② 读取数据:

# 导入原始数据
item_train, user_train, y_train, item_features, user_features, item_vecs, movie_dict, user_to_genre = load_data()num_user_features = user_train.shape[1] - 3  # 原始数据移除用户ID、评论总数、平均评分三列
num_item_features = item_train.shape[1] - 1  # 原始数据移除电影ID列
uvs = 3  # 用户特征向量开始索引
ivs = 3  # 物品特征向量开始索引
u_s = 3  # 训练集中用户内容开始列索引
i_s = 1  # 训练集中物品内容开始列索引
scaledata = True  # 应用标准化尺度缩放

     (3)数据处理、拆分

       ① 数据标准化:

# 原始数据标准化:均值为0、标准差为1
if scaledata:item_train_save = item_trainuser_train_save = user_trainscalerItem = StandardScaler()scalerItem.fit(item_train)item_train = scalerItem.transform(item_train)scalerUser = StandardScaler()scalerUser.fit(user_train)user_train = scalerUser.transform(user_train)

       ② 数据拆分为训练集和测试集:

# 数据集拆分,训练集:测试集=8:2
item_train, item_test = train_test_split(item_train, train_size=0.80, shuffle=True, random_state=1)
user_train, user_test = train_test_split(user_train, train_size=0.80, shuffle=True, random_state=1)
y_train, y_test       = train_test_split(y_train,    train_size=0.80, shuffle=True, random_state=1)

       ③ 数据集的数据归一化:

# 训练集、测试集数据归一化
scaler = MinMaxScaler((-1, 1))
scaler.fit(y_train.reshape(-1, 1))
ynorm_train = scaler.transform(y_train.reshape(-1, 1))
ynorm_test = scaler.transform(y_test.reshape(-1, 1))

      (4)人工神经网络模型提取特征

       ① 定义模型架构:

# 定义神经网络模型
num_outputs = 32
tf.random.set_seed(1)
user_NN = tf.keras.models.Sequential([  tf.keras.layers.Dense(256, activation='relu'),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dense(num_outputs) 
])item_NN = tf.keras.models.Sequential([     tf.keras.layers.Dense(256, activation='relu'),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dense(num_outputs)  
])# 创建用户输入并指向基础网络
input_user = tf.keras.layers.Input(shape=(num_user_features,))
vu = user_NN(input_user)# 创建物品输入并指向基础网络
input_item = tf.keras.layers.Input(shape=(num_item_features,))
vm = item_NN(input_item)# 使用 Keras Lambda layer计算L2范数标准化向量
norm_user = Lambda(lambda x: tf.linalg.l2_normalize(x, axis=1))(vu)
norm_item = Lambda(lambda x: tf.linalg.l2_normalize(x, axis=1))(vm)# 点乘计算两个标准化向量
output = tf.keras.layers.Dot(axes=1)([norm_user, norm_item])# 指定模型的输入和输出
model = Model([input_user, input_item], output)model.summary()

       运行以上代码,结果如下(warning不用理会)

       ② 模型编译及训练:

# 模型编译、训练
tf.random.set_seed(1)
cost_fn = tf.keras.losses.MeanSquaredError()
opt = keras.optimizers.Adam(learning_rate=0.01)
model.compile(optimizer=opt,loss=cost_fn)
model.fit([user_train[:, u_s:], item_train[:, i_s:]], ynorm_train, epochs=20)

        运行以上代码,结果如下:

      

      ③ 模型评估:

# 模型评估
model.evaluate([user_test[:, u_s:], item_test[:, i_s:]], ynorm_test)

         运行以上代码,结果如下:

      

       (5)用户喜好预测

        ① 定义函数,提取指定用户的信息内容:

def get_user_vecs(user_id, user_train, item_vecs, user_to_genre):""" 输入user_id, user_train, item_vecs, user_to_genre,返回:user_vecs:用户特征向量,与电影特征向量item_vecs同尺度y vector: 用户对所有电影的评分 """# 判断id是否存在if user_id not in user_to_genre:print("error: unknown user id")return(None)else:user_vec_found = Falsefor i in range(len(user_train)):if user_train[i, 0] == user_id:user_vec = user_train[i]user_vec_found = Truebreakif not user_vec_found:print("error in get_user_vecs, did not find uid in user_train")num_items = len(item_vecs)user_vecs = np.tile(user_vec, (num_items, 1))y = np.zeros(num_items)for i in range(num_items): movie_id = item_vecs[i, 0]if movie_id in user_to_genre[user_id]['movies']:rating = user_to_genre[user_id]['movies'][movie_id]else:rating = 0y[i] = ratingreturn(user_vecs, y)

         ② 定义函数,根据用户内容(利用model)预测并对预测结果排序:

# 对预测结果排序
def predict_uservec(user_vecs, item_vecs, model, u_s, i_s, scaler, ScalerUser, ScalerItem, scaledata=False):""" given a user vector, does the prediction on all movies in item_vecs returnsan array predictions sorted by predicted rating,arrays of user and item, sorted by predicted rating sorting index"""if scaledata:scaled_user_vecs = ScalerUser.transform(user_vecs)scaled_item_vecs = ScalerItem.transform(item_vecs)y_p = model.predict([scaled_user_vecs[:, u_s:], scaled_item_vecs[:, i_s:]])else:y_p = model.predict([user_vecs[:, u_s:], item_vecs[:, i_s:]])y_pu = scaler.inverse_transform(y_p)if np.any(y_pu < 0) : print("Error, expected all positive predictions")sorted_index = np.argsort(-y_pu,axis=0).reshape(-1).tolist()  #negate to get largest rating firstsorted_ypu   = y_pu[sorted_index]sorted_items = item_vecs[sorted_index]sorted_user  = user_vecs[sorted_index]return(sorted_index, sorted_ypu, sorted_items, sorted_user)

        ③ 定义函数,展示(预测的)用户喜好:

def print_existing_user(y_p, y, user, items, item_features, ivs, uvs, movie_dict, maxcount=10):""" print results of prediction a user who was in the datatbase. inputs are expected to be in sorted order, unscaled. """count = 0movies_listed = defaultdict(int)disp = [["y_p", "y", "user", "user genre ave", "movie rating ave", "title", "genres"]]listed = []count = 0for i in range(0, y.shape[0]):if y[i, 0] != 0:if count == maxcount:breakcount += 1movie_id = items[i, 0].astype(int)offset = np.where(items[i, ivs:] == 1)[0][0]genre_rating = user[i, uvs + offset]genre = item_features[ivs + offset]disp.append([y_p[i, 0], y[i, 0],user[i, 0].astype(int),      # useridgenre_rating.astype(float),items[i, 2].astype(float),    # movie average ratingmovie_dict[movie_id]['title'], genre])table = tabulate.tabulate(disp, tablefmt='html', headers="firstrow", floatfmt=[".1f", ".1f", ".0f", ".2f", ".2f"])return(table)

       ④ 开始为ID=18的用户预测其电影喜好:

# 从原始数据中指定一位用户ID进行推荐
uid =  18 
# 处理原始数据,形成一组用户内容向量
user_vecs, y_vecs = get_user_vecs(uid, scalerUser.inverse_transform(user_train), item_vecs, user_to_genre)# 缩放内容向量,并预测该用户对所有电影的评分
sorted_index, sorted_ypu, sorted_items, sorted_user = predict_uservec(user_vecs, item_vecs, model, u_s, i_s, scaler, scalerUser, scalerItem, scaledata=scaledata)
sorted_y = y_vecs[sorted_index]   # 返回按评级排序的结果# 生成预测排序结果
print_existing_user(sorted_ypu, sorted_y.reshape(-1,1), sorted_user, sorted_items, item_features, ivs, uvs, movie_dict, maxcount = 10)

      运行以上代码,结果如下:

      (6)为用户推荐电影

        ① 定义函数,计算电影相似度:

def sq_dist(a,b):"""Returns the squared distance between two vectorsArgs:a (ndarray (n,)): vector with n featuresb (ndarray (n,)): vector with n featuresReturns:d (float) : distance"""   d = np.sum((a - b) ** 2)    return (d)

        ②  新建模型计算电影特征向量:

# 利用已训练参数新建模型计算物品向量
input_item_m = tf.keras.layers.Input(shape=(num_item_features,))    # input layer
vm_m = item_NN(input_item_m)                                        # use the trained item_NN
# 使用item_NN的输出在Lambda层中定义L2归一化
normalized_output = Lambda(lambda x: tf.linalg.l2_normalize(x, axis=1))(vm_m)# 使用修正后的逻辑重新定义model_m,确保输入和输出正确关联
model_m = Model(inputs=input_item_m, outputs=normalized_output)model_m.summary()# 标准化处理特征向量,并使用model_m预测
scaled_item_vecs = scalerItem.transform(item_vecs)
vms = model_m.predict(scaled_item_vecs[:,i_s:])

       运行以上代码,结果如下:

     

       (新建一个模型,利用已经训练好的参数进行预测,好处是易于分离职责和维护、效率更高、便于调试和优化) 

       ③ 向用户(ID=18)推荐:

# 向用户推荐
count = 10  # 推荐数量
liked_movie_ids = item_vecs[sorted_index[:10], 0].astype(int)  # 实际应用中应根据用户评分数据动态获取# 使用训练好的模型和标准化的电影特征向量计算喜欢的电影的特征向量
liked_movie_vectors = []
for movie_id in liked_movie_ids:try:# 尝试在item_vecs中找到movie_idmovie_index = np.where(item_vecs[:, 0] == movie_id)[0]if len(movie_index) > 0:# 如果找到了,进行后续操作scaled_movie_vec = scalerItem.transform(item_vecs[movie_index, :])[:, i_s:]liked_movie_vectors.append(model_m.predict(scaled_movie_vec).flatten())else:print(f"Warning: Movie ID {movie_id} not found in item vectors despite attempt.")except Exception as e:print(f"An unexpected error occurred while processing movie ID {movie_id}: {e}")# 初始化相似度计算和推荐列表
distances = []
recommendations = []# 对于每部喜欢的电影,找到与其最相似的未看过的电影
for liked_vector in liked_movie_vectors:liked_vector_normalized = liked_vector[:32] / np.linalg.norm(liked_vector[:32])  # 取前32个元素并正则化#for i, v in enumerate(vms):v_normalized = v / np.linalg.norm(v)  # 正则化处理当前比较的电影向量sim_score = np.dot(liked_vector_normalized, v_normalized)  # 使用点积计算相似度distances.append((sim_score, i))  # 记录电影索引及其相似度# 对所有相似度进行排序并选择前N部电影(排除已喜欢的)
top_movies_indices = [idx for _, idx in sorted(distances, reverse=True)[:count]]
unique_top_movies = list(set(top_movies_indices))# 构建推荐列表
disp = [["Movie Title", "Genres"]]
for movie_idx in unique_top_movies:movie_id = int(item_vecs[movie_idx, 0])title, genres = movie_dict[movie_id]['title'], movie_dict[movie_id]['genres']disp.append([title, genres])table = tabulate.tabulate(disp, tablefmt='html', headers="firstrow")
table

       运行以上代码,结果如下:

     

        至此,完成了“基于内容过滤的电影推荐系统”搭建任务,上述系统尽量复现了吴恩达机器学习course3(week2)中的课后代码(因为原代码跑不通略作修改),推荐的结果可采纳度较高,预测用户(ID=18)的电影喜好类型为悬疑片(mystery)和恐怖片(thriller),最终推荐的电影也以恐怖片为主。

04 大项目(数据很大)的推荐方法※

4.1 方法原理

      针对数据量特别大的推荐系统,课程中给出一种提升推荐效率的方法:检索 + 排序

4.2 实施示例

       假设一个电影视频网站有数万部电影,即使仅为1位用户作推荐,每次从数万部电影中寻找适合的电影也很耗时。

      “检索 + 排序”的步骤为:

     (1)检索

       首先,根据用户最近观看的10部电影,作近似度计算,各取前3;

       然后,用户历史记录中选出3类电影类型,找出各类型榜单前10部;

       最后,找出该用户所在国家榜单的前10部;

       还可以采用其他筛选标准……

     (2)排序

        假设以上检索出100部电影,将这100部电影的特征输入神经网络模型进行计算,然后采用基于内容的过滤算法向用户推荐。

05 总结

     (1)基于内容的过滤算法,其推荐分5个步骤:准备内容>神经网络模型提取特征>用户喜好预测>近似度计算>向用户推荐。

     (2)推荐算法的原理易于理解而实际操作较为复杂,根本原因为数据量大,所以各类变量较多,因此数据处理是重点工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/38719.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式问题分析思路

BUG解决总体思路: 1.1 定位bug范围及性质 要有效解决问题&#xff0c;首先要缩小范围&#xff0c;集中关注最近的代码变化。这有助于迅速定位可能引入问题的部分&#xff0c;避免无谓的时间浪费。检查最近的代码提交记录和修改日志&#xff0c;找出可能影响现有功能的变更。然…

如果使用Outlook 2024出现问题

大家好&#xff0c;才是真的好。 很多企业使用Domino服务器当作POP/IMAP邮箱服务器来使用&#xff0c;虽然这不能发挥Domino最佳效能&#xff0c;但也不失为一种简单用法。 另一种企业则使用Domino仅作为应用app平台&#xff0c;邮箱早已迁移至O365或其他平台&#xff0c;他们…

报销又乱又慢,财务如何解决报销困局?

费用报销是企业频繁发生的业务场景&#xff0c;不同的企业在费用报销的流程、标准、制度、管理上各有不同。作为一些公司日常运作中的薄弱环节&#xff0c;费用报销环节存在着较大的内控风险&#xff0c;如&#xff1a;费用报销滞后&#xff0c;造成会计信息的失真&#xff0c;…

【YOLOv5/v7改进系列】更换损失函数为CIOU、GIOU、SIOU、DIOU、EIOU、WIOUv1/v2/v3、Focal C/G/S/D/EIOU等

一、导言 在目标检测任务中&#xff0c;损失函数的主要作用是衡量模型预测的边界框&#xff08;bounding boxes&#xff09;与真实边界框之间的匹配程度&#xff0c;并指导模型学习如何更精确地定位和分类目标。损失函数通常由两部分构成&#xff1a;分类损失&#xff08;用于…

我的世界服务器-高版本服务器-MC服务器-生存服务器-RPG服务器-幻世星辰

生存为主&#xff0c;RPG乐趣为辅&#xff0c;重视每位玩家的建议&#xff0c;一起打造心目中的服务器&#xff0c;与小伙伴一起探险我的世界&#xff01; 服务器版本: 1.18.2 ~ 1.20.4 Q群&#xff1a; 338238381 服务器官网: 星辰毛毛雨-Minecraft高版本生存服务器我的世界…

springboot是否可以代替spring

Spring Boot不能直接代替Spring&#xff0c;但它是Spring框架的一个扩展和增强&#xff0c;提供了更加便捷和高效的开发体验。以下是关于Spring Boot和Spring关系的详细解释&#xff1a; Spring框架&#xff1a; Spring是一个广泛应用的开源Java框架&#xff0c;提供了一系列模…

EDI是什么?与ERP有何关系

EDI的发展过程 电子数据交换&#xff08;Electronic Data Interchange&#xff0c;EDI&#xff09;是一种通过电子方式传输商业文件的技术。EDI的历史可以追溯到20世纪60年代&#xff0c;当时企业开始使用计算机进行数据处理。最早的EDI系统是为解决大型企业间的信息交换问题而…

nccl 04 nvidia 官方小程序

1&#xff0c;代码重新编辑 为了地毯式地检查结果的正确性&#xff0c;这里修改了代码 主要步骤为 step1: data_p指向的空间中&#xff0c;分别生成随机数&#xff1b; step2: 分别拷贝到gpu的sendbuff的显存中&#xff1b; step3: 通过nccl_all_reduce sum&#xff1b;…

掌握 Python 中 isinstance 的正确用法

&#x1f44b; 简介 isinstance() 函数用于判断一个对象是否是一个特定类型或者在继承链中是否是特定类型的实例。它常用于确保函数接收到的参数类型是预期的。 &#x1f4d6; 正文 1 语法 isinstance(object, classinfo) object参数是要检查的对象&#xff1b;classinfo参数…

【工具推荐】ONLYOFFICE8.1版本编辑器测评——时下的办公利器

文章目录 一、产品介绍1. ONLYOFFICE 8.1简介2. 多元化多功能的编辑器 二、产品体验1. 云端协作空间2. 桌面编辑器本地版 三、产品界面设计1. 本地版本2. 云端版本 四、产品文档处理1. 文本文档&#xff08;Word)2. 电子表格&#xff08;Excel&#xff09;3. PDF表单&#xff0…

【C++ | 继承】|概念、方式、特性、作用域、6类默认函数

继承 1.继承的概念与定义2.继承的方式2.1继承基本特性2.2继承的作用域2.2.1隐藏赋值兼容 派生类的创建和销毁构造函数拷贝构造赋值重载 1.继承的概念与定义 继承是面向对象编程中的一个重要概念。它的由来可以追溯到软件开发中的模块化设计和代码复用的需求。 在软件开发过程…

【Android面试八股文】性能优化相关面试题: 什么是内存抖动?什么是内存泄漏?

文章目录 一、什么是内存抖动?内存抖动的问题卡顿OOM(Out Of Memory)二、什么是内存泄漏(Memory Leak)?引用计数法可达性分析法一、什么是内存抖动? 在Java中,每创建一个对象,就会申请一块内存,存储对象信息; 每分配一块内存,程序的可用内存也就少一块; 当程序…

什么是协程?协程和线程的区别

文章目录 前置知识应用程序和内核阻塞和非阻塞同步和异步并发和并行IO 发展历史同步编程异步多线程/进程异步消息 回调函数&#xff08;响应式编程&#xff09; 协程协程基本概念go 示例代码协程和线程的区别 个人简介 前置知识 在了解协程前&#xff0c;我们先理解一些相关的…

强化学习原理入门-1绪论

1 绪论 1.1 这是一本什么书 强化学习算法&#xff1f; AlphaGo大胜世界围棋冠军李世石和柯洁事件&#xff0c;核心算法就用到了强化学习算法。 1.2 强化学习解决什么问题 案例1 非线性系统二级倒立摆 案例2 AlphaGo与柯洁的第二局棋 案例3 机器人学习站立 ...... 智能…

沙箱在“一机两用”新规下的价值体现

在数字化时代&#xff0c;随着企业信息化建设的深入&#xff0c;数据安全问题愈发凸显其重要性。一机两用新规的出台&#xff0c;旨在通过技术创新和管理手段&#xff0c;实现终端设备的安全可控&#xff0c;确保敏感数据的安全存储与传输。SDC沙箱技术作为一种创新的安全防护手…

springcloud-config服务器,同样的配置在linux环境下不生效

原本在windows下能争取的获取远程配置但是部署到linux上死活都没有内容&#xff0c;然后开始了远程调试&#xff0c;这里顺带讲解下获取配置文件如果使用的是Git源&#xff0c;config service是如何响应接口并返回配置信息的。先说问题&#xff0c;我的服务名原本是abc-abc-abc…

图像基础知识入门【图像概念不同图像格式】

图像基础知识入门【图像概念&不同图像格式】 最近有在处理图像转换&#xff0c;因此稍微补足了一下图像相关知识&#xff0c;特在此记录。下面汇总是我根据自己理解和网上查阅资料而来。如有错误&#xff0c;欢迎大家指正。 1 基础概念 像素/分辨率 像素(Pixel)&#xff…

如何实现电子签名签章功能?

随着技术的发展&#xff0c;传统的纸质合同签署方式逐渐暴露出效率低下、存储不便和安全性不足等问题。为了解决这些问题&#xff0c;电子签署服务为用户提供了一个安全、高效、环保的合同管理解决方案。 电子合同管理与签署平台的核心功能 1、用户管理&#xff1a;平台提供用…

怎么永久禁止win10系统自动更新?一键屏蔽系统自动更新

现在 Windows 10 系统是很多办公用户的主力操作系统&#xff0c;可是 Windows 系统会自动更新&#xff0c;这会严重影响系统稳定性。因为微软虽然以提供更新为服务&#xff0c;但并不是每次更新它都是安全的。 接下来和我一起看看如何使用联想开发的小工具一键屏蔽系统自动更新…