機器學習實戰之K-近鄰演算法總結和程式碼解析
機器學習實戰是入手機器學習和python實戰的比較好的書,可惜我現在才開始練習程式碼!先宣告:本人菜鳥一枚,機器學習的理論知識剛看了一部分,python的知識也沒學很多,所以寫程式碼除錯的過程很痛可!但是還是挨個找出了問題所在,蠻開心的!看了很多大牛分享的經驗說,記住演算法和模型的特點以及優缺點、適用場景是非常重要的,所以藉著剛看完kNN的熱度,趕緊把這些總結一下啦!
特點: (1)kNN演算法是最簡單最容易理解的機器學習演算法,沒有明顯的訓練過程。所做只是將資料儲存起來,沒
有訓練時間,當需要對測試樣本處理的時候才會計算的。
(2)我們通常看到的kNN演算法處理的都是分類的問題,實際上kNN演算法也可以做迴歸的問題。對於分類
問題,一般採用多數表決的方法進行預測,而對於迴歸問題, 則將k個最近鄰的訓練例項的平均值
作為預測值。
(3)kNN演算法的三要素: k值選擇、距離度量以及決策規則
1> k值選擇較大或者較小都會產生不同的優點或者缺點,而且k值的選擇會對結果產生重大
的影響。但是應用中,k值一般取一個較小的數值,並且通常採取交叉驗證法來選取最優
的k值。
2>距離度量之前一定要做歸一化處理。計算距離有很多種方法,而且不同的距離公式算出來
的最近k值是不同的。一般情況下,選擇歐式距離作為距離度量。
3>分類決策規則通常選擇多數表決法。
(4)當k=1時稱為“最近鄰分類器”,最近鄰分類器雖然很簡單,但是它的泛化錯誤率不超過貝葉斯最優
分類器的兩倍。
機器學習實戰中的程式碼及解析:(因為本人python水平太差,所以開始的時候好多程式碼只能看懂大概,但是細分析分析不出來結果。但是網上有大神寫出來了http://lib.csdn.net/snippet/machinelearning/43106 我又做了點修改和補充,程式碼都是可以執行的 Ps:不知道為什麼下載的原始碼包裡面的程式碼淨出現問題,所以小的地方也做了程式碼修改哦!)
#coding:utf-8
from numpy import *
from os import listdir # os是和作業系統聯絡密切的一個模組
import operator #運算子模組
#k-近鄰演算法
def classify0(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] #shape是array的一個屬性,返回矩陣array的各個維度大小,shape(0)是矩陣第一列的資料個數
#距離計算
diffMat=tile(inX,(dataSetSize,1))-dataSet # 求差(求出的結果也是一個矩陣),(tile函式表示將inX在x軸上重複dataSetSize次,y軸上重複1次,注意向量是
#橫著的,每個行向量的裡面元素的個數代表了維數)
sqDiffMat=diffMat**2 #求平方(結果還是矩陣)
sqDistances=sqDiffMat.sum(axis=1) #對平方求和(求出的結果是一個列向量),sum函式,axis=1,表示將[]裡面數相加(行相加),axis=0表示(列相加),
#axis=None(行列相加)
distances=sqDistances**0.5 #對平方和開方(結果還是列向量),此處算完之後就是距離
#排序
sortedDistIndicies=distances.argsort() #返回distances排序的索引,用於下面查詢標籤
classCount={} #定義元字典
#選擇距離最小的k個點
for i in range(k): # 遍歷前k個元素
voteIlabel=labels[sortedDistIndicies[i]] #獲得前k個元素的標籤
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1 #統計標籤個數
#排序
sortedClassCount=sorted(classCount.iteritems(), key=operator.itemgetter(1),reverse=True) # 對標籤字典根據對應個數進行降序排序
return sortedClassCount[0][0] #返回個數最多的標籤和對應個數
#小試牛刀的小程式碼
def createDataSet():
group=array([[1.0,1.0],[1.0,1.0],[0,0],[0,0.1]])
labels=['A','A','B','B']
return group,labels
#將文字記錄轉換為Numpy的解析程式
def file2matrix(filename):
fr=open(filename) #開啟檔案
arrayOLines=fr.readlines() #獲取檔案所有行
numberOfLines=len(arrayOLines) #得到檔案行數
returnMat=zeros((numberOfLines,3)) #先用零元素建立需要返回的numpy矩陣,(行數,列數)
classLabelVector=[] # 建立空的標籤列表
index=0
for line in arrayOLines:
line=line.strip() #擷取掉尾部的回車字元
listFromLine=line.split('\t') #用‘\t’作為分隔符將整行元素分割成元素列表,將一行資料按空進行分割,
returnMat[index,:]=listFromLine[0:3] #選取列表前三個元素到=矩陣中
classLabelVector.append(listFromLine[-1]) #將列表的最後一列儲存到向量中
index += 1
return returnMat,classLabelVector #返回資料集矩陣和對應的標籤向量
#下面的是用命令列執行的程式碼
# import matplotlib
# import matplotlib.pyplot as plt
# fig=plt.figure()
# ax=fig.add_subplot(111)
# ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
# 上句替換為:ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
# plt.show()
#歸一化特徵值
def autoNorm(dataSet):
minVals = dataSet.min(0) #找到資料集中的最小值(實際上應該是樣本資料中的一列中的最小值,引數0就代表這個,下同),這樣說的話minVals和maxVals都應該
#是一個行向量(1*n)
maxVals = dataSet.max(0) #找到資料集中的最大值
ranges = maxVals - minVals #得到資料的範圍差值
normDataSet = zeros(shape(dataSet)) # 定義空的要返回的歸一化後的矩陣,該矩陣和傳入的資料集是一樣的大小
m = dataSet.shape[0] #得到矩陣第一行的資料個數,也就是維數
normDataSet = dataSet - tile(minVals, (m,1)) #資料集與最小值相減(title()函式將按照括號中的引數製作對應大小的矩陣,用給定的minVals內容來填充
normDataSet = normDataSet/tile(ranges, (m,1)) #除以範圍值之後就是歸一化的值了。(注意是矩陣除法)
return normDataSet, ranges, minVals
#分類器針對約會網站的測試程式碼
def datingClassTest():
hoRatio = 0.10 #測試所佔的比例
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #將檔案中的資料轉換為矩陣形式和提取出標籤矩陣
normMat, ranges, minVals = autoNorm(datingDataMat) #對提取出的矩陣資料歸一化處理
m = normMat.shape[0] #獲得資料總的條數
numTestVecs = int(m*hoRatio) #得出作為測試的資料個數
errorCount = 0.0 #初始化錯誤個數為0
for i in range(numTestVecs): #對測試的資料進行遍歷
classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) # 對資料進行分類
print "the classifier came back with: %s, the real answer is: %s" % (classifierResult, datingLabels[i]) #輸出分類結果和實際的類別(之前的程式碼
#有問題啊,要將%d,改為%s)
if (int(classifierResult) != int(datingLabels[i])): errorCount += 1.0 # 如果分類結果與實際結果不一致 ,錯誤數加1
print "the total error rate is: %f" % (errorCount/float(numTestVecs)) # 輸出錯誤率
print errorCount #輸出錯誤總數
#約會網站預測函式
def classiyPerson():
resultList = ['not at all','in small doses','in large doses'] # 定義分類結果的類別
percentTats = float(raw_input("percentage of time spent playing video games?")) # 讀取輸入資料
ffMiles = float(raw_input("frequent flier miles earned per year?")) # 讀取輸入資料
iceCream = float(raw_input("liters of ice cream consumed per year?")) # 讀取輸入資料
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') # 從檔案中讀取已有資料
normMat,ranges,minVals = autoNorm(datingDataMat) # 對資料進行歸一化
inArr =array([ffMiles,percentTats,iceCream]) # 將單個輸入資料定義成一條資料
classifierResult = classify0(inArr,datingDataMat,datingLabels,3) # 對輸入資料進行分類
print 'You will probably like this person: %s' % (resultList[int(classifierResult) - 1]) # 輸出預測的分類類別
# 將單個手寫字元檔案變成向量
def img2vector(filename):
returnVect = zeros((1,1024)) #建立要返回的1*1024的矩陣並初始化為0
fr = open(filename) # 開啟檔案
for i in range(32): #從0到31行遍歷
lineStr = fr.readline() #讀取一行(自動成為一個列表)
for j in range(32): #從0到31列
returnVect[0,32*i+j] = int(lineStr[j]) #將一行中的每個元素複製到要返回的矩陣中
return returnVect #返回該1*1024的矩陣
# 手寫字元識別測試
def handwritingClassTest():
hwLabels = [] # 定義手寫字元標籤(類別)
trainingFileList = listdir('trainingDigits') # 列出目錄下所有的檔案
m = len(trainingFileList) # 計算訓練檔案的數目
trainingMat = zeros((m,1024)) # 定義手寫字元資料矩陣
for i in range(m): # 依次讀取每個檔案
fileNameStr = trainingFileList[i] # 依次獲得檔名
fileStr = fileNameStr.split('.')[0] # 對檔名進行分割
classNumStr = int(fileStr.split('_')[0]) # 獲得檔名中的類標籤
hwLabels.append(classNumStr) # 把類標籤放到hwLabels中
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) # 把檔案變成向量並賦值到trainingMat這個矩陣中
testFileList = listdir('testDigits') # 列出測試目錄下的所有檔案
errorCount = 0.0 # 定義錯誤數
mTest = len(testFileList) # 獲得測試檔案數目
for i in range(mTest): # 遍歷測試檔案
fileNameStr = testFileList[i] # 定義測試檔名
fileStr = fileNameStr.split('.')[0] # 對測試檔名進行分割
classNumStr = int(fileStr.split('_')[0]) # 獲得測試檔案的類標籤
vectorUnderTest = img2vector('testDigits/%s' % fileNameStr) # 將測試檔案轉換成向量
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) # 進行分類
print "the classifier came back with: %d, the real answer is: %d" % (int(classifierResult), int(classNumStr)) # 輸出預測類別和實際類別
if (int(classifierResult) != int(classNumStr)): errorCount += 1.0 # 如果二者不一致,累加錯誤數量
print "\nthe total number of errors is: %d" % errorCount # 輸出分類錯誤的數目
print "\nthe total error rate is: %f" % (errorCount/float(mTest)) # 輸出分類的錯誤率
還有一部分的解析是關於不熟悉的python模組函式的,我單獨做了整理。之後有時間再傳上來吧!