Python大法好啊,Python中有好几种机器学习通用库,提供了类似于STL那样的算法模板函数。由于Python函数参数传递的特殊用法,它们还支持手动调参。
目前常用的机器学习库有sklearn(通用机器学习库)、TensorFlow(深度学习库,支持NVIDIA的GPU加速)、pytorch(深度学习库)、Keras(也算深度学习库,特别适合构建神经网络)等。我们用sklearn,它特别适用于解决轻量级、不需要搭建神经网络的机器学习问题,比如简单的分类和回归。
如果没有安装sklearn,立即打开命令行,输入pip install sklearn即可。如果需要国内镜像源,输入pip install sklearn -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com
我们今天要解决的是手写阿拉伯数字识别。它是一个10分类问题(阿拉伯数字只有10个)。
分类是这样一种问题,对于每一个数据,都有一个类别,并且类别的总个数是有限的、离散的。任务是通过一定算法,根据训练数据,找到测试数据对应的类别。
我们采取的算法是k-近邻分类。它的基本思想是,取训练集中,和测试目标的特征向量最接近的k个数据中,出现次数最多的类别进行分类。也就是说,它是用统计学规律来分类的算法。算法的细节就不讲了,因为我们的目的是解决问题,不是研究算法。感兴趣的童鞋可以自行百度。
再来说一下分类算法中的数据。数据是一个复合结构,它包含多个特征,所以,可以把数据看成向量。向量每个维度(分量)都是一个特征。
sklearn已经替我们写好了k近邻的实现。我们只需要传入相关参数,进行训练和分类预测即可。下面就来说说sklearn中相关函数的用法。
这个函数用来创建一个k近邻分类器。它位于sklearn.neighbors包中。用法:
knn = sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)
n_neighbors是关键字参数,用来指定k的大小,即算法中选取几个邻居。使用时,要在文件开头import sklearn.neighbors
。
注意,两次调用这个函数,所创建的分类器是不同的对象。另外,分类器还可以用其它函数创建其他种类的,比如逻辑斯蒂回归等等,但它们都是分类器。这个设计模式称为工厂模式,是类设计的一个有力武器。感兴趣的自行百度。
fit函数是训练函数,它用于把训练集的数据和训练目标(也就是分类的类别)传入到一个分类器中。分类器会用一个表格来存储你传入的数据,称之为“学习”。用法:
knn.fit(x_train, y_train) # knn就是你刚才创建的分类器
说明:
1、x_train必须是二维数组,称之为训练集。每一行表示一个数据,或称样本,比如一个具体的人;每一列表示数据的一个特征,比如,人的“虹膜颜色”、“发色”、“肤色”三个特征。
2、y_train必须是一维数组,称之为训练目标。每个元素表示一个类别。比如,黄种人、白种人、黑种人。
3、使用时,x_train的行必须和y_train对应位置的元素一一对应。比如,x_train的第一行是“黑眼睛”、“黑头发”、“黄皮肤”,那么y_train的第一个元素就是“黄种人”。
predict是预测函数。它的作用是,以刚才训练好的数据集作为依据,来预测新数据的分类结果。x_test必须是一个二维数组,称之为测试集。数据的结构同训练集。用法:
result = knn.predict(x_test)
它的返回值是一个一维数组,也就是对应位置数据的预测结果。
数据收集到之后,是不能直接使用的。比如我们今天要手写数字识别,它是图片。所以,必须用图像处理软件将其灰度化、二值化之后,形成32*32的01矩阵,并存储为文本文件形式。也就是说,我们把像素调成1K,并且不用灰度值,而是设定阈值并转化为01。好在我们的数据集已经处理好,百度网盘链接:https://pan.baidu.com/s/1Xlte01al2_eJFpTRHhb0Rw 提取码:i9nr
它有两个文件夹,一个是训练数据trainingDigits,一个是测试数据testDigits。我们解压后,把它们放在py文件的同级目录下。注意这一步非常重要,如果目录放错,则下面的程序根本无法运行。
每个文件都是一个01矩阵,但我们要求一个数据是一个一维向量。接下来要解决的是,二维矩阵如何转化为一维向量。答案就是,按行拼接。比如4*4的矩阵:
0100
0010
0001
1000
拼接成0100001000011000
这样,它就是一个具有16个特征的数据。同理,我们的32阶矩阵,就能按行拼接成一个具有1024个特征的数据。
我们的每个文件名第一个字符就是相应的阿拉伯数字,所以将它提取出来,并转化为整数即可。
编写一个遍历函数,来对每个文件进行处理:
# 需要import os
def walk_files(directory, x, y): # x是二维数组,y是一维数组
for root, dirs, files in os.walk(directory):
for filename in files:
vector = ""
fp = open(directory + "\\" + filename, "r")
line = fp.readline()
while line:
vector += line.strip("\n\r") # 字符串拼接,并去掉换行符
line = fp.readline()
fp.close() # 随手关闭文件是一个好习惯
x.append(list(vector)) # 字符串转化为数组,并追加到x之后
y.append(int(filename[0])) # 文件名的第一个字符就是对应的阿拉伯数字
有了文件遍历的函数,我们就可以抓取数据并训练了。接下来的操作非常简单。直接上代码:
train_dir = "trainingDigits"
test_dir = "testDigits"
x_train = []
y_train = []
x_test = []
y_test = []
walk_files(train_dir, x_train, y_train) # 构造训练集
knn = sklearn.neighbors.KNeighborsClassifier(n_neighbors=3) # 创建分类器
knn.fit(x_train, y_train) # 训练
walk_files(test_dir, x_test, y_test) # 构造测试集
result = knn.predict(x_test) # 预测
correct = 0
for i in range(0, len(result)):
if result[i] == y_test[i]:
correct += 1
print("score: %.3f" % (correct / len(result))) # 检测预测正确率
运行结果:
score: 0.987
错误率1.3%。我们圆满完成了提出的问题。
如果有兴趣,可以试试手动调整k(也就是n_neighbors)的数值,看看正确率的变化。我可以把结论先给大家,k=3的时候预测效果最好。
最后,我把完整的Python代码贴给大家:
import os
import sklearn.neighbors
def walk_files(directory, x, y):
for root, dirs, files in os.walk(directory):
for filename in files:
vector = ""
fp = open(directory + "\\" + filename, "r")
line = fp.readline()
while line:
vector += line.strip("\n\r")
line = fp.readline()
fp.close()
x.append(list(vector))
y.append(int(filename[0]))
train_dir = "trainingDigits"
test_dir = "testDigits"
x_train = []
y_train = []
x_test = []
y_test = []
walk_files(train_dir, x_train, y_train)
knn = sklearn.neighbors.KNeighborsClassifier(n_neighbors=3)
knn.fit(x_train, y_train)
walk_files(test_dir, x_test, y_test)
result = knn.predict(x_test)
correct = 0
for i in range(0, len(result)):
if result[i] == y_test[i]:
correct += 1
print("score: %.3f" % (correct / len(result)))
python的最大魅力就在于import,对不对?很简短,一个机器学习的程序也就不到40行。