反向传播(BP)网络的mnist实例(提升篇)

提高神经学习的学习效率

并行计算

可以使用GPU进行并行计算,以此提高学习效率。

梯度消失问题

梯度消失问题的存在使得学习效率会变慢,出现梯度消失的原因如下:
在这里插入图片描述
在这里插入图片描述
其中,对sigmoid函数求导的时候,在[-4, 4]的范围外会发生导数很小,接近于0的情况,进而导致学习的梯度消失。

改进的思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

归一化

因为数据度量的量纲可能不同,所以需要对数据进行归一化处理。
在这里插入图片描述
归一化的效果如图:
在这里插入图片描述

参数初始化问题

在这里插入图片描述
上图的参数初始化方法是业界比较认可的一种初始化方式。

参数初始化的代码如下:
def default_weight_initializer(self):
    # 初始化每层的偏置
    self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
    # 初始化每层的权重
    self.weights = [np.random.randn(y, x)/np.sqrt(x) for x, y in zip(self.sizes[:-1], self.sizes[1:])]

正则化

L1正则化:

在这里插入图片描述

L1正则化项的导数:

在这里插入图片描述

L2正则化:

在这里插入图片描述

L2正则化项的导数:

在这里插入图片描述
代码需要修改如下:

self.weights = [(1-eta*(lmbda/n))*w - (eta / len(mini_batch)) * nw for w, nw in zip(self.weights, nabla_w)]

学习率

在这里插入图片描述
在这里插入图片描述

交叉熵

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

交叉熵定义

在这里插入图片描述

交叉熵求导

在这里插入图片描述
在这里插入图片描述

交叉熵代码:
class CrossEntropyCost(object):
    '''
    a = np.array([[np.nan, np.inf],
                  [-np.nan, -np.inf]])
    np.nan_to_sum(a)

    array([[0.00000000e+000, 1.79769313e+308],
           [0.00000000e+000, -1.79769313e+308]])
    '''
    @staticmethod
    def fn(a, y):
        return np.sum(np.nan_to_num(-y * np.log(a) - (1-y) * np.log(1-a)))

    @staticmethod
    def delta(z, a, y):
        return (a-y)

模型的保存与加载

在这里插入图片描述

模型保存与加载代码如下:
# 保存模型
    def save(self, filename):
        data = {"sizes": self.sizes,
                "weights": [w.tolist() for w in self.weights],
                "biases": [b.tolist() for b in self.biases],
                "cost": str(self.cost.__name__)
                }
        f = open(filename, "w")
        json.dump(data, f)  # json把字典类型转换成字符串
        f.close()

# 加载模型
def load(filename):
    f = open(filename, "r")
    data = json.load(f)
    f.close()
    cost = getattr(sys.modules[__name__], data["cost"])
    net = Network(data["sizes"], cost=cost)
    net.weights = [np.array(w) for w in data["weights"]]
    net.biases = [np.array(b) for b in data["biases"]]
    return net

最后在MNIST数据集上训练改进的模型,同时加上准确率等度量方法。

完整代码如下:
#!/user/bin/env python3
# -*- coding: utf-8 -*-
import random
import json
import sys

import numpy as np

#定义神经网络结构
class QuadraticCost(object):
    @staticmethod
    def fn(a, y):
        return 0.5 * np.linalg.norm(a-y) ** 2
    
    @staticmethod
    def delta(z, a, y):
        return (a-y) * sigmoid_prime(z)

class CrossEntropyCost(object):
    '''
    a = np.array([[np.nan, np.inf],
                  [-np.nan, -np.inf]])
    np.nan_to_sum(a)

    array([[0.00000000e+000, 1.79769313e+308],
           [0.00000000e+000, -1.79769313e+308]])
    '''
    @staticmethod
    def fn(a, y):
        return np.sum(np.nan_to_num(-y * np.log(a) - (1-y) * np.log(1-a)))

    @staticmethod
    def delta(z, a, y):
        return (a-y)

