K近邻模型、KNN算法1

案例

假设有用户对曾经购买过的商品的评分数据,如何利用这份数据给用户做商品推荐?或者对一个用户,应该给他推荐哪些商品?

思路逻辑图

Python代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#@Time: 2019-11-18 22:36
#@Author: gaoll

import time
import random
import math
from math import sqrt
import numpy as np
import matplotlib.pyplot as plt

#构造相似度函数
#欧几里德距离评价--基于距离的相似度评价
def sim_distance(vec1,vec2):
	#先确认两人有哪些是共同评价过的
	same = []
	for i in range(len(vec1)):
		if vec1[i] !='-1' and vec2[i]!='-1':
			#-1表示此人未对这个商品作为评价。筛选出两人共同评价过的商品
			same.append(i)
	n = len(same)
	if n == 0 :
		return 0  #两人没有共同评价过的商品,相似度为0
	sumsq = sum([pow(float(vec1[i])-float(vec2[i]),2) for i in same])
	return 1/(1+sqrt(sumsq))

#皮尔逊相关度评价--去中心化的余弦公式
def sim_pearson(vec1,vec2):
	#先确认两人有哪些是共同评价过的
	same = []
	for i in range(len(vec1)):
		if vec1[i] !='-1' and vec2[i]!='-1':
			same.append(i)
	n = len(same)
	if n == 0 :
		return 0 

	#求简单和
	sum1 = sum([ float(vec1[i]) for i in same ])
	sum2 = sum([ float(vec2[i]) for i in same ])
	#求平方和
	sumsq1 = sum([ pow(float(vec1[i]),2) for i in same ])
	sumsq2 = sum([ pow(float(vec2[i]),2) for i in same ])
	#求点积和
	pdsum = sum([ float(vec1[i])* float(vec2[i]) for i in same])
	#根据皮尔逊等价公式
	num = pdsum - (sum1*sum2)/n
	den = sqrt((sumsq1-pow(sum1,2)/n)*(sumsq2-pow(sum2,2)/n))
	if den == 0:
		return 0
	return num/den

#获取跟物品vec最相似的物品TopN
def topmatches(data,vec,k=5,simf=sim_pearson):
	scores = [(simf(data[i],vec),i) for i in range(len(data)) if data[i] !=vec]
	scores.sort()
	scores.reverse()
	return scores[0:k]


#解释Topmatch对应的物品名
def printtopmatches(scorestop,productnames):
	topmatchproduct = [] 
	for (sim,id) in scorestop:
		name = productnames[id]
		topmatchproduct.append((sim,name))
	return topmatchproduct

#为所有物品构造相互比较的数据集,并存放最相似的K项。这个数据可以提前计算好保存使用,只需定期更新计算即可
def calculateSimilarItems(data,k=10):
	result ={}
	n = len(data)
	for i in range(n):
		vec = data[i]
		if i%100 ==0:
			print('Processing: %d/%d'%(i,n)) #打印计算进程,大数据集时方便查看
		scorestop = topmatches(data,vec,k)
		result[i]=scorestop
	return result

#根据用户过去评价过的物品,从上述计算过的物品比较数据集中找出相近的物品,对于用户没有浏览或评价过的物品,根据KNN算法原理计算他可能给出的评价,根据这个预测的评价为该用户提供推荐
#利用所有人的评价的加权平均,为他人提供推荐。权重为相似度系数
def getrecommendations(similaritems,user_prefers,productnames):
	#遍历下用户已经评价过哪些商品
	userScored = set()
	for (score,item_id) in user_prefers:
		userScored.add(item_id)

	predict_scores = {}
	total_similarity = {}
	#遍历循环用户评价过的物品
	for (score,item_id) in user_prefers:
		#与物品item_id相似的Top
		topmatches = similaritems[item_id]
		for (similarity,match_id) in topmatches:
			if match_id in userScored:
				continue
			predict_scores.setdefault(match_id,0.0)
			predict_scores[match_id] += float(similarity)*float(score) #用户对已有物品评价 * 两物品的相似系数
			total_similarity.setdefault(match_id,0.0)
			total_similarity[match_id] +=float(similarity)
	
	ranking = [] 
	for id in predict_scores.keys():
		if total_similarity[id] == 0:
			ranking.append((0.0,productnames[id]))
		else:
			ranking.append((predict_scores[id]/total_similarity[id],productnames[id]) )
	#返回从高到低的评价
	ranking.sort()
	ranking.reverse()
	return ranking


if __name__ == '__main__':
	#加载数据
	import prefers_data
	productnames = []
	prefers_data = 'prefers.txt'
	scores = []
	for line in open(prefers_data):
		arr = line.strip('\n').split(',')
		name = arr[0]
		score = arr[1:]
		productnames.append(name)
		scores.append(score)

	#计算商品的相似系数
	ItemSimScores=calculateSimilarItems(scores)

	#用户曾经评价过商品1,2,给这个用户做商品推荐
	user_prefers=[(3.0,1),(4.5,2)]
	getrecommendations(ItemSimScores,user_prefers,productnames)

	

结果展示

Out:
[(4.288867926988538, 'Just My Luck'),
 (4.131737499239237, 'Superman returns'),
 (3.845774165407422, 'You and I'),
 ...]

Done

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