01-kNN算法實戰-(機器學習實戰)

最近在看機器學習實戰這本書。剛開始看kNN算法,并寫了些程序,分享下一些感悟和細節。

什么是kNN

kNN中文又稱為k-近鄰算法,其基本思想是通過計算輸入樣本點 和 訓練集樣本點之前的這兩個向量之前的距離,距離越近的話,說明其特征越靠近,通過取出其k個距離最近的樣本點,然后計算這k個樣本點中類別占比最大的類比以此來預測輸入樣本點(被測樣本點)的類別。

kNN的優勢

  • kNN是ML里最簡單,最基本的算法。
  • kNN不會受到差別特別大的樣本中的特征元素的影響(對異常值不敏感)。因為采用了歸一化技術
  • kNN的精度高

kNN的劣勢

  • kNN算法時間復雜度較高,需要計算被測樣本點和訓練集中所有樣本點的距離

kNN算法的實現

from numpy import *
import operator
# 該分類器模型,只需要輸入向量, 訓練數據集矩陣dataSet,每一行是一個樣本。labels(每一行的樣本標簽)。k取前幾個
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]
  • 這里的dataSet是numpy模塊里的數組(也可以看成矩陣),不是python中內置的數組,shape屬性會返回這個數組的緯度.(比如是2 * 3的二維數組,則會返回(2, 3)表示這是一個2緯數組,每個緯度的大小分別是2,3)
  • tile函數用來創建矩陣,其中(dataSetSize, 1)表示沿著inX向量的行方向(inX是一個行向量),賦值dataSetSize次,沿著列方向復制1次(既列不變)。
  • diffMat矩陣計算出了各個向量之前的距離的平方值。其中2表示平方,0.5表示開平方

使用kNN來改進婚戀網站的匹配

這里的數據集如下

image.png

datingTestSet.txt存放了該網站關于個人信息的集合。每一行代表一個人,一共有三個特征屬性,和一個標簽屬性用來標識是哪一類人。
datingTestSet2.txt 存放的是處理過的datingTestSet數據,將類別標簽處理成數字

  • 三個特征屬性分別是: 每年航班的行程公里數,玩游戲的時間所占的時間百分比,每周消費的冰淇淋公升數
  • 人一共分為三類(不喜歡的、魅力一般、極具魅力)

兩個文件的內容如下


image.png

目的

現在的需求是,我們必須根據提供的數據,來準確的劃分出這三類人。才能精確的從數據中挑選出的人是用戶感興趣的。

分析

  • 根據之前總結的kNN分類器模型,我們需要將數據進行處理。分別分離出訓練數據集、測試數據集、數據集對應的標簽。其實最重要的是準本和分析數據集,然后進行建模,但是由于這里數據集已經是現有的,直接用就行。
  • 接著我們編寫測試程序,將測試數據集、訓練數據集、標簽丟入改模型進行計算
  • 統計識別的錯誤率,如果錯誤率很低。那基本上可以使用。如果錯誤率高,那就要改進數據集,進行其他特征點的抽取。

步驟

處理數據(提取數據,歸一化)

改函數用來讀取訓練集數據將其轉化為矩陣,并提取出標簽集合。具體代碼看下面

from numpy import *
def file2matrix(filename):
    file = open(filename)
    arrayOfLines = file.readlines()
    numberOfLines = len(arrayOfLines)
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    index = 0
    for line in arrayOfLines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))  # 這邊要十分小心,必須強制轉換為整形,不然編譯器會當做字符串處理
        index += 1
    return returnMat, classLabelVector

將特征數據都歸一化
因為航程特征的數字太大,對其他兩個影響太大,但是又不能忽略,為了減少這種影響。將數據進行歸一行,既處理層0到1之前的小數。利用了如下原理
newValue = oldValue - minValue/(maxValue - minValue)

# 數據歸一化 newValue = oldValue - minValue/(maxValue - minValue)
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normaMat = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normaMat = dataSet - tile(minVals, (m, 1))
    normaMat = normaMat / tile(ranges, (m, 1))
    return normaMat, ranges, minVals
通過圖形分析數據
  • (x軸為游戲時間占比,y軸為每周吃冰淇淋的公斤數)
# 列2和列1的比較
datingDataMat,datingDataLabels = file2matrix('/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/datingTestSet2.txt')
print datingDataLabels
import matplotlib
import matplotlib.pyplot as plt
from numpy import *
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * array(datingDataLabels), 15.0 * array(datingDataLabels))
plt.show()
image.png
  • (x軸為航班占比,y軸為游戲時間耗時占比)
