第三章.神经网络的学习—梯度,手写数字识别2层神经网络的实现

news2024/11/28 4:34:37

第三章.神经网络的学习

3.2 梯度

梯度法使用梯度的信息决定前进的方向,在介绍梯度法之前,先介绍一下导数和偏导。

1.导数

1).公式:

在这里插入图片描述

2).代码实现:

  • 注意:

    ①.h = 1e-4不可以使用过小的值,会出现计算出错的问题,例如:h = 1e-50 , result = 0.0

    ②.计算函数f(x)在x与x+h之间的差分,这个计算从一开始就有误差,“真的导数”对应函数在x处的斜率,但是上述公式中计算的导数对应的是x和(h+x)之间的斜率,因此,真的导数(“真的切线”)和上述公式中得到的导数值 在严格意义上是一致的,这个差异的出现是因为h不可能无限接近0
    在这里插入图片描述
    ③.为了减少这个误差,我们可以计算函数f(x)在(x-h)和(x+h)之间的差分。这种计算方式以x为中心,计算它左右两边的差分,这也称为“中心差分”

· 公式优化后的代码

def numerical_diff(f, x):
    h = 1e-4
    return (f(x + h) - f(x - h)) / (2 * h)


# y=0.01x^2+0.1x
def function1(x):
    return 0.01 * x ** 2 + 0.1 * x


# x=5的导数
x_data = 5
Derivative5 = numerical_diff(function1, x_data)
print('Derivative5:', Derivative5)

# x=10的导数
x_data = 10
Derivative10 = numerical_diff(function1, x_data)
print('Derivative10:', Derivative10)

3).结果展示:

在这里插入图片描述

2.偏导数

有多个变量函数的导数称为偏导数。

1).示例:

在这里插入图片描述

  • 说明
    ①.公式中有两个变量,所以需要区分对哪个变量求导数,用数学式表示的话,可以写成∂f/∂x0,∂f/∂x1

2).代码实现:

# f(x)=x0^2+x1^2
def numerical_diff(f, x):
    h = 1e-4
    return (f(x + h) - f(x - h)) / (2 * h)


# 当x0=3,x1=4时,关于x0的偏导数∂f/∂x0
def function1(x0):
    return x0 ** 2 + 4.0 ** 2


# 当x0=3,x1=4时,关于x1的偏导数∂f/∂x1
def function2(x1):
    return 3.0 ** 2 + x1 ** 2


# 当x0=3,x1=4时,关于x0的偏导数∂f/∂x0
x = 3.0
y = numerical_diff(function1, x)
print(y)

# 当x0=3,x1=4时,关于x1的偏导数∂f/∂x1
x = 4.0
y = numerical_diff(function2, x)
print(y)

3).结果展示:

在这里插入图片描述

3.梯度

在刚才的例子中,我们按变量分别计算x0和x1的偏导数,现在我们希望一起计算x0和x1的偏导数(∂f/∂x0,∂f/∂x1),像(∂f/∂x0,∂f/∂x1)这样由全部变量的偏导数汇总而成的向量称为梯度

1).示例:

  • 以上述示例为例的代码:
import numpy as np

def function(x):
    return np.sum(x ** 2)


def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for i in range(x.size):
        # f(x+h)
        fx1 = f(x[i] + h)
        # f(x-h)
        fx2 = f(x[i] - h)

        grad[i] = (fx1 - fx2) / (2 * h)

    return grad


x_data = np.array([3.0, 4.0])
y_data = numerical_gradient(function, x_data)
print(y_data)#output:[6. 8.]

x_data = np.array([0.0, 2.0])
y_data = numerical_gradient(function, x_data)
print(y_data)#output:[0. 4.]

x_data = np.array([3.0, 0.0])
y_data = numerical_gradient(function, x_data)
print(y_data)#output:[6. 0.]

2).图像描述:

  • ①.如图所示,f(x0,x1)=x02 +x12的梯度呈现为有向向量(箭头),我们发现梯度指向函数f(x)的"最低处"(最小值),就像指南针一样,所有箭头都指向同一点,其次我们发现离“最低处”越远,箭头越大。

    ②.梯度指示的方向是各点处函数值减少最多的方向。

在这里插入图片描述

4.梯度法

