birk 发表于 2015-11-29 10:10:17

[Python] 应用kNN算法预测豆瓣电影用户的性别

应用kNN算法预测豆瓣电影用户的性别

摘要
  本文认为不同性别的人偏好的电影类型会有所不同,因此进行了此实验。利用较为活跃的274位豆瓣用户最近观看的100部电影,对其类型进行统计,以得到的37种电影类型作为属性特征,以用户性别作为标签构建样本集。使用kNN算法构建豆瓣电影用户性别分类器,使用样本中的90%作为训练样本,10%作为测试样本,准确率可以达到81.48%。

实验数据
  本次实验所用数据为豆瓣用户标记的看过的电影,选取了274位豆瓣用户最近看过的100部电影。对每个用户的电影类型进行统计。本次实验所用数据中共有37个电影类型,因此将这37个类型作为用户的属性特征,各特征的值即为用户100部电影中该类型电影的数量。用户的标签为其性别,由于豆瓣没有用户性别信息,因此均为人工标注。
  数据格式如下所示:
X1,1,X1,2,X1,3,X1,4……X1,36,X1,37,Y1
X2,1,X2,2,X2,3,X2,4……X2,36,X2,37,Y2
…………
X274,1,X274,2,X274,3,X274,4……X274,36,X274,37,Y274
  示例:
0,0,0,3,1,34,5,0,0,0,11,31,0,0,38,40,0,0,15,8,3,9,14,2,3,0,4,1,1,15,0,0,1,13,0,0,1,1 0,1,0,2,2,24,8,0,0,0,10,37,0,0,44,34,0,0,3,0,4,10,15,5,3,0,0,7,2,13,0,0,2,12,0,0,0,0
  像这样的数据一共有274行,表示274个样本。每一个的前37个数据是该样本的37个特征值,最后一个数据为标签,即性别:0表示男性,1表示女性。

kNN算法
  k-近邻算法(KNN),是最基本的分类算法,其基本思想是采用测量不同特征值之间的距离方法进行分类。
  算法原理:存在一个样本数据集合(训练集),并且样本集中每个数据都存在标签(即每一数据与所属分类的关系已知)。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较(计算欧氏距离),然后提取样本集中特征最相似数据(最近邻)的分类标签。一般会取前k个最相似的数据,然后取k个最相似数据中出现次数最多的标签(分类)最后新数据的分类。
  在此次试验中取样本的前10%作为测试样本,其余作为训练样本。
  首先对所有数据归一化。对矩阵中的每一列求取最大值(max_j)、最小值(min_j),对矩阵中的数据X_j,
X_j=(X_j-min_j)/(max_j-min_j) 。
  然后对于每一条测试样本,计算其与所有训练样本的欧氏距离。测试样本i与训练样本j之间的距离为:
distance_i_j=sqrt((Xi,1-Xj,1)^2+(Xi,2-Xj,2)^2+……+(Xi,37-Xj,37)^2) ,
对样本i的所有距离从小到大排序,在前k个中选择出现次数最多的标签,即为样本i的预测值。

实验结果
  首先选择一个合适的k值。 对于k=1,3,5,7,均使用同一个测试样本和训练样本,测试其正确率,结果如下表所示。
  表1 选取不同k值的正确率表




k
1
3
5
7


测试集1
62.96%
81.48%
70.37%
77.78%


测试集2
66.67%
66.67%
59.26%
62.96%


测试集3
62.96%
74.07%
70.37%
74.07%


平均值
64.20%
74.07%
66.67%
71.60%


  由上述结果可知,在k=3时,测试的平均正确率最高,为74.07%,最高可以达到81.48%。
  上述不同的测试集均来自同一样本集中,为随机选取所得。

Python代码
  这段代码并非原创,来自《机器学习实战》(Peter Harrington,2013),并有所改动。



#coding:utf-8
from numpy import *
import operator
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape
diffMat = tile(inX, (dataSetSize,1)) - dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()   
classCount={}         
for i in range(k):
voteIlabel = labels]
classCount = classCount.get(voteIlabel,0) + 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1))   #element wise divide
return normDataSet, ranges, minVals

def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines())         #get the number of lines in the file
returnMat = zeros((numberOfLines,37))       #prepare matrix to return
classLabelVector = []                     #prepare labels return   
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip()
listFromLine = line.split(',')
returnMat = listFromLine
classLabelVector.append(int(listFromLine[-1]))
index += 1
fr.close()
return returnMat,classLabelVector
def genderClassTest():
hoRatio = 0.10      #hold out 10%
datingDataMat,datingLabels = file2matrix('doubanMovieDataSet.txt')       #load data setfrom file
normMat,ranges,minVals=autoNorm(datingDataMat)
m = normMat.shape
numTestVecs = int(m*hoRatio)
testMat=normMat
trainMat=normMat
trainLabels=datingLabels
k=3
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(testMat,trainMat,trainLabels,k)
print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels)
if (classifierResult != datingLabels):
errorCount += 1.0
print "Total errors:%d" %errorCount
print "The total accuracy rate is %f" %(1.0-errorCount/float(numTestVecs))
  

参考文献
  (美)Peter Harrington;李锐,李鹏,曲亚东,王斌译者. 机器学习实战. 北京:人民邮电出版社, 2013.06.
页: [1]
查看完整版本: [Python] 应用kNN算法预测豆瓣电影用户的性别