神经网络的学习之好久没学习辣!

这次的主题是神经网络的学习——从训练数据中自动获取最优权重参数的过程。那么最优参数的标准是什么呢?我们在这里导入损失函数这一指标,找到使损失函数值最小的权重参数,那么怎么找到它呢?这里我们采用函数斜率的梯度法。
成千上万的参数需要“人工”来智能是不可能的,我们需要机器来学习这些东西,利用数据来决定参数值。
对于线性可分问题,感知机是可以利用数据自动学习的。
在这里插入图片描述

通过上图我们可以看出,神经网络是不需要人类介入的,而机器学习尚且需要人为确定特征量。深度学习有时也成为端到端机器学习,也就是从原始输入中获得输出的意思。
神经网络的优秀之处在于对于所有问题都可以用同样的流程解决(eg:无论是区分手写数字还是狗的脸的识别),都是通过不断学习提供给的数据,尝试发现待解决问题的模式。也就是与待处理问题无关,神经网络都直接将输入作为原始数据,进行“端到端”的学习。

损失函数

损失函数一般使用均方误差和交叉熵误差等。
均方误差很好理解:
在这里插入图片描述

import numpy as np
def mean_squared_error(y,t):
    return 0.5*np.sum((y-t)**2)
#假设“2”是正解
t=[0,0,1,0,0,0,0,0,0,0]
#“2"的概率最高
y=[0.1,0.05,0.6,0.0,0.05,0.1,0.0,0.1,0.0,0.0]
res = mean_squared_error(np.array(y),np.array(t))
print(res)#0.09750000000000003
#“7"的概率最高
y=[0.1,0.05,0.1,0.0,0.05,0.1,0.0,0.6,0.0,0.0]
res = mean_squared_error(np.array(y),np.array(t))
print(res)#0.5975

上面一种损失函数的值更小,说明“2”的结果更接近真实值。

交叉熵误差

在这里插入图片描述

由于是相乘的关系,而且正解标签采用独热编码,所以结果只与正解对应的神经网络的输出有关;又由于采用对数函数取反,所以神经网络的输出值越大,E就越小(误差越小);反之就越大。
在这里插入图片描述

自然对数y=lnx图像
虾面用代码实现交叉熵误差:

def cross_entropy_erroe(y,t):
    delta = 1e-7
    return -np.sum(t*np.log(y+delta))
    #防止np.log(0)无限大,让后续计算无法进行,加上一个微小值可以防止这种情况的发生
t=[0,0,1,0,0,0,0,0,0,0]
y=[0.1,0.05,0.6,0.0,0.05,0.1,0.0,0.1,0.0,0.0]
res = cross_entropy_erroe(np.array(y),np.array(t))
print(res)#0.510825457099338
y=[0.1,0.05,0.1,0.0,0.05,0.1,0.0,0.6,0.0,0.0]
res = cross_entropy_erroe(np.array(y),np.array(t))
print(res)#2.302584092994546
mini-batch学习

前面我们考虑的都是针对单个数据的损失函数,但是如果要求所有训练数据的损失函数总和,以交叉熵误差为例,可以写成:
在这里插入图片描述
如果训练数据有6w+个,我们计算如此海量的数据是不可能的,所以就从中选取一小部分进行“近似”。
那么怎么随机抽取呢?
用np.random.choice(x,y),表示从0~(x-1)中随机选取y个数,用这随机选取的mini-batch作为全体训练数据的近似值。
怎么实现对应mini-batch的交叉熵误差呢?

import sys, os
sys.path.append('2020/') #为了导入父目录中的文件而进行的设定,括号里就是单引号封住的夫目录名字+/
from dataset.mnist import load_mnist
(x_train,t_train), (x_test, t_test) = load_mnist(normalize=True,one_hot_label=False)
print(x_train.shape)#(60000, 784)
print(t_train.shape)#(60000, 10)
#虾面从这些训练数据中随机抽取10笔
train_size=x_train.shape[0]#就是60000
batch_size=10
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
def cross_entropy_error(y,t):
    if y.ndim == 1:#维数为1
        t = t.reshape(1,t.size)
        y = y.reshape(1,y.size)
    batch_size=y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t]+1e-7))/batch_size
print(cross_entropy_error(x_batch,t_batch))
为什么需要损失函数