class Network(object):
    def __init__(self, sizes, cost=CrossEntropyCost):
        # 网络层数
        self.num_layers = len(sizes)
        # 每层神经元的个数
        self.sizes = sizes
        # 初始化每层的偏置和权重
        self.default_weight_initializer()
        # 损失函数
        self.cost = cost

    def default_weight_initializer(self):
        # 初始化每层的偏置
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        # 初始化每层的权重
        self.weights = [np.random.randn(y, x)/np.sqrt(x) for x, y in zip(self.sizes[:-1], self.sizes[1:])]

    def large_weight_initializer(self):
        # 初始化每层的偏置
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        # 初始化每层的权重
        self.weights = [np.random.randn(y, x) for x, y in zip(self.sizes[:-1], self.sizes[1:])]

    def feedforward(self, a):
        for b, w in zip(self.biases, self.weights):
            a = sigmoid(np.dot(w, a) + b)
        return a

    # 梯度下降
    def SGD(self, training_data, epochs, mini_batch_size, eta, lmbda=0.0, test_data=None):
        if test_data:
            n_test = len(test_data)
        # 训练数据总个数
        n = len(training_data)

        # 开始训练,循环每一个epochs
        for j in range(epochs):  # 在python2.7中为xrange
            # 洗牌 打乱训练数据
            random.shuffle(training_data)

            # mini_batch
            mini_batches = [training_data[k:k+mini_batch_size] for k in range(0, n, mini_batch_size)]

            # 训练mini_batch
            for mini_batch in mini_batches:
                self.update_mini_batch(mini_batch, eta, lmbda, n)

            print("Epoch {0} complete".format(j))

            cost = self.total_cost(training_data, lmbda)
            print("Cost on training data: {}".format(cost))
            accuracy = self.accuracy(training_data, convert=True)
            print("Accuracy on training data: {} / {}".format(accuracy, n))

            if test_data:
                cost = self.total_cost(test_data, lmbda, convert=True)
                print("Cost on test data: {}".format(cost))
                accuracy = self.accuracy(test_data)
                print("Accuracy on test data: {} / {}".format(accuracy, len(test_data)))

    def update_mini_batch(self, mini_batch, eta, lmbda, n):
        # 保存每层偏导
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]

        # 训练一个mini_batch
        for x, y in mini_batch:
            delta_nabla_b, delta_nabla_w = self.update(x, y)

            # 保存一次训练网络中每层的偏导
            nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

        # 更新权重和偏置 Wn+1 = Wn - eta * nw
        self.weights = [(1-eta*(lmbda/n))*w - (eta / len(mini_batch)) * nw for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b - (eta / len(mini_batch)) * nb for b, nb in zip(self.biases, nabla_b)]

    # 前向传播
    def update(self, x, y):
        # 保存每层偏导
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        activation = x  # 保存的输入(训练数据)

        # 保存每一层的激励值a=sigmoid(z)
        activations = [x]

        # 保存每一层的z=wx+b
        zs = []
        # 前向传播
        for b, w in zip(self.biases, self.weights):
            # 计算每层的z
            z = np.dot(w, activation) + b

            # 保存每层的z
            zs.append(z)

            # 计算每层的a
            activation = sigmoid(z)

            # 保存每一层的a
            activations.append(activation)
        # 反向更新
        # 计算最后一层的误差
        # delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])
        delta = (self.cost).delta(zs[-1], activations[-1], y)

        # 最后一层权重和偏置的导数
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].T)

        # 倒数第二层一直到第一层 权重和偏置的导数
        for l in range(2, self.num_layers):
            z = zs[-l]

            sp = sigmoid_prime(z)

            # 当前层的误差
            delta = np.dot(self.weights[-l+1].T, delta) * sp

            # 当前层的偏置和权重的导数
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l-1].T)

        return (nabla_b, nabla_w)

    def accuracy(self, data, convert=False):
        if convert:
            # 如果是训练集数据,得到的结果是非0即1的one-hot编码方式
            results = [(np.argmax(self.feedforward(x)), np.argmax(y)) for (x, y) in data]
        else:
            # 如果是测试集数据,得到的结果就是0-9之间的数字分类
            results = [(np.argmax(self.feedforward(x)), y) for (x, y) in data]
        return sum(int(x == y) for (x, y) in results)

    def total_cost(self, data, lmbda, convert=False):
        cost = 0.0
        for x, y in data:
            a = self.feedforward(x)
            if convert:
                # convert为真,表示是测试数据集,将y转换为一个one-hot编码的十维向量
                y = mnist_loader.vectorized_result(y)
            cost += self.cost.fn(a, y) / len(data)
        cost += 0.5*(lmbda/len(data))*sum(np.linalg.norm(w)**2 for w in self.weights)
        return cost

    def cost_derivative(self, output_activation, y):
        return (output_activation - y)

    # 保存模型
    def save(self, filename):
        data = {"sizes": self.sizes,
                "weights": [w.tolist() for w in self.weights],
                "biases": [b.tolist() for b in self.biases],
                "cost": str(self.cost.__name__)
                }
        f = open(filename, "w")
        json.dump(data, f)  # json把字典类型转换成字符串
        f.close()

# 加载模型
def load(filename):
    f = open(filename, "r")
    data = json.load(f)
    f.close()
    cost = getattr(sys.modules[__name__], data["cost"])
    net = Network(data["sizes"], cost=cost)
    net.weights = [np.array(w) for w in data["weights"]]
    net.biases = [np.array(b) for b in data["biases"]]
    return net

# sigmoid激励函数
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def sigmoid_prime(z):
    return sigmoid(z) * (1-sigmoid(z))

