在MovieLens1M数据集上使⽤深度学习进⾏评分预测
在MovieLens 1M数据集上使⽤深度学习进⾏评分预测
MovieLen 1M数据及简介
电影元数据
电影元数据的格式为:MovieID::Title::Genres。
Title:电影名IMDB提供的电影名相同(包括发布年份)
Genres:多种电影题材由是“|”分隔,题材种类有以下18种:
Action
Adventure
Animation
Children’s
Comedy
Crime
Documentary
Drama
Fantasy
Film-Noir
Horror
Musical
Mystery
Romance
Sci-Fi
Thriller
Warpython新手代码userid
Western
MovieID:模型MovieID没有对应的电影(MovieID不是连续递增的)
⽤户属性信息
⽤户属性信息的格式为:UserID::Gender::Age::Occupation::Zip-code,⽤户属性信息是⽤户⾃愿填写的,真实性没有做过校验。
Gender:“M”表⽰男,“F”表⽰⼥
Age:年龄值有以下⼏种:
1: “⼩于18岁”
18: “18-24”
25: “25-34”
35: “35-44”
45: “45-49”
50: “50-55”
56: “56+”
Occupation:职业有以下⼏种:
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”
电影评分
电影评分的格式为:UserID::MovieID::Rating::Timestamp UserID: [1-6040]
MovieIDs:[1,3952]
Ratings:1-5的整数
Timestamp:时间戳
每个⽤户⾄少有20个评分
数据预处理与⽹络模型设计
MovieLens数据集中,⽤户特征中UserID、Gender、Age、Job以及电影特征中MovieID都可以认为是类别型数据,通常使⽤One-Hot编码。
但是MovieID和UserID值得类型⽐较多,如果使⽤One-Hot编码,每个值都会被编码成⼀个维数很⾼的稀疏向量,作为神经⽹络输⼊是计算量很⼤。
除此之外,采⽤One-Hot编码,不同属性值的距离都是相等的, ⽐如“⼩于18岁”和“50+”与“45-49”与“50+”的距离平⽅都是2。
所以在数据预处理阶段,我们不使⽤One-Hot编码,⽽仅仅将这些数据编码成数字,⽤这些数据当作嵌⼊矩阵的索引。
神经⽹络的第⼀层使⽤嵌⼊层,嵌⼊矩阵通过学习得到。
电影题材和电影名⽐较特殊,他们可以视作多值属性,且长度不⾏等。对于电影题材,因为类型不多,可以直接使⽤Multi-Hot编码,在神经⽹络中通过编码后的向量与嵌⼊矩阵相乘实现不同长度的输⼊。对于电影名的处理稍微复杂⼀点,⾸先创建word->int的映射字典,然后使⽤数字列表编码,并填充为相同的长度,经过⼀个LSTM⽹络,并对⽹络的所有输出求均值得到电影名特征。
数据预处理
UserID、Occupation、MovieID不变
Gender字段:需要将‘F’和‘M’转换成0和1
Age字段:转成7个连续数字0-6
Genres字段:多值属性,使⽤Multi-Hot编码,维数为18
Title字段:创建word->int的映射字典,然后使⽤数字列表编码,并填充为相同的长度,维数为15
电影题材的multi-hot编码函数
def genres_multi_hot(genre_int_map):
"""
电影类型使⽤multi-hot编码
:param genre_int_map:genre到数字的映射字典
:return:
"""
def helper(genres):
genre_int_list =[genre_int_map[genre]for genre in genres.split(b'|')]
multi_hot = np.zeros(len(genre_int_map))
multi_hot[genre_int_list]=1
return multi_hot
return helper
电影数字列表编码函数
def title_encode(word_int_map):
"""
将电影Title转成长度为15的数字列表,如果长度⼩于15则⽤0填充,⼤于15则截断
:
param word_int_map:word到数字的映射字段
:return:
"""
def helper(title):
title_words =[word_int_map[word]for word in title.split()]
if len(title_words)>15:
return np.array(title[:15])
else:
title_vector = np.zeros(15)
title_vector[:len(title_words)]= title_words
return title_vector
return helper
数据预处理函数
def load_data(dataset_zip):
"""
Load Dataset from Zip File
"""
with zipfile.ZipFile(dataset_zip)as zf:
# 读取User数据
with zf.open('ml-1m/users.dat')as users_raw_data:
users_title =['UserID','Gender','Age','JobID','Zip-code']
users = pd.read_table(users_raw_data, sep=b'::', header=None, names=users_title, engine='python')
users = users.filter(regex='UserID|Gender|Age|JobID')
# 改变User数据中性别和年龄
gender_map ={b'F':0, b'M':1}
users['GenderIndex']= users['Gender'].map(gender_map)
age_map ={val: ii for ii, val in enumerate(set(users['Age']))}
users['AgeIndex']= users['Age'].map(age_map)
# 读取Movie数据集
with zf.open('ml-1m/movies.dat')as movies_raw_data:
movies_title =['MovieID','Title','Genres']
movies = pd.read_table(movies_raw_data, sep=b'::', header=None, names=movies_title, engine='python') # 将Title中的年份去掉
pattern = repile(b'^(.*)\((\d+)\)$')
movies['TitleWithoutYear']= movies['Title'].map(lambda x: pattern.match(x).group(1)) # 电影题材Multi-Hot编码
genre_set =set()
for val in movies['Genres'].str.split(b'|'):
genre_set.update(val)
genre_int_map ={val: ii for ii, val in enumerate(genre_set)}
movies['GenresMultiHot']= movies['Genres'].map(genres_multi_hot(genre_int_map))
# 电影Title转数字列表,word的下标从1开始,0作为填充值
word_set =set()
for val in movies['TitleWithoutYear'].str.split():
word_set.update(val)
word_int_map ={val: ii for ii, val in enumerate(word_set, start=1)}
movies['TitleIndex']= movies['TitleWithoutYear'].map(title_encode(word_int_map))
# 读取评分数据集
with zf.open('ml-1m/ratings.dat')as ratings_raw_data:
ratings_title =['UserID','MovieID','ratings','timestamps']
ratings = pd.read_table(ratings_raw_data, sep=b'::', header=None, names=ratings_title, engine='python') ratings = ratings.filter(regex='UserID|MovieID|ratings')
# 合并三个表
data = pd.(ratings, users), movies)
# 将数据分成X和y两张表
features, targets = data.drop(['ratings'], axis=1), data[['ratings']]
return features, targets, age_map, gender_map, genre_int_map, word_int_map, users, movies
模型设计
本⽂使⽤的⽹络模型如上图所⽰。⽹络可以分成两⼤部分,分别是⽤户特征⽹络和电影特征⽹络,这两个⼦⽹络最终通过全连接层输出⼀个200维的向量,作为⽤户特征和电影特征。
有了⽤户特征向量和电影特征向量之后,就可以通过各种⽅式拟合评分,本⽂中将两个输⼊通过只有⼀个神经元的全连接层,将输出作为评分,将MSE作为损失函数去优化⽹络。
⽤户特征⽹络
UserID和Age、Gender、Job的处理⽅式相同,⾸先将输⼊作为索引从嵌⼊矩阵中取出对应的特征向量,其中UserID编码为32维向量,其他特征编码为16维向量。
然后分别在其后添加⼀个全连接层和⼀个dropout层,全连接层的神经元个数为32。最后将得到的四个32维的向量拼接到⼀起形成⼀个128维的向量,作为全连接层的输⼊,最后输出⼀个200维的⽤户特征向量。
⽤户特征⽹络核⼼代码
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论