神经网络原理+从零创建两层神经网络(基于Python)

神经网络的参数主要有两大块,一是各神经元之间连接的权重参数,而是表示各功能神经元阈值的偏置参数。通过对损失函数使用梯度下降法,可以找到最优的权重和偏置参数,使得损失函数达到极小。

神经网络原理介绍(以二层神经网络为例)

在这里插入图片描述

如上图所示,一个简单二层神经网络包含输入层、隐层和输出层。输入的数据乘以第一层权重参数矩阵 W ( 1 ) W^{(1)} W(1)后,到达隐层,经隐层的激活函数 h ( x ) h(x) h(x)作用后,乘以第二层权重参数矩阵 W ( 2 ) W^{(2)} W(2)后到达输出层,经输出层的激活函数处理后输出。其中,隐层的激活函数一般为Sigmoid函数或是Relu函数,而输出层的激活函数一般为恒等函数或是Softmax函数。

假如输入数据为 x = ( x 1 , x 2 ) \pmb{x} = (x_1, x_2) xxx=(x1,x2),隐层有三个神经元,记 x 1 x_1 x1到第一个隐层神经元的权重参数为 w 11 ( 1 ) w^{(1)}_{11} w11(1) x 2 x_2 x2到第一个隐层神经元的权重参数为 w 12 ( 1 ) w^{(1)}_{12} w12(1) x 1 x_1 x1到第二个隐层神经元的权重参数为 w 21 ( 1 ) w^{(1)}_{21} w21(1),那么权重参数矩阵可以表示为
W ( 1 ) = [ w 11 ( 1 ) w 21 ( 1 ) w 31 ( 1 ) w 12 ( 1 ) w 22 ( 1 ) w 32 ( 1 ) ] W^{(1)} = \begin{bmatrix} w^{(1)}_{11}\quad w^{(1)}_{21} \quad w^{(1)}_{31}\\ w^{(1)}_{12}\quad w^{(1)}_{22} \quad w^{(1)}_{32} \end{bmatrix} W(1)=[w11(1)w21(1)w31(1)w12(1)w22(1)w32(1)]
从输入层到隐层的变换可以表示为
( x 1 x 2 ) [ w 11 ( 1 ) w 21 ( 1 ) w 31 ( 1 ) w 12 ( 1 ) w 22 ( 1 ) w 32 ( 1 ) ] = ( a 1 a 2 a 3 ) (x_1\quad x_2)\begin{bmatrix} w^{(1)}_{11}\quad w^{(1)}_{21} \quad w^{(1)}_{31}\\ w^{(1)}_{12}\quad w^{(1)}_{22} \quad w^{(1)}_{32} \end{bmatrix}=(a_1\quad a_2\quad a_3) (x1x2)[w11(1)w21(1)w31(1)w12(1)w22(1)w32(1)]=(a1a2a3)

