2021-06-13

@第一章神经网络基本概念

第一步 概论与基本概念

摘要
在这一步中,我们将针对零基础的初学者们,用通俗易懂的语言,讲述神经网络的基本概念。
在概论与基本概念中,首先对人工智能的发展简史、定义、以及科学范式的演化进行介绍,并列举了一些有趣的实例,让大家对人工智能的世界观方法论形成一个基本的认识。
然后讲解一下神经网络基本的训练和工作原理,因为基本上各种教程里都没有提到这一点,以至于笔者在刚开始学习神经网络时一头雾水,不得要领,不知从何处开始下手。
再后面是反向传播和梯度下降,我们先从简单的线性方式说起(只有加法和乘法),而且用代入数值的方式来消除对公式的恐惧心理。然后会说到分层的复杂(非线性)函数的反向传播,同样用数值代入方式手推反向过程。
梯度下降是神经网络的基本学习方法,我们会用单变量和双变量两种方式说明,配以可视化的图解。再多的变量就无法用可视化方式来解释了,所以我们力求用简单的方式理解复杂的事物。
本部分最后是损失函数的讲解,着重说明了神经网络中目前最常用的均方差损失函数(用于回归)和交叉熵损失函数(用于分类)。

2.3.1 从自然现象中理解梯度下降
在大多数文章中,都以“一个人被困在山上,需要迅速下到谷底”来举例,这个人会“寻找当前所处位置最陡峭的地方向下走”。这个例子中忽略了安全因素,这个人不可能沿着最陡峭的方向走,要考虑坡度。
在自然界中,梯度下降的最好例子,就是泉水下山的过程:
水受重力影响,会在当前位置,沿着最陡峭的方向流动,有时会形成瀑布(梯度下降);
水流下山的路径不是唯一的,在同一个地点,有可能有多个位置具有同样的陡峭程度,而造成了分流(可以得到多个解);
遇到坑洼地区,有可能形成湖泊,而终止下山过程(不能得到全局最优解,而是局部最优解)。
2.3.2 梯度下降的数学理解
梯度下降的数学公式:
θ n + 1 = θ n − η ⋅ ∇ J ( θ ) (1) \theta{n+1} = \theta{n} - \eta \cdot \nabla J(\theta) \tag{1} θn+1=θnηJ(θ)(1)
其中:
θ n + 1 \theta_{n+1} θn+1:下一个值;
θ n \theta_n θn:当前值;
− - :减号,梯度的反向;
η \eta η:学习率或步长,控制每一步走的距离,不要太快以免错过了最佳景点,不要太慢以免时间太长;
∇ \nabla :梯度,函数当前位置的最快上升点;
J ( θ ) J(\theta) J(θ):函数。
梯度下降的三要素
当前点;
方向;
步长。
为什么说是“梯度下降”?
“梯度下降”包含了两层含义:
梯度:函数当前位置的最快上升点;
下降:与导数相反的方向,用数学语言描述就是那个减号。
亦即与上升相反的方向运动,就是下降。

图2-9 梯度下降的步骤
图2-9解释了在函数极值点的两侧做梯度下降的计算过程,梯度下降的目的就是使得x值向极值点逼近。
2.3.3 单变量函数的梯度下降
假设一个单变量函数:
J ( x ) = x 2 J(x) = x ^2 J(x)=x2
我们的目的是找到该函数的最小值,于是计算其微分:
J ′ ( x ) = 2 x J'(x) = 2x J(x)=2x
假设初始位置为:
x 0 = 1.2 x_0=1.2 x0=1.2
假设学习率:
η = 0.3 \eta = 0.3 η=0.3
根据公式(1),迭代公式:
x n + 1 = x n − η ⋅ ∇ J ( x ) = x n − η ⋅ 2 x x{n+1} = x{n} - \eta \cdot \nabla J(x)= x_{n} - \eta \cdot 2x xn+1=xnηJ(x)=xnη2x
假设终止条件为 J ( x ) < 0.01 J(x)<0.01 J(x)<0.01,迭代过程是: x=0.480000, y=0.230400 x=0.192000, y=0.036864 x=0.076800, y=0.005898 x=0.030720, y=0.000944
上面的过程如图2-10所示。
图2-10 使用梯度下降法迭代的过程
代码如下:


