機器學習實戰之kNN算法

     機器學習實戰這本書是基於python的,若是咱們想要完成python開發,那麼python的開發環境必不可少:python

(1)python3.52,64位,這是我用的python版本git

(2)numpy 1.11.3,64位,這是python的科學計算包,是python的一個矩陣類型,包含數組和矩陣,提供了大量的矩陣處理函數,使運算更加容易,執行更加迅速。算法

(3)matplotlib 1.5.3,64位,在下載該工具時,必定要對應好python的版本,處理器版本,matplotlib能夠認爲是python的一個可視化工具python3.x

    好了,若是你已經完成了上述的環境配置,下面就能夠開始完成真正的算法實戰了。數組

一,k近鄰算法的工做原理:網絡

     存在一個樣本數據集,也稱做訓練數據集,而且樣本集中每一個數據都存在標籤,即咱們知道樣本集中每一個數據與所屬分類的對應關係。當輸入沒有標籤的新數據後,將新數據的每一個特徵與樣本集中數據對應的特徵進行比較,而後算法提取樣本集中特徵最類似的數據的分類標籤。通常來水,咱們只選擇樣本數據集中最類似的k個數據(一般k不大於20),再根據多數表決原則,選擇k個最類似數據中出現次數最多的分類,做爲新數據的分類。app

     k近鄰算法的通常流程:
(1)收集數據:能夠採用公開的數據源機器學習

(2)準備數據:計算距離所須要的數值ide

(3)分析數據:剔除垃圾信息函數

(4)測試算法:計算錯誤率

(5)使用算法:運用在實際中,對實際狀況進行預測

二,算法具體實施過程

(1)使用python導入數據,代碼解析以下:

#-------------------------1 準備數據-------------------------------
#能夠採用公開的數據集,也能夠利用網絡爬蟲從網站上抽取數據,方式不限
#-------------------------2 準備數據------------------------------- 
#確保數據格式符合要求
#導入科學計算包(數組和矩陣)
from numpy import *
from os import listdir
#導入運算符模塊
import operator

#建立符合python格式的數據集
def createDataSet():
    #數據集 list(列表形式)   
    group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    #標籤
    labels=['A','A','B','B']
    return group, labels

(2)咱們可使用matplotlib 對數據進行分析

在python命令環境中,輸入以下命令:

當輸入以下命令時:

#導入製圖工具
import matplotlib
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)

ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
plt.show()

 

     從上面能夠看到,因爲沒有使用樣本分類的特徵值,咱們很難看到比較有用的數據模式信息

     通常而言,咱們會採用色彩或其餘幾號來標記不一樣樣本的分類,以便更好的理解數據,從新輸入命令:

#導入製圖工具
import matplotlib
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)

#記得導入array函數
from numpy import array
#色彩不等,尺寸不一樣
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()

(3)實施kNN算法

k近鄰算法對未知類別屬性的數據集中每一個點依次執行以下步驟:
1)計算已知類別數據集中的點與當前點之間的距離

2)按照距離遞增次序排序

3)選取與當前點距離最小的k個點

4)肯定前k個點所在類別的出現頻率

5)返回前k個點出現頻率最高的類別做爲當前點的預測分類

具體代碼解析以下:

#-------------------------構建分類器------------------------------- 
#KNN算法實施
#@inX  測試樣本數據
#@dataSet  訓練樣本數據
#@labels  測試樣本標籤
#@k  選取距離最近的k個點
def classify0(inX,dataSet,labels,k):
    #獲取訓練數據集的行數
    dataSetSize=dataSet.shape[0]
    #---------------歐氏距離計算-----------------
    #各個函數均是以矩陣形式保存
    #tile():inX沿各個維度的複製次數
    diffMat=tile(inX,(dataSetSize,1))-dataSet
    sqDiffMat=diffMat**2
    #.sum()運行加函數,參數axis=1表示矩陣每一行的各個值相加和
    sqDistances=sqDiffMat.sum(axis=1)
    distances=sqDistances**0.5
    #--------------------------------------------
    #獲取排序(有小到大)後的距離值的索引(序號)
    sortedDistIndicies=distances.argsort()
    #字典,鍵值對,結構相似於hash表
    classCount={}
    for i in range(k):
        #獲取該索引對應的訓練樣本的標籤
        voteIlabel=labels[sortedDistIndicies[i]]
        #累加幾類標籤出現的次數,構成鍵值對key/values並存於classCount中
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
    #將字典列表中按照第二列,也就是次數標籤,反序排序(由大到小排序)
    sortedClassCount=sorted(classCount.items(),
     key=operator.itemgetter(1),reverse=True)
    #返回第一個元素(最高頻率)標籤key
    return sortedClassCount[0][0]

