1. 程式人生 > >機器學習實戰之K-近鄰演算法總結和程式碼解析

機器學習實戰之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模組函式的,我單獨做了整理。之後有時間再傳上來吧!