深度学习电影推荐-CNN算法
文章目录
- 前言
- 视频演示效果
- 1.数据集
- 环境配置安装教程与资源说明
- 1.1 ML-1M 数据集概述
- 1.1.1数据集内容
- 1.1.2. 数据集规模
- 1.1.3. 数据特点
- 1.1.4. 文件格式
- 1.1.5. 应用场景
- 2.模型架构
- 3.推荐实现
- 3.1 用户数据
- 3.2 电影数据
- 3.3 评分数据
- 3.4 数据预处理
- 3.5实现数据预处理
- 3.6 加载数据并保存到本地
- 3.7从本地读取数据
- 4 模型设计
- 4.1 概述
- 4.2 文本卷积网络
- 辅助函数
- 构建神经网络
- 定义User的嵌入矩阵
- 将User的嵌入矩阵一起全连接生成User的特征
- 定义Movie ID的嵌入矩阵
- 对电影类型的多个嵌入向量做加和
- Movie Title的文本卷积网络实现
- 将Movie的各个层一起做全连接
- 生成Movie特征矩阵
- 生成User特征矩阵
- 开始推荐电影
- 结论
前言
随着互联网和流媒体平台的快速发展,用户可以随时访问海量的电影资源。然而,如何帮助用户在繁杂的选择中快速找到符合其兴趣的电影,成为了一个重要且具有挑战性的问题。推荐系统作为解决信息过载的重要工具,在电影行业得到了广泛应用。近年来,深度学习技术的崛起为推荐系统的构建提供了新的方法和更高的精度,其强大的特征提取能力和非线性建模能力使电影推荐系统更加智能化和个性化。
MovieLens 1M(简称 ML-1M)是电影推荐领域最常用的公开数据集之一,包含约6000名用户对3900多部电影的100万条评分记录。该数据集提供了用户的基本信息(如年龄、性别、职业等)、电影的元数据(如标题、类型、年份等)以及用户对电影的评分。这些信息为电影推荐系统的研究提供了丰富的实验数据。基于 ML-1M 数据集,研究者可以开发和验证各种推荐算法,同时通过标准化的数据便于不同方法的横向比较。
传统的推荐方法,如基于协同过滤和矩阵分解的技术,在处理稀疏数据和冷启动问题时表现有限。而深度学习方法以其在高维数据中的强大建模能力,能够挖掘用户和电影之间的复杂非线性关系,并捕捉多模态数据的深层特征。例如,通过神经网络构建用户和电影的嵌入向量,深度学习方法可以对用户行为和电影内容进行联合建模;通过融合用户的评分历史和电影的文本信息、图像特征等多源数据,可以显著提升推荐结果的精准度和多样性。
本研究以 ML-1M 数据集为基础,探索深度学习技术在电影推荐中的应用。研究目标包括:构建高效的深度学习模型,挖掘用户行为模式;融合电影的多模态信息,提升推荐系统的准确性;以及优化推荐策略,为用户提供个性化的观影建议。本研究不仅具有理论意义,也为实际推荐系统的开发提供了宝贵的参考。
视频演示效果
深度学习实战电影推荐系统
觉得不错的小伙伴,感谢点赞、关注加收藏哦!更多干货内容持续更新…
1.数据集
环境配置安装教程与资源说明
离线安装配置文件说明
1.1 ML-1M 数据集概述
MovieLens 1M(简称 ML-1M)数据集是由明尼苏达大学的 GroupLens 研究团队发布的一个电影推荐系统研究数据集,是推荐算法开发和评估领域的经典数据集之一。该数据集包含 100 万条电影评分记录,为研究电影推荐系统提供了标准化和高质量的数据资源。以下是 ML-1M 数据集的详细概述:
1.1.1数据集内容
ML-1M 数据集包括以下几部分信息:
- 用户数据:
- 用户唯一 ID。
- 性别(Male 或 Female)。
- 年龄段(如 18-24、25-34 等)。
- 职业编号(对应具体职业类别)。
- 电影数据:
- 电影唯一 ID。
- 电影标题及其上映年份。
- 电影的分类标签(如动作、喜剧、科幻等)。
- 评分数据:
- 用户对电影的评分,范围为 1 到 5 的整数。
- 评分的时间戳,用于分析评分的时间分布和用户行为模式。
1.1.2. 数据集规模
- 用户数量:6,040 名用户。
- 电影数量:3,900 多部电影。
- 评分数量:1,000,209 条评分。
1.1.3. 数据特点
- 稀疏性:尽管数据集包含大量评分记录,但与可能的评分总量相比(即用户数 × 电影数),实际评分所占比例较小,表现出典型的稀疏性问题。
- 时间维度:评分记录带有时间戳信息,可以分析用户行为的时间动态。
- 多样性:用户和电影的元数据涵盖了性别、年龄、职业、类型等多个维度,为推荐算法提供了丰富的上下文信息。
1.1.4. 文件格式
ML-1M 数据集通常以分隔符文本文件(如 CSV 或 TXT)形式存储,包括以下文件:
users.dat
:包含用户的基本信息。movies.dat
:包含电影的基本信息。ratings.dat
:包含用户的评分记录。
1.1.5. 应用场景
ML-1M 数据集广泛应用于以下研究方向:
- 推荐系统算法开发:用于测试协同过滤、矩阵分解、深度学习等推荐算法的性能。
- 用户行为建模:分析用户的观影习惯、兴趣动态和评分模式。
- 冷启动问题研究:通过新用户或新电影的推荐,解决数据稀疏性问题。
- 多模态融合:结合文本、图像、时间序列等信息,优化推荐效果。
ML-1M 数据集因其数据结构清晰和实验价值高,成为推荐系统研究的重要基准,许多先进的算法和模型均在该数据集上进行了验证。
2.模型架构
3.推荐实现
本项目使用的是MovieLens 1M 数据集,包含6000个用户在近4000部电影上的1亿条评论。
数据集分为三个文件:用户数据users.dat,电影数据movies.dat和评分数据ratings.dat。
3.1 用户数据
分别有用户ID、性别、年龄、职业ID和邮编等字段。
数据中的格式:UserID::Gender::Age::Occupation::Zip-code
-
Gender is denoted by a “M” for male and “F” for female
-
Age is chosen from the following ranges:
- 1: “Under 18”
- 18: “18-24”
- 25: “25-34”
- 35: “35-44”
- 45: “45-49”
- 50: “50-55”
- 56: “56+”
-
Occupation is chosen from the following choices:
- 0: “other” or not specified
- 1: “academic/educator”
- 2: “artist”
- 3: “clerical/admin”
- 4: “college/grad student”
- 5: “customer service”
- 6: “doctor/health care”
- 7: “executive/managerial”
- 8: “farmer”
- 9: “homemaker”
- 10: “K-12 student”
- 11: “lawyer”
- 12: “programmer”
- 13: “retired”
- 14: “sales/marketing”
- 15: “scientist”
- 16: “self-employed”
- 17: “technician/engineer”
- 18: “tradesman/craftsman”
- 19: “unemployed”
- 20: “writer”
users_title = [‘UserID’, ‘Gender’, ‘Age’, ‘OccupationID’, ‘Zip-code’]
users = pd.read_table(‘./ml-1m/users.dat’, sep=‘::’, header=None, names=users_title, engine = ‘python’)
users.head()
可以看出UserID、Gender、Age和Occupation都是类别字段,其中邮编字段是我们不使用的。
3.2 电影数据
分别有电影ID、电影名和电影风格等字段。
数据中的格式:MovieID::Title::Genres
-
Titles are identical to titles provided by the IMDB (including
year of release) -
Genres are pipe-separated and are selected from the following genres:
- Action
- Adventure
- Animation
- Children’s
- Comedy
- Crime
- Documentary
- Drama
- Fantasy
- Film-Noir
- Horror
- Musical
- Mystery
- Romance
- Sci-Fi
- Thriller
- War
- Western
movies_title = [‘MovieID’, ‘Title’, ‘Genres’]
movies = pd.read_table(‘./ml-1m/movies.dat’, sep=‘::’, header=None, names=movies_title, engine = ‘python’)
movies.head()
MovieID是类别字段,Title是文本,Genres也是类别字段
3.3 评分数据
分别有用户ID、电影ID、评分和时间戳等字段。
数据中的格式:UserID::MovieID::Rating::Timestamp
- UserIDs range between 1 and 6040
- MovieIDs range between 1 and 3952
- Ratings are made on a 5-star scale (whole-star ratings only)
- Timestamp is represented in seconds since the epoch as returned by time(2)
- Each user has at least 20 ratings
ratings_title = [‘UserID’,‘MovieID’, ‘Rating’, ‘timestamps’]
ratings = pd.read_table(‘./ml-1m/ratings.dat’, sep=‘::’, header=None, names=ratings_title, engine = ‘python’)
ratings.head()
评分字段Rating就是我们要学习的targets,时间戳字段我们不使用。
3.4 数据预处理
- UserID、Occupation和MovieID不用变。
- Gender字段:需要将‘F’和‘M’转换成0和1。
- Age字段:要转成7个连续数字0~6。
- Genres字段:是分类字段,要转成数字。首先将Genres中的类别转成字符串到数字的字典,然后再将每个电影的Genres字段转成数字列表,因为有些电影是多个Genres的组合。
- Title字段:处理方式跟Genres字段一样,首先创建文本到数字的字典,然后将Title中的描述转成数字的列表。另外Title中的年份也需要去掉。
- Genres和Title字段需要将长度统一,这样在神经网络中方便处理。空白部分用‘< PAD >’对应的数字填充。
3.5实现数据预处理
def load_data():"""Load Dataset from File"""#读取User数据users_title = ['UserID', 'Gender', 'Age', 'JobID', 'Zip-code']users = pd.read_table('./ml-1m/users.dat', sep='::', header=None, names=users_title, engine = 'python')# 保留以下特征users = users.filter(regex='UserID|Gender|Age|JobID')users_orig = users.values#改变User数据中性别和年龄gender_map = {'F':0, 'M':1}users['Gender'] = users['Gender'].map(gender_map)age_map = {val:ii for ii,val in enumerate(set(users['Age']))}users['Age'] = users['Age'].map(age_map)#读取Movie数据集movies_title = ['MovieID', 'Title', 'Genres']movies = pd.read_table('./ml-1m/movies.dat', sep='::', header=None, names=movies_title, engine = 'python')movies_orig = movies.values#将Title中的年份去掉pattern = re.compile(r'^(.*)\((\d+)\)$')title_map = {val:pattern.match(val).group(1) for ii,val in enumerate(set(movies['Title']))}movies['Title'] = movies['Title'].map(title_map)#电影类型转数字字典genres_set = set()for val in movies['Genres'].str.split('|'):genres_set.update(val)genres_set.add('<PAD>')# 将类型进行编号genres2int = {val:ii for ii, val in enumerate(genres_set)}#将电影类型转成等长数字列表genres_map = {val:[genres2int[row] for row in val.split('|')] for ii,val in enumerate(set(movies['Genres']))}# 将每个样本的电影类型数字列表处理成相同长度,长度不够用<PAD>填充for key in genres_map:for cnt in range(max(genres2int.values()) - len(genres_map[key])):genres_map[key].insert(len(genres_map[key]) + cnt,genres2int['<PAD>'])movies['Genres'] = movies['Genres'].map(genres_map)#电影Title转数字字典title_set = set()for val in movies['Title'].str.split():title_set.update(val)title_set.add('<PAD>')title2int = {val:ii for ii, val in enumerate(title_set)}#将电影Title转成等长数字列表,长度是15title_count = 15title_map = {val:[title2int[row] for row in val.split()] for ii,val in enumerate(set(movies['Title']))}for key in title_map:for cnt in range(title_count - len(title_map[key])):title_map[key].insert(len(title_map[key]) + cnt,title2int['<PAD>'])movies['Title'] = movies['Title'].map(title_map)#读取评分数据集ratings_title = ['UserID','MovieID', 'ratings', 'timestamps']ratings = pd.read_table('./ml-1m/ratings.dat', sep='::', header=None, names=ratings_title, engine = 'python')ratings = ratings.filter(regex='UserID|MovieID|ratings')#合并三个表data = pd.merge(pd.merge(ratings, users), movies)#将数据分成X和y两张表target_fields = ['ratings']features_pd, targets_pd = data.drop(target_fields, axis=1), data[target_fields]features = features_pd.valuestargets_values = targets_pd.valuesreturn title_count, title_set, genres2int, features, targets_values, ratings, users, movies, data, movies_orig, users_orig
3.6 加载数据并保存到本地
- title_count:Title字段的长度(15)
- title_set:Title文本的集合
- genres2int:电影类型转数字的字典
- features:是输入X
- targets_values:是学习目标y
- ratings:评分数据集的Pandas对象
- users:用户数据集的Pandas对象
- movies:电影数据的Pandas对象
- data:三个数据集组合在一起的Pandas对象
- movies_orig:没有做数据处理的原始电影数据
- users_orig:没有做数据处理的原始用户数据
title_count, title_set, genres2int, features, targets_values, \ratings, users, movies, data, movies_orig, users_orig = load_data()with open('./processed_data/preprocess.pkl', 'wb') as f:pickle.dump((title_count, title_set, genres2int, features, targets_values, ratings, users, movies, data, movies_orig, users_orig), f)
3.7从本地读取数据
with open('./processed_data/preprocess.pkl', mode='rb') as f:title_count, title_set, genres2int, features, \targets_values, ratings, users, movies, data, movies_orig, users_orig = pickle.load(f)
4 模型设计
4.1 概述
通过研究数据集中的字段类型,我们发现有一些是类别字段,通常的处理是将这些字段转成one hot编码,但是像UserID、MovieID这样的字段就会变成非常的稀疏,输入的维度急剧膨胀,
所以在预处理数据时将这些字段转成了数字,我们用这个数字当做嵌入矩阵的索引,在网络的第一层使用了嵌入层,维度是(N,32)和(N,16)。
这里的思想其实和 word2vec 比较类似。我们会对用户或者电影的每个属性都指定一个特征维度空间,这就好比我们在自然语言处理中对每个单词指定特征维度空间。从下面的代码中可以看到,我们将用到的属性的特征维度设置为了 32 或者 16.
电影类型的处理要多一步,有时一个电影有多个电影类型,这样从嵌入矩阵索引出来是一个(n,32)的矩阵,因为有多个类型嘛,我们要将这个矩阵求和,变成(1,32)的向量。
电影名的处理比较特殊,没有使用循环神经网络,而是用了文本卷积网络,下文会进行说明。
从嵌入层索引出特征以后,将各特征传入全连接层,将输出再次传入全连接层,最终分别得到(1,200)的用户特征和电影特征两个特征向量。
我们的目的就是要训练出用户特征和电影特征,在实现推荐功能时使用。得到这两个特征以后,就可以选择任意的方式来拟合评分了。我使用了两种方式,一个是上图中画出的将两个特征做向量乘法,将结果与真实评分做回归,采用MSE优化损失。因为本质上这是一个回归问题,另一种方式是,将两个特征作为输入,再次传入全连接层,输出一个值,将输出值回归到真实评分,采用MSE优化损失。
实际上第二个方式的MSE loss在0.8附近,第一个方式在1附近,5次迭代的结果。
4.2 文本卷积网络
将卷积神经网络用于文本,网络的第一层是词嵌入层,由每一个单词的嵌入向量组成的嵌入矩阵。下一层使用多个不同尺寸(窗口大小)的卷积核在嵌入矩阵上做卷积,窗口大小指的是每次卷积覆盖几个单词。这里跟对图像做卷积不太一样,图像的卷积通常用2x2、3x3、5x5之类的尺寸,而文本卷积要覆盖整个单词的嵌入向量,所以尺寸是(单词数,向量维度),比如每次滑动3个,4个或者5个单词。第三层网络是max pooling得到一个长向量,最后使用dropout做正则化,最终得到了电影Title的特征。
辅助函数
import tensorflow as tf
import os
import pickledef save_params(params):"""Save parameters to file"""with open('params.p', 'wb') as f:pickle.dump(params, f)def load_params():"""Load parameters from file"""with open('params.p', mode='rb') as f:return pickle.load(f)
构建神经网络
定义User的嵌入矩阵
def get_user_embedding(uid, user_gender, user_age, user_job):with tf.name_scope("user_embedding"):# 下面的操作和情感分析项目中的单词转换为词向量的操作本质上是一样的# 用户的特征维度设置为 32# 先初始化一个非常大的用户矩阵# tf.random_uniform 的第二个参数是初始化的最小值,这里是-1,第三个参数是初始化的最大值,这里是1uid_embed_matrix = tf.Variable(tf.random_uniform([uid_max, embed_dim], -1, 1), name = "uid_embed_matrix")# 根据指定用户ID找到他对应的嵌入层uid_embed_layer = tf.nn.embedding_lookup(uid_embed_matrix, uid, name = "uid_embed_layer")# 性别的特征维度设置为 16
将User的嵌入矩阵一起全连接生成User的特征
def get_user_feature_layer(uid_embed_layer, gender_embed_layer, age_embed_layer, job_embed_layer):with tf.name_scope("user_fc"):#第一层全连接# tf.layers.dense 的第一个参数是输入,第二个参数是层的单元的数量uid_fc_layer = tf.layers.dense(uid_embed_layer, embed_dim, name = "uid_fc_layer", activation=tf.nn.relu)gender_fc_layer = tf.layers.dense(gender_embed_layer, embed_dim, name = "gender_fc_layer", activation=tf.nn.relu)age_fc_layer = tf.layers.dense(age_embed_layer, embed_dim, name ="age_fc_layer", activation=tf.nn.relu)job_fc_layer = tf.layers.dense(job_embed_layer, embed_dim, name = "job_fc_layer", activation=tf.nn.relu)#第二层全连接# 将上面的每个分段组成一个完整的全连接层user_combine_layer = tf.concat([uid_fc_layer, gender_fc_layer, age_fc_layer, job_fc_layer], 2) #(?, 1, 128)# 验证上面产生的 tensorflow 是否是 128 维度的print(user_combine_layer.shape)# tf.contrib.layers.fully_connected 的第一个参数是输入,第二个参数是输出# 这里的输入是user_combine_layer,输出是200,是指每个用户有200个特征# 相当于是一个200个分类的问题,每个分类的可能性都会输出,在这里指的就是每个特征的可能性user_combine_layer = tf.contrib.layers.fully_connected(user_combine_layer, 200, tf.tanh) #(?, 1, 200)user_combine_layer_flat = tf.reshape(user_combine_layer, [-1, 200])return user_combine_layer, user_combine_layer_flat
定义Movie ID的嵌入矩阵
def get_movie_id_embed_layer(movie_id):with tf.name_scope("movie_embedding"):movie_id_embed_matrix = tf.Variable(tf.random_uniform([movie_id_max, embed_dim], -1, 1), name = "movie_id_embed_matrix")movie_id_embed_layer = tf.nn.embedding_lookup(movie_id_embed_matrix, movie_id, name = "movie_id_embed_layer")return movie_id_embed_layer
对电影类型的多个嵌入向量做加和
def get_movie_categories_layers(movie_categories):with tf.name_scope("movie_categories_layers"):movie_categories_embed_matrix = tf.Variable(tf.random_uniform([movie_categories_max, embed_dim], -1, 1), name = "movie_categories_embed_matrix")movie_categories_embed_layer = tf.nn.embedding_lookup(movie_categories_embed_matrix, movie_categories, name = "movie_categories_embed_layer")if combiner == "sum":movie_categories_embed_layer = tf.reduce_sum(movie_categories_embed_layer, axis=1, keep_dims=True)# elif combiner == "mean":return movie_categories_embed_layer
Movie Title的文本卷积网络实现
def get_movie_cnn_layer(movie_titles):#从嵌入矩阵中得到电影名对应的各个单词的嵌入向量with tf.name_scope("movie_embedding"):movie_title_embed_matrix = tf.Variable(tf.random_uniform([movie_title_max, embed_dim], -1, 1), name = "movie_title_embed_matrix")movie_title_embed_layer = tf.nn.embedding_lookup(movie_title_embed_matrix, movie_titles, name = "movie_title_embed_layer")# 为 movie_title_embed_layer 增加一个维度# 在这里是添加到最后一个维度,最后一个维度是channel# 所以这里的channel数量是1个# 所以这里的处理方式和图片是一样的movie_title_embed_layer_expand = tf.expand_dims(movie_title_embed_layer, -1)#对文本嵌入层使用不同尺寸的卷积核做卷积和最大池化pool_layer_lst = []for window_size in window_sizes:with tf.name_scope("movie_txt_conv_maxpool_{}".format(window_size)):# [window_size, embed_dim, 1, filter_num] 表示输入的 channel 的个数是1,输出的 channel 的个数是 filter_numfilter_weights = tf.Variable(tf.truncated_normal([window_size, embed_dim, 1, filter_num],stddev=0.1),name = "filter_weights")filter_bias = tf.Variable(tf.constant(0.1, shape=[filter_num]), name="filter_bias")# conv2d 是指用到的卷积核的大小是 [filter_height * filter_width * in_channels, output_channels]# 在这里卷积核会向两个维度的方向进行滑动# conv1d 是将卷积核向一个维度的方向进行滑动,这就是 conv1d 和 conv2d 的区别# strides 设置要求第一个和最后一个数字是1,四个数字的顺序要求默认是 NHWC,也就是 [batch, height, width, channels]# padding 设置为 VALID 其实就是不 PAD,设置为 SAME 就是让输入和输出的维度是一样的conv_layer = tf.nn.conv2d(movie_title_embed_layer_expand, filter_weights, [1,1,1,1], padding="VALID", name="conv_layer")# tf.nn.bias_add 将偏差 filter_bias 加到 conv_layer 上# tf.nn.relu 将激活函数设置为 relurelu_layer = tf.nn.relu(tf.nn.bias_add(conv_layer,filter_bias), name ="relu_layer")# tf.nn.max_pool 的第一个参数是输入# 第二个参数是 max_pool 窗口的大小,每个数值表示对每个维度的窗口设置# 第三个参数是 strides,和 conv2d 的设置是一样的# 这边的池化是将上面每个卷积核的卷积结果转换为一个元素# 由于这里的卷积核的数量是 8 个,所以下面生成的是一个具有 8 个元素的向量maxpool_layer = tf.nn.max_pool(relu_layer, [1,sentences_size - window_size + 1 ,1,1], [1,1,1,1], padding="VALID", name="maxpool_layer")pool_layer_lst.append(maxpool_layer)#Dropout层with tf.name_scope("pool_dropout"):# 这里最终的结果是这样的,# 假设卷积核的窗口是 2,卷积核的数量是 8# 那么通过上面的池化操作之后,生成的池化的结果是一个具有 8 个元素的向量# 每种窗口大小的卷积核经过池化后都会生成这样一个具有 8 个元素的向量# 所以最终生成的是一个 8 维的二维矩阵,它的另一个维度就是不同的窗口的数量# 在这里就是 2,3,4,5,那么最终就是一个 8*4 的矩阵,pool_layer = tf.concat(pool_layer_lst, 3, name ="pool_layer")max_num = len(window_sizes) * filter_num# 将这个 8*4 的二维矩阵平铺成一个具有 32 个元素的一维矩阵pool_layer_flat = tf.reshape(pool_layer , [-1, 1, max_num], name = "pool_layer_flat")dropout_layer = tf.nn.dropout(pool_layer_flat, dropout_keep_prob, name = "dropout_layer")return pool_layer_flat, dropout_layer
将Movie的各个层一起做全连接
def get_movie_feature_layer(movie_id_embed_layer, movie_categories_embed_layer, dropout_layer):with tf.name_scope("movie_fc"):#第一层全连接movie_id_fc_layer = tf.layers.dense(movie_id_embed_layer, embed_dim, name = "movie_id_fc_layer", activation=tf.nn.relu)movie_categories_fc_layer = tf.layers.dense(movie_categories_embed_layer, embed_dim, name = "movie_categories_fc_layer", activation=tf.nn.relu)#第二层全连接movie_combine_layer = tf.concat([movie_id_fc_layer, movie_categories_fc_layer, dropout_layer], 2) #(?, 1, 96)movie_combine_layer = tf.contrib.layers.fully_connected(movie_combine_layer, 200, tf.tanh) #(?, 1, 200)movie_combine_layer_flat = tf.reshape(movie_combine_layer, [-1, 200])return movie_combine_layer, movie_combine_layer_flat
生成Movie特征矩阵
loaded_graph = tf.Graph() #
movie_matrics = []
with tf.Session(graph=loaded_graph) as sess: ## Load saved modelloader = tf.train.import_meta_graph(load_dir + '.meta')loader.restore(sess, load_dir)# Get Tensors from loaded modeluid, user_gender, user_age, user_job, movie_id, movie_categories, movie_titles, targets, lr, dropout_keep_prob, _, movie_combine_layer_flat, __ = get_tensors(loaded_graph) #loaded_graphfor item in movies.values:categories = np.zeros([1, 18])categories[0] = item.take(2)titles = np.zeros([1, sentences_size])titles[0] = item.take(1)feed = {movie_id: np.reshape(item.take(0), [1, 1]),movie_categories: categories, #x.take(6,1)movie_titles: titles, #x.take(5,1)dropout_keep_prob: 1}movie_combine_layer_flat_val = sess.run([movie_combine_layer_flat], feed) movie_matrics.append(movie_combine_layer_flat_val)pickle.dump((np.array(movie_matrics).reshape(-1, 200)), open('movie_matrics.p', 'wb'))
movie_matrics = pickle.load(open('movie_matrics.p', mode='rb'))
生成User特征矩阵
将训练好的用户特征组合成用户特征矩阵并保存到本地
loaded_graph = tf.Graph() #
users_matrics = []
with tf.Session(graph=loaded_graph) as sess: ## Load saved modelloader = tf.train.import_meta_graph(load_dir + '.meta')loader.restore(sess, load_dir)# Get Tensors from loaded modeluid, user_gender, user_age, user_job, movie_id, movie_categories, movie_titles, targets, lr, dropout_keep_prob, _, __,user_combine_layer_flat = get_tensors(loaded_graph) #loaded_graphfor item in users.values:feed = {uid: np.reshape(item.take(0), [1, 1]),user_gender: np.reshape(item.take(1), [1, 1]),user_age: np.reshape(item.take(2), [1, 1]),user_job: np.reshape(item.take(3), [1, 1]),dropout_keep_prob: 1}user_combine_layer_flat_val = sess.run([user_combine_layer_flat], feed) users_matrics.append(user_combine_layer_flat_val)pickle.dump((np.array(users_matrics).reshape(-1, 200)), open('users_matrics.p', 'wb'))
users_matrics = pickle.load(open('users_matrics.p', mode='rb'))
开始推荐电影
结论
以上就是实现的常用的推荐功能,将网络模型作为回归问题进行训练,得到训练好的用户特征矩阵和电影特征矩阵进行推荐。
相关文章:
深度学习电影推荐-CNN算法
文章目录 前言视频演示效果1.数据集环境配置安装教程与资源说明1.1 ML-1M 数据集概述1.1.1数据集内容1.1.2. 数据集规模1.1.3. 数据特点1.1.4. 文件格式1.1.5. 应用场景 2.模型架构3.推荐实现3.1 用户数据3.2 电影数据3.3 评分数据3.4 数据预处理3.5实现数据预处理3.6 加载数据…...
CSS3的aria-hidden学习
前言 aria-hidden 属性可用于隐藏非交互内容,使其在无障碍 API 中不可见。即当aria-hidden"true" 添加到一个元素会将该元素及其所有子元素从无障碍树中移除,这可以通过隐藏来改善辅助技术用户的体验: 纯装饰性内容,如…...
如何在前端给视频进行去除绿幕并替换背景?-----Vue3!!
最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近,我在进行前端开发时,遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需,我一直在冥思苦想。终于有了一个解决方法…...
【Linux】进程间通信IPC
目录 进程间通信 IPC 1. 进程间通信方式 2. 无名管道 2.1 特点 2.2 函数接口 2.3 注意事项 3. 有名管道 3.1 特点 3.2 函数接口 3.3 注意事项 3.4 有名管道和无名管道的区别 4. 信号 4.1概念 4.2信号的响应方式 4.3 信号种类 4.4 函数接口 4.4.1 信号发送和挂…...
go语言实现UTF8与GB2312内码转换
使用Go语言做个UTF-8转GB2312的代码,输入utf-8编码的文本,输出转换后的国标编码的hex内码 package mainimport ("fmt""os""strings""golang.org/x/text/encoding/simplifiedchinese""golang.org/x/text/transform&quo…...
【WPS】【WORDWORD】【JavaScript】实现微软WORD自动更正的效果
1.效果展示 2.核心代码展示 function readTableData(filePath) {let tableData {};let doc Application.Documents.Open(filePath); // 打开文档let table doc.Tables(1); // 获取第一个表格// 遍历表格,存储编号和描述的映射for (let i 1; i < table.Rows.…...
Excel数据叠加生成新DataFrame:操作指南与案例
目录 一、准备工作 二、读取Excel文件 三、数据叠加 四、处理重复数据(可选) 五、保存新DataFrame到Excel文件 六、案例演示 七、注意事项 八、总结 在日常数据处理工作中,我们经常需要将不同Excel文档中的数据整合到一个新的DataFrame中,以便进行进一步的分析和处…...
springBoot项目使用Elasticsearch教程
目录 一、引言(一)使用背景(二)版本库区别 二、引入依赖(一)springboot集成的es依赖(建议)(二)es提供的客户端库 三、配置(以yaml文件为例&#x…...
摄像头模块在狩猎相机中的应用
摄像头模块是狩猎相机的核心组件,在狩猎相机中发挥着关键作用,以下是其主要应用: 图像与视频拍摄 高清成像:高像素的摄像头模块可确保狩猎相机拍摄出清晰的图像和视频,能够捕捉到动物的毛发纹理、行为细节及周围环境的…...
栈溢出原理
文章目录 前言一、基本示例二、分析栈1. 先不考虑gets函数的栈情况2. 分析gets函数的栈区情况 三、利用栈1. 构造字符串2. 利用漏洞 前言 栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。…...
38.【3】CTFHUB web sql 报错注入
进入靶场 按照提示输入1 显示查询正确 既然是报错注入,先判断整形还是字符型注入 先输入1 and 11 再输入1 and 12 都显示查询正确,可知此为字符串型注入,不是数字型注入 然后就不会了 求助AI和其他wp 由以上2张搜索结果知updatexml是适用…...
SSE部署后无法连接问题解决
1. 问题现象 通过域名访问 https://api-uat.sfxs.com/sse/subscribe?tokenBearer%20eyJUxMiJ9.eyJhY2NvdW50IjoiYWRtaWZ0NvZGUiOiIwMDEiLCJyb2xidXNlcm5hbWUiOiLotoXnuqfnrqHnkIblkZgifQ.tlz9N61Y4 一直无法正常连接 2. 问题解决 nginx.conf进行配置 server {location /ss…...
sparkRDD教程之基本命令
作者:nchu可乐百香果 指导者:nchu-YoungDragon 1.前期准备 (1)从迅雷网盘上面下载这个项目,并且把scala,maven和java环境配置好 网盘链接: 分享文件:SparkRDD.zip 链接…...
【JavaScript】比较运算符的运用、定义函数、if(){}...esle{} 语句
比较运算符 !><> < 自定义函数: function 函数名(){ } 判断语句: if(判断){ }else if(判断){ 。。。。。。 }else{ } 代码示例: <!DOCTYPE html> <html> <head><meta charset&quo…...
ShardingSphere—SQL 路由与执行解析原理
在分布式数据库中,SQL 路由、解析及执行是核心机制,用于高效处理 SQL 请求并将其分发到合适的数据节点。在 ShardingSphere 中,这一过程分为三个主要阶段:SQL 路由、SQL 解析 和 SQL 执行,通过灵活的策略和优化机制实现…...
机器学习——什么是代价函数?
1.代价函数的定义 首先,提到代价函数是估计值和实际值的差,这应该是指预测值和真实值之间的差异,用来衡量模型的好坏。 在一元线性模型中,模型是直线,有两个参数,可能是斜率和截距。 通过调整这两个参数,让代价函数最小,这应该是说我们要找到最佳的斜率和截距,使得预测…...
k8s集群换IP
k8s集群搭建及节点加入时需要确定IP,但安装完成后设备移动到新环境可能出现网段更换或者IP被占用的情况,导致无法ping通节点或者无法打开原IP的服务。 解决方法为保持原有IP不更换,给网卡再加一个IP 这边使用两个ubuntu虚拟机模拟服务器和w…...
RPC 源码解析~Apache Dubbo
解析 RPC(远程过程调用)的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析,我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架,广泛应用于企业级应用…...
【认识油管头部频道】ep5 “5-Minute Crafts”——DIY 和生活技巧
5-Minute Crafts 是一个非常受欢迎的 DIY 和生活技巧频道,它的火爆有多方面的原因: 1. 简单实用的内容 视频主要以解决日常生活中遇到的小问题为主,提供简单易学的技巧,吸引了想快速获取实用知识的观众。 2. 短视频形式 每个视…...
vue3 uniapp封装一个瀑布流组件
新增组件m-waterfall 这样就可以在页面直接使用 不用在引入了 <template><view class"m-waterfall"><view id"m-left-column" class"m-column"><slot name"left" :leftList"leftList"></slot&…...
基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩
丰富的经验、成熟的技术,打造适合当下市场发展的语音交友软件源码。Java 语言凭借其独特的优势,为这款语音陪聊软件的稳健运行和持续发展奠定了坚实基础。它不仅融合了聊天私聊、礼物系统和直播系统等实用且有趣的功能,还创新性地引入了缘分匹…...
山石防火墙命令行配置示例
现网1台山石SG6000防火墙,配置都可以通过GUI实现。 但有一些配置在命令行下配置效率更高,比如在1个已有策略中添加1个host或端口。 下面的双引号可以不加 1 创建服务 1.1 单个端口 service "tcp-901"tcp dst-port 901 1.2 端口范围 servi…...
WordPress内容保护策略:如何反击并利用被盗内容
当你的网站开始获得大量流量时,内容盗窃成为不可避免的问题。除了通过高级的保护措施防止内容被盗,你还可以采取一些策略来反击内容盗窃,并从中获益。 1. 识别并封锁恶意IP地址 当你发现某些IP地址频繁访问并抓取你的网站内容时,…...
UDP报文格式
UDP是传输层的一个重要协议,他的特性有面向数据报、无连接、不可靠传输、全双工。 下面是UDP报文格式: 1,报头 UDP的报头长度位8个字节,包含源端口、目的端口、长度和校验和,其中每个属性均为两个字节。报头格式为二…...
【工具类】获取日出日落时间的Java工具类
博主介绍:✌全网粉丝22W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
ES7【2016】、ES8【2017】新增特性(六)
ES7【2016】新增特性 幂指数操作符 在ES7【2016】中新增了幂指数操作,幂指数操作符是**。它用于指数计算 基本语法:baseValue ** exponent 参数说明:baseValue是基数,exponent是指数。 let base 2; let exponent 4; let resul…...
SparX:一种用于层次视觉Mamba和变换器网络的稀疏跨层连接机制
摘要 https://arxiv.org/pdf/2409.09649 由于动态状态空间模型(SSMs)能够以线性时间计算复杂度捕获长距离依赖关系,Mamba在自然语言处理(NLP)任务中表现出了显著的性能。这激发了基于Mamba的视觉模型的快速发展&#…...
Spring Boot 中实现 WebSocket 的方式
在 Spring Boot 中实现 WebSocket 的方式主要有以下几种,每种方式适用于不同的场景和需求: 1. 基于 Spring WebSocket 的实现 特点: 原生支持 WebSocket,基于 Spring 提供的 API。使用 WebSocketConfigurer 和 WebSocketHandler 配置端点和消息处理逻辑。可以通过拦截器访…...
时序数据库TDengine 3.3.5.0 发布:高并发支持与增量备份功能引领新升级
近日,TDengine 3.3.5.0 版本正式发布,带来了多项重磅更新与优化,从功能拓展到性能提升,再到用户体验进行了全面改进。本次更新围绕用户核心需求展开,涵盖了开发工具、数据管理、安全性、可视化等多个层面,为…...
Elasticsearch:Jira 连接器教程第一部分
作者:来自 Elastic Gustavo Llermaly 将我们的 Jira 内容索引到 Elaasticsearch 中以创建统一的数据源并使用文档级别安全性进行搜索。 在本文中,我们将回顾 Elastic Jira 原生连接器的一个用例。我们将使用一个模拟项目,其中一家银行正在开发…...
HarmonyOS NEXT开发进阶(六):HarmonyOS NEXT实现嵌套 H5 及双向通信
文章目录 一、前言二、鸿蒙应用加载Web页面2.1 加载网络地址页面2.2 加载本地H5页面 三、实现Web组件 H5 层与鸿蒙应用层进行相互通讯3.1 鸿蒙应用向 H5 页面发送数据3.2 H5页面向鸿蒙应用发送数据 四、拓展阅读 一、前言 随着HarmonyOS NEXT的快速发展,越来越多的…...
Myeclipse最新版本 C1 2019.4.0
Myeclipse C1 2019.4.0下载地址:链接: https://pan.baidu.com/s/1MbOMLewvAdemoQ4FNfL9pQ 提取码: tmf6 1.1、什么是集成开发环境? ★集成开发环境讲究-站式开发,使用这个工具即可。有提示功能,有自动纠错功能。 ★集成开发环境可以让软件开…...
深度学习中PyTorch张量的重塑操作
深度学习中PyTorch张量的重塑操作 张量操作类型 在我们深入具体的张量操作之前,让我们先快速浏览一下主要的操作类别,这些类别包括我们将要介绍的操作。我们有以下高级类别的操作: 重塑操作元素级操作归约操作访问操作 有很多单独的操作&…...
lua下标是可以从0开始
故事背景,策划搞了一个功能配置表,我看居然是0开始的,功能也正常。于是测试了下,还真的可以。网上看了资料确实可以,但是也有需要注意的问题 local test {[0] 0} for k,v in pairs(test)doprint(k,v) endhttps://bl…...
从AI原理到模型演进及代码实践 的学习二
参考:全面解析:从AI原理到模型演进及代码实践-CSDN博客 训练过程 Transformer仅一个Encoder模块就可以工作,可以处理信息抽取、识别、主体识别等任务,比如 BERT(Bidirectional Encoder Representations from Transfor…...
计算机组成原理(计算机系统3)--实验二:MIPS64乘法实现实验
一、实验目标: 实际运用WinMIPS64进行试验,以期更了解WinMIPS64的操作; 更加深入地了解MIPS程序的语法; 深入地了解在计算机中乘法的实现以及加法与乘法之间的关系。 二、实验内容 按照实验报告指导,完成相关操作…...
WPS excel使用宏编辑器合并 Sheet工作表
使用excel自带的工具合并Sheet表,我们会发现需要开通WPS会员才能使用合并功能; 那么WPS excel如何使用宏编辑器进行合并 Sheet表呢? 1、首先我们要看excel后缀是 .xlsx 还是 .xls ;如果是.xlsx 那么 我们需要修改为 .xls 注…...
ASP.NET Core - 配置系统之配置添加
ASP.NET Core - 配置系统之配置添加 2. 配置添加 2. 配置添加 配置系统可以读取到配置文件中的信息,那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中,builder(WebApplicationBuilder 对象) 中有一个 Config…...
【Redis】Redis 集群中节点之间如何通信?
【Redis】Redis 集群中节点之间如何通信? 一背景概述二通信协议Gossip 协议 三通信机制Gossip 消息类型(1).Ping消息(2).Pong消息(3).Meet消息(4).Fail消息 消息传播模式(1).反熵(Anti-entropy)(2).谣言传播(Rumor mongering) 四通信过程通信端口通信频率故障检测与…...
iOS - 内存对齐
1. 基本的内存对齐 // 对象内存对齐 struct objc_object {// isa 指针 8 字节对齐isa_t isa __attribute__((aligned(8))); };// 定义对齐常量 #define WORD_MASK 7UL // 字对齐掩码 #define WORD_SHIFT 3UL // 字对齐位移 #define WORD_SIZE 8 …...
RabbitMQ(四)
SpringBoot整合RabbitMQ SpringBoot整合1、生产者工程①创建module②配置POM③YAML④主启动类⑤测试程序 2、消费者工程①创建module②配置POM③YAML文件内配置: ④主启动类⑤监听器 3、RabbitListener注解属性对比①bindings属性②queues属性 SpringBoot整合 1、生…...
unity学习17:unity里的旋转学习,欧拉角,四元数等
目录 1 三维空间里的旋转与欧拉角,四元数 1.1 欧拉角比较符合直观 1.2 四元数 1.3 下面是欧拉角和四元数的一些参考文章 2 关于旋转的这些知识点 2.1 使用euler欧拉角旋转 2.2 使用quaternion四元数,w,x,y,z 2.3 使用quaternion四元数,类 Vector3.zero 这种…...
当PHP遇上区块链:一场奇妙的技术之旅
PHP 与区块链的邂逅 在技术的广袤宇宙中,区块链技术如同一颗耀眼的新星,以其去中心化、不可篡改、透明等特性,掀起了一场席卷全球的变革浪潮。众多开发者怀揣着对新技术的热忱与探索精神,纷纷投身于区块链开发的领域,试…...
Portainer.io安装并配置Docker远程访问及CA证书
Portainer.io安装并配置Docker远程访问及CA证书 文章目录 Portainer.io安装并配置Docker远程访问及CA证书一.安装 Portainer.io2.启动容器 二.docker API远程访问并配置CA安全认证1.配置安全(密钥)访问2.补全CA证书信息3.生成server-key.pem4.创建服务端签名请求证书文件5.创建…...
从漏洞管理到暴露管理:网络安全的新方向
在网络安全的快速发展中,传统的漏洞管理仍然是防御体系的基石。然而,面对日益复杂的威胁和不断增长的漏洞数量,单靠漏洞优先级评分(如VPR)已经无法满足现代安全需求。暴露管理这一全新的思维模式正在引领企业从被动应对…...
GraphRAG如何使用ollama提供的llm model 和Embedding model服务构建本地知识库
使用GraphRAG踩坑无数 在GraphRAG的使用过程中将需要踩的坑都踩了一遍(不得不吐槽下,官方代码有很多遗留问题,他们自己也承认工作重心在算法的优化而不是各种模型和框架的兼容性适配性上),经过了大量的查阅各种资料以…...
HTTP/HTTPS ⑤-CA证书 || 中间人攻击 || SSL/TLS
这里是Themberfue ✨上节课我们聊到了对称加密和非对称加密,实际上,单纯地非对称加密并不能保证数据不被窃取,我们还需要一个更加重要的东西——证书 中间人攻击 通过非对称加密生成私钥priKey和公钥pubKey用来加密对称加密生成的密钥&…...
MYSQL学习笔记(二):基本的SELECT语句使用(基本、条件、聚合函数查询)
前言: 学习和使用数据库可以说是程序员必须具备能力,这里将更新关于MYSQL的使用讲解,大概应该会更新30篇,涵盖入门、进阶、高级(一些原理分析);这一篇是讲解SELECT语句使用,包括基本、条件、聚合函数查询,…...
云原生周刊:Prometheus 3.0 正式发布
开源项目推荐 Achilles-SDK Achilles-SDK 是一个专为构建 Kubernetes 控制器而设计的开源开发工具包。它简化了控制器的开发流程,提供了强大的 API 和高效的抽象层,使开发者能够专注于业务逻辑的实现,而无需处理底层复杂性。Achilles-SDK 支…...
维护数据完整性(三)
立即检查的(IMMEDIATE)或者延迟检查的(DEFERRED) 使用 SET CONSTRAINTS 语句 SET CONSTRAINTS语句可以用来在事务内部改变约束的检查时机。以下是如何使用该语句的示例: SET CONSTRAINTS constraint_name DEFERRED;…...