写点什么

支持向量机实现光学字符识别

发布于: 2021 年 04 月 05 日
支持向量机实现光学字符识别

OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机字符的过程;即,针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,并通过字符识别模型将图像中的文字处理成文本格式。

光学字符识别是 OCR 的核心,然而对于许多类型的机器学习算法来说,这种图像处理都是一项艰巨的任务。 将像素模式连接到更高概念的关系是极其复杂的,而且很难定义。 例如,让一个人识别一张面孔、一只猫或字母 A 是容易的,但用严格的规则来定义这些模式是很困难的。 此外,图像数据往往是噪声数据,对于光学字符图像,灯光、定位和对象的位置都能影响最终的图像数据。

支持向量机非常适合处理图像数据,它能够学习复杂的图案而不需要对噪声数据过度敏感,能够以较高的准确度识别光学图案。

1、数据来源

本博文中,将使用 UCI 公开的光学字符识别数据集(http://archive.ics.uci.edu/ml/datasets/Letter+Recognition),利用支持向量机(SVM)来构建光学字符识别模型。

该数据集包含了 26 个英文大写字母的 20000 个样本。 每一个样本代表光学图像中的一个矩形区域,该区域只包含单一字符。 每一个样本包含 16 个自变量和 letter 目标变量,letter 指示当前样本是哪一个字母。每一个特征变量的具体含义如下:


  • letter 字符 (取值为 A,B,...,Z)

  • x-box 字符所在矩形区域的水平位置

  • y-box 字符所在矩形区域的竖直位置

  • width 矩形区域的宽度

  • high 矩形区域的高度

  • onpix 矩阵区域的黑色像素数

  • x-bar 矩形区域内黑色像素的平均 x 值

  • y-bar 矩形区域内黑色像素的平均 y 值

  • x2bar x 平均方差

  • y2bar y 平均方差

  • xybar x 和 y 的平均相关性

  • x2ybr x * x * y 均值

  • xy2br x * y * y 均值

  • x-ege 从左到右的边缘数目

  • xegvy x 边缘与 y 的相关性

  • y-ege 从下到上的边缘数目

  • yegvx y 边缘与 x 的相关性

2、数据预处理

光学字符识别数据集中包含 16 个特征变量,这些变量用字符矩形区域的水平位置和竖直位置、黑色像素比例、黑色像素的平均水平和竖直位置来度量一个字符。

首先,使用 pandas 中的 read_csv()函数将数据导入,实现代码如下所示:

import pandas as pdletters = pd.read_csv("./input/letterecognition.csv")letters.head(10)
复制代码

前 10 行数据格式如下所示:

接下来使用 pandas 中 Series 的 value_counts()函数,观察数据集中每一种字符的数量分布。

sort_index()函数可以让结果按照字母排序展示结果,实现代码如下所示:

letters["letter"].value_counts().sort_index()
复制代码

效果如下所示:

A    789B    766C    736D    805E    768F    775G    773H    734I    755J    747K    739L    761M    792N    783O    753P    803Q    783R    758S    748T    796U    813V    764W    752X    787Y    786Z    734dtype: int64
复制代码

可见,各个字符的样本数量分布相对均衡。

现在,进一步观察每一个自变量的取值分布,实现代码如下所示:

letters.iloc[:,1:].describe()
复制代码

数据取值分布如下所示(局部数据): 

观察发现 16 个自变量的取值范围都在 0~15 之间,因此对于该数据集不需要对变量进行标准化操作。

此外,数据集作者已经将样本随机排列,所以也不需要我们对数据进行随机打散。 此处,直接取前 14000 个样本(70%)作为训练集,后 6000 个样本(30%)作为测试集,实现代码如下所示:

letters_train = letters.iloc[0:14000,]letters_test = letters.iloc[14000:20000,]
复制代码

3、模型训练

接下来使用 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 的常用取值及其对应含义如下:


  • "linear":线性核函数

  • "poly":多项式核函数

  • "rbf":径向基核函数

  • "sigmoid": sigmoid 核函数

约束惩罚参数 C 为对超过约束条件的样本的惩罚项。C 值越大,惩罚越大,支持向量机的决策边界越窄。

现在,可以使用训练集构建分类模型了,选用最简单的线性核函数,C 采用默认值 1。实现代码如下所示:

from sklearn.svm import SVCletter_recognition_model = SVC(C = 1, kernel = "linear")letter_recognition_model.fit(letters_train.iloc[:,1:],letters_train['letter'])
复制代码

设置成功后,SVC 配置参数效果如下所示:

SVC(C=1, cache_size=200, class_weight=None, coef0=0.0,  decision_function_shape=None, degree=3, gamma='auto', kernel='linear',  max_iter=-1, probability=False, random_state=None, shrinking=True,  tol=0.001, verbose=False)
复制代码

4、模型性能评估

接下来,使用 predict()函数得到上一节训练的支持向量机模型在测试集合上的预测结果,然后使用 sklearn.metrics 中的相关函数对模型的性能进行评估,实现代码如下所示:

from sklearn import metricsletters_pred = letter_recognition_model.predict(letters_test.iloc[:,1:])print(metrics.classification_report(lettters_test["letter"], letters_pred))print(pd.DataFrame(metrics.confusion_matrix(lettters_test["letter"], letters_pred),\                  columns = letters["letter"].value_counts().sort_index().index,\                  index = letters["letter"].value_counts().sort_index().index))
复制代码

效果如下所示:

             precision    recall  f1-score   support
A 0.92 0.92 0.92 245 B 0.78 0.87 0.82 207 C 0.82 0.84 0.83 202 D 0.77 0.91 0.83 251 E 0.80 0.86 0.83 230 F 0.77 0.89 0.82 240 G 0.73 0.75 0.74 235 H 0.65 0.70 0.67 210 I 0.89 0.86 0.87 243 J 0.83 0.88 0.86 216 K 0.79 0.84 0.81 214 L 0.95 0.86 0.90 250 M 0.89 0.94 0.92 224 N 0.95 0.88 0.91 246 O 0.87 0.71 0.78 216 P 0.92 0.80 0.86 246 Q 0.85 0.75 0.80 252 R 0.81 0.84 0.82 242 S 0.75 0.67 0.71 240 T 0.89 0.90 0.90 226 U 0.91 0.92 0.92 248 V 0.91 0.91 0.91 212 W 0.90 0.92 0.91 216 X 0.89 0.84 0.86 230 Y 0.93 0.88 0.90 223 Z 0.86 0.83 0.84 236
avg / total 0.85 0.84 0.84 6000
A B C D E F G H I J ... Q R S T \A 225 1 0 2 0 0 2 0 0 1 ... 0 2 1 0 B 0 181 0 4 1 0 1 2 1 1 ... 0 10 4 0 C 1 0 169 0 8 0 7 0 0 0 ... 0 0 0 0 D 1 9 0 228 0 1 1 2 0 1 ... 0 0 0 0 E 0 2 5 0 197 2 11 0 0 0 ... 1 1 1 5 F 0 1 3 1 3 213 1 2 2 3 ... 0 0 0 4 G 0 2 14 2 1 4 177 2 0 0 ... 9 3 5 0 H 1 4 2 12 0 5 4 147 0 1 ... 3 9 0 1 I 0 1 2 4 0 7 0 0 208 12 ... 0 0 2 0 J 2 0 0 2 0 2 0 3 11 190 ... 0 0 2 0 K 0 0 2 5 4 0 1 5 0 0 ... 0 12 0 0 L 0 0 5 5 6 0 3 2 0 0 ... 4 1 3 1 M 1 3 0 0 0 0 0 3 0 0 ... 0 2 0 0 N 1 0 0 7 0 0 0 10 0 0 ... 0 2 0 0 O 3 0 3 7 0 0 2 26 0 1 ... 5 1 0 0 P 0 2 0 3 0 25 5 0 1 1 ... 1 1 0 0 Q 5 5 0 1 7 1 14 3 0 4 ... 190 1 13 0 R 0 11 0 4 0 0 2 6 0 0 ... 0 203 0 0 S 1 8 0 1 10 7 7 0 4 1 ... 9 1 160 3 T 1 0 0 0 0 3 2 5 0 0 ... 0 1 2 204 U 1 0 0 1 0 0 0 1 0 0 ... 0 0 0 1 V 0 2 0 0 0 2 0 4 0 0 ... 0 1 0 0 W 1 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 X 0 1 0 5 5 1 1 0 6 3 ... 0 0 2 2 Y 0 0 0 3 0 4 0 3 1 0 ... 2 0 0 5 Z 1 0 0 1 4 1 0 0 1 9 ... 0 0 18 3
U V W X Y Z A 2 0 0 0 3 3 B 0 0 0 1 0 0 C 4 0 0 0 0 0 D 3 0 0 0 0 0 E 0 0 0 2 0 2 F 0 0 0 0 1 0 G 0 6 1 0 0 0 H 2 3 0 2 0 0 I 0 0 0 4 0 3 J 0 0 0 0 0 2 K 2 0 0 4 0 0 L 0 0 0 6 0 0 M 1 0 4 0 0 0 N 1 2 0 0 0 0 O 3 1 4 1 0 0 P 1 0 0 0 5 0 Q 0 1 0 0 0 0 R 0 1 0 1 0 0 S 0 0 0 2 1 20 T 0 0 0 0 3 3 U 228 0 6 0 0 0 V 0 193 6 0 1 0 W 2 2 199 0 0 0 X 1 0 0 193 1 0 Y 0 4 1 1 196 0 Z 0 0 0 1 0 196
[26 rows x 26 columns]
复制代码

上述混淆矩阵中对角线的元素表示模型正确预测数,对角元素之和表示模型整体预测正确的样本数。

而非对角线元素上的值则可以反映模型在哪些类的预测上容易犯错,例如第 P 行第 F 列的取值为 25,说明模型有 25 次将“P”字符错误地识别为“F”字符。直观来看,“P”和“F”相似度比较高,对它们的区分也更具有挑战性。 现在,来通过这个来计算模型在测试集中的预测正确率。代码如下所示:

agreement = lettters_test["letter"] == letters_predprint(agreement.value_counts())print("Accuracy:", metrics.accuracy_score(lettters_test["letter"], letters_pred))
复制代码

可见,初步模型在 6000 个测试样本中,正确预测 5068 个,整体正确率(Accuaray)为 84.47%。

5、模型性能提升

对于支持向量机,有两个主要的参数能够影响模型的性能:一是核函数的选取,二是惩罚参数 C 的选择。 下面,期望通过分别尝试这两个参数来进一步改善模型的预测性能。

5.1、核函数的选取

在 SVC 中,核函数参数 kernel 可选值为"rbf"(径向基核函数)、“poly”(多项式核函数)、"sigmoid"(sigmoid 核函数)和"linear"(线性核函数)。我们的初始模型选取的是线性核函数,下面我们观察在其他三种核函数下模型正确率的改变。实现代码如下所示:

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(lettters_test["letter"], letters_pred))
复制代码

效果如下所示:

kernel =  rbf , Accuracy: 0.971166666667kernel =  poly , Accuracy: 0.943166666667kernel =  sigmoid , Accuracy: 0.0376666666667
复制代码

从结果可以看到,当选取 RBF 核函数时:

  • 模型正确率由 84.47%提高到 97.12%

  •  多项式核函数下模型正确率为 94.32%

  •  sigmoid 核函数下模型的正确率只有 3.77%

5.2、惩罚参数 C 的选取

我们将分别测试 𝐶=0.01,0.1,1,10,100C=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(lettters_test["letter"], letters_pred))
复制代码

效果如下所示:

C =  0.01 , Accuracy: 0.059C =  0.1 , Accuracy: 0.886333333333C =  1 , Accuracy: 0.971166666667C =  10 , Accuracy: 0.976166666667C =  100 , Accuracy: 0.976333333333
复制代码

可见,当惩罚参数 C 设置为 10 和 100 时,模型正确率进一步提升,分别达到 97.62%和 97.63%。

发布于: 2021 年 04 月 05 日阅读数: 35
用户头像

【研究方向】物联网、嵌入式、AI、Python 2018.02.09 加入

【公众号】美男子玩编程

评论

发布
暂无评论
支持向量机实现光学字符识别