# 列2和列1的比較
datingDataMat,datingDataLabels = file2matrix('/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/datingTestSet2.txt')
print datingDataLabels
import matplotlib
import matplotlib.pyplot as plt
from numpy import *
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * array(datingDataLabels), 15.0 * array(datingDataLabels))
plt.show()
image.png

通過上面的分析,可以很明顯的發現,使用前兩個特征,我們就可以將這三類人比較精確的分離出來。但如果只使用第二個和第三個特征難以分離出該三類。

編寫測試用例,測試kNN分類器的效果
# 針對約會網站的測試代碼,測試分類器的效果
def datingClassTest():
    hoRatio = 0.10
    datingDataMat, datingDataLabels = file2matrix('/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]   # 獲取訓練集行數
    numTestVecs = int(m * hoRatio)  # 取10%的行數作為測試集  
    errorCount = 0.0
    for i in range(numTestVecs):  
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,], datingDataLabels[numTestVecs:m], 3)  # 取從小到的前三個
        print "分類的結果是: %d, 目標結果是: %d" % (int(classifierResult), int(datingDataLabels[i]))
        if (classifierResult != datingDataLabels[i]): errorCount += 1.0
    print "總的錯誤率為: %f%%" %(errorCount / float(numTestVecs) * 100.0)

運行datingClassTest()方法我們可以看到如下結果(其中1,2,3分別代表三類人的標簽映射。),改分類器的錯誤率為5%,也就是說95%的情況下匹配都是準確的,算是還不錯的分類器。

image.png

使用KNN實現識別手寫數字

具體思路和上面的例子一樣。這邊有個比較不一樣的步驟是我們需要對圖片進行處理,這里我們統一對圖片做了以下處理。

  • 將圖片的大小處理層一樣的黑白圖。
  • 對于每張圖片,我們使用 正確的對應數字_樣本索引.txt來命名。(之所以處理成文本是為了在這里比較直觀)


    image.png

    image.png

分析

1.為了使用kNN模型,我們需要將圖片轉化為一個行向量。由于圖片大小事32*32,我們需要一個1024大小的行向量即可存儲。

from numpy import *
def img2vector(filename):
    fr = open(filename)
    returnVect = zeros((1, 1024))
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVec[0, 32 * i + j] = int(lineStr[j])
    return returnVect

2.使用kNN模型進行測試

from os import listdir
from numpy import *
def handwritingClassTest():
# 遍歷訓練數據集,將數據集裝載進矩陣。
    trainingFilesPath = "/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/digits/trainingDigits/"
    arrayOfTrainingFiles = listdir(trainingFilesPath)
    m = len(arrayOfTrainingFiles)
    trainingMat = zeros((m, 1024))
    classLabels = []
    for i in range(m):
        fileNameStr = arrayOfTrainingFiles[i]
        fileStr = fileNameStr.split('.')[0]
        classNum = int(fileStr.split('_')[0])
        classLabels.append(classNum)
        trainingMat[i,:] = img2vector(trainingFilesPath + fileNameStr)
    
    testFilePath = "/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/digits/testDigits/"
    arrayOfTestFiles =  listdir(testFilePath)
    mTest = len(arrayOfTestFiles)
    errorCount = 0.0;
    for j in range(mTest):
        testFileNameStr = arrayOfTestFiles[j]
        testFile = testFileNameStr.split('.')[0]
        testNum = testFile.split('_')[0]
        testImageVec = img2vector(testFilePath + testFileNameStr)
        classifierResult = classify0(testImageVec, trainingMat, classLabels, 3)
        print "識別結果為: %d, 正確結果為: %d" % (int(classifierResult), int(testNum))
        if classifierResult != int(testNum): errorCount += 1.0
    print "識別錯誤個數為: %d" % (int(errorCount))
    print "識別錯誤率為: %f%%" % (errorCount / float(mTest) * 100.0)    
image.png

效果還是相當不錯,基本達到了99%識別效率

總結:

  • 一般機器學習解決問題需要以下步驟(準備數據分析數據訓練算法(kNN不適用,kNN無需訓練)測試算法,使用算法)。
  • 對于kNN算法模型來說,分析數據過程由于重要,只有有價值的數據使用kNN才能有精確的結果。
  • kNN算法比較簡單,穩定。但是效率低。其思想主要是計算相似度(通過計算向量距離),并使用概率來得出分類結果。

by sixleaves 20170726 FuZhou

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容