欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python——numpy实现简单BP神经网络识别手写数字

程序员文章站 2022-05-21 23:53:37
...

Python——numpy实现简单BP神经网络识别手写数字

直接上代码:

1.核心代码:神经网络以及BP算法
import numpy as np
from tqdm import trange	# 替换range()可实现动态进度条,可忽略


def sigmoid(x): # **函数采用Sigmoid
    return 1 / (1 + np.exp(-x))


def sigmoid_derivative(x):	# Sigmoid的导数
    return sigmoid(x) * (1 - sigmoid(x))


class NeuralNetwork:	# 神经网络
    def __init__(self, layers):	# layers为神经元个数列表
        self.activation = sigmoid	# **函数
        self.activation_deriv = sigmoid_derivative	# **函数导数
        self.weights = []	# 权重列表
        self.bias = []	# 偏置列表
        for i in range(1, len(layers)):	# 正态分布初始化
            self.weights.append(np.random.randn(layers[i-1], layers[i]))
            self.bias.append(np.random.randn(layers[i]))

    def fit(self, x, y, learning_rate=0.2, epochs=3):	# 反向传播算法
        x = np.atleast_2d(x)
        n = len(y)	# 样本数
        p = max(n, epochs)	# 样本过少时根据epochs减半学习率
        y = np.array(y)

        for k in trange(epochs * n):	# 带进度条的训练过程
            if (k+1) % p == 0:
                learning_rate *= 0.5	# 每训练完一代样本减半学习率
            a = [x[k % n]]	# 保存各层**值的列表
            # 正向传播开始
            for lay in range(len(self.weights)):
                a.append(self.activation(np.dot(a[lay], self.weights[lay]) + self.bias[lay]))
            # 反向传播开始
            label = np.zeros(a[-1].shape)
            label[y[k % n]] = 1	# 根据类号生成标签
            error = label - a[-1]	# 误差值
            deltas = [error * self.activation_deriv(a[-1])]	# 保存各层误差值的列表

            layer_num = len(a) - 2	# 导数第二层开始
            for j in range(layer_num, 0, -1):
                deltas.append(deltas[-1].dot(self.weights[j].T) * self.activation_deriv(a[j]))	# 误差的反向传播
            deltas.reverse()
            for i in range(len(self.weights)):	# 正向更新权值
                layer = np.atleast_2d(a[i])
                delta = np.atleast_2d(deltas[i])
                self.weights[i] += learning_rate * layer.T.dot(delta)
                self.bias[i] += learning_rate * deltas[i]

    def predict(self, x):	# 预测
        a = np.array(x, dtype=np.float)
        for lay in range(0, len(self.weights)):	# 正向传播
            a = self.activation(np.dot(a, self.weights[lay]) + self.bias[lay])
        a = list(100 * a/sum(a))	# 改为百分比显示
        i = a.index(max(a))	# 预测值
        per = []	# 各类的置信程度
        for num in a:
            per.append(str(round(num, 2))+'%')
        return i, per

2.训练
from NeuralNetwork import NeuralNetwork
import numpy as np
import pickle
import csv


def train():
    file_name = 'data/train.csv'	# 数据集为42000张带标签的28x28手写数字图像
    y = []
    x = []
    y_t = []
    x_t = []
    with open(file_name, 'r') as f:
        reader = csv.reader(f)
        header_row = next(reader)
        print(header_row)
        for row in reader:
            if np.random.random() < 0.8:	# 大约80%的数据用于训练
                y.append(int(row[0]))
                x.append(list(map(int, row[1:])))
            else:
                y_t.append(int(row[0]))
                x_t.append(list(map(int, row[1:])))
    len_train = len(y)
    len_test = len(y_t)
    print('训练集大小%d,测试集大小%d' % (len_train, len_test))
    x = np.array(x)
    y = np.array(y)
    nn = NeuralNetwork([784, 784, 10])	# 神经网络各层神经元个数
    nn.fit(x, y)
    file = open('NN.txt', 'wb')
    pickle.dump(nn, file)
    count = 0
    for i in range(len_test):
        p, _ = nn.predict(x_t[i])
        if p == y_t[i]:
            count += 1
    print('模型识别正确率:', count/len_test)


def mini_test():	# 小型测试,验证神经网络能正常运行
    x = [[0, 0], [0, 1], [1, 0], [1, 1]]
    y = [0, 1, 2, 3]
    nn = NeuralNetwork([2, 4, 16, 4])
    nn.fit(x, y, epochs=10000)
    for i in x:
        print(nn.predict(i))


# mini_test()
train()

运行结果:

  • [784,784,10][784,784,10]
    Python——numpy实现简单BP神经网络识别手写数字
  • [784,1568,10][784,1568,10]
    Python——numpy实现简单BP神经网络识别手写数字
  • [784,784,784,10][784,784,784,10]
    Python——numpy实现简单BP神经网络识别手写数字
  • [784,1568,1568,10][784,1568,1568,10]
    Python——numpy实现简单BP神经网络识别手写数字
    可以发现提高神经网络规模几乎无法提高正确率,但如果加入正则化的话应该能进一步提高正确率。
3.测试
import csv
import pickle
import numpy as np
from matplotlib import pyplot as plt


def diplay_test():	# 读取测试集,预测,画图
    file_name = 'data/test.csv'
    file = open('NN.txt', 'rb')
    nn = pickle.load(file)
    with open(file_name, 'r') as f:
        reader = csv.reader(f)
        header_row = next(reader)
        print(header_row)
        i = 0
        for row in reader:
            i += 1
            img = np.array(row, dtype=np.uint8)
            img = img.reshape(28, 28)
            plt.imshow(img, cmap='gray')
            pre, lst = nn.predict(row)
            plt.title(str(pre), fontsize=24)
            plt.axis('off')
            plt.savefig('img/img' + str(i) + '.png')


diplay_test()

运行结果:
Python——numpy实现简单BP神经网络识别手写数字
Python——numpy实现简单BP神经网络识别手写数字