在梯度法中,函数的取值从当前位置沿着梯度方向前进一定距离,然后在新的位置重新求梯度,在沿着新的梯度方向前进,如何反复,不断沿着梯度方向前进,逐渐减少函数值的过程称为梯度法

1).梯度法存在的缺陷:

  • 梯度表示的是各点处的函数值减小最多的方向,因此无法保证梯度所指向的方向就是函数的最小值或者真正应该前进的方向,可能是局部极小值或者鞍点。[局部极小值:限定在某个范围内的最小值;鞍点:从某个方向上看极大值,从另一个方向上看则是极小值的点]

2).数学式表示梯度:

在这里插入图片描述

  • 参数说明:
    η:学习率

5.学习算法的实现

1).神经网络的学习步骤:

  • 前提:
    神经网络存在合适的权重和偏置,调整权重和偏置以便拟合训练数据的过程称为“学习”,神经网络的学习分成下面4个步骤。

  • 步骤1 (mini-batch):
    从训练数据中随机选出一部分数据,这部分数据称为mini-batch,我们的目标是减少mini-batch损失函数的值。

  • 步骤2 (计算梯度):
    为了减少mini_batch损失函数的值,需要求出各个权重参数的梯度,梯度表示损失函数的值减少最多的方向。

  • 步骤3 (更新参数):
    将权重参数沿梯度方向进行微小更新

  • 步骤4 (重复):
    重复步骤1,步骤2,步骤3

2).示例:(手写数字识别2层神经网络的实现)

import numpy as np
import matplotlib.pyplot as plt
import sys, os

sys.path.append(os.pardir)
from dataset.mnist import load_mnist


# 加载训练数据
def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    return (x_train, t_train), (x_test, t_test)


# 2层神经网络的类
class TwoLayerNet:
    # 参数初始化
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

    # 激活函数:sigmoid
    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    # 输出层函数:softmax
    def softmax(self, x):
        if x.ndim == 2:
            x = x.T
            x = x - np.max(x, axis=0)
            y = np.exp(x) / np.sum(np.exp(x), axis=0)
            return y.T

        x = x - np.max(x)  # 溢出对策
        return np.exp(x) / np.sum(np.exp(x))

    def sigmoid_grad(self, x):
        return (1.0 - self.sigmoid(x)) * self.sigmoid(x)

    # 微分函数
    def numerical_gradient(self, f, x):
        h = 1e-4  # 0.0001
        grad = np.zeros_like(x)

        it = np.nditer(x, flags=['multi_index'],
                       op_flags=['readwrite'])  # 多维迭代,遍历数组.为了在遍历数组的同时,实现对数组元素值得修改,必须指定op_flags=['readwrite']模式
        while not it.finished:
            idx = it.multi_index
            tmp_val = x[idx]
            x[idx] = float(tmp_val) + h
            fxh1 = f(x)  # f(x+h)

            x[idx] = tmp_val - h
            fxh2 = f(x)  # f(x-h)
            grad[idx] = (fxh1 - fxh2) / (2 * h)

            x[idx] = tmp_val  # 还原值
            it.iternext()

        return grad

    # 推理函数
    def predict(self, x):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']

        # 第一层
        a1 = np.dot(x, W1) + b1
        z1 = self.sigmoid(a1)

        # 第二层
        a2 = np.dot(z1, W2) + b2
        y = self.softmax(a2)

        return y

    # 交叉熵误差
    def cross_entropy_error(self, y, t):
        if y.ndim == 1:
            t = t.reshape(1, t.size)
            y = y.reshape(1, y.size)

        # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
        if t.size == y.size:
            t = t.argmax(axis=1)

        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

    # 损失函数
    def loss(self, x, t):
        y = self.predict(x)
        loss = self.cross_entropy_error(y, t)
        return loss

    # 识别精度
    def Accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        t = np.argmax(t, axis=1)

        accuracy = np.sum(y == t) / float(x.shape[0])

        return accuracy

    # 梯度法
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)

        grads = {}
        grads['W1'] = self.numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = self.numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = self.numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = self.numerical_gradient(loss_W, self.params['b2'])

        return grads

    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        grads = {}

        batch_num = x.shape[0]

        # forward
        a1 = np.dot(x, W1) + b1
        z1 = self.sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = self.softmax(a2)

        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)

        da1 = np.dot(dy, W2.T)
        dz1 = self.sigmoid_grad(a1) * da1
        grads['W1'] = np.dot(x.T, dz1)
        grads['b1'] = np.sum(dz1, axis=0)

        return grads