好问题。
这里我们引入高数中的导数和梯度来理解。“如果稍微改变这个权重参数的值,损失函数会如何变化”?如果导数值为负,说明参数增大,损失减小;如果导数值为正,说明参数减小,损失减小;如果导数为0,无论参数怎么变化,损失函数值都不变。
不能将识别精度作为指标,因为如果以识别精度作为指标,则参数的导数在绝大部分地方都会变为0。为什么呢?举个栗子,如果神经网络能在100笔训练数据中正确识别32笔,那么识别精度就是32%,若稍微改变参数,还有可能保持在32%,不会出现变化,也就是说微调无法改善精度。
与之形成鲜明对比的是,损失函数可以发生连续变化。就像我们之前探讨过的阶跃函数的“竹筒敲石”和sigmoid的“涓涓细流”一样。神经网络正是得益于斜率不为0的性质,才可以正确进行。
在这里插入图片描述

数值微分

学过导数的我们就不过多介绍了,直接放图就懂了。
在这里插入图片描述

绘图:y=0.01x^2+0.1x
在这里插入图片描述

虾面我们来计算这个函数在5和10处的导数:

def function_1(x):
    return 0.01*x**2+0.1*x
def numertical_diff(f,x):
    h = 1e-4 #0.0001
    return (f(x+h)-f(x-h))/(2*h)#中心差分
print(numertical_diff(function_1,5))#0.1999999999990898
print(numertical_diff(function_1,10))#0.2999999999986347

通过计算我们知道,导函数是0.02*x+0.1,算出来应该分别是0.2和0.3,误差已经是很小了。
绘出5处的切线:
在这里插入图片描述

偏导数

在这里插入图片描述

f(X0,X1)=X0 ^ 2+X1 ^ 2的图像

import numpy as np
import matplotlib.pylab as plt
def function_2(x):
    return x[0]**2+x[1]**2 #或者return np.sum(x**2)
def numertical_diff(f,x):
    h = 1e-4 #0.0001
    return (f(x+h)-f(x-h))/(2*h)
def function_temp1(x0):
    return x0*x0+4.0**2.0
def function_temp2(x1):
    return 3.0**2.0+x1*x1
print(numertical_diff(function_temp1,3.0))#x0=3,x1=4时关于X0的偏导,6.00000000000378
print(numertical_diff(function_temp2,4.0))#x0=3,x1=4时关于X1的偏导,7.999999999999119
梯度

由全部变量的偏导数汇总而成的向量称为梯度(gradient)。

import numpy as np
def numerical_gradient(f,x):
    h = 1e-4
    grad = np.zeros_like(x) #生成和x形状相同的数组

    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x) #f(h+x)的计算
        x[idx] = tmp_val - h
        fxh2 = f(x) #f(h-x)的计算
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val #还原置
    return grad
print(numerical_gradient(function_2,np.array([3.0,4.0]))) #[6. 8.]
print(numerical_gradient(function_2,np.array([0.0,2.0]))) #[0. 4.]
print(numerical_gradient(function_2,np.array([3.0,0.0]))) #[6. 0.]

梯度代表了什么呢?我们会发现,所有的梯度都呈向量形式,指向“最低处”(最小值),就像指南针一样,指向同一点(距最低处越远,箭头越大)【梯度指示的方向是函数值减小最多的方向】
因为方向导数=cosθ×梯度,θ=0时,方向导数最大,也就是所有的下降方向中,梯度方向下降最多。
在这里插入图片描述

梯度法

我们机器学习的主要任务就是在学习时找到最优参数,可以用梯度法来寻找函数最小值(或者尽可能最小的值)
虽然梯度的方向并不一定指向最小值,甚至在复杂函数里指的基本都不是最小值(“学习高原”的停滞区等),但我们仍要以梯度的信息为线索,决定前进的方向。
不断沿着梯度方向前进,逐渐缩小函数值的方法就是梯度法。
一般的梯度法都是梯度下降法,我们在这里用梯度下降法来找到上述函数的最小值:

def gradient_descent(f,init_x,lr=0.01,step_num=100):
    x = init_x
    for i in range(step_num): #step_num就是重复次数
        grad = numerical_gradient(f,x)
        x -= lr * grad #lr就是learning rate学习率
    return x
init_x = np.array([-3.0,4.0])
print(gradient_descent(function_2,init_x=init_x,lr=0.1,step_num=100))
#[-6.11110793e-10  8.14814391e-10]

我们发现结果很接近0了
在这里插入图片描述

另外,学习率很重要,过大或过小都无法得到很好的结果——学习率过大,会发散成一个很大的值;学习率过小,会基本原地不动。
像学习率这样的参数称为超参数。这是一种和权重以及偏置性质不同的参数。

神经网络的梯度

在这里插入图片描述

好了,啰里啰唆地讲完了,来回顾一下神经网络学习的步骤:

  1. mini-batch
  2. 计算梯度
  3. 更新参数
  4. 重复1、2、3步
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值