SVM算法实现光学字符识别

美男子玩编程

共 9646字,需浏览 20分钟

 · 2021-04-14

点击上方蓝色字体,关注我们


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()


效果如下所示:



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


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

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%。


往期推荐



点击阅读原文,更精彩~
浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报