zoukankan      html  css  js  c++  java
  • Python 3 利用机器学习模型 进行手写体数字检测

    0.引言

     介绍了如何生成手写体数字的数据,提取特征,借助 sklearn 机器学习模型建模,进行识别手写体数字 1-9 模型的建立和测试。

     用到的几种模型:

        1. LR,Logistic Regression,                (线性模型)中的逻辑斯特回归

        2. Linear SVC,Support Vector Classification,      (支持向量机)中的线性支持向量分类 

        3. MLPC,Multi-Layer Perceptron Classification,       (神经网络)多层感知机分类

        4. SGDC,Stochastic Gradient Descent Classification,   (线性模型)随机梯度法求解

        手写体的识别是一个 分类 问题,提取图像特征作为模型输入,输出到标记数字 1-9;

     

     主要内容:

          1. 生成手写体数字数据集;

          2. 提取图像特征存入 CSV;

          3. 利用机器学习建立和测试手写体数字识别模型;

        (如果你想尝试生成自己的数据集可以参考我的另一篇博客:http://www.cnblogs.com/AdaminXie/p/8379749.html

        源码上传到了我的 GitHub: https://github.com/coneypo/ML_handwritten_number, 有问题可以留言或者联系我邮箱;

       得到不同样本量训练下,几种机器学习模型精度随样本的变化关系曲线:

    图 0  不同样本数目下的四种模型的测试精度( 数据集大小从 100 到 5800,间隔 100 )

    1. 开发环境

      python:  3.6.3

      import PIL, cv2, pandas, numpy, os, csv, random

      需要调用的 sklearn 库:

    1 from sklearn.linear_model import LogisticRegression     # 线性模型中的 Logistic 回归模型
    2 from sklearn.linear_model import SGDClassifier          # 线性模型中的随机梯度下降模型
    3 from sklearn.svm import LinearSVC                       # SVM 模型中的线性 SVC 模型
    4 from sklearn.neural_network import MLPClassifier        # 神经网络模型中的多层网络模型

    2.整体设计思路

    图 1 整体的框架设计

      工程的目的,是想利用机器学习模型去训练识别生成的随机验证码图像(单个数字 1-9 ),通过以下三个步骤实现:

        1. 生成手写体数据集 

        2. 提取特征向量写入 CSV

        3. sklearn 模型训练和测试    

       

    图 2 整体的设计流程

       

    3. 编程过程

    3.1 生成多张单个验证码图像 ( generate_folders.py, generate_handwritten_numbers.py )

              

    图 3 生成的多张单个验证码图像

       

      手写体数据集的生成在我的另一篇博客详细介绍:( Link:http://www.cnblogs.com/AdaminXie/p/8379749.html 

      思路就是 random 随机生成数字 1-9,然后利用PIL的画笔工具进行画图,对图像进行扭曲,然后根据随机数的真实标记 1-9,保存到对应文件夹内,用标记+序号命名。

    1 draw = ImageDraw.Draw(im)  # 画笔工具

      

    3.2 提取特征向量写入 CSV ( get_features.py )

      这一步是提取图像中的特征。生成的单个图像是 30*30 即 900 个像素点的;

      为了降低维度,没有选择 900 个像素点每点的灰度作为输入,而是选取了 30 行每行的黑点数,和 30 列每列的黑点数作为输入,这样降到了 60 维。

       

    (a) 提取 900 维特征

    (b) 提取 60 维特征

    图 4 提取图像特征

      特征的提取也比较简单,逐行逐列计算然后计数求和:

     1     def get_feature(img):
     2         # 提取特征
     3         # 30*30的图像,
     4 
     5         width, height = img.size
     6 
     7         global pixel_cnt_list
     8         pixel_cnt_list=[]
     9 
    10         height = 30
    11         for y in range(height):
    12             pixel_cnt_x = 0
    13             for x in range(width):
    14                 # print(img.getpixel((x,y)))
    15                 if img.getpixel((x, y)) == 0:  # 黑点
    16                     pixel_cnt_x += 1
    17 
    18             pixel_cnt_list.append(pixel_cnt_x)
    19 
    20         for x in range(width):
    21             pixel_cnt_y = 0
    22             for y in range(height):
    23                 if img.getpixel((x, y)) == 0:  # 黑点
    24                     pixel_cnt_y += 1
    25 
    26             pixel_cnt_list.append(pixel_cnt_y)
    27 
    28         return pixel_cnt_list

        

      所以我们接下来需要做的工作是,遍历访问文件夹 num_1-9 中的所有图像文件,进行特征提取,然后写入 CSV 文件中:

     1   with open(path_csv+"tmp.csv", "w", newline="") as csvfile:
     2         writer = csv.writer(csvfile)
     3         # 访问文件夹 1-9
     4         for i in range(1, 10):
     5             num_list = os.listdir(path_images + "num_" + str(i))
     6             print(path_images + "num_" + str(i))
     7             print("num_list:", num_list)
     8             # 读到图像文件
     9             if os.path.isdir(path_images + "num_" + str(i)):
    10                 print("样本个数:", len(num_list))
    11                 sum_images = sum_images + len(num_list)
    12 
    13                 # Travsel every single image to generate the features
    14                 for j in range(0, (len(num_list))):
    15 
    16                     # 处理读取单个图像文件提取特征
    17                     img = Image.open(path_images + "num_" + str(i)+"/" + num_list[j])
    18                     get_features_single(img)
    19                     pixel_cnt_list.append(num_list[j][0])
    20 
    21                     # 写入CSV
    22                     writer.writerow(pixel_cnt_list)

       

      图 5 提取出来的 CSV 文件(前 60 列为输入特征,第 61 列为输出标记)

     

    3.3 sklearn 模型训练和测试 ( ml_ana.py, test_single_images.py )

      之前的准备工作都做完之后,我们生成了存放着 60 维输入特征和 1 维输出标记的 61 列的 CSV 文件;

      然后就可以利用这些数据,交给 sklearn 的机器学习模型进行建模处理。

    3.3.1 特征数据加工

      第一步需要对 CSV 文件中的数据进行提取,利用 pd.read_csv 进行读取。写入 CSV 时,前 60 列为 60 维的特征向量,第 61 列为输出标记 1-9;

      利用前面已经提取好的特征 CSV;

     1 # 从 CSV 中读取数据
     2 def pre_data():
     3     # CSV61维表头名
     4     column_names = []
     5 
     6     for i in range(0, 60):
     7         column_names.append("feature_" + str(i))
     8     column_names.append("true_number")
     9 
    10     # 读取csv
    11     path_csv = "../data/data_csvs/"
    12     data = pd.read_csv(path_csv + "data_10000.csv", names=column_names)
    13 
    14     # 提取数据集
    15     global X_train, X_test, y_train, y_test
    16     X_train, X_test, y_train, y_test = train_test_split(
    17         data[column_names[0:60]],
    18         data[column_names[60]],
    19         test_size=0.25,  # 75% for 训练,25% for 测试
    20         random_state=33
    21         )

      利用sklearn库的 train_test_split 函数 将数据进行分割,

        得到训练集数据:X_train, y_train

        得到测试集数据:X_test, y_test

    3.3.2 模型训练和测试

      经过前面一系列的准备工作做完,这里正式开始使用 sklearn 的机器学习模型建模;

      调用 sklearn 利用训练数据对模型进行训练,然后利用测试数据进行性能测试,并且保存模型到本地 ( "/data/data_models/model_xxx.m");

      ml_ana.py:

      1 # created at 2018-01-29
      2 # updated at 2018-09-28
      3 
      4 # Author:   coneypo
      5 # Blog:     http://www.cnblogs.com/AdaminXie
      6 # GitHub:   https://github.com/coneypo/ML_handwritten_number
      7 
      8 
      9 from sklearn.model_selection import train_test_split
     10 import pandas as pd
     11 
     12 from sklearn.preprocessing import StandardScaler     # 标准化
     13 
     14 # 调用模型
     15 from sklearn.linear_model import LogisticRegression  # 线性模型中的 Logistic 回归模型
     16 from sklearn.svm import LinearSVC                    # SVM 模型中的线性 SVC 模型
     17 from sklearn.neural_network import MLPClassifier     # 神经网络模型中的多层网络模型
     18 from sklearn.linear_model import SGDClassifier       # 线性模型中的随机梯度下降模型
     19 
     20 # 保存模型
     21 from sklearn.externals import joblib
     22 
     23 
     24 # 从 CSV 中读取数据
     25 def pre_data():
     26     # CSV61维表头名
     27     column_names = []
     28 
     29     for i in range(0, 60):
     30         column_names.append("feature_" + str(i))
     31     column_names.append("true_number")
     32 
     33     # 读取csv
     34     path_csv = "../data/data_csvs/"
     35     data = pd.read_csv(path_csv + "data_10000.csv", names=column_names)
     36 
     37     # 提取数据集
     38     global X_train, X_test, y_train, y_test
     39     X_train, X_test, y_train, y_test = train_test_split(
     40         data[column_names[0:60]],
     41         data[column_names[60]],
     42         test_size=0.25,  # 75% for 训练,25% for 测试
     43         random_state=33
     44         )
     45 
     46 
     47 path_saved_models = "../data/data_models/"
     48 
     49 
     50 # LR, logistic regression, 逻辑斯特回归分类(线性模型)
     51 def way_LR():
     52     X_train_LR = X_train
     53     y_train_LR = y_train
     54 
     55     X_test_LR = X_test
     56     y_test_LR = y_test
     57 
     58     # 数据预加工
     59     # ss_LR = StandardScaler()
     60     # X_train_LR = ss_LR.fit_transform(X_train_LR)
     61     # X_test_LR = ss_LR.transform(X_test_LR)
     62 
     63     # 初始化LogisticRegression
     64     LR = LogisticRegression()
     65 
     66     # 调用LogisticRegression中的fit()来训练模型参数
     67     LR.fit(X_train_LR, y_train_LR)
     68 
     69     # 使用训练好的模型lr对X_test进行预测
     70     # 结果储存在y_predict_LR中
     71     global y_predict_LR
     72     y_predict_LR = LR.predict(X_test_LR)
     73 
     74     # 评分函数
     75     global score_LR
     76     score_LR = LR.score(X_test_LR, y_test_LR)
     77     print("The accurary of LR:", '	', score_LR)
     78 
     79     # 保存模型
     80     joblib.dump(LR, path_saved_models + "model_LR.m")
     81 
     82     return LR
     83 
     84 
     85 # 多层感知机分类(神经网络)
     86 def way_MLPC():
     87     X_train_MLPC = X_train
     88     y_train_MLPC = y_train
     89 
     90     X_test_MLPC = X_test
     91     y_test_MLPC = y_test
     92 
     93     # ss_MLPC = StandardScaler()
     94     # X_train_MLPC = ss_MLPC.fit_transform(X_train_MLPC)
     95     # X_test_MLPC = ss_MLPC.transform(X_test_MLPC)
     96 
     97     MLPC = MLPClassifier(hidden_layer_sizes=(13, 13, 13), max_iter=500)
     98     MLPC.fit(X_train_MLPC, y_train_MLPC)
     99 
    100     global y_predict_MLPC
    101     y_predict_MLPC = MLPC.predict(X_test_MLPC)
    102 
    103     global score_MLPC
    104     score_MLPC = MLPC.score(X_test_MLPC, y_test_MLPC)
    105     print("The accurary of MLPC:", '	', score_MLPC)
    106 
    107     # 保存模型
    108     joblib.dump(MLPC, path_saved_models + "model_MLPC.m")
    109 
    110     return MLPC
    111 
    112 
    113 # Linear SVC, Linear Supported Vector Classifier, 线性支持向量分类(SVM支持向量机)
    114 def way_LSVC():
    115     X_train_LSVC = X_train
    116     y_train_LSVC = y_train
    117 
    118     X_test_LSVC = X_test
    119     y_test_LSVC = y_test
    120 
    121     # Standard Scaler
    122     # ss_LSVC = StandardScaler()
    123     # X_train_LSVC = ss_LSVC.fit_transform(X_train_LSVC)
    124     # X_test_LSVC = ss_LSVC.transform(X_test_LSVC)
    125 
    126     LSVC = LinearSVC()
    127     LSVC.fit(X_train_LSVC, y_train_LSVC)
    128 
    129     global y_predict_LSVC
    130     y_predict_LSVC = LSVC.predict(X_test_LSVC)
    131 
    132     global score_LSVC
    133     score_LSVC = LSVC.score(X_test_LSVC, y_test_LSVC)
    134     print("The accurary of LSVC:", '	', score_LSVC)
    135 
    136     # 保存模型
    137     joblib.dump(LSVC, path_saved_models + "model_LSVC.m")
    138 
    139     return LSVC
    140 
    141 
    142 # SGDC, stochastic gradient decent 随机梯度下降法求解(线性模型)
    143 def way_SGDC():
    144     X_train_SGDC = X_train
    145     y_train_SGDC = y_train
    146 
    147     X_test_SGDC = X_test
    148     y_test_SGDC = y_test
    149 
    150     # ss_SGDC = StandardScaler()
    151     # X_train_SGDC = ss_SGDC.fit_transform(X_train_SGDC)
    152     # X_test_SGDC = ss_SGDC.transform(X_test_SGDC)
    153 
    154     SGDC = SGDClassifier(max_iter=5)
    155 
    156     SGDC.fit(X_train_SGDC, y_train_SGDC)
    157 
    158     global y_predict_SGDC
    159     y_predict_SGDC = SGDC.predict(X_test_SGDC)
    160 
    161     global score_SGDC
    162     score_SGDC = SGDC.score(X_test_SGDC, y_test_SGDC)
    163     print("The accurary of SGDC:", '	', score_SGDC)
    164 
    165     # 保存模型
    166     joblib.dump(SGDC, path_saved_models + "model_SGDC.m")
    167 
    168     return SGDC
    169 
    170 
    171 pre_data()
    172 way_LR()
    173 way_LSVC()
    174 way_MLPC()
    175 way_SGDC()

    3.3.3 测试 ( test_single_images.py ) 

      对于一张手写体数字,提取特征然后利用保存的模型进行预测;

     1 # created at 2018-01-29
     2 # updated at 2018-09-28
     3 
     4 # Author:   coneypo
     5 # Blog:     http://www.cnblogs.com/AdaminXie
     6 # GitHub:   https://github.com/coneypo/ML_handwritten_number
     7 
     8 # 利用保存到本地的训练好的模型,来检测单张 image 的标记
     9 
    10 from sklearn.externals import joblib
    11 from PIL import Image
    12 
    13 img = Image.open("../test/test_1.png")
    14 
    15 # Get features
    16 from generate_datebase import get_features
    17 features_test_png = get_features.get_features_single(img)
    18 
    19 path_saved_models = "../data/data_models/"
    20 
    21 # LR
    22 LR = joblib.load(path_saved_models + "model_LR.m")
    23 predict_LR = LR.predict([features_test_png])
    24 print("LR:", predict_LR[0])
    25 
    26 # LSVC
    27 LSVC = joblib.load(path_saved_models + "model_LSVC.m")
    28 predict_LSVC = LSVC.predict([features_test_png])
    29 print("LSVC:", predict_LSVC[0])
    30 
    31 # MLPC
    32 MLPC = joblib.load(path_saved_models + "model_MLPC.m")
    33 predict_MLPC = MLPC.predict([features_test_png])
    34 print("MLPC:", predict_MLPC[0])
    35 
    36 # SGDC
    37 SGDC = joblib.load(path_saved_models + "model_SGDC.m")
    38 predict_SGDC = SGDC.predict([features_test_png])
    39 print("SGDC:", predict_SGDC[0])

    3.3.4 绘制样本数-精度图像

      可以绘图来更加直观的精度:

     1 # 2018-01-29
     2 # By TimeStamp
     3 # cnblogs: http://www.cnblogs.com/AdaminXie/
     4 # plot_from_csv.py
     5 # 从存放样本数-精度的CSV中读取数据,绘制图形
     6 
     7 
     8 import numpy as np
     9 import matplotlib.pyplot as plt
    10 import pandas as pd
    11 
    12 # CSV路径
    13 path_csv = "F:/***/P_ML_handwritten_number/data/score_csv/"
    14 
    15 # 存储x轴坐标
    16 x_array = []
    17 
    18 # 存储精度
    19 LR_score_arr = []
    20 LSVC_score_arr = []
    21 MLPC_score_arr = []
    22 SGDC_score_arr = []
    23 
    24 # 读取CSV数据
    25 column_names = ["samples", "acc_LR", "acc_LSVC", "acc_MLPC", "acc_SGDC"]
    26 rd_csv = pd.read_csv(path_csv + "score_100to5800.csv", names=column_names)
    27 
    28 print(rd_csv.shape)
    29 
    30 for i in range(len(rd_csv)):
    31     x_array.append(float(rd_csv["samples"][i]))
    32     LR_score_arr.append(float(rd_csv["acc_LR"][i]))
    33     LSVC_score_arr.append(float(rd_csv["acc_LSVC"][i]))
    34     MLPC_score_arr.append(float(rd_csv["acc_MLPC"][i]))
    35     SGDC_score_arr.append(float(rd_csv["acc_SGDC"][i]))
    36 
    37 ################ 3次线性拟合 ################
    38 xray = np.array(x_array)
    39 y_LR = np.array(LR_score_arr)
    40 y_LSVC = np.array(LSVC_score_arr)
    41 y_MLPC = np.array(MLPC_score_arr)
    42 y_SGDC = np.array(SGDC_score_arr)
    43 
    44 z1 = np.polyfit(xray, y_LR, 5)
    45 z2 = np.polyfit(xray, y_LSVC, 5)
    46 z3 = np.polyfit(xray, y_MLPC, 5)
    47 z4 = np.polyfit(xray, y_SGDC, 5)
    48 
    49 p1 = np.poly1d(z1)
    50 p2 = np.poly1d(z2)
    51 p3 = np.poly1d(z3)
    52 p4 = np.poly1d(z4)
    53 
    54 y_LR_vals = p1(xray)
    55 y_LSVC_vals = p2(xray)
    56 y_MLPC_vals = p3(xray)
    57 y_SGDC_vals = p4(xray)
    58 #################################
    59 
    60 # 标明线条说明
    61 plt.annotate("— LR", xy=(5030, 0.34), color='b', size=12)
    62 plt.annotate("— LSVC", xy=(5030, 0.26), color='r', size=12)
    63 plt.annotate("— MLPC", xy=(5030, 0.18), color='g', size=12)
    64 plt.annotate("— SGDC", xy=(5030, 0.10), color='black', size=12)
    65 
    66 # 画拟合曲线
    67 plt.plot(xray, y_LR_vals, color='b')
    68 plt.plot(xray, y_LSVC_vals, color='r')
    69 plt.plot(xray, y_MLPC_vals, color='g')
    70 plt.plot(xray, y_SGDC_vals, color='black')
    71 
    72 # 画离散点
    73 plt.plot(xray, y_LR, color='b', linestyle='None', marker='.', label='y_test', linewidth=100)
    74 plt.plot(xray, y_LSVC, color='r', linestyle='None', marker='.', label='y_test', linewidth=0.01)
    75 plt.plot(xray, y_MLPC, color='g', linestyle='None', marker='.', label='y_test', linewidth=0.01)
    76 plt.plot(xray, y_SGDC, color='black', linestyle='None', marker='.', label='y_test', linewidth=0.01)
    77 
    78 # 绘制y=1参考线
    79 plt.plot([0, 6000], [1, 1], 'k--')
    80 
    81 # 设置y轴坐标范围
    82 plt.ylim(0, 1.1)
    83 
    84 # 标明xy轴
    85 plt.xlabel('samples')
    86 plt.ylabel('accuracy')
    87 
    88 plt.show()

    3.3.4 测试结果

      在样本数 sample_num = 50 的情况下,训练 75% 数据,用 25% 的数据即 13 个样本进行测试;

      几种模型的测试结果如 图 6 所示,可见除了 SVM 达到 84.7% 的精度之外,其他都在 60-70% 左右;

      但是因为只有 50 个样本点,小样本的情况下测试精度的偶然性误差比较大。

       

    图 6 手写体识别的性能分析( 在样本数为 50 的情况下 )

      增加样本数到 100,即生成了 100 张单个手写体图像,75 张用来训练,25 张用来测试;

      25 张的测试结果 图 6 所示,几种模型的测试精度都达到了 90% 左右。

    图 7  手写体识别的性能分析(在样本数为 100 的情况下)

    图 8 不同样本数目下的四种模型的测试精度( 5次拟合 )

    # 如果对您有帮助,欢迎在 GitHub 上 Star 支持我:          https://github.com/coneypo/ML_handwritten_number

    # 请尊重他人劳动成果,转载或者使用源码请注明出处: http://www.cnblogs.com/AdaminXie

    # 如有问题联系邮箱 :coneypo@foxmail.com

  • 相关阅读:
    2019-2020-1 20199325《Linux内核原理与分析》第十二周作业
    2019-2020-1 20199325《Linux内核原理与分析》第十一周作业
    2019-2020-1 20199325《Linux内核原理与分析》第九周作业
    2019-2020-1 20199325《Linux内核原理与分析》第八周作业
    20199328 2019-2020-2 《网络攻防实践》第3周作业
    20199328 2019-2020-2 《网络攻防实践》第2周作业
    20199328 2019-2020-2 《网络攻防实践》第1周作业
    20199328《网络攻防实践》假期作业
    2019-2020-1 20199328《Linux内核原理与分析》第十二周作业
    2019-2020-1 20199328《Linux内核原理与分析》第十一周作业
  • 原文地址:https://www.cnblogs.com/AdaminXie/p/8249858.html
Copyright © 2011-2022 走看看