Python BP神经网络实现手写体识别

Posted 今晚看星星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python BP神经网络实现手写体识别相关的知识,希望对你有一定的参考价值。

1 BP公式的推导

  1. 神经网络的模型
    在这里插入图片描述

1.1 激活函数

在这里插入图片描述

1.1.1 Sigmod函数导数

在这里插入图片描述

  • 注意:Sigmod函数与矩阵相乘的时候是对应位置相乘,对应于numpy.mutiply()

1.2 损失函数

在这里插入图片描述

1.3 前向传播

  • N为神经网络的输出
    在这里插入图片描述

1.3 计算误差反向传播

  • 矩阵求导时,系数矩阵为导数时,要对其进行求逆
    在这里插入图片描述

1.3.1 误差为方差时,输出层误差的计算

在这里插入图片描述

1.3.2 误差为方差时,隐层层误差的计算

在这里插入图片描述

1.4 调整参数

在这里插入图片描述

  • 其中误差的计算,E对输出Z的偏导即为误差

在这里插入图片描述

在这里插入图片描述

2 神经网络设计

  • 根据BP公式设计神经网络
import numpy as np


class neuron():
    def __init__(self, input_len, output_len, is_output_layer, learning_rate):  # 构造函数,随机生成网络中的权值与偏置
        self.RANDOM_INT = 10
        self.is_output_layer = is_output_layer
        self.w = np.random.randint(-self.RANDOM_INT, self.RANDOM_INT, (input_len, output_len)).astype(np.float64) / 10
        self.b = np.random.randint(-self.RANDOM_INT, self.RANDOM_INT, (1, output_len)).astype(np.float64) / 10
        self.output = 0
        self.error = 0
        self.learning_rate = learning_rate
        self.input_data = 0

    def active_function(self, input_z):
        return 1 / (1 + np.exp(-input_z))

    def forward_propagation(self, input_data):
        self.input_data = input_data
        self.output = self.active_function(np.dot(self.input_data, self.w).astype(np.float64) - self.b)
        return self.output

    def get_output(self, input_data):
        return self.active_function(np.dot(input_data, self.w).astype(np.float64) - self.b)
        pass

    def back_propagation(self, error, next_w):
        if self.is_output_layer:
            # 是输出层, sigmod函数与矩阵点成, 采用方差形式的误差
            self.error = np.multiply((error - self.output), np.multiply(self.output, 1 - self.output))
            return self.error
        else:
            # 该层不是输出层,则从下一层的误差中,计算本层的误差
            self.error = np.multiply(np.multiply(self.output, 1 - self.output), np.dot(error, np.transpose(next_w)))
            return self.error
        pass

    def adjust_param(self, error):
        delta_w = np.dot(np.transpose(self.input_data), error)
        delta_b = error
        self.w = self.w + self.learning_rate * delta_w
        self.b = self.b - self.learning_rate * delta_b
        pass

3 使用OpenCV 分割图片

3.1 整张图片资源

在这里插入图片描述

  • 对图片进行分割
import cv2
import os

def cut_image(src_image, dst_src, row_size, col_size):
    img_1 = cv2.imread(src_image)
    img = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
    row_num = img.shape[0] // row_size
    col_num = img.shape[1] // col_size
    print(row_num, col_num)

    # 保存到不同的文件夹
    file_name = 0
    file_count = 0

    for i in range(row_num):
        offset_row = i * row_size
        if i % 5 == 0 and i != 0:
            file_name += 1
            file_count = 0
        path = dst_src + "\\\\" + str(file_name)

        if not os.path.exists(path):
            os.makedirs(path)

        for j in range(col_num):
            offset_col = j * col_size
            filepath = path + "\\\\" + str(file_count) + ".jpg"
            file_count += 1
            im = img[offset_row:offset_row + row_size - 1, offset_col:offset_col + col_size - 1]
            '''
            function: cv2.imwrite('F:/images',image,[int(cv2.IMWRITE_JPEG_QUALITY),5])
            - 三个参数分别对应保存的路径及文件名、图像矩阵、指定格式
            - (对于JPEG,其表示的是图像的质量,用0-100的整数表示,默认为95。 
            - 注意,cv2.IMWRITE_JPEG_QUALITY类型为Long,必须转换成int;对于PNG,第三个参数表示的是压缩级别。
            - cv2.IMWRITE_PNG_COMPRESSION,
            - 从0到9,压缩级别越高,图像尺寸越小。这个是可选参数)
            '''
            cv2.imwrite(filepath, im, [int(cv2.IMWRITE_JPEG_QUALITY), 95])


if __name__ == '__main__':
    cut_image("test.png", "images_data", 20, 20)     # 块大小为20 * images_test2
    pass