# 加载训练数据
(x_train, t_train), (x_test, t_test) = get_data()

train_loss_list = []
train_acc_list = []
test_acc_list = []

# 超参数
iters_num = 10000
lr = 0.1
batch_size = 100
train_size = x_train.shape[0]

# 平均每个epoch的重复次数
iter_per_epoch = max(train_size / batch_size, 1)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

for i in range(iters_num):

    # 获取mini-batch
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    # 计算梯度
    # grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)

    # 参数更新
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= lr * grad[key]

    # 记录学习过程
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)

    # 计算每个epoch的识别精度
    if i % iter_per_epoch == 0:
        train_acc = network.Accuracy(x_train, t_train)
        test_acc = network.Accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print("train acc,test acc|" + str(train_acc) + ',' + str(test_acc))

# 绘制识别精度图像
plt.rcParams['font.sans-serif'] = ['SimHei']  # 解决中文乱码
plt.rcParams['axes.unicode_minus'] = False  # 解决负号不显示的问题

plt.figure(figsize=(8, 4))
plt.subplot(1, 2, 1)
x_data = np.arange(0, len(train_acc_list))
plt.plot(x_data, train_acc_list, 'b')
plt.plot(x_data, test_acc_list, 'r')
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.ylim(0.0, 1.0)
plt.title('训练数据和测试数据的识别精度')
plt.legend(['train_acc', 'test_acc'])

plt.subplot(1, 2, 2)
x_data = np.arange(0, len(train_loss_list))
plt.plot(x_data, train_loss_list, 'g')
plt.xlabel('iters_num')
plt.ylabel('loss')
plt.title('损失函数')
plt.show()

3).结果展示:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/331593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

室外定位靠卫星 室内定位又如何?

人类为了不让自己迷失在茫茫大自然中,先后发明罗盘、指南针等工具,卫星定位的问世,解决了“我在哪里”的问题。如今物联网是“信息化”时代的重要发展阶段,随着社会信息化水平的普遍提高,其社会的重要性日益显现。云计…

全国青少年编程等级考试scratch四级真题2022年9月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击电子学会-全国青少年编程等级考试真题Scratch一级(2019年3月)在线答题_程序猿下山的博客-CSDN博客_小航答题助手1、运行下列程序,说法正确的是?( )A.列表…

Android 图形系统详解

概述 一个页面(Activity)显示到屏幕上主要经过一下几个流程: 启动 Activity → 创建 Window → WMS 注册 Window → SurfaceFlinger 创建 Surface → 合成 layer → 显示 主要涉及三个进程:App进程、System_server进程、SurfaceF…

常用的硬件端口中各个引脚代表的含义(持续更新)

常见接口 参考:https://blog.csdn.net/qlexcel/article/details/117429653 type-c 示意图 参考:https://blog.csdn.net/qlexcel/article/details/117431413,https://blog.csdn.net/HandsomeHong/article/details/119823915&#xff…

Hystrix容错组件

Hystrix简介Hystrix,英文意思是豪猪,全身是刺,看起来就不好惹,是一种保护机制。它是容错组件,Hystrix也是Netflix公司的一款组件。那么Hystix的作用是什么呢?具体要保护什么呢?Hystix是Netflix开源的一个延…

基于WebGl的智慧校园Web3D管理系统

学校是社会进步与学生成才的重要载体。随着信息化趋势的不断加强,构建"智慧型"校园,继续加强把学校作为主体的教育信息化进程,是教育信息化的主要构成部分。今天给大家分享一个基于 数维图 的 Sovit3D编辑器 构建的学校3D可视化场景…

BIM技巧 | Revit绘制围栏12步骤

首先简单介绍一下revit中的栏杆其实是有三部分的族组成,分别是:支柱,扶栏结构,栏杆。 所以要灵活的运用栏杆命令绘制需要的构造,最少要新建三个族。 第一步:绘制支柱族 用“公制栏杆-支柱”族样板绘制围墙…

