機器學習實戰這本書是基於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近鄰算法沒法給出數據的基礎結構信息,所以咱們沒法知道平均實例樣本和典型實例樣本具備怎樣的特徵。