分开的图片资源

import cv2
import os


def cut_image(image_src, row_size, column_size, output_src):
    image = cv2.imread(image_src)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 转化为2值灰度图像
    row_num = image.shape[0] // row_size  # 图像的行数 = 图像行像素 / 子图行像素
    col_num = image.shape[1] // column_size  # 图像的列数 = 图像列像素 / 子图列像素

    print("图像的行数为: ", row_num, "图像的列数为: ", col_num)
    file_num = 0
    for i in range(row_num):

        offset_row = i * row_size  # 图像的宽度
        path = output_src

        if not os.path.exists(path):
            os.makedirs(path)

        for j in range(col_num):
            offset_col = j * column_size
            file_path = path + "\\\\" + str(file_num) + ".jpg"
            file_num += 1

            '''
            按照像素的位置取出图像
            '''
            im = image[offset_row: offset_row + row_size - 1, offset_col: offset_col + column_size - 1]
            # 转化为灰度值, 进行保存

            '''
            function: cv2.imwrite(F:/images',image,[int(cv2.IMWRITE_JPEG_QUALITY),5])
            - 三个参数分别对应保存的路径及文件名、图像矩阵、指定格式
            - (对于JPEG,其表示的是图像的质量,用0-100的整数表示,默认为95。 
            - 注意,cv2.IMWRITE_JPEG_QUALITY类型为Long,必须转换成int;对于PNG,第三个参数表示的是压缩级别。
            - cv2.IMWRITE_PNG_COMPRESSION,
            - 从0到9,压缩级别越高,图像尺寸越小。这个是可选参数)
            '''
            cv2.imwrite(file_path, im, [int(cv2.IMWRITE_JPEG_QUALITY), 95])  #


if __name__ == '__main__':
    cut_image("mnist_test0.jpg", 28, 28, "images/images_test0")
    cut_image("mnist_test1.jpg", 28, 28, "images/images_test1")
    cut_image("mnist_test2.jpg", 28, 28, "images/images_test2")
    cut_image("mnist_test3.jpg", 28, 28, "images/images_test3")
    cut_image("mnist_test4.jpg", 28, 28, "images/images_test4")
    cut_image("mnist_test5.jpg", 28, 28, "images/images_test5")
    cut_image("mnist_test6.jpg", 28, 28, "images/images_test6")
    cut_image("mnist_test7.jpg", 28, 28, "images/images_test7")
    cut_image("mnist_test8.jpg", 28, 28, "images/images_test8")
    cut_image("mnist_test9.jpg", 28, 28, "images/images_test9")

4 进行训练

4.1 读取图片数据,并归一化

  • 训练采用灰度图片,读取图片的数据,转化为灰度数据,放入到神经网络中

  • 根据图片所在文件夹的顺序,对图片添加分类
    在这里插入图片描述

  • 返回一维的图像灰度矩阵与标签矩阵

import cv2


def select_label(count):
    label = []
    for i in range(10):
        if i == count:
            label.append(1)
        else:
            label.append(0)
    return label
    pass

def get_array_image(file, count, num):

    url = file + "/images_train" + str(count) + "/" + str(num) + ".jpg"
    # print("url: ", url)
    # images_test1. 读入图片数据
    src_image = cv2.imread(url)
    new_image = cv2.resize(src_image, (30, 30))  # 缺省,双线性插值
    # 3. 转化为灰度图
    gray_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2GRAY)

    data_list = []
    for i in range(len(gray_image)):

        data_list.extend(gray_image[i])

    label_list = select_label(count)

    return data_list, label_list
    pass


def get_data_label(file_name, file_num, num_start, num_end):
    data_set = []
    label_set = []

    # 读取多个文件夹
    for i in range(file_num):
        for j in range(num_start, num_end):
            data_row, label_row = get_array_image(file_name, i, j)
            data_set.append(data_row)
            label_set.append(label_row)
    return data_set, label_set
    pass

if __name__ == '__main__':
    # data, la = get_data_label("train_images", 10, images_test0, 10)
    data_row, label_row = get_array_image("train_set_images_cut/train_images/images", 0, 0)
    print(data_row)
    # print(data)
    pass

5.2 对神经网络进行训练,训练好的参数放入Excel文件中

import numpy as np
from neuron import neuron
import data_util
import excel_util
import data_util2


# 激活函数
def sigmoid(value):
    return 1 / (1 + np.exp(-value))
    pass


def get_e(data_test, label, neurons):
    data_set_len = len(data_test)
    tmp = 0
    for i in range(data_set_len):
        # images_test1. 前向传播的输出
        output = np.mat(data_test[i]).astype(np.float64)

        for neuron_item in neurons:
            output = neuron_item.get_output(output)

        # images_test2. 计算误差
        tmp += np.multiply((output - label[i]), (output - label[i]).T)  # .T 为matrix的转置
    return tmp / data_set_len
    pass


