機器學習實戰——KNN算法及機器學習初識

點擊查看:數據集+代碼

機器學習算法

機器學習的任務可分爲迴歸分類

  1. 對於分類算法,通常我們輸入大量已分類數據作爲算法的訓練集訓練集訓練樣本的集合
  2. 每個訓練樣本包含特徵(也稱屬性)以及目標變量,在分類算法中,我們目標變量稱爲類別,並且假定分類問題只存在有限個數的類別
  3. 機器學習算法目標變量的類型通常是標稱型的如:男和女, 而在迴歸算法中通常是連續型
  4. 特徵或者屬性通常是訓練樣本集的,它們是獨立測量得到的結果,多個特徵聯繫在一 起共同組成一個訓練樣本。即每一訓練樣本可視爲一個行向量,每一個特徵即行向量的某一項,多個訓練樣本構成訓練集矩陣,矩陣的相同列屬於訓練樣本的同一特徵

k-近鄰算法屬於分類算法

k-近鄰算法的一般流程

  1. 收集數據:可以使用任何方法。
  2. 準備數據:距離計算所需要的數值,最好是結構化的數據格式。
  3. 分析數據:可以使用任何方法。
  4. 訓練算法:此步驟不適用於k-近鄰算法。
  5. 測試算法:計算錯誤率。
  6. 使用算法:首先需要輸入樣本數據和結構化的輸出結果,然後運行k-近鄰算法判定輸入數據分別屬於哪個分類,最後應用對計算出的分類執行後續的處理。
  • 收集數據涉及爬蟲,數據提取,數據處理等等各種方式
  • 準備數據是將收集到的數據轉化爲計算機能夠處理的標準化格式,如下圖中的格式
  • 在這裏插入圖片描述 - 分析數據的目的是保證數據的可用性,數據是否能正常用於分類區分,通常會用到數據可視化有關技術,如將高維數據轉化爲三維及以內數據進行可視化,比如使用PCA主成分分析進行最低信息損失的降維
  • K-近鄰算法不需要進行訓練,每次使用都會調用所有訓練數據
  • 通過測試集對算法的分類成功率進行測試

算法原理:

存在一個樣本數據集合,也稱作爲訓練樣本集,並且樣本集中每個數據都存在標籤,即我們知道樣本集中每一個數據與所屬分類的對應關係。輸入沒有標籤的新數據後,將新的數據的每個特徵與樣本集中數據對應的特徵進行比較,然後算法提取樣本最相似數據(最近鄰)的分類標籤。一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法中k的出處,通常k是不大於20的整數。最後,選擇k個最相似數據中出現次數最多的分類,作爲新數據的分類。

由此,KNN算法的最重要的三個點可以總結爲:

  1. K值的確定
  2. 權重設置:即雖然最後選擇了K個數據作爲參考依據,但K個數據與測試數據的相對位置各不相同,因此需要對K個數據進行不同的權重設置
  3. 距離的度量方式:計算距離是使用歐氏距離還是馬氏距離巴氏距離曼哈頓距離等等,亦或有其他算法。

核心代碼實現(本例爲計算歐氏距離,權重均分)

inX -測試集,即分類對象
dataSet - 訓練集
labes - 分類標籤
k - 選擇距離最小的k個點
sortedClassCount[0][0] - 分類結果

其中
inX爲一個行向量
dataSet爲訓練數據組成的numpy矩陣,每一行爲一個訓練數據行向量
lables爲行向量,表徵訓練數據與測試數據的每一列代表特徵

def classify0(inX, dataSet, labels, k):
	#numpy函數shape[0]返回dataSet的行數
	dataSetSize = dataSet.shape[0]
	#在列向量方向上重複inX共1次(橫向),行向量方向上重複inX共dataSetSize次(縱向)
	diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
	#二維特徵相減後平方
	sqDiffMat = diffMat**2
	#sum()所有元素相加,sum(0)列相加,sum(1)行相加
	sqDistances = sqDiffMat.sum(axis=1)
	#開方,計算出距離
	distances = sqDistances**0.5
	#返回distances中元素從小到大排序後的索引值
	sortedDistIndices = distances.argsort()
	#定一個記錄類別次數的字典
	classCount = {}
	for i in range(k):
		#取出前k個元素的類別
		voteIlabel = labels[sortedDistIndices[i]]
		#dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回默認值。
		#計算類別次數
		classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
	#python3中用items()替換python2中的iteritems()
	#key=operator.itemgetter(1)根據字典的值進行排序
	#key=operator.itemgetter(0)根據字典的鍵進行排序
	#reverse降序排序字典
	sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
	print(sortedClassCount)
	#返回次數最多的類別,即所要分類的類別
	return sortedClassCount[0][0]

