光学字符识别是OCR的核心,然而对于许多类型的机器学习算法来说,这种图像处理都是一项艰巨的任务。 将像素模式连接到更高概念的关系是极其复杂的,而且很难定义。
本案例中,我们将使用公开的光学字符数据集,利用支持向量机来构建光学字符识别模型。
OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机字符的过程;即,针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,并通过字符识别模型将图像中的文字处理成文本格式。
光学字符识别是OCR的核心,然而对于许多类型的机器学习算法来说,这种图像处理都是一项艰巨的任务。 将像素模式连接到更高概念的关系是极其复杂的,而且很难定义。 例如,让一个人识别一张面孔、一只猫或字母A是容易的,但用严格的规则来定义这些模式是很困难的。 此外,图像数据往往是噪声数据,对于光学字符图像,灯光、定位和对象的位置都能影响最终的图像数据。 支持向量机非常适合处理图像数据,它能够学习复杂的图案而不需要对噪声数据过度敏感,能够以较高的准确度识别光学图案。
本案例中,我们将使用公开的光学字符数据集,利用支持向量机来构建光学字符识别模型。
我们将使用UCI的光学字符识别数据集(http://archive.ics.uci.edu/ml/datasets/Letter+Recognition)。 该数据集包含了26个英文大写字母的20000个样本。 每一个样本代表光学图像中的一个矩形区域,该区域只包含单一字符。 每一个样本包含16个自变量和letter目标变量,letter指示当前样本是哪一个字母。每一个特征变量的具体含义如下:
我们的数据集中包含16个特征变量,这些变量用字符矩形区域的水平位置和竖直位置、黑色像素比例、黑色像素的平均水平和竖直位置来度量一个字符。首先,我们使用pandas中的read_csv()函数将数据导入:
import pandas as pd
letters = pd.read_csv("./input/letterecognition.csv")
letters.head(10)
首先,使用pandas中Series的value_counts()函数,我们观察数据集中每一种字符的数量分布。 sort_index()函数可以让结果按照字母排序展示结果。
letters["letter"].value_counts().sort_index()
可见,各个字符的样本数量分布相对均衡。现在,我们进一步观察每一个自变量的取值分布:
letters.iloc[:,1:].describe()
观察发现16个自变量的取值范围都在0~15之间,因此对于该数据集我们不需要对变量进行标准化操作。 此外,数据集作者已经将样本随机排列,所以也不需要我们对数据进行随机打散。 此处,我们直接取前14000个样本(70%)作为训练集,后6000个样本(30%)作为测试集。
letters_train = letters.iloc[0:14000,]
letters_test = letters.iloc[14000:20000,]
本案例中,我们将使用sklearn.svm包中的相关类来实现来构建基于支持向量机的光学字符识别模型。 在sklearn.svm包中,有三个类均实现了支持向量机算法:SVC, NuSVC 和 LinearSVC。 SVC 和 NuSVC接受的参数有细微差别,且底层的数学形式不一样。 而 LinearSVC 则是使用简单的线性核函数,其实现基于liblinear (https://www.csie.ntu.edu.tw/~cjlin/liblinear/), 对于大规模的样本训练速度会更快。 这三个支持向量机的具体介绍参考sklearn官方文档http://scikit-learn.org/stable/modules/svm.html。
本案例中,我们选用 SVC 来进行模型构建。 SVC 有两个主要的参数可以设置:核函数参数 kernel 和约束惩罚参数C。 核函数参数 kernel的常用取值及其对应含义如下:
约束惩罚参数C为对超过约束条件的样本的惩罚项。C值越大,惩罚越大,支持向量机的决策边界越窄。
现在,我们可以使用训练集构建分类模型了,我们选用最简单的线性核函数,C采用默认值1。
from sklearn.svm import SVC
letter_recognition_model = SVC(C = 1, kernel = "linear")
letter_recognition_model.fit(letters_train.iloc[:,1:],letters_train['letter'])
首先,使用predict()函数得到上一节训练的支持向量机模型在测试集合上的预测结果,然后使用 sklearn.metrics中的相关函数对模型的性能进行评估。
from sklearn import metrics
letters_pred = letter_recognition_model.predict(letters_test.iloc[:,1:])
print metrics.classification_report(letters_test["letter"], letters_pred)
print pd.DataFrame(metrics.confusion_matrix(letters_test["letter"], letters_pred),\
columns = letters["letter"].value_counts().sort_index().index,\
index = letters["letter"].value_counts().sort_index().index)
上述混淆矩阵中对角线的元素表示模型正确预测数,对角元素之和表示模型整体预测正确的样本数。 而非对角线元素上的值则可以反映模型在哪些类的预测上容易犯错,例如第P行第F列的取值为25,说明模型有25次将“P”字符错误地识别为“F”字符。直观来看,“P”和“F”相似度比较高,对它们的区分也更具有挑战性。 现在,让我们来通过这个来计算模型在测试集中的预测正确率。
agreement = letters_test["letter"] == letters_pred
print agreement.value_counts()
print "Accuracy:", metrics.accuracy_score(letters_test["letter"], letters_pred)
可见,我们的初步模型在6000个测试样本中,正确预测5068个,整体正确率(Accuaray)为84.47%。
kernels = ["rbf","poly","sigmoid"]
for kernel in kernels:
letters_model = SVC(C = 1, kernel = kernel)
letters_model.fit(letters_train.iloc[:,1:],letters_train['letter'])
letters_pred = letters_model.predict(letters_test.iloc[:,1:])
print "kernel = ", kernel , ", Accuracy:",\
metrics.accuracy_score(letters_test["letter"], letters_pred)
从结果可以看到,当选取RBF核函数时,模型正确率由84.47%提高到97.12%。 多项式核函数下模型正确率为94.32%。 sigmoid核函数下模型的正确率只有3.77%。
我们将分别测试 $C = 0.01,0.1,1,10,100$时字符识别模型正确率的变化。核函数选取径向基核函数(即"rbf")。
c_list = [0.01, 0.1, 1, 10, 100]
for C in c_list:
letters_model = SVC(C = C, kernel = "rbf")
letters_model.fit(letters_train.iloc[:,1:],letters_train['letter'])
letters_pred = letters_model.predict(letters_test.iloc[:,1:])
print "C = ", C , ", Accuracy:",\
metrics.accuracy_score(letters_test["letter"], letters_pred)
可见,当惩罚参数C设置为10和100时,模型正确率进一步提升,分别达到97.62%和97.63%。