(3)測試分類器

下面以兩個實例對分類器效果進行測試

實例1:使用kNN改進某約會網站的配對效果

#-------------------------knn算法實例-----------------------------------
#-------------------------約會網站配對-----------------------------------
#---------------1 將text文本數據轉化爲分類器能夠接受的格式---------------
def file2matrix(filename):
    #打開文件
    fr=open(filename)
    #讀取文件每一行到array0Lines列表
    #read():讀取整個文件,一般將文件內容放到一個字符串中
    #readline():每次讀取文件一行,當沒有足夠內存一次讀取整個文件內容時,使用該方法
    #readlines():讀取文件的每一行,組成一個字符串列表,內存足夠時使用
    array0Lines=fr.readlines()
    #獲取字符串列表行數行數
    numberOfLines=len(array0Lines)
    #返回的特徵矩陣大小
    returnMat=zeros((numberOfLines,3))
    #list存儲類標籤
    classLabelVector=[]
    index=0
    for line in array0Lines:
        #去掉字符串頭尾的空格,相似於Java的trim()
        line=line.strip()
        #將整行元素按照tab分割成一個元素列表
        listFromLine=line.split('\t')
        #將listFromLine的前三個元素依次存入returnmat的index行的三列
        returnMat[index,:]=listFromLine[0:3]
        #python可使用負索引-1表示列表的最後一列元素,從而將標籤存入標籤向量中
        #使用append函數每次循環在list尾部添加一個標籤值
        classLabelVector.append(int(listFromLine[-1]))
        index+=1
    return returnMat,classLabelVector
#----------------2 準備數據:歸一化----------------------------------------------
#計算歐式距離時,若是某一特徵數值相對於其餘特徵數值較大,那麼該特徵對於結果影響要
#遠大於其餘特徵,而後假設特徵都是同等重要,即等權重的,那麼可能某一特徵對於結果存
#在嚴重影響
def autoNorm(dataSet):
    #找出每一列的最小值
    minVals=dataSet.min(0)
    #找出每一列的最大值
    maxVals=dataSet.max(0)
    ranges=maxVals-minVals
    #建立與dataSet等大小的歸一化矩陣
    #shape()獲取矩陣的大小
    normDataSet=zeros(shape(dataSet))
    #獲取dataSet第一維度的大小
    m=dataSet.shape[0]
    #將dataSet的每一行的對應列減去minVals中對應列的最小值
    normDataSet=dataSet-tile(minVals,(m,1))
    #歸一化,公式newValue=(value-minvalue)/(maxVal-minVal)
    normDataSet=normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals
#-------------------------3 測試算法----------------------------------------------
#改變測試樣本佔比,k值等都會對最後的錯誤率產生影響
def datingClassTest():
    #設定用來測試的樣本佔比
    hoRatio=0.10
    #從文本中提取獲得數據特徵,及對應的標籤
    datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
    #對數據特徵進行歸一化
    normMat,ranges,minVals=autoNorm(datingDataMat)
    #獲得第一維度的大小
    m=normMat.shape[0]
    #測試樣本數量
    numTestVecs=int(hoRatio*m)
    #錯誤數初始化
    errorCount=0.0
    for i in range(numTestVecs):
        #利用分類函數classify0獲取測試樣本數據分類結果
        classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],\
        datingLabels[numTestVecs:m],3)
        #打印預測結果和實際標籤
        print("the classifier came back with: %d, the real answer is: %d"\
        %(classifierResult,datingLabels[i]))
        #若是預測輸出不等於實際標籤,錯誤數增長1.0
        if(classifierResult != datingLabels[i]):errorCount+=1.0
    #打印最後的偏差率
    print("the total error rate is: %f" %(errorCount/float(numTestVecs)))