# 进行预测
def predict_test(data_test, label_test, neurons):
    right_cnt = 0
    # 测试的个数
    test_set_len = len(data_test)
    # 对数据集的每一个输入进行测试
    for i in range(test_set_len):
        # # 传播
        output = np.mat(data_test[i]).astype(np.float64)

        for neuron_item in neurons:
            output = neuron_item.get_output(output)

        predict_class = 1000
        for row in range(output.shape[0]):
            for col in range(output.shape[1]):
                if output[row, col] >= 0.5:
                    predict_class = col
                    break

        label_class = 1000
        for index in range(len(label_test[i])):
            if label_test[i][index] == 1:
                label_class = index
                break
        print("第", i + 1, "次预测:predict: ", output, ", \\n真实: ", label_test[i])
        print("第", i + 1, "次预测:predict: ", predict_class, ", \\n真实: ", label_class)
        if predict_class == label_class != 1000:
            right_cnt += 1
    return right_cnt / test_set_len


def plot_e(a_list):
    import matplotlib.pyplot as plt
    x = np.arange(1, len(a_list) + 1)
    y = np.array(a_list)  # 转化为向量
    print("最后误差:", y[-1])
    # 绘制
    plt.plot(x, y)
    plt.xlabel("practiseTime")
    plt.ylabel("variance")
    plt.show()
    pass


def main():
    # 设置训练图片的范围
    file_num = 10
    train_num_start = 0
    train_num_end = 300
    data_set, label_set = data_util2.get_data_label("train_set_images_cut/train_images/images", file_num, train_num_start, train_num_end)

    # images_test1. 设置神经网络的参数
    input_len = len(data_set[0])
    hidden_one_len = 50
    output_len = len(label_set[0])

    # 构建三层神经网络
    hidden_one_layer = neuron(input_len, hidden_one_len, False, 0.01)
    output_layer = neuron(hidden_one_len, output_len, True, 0.01)

    # 进行一定次数的训练
    a_list = []
    for num in range(150):
        for j in range(len(data_set)):
            # 进行训练
            # images_test1. 前向传播
            hidden_one_output = hidden_one_layer.forward_propagation(np.mat(data_set[j]).astype(np.float64))
            output_one_later = output_layer.forward_propagation(hidden_one_output)

            # images_test2. 计算误差
            error_2 = output_layer.back_propagation(label_set[j], 1)
            error_1 = hidden_one_layer.back_propagation(error_2, output_layer.w)

            # 3. 反向传播
            output_layer.adjust_param(error_2)
            hidden_one_layer.adjust_param(error_1)

        # 计算方差误差,为数组中绝对值的和
        e = get_e(data_set, label_set, [hidden_one_layer, output_layer])
        e_value = 0
        for row in range(len(e)):
            for col in range(len(e[0])):
                e_value = e_value + e[row, col]
        a_list.append(abs(e_value))

    # 保存数据到txt文件
    excel_util.save_matrix2excel(hidden_one_layer.w, "excel_param/hidden_one_layer_w1.xlsx")
    excel_util.save_matrix2excel(hidden_one_layer.b, "excel_param/hidden_one_layer_b1.xlsx")
    excel_util.save_matrix2excel(output_layer.w, "excel_param/output_layer_w1.xlsx")
    excel_util.save_matrix2excel(output_layer.b, "excel_param/output_layer_b1.xlsx")

    # 绘制方差的图像
    plot_e(a_list)

    # 设置测试图片的范围
    test_num_start = 100
    test_num_end = 120
    data_set_test, label_set_test = data_util2.get_data_label("train_set_images_cut/train_images/images", file_num, test_num_start, test_num_end)
    rate = predict_test(data_set_test, label_set_test, [hidden_one_layer, output_layer])
    print("预测的正确率为: ", rate * 100, "%")
    pass


if __name__ == '__main__':
    main()

6 进行预测

  • 读取Excel文件中网络的参数进行预测

6.1 存取excel工具

import pandas as pd
import numpy as np


def save_matrix2excel(mat, dst_path):<

以上是关于Python BP神经网络实现手写体识别的主要内容,如果未能解决你的问题,请参考以下文章

Java实现BP神经网络MNIST手写数字识别

基于BP神经网络的手写体数字识别matlab仿真实现

图像识别基于BP神经网络实现手写字母识别matlab源码

C#实现基于BP神经网络的中文手写识别算法

纯C#实现基于BP神经网络的中文手写识别算法

Java实现BP神经网络MNIST手写数字识别