相同維度的numpy.array對象可直接進行對應位置元素的,乘方,加減乘除操作

numpy.tile(mat,reps)

在這裏插入圖片描述

>>> a
array([[1, 2],
       [4, 5]])
>>> numpy.tile(a,(2,2))
array([[1, 2, 1, 2],
       [4, 5, 4, 5],
       [1, 2, 1, 2],
       [4, 5, 4, 5]])
>>> numpy.tile(a,(2,2,2))
array([[[1, 2, 1, 2],
        [4, 5, 4, 5],
        [1, 2, 1, 2],
        [4, 5, 4, 5]],

       [[1, 2, 1, 2],
        [4, 5, 4, 5],
        [1, 2, 1, 2],
        [4, 5, 4, 5]]])
>>> numpy.tile(a,(1,2,2))
array([[[1, 2, 1, 2],
        [4, 5, 4, 5],
        [1, 2, 1, 2],
        [4, 5, 4, 5]]])

argsort方法:

返回一個排序後的序列,但該序列每一項不是sort方法的結果,而是替代爲排序前原來的索引

海倫約會數據可視化代碼

主要涉及Matplot庫的使用

datingDataMat - 特徵矩陣
datingLabels - 分類Label
from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt

def showdatas(datingDataMat, datingLabels):
	#設置漢字格式
	font = FontProperties(fname=r"c:\windows\fonts\simsunb.ttf", size=14)  ##需要查看自己的電腦是否會包含該字體
	#將fig畫布分隔成1行1列,不共享x軸和y軸,fig畫布的大小爲(13,8)
	#當nrow=2,nclos=2時,代表fig畫布被分爲四個區域,axs[0][0]表示第一行第一個區域
	fig, axs = plt.subplots(nrows=2, ncols=2,sharex=False, sharey=False, figsize=(13,8))

	numberOfLabels = len(datingLabels)
	LabelsColors = []
	for i in datingLabels:
		if i == 1:
			LabelsColors.append('black')
		if i == 2:
			LabelsColors.append('orange')
		if i == 3:
			LabelsColors.append('red')
	#畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第二列(玩遊戲)數據畫散點數據,散點大小爲15,透明度爲0.5
	axs[0][0].scatter(x=datingDataMat[:,0], y=datingDataMat[:,1], color=LabelsColors,s=15, alpha=.5)
	#設置標題,x軸label,y軸label
	axs0_title_text = axs[0][0].set_title(u'每年獲得的飛行常客里程數與玩視頻遊戲所消耗時間佔比',FontProperties=font)
	axs0_xlabel_text = axs[0][0].set_xlabel(u'每年獲得的飛行常客里程數',FontProperties=font)
	axs0_ylabel_text = axs[0][0].set_ylabel(u'玩視頻遊戲所消耗時間佔比',FontProperties=font)
	plt.setp(axs0_title_text, size=9, weight='bold', color='red')  
	plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')  
	plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black') 

	#畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第三列(冰激凌)數據畫散點數據,散點大小爲15,透明度爲0.5
	axs[0][1].scatter(x=datingDataMat[:,0], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)
	#設置標題,x軸label,y軸label
	axs1_title_text = axs[0][1].set_title(u'每年獲得的飛行常客里程數與每週消費的冰激淋公升數',FontProperties=font)
	axs1_xlabel_text = axs[0][1].set_xlabel(u'每年獲得的飛行常客里程數',FontProperties=font)
	axs1_ylabel_text = axs[0][1].set_ylabel(u'每週消費的冰激淋公升數',FontProperties=font)
	plt.setp(axs1_title_text, size=9, weight='bold', color='red')  
	plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')  
	plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black') 

	#畫出散點圖,以datingDataMat矩陣的第二(玩遊戲)、第三列(冰激凌)數據畫散點數據,散點大小爲15,透明度爲0.5
	axs[1][0].scatter(x=datingDataMat[:,1], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)
	#設置標題,x軸label,y軸label
	axs2_title_text = axs[1][0].set_title(u'玩視頻遊戲所消耗時間佔比與每週消費的冰激淋公升數',FontProperties=font)
	axs2_xlabel_text = axs[1][0].set_xlabel(u'玩視頻遊戲所消耗時間佔比',FontProperties=font)
	axs2_ylabel_text = axs[1][0].set_ylabel(u'每週消費的冰激淋公升數',FontProperties=font)
	plt.setp(axs2_title_text, size=9, weight='bold', color='red')  
	plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')  
	plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black') 
	#設置圖例
	didntLike = mlines.Line2D([], [], color='black', marker='.',
                      markersize=6, label='didntLike')
	smallDoses = mlines.Line2D([], [], color='orange', marker='.',
	                  markersize=6, label='smallDoses')
	largeDoses = mlines.Line2D([], [], color='red', marker='.',
	                  markersize=6, label='largeDoses')
	#添加圖例
	axs[0][0].legend(handles=[didntLike,smallDoses,largeDoses])
	axs[0][1].legend(handles=[didntLike,smallDoses,largeDoses])
	axs[1][0].legend(handles=[didntLike,smallDoses,largeDoses])
	#顯示圖片
	plt.show()