#-------------------------4 構建可手動輸入系統------------------------------------
#用戶輸入相關數據,進行預測
def classifyPerson():
    #定義預測結果
    resultList=['not at all','in small does','in large does']
    #在python3.x中,已經刪除raw_input(),取而代之的是input()
    percentTats=float(input(\
    "percentage of time spent playing video games?"))
    ffMiles=float(input("frequent filer miles earned per year?"))
    iceCream=float(input("liters of ice cream consumed per year?"))
    datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
    normMat,ranges,minValues=autoNorm(datingDataMat)
    #將輸入的數值放在數組中
    inArr=array([ffMiles,percentTats,iceCream])
    classifierResult=classify0((inArr-minValues)/ranges,normMat,datingLabels,3)
    print("you will probably like this person:",resultList[classifierResult-1])

實驗結果:

固然用戶也能夠本身手動輸入,進行預測:

實例2 手寫識別系統

#-------------------------knn算法實例-----------------------------------
#-------------------------手寫識別系統-----------------------------------
#-------------------------1 將圖像轉化爲測試向量-------------------------
#圖像大小32*32,轉化爲1024的向量
def img2vector(filename):
    returnVec=zeros((1,1024))
    fr=open(filename)
    for i in range(32):
        #每次讀取一行
        lineStr=fr.readline()
        for j in range(32):
            #通俗講:就是根據首地址(位置)的偏移量計算出當前數據存放的地址(位置)
            returnVec[0,32*i+j]=int(lineStr[j])
    return returnVec
#-------------------------2 測試代碼--------------------------------------
def handwritingClassTest():
    hwLabels=[]
    #列出給定目錄的文件名列表,使用前需導入from os import listdir
    trainingFileList=listdir('knn/trainingDigits')
    #獲取列表的長度
    m=len(trainingFileList)
    #建立一個m*1024的矩陣用於存儲訓練數據
    trainingMat=zeros((m,1024))
    for i in range(m):
        #獲取當前行的字符串
        fileNameStr=trainingFileList[i]
        #將字符串按照'.'分開,並將前一部分放於fileStr
        fileStr=fileNameStr.split('.')[0]
        #將fileStr按照'_'分開,並將前一部分存於classNumStr
        classNumStr=int(fileStr.split('_')[0])
        #將每一個標籤值所有存入一個列表中
        hwLabels.append(classNumStr)
        #解析目錄中的每個文件,將圖像轉化爲向量,最後存入訓練矩陣中
        trainingMat[i,:]=img2vector('knn/trainingDigits/%s' %fileNameStr)
    #讀取測試數據目錄中的文件列表
    testFileList=listdir('knn/testDigits')
    errorCount=0.0
    mTest=len(testFileList)
    for i in range(mTest):
        #獲取第i行的文件名
        fileNameStr=testFileList[i]
        #將字符串按照'.'分開,並將前一部分放於fileStr
        fileStr=fileNameStr.split('.')[0]
        #將fileStr按照'_'分開,並將前一部分存於classNumStr
        classNumStr=int(fileStr.split('_')[0])
        #解析目錄中的每個文件,將圖像轉化爲向量
        vectorUnderTest=img2vector('knn/testDigits/%s' %fileNameStr)
        #分類預測
        classifierResult=classify0(vectorUnderTest,trainingMat,hwLabels,3)
        #打印預測結果和實際結果
        print("the classifierResult came back with: %d,the real answer is: %d" %(classifierResult,classNumStr))
        #預測錯誤,錯誤數加1次
        if(classifierResult!=classNumStr):errorCount+=1.0
        #打印錯誤數和錯誤率
        print("\nthe total number of errors is: %d" %errorCount)
        print("\nthe total error rate is: %f" %(errorCount/float(mTest)))
    

實驗結果(錯誤率):

三,算法小結:
(1) 若是咱們改變訓練樣本的數目,調整相應的k值,都會對最後的預測錯誤率產生影響,咱們能夠根據錯誤率的狀況,對這些變量進行調整,從而下降預測錯誤率

(2)k近鄰算法的優缺點:

k近鄰算法具備精度高,對異常值不敏感的優勢

k近鄰算法是基於實例的學習,使用算法時咱們必須有接近實際數據的訓練樣本數據。k近鄰算法必須保存所有數據集,若是訓練數據集很大,必須使用大量的存儲空間。此外,因爲必須對數據集中的每一個數據計算距離,實際使用時也可能會很是耗時

此外,k近鄰算法沒法給出數據的基礎結構信息,所以咱們沒法知道平均實例樣本和典型實例樣本具備怎樣的特徵。

相關文章
相關標籤/搜索