```python
在这里插入代码片# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.

import numpy as np
import matplotlib.pyplot as plt

def target_function(x):
    y = x*x
    return y

def derivative_function(x):
    return 2*x

def draw_function():
    x = np.linspace(-1.2,1.2)
    y = target_function(x)
    plt.plot(x,y)

def draw_gd(X):
    Y = []
    for i in range(len(X)):
        Y.append(target_function(X[i]))
    
    plt.plot(X,Y)

if __name__ == '__main__':
    x = 1.2
    eta = 0.3
    error = 1e-3
    X = []
    X.append(x)
    y = target_function(x)
    while y > error:
        x = x - eta * derivative_function(x)
        X.append(x)
        y = target_function(x)
        print("x=%f, y=%f" %(x,y))


    draw_function()
    draw_gd(X)
    plt.show()

结果图像如下:
在这里插入图片描述

2.3.4 双变量的梯度下降
假设一个双变量函数:
J ( x , y ) = x 2 + sin ⁡ 2 ( y ) J(x,y) = x^2 + \sin^2(y) J(x,y)=x2+sin2(y)
我们的目的是找到该函数的最小值,于是计算其微分:
∂ J ( x , y ) ∂ x = 2 x {\partial{J(x,y)} \over \partial{x}} = 2x xJ(x,y)=2x ∂ J ( x , y ) ∂ y = 2 sin ⁡ y cos ⁡ y {\partial{J(x,y)} \over \partial{y}} = 2 \sin y \cos y yJ(x,y)=2sinycosy
假设初始位置为:
( x 0 , y 0 ) = ( 3 , 1 ) (x0,y0)=(3,1) (x0,y0)=(3,1)
假设学习率:
η = 0.1 \eta = 0.1 η=0.1
根据公式(1),迭代过程是的计算公式: ( x n + 1 , y n + 1 ) = ( x n , y n ) − η ⋅ ∇ J ( x , y ) (x{n+1},y{n+1}) = (xn,yn) - \eta \cdot \nabla J(x,y) (xn+1,yn+1)=(xn,yn)ηJ(x,y) = ( x n , y n ) − η ⋅ ( 2 x , 2 ⋅ sin ⁡ y ⋅ cos ⁡ y ) (1) = (xn,yn) - \eta \cdot (2x,2 \cdot \sin y \cdot \cos y) \tag{1} =(xn,yn)η(2x,2sinycosy)(1)
根据公式(1),假设终止条件为 J ( x , y ) < 0.01 J(x,y)<0.01 J(x,y)<0.01,迭代过程如表2-3所示。
表2-3 双变量梯度下降的迭代过程
|迭代次数|x|y|J(x,y)| |—|---|—|---| |1|3|1|9.708073| |2|2.4|0.909070|6.382415| |…|…|…|…| |15|0.105553|0.063481|0.015166| |16|0.084442|0.050819|0.009711|
迭代16次后, J ( x , y ) J(x,y) J(x,y) 的值为 0.009711 0.009711 0.009711,满足小于 0.01 0.01 0.01 的条件,停止迭代。
上面的过程如表2-4所示,由于是双变量,所以需要用三维图来解释。请注意看两张图中间那条隐隐的黑色线,表示梯度下降的过程,从红色的高地一直沿着坡度向下走,直到蓝色的洼地。
表2-4 在三维空间内的梯度下降过程
|观察角度1|观察角度2| |–|--| |||
代码如下:

在这里插入代码片# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def target_function(x,y):
    J = x**2 + np.sin(y)**2
    return J

def derivative_function(theta):
    x = theta[0]
    y = theta[1]
    return np.array([2*x,2*np.sin(y)*np.cos(y)])

def show_3d_surface(x, y, z):
    fig = plt.figure()
    ax = Axes3D(fig)
 
    u = np.linspace(-3, 3, 100)
    v = np.linspace(-3, 3, 100)
    X, Y = np.meshgrid(u, v)
    R = np.zeros((len(u), len(v)))
    for i in range(len(u)):
        for j in range(len(v)):
            R[i, j] = X[i, j]**2 + np.sin(Y[i, j])**2

    ax.plot_surface(X, Y, R, cmap='rainbow')
    plt.plot(x,y,z,c='black')
    plt.show()

if __name__ == '__main__':
    theta = np.array([3,1])
    eta = 0.1
    error = 1e-2

    X = []
    Y = []
    Z = []
    for i in range(100):
        print(theta)
        x=theta[0]
        y=theta[1]
        z=target_function(x,y)
        X.append(x)
        Y.append(y)
        Z.append(z)
        print("%d: x=%f, y=%f, z=%f" %(i,x,y,z))
        d_theta = derivative_function(theta)
        print("    ",d_theta)
        theta = theta - eta * d_theta
        if z < error:
            break
    show_3d_surface(X,Y,Z)

结果图像如下:
在这里插入图片描述

2.3.5 学习率η的选择
在公式表达时,学习率被表示为 η \eta η。在代码里,我们把学习率定义为learning_rate,或者eta。针对上面的例子,试验不同的学习率对迭代情况的影响,如表2-5所示。
表2-5 不同学习率对迭代情况的影响
|学习率|迭代路线图|说明| |—|---|—| |1.0||学习率太大,迭代的情况很糟糕,在一条水平线上跳来跳去,永远也不能下降。| |0.8||学习率大,会有这种左右跳跃的情况发生,这不利于神经网络的训练。| |0.4||学习率合适,损失值会从单侧下降,4步以后基本接近了理想值。| |0.1||学习率较小,损失值会从单侧下降,但下降速度非常慢,10步了还没有到达理想状态。|
代码如下:

# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.

import numpy as np
import matplotlib.pyplot as plt

def targetFunction(x):
    y = (x-1)**2 + 0.1
    return y

def derivativeFun(x):
    y = 2*(x-1)
    return y

def create_sample():
    x = np.linspace(-1,3,num=100)
    y = targetFunction(x)
    return x,y

def draw_base():
    x,y=create_sample()
    plt.plot(x,y,'.')
    plt.show()
    return x,y
   
def gd(eta):
    x = -0.8
    a = np.zeros((2,10))
    for i in range(10):
        a[0,i] = x
        a[1,i] = targetFunction(x)
        dx = derivativeFun(x)
        x = x - eta*dx
    
    plt.plot(a[0,:],a[1,:],'x')
    plt.plot(a[0,:],a[1,:])
    plt.title("eta=%f" %eta)
    plt.show()

if __name__ == '__main__':

    eta = [1.1,1.,0.8,0.6,0.4,0.2,0.1]

    for e in eta:
        X,Y=create_sample()
        plt.plot(X,Y,'.')
        #plt.show()
        gd(e)

结果图像如下:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值