协同过滤-爱你所爱

     协同过滤,之所以协同是因为该方法是基于其他用户进行推荐的。工作流程如下:假设我们的任务是向你推荐一本书。我们会在网络上搜索与你相似兴趣的用户。一旦找到了这个用户,就看看这个用户喜欢的书,然后将其推荐给你。

      如何寻找相似用户?一般而言,通过计算两个用户的距离,两个用户距离越小则相似度越高。先来看距离的定义。

1 曼哈顿距离(Manhattan Distance)N维下,表示为:d(x,y)=sigma(|xk-yk|) (k=1...n)

2 欧氏距离 (Euclidean Distance)  N维下,表示为:d(x,y)=(sigma((xk-yk)2))1/2 (k=1,2...n)

3 明氏距离 (Minkoeski Distance) N维下,表示为:d(x,y)=(sigma(|xk-yk|r))1/r  ,可见,当r=1时,即为曼哈顿距离,r=2时为欧氏距离,r=∞时,为上确界距离(Supermun Distance)

以下是8个用户对8个音乐的评分。通过计算两个人的明氏距离来实现推荐

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ : '小糖果'

import xlrd
import json

def load_datas(filepath):
	wb = xlrd.open_workbook(filepath)
	table = wb.sheet_by_name('Sheet1')
	users = {}                               # 用户字典
	for c in range(1,table.ncols):           # 循环每一列
		name = table.cell_value(0,c)         # 用户名
		users[name] = {}                     # 建立空字典
		for r in range(1,table.nrows):		 # 循环每一列
			if table.cell_value(r,c):        # 如果当前值不空
				key = table.cell_value(r,0)
				value = float(table.cell_value(r,c))   #建立字典
				users[name][key] = value
	return users

def minkowski(rating1,rating2,r):
	"""
	Compute the minkowski diatance between tow uses

	"""
	distance = 0.0
	for key in rating1:
		if key in rating2:
			distance += pow((abs(rating1[key]-rating2[key])),r)
	return pow(distance,1./r)

def find_nearest_neighber(username,users):
	"""
	Create a sorted list bsaed on their distance

	"""
	neighbers = []
	for user in users:
		if username != user:
			d = minkowski(users[username],users[user],2)
			neighbers.append((d,user))
	# sort by distance
	neighbers.sort(cmp)
	return neighbers # 返回最近的人

def recommend(username,users):
	"""
  	Give list of recomends

	"""
	recommends = []
	neighber = find_nearest_neighber(username,users)[0][1]
	for artist in users[neighber]:
		if artist not in users[username]:
			recommends.append((artist,users[neighber][artist]))
	return sorted(recommends,key = lambda artist_tupe:artist_tupe[1],reverse = True)

def test():
	filepath = r'C:\Users\TD\Desktop\PythonProject\records.xlsx'
	users = load_datas(filepath)
	print recommend('Chan',users)
	print recommend('Hailey',users)

if __name__ == '__main__':
	test()

用户评级差异

    对用户评级结果进行考察发现,单个用户的评级行为差距很大。上表中,Bill想避免极端结果,它的评级都在2到4之间,而Jordy好像喜欢任何乐队,他的评级都在4到5.而Hailey只有两种选择,不是1就是4.解决这个问题的方法是考虑皮尔逊相关系数(Pearson Correlation Coefficient) 。皮尔逊相关系数是度量两个变量的相关性指标,取值区间为[-1,1]

值越接近1表示相关性越好。皮尔逊计算公式如下:

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}}.

但是一般而言,上述公式计算复杂度达到O(n2)。实际应用时采用以下近似公式: 

有这样一个列子,在音乐播放器上有1.5亿首歌曲,现在来考虑歌曲播放的次数问题。对于个人而言,播放的歌曲数有限,可能就4000首,也就是说其他歌曲播放次数为0,于是对于个人来说,得到的数据信息是很稀少的。而在1.5亿首个上去比较2个人时,绝大部分公共为0.这种情况下,无论使用明氏距离还是皮尔逊相关系数都是不明智的。我们还有一种选择,那就是余弦相似度,公式如下:  

也就是欧氏空间中两个N维向量的夹角的余弦。余弦相似度取值在1到-1之间,1表示完全相似,-1表示完全不相似。

那么问题来了。上面说了三个相似度,在时间问题中如何选择呢?可以按照以下规则:

 1 如果数据稠密(几乎所有的属性搜没有0值),那么使用明氏距离比较合适

 2 如果数据受到分数贬值(即为不同用户使用不同的评价范围),则使用皮尔逊相关系数比较合理

 3 如果数据稀疏(很多0出现),那么考虑使用余弦相似度是合理的

一些怪异的事情:

   当我们在依赖最相似的用户进行推荐时,该用户的个人怪癖也会被推荐,而这些怪癖别是可能是不喜欢的。一种解决的办法是基于多个相似用户进行推荐。这里采用k近邻法。在协同过滤的k近邻法中,我们用k个最相似的用户来确定推荐结果。每一个用户根据相似度大小获得一个权重。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ : '小糖果'

import json
import sys
from math import sqrt
from pprint import  pprint

class Recommender(object):
	def __init__(self, data,k = 1,n = 5):
		"""
		initialize recomender,data is  dict
		k is the value for k nearest neighbor
		n is the maximum number recommendations to make
		"""
		self.k = k
		self.n = n
		if type(data).__name__ == 'dict':
			self.data = data
		else:
			raise Exception('输入数据类型必须是字典')
			sys.exit(1)

	def pearson(self,rating1, rating2):
		sum_xy = 0
		sum_x = 0
		sum_y = 0
		sum_x2 = 0
		sum_y2 = 0
		n = 0
		for key in rating1:
			if key in rating2:
				n += 1
				x = rating1[key]
				y = rating2[key]
				sum_xy += x * y
				sum_x += x
				sum_y += y
				sum_x2 += x ** 2
				sum_y2 += y ** 2
		if n == 0:
			return 0
		denominator = sqrt(sum_x2 - (sum_x ** 2) / n) * sqrt(sum_y2 - (sum_y ** 2) / n)
		if denominator == 0:
			return 0
		else:
			return (sum_xy - sum_x * sum_y / n) / denominator

	def find_nearest_neighbors(self, username):
		neighbors = []
		for user in self.data:
			if username != user:
				per = self.pearson(self.data[user],
								   self.data[username])
				neighbors.append((user,per))

		return sorted(neighbors,key = lambda element:element[1],reverse = True)

	def recommend(self,username):
		neighbors = self.find_nearest_neighbors(username)
		tol_distance = 0
		for i in range(self.k):
			tol_distance += neighbors[i][1]
		recommends = {}
		user_ratings = self.data[username]
		for i in range(self.k):
			name = neighbors[i][0]
			neighbor_ratings = self.data[name]
			weight = neighbors[i][1]/tol_distance
			for artist in neighbor_ratings:
				if artist not in user_ratings:
					try:
						recommends[artist] += weight*neighbor_ratings[artist]
					except KeyError:
						recommends[artist] = weight*neighbor_ratings[artist]
		recommends = [(item[0],item[1]) for item in recommends.items()]
		recommends.sort(key = lambda element:element[1],reverse = True)
		return recommends[:self.n]

def test():
	filepath = r'C:\Users\TD\Desktop\PythonProject\records.json'
	with open(filepath,'rb') as f:
		data = json.load(f)
	instance  = Recommender(data)
	print instance.recommend('Hailey')

if __name__ == '__main__':
	test()

  

 

转载于:https://www.cnblogs.com/td15980891505/p/5978902.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值