电影推荐系统代码详细解释[python]
1.
[python]
1. # -*- coding: utf8 -*-
2. '''''
3. Created on 2015-06-22
4. @author: Lockvictor
5. '''
6. import sys, random, math
7. import os
8. from operator import itemgetter
9. random.seed(0)
10. class ItemBasedCF():
11. ''''' TopN recommendation - ItemBasedCF '''
12. def __init__(self):
13.        ainset = {}
14.        stset = {}
15. #此处依然⽆法输出
16. print("类型=",ainset))
17.
18.        self.n_sim_movie = 20#训练集⽤的电影数量
19.        self.n_rec_movie = 10#推荐电影数量
20.
21.        vie_sim_mat = {}#初始化为字典
22.        vie_popular = {}#初始化为字典
23.        vie_count = 0
24.
25. print >> sys.stderr, 'Similar movie number = %d' % self.n_sim_movie
26. print >> sys.stderr, 'Recommended movie number = %d' % self.n_rec_movie
27.
28. #def __init__(self)解释
29. #初始化7个变量,四个字典,三个整形
30. #self代表this指针
31. #意思是,这些变量是归这个类管辖的
32. #__init__是构造函数,⽤来初始化,写法固定
33.
34.    @staticmethod
35. def loadfile(filename):
36. ''''' load a file, return a generator. '''
37. print("loadfile filename=", filename)
38.        fp = open(filename, 'r')
39. for i, line in enumerate(fp):
40. yield line.strip('\r\n')
41. if i % 100000 == 0:
42. print >> sys.stderr, 'loading %s(%s)' % (filename, i)
43.        fp.close()
44. print >> sys.stderr, 'load %s succ' % filename
45.
46. #def loadfile(filename)解释
46. #def loadfile(filename)解释
47. #这⾥的filename指的是ratings.dat
48. #line代表数据集中每⾏的内容
49. #i是个计数器,表⽰当前读到第⼏⾏了,每当读取到100000⾏的整数倍
50. #输出语句报个信⼉。
51. #@staticmethod表⽰这个函数可以定义在类的外⾯
52. #enumerate是为了配合i⽽存在的,
53. #也就是说,这个for循环原本可以简化为:
54. #for line in (fp)
55. #yield line.strip('\r\n')
56. #line.strip('\r\n')
57. #表⽰删除每⾏的回车符的ASCII编码
58. #yield是加强版的return,类似于C语⾔⾥⾯的升级版return
59. #可以返回多个元素,这⾥估计是返回多个属性的意思吧
60. >>>>>>>>>>
61.
62. def generate_dataset(self, filename, pivot=0.7):
63. ''''' load rating data and split it to training set and test set '''
64. print("generate_data filename=",filename)
65.        trainset_len = 0
66.        testset_len = 0
67. >>>>####added by yuchi as follows>>>>>>#
68.        train_file = os.getcwd() + '/'#数据集分割后的得到的训练集
69.        output1 = open(train_file, 'w')
70.        test_file = os.getcwd() + '/'#数据集分割后得到的测试集
71.        output2 = open(test_file, 'w')
72. >>>>>added by yuchi above>>>>>>#
73. for line in self.loadfile(filename):
74.            user, movie, rating, _ = line.split('::')
75. # split the data by pivot
76. if (random.random() < pivot):#待会⼉需要改回来,⽤上⾯⼀句替换
77.                ainset.setdefault(user, {})
78.                ainset[user][movie] = int(rating)
79. #print("trainset[user][movie]=",trainset[user][movie])
80.                trainset_len += 1#70% of all data
81. >>>>####added by yuchi above>>>>###
82.                train_str = str(user) + ' ' + str(movie) + ' ' +  '%d' %ainset[user][movie] + '\n'#在前⾯加上 '%d' %是为
了让数字转化为字符串
83.                output1.write(train_str)
84. >>>>####added by yuchi above>>>>###
85. else:
86.                stset.setdefault(user, {})
87.                stset[user][movie] = int(rating)
88.                testset_len += 1#30% of all data
89.                test_str = str(user) + ' ' + str(movie) + ' ' +  '%d' %stset[user][movie] + '\n'
90. >>>>####added by yuchi above>>>>###
91.                output2.write(test_str)
92. >>>>####added by yuchi above>>>>###
93.        output1.close()
94.        output2.close()
95. print >> sys.stderr, 'split training set and test set succ'
96. print >> sys.stderr, 'train set = %s' % trainset_len
97. print >> sys.stderr, 'test set = %s' % testset_len
98. >>>>####下⾯是解释>>>###
99. #def generate_dataset函数解释
100. #    user, movie, rating, _ = line.split('::')
100. #    user, movie, rating, _ = line.split('::')
101. #这⾥的双冒号是分隔符,⽤来获取属性,这⾥的单独的⼀个下划线“_”是⼀个变量名,代表中的第四个属性,Timestamp(时间戳)。
102. #所以可以直接⽤print语句输出这个下划线变量。
103. #这个函数中的filename也是指的是ratings.dat
104. #这个函数既需要产⽣数据集,⼜需要产⽣测试集
105. #因为在构造函数__init__中初始化了两个字典(字典其实就是C + +中的map类型)变量:
106. #trainset和testset,他们分别表⽰训练集和测试集
107. #所以在这⾥使⽤_len分别对这两个字典变量的容量进⾏初始化。
108. #random.random()
109. #⽣成0和1之间的随机浮点数float
110. #由于random.random会随机⽣成浮点数,pivot设置为0.7, 也就是说,这个filename中
111. #会有70%变成训练⽤数据集,30 % 变成测试⽤数据集。
112. #那么哪些数据会成为那70 % 中的⼀部分, 哪些数据会成为30 % 的⼀部分呢?随机决定。
113. #因此,在分割filename的时候:
114. #也就是说:
115. #⼀堆糖果,分给两个⼩朋友A和B,设定临界点为2,抛骰⼦,如果抛到1和2,⼀颗糖果归A;116. #如果抛到3~6,⼀颗糖果归B, 最后分成两堆。
117. >####
118. #70 % 的概率会执⾏if语句,变成训练⽤数据集
119. #添加完后,⽤以下语句表⽰容量+1
120. #trainset_len += 1
121. >>
122. #30 % 的概率会执⾏else语句,成为测试集
123. #添加完后,⽤以下语句表⽰容量+1
124. #testset_len += 1
125. >>
126. #其中
127. #int(rating)⽤来数据格式转化
128. #user, movie, rating, _ = line.split('::')与下⾯的
129. #ainset.setdefault(user, {})
130. #对应
有个叫什么代码的电影131. #单独的⼀个_表⽰这个属性本代码不关⼼,随便起个名字,占坑
132. #for循环在执⾏每次循环时,都会得到新的⼀条数据,⽤user,movie和rating和_去获得这个数据中的四个属性
133. #然后在trainset这个字典变量中建⽴映射关系。
134. #ainset.setdefault(user, {})
135. #表⽰对字典的新⼀项初始化。
136. #ainset[user][movie] = int(rating)
137. #表⽰索引变量是user、movie
138. #索引值是rating。
139. #总得来讲,也就是说,从的每⾏的四个属性中,获取三个属性,丢掉⼀个属性,来重新建⽴数据集中的⼀个项。
140. #函数的功能,从ratings中筛选得到有⽤的属性,重新建⽴映射关系,⼀部分变成训练⽤数据集,⼀部分变成测试⽤数据集。
141. #-----------------------------------------------------------------------------
142. def calc_movie_sim(self):#这个函数总共3个双重for循环
143. ''''' calculate movie similarity matrix '''
144. print >> sys.stderr, 'counting movies number '
145. for user, movies ainset.iteritems():#训练集的前两两个属性就是⽤户和电影编号,利⽤for循环遍历整个测试集。
146. #这⾥的movies表⽰某特定⽤户看过的所有电影,所以movies不是指⼀部电影,是⼀个集合
147. for movie in movies:#
148. if movie not vie_popular:# have been defined as map(dictionary)
149.                    vie_popular[movie] = 0#流⾏度指的是⽤户对电影的评价数量。
150.                vie_popular[movie] += 1#这⾥没法直接写⼊txt,因为相同的电影,流⾏度刷新后,新的⼀⾏写⼊txt,旧的⼀⾏不会被删除
151. #这⾥的mouvie_popular在离开for循环以后得到的是两列属性,movieID和评价次数。
151. #这⾥的mouvie_popular在离开for循环以后得到的是两列属性,movieID和评价次数。
152. print >> sys.stderr, 'count movies number and popularity succ'
153. print("流⾏度初步计算结束")
154. # save the total number of movies
155.        vie_count = vie_popular)#流⾏电影的容量
156. print >> sys.stderr, 'total movie number = %d' % vie_count
157. #-------------------------------以上得到的是每部电影被评价的次数--------------------------------------
158. # count co-rated users between items
159. #movie_sim_mat是相似度矩阵的意思
160.        itemsim_mat = vie_sim_mat#movie_sim_mat已经在构造函数中进⾏初始化
161. #同样地,itemsim_mat也是个字典,
162. print >> sys.stderr, 'building co-rated '
163.
164. for user, movies ainset.iteritems():
165. for m1 in movies:
166. for m2 in movies:
167. if m1 == m2: continue#数据没清洗过的情况下使⽤
168.                    itemsim_mat.setdefault(m1,{})
169.                    itemsim_mat[m1].setdefault(m2,0)
170.                    itemsim_mat[m1][m2] += 1#被同⼀个⽤户评过分的两个不同电影,他们在相似度矩阵中+1
171. #注意,对itemsim_mat操作的同时,改变了movie_sim_mat
172. #也就是说,类似于C++中,itemsim_mat就是vie_sim_mat的别名
173. #注意,vie_sim_mat是对象中的成员,itemsim_mat不是
174. #注意,代码中只有vie_sim_mat,不存在movie_sim_mat
175. #注意,代码中只有itemsim_mat,不存在self.itemsim_mat
176. >>>>以上是相似度矩阵的"初步计算",没有使⽤很复杂的计算⽅法,后⾯还要进⾏计算,才能得到最终的相似度矩阵
177. # print >> sys.stderr, 'build co-rated users matrix succ'
178. #物品的流⾏度即指有多少⽤户为某物品评分
179. # calculate similarity matrix
180. print("☆☆☆☆☆☆×××××××××××××☆☆☆☆☆☆☆", vie_sim_mat[movie].items())
181. print >> sys.stderr, 'calculating movie '
182.        simfactor_count = 0#控制程序运⾏进度输出的,没啥⽤
183.        PRINT_STEP = 2000000#控制程序运⾏进度输出的,没啥⽤
184.
185. for m1, related_movies in itemsim_mat.iteritems():#注意,这⾥使⽤的是余弦相似度
186. for m2, count in related_movies.iteritems():
187.                itemsim_mat[m1][m2] = count / math.sqrt(
188.                        vie_popular[m1] * vie_popular[m2])
189.                simfactor_count += 1
190. if simfactor_count % PRINT_STEP == 0:
191. print >> sys.stderr, 'calculating movie similarity factor(%d)' % simfactor_count
192. print("☆☆☆☆☆☆×××××××××××××☆☆☆☆☆☆☆", vie_sim_mat[movie].items())
193. print >> sys.stderr, 'calculate movie similarity matrix(similarity factor) succ'
194. print >> sys.stderr, 'Total similarity factor number = %d' %simfactor_count
195.
196. # -----------------------------------------------------------------------------
197.
198. def recommend(self, user):
199. ''''' Find K similar movies and recommend N movies. '''
200.        K = self.n_sim_movie#在构造函数中已经定义和初始化
201.        N = self.n_rec_movie#在构造函数中已经定义和初始化,某特定⽤户将会被推荐的电影数量
202.        rank = {}
203.        watched_movies = ainset[user]
204. #这⾥之所以有sort函数是为了推荐符合度最⾼的⼏个电影给⽤户
205. for movie, rating in watched_movies.iteritems():#从数据集中提取某个⽤户看过的电影中的两个数据
206. for related_movie, w in vie_sim_mat[movie].items(),key=itemgetter(1), reverse=True)[:K]:#从⼤到⼩排序
207. #上⾯的movie_sim_mat是个具备有3个属性的字典:两个相似的电影,以及他们的相似度,所以w是相似度的意思,related_movie是根据代码后⾯的[movie]得到的相关电影
208. #因为⼀⾏有许多属性,所以上⾯这句代码中items的意思是取得该属性所在⾏的其他所有属性
209. #由于movie_sim_mat中本来每⾏数据只有三个属性,由于这⾥使⽤了[movie]索引,所以得到剩下两个属性
210. #⽽上⾯这句代码后⾯使⽤了itemgetter(1),表⽰对所得到的两个属性,按照第2的属性(也就是相似度系数)进⾏排序
211. #reverse=true代表从⼤⼩排序,在代码中的意思是,在相似度矩阵中获取与movie这个变量相关的所有电影,并且按照相似度系数的⼤⼩从⼤到⼩排序
212. #最后[:K]:表⽰取得K个项
213. if related_movie in watched_movies:
214. continue#如果相关电影在已经看过的电影中,则跳过,进⾏下⼀轮循环(我想这应该是数据没有清洗导致的)
215.                rank.setdefault(related_movie, 0)#这句话不属于上⾯的if的管辖范畴
216.                rank[related_movie] += w * rating
217. # return the N best movies
218. # 以上双循环的意思是,对某⽤户看过的所有电影进⾏遍历,
219. # 对于某个特定的已经看过的电影⽽⾔,便利相似度矩阵中所有和这个“已经看过的电影”相关的电影220. # 相关的电影的意思是,矩阵中都是aij中,i对应于movie,j对应于related_movie
221. # vie_sim_mat[movie].items()会返回两个参数,第⼀个参数赋值给related_movie,
222. # 第⼆个参数赋值给w,代表“movie”和“related_movie”这两个变量的相似度,相似度在前⾯已经计算得出223. # 他这⾥把权重系数去乘以评分次数,制造出⼀个参数w*rating,作为rank中排序的指标
224. # 来计算与“已经看过的每个电影”相关的
225. return sorted(rank.items(), key=itemgetter(1), reverse=True)[:N]
226. # 这句return的意思是相当于excel中的排序,这⾥的itemgetter(1)表⽰按照rank中
227. # 数据的第⼆项对rank中所有数据进⾏排序
228. # 注意itemgetter(i)的括号中的序号i从0开始,代表第1项
229. # 另外注意,这⾥rank虽然是字典,但是return返回的类型是list
230.
231.
232. def evaluate(self):#这个是⽤来评价推荐的电影是否准确的。
233. ''''' return precision, recall, coverage and popularity '''
234. print >> sys.stderr,''
235.
236. >>>>>####
237.        N = self.n_rec_movie
238. #  varables for precision and recall
239.        hit = 0
240.        rec_count = 0
241.        test_count = 0
242. # varables for coverage
243.        all_rec_movies = set()
244. # varables for popularity
245.        popular_sum = 0
246.            f = open("", "w")
247. for i, user in ainset):#i对应enumerate,user对应测试集trainset
248. if i % 500 == 0:
249. print >> sys.stderr, 'recommended for %d users' % i
250.            test_movies = (user, {})
251.            rec_movies = end(user)#这⼀句代表推荐结果,注意推荐结果的类型是list,不是dict(字典)252.            recommend_str = str(user) + ' ' + str(rec_movies) + ' ' +'\n'
253.                f.write(str(recommend_str))
254. #后⾯的这个for循环是⽤来评价推荐的电影是否准确的
255. for movie, w in rec_movies:
256. if movie in test_movies:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。

发表评论