可視化結果如圖:
可見,數據確實可以對分類結果進行相對有效的區分
在這裏插入圖片描述

手寫數字識別與通過Sklearn實現調用KNN算法

由於KNN只能實現行向量的輸入
因此當輸入圖片數據時,需要轉化爲一維數據,從而丟失原圖像的結構數據,如3×4的圖像與4×3的矩陣沒有區分度
而CNN算法的優勢在於可以實現二維數據的輸入,保留原結構
在這裏插入圖片描述
sklearn.neighbors.KNeighborsClassifier函數一共有8個參數
詳細點擊:
官方文檔

最簡單的例子用下面三個

from sklearn.neighbors import KNeighborsClassifier as kNN

#構建kNN分類器
neigh = kNN(n_neighbors = 3, algorithm = 'auto')
#擬合模型, trainingMat爲訓練矩陣,hwLabels爲對應的標籤
neigh.fit(trainingMat, hwLabels)
classifierResult = neigh.predict(vectorUnderTest)

SKlearn實現完整代碼

# -*- coding: UTF-8 -*-
import numpy as np
import operator
from os import listdir
from sklearn.neighbors import KNeighborsClassifier as kNN

"""
函數說明:將32x32的二進制圖像轉換爲1x1024向量。

Parameters:
	filename - 文件名
Returns:
	returnVect - 返回的二進制圖像的1x1024向量

Modify:
	2017-07-15
"""
def img2vector(filename):
	#創建1x1024零向量
	returnVect = np.zeros((1, 1024))
	#打開文件
	fr = open(filename)
	#按行讀取
	for i in range(32):
		#讀一行數據
		lineStr = fr.readline()
		#每一行的前32個元素依次添加到returnVect中
		for j in range(32):
			returnVect[0, 32*i+j] = int(lineStr[j])
	#返回轉換後的1x1024向量
	return returnVect

"""
函數說明:手寫數字分類測試

Parameters:
	無
Returns:
	無

Modify:
	2017-07-15
"""
def handwritingClassTest():
	#測試集的Labels
	hwLabels = []
	#返回trainingDigits目錄下的文件名
	trainingFileList = listdir('trainingDigits')
	#返回文件夾下文件的個數
	m = len(trainingFileList)
	#初始化訓練的Mat矩陣,測試集
	trainingMat = np.zeros((m, 1024))
	#從文件名中解析出訓練集的類別
	for i in range(m):
		#獲得文件的名字
		fileNameStr = trainingFileList[i]
		#獲得分類的數字
		classNumber = int(fileNameStr.split('_')[0])
		#將獲得的類別添加到hwLabels中
		hwLabels.append(classNumber)
		#將每一個文件的1x1024數據存儲到trainingMat矩陣中
		trainingMat[i,:] = img2vector('trainingDigits/%s' % (fileNameStr))
	#構建kNN分類器
	neigh = kNN(n_neighbors = 3, algorithm = 'auto')
	#擬合模型, trainingMat爲訓練矩陣,hwLabels爲對應的標籤
	neigh.fit(trainingMat, hwLabels)
	#返回testDigits目錄下的文件列表
	testFileList = listdir('testDigits')
	#錯誤檢測計數
	errorCount = 0.0
	#測試數據的數量
	mTest = len(testFileList)
	#從文件中解析出測試集的類別並進行分類測試
	for i in range(mTest):
		#獲得文件的名字
		fileNameStr = testFileList[i]
		#獲得分類的數字
		classNumber = int(fileNameStr.split('_')[0])
		#獲得測試集的1x1024向量,用於訓練
		vectorUnderTest = img2vector('testDigits/%s' % (fileNameStr))
		#獲得預測結果
		# classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
		classifierResult = neigh.predict(vectorUnderTest)
		print("分類返回結果爲%d\t真實結果爲%d" % (classifierResult, classNumber))
		if(classifierResult != classNumber):
			errorCount += 1.0
	print("總共錯了%d個數據\n錯誤率爲%f%%" % (errorCount, errorCount/mTest * 100))


"""
函數說明:main函數

Parameters:
	無
Returns:
	無

Modify:
	2017-07-15
"""
if __name__ == '__main__':
	handwritingClassTest()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章