但是一般情况下,我们会设定一个阈值 b \pmb{b} bbb,用它来控制神经元被激活的容易程度,即(这里先以最简单的阶跃函数 h ( x ) h(x) h(x)来作为激活函数)
( x 1 x 2 ) [ w 11 ( 1 ) w 21 ( 1 ) w 31 ( 1 ) w 12 ( 1 ) w 22 ( 1 ) w 32 ( 1 ) ] + ( b 1 ( 1 ) b 2 ( 1 ) b 3 ( 1 ) ) = ( a 1 a 2 a 3 ) (x_1\quad x_2)\begin{bmatrix} w^{(1)}_{11}\quad w^{(1)}_{21} \quad w^{(1)}_{31}\\ w^{(1)}_{12}\quad w^{(1)}_{22} \quad w^{(1)}_{32} \end{bmatrix}+(b^{(1)}_1\quad b^{(1)}_2\quad b^{(1)}_3)=(a_1\quad a_2\quad a_3) (x1x2)[w11(1)w21(1)w31(1)w12(1)w22(1)w32(1)]+(b1(1)b2(1)b3(1))=(a1a2a3)
h ( x ) = { 0 , w 11 ( 1 ) x 1 + w 21 ( 1 ) x 2 + b &lt; = 0 1 , w 11 ( 1 ) x 1 + w 21 ( 1 ) x 2 + b &gt; 0 h(x)=\begin{cases} 0,\qquad w^{(1)}_{11}x_1+w^{(1)}_{21}x_2+b&lt;=0\\ 1,\qquad w^{(1)}_{11}x_1+w^{(1)}_{21}x_2+b&gt;0 \end{cases} h(x)={0,w11(1)x1+w21(1)x2+b<=01,w11(1)x1+w21(1)x2+b>0

输入到隐层的信息经过激活函数作用后,变为向量 z \pmb{z} zzz,向量 z \pmb{z} zzz再经第二层权重矩阵 W ( 2 ) W^{(2)} W(2)作用后到达输出层,第一个隐层神经元与第一个输出神经元之间的权重参数记作 w 11 ( 2 ) w^{(2)}_{11} w11(2),显然,由隐层到输出层的过程可以表示为
( z 1 z 2 z 3 ) [ w 11 ( 2 ) w 21 ( 2 ) w 12 ( 2 ) w 22 ( 2 ) w 13 ( 2 ) w 23 ( 2 ) ] + ( b 1 ( 2 ) b 2 ( 2 ) ) = ( y 1 y 2 ) (z_1\quad z_2\quad z_3)\begin{bmatrix} w^{(2)}_{11}\quad w^{(2)}_{21} \\ w^{(2)}_{12}\quad w^{(2)}_{22} \\ w^{(2)}_{13}\quad w^{(2)}_{23} \\ \end{bmatrix}+(b^{(2)}_1\quad b^{(2)}_2)=(y_1\quad y_2) (z1z2z3)w11(2)w21(2)w12(2)w22(2)w13(2)w23(2)+(b1(2)b2(2))=(y1y2)

神经网络的学习步骤

SGD(stochastic gradient descent)随机梯度下降法

  • 从训练集中随机选出一部分数据(这部分数据成为mini-batch)。训练的目标是减小mini-batch的损失函数的值。
  • 求出各参数的梯度,梯度指向了损失函数减小最多的方向。
  • 将损失函数向着梯度方向进行更新
  • 将上述步骤进行迭代

从零创建一个两层神经网络

首先准备好要用到的几个函数
其中,定义求梯度函数 g r e d i e n t ( f , x ) gredient(f, x) gredient(f,x),在上一篇博客中有提到提速下降的原理和实现,详见这篇博客

#交叉熵误差
def cross_entropy_error(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)

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

#softmax函数
def softmax(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))

#定义求梯度的函数,在上一篇博客中有提到梯度下降的实现
#详见 https://blog.csdn.net/ISMedal/article/details/87893200 这里
def gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    it = np.nditer(x, flags=['multi_index'], 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

准备好了这些函数以后,我们定义一个类,来实现上面说的最简单的二层神经网络

import numpy as np
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)
        #np.random.randn(...)是为了创建初始化的第一层权重矩阵,其行数为输入层神经元的个数,列数为隐层神经元的个数
        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) #初始化的第二层偏置项
    
    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 = sigmoid(a1) #使用sigmoid函数作为隐层的激活函数
        a2 = np.dot(z1, W2) + b2 #输出层接收到的信息
        y = softmax(a2) #使用softmax函数作为输出层的激活函数
        return y
    
    def loss(self, x, t): #定义损失函数,其中x为输入的待预测数据,t为真实数据
        y = self.predict(x)
        return cross_entropy_error(y, t) #使用交叉熵误差作为损失函数
    
    #真实结果都是经过One-hot编码后的
    #如果预测结果y_1=[0.1, 0.3, 0.5, 0.1],真实结果为[0, 0, 1, 0]
    #那么表示该条数据属于第三类的可能性最大,而真实结果说明它就是属于第三类,则该条数据分类正确
    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]) #要将int格式转化为可以参与计算的float格式
        return accuracy
        
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t) 
        #将损失函数定义为关于W的函数(而不是关于x,更不是关于t的),是以W为自变量,后面对W求偏导
        grads = {}
        grads['W1'] = gradient(loss_W, self.params['W1']) #求权重参数矩阵W1的梯度(导数)
        grads['b1'] = gradient(loss_W, self.params['b1'])
        grads['W2'] = gradient(loss_W, self.params['W2'])
        grads['b2'] = gradient(loss_W, self.params['b2'])
        return grads

以手写字体数据集为例,实现mini-batch学习

mini-batch学习,指的是从训练数据中,随机选择一部分数据,再以这些数据为对象,使用梯度法(在这篇博客有解释)更新参数的过程。

注:导入需要的数据集mnist,其中需要用到的py文件dataset.mnist.py在这里取(把它放在python或ipynb文件所在的目录就可以用了)

from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = load_mnist(normalize = True, one_hot_label = True)
train_loss_list = []
#人为设定的超参数
iters_num = 10000 #迭代10000次
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
network = TwoLayerNet(input_size = 784, hidden_size = 50, output_size = 10)
#因为输入的图像大小是784(28*28),因此设定input_size = 784

for i in range(iters_num):
    #获取每一次训练的那个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)
    #更新参数
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    #记录学习过程
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值