最近在学习机器学习算法,感觉有本书写得很不错——《机器学习实战》,如果有一点基础去看这本书,然后在结合书中实例进行实践,还是很有收获的。
之后,可能会不定时的更新此书的相关内容,主要内容参考此书,夹杂一些我自己实践中的经验。
KNN算法
本书介绍的第一个机器学习算法是kNN算法,这个算法比较基础,简单易懂。
实现的主要步骤:
(1)收集数据。
(2)准备数据:使用python解析文本文件
(3)分析数据:主要是可视化数据
(4)训练算法:kNN不用训练
(5)测试算法:计算错误率,或者准确率
(6)使用算法:
A. 输入训练样本向量或者特征向量;
B. 计算待分类样本(或特征向量)到这些训练样本的距离,一般是欧式距离
C. 选取最小的k个距离,统计对应的类别,哪个类别多,待分类的样本就属于哪一类
kNN的简单实现
注:代码主要参考自书本,有一些改动。
kNN的简单实现
#-*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2 as cv
# kNN算法的流程
# 收集、整理数据->计算数据到分类点的距离
# 选取k个距离最近的数据,看这几个数据属于哪一类,
# 所属类别多的,待分类的点就属于此类
# kNN算法不需要训练!!
def createDataSet():
# 构造简单的数据集
group = np.array([[1.0, 1.1],[1.0,1.0],[0,0.2],[0,0.1]])
# 这里对数据集进行修改,感觉书上的有点不合理 [0,0]->[0,0.2]
labels = ['A','A','B','B']
return group, labels
数据的可视化
def visualData(group,labels):
# 数据可视化
x1 = group[:2,0]
y1 = group[:2,1]
x2 = group[2:,0]
y2 = group[2:,1]
p1= plt.scatter(x1,y1)
p2= plt.scatter(x2,y2)
plt.legend([p1,p2],["A","B"])
plt.show()
def classify0(inX, dataSet, labels, k):
"""
:param inX: 待分类的向量 - [x0,y0]
:param dataSet: 数据集 - [[x1,y1],[x2,y2],...] - numpy类型
:param labels: 标签数据 - 'A'或者'B'
:param k: 设置的k值,选取前k个结果 - int
:return:分类的结果,'A'或者'B'
"""
d = inX-dataSet
sqMat = d**2
sqDis = sqMat.sum(axis=1)
distances = sqDis**0.5
sortedDis = distances.argsort() # 将距离排序,并提取其对应的序号
assert len(sortedDis)>=k # 检查k的设置是否有问题
classCount = {}
for i in range(k):
votelabel = labels[sortedDis[i]]
classCount[votelabel] = classCount.get(votelabel, 0)+1
keylist = list(classCount.keys())
s = 0
for l in keylist:
if classCount[l] > s:
s = classCount[l]
res = l
return res
运行结果:
可视化的结果:
最终分类结果:
kNN的手写数字识别
思路:将手写数字图像转为向量,然后计算测试图像向量和训练图像向量的距离,然后选k个所属类别最多的作为最终类别。
# 以下为手写数字识别的内容
def img2vector(filename):
# 将二进制图转换为向量,方便进行距离的计算
resVec = np.zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
resVec[0, 32*i+j] = int(lineStr[j])
return resVec
def visualImg(filename):
# 可视化手写数字图像
resimg = np.zeros((32, 32))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
resimg[i][j] = int(lineStr[j])
cv.imshow("numberImg", resimg)
cv.waitKey(0)
return resimg
def handwritingClassTest():
hwLabels = []
trainingFileList = os.listdir('./digits/trainingDigits/')
m = len(trainingFileList)
trainingMat = np.zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('./digits/trainingDigits/%s' % fileNameStr)
testFileList = os.listdir('./digits/testDigits')
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split("_")[0])
vectorUnderTest = img2vector('./digits/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)))
手写数字图像的可视化:
最终的分类结果:
单看这个结果,好像比用CNN的结果还要好,https://blog.csdn.net/louishao/article/details/60867339
但是,kNN分类的这个数据集是比较小的,而且算法较为简单,比较复杂的情况分类的效果就会变得差一些,而且kNN无法表示图像/数据的特征。
完整代码
#-*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2 as cv
# kNN算法的流程
# 收集、整理数据->计算数据到分类点的距离
# 选取k个距离最近的数据,看这几个数据属于哪一类,
# 所属类别多的,待分类的点就属于此类
# kNN算法不需要训练!!
def createDataSet():
# 构造简单的数据集
group = np.array([[1.0, 1.1],[1.0,1.0],[0,0.2],[0,0.1]])
# 这里对数据集进行修改,感觉书上的有点不合理 [0,0]->[0,0.2]
labels = ['A','A','B','B']
return group, labels
def visualData(group,labels):
# 数据可视化
x1 = group[:2,0]
y1 = group[:2,1]
x2 = group[2:,0]
y2 = group[2:,1]
p1= plt.scatter(x1,y1)
p2= plt.scatter(x2,y2)
plt.legend([p1,p2],["A","B"])
plt.show()
def classify0(inX, dataSet, labels, k):
"""
:param inX: 待分类的向量 - [x0,y0]
:param dataSet: 数据集 - [[x1,y1],[x2,y2],...] - numpy类型
:param labels: 标签数据 - 'A'或者'B'
:param k: 设置的k值,选取前k个结果 - int
:return:分类的结果,'A'或者'B'
"""
d = inX-dataSet
sqMat = d**2
sqDis = sqMat.sum(axis=1)
distances = sqDis**0.5
sortedDis = distances.argsort() # 将距离排序,并提取其对应的序号
assert len(sortedDis)>=k # 检查k的设置是否有问题
classCount = {}
for i in range(k):
votelabel = labels[sortedDis[i]]
classCount[votelabel] = classCount.get(votelabel, 0)+1
keylist = list(classCount.keys())
s = 0
for l in keylist:
if classCount[l] > s:
s = classCount[l]
res = l
return res
# 以下为手写数字识别的内容
def img2vector(filename):
# 将二进制图转换为向量,方便进行距离的计算
resVec = np.zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
resVec[0, 32*i+j] = int(lineStr[j])
return resVec
def visualImg(filename):
# 可视化手写数字图像
resimg = np.zeros((32, 32))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
resimg[i][j] = int(lineStr[j])
cv.imshow("numberImg", resimg)
cv.waitKey(0)
return resimg
def handwritingClassTest():
hwLabels = []
trainingFileList = os.listdir('./digits/trainingDigits/')
m = len(trainingFileList)
trainingMat = np.zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('./digits/trainingDigits/%s' % fileNameStr)
testFileList = os.listdir('./digits/testDigits')
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split("_")[0])
vectorUnderTest = img2vector('./digits/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)))
if __name__ == '__main__':
'''
group, labels = createDataSet()
visualData(group, labels)
inX = [0, 0]
classres = classify0(inX, group, labels, 3)
print(classres)
'''
'''
handWriteTrainDir = "./digits/trainingDigits/"
handWriteTestDir = "./digits/testDigits/"
trainlist = os.listdir(handWriteTestDir)
visualImg(handWriteTrainDir+trainlist[500])
vec = img2vector(handWriteTrainDir+trainlist[0])
'''
handwritingClassTest()