文章目錄
一、說明
我是在jupyter完成的,然後導出成markdown格式,ipynb文件導出爲markdown的命令如下:
jupyter nbconvert --to markdown xxx.ipynb
二、題目
構造使用K-近鄰分類器的手寫識別系統。由於能力有限,這裏構造的系統只能識別0-9。需要識別的數字已經使用圖形處理軟件,處理成具有相同的色彩和大小:32像素*32像素的黑白圖像。
例如:
當前使用文本格式存儲圖像,即使不能有效的利用空間,但是爲了方便理解,還是將圖像轉換成文本格式。
示例:使用k-近鄰算法的手寫識別系統
(1)收集數據:提供文本文件。
(2)處理數據:編寫img2vector()函數,將圖像格式轉換成分類器使用的向量格式。
(3)分析數據:在Python命令提示符中檢查數據,確保它符合要求。
(4)訓練算法:此步驟不適用於k-近鄰算法。
(5)測試算法:編寫函數使用提供的部分數據集作爲測試樣本,對學習算法進行測試。
(6)使用算法:本例沒有完成此步驟
三、實踐部分
3.1 準備數據:將圖像轉換爲測試向量
trainingDigits中包含了大約2000個例子,每個數字大約有200個樣本;測試文件testDigits中包含了大約900個測試數據。兩組數據沒有重疊。爲了使用kNN算法分類器必須將一個3232的二進制矩陣轉換爲11024的向量,以便我們使用分類器處理數字圖像信息。
首先定義img2vector()函數,將3232的二進制矩陣轉換成11024的矩陣並返回。
# 圖片 to 向量
def img2vector(filename):
returnVect = zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0, 32 * i + j] = int(lineStr[j])
return returnVect
該函數使用方法
testVector = img2vector("testDigits/0_1.txt")
print(testVector[0, 0:31])
print(testVector[0, 32:61])
3.2 KNN算法
# kNN分類器
def classify0(inX, dataSet, labels, k):
'''
:param inX:
:param dataSet: 數據集合 矩陣
:param labels: 類別名
:param k: K值 int
:return:
'''
dataSetSize = dataSet.shape[0] #得到數據總量
diffMat = tile(inX,(dataSetSize,1)) - dataSet #將輸入數據擴充成與數據集同樣大小的矩陣並作差
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1) #axis = 1 參數是維度參數等於1在此處表示將一個矩陣的每一行向量相加
distances = sqDistances** 0.5
sortedDistancesIndicies = distances .argsort() #將列表值進行對比返回一個按照數值升序的下標值
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistancesIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0)+1
#dict.get("key") 返回value dict.get("key",default= None)如果能找到就返回對應的value找不到返回默認值
sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse=True)
#sorted 返回一個list operator.itemgetter(x,y)表示根據x+1維度的第y+1維度
return sortedClassCount[0][0]
3.3 測試算法:使用kNN識別手寫數字
def handwritingClassTest():
hwLabels = [] # 訓練數據真實值數組
trainingFileList = listdir("trainingDigits") # 獲取trainingDigits文件子目錄的列表
m = len(trainingFileList) # 獲得訓練數據總數
trainingMat = zeros((m, 1024)) # 初始化訓練數據矩陣
for i in range(m): # 循環將trainingDigits文件下的訓練數據文本文件放入矩陣traningMat中,真實值放入hwLabels中
fileNameStr = trainingFileList[i] # 獲取該次循環的文件名字符串
fileStr = fileNameStr.split('.')[0] # 將獲得的字符串按分隔符'.'分隔並取第一個即去拓展名的文件名
classNumber = int(fileStr.split('_')[0]) # 獲取訓練數據的真實值 非numpy數據需要指定數據類型int
hwLabels.append(classNumber) # 將得到的單個真實值按順序加入到真實值列表hwLabels中
trainingMat[i, :] = img2vector("trainingDigits/%s" % fileNameStr) # 把32*32的二進制文本文件轉換成1*1024矩陣並按行存儲到訓練數據總矩陣中
testFileList = listdir("testDigits")
errorCount = 0.0 # 錯誤預測計數器
mTest = len(testFileList) # 測試數據總量
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumber = int(fileStr.split('_')[0])
vectorUnderTest = img2vector("testDigits/%s" % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) # 用kNN分類算法分類
if (classifierResult != classNumber): # 判斷預測是否正確,不正確計數器+1打印錯誤預測
errorCount += 1.0
print("預測值爲:%d ,真實值爲:%d " % (classifierResult, classNumber))
print("測試總數:%d,預測錯誤總數:%d ,錯誤率爲:%f" % (mTest, errorCount, errorCount / float(mTest)))
3.4 截圖
四、源代碼
4.1 KNN.py
# 自定義knn分類器
def classify0(inX, dataSet, labels, k):
'''
:param inX:
:param dataSet: 數據集合 矩陣
:param labels: 類別名
:param k: K值 int
:return:
'''
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.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
# 圖片 to 向量
def img2vector(filename):
returnVect = zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0, 32 * i + j] = int(lineStr[j])
return returnVect
# 手寫數據集測試類
def handwritingClassTest():
hwLabels = []
trainingFileList = listdir('trainingDigits') # load the training set
m = len(trainingFileList)
trainingMat = zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] # take off .txt
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)
testFileList = listdir('testDigits') # iterate through the test set
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0] # take off .txt
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
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)))
4.2 KNN.ipynb
import numpy as np
from sklearn import datasets
import matplotlib.pyplot as plt
import time
# 自定義一個kNN分類器類
class MyKNNClassfier(object):
def __init__(self, k=5, distance='euc'):
self.k = k
self.distance = distance
self.x = None
self.y = None
def fit(self, X, Y):
'''
:param X: X_train array-like [n_samples,shape]
:param Y: Y_train array-like [n_samples,1]
:return:
'''
self.x = X
self.y = Y
def predict(self, X_test):
'''
用X_test預測出Y_test_predict
Y_test : array-like [n_samples,1]
:param X_test: array-like [n_samples,shape]
:return: output array-like [n_samples,1]
'''
output = np.zeros((X_test.shape[0], 1))
for i in range(X_test.shape[0]):
dis = []
for j in range(self.x.shape[0]):
if self.distance == 'euc': # 歐式距離
dis.append(np.linalg.norm(X_test[i] - self.x[j, :]))
labels = []
index = sorted(range(len(dis)), key=dis.__getitem__)
for j in range(self.k):
labels.append(self.y[index[j]])
counts = []
for label in labels:
counts.append(labels.count(label))
output[i] = labels[np.argmax(counts)]
return output
def score(self, x, y):
'''
分數評估,用來比較test數據集,查看預測成功的百分數
:param x: X_test
:param y: Y_test
:return: 準確率xx%
'''
pred = self.predict(x)
err = 0.0
for i in range(x.shape[0]):
if pred[i] != y[i]:
err = err + 1
return 1 - float(err / x.shape[0])
# 加載手寫圖片數據集
# 數據集詳情說明: https://blog.csdn.net/Asun0204/article/details/75607948
digits = datasets.load_digits()
x = digits.data
y = digits.target
digits.keys()
dict_keys(['data', 'target', 'target_names', 'images', 'DESCR'])
x[:3]
array([[ 0., 0., 5., 13., 9., 1., 0., 0., 0., 0., 13., 15., 10.,
15., 5., 0., 0., 3., 15., 2., 0., 11., 8., 0., 0., 4.,
12., 0., 0., 8., 8., 0., 0., 5., 8., 0., 0., 9., 8.,
0., 0., 4., 11., 0., 1., 12., 7., 0., 0., 2., 14., 5.,
10., 12., 0., 0., 0., 0., 6., 13., 10., 0., 0., 0.],
[ 0., 0., 0., 12., 13., 5., 0., 0., 0., 0., 0., 11., 16.,
9., 0., 0., 0., 0., 3., 15., 16., 6., 0., 0., 0., 7.,
15., 16., 16., 2., 0., 0., 0., 0., 1., 16., 16., 3., 0.,
0., 0., 0., 1., 16., 16., 6., 0., 0., 0., 0., 1., 16.,
16., 6., 0., 0., 0., 0., 0., 11., 16., 10., 0., 0.],
[ 0., 0., 0., 4., 15., 12., 0., 0., 0., 0., 3., 16., 15.,
14., 0., 0., 0., 0., 8., 13., 8., 16., 0., 0., 0., 0.,
1., 6., 15., 11., 0., 0., 0., 1., 8., 13., 15., 1., 0.,
0., 0., 9., 16., 16., 5., 0., 0., 0., 0., 3., 13., 16.,
16., 11., 5., 0., 0., 0., 0., 3., 11., 16., 9., 0.]])
y[:3]
array([0, 1, 2])
myknn_start_time = time.time()
clf = MyKNNClassfier(k=5)
clf.fit(x,y)
print('myknn score:',clf.score(x,y))
myknn_end_time = time.time()
print('myknn uses time:',myknn_end_time-myknn_start_time)
myknn score: 0.991652754590985
myknn uses time: 20.894031763076782
from sklearn.neighbors import KNeighborsClassifier
sklearnknn_start_time = time.time()
clf_sklearn = KNeighborsClassifier(n_neighbors=5)
clf_sklearn.fit(x,y)
print('sklearn score:',clf_sklearn.score(x,y))
sklearnknn_end_time = time.time()
print('sklearn uses time:',sklearnknn_end_time-sklearnknn_start_time)
sklearn score: 0.9905397885364496
sklearn uses time: 0.44499993324279785
# 對數據進行切分,即分出數據集和測試集
from sklearn.model_selection import train_test_split #引入數據集拆分的模塊
# 劃分訓練集
(X_train,
X_test,
Y_train,
Y_test) = train_test_split(x, y, train_size=0.8, random_state=1)
X_train[:3]
array([[ 0., 0., 0., 2., 15., 8., 0., 0., 0., 0., 1., 15., 13.,
3., 0., 0., 0., 0., 9., 13., 1., 0., 0., 0., 0., 1.,
15., 6., 0., 5., 11., 0., 0., 7., 14., 0., 1., 15., 8.,
0., 0., 8., 15., 9., 15., 16., 3., 0., 0., 1., 11., 16.,
16., 10., 0., 0., 0., 0., 0., 2., 15., 5., 0., 0.],
[ 0., 0., 0., 7., 16., 16., 11., 0., 0., 0., 6., 16., 16.,
16., 16., 0., 0., 0., 11., 16., 16., 16., 9., 0., 0., 0.,
2., 9., 11., 14., 10., 0., 0., 0., 0., 0., 0., 10., 6.,
0., 0., 0., 0., 0., 4., 11., 1., 0., 0., 0., 0., 2.,
14., 2., 0., 0., 0., 0., 0., 11., 3., 0., 0., 0.],
[ 0., 0., 0., 6., 11., 0., 0., 0., 0., 0., 0., 15., 10.,
0., 0., 0., 0., 0., 7., 15., 2., 0., 0., 0., 0., 0.,
16., 6., 0., 0., 0., 0., 0., 3., 16., 7., 5., 5., 0.,
0., 0., 2., 16., 13., 9., 13., 11., 0., 0., 0., 8., 13.,
7., 5., 15., 3., 0., 0., 0., 5., 11., 13., 12., 2.]])
Y_train[:3]
array([4, 9, 6])
# 新創建一個knn分類器
knn01 = KNeighborsClassifier()
# 創建模型
knn01.fit(X_train, Y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=5, p=2,
weights='uniform')
# 查看預測的準確性
knn01.score(X_test, Y_test)
0.9944444444444445
knn01.predict(X_test[0:3])
array([1, 5, 0])
# 採用支持向量機分類器來嘗試看看
from sklearn import svm
#在數字數據集的情況下,任務是給出圖像來預測其表示的數字,共有10個可能類,在這些類上擬合一個估計
clf01 = svm.SVC(gamma=0.001,C=100.)# 估計器實例命名爲clf01
#使用除了最後一張意外的所有圖像,用[:-1]語法選擇這個訓練集
clf01.fit(digits.data[:-1],digits.target[:-1])
SVC(C=100.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma=0.001, kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
# 預測
clf01.predict(digits.data[-1:])
array([8])
# 建立svm模型
clf01.fit(X_train,Y_train)
SVC(C=100.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma=0.001, kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
# 查看準確率
clf01.score(X_test,Y_test)
0.9916666666666667