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

识别结果

数据集:

链接:https://pan.baidu.com/s/1XzexzXGYhUdFpM4UaIb3yA
提取码:9wjm

代码:

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] [784,784,10]
    [784,784,10]
  • [ 784 , 1568 , 10 ] [784,1568,10] [784,1568,10]
    [784,1568,10]
  • [ 784 , 784 , 784 , 10 ] [784,784,784,10] [784,784,784,10]
    [784,784,784,10]
  • [ 784 , 1568 , 1568 , 10 ] [784,1568,1568,10] [784,1568,1568,10]
    [784,1568,1568,10]
    可以发现提高神经网络规模几乎无法提高正确率,但如果加入正则化的话应该能进一步提高正确率。
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()

运行结果:
图像
部分图片以及识别结果

  • 28
    点赞
  • 125
    收藏
    觉得还不错? 一键收藏
  • 86
    评论
评论 86
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值