【GD32F427开发板试用】二、USB库移植与双USB CDC-ACM功能开发

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:chenjie 【GD32F427开发板试用】一、环境搭建与freertos移植 【GD32F427开发板试用】二、USB库移植与双USB CDC-ACM功能开发 【GD32F427开发板…

SSM之前回顾

1、技术栈总览 学习要搞清楚你的目标;先学C/JAVA;搞程序不要太浮躁; java基础:计算机基础、写博客、java基础语法、流程控制和方法、数组、面向对象、异常、常用类、集合框架、IO、多线程、GUI、网络编程、注解与反射、JUC编程、…

操作符详解(上篇)

前言小伙伴们大家好,随着对c的不断学习今天我们将来学习操作符。在初始c语言中也介绍过操作符但也只是点到即可,今天我们将详细了解操作符。操作符分类:算术操作符移位操作符位操作符赋值操作符单目操作符关系操作符逻辑操作符条件操作符逗号…

不用自己排版设计的海报设计工具!在线海量模板!

人才招聘旺季,如何再众多的招聘海报中脱颖而出,招聘到心意人才呢?HR要如何排版设计招聘海报呢?只需跟着小编下面的乔拓云工具使用教程,不仅能帮你解决海报设计文案灵感和排版灵感,还不需要任何设计基础就能…

15.面向对象程序设计

文章目录面向对象程序设计15.1OOP:概述继承动态绑定15.2定义基类和派生类15.2.1定义基类成员函数与继承访问控制与继承15.2.2定义派生类派生类对象及派生类向基类的类型转换派生类构造函数派生类使用基类的成员继承与静态成员派生类的声明被用作基类的类防止继承的发…

【虹科公告】好消息!云展厅开放时间长达1年,2023年不限次云观展

云展厅开放通知 2023年,【虹科赋能汽车智能化云展厅】将持续开放,开放时间长达一年,开放期内,均可进入观展,没有次数及观看时长限制,欢迎大家随时进入云展厅观展。 虹科赋能汽车智能化云展厅 聚焦前沿技…

【手撕面试题】HTML+CSS(高频知识点五)

目录 面试官:css 如何实现左侧固定 300px,右侧自适应的布局? 面试官:flex 布局中 align-content 与 align-items 有何区别? 面试官:Grid 布局的优势在哪里? 面试官:Flex 布局中的…

【1797. 设计一个验证系统】

来源:力扣(LeetCode) 描述: 你需要设计一个包含验证码的验证系统。每一次验证中,用户会收到一个新的验证码,这个验证码在 currentTime 时刻之后 timeToLive 秒过期。如果验证码被更新了,那么它…

游戏多开的分析与实现

大部分游戏为了防止工作室通过多开游戏牟利,都会采取各种手段来防止游戏被多次打开。检测窗口标题,创建互斥体,创建内存映射这些都是防止游戏多开的常用手段。 主要内容 游戏运行后,无非执行两步操作 若已经存在,退出…

【问题代码】顺序点的深入理解(汇编剖析+手画图解)

这好像是一个哲学问题。 目录 前言 一、顺序点是什么? 二、发生有关顺序点的问题代码 vs中: gcc中: 三、细读汇编 1.vs汇编如下(示例): 2.gcc汇编如下(示例): 四…

R语言raster包遍历多个文件夹并批量计算每一个文件夹下全部遥感影像的平均值

本文介绍基于R语言中的raster包,遍历读取多个文件夹下的多张栅格遥感影像,分别批量对每一个文件夹中的多个栅格图像计算平均值,并将所得各个结果栅格分别加以保存的方法。 其中,本文是用R语言来进行操作的;如果希望基于…

每天10个前端小知识 【Day 9】

前端面试基础知识题 1. bind、call、apply 有什么区别?如何实现一个bind? apply、call、bind三者的区别在于: 三者都可以改变函数的this对象指向三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null&#x…

智能硬件的工作原理与发展定位

一、硬件概述 智能硬件是以平台性底层软硬件为基础,以智能传感互联、人机交互、新型显示及大数据处理等新一代信息技术为特征,以新设计、新材料、新工艺硬件为载体的新型智能终端产品及服务。 与传统硬件相比,智能硬件相比传统硬件&#xf…