if __name__ == "__main__":
    import mnist_loader

    training_data, validation_data, test_data = mnist_loader.load_data_wrapper()

    # 28*28=784个像素, 可以定义30个神经元, 共有10种分类
    net = Network([784, 30, 10])
    net.SGD(training_data, 30, 10, 0.5, test_data=test_data)
运行结果如下图所示:

可以看到,模型的效果显然比不采取提升学习效率的措施时效果要好很多
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 神经网络是一种机器学习算法,通过模拟人脑的神经元之间的连接来解决问题。BP反向传播)是一种用于训练神经网络的算法,通过反向传播误差来调整网络权重和偏差,从而使网络输出越来越接近期望输出。MNIST是一个手写数字识别的数据集,包含了一万个28*28像素的灰度图像,每个图像都有对应的标签,表示图像中的数字。MATLAB是一种常用的科学计算软件,适合进行神经网络的模型构建和训练。 在MATLAB中,可以使用神经网络工具箱来构建和训练BP神经网络。首先,需要导入MNIST数据集,并将其分为训练集和测试集。然后,可以定义一个神经网络模型,包括输入层、隐藏层和输出层,并初始化连接权重和偏差。接下来,通过使用训练集的样本对网络进行训练,将样本输入到网络中,并根据网络输出和期望输出的差异来调整权重和偏差。重复该过程直到网络达到期望的准确率。 在训练完毕后,可以使用测试集来评估网络的性能,计算其准确率等指标。此外,还可以使用训练好的网络对新的数字图像进行分类预测。 总之,利用MATLAB的神经网络工具箱,结合BP算法,可以实现对MNIST数据集中手写数字图像的识别任务。 ### 回答2: 神经网络是一种计算模型,具有学习和适应能力,可以通过输入和输出之间的关系进行学习和预测。BP神经网络是一种反向传播神经网络,其主要思想是通过计算输入与实际输出之间的误差,并通过反向传播来调整网络的权重和偏置,以使得网络的输出与实际输出更加接近。 MNIST是一个非常常用的手写数字识别数据集,包含有60,000张用于训练的手写数字图片和10,000张用于测试的手写数字图片。使用BP神经网络可以对这些手写数字进行分类和识别。 MATLAB是一个功能强大的数值计算和数据可视化软件,非常适合进行神经网络的建模、训练和测试。在MATLAB中,我们可以使用内置的神经网络工具箱,通过编写简单的代码来实现BP神经网络的训练和测试。 使用MATLAB进行MNIST手写数字识别的步骤如下: 1. 加载MNIST数据集,将图像和对应的标签分别作为训练和测试的输入和输出。 2. 创建一个BP神经网络模型,指定网络的结构和参数,如输入层、隐藏层和输出层的神经元个数,学习率等。 3. 使用训练数据对神经网络进行训练,通过不断调整网络的权重和偏置来减小预测输出与实际输出之间的误差。 4. 使用测试数据对训练好的神经网络进行测试,评估神经网络的性能和准确率。 5. 根据测试结果进行调整和优化,提高神经网络的准确率和泛化能力。 总之,神经网络BP MNIST MATLAB是一种使用MATLAB实现BP神经网络MNIST手写数字进行识别的方法,通过训练和测试神经网络可以实现对手写数字的自动识别。 ### 回答3: 神经网络反向传播(Backpropagation,简称BP)是一种用于训练神经网络的常用算法。MNIST是一个经典的手写数字识别数据集,包含了大量的手写数字图片和对应的标签。在Matlab中,我们可以使用BP算法来训练神经网络进行MNIST手写数字识别。 首先,我们需要加载MNIST数据集到Matlab中。可以使用mnistread函数将数据集中的图片和标签读入到Matlab的变量中。 然后,我们需要定义一个具有输入层、隐藏层和输出层的神经网络。可以使用Matlab中的nprtool工具来创建一个BP神经网络模型,并进行网络参数设置,如隐藏层节点数、学习率等。 接下来,我们将使用反向传播算法来训练神经网络。首先,将训练样本输入到神经网络中,并计算网络的输出值。然后,使用标签值与网络输出值之间的误差来调整网络的权重,从而减小误差。这个误差调整的过程是反向进行的,从输出层开始,通过隐藏层最终到达输入层。 训练完成后,我们可以使用训练好的网络模型对测试样本进行识别。将测试样本输入到网络中,根据输出层的结果判断输入的数字是什么。 最后,我们可以评估训练模型的性能。可以计算准确率、精确度和召回率等指标来评估模型对于不同类别数字的识别能力。 综上所述,神经网络BP算法在Matlab中可以应用于MNIST手写数字识别。通过加载数据集、定义网络模型、训练网络和评估模型性能等步骤,我们能够利用BP算法构建出一个较为准确的手写数字识别模型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值