摘要
本文認為不同性別的人偏好的電影型別會有所不同,因此進行了此實驗。利用較為活躍的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),並有所改動。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#coding:utf-8 from numpy import * import operator def classify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] 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[sortedDistIndicies[i]] classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet)) m = dataSet.shape[0] 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[index,:] = listFromLine[0:37] 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[0] numTestVecs = int(m*hoRatio) testMat=normMat[0:numTestVecs,:] trainMat=normMat[numTestVecs:m,:] trainLabels=datingLabels[numTestVecs:m] k=3 errorCount = 0.0 for i in range(numTestVecs): classifierResult = classify0(testMat[i,:],trainMat,trainLabels,k) print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]) if (classifierResult != datingLabels[i]): errorCount += 1.0 print "Total errors:%d" %errorCount print "The total accuracy rate is %f" %(1.0-errorCount/float(numTestVecs)) |