【机器学习实验二】k-NN算法—改进约会网站以及手写体数字识别
目錄
一、改進(jìn)約會網(wǎng)站
1、項目背景
2、數(shù)據(jù)收集
3、在約會網(wǎng)站中使用k-近鄰算法的流程
4、代碼實現(xiàn)
二、手寫體數(shù)字識別?
1.了解手寫體數(shù)字識別
?2、手寫體數(shù)字識別思路
?3.1、導(dǎo)入模塊
?3.2、引入訓(xùn)練樣本文件和測試樣本文件,定義一個讀取數(shù)據(jù)的轉(zhuǎn)換數(shù)據(jù)的函數(shù)
3.3、定義一個字段轉(zhuǎn)列表的函數(shù)
?3.4、定義相似度函數(shù)
?3.5、編寫識別函數(shù)
?3.6、調(diào)用
?3.7、運行結(jié)果
一、改進(jìn)約會網(wǎng)站
1、項目背景
? ? ? ? 我的朋友海倫一直使用在線約會網(wǎng)站尋找適合自己的約會對象。盡管約會網(wǎng)站會推薦不同的人選,但她并不是喜歡每一個人。經(jīng)過一番總結(jié),她發(fā)現(xiàn)自己交往過的人可以進(jìn)行如下分類:
- 不喜歡的人
- 魅力一般的人
- 極具魅力的人
海倫認(rèn)為,盡管發(fā)現(xiàn)了這些規(guī)律,仍無法將約會網(wǎng)站推薦的匹配對象歸入恰當(dāng)?shù)姆诸?#xff0c;她希望借助我們的分類軟件更好的幫助她將匹配的對象劃分到確切的分類中。
2、數(shù)據(jù)收集
海倫收集約會數(shù)據(jù)已經(jīng)有了一段時間,她把這些數(shù)據(jù)存放在文本文件datingTestSet.txt中,每個樣本數(shù)據(jù)占據(jù)一行,總共有1000行。
?海倫收集的樣本數(shù)據(jù)主要包含以下3種特征:
每年獲得的飛行常客里程數(shù)
玩視頻游戲所消耗時間百分比
每周消費的冰淇淋公升數(shù)
3、在約會網(wǎng)站中使用k-近鄰算法的流程
(1)收集數(shù)據(jù):提供文本文件,即datingTestSet.txt。
(2)準(zhǔn)備數(shù)據(jù):使用Python解析文本文件。
(3)分析數(shù)據(jù):使用Matplotlib畫二維擴散圖。
(4)測試算法:使用文本文件的部分?jǐn)?shù)據(jù)作為測試樣本,計算錯誤率。
(5)使用算法:錯誤率在可接受范圍內(nèi),就可以運行k-近鄰算法進(jìn)行分類。
4、代碼實現(xiàn)
#-*- coding:utf-8 -*-import matplotlib.lines as mlines import matplotlib.pyplot as plt import numpy as np import matplotlib as mpl import operator''' #準(zhǔn)備數(shù)據(jù),從文本文件中解析數(shù)據(jù) ''' def file2matrix(filename):#打開文件with open(filename,'r') as fr:# 讀取文件所有內(nèi)容arrayOLines = fr.readlines()# 得到文件行數(shù)numberOfLines = len(arrayOLines)# 返回的NumPy矩陣,解析完成的數(shù)據(jù):numberOfLines行,3列returnMat = np.zeros((numberOfLines, 3))# 返回的分類標(biāo)簽向量classLabelVector = []# 行的索引值index = 0for line in arrayOLines:# s.strip(rm),當(dāng)rm空時,默認(rèn)刪除空白符(包括'\n','\r','\t',' ')line = line.strip()# 使用s.split(str="",num=string,cout(str))將字符串根據(jù)'\t'分隔符進(jìn)行切片。listFromLine = line.split('\t')# 將數(shù)據(jù)前三列提取出來,存放到returnMat的NumPy矩陣中,也就是特征矩陣returnMat[index, :] = listFromLine[0:3]# 根據(jù)文本中標(biāo)記的喜歡的程度進(jìn)行分類,1代表不喜歡,2代表魅力一般,3代表極具魅力if listFromLine[-1] == 'didntLike':classLabelVector.append(1)elif listFromLine[-1] == 'smallDoses':classLabelVector.append(2)elif listFromLine[-1] == 'largeDoses':classLabelVector.append(3)index += 1return returnMat, classLabelVector''' #分析數(shù)據(jù),數(shù)據(jù)可視化,使用Matplotlib創(chuàng)建散點圖 ''' def showdatas(datingDataMat, datingLabels):#設(shè)置漢字格式# sans-serif就是無襯線字體,是一種通用字體族。# 常見的無襯線字體有 Trebuchet MS, Tahoma, Verdana, Arial, Helvetica, 中文的幼圓、隸書等等mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認(rèn)字體 SimHei為黑體mpl.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號#將fig畫布分隔成2行2列,不共享x軸和y軸,fig畫布的大小為(13,8)#當(dāng)nrow=2,nclos=2時,代表fig畫布被分為四個區(qū)域,axs[0][0]表示第一行第一個區(qū)域fig, axs = plt.subplots(nrows=2, ncols=2,sharex=False, sharey=False, figsize=(13,9))LabelsColors = []for i in datingLabels:if i == 1:LabelsColors.append('green')if i == 2:LabelsColors.append('red')if i == 3:LabelsColors.append('blue')#畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第二列(玩游戲)數(shù)據(jù)畫散點數(shù)據(jù),散點大小為15,透明度為0.5axs[0][0].scatter(x=datingDataMat[:,0], y=datingDataMat[:,1], color=LabelsColors,s=15, alpha=.5)#設(shè)置標(biāo)題,x軸label,y軸labelaxs0_title_text = axs[0][0].set_title('每年獲得的飛行常客里程數(shù)與玩視頻游戲所消耗時間占比')axs0_xlabel_text = axs[0][0].set_xlabel('每年獲得的飛行常客里程數(shù)')axs0_ylabel_text = axs[0][0].set_ylabel('玩視頻游戲所消耗時間占')plt.setp(axs0_title_text, size=12, weight='bold', color='red')plt.setp(axs0_xlabel_text, size=10, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=10, weight='bold', color='black')#畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第三列(冰激凌)數(shù)據(jù)畫散點數(shù)據(jù),散點大小為15,透明度為0.5axs[0][1].scatter(x=datingDataMat[:,0], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)#設(shè)置標(biāo)題,x軸label,y軸labelaxs1_title_text = axs[0][1].set_title('每年獲得的飛行常客里程數(shù)與每周消費的冰激淋公升數(shù)',)axs1_xlabel_text = axs[0][1].set_xlabel('每年獲得的飛行常客里程數(shù)')axs1_ylabel_text = axs[0][1].set_ylabel('每周消費的冰激淋公升數(shù)')plt.setp(axs1_title_text, size=12, weight='bold', color='red')plt.setp(axs1_xlabel_text, size=10, weight='bold', color='black')plt.setp(axs1_ylabel_text, size=10, weight='bold', color='black')#畫出散點圖,以datingDataMat矩陣的第二(玩游戲)、第三列(冰激凌)數(shù)據(jù)畫散點數(shù)據(jù),散點大小為15,透明度為0.5axs[1][0].scatter(x=datingDataMat[:,1], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)#設(shè)置標(biāo)題,x軸label,y軸labelaxs2_title_text = axs[1][0].set_title('玩視頻游戲所消耗時間占比與每周消費的冰激淋公升數(shù)')axs2_xlabel_text = axs[1][0].set_xlabel('玩視頻游戲所消耗時間占比')axs2_ylabel_text = axs[1][0].set_ylabel('每周消費的冰激淋公升數(shù)')plt.setp(axs2_title_text, size=12, weight='bold', color='red')plt.setp(axs2_xlabel_text, size=10, weight='bold', color='black')plt.setp(axs2_ylabel_text, size=10, weight='bold', color='black')#設(shè)置圖例didntLike = mlines.Line2D([], [], color='green', marker='.', markersize=6, label='不喜歡')smallDoses = mlines.Line2D([], [], color='red', marker='.',markersize=6, label='魅力一般')largeDoses = mlines.Line2D([], [], color='blue', marker='.',markersize=6, label='極具魅力')#添加圖例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()''' #準(zhǔn)備數(shù)據(jù),數(shù)據(jù)歸一化處理 ''' def autoNorm(dataSet):#獲得每列數(shù)據(jù)的最小值和最大值minVals = dataSet.min(0)maxVals = dataSet.max(0)#最大值和最小值的范圍ranges = maxVals - minVals#shape(dataSet)返回dataSet的矩陣行列數(shù)#normDataSet = np.zeros(np.shape(dataSet))#返回dataSet的行數(shù)m = dataSet.shape[0]#原始值減去最小值normDataSet = dataSet - np.tile(minVals, (m, 1))#除以最大和最小值的差,得到歸一化數(shù)據(jù)normDataSet = normDataSet / np.tile(ranges, (m, 1))#返回歸一化數(shù)據(jù)結(jié)果,數(shù)據(jù)范圍,最小值return normDataSet, ranges, minVals''' KNN算法分類器 # inX - 用于分類的數(shù)據(jù)(測試集) # dataSet - 用于訓(xùn)練的數(shù)據(jù)(訓(xùn)練集) # labes - 訓(xùn)練數(shù)據(jù)的分類標(biāo)簽 # k - kNN算法參數(shù),選擇距離最小的k個點 # sortedClassCount[0][0] - 分類結(jié)果 ''' def classify0(inX, dataSet, labels, k):#numpy函數(shù)shape[0]返回dataSet的行數(shù)dataSetSize = dataSet.shape[0]#在列向量方向上重復(fù)inX共1次(橫向),行向量方向上重復(fù)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()#定一個記錄類別次數(shù)的字典classCount = {}for i in range(k):#取出前k個元素的類別voteIlabel = labels[sortedDistIndices[i]]#dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回默認(rèn)值。#計算類別次數(shù)classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#python3中用items()替換python2中的iteritems()#key=operator.itemgetter(1)根據(jù)字典的值進(jìn)行排序#key=operator.itemgetter(0)根據(jù)字典的鍵進(jìn)行排序#reverse降序排序字典sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)#返回次數(shù)最多的類別,即所要分類的類別return sortedClassCount[0][0]''' #測試算法,計算分類器的準(zhǔn)確率,驗證分類器 ''' def datingClassTest():#打開的文件名filename = "datingTestSet.txt"#將返回的特征矩陣和分類向量分別存儲到datingDataMat和datingLabels中datingDataMat, datingLabels = file2matrix(filename)#取所有數(shù)據(jù)的百分之十hoRatio = 0.10#數(shù)據(jù)歸一化,返回歸一化后的矩陣,數(shù)據(jù)范圍,數(shù)據(jù)最小值normMat, ranges, minVals = autoNorm(datingDataMat)#獲得normMat的行數(shù)m = normMat.shape[0]#百分之十的測試數(shù)據(jù)的個數(shù)numTestVecs = int(m * hoRatio)#分類錯誤計數(shù)errorCount = 0.0for i in range(numTestVecs):#前numTestVecs個數(shù)據(jù)作為測試集,后m-numTestVecs個數(shù)據(jù)作為訓(xùn)練集classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],datingLabels[numTestVecs:m], 4)print("分類結(jié)果:%d\t真實類別:%d" % (classifierResult, datingLabels[i]))if classifierResult != datingLabels[i]:errorCount += 1.0print("錯誤率:%f%%" %(errorCount/float(numTestVecs)*100))''' #使用算法,構(gòu)建完整可用系統(tǒng) ''' def classifyPerson():#輸出結(jié)果resultList = ['不喜歡','有些喜歡','非常喜歡']#三維特征用戶輸入ffMiles = float(input("每年獲得的飛行常客里程數(shù):"))precentTats = float(input("玩視頻游戲所耗時間百分比:"))iceCream = float(input("每周消費的冰激淋公升數(shù):"))#打開的文件名filename = "datingTestSet.txt"#打開并處理數(shù) 據(jù)datingDataMat, datingLabels = file2matrix(filename)#訓(xùn)練集歸一化normMat, ranges, minVals = autoNorm(datingDataMat)#生成NumPy數(shù)組,測試集inArr = np.array([ffMiles,precentTats, iceCream])#測試集歸一化norminArr = (inArr - minVals) / ranges#返回分類結(jié)果classifierResult = classify0(norminArr, normMat, datingLabels, 3)#打印結(jié)果print("你可能%s這個人" % (resultList[classifierResult-1]))''' #主函數(shù),測試以上各個步驟,并輸出各個步驟的結(jié)果 ''' if __name__ == '__main__':#打開的文件名filename = "datingTestSet.txt"#打開并處理數(shù)據(jù)datingDataMat, datingLabels = file2matrix(filename)#數(shù)據(jù)可視化showdatas(datingDataMat, datingLabels)#驗證分類器datingClassTest()#使用分類器classifyPerson()項目運行結(jié)果:
(1)數(shù)據(jù)可視化:
?
(2)?驗證分類器計算錯誤率結(jié)果
(3)使用分類器根據(jù)輸入數(shù)據(jù)獲得預(yù)測結(jié)果
二、手寫體數(shù)字識別?
1.了解手寫體數(shù)字識別
我們通過畫圖,或者在紙上寫上數(shù)字或字母,將照片進(jìn)行處理,得到固定的照片規(guī)格,將照片轉(zhuǎn)換為文本0,1表示的內(nèi)容。例如下圖:?
?
?2、手寫體數(shù)字識別思路
將測試數(shù)據(jù)轉(zhuǎn)換成只有一列的0-1矩陣形式 將所有(N個)訓(xùn)練數(shù)據(jù)也都用上方法轉(zhuǎn)換成只有一列的0-1矩陣形式
把N個單列數(shù)據(jù)存入新矩陣A中——矩陣A每一列存儲一個字的所有信息
用測試數(shù)據(jù)與矩陣A中的每一列求距離,求得的N個距離存入距離數(shù)組中
從距離數(shù)組中取出最小的K個距離所對應(yīng)的訓(xùn)練集的索引 擁有最多索引的值就是預(yù)測值
?
?3.1、導(dǎo)入模塊
import os,time,operator #導(dǎo)入os內(nèi)置庫來讀取文件名 導(dǎo)入time來測試效率 import pandas as pd #導(dǎo)入數(shù)據(jù)處理庫pandas 安裝方法pip install pandas import numpy as np #導(dǎo)入科學(xué)計算庫numpy 安裝方法pip install numpy import matplotlib.pyplot as plt #導(dǎo)入繪圖庫matplotlib 安裝方法pip install matplotlib?3.2、引入訓(xùn)練樣本文件和測試樣本文件,定義一個讀取數(shù)據(jù)的轉(zhuǎn)換數(shù)據(jù)的函數(shù)
trainingDigits =r'D:\機器學(xué)習(xí)代碼\MLiA_SourceCode\machinelearninginaction\Ch02\digits\trainingDigits' testDigits = r'D:\機器學(xué)習(xí)代碼\MLiA_SourceCode\machinelearninginaction\Ch02\digits\testDigits'## ↑數(shù)據(jù)路徑 tarining = (os.listdir(trainingDigits)) ## 讀取訓(xùn)練集 test = (os.listdir(testDigits)) ## 讀取測試集 def read_file(doc_name): ## 定義一個把32x32格式轉(zhuǎn)為1行的函數(shù)data=np.zeros((1,1024)) ## 創(chuàng)建1個zero數(shù)組f=open(doc_name) ## 打開文件for i in range(32): ## 已知每個文件中有32行32列hang=f.readline() ## 取行for j in range(32): ## 取每行中的每一列data[0,32*i+j]=int(hang[j]) ## 給data值# print(pd.DataFrame(data)) ## 不要在這里轉(zhuǎn)換成DataFrame。return data ## 否則測試集效率會降低7倍## 讀取訓(xùn)練集效率會降低12倍3.3、定義一個字段轉(zhuǎn)列表的函數(shù)
def dict_list(dic:dict): ## 定義函數(shù)將字典轉(zhuǎn)化為列表keys = dic.keys() ## dic.keys()就是字典的kvalues = dic.values() ## dic.values()就是字典的Vlst = [(key,val) for key,val in zip(keys, values)] ## for k,v in zip(k,v)return lst ## zip是一個可迭代對象## 返回一個列表?3.4、定義相似度函數(shù)
def xiangsidu(tests,xunlians,labels,k): ## tests:測試集 # xulians:訓(xùn)練樣本集 # labels:標(biāo)簽 # k: 鄰近的個數(shù)data_hang=xunlians.shape[0] ## 獲取訓(xùn)練集的行數(shù)data_hangzu=np.tile(tests,(data_hang,1))-xunlians ## 用tile把測試集tests重構(gòu)成一個 data_hang行、1列的1維數(shù)組q=np.sqrt((zu**2).sum(axis=1)).argsort() ## 計算完距離后從低到高排序,argsort返回的是索引my_dict = {} ## 設(shè)置一個dictfor i in range(k): ## 根據(jù)我們的k來統(tǒng)計出現(xiàn)頻率,樣本類別votelabel=labels[q[i]] ## q[i]是索引值,通過labels來獲取對應(yīng)標(biāo)簽my_dict[votelabel] = my_dict.get(votelabel,0)+1 ## 統(tǒng)計每個標(biāo)簽的次數(shù)sortclasscount=sorted(dict_list(my_dict),key=operator.itemgetter(1),reverse=True)## 獲取votelabel鍵對應(yīng)的值,無返回默認(rèn)return sortclasscount[0][0] ## 返回出現(xiàn)頻次最高的類別?3.5、編寫識別函數(shù)
def shibie(): ## 定義一個識別手寫數(shù)字的函數(shù)label_list = [] ## 將訓(xùn)練集存儲到一個矩陣并存儲他的標(biāo)簽train_length = len(tarining) ## 直接一次獲取訓(xùn)練集長度train_zero = np.zeros((train_length,1024)) ## 創(chuàng)建(訓(xùn)練集長度,1024)維度的zeros數(shù)組for i in range(train_length): ## 通過遍歷訓(xùn)練集長度doc_name = tarining[i] ## 獲取所有的文件名file_label = int(doc_name[0]) ## 取文件名第一位文件的標(biāo)簽label_list.append(file_label) ## 將標(biāo)簽添加至handlabel中train_zero[i,:] = read_file(r'%s\%s'%(trainingDigits,doc_name))## 轉(zhuǎn)成1024的數(shù)組## 下面是測試集errornum = 0 ## 記錄error的初值testnum = len(test) ## 同上 獲取測試集的長度errfile = [] ## 定義一個空列表for i in range(testnum): ## 將每一個測試樣本放入訓(xùn)練集中使用KNN進(jìn)行測試testdoc_name = test[i] ## 通過i當(dāng)作下標(biāo)來獲取測試集里面的文件test_label = int(testdoc_name[0]) ## 拿到測試文件的名字 拿到我們的數(shù)字標(biāo)簽testdataor = read_file(r'%s\%s' %(testDigits,testdoc_name)) ## 調(diào)用read_file操作測試集result = xiangsidu(testdataor, train_zero, label_list, 3) ## 調(diào)用xiangsidu返回了resultprint("正在測試 %d, 內(nèi)容是 %d" % (test_label,result)) ## 輸出result和標(biāo)簽if (result != test_label): ## 判斷標(biāo)簽是否等于測試名errornum += 1 ## 不是則+1 記錄次數(shù)errfile.append(testdoc_name) ## 并把錯誤的文件名加入錯誤列表print("錯誤數(shù)量有 :%d" % errornum) ## 輸出錯誤的數(shù)量print("錯誤的有 :%s"%[i for i in errfile]) ## 輸出錯誤的列表中的名字print("準(zhǔn)確率 %.2f%%" % ((1 - (errornum / float(testnum))) * 100)) ## 計算準(zhǔn)確率?3.6、調(diào)用
if __name__ == '__main__': ## 聲明主函數(shù)a = time.time() ## 設(shè)置起始時間shibie() ## 調(diào)用測試函數(shù)b= time.time() - a ## 計算運行時間print("運行時間:",b) ## 輸出運行時間?3.7、運行結(jié)果
總結(jié)
以上是生活随笔為你收集整理的【机器学习实验二】k-NN算法—改进约会网站以及手写体数字识别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [附源码]Python计算机毕业设计Dj
- 下一篇: Maven项目 混合编译Java和Sca