大模型系列6--神经网络(WIP)

1. 背景

为了学习Transformer,我开始投入一部分精力来学习神经网络。这篇文章会持续更新。

文章参考:https://towardsdatascience.com/math-neural-network-from-scratch-in-python-d6da9f29ce65
文章参考:https://blog.csdn.net/ourkix/article/details/134042761
文章参考:https://zhuanlan.zhihu.com/p/589538557
文章参考:https://www.bilibili.com/video/BV16r4y1Y7jv/?p=2&spm_id_from=pageDriver&vd_source=dcc37f900cca3b9bbf065e588e781e14

2. 理论知识

2.1. 单个神经元

2.1.1. 基础

如下图单个神经元

  • 输入为 x 1 x_1 x1 x 2 x_2 x2
  • 经过权重参数和偏置参数得: x 1 ∗ w 1 + x 2 ∗ w 2 + b x_1 * w_1 + x_2 * w_2 + b x1w1+x2w2+b
  • 最后总和经过激活函数得: y = f ( x 1 ∗ w 1 + x 2 ∗ w 2 + b ) y = f(x_1 * w_1 + x_2 * w_2 + b) y=f(x1w1+x2w2+b)
  • 常用的激活函数为 y = 1 1 + e − x y = \frac{1}{1+e^{-x}} y=1+ex1,该激活函数可以将(负无穷,正无穷)映射到(0,1)的范围。

在这里插入图片描述

2.1.2. 神经元激活代码

如下代码定义一个基本的神经元,只实现forward函数

  • 神经元接受初始化参数:weight 和 bias
  • forward函数:进行input和weight进行向量点乘,并sigmoid激活
import numpy as np

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

class Neuron:
    def __init__(self, weight, bias):
        self.weight = weight
        self.bias = bias

    def forward(self, input):
        output = np.dot(input, self.weight) + self.bias
        return sigmoid(output)

weight = [1, 2]
bias = 10
neuron = Neuron(weight, bias)
print(neuron.forward([1, 2])) # sigmoid(1*1 + 2*2 + 10)
print(neuron.forward([-2, -4])) # sigmoid(-2 -8 + 10)

2.2. 多个神经元

2.2.1. 基础

考虑如下图的一个最简单的网络,有两个神经元的隐含层和一个神经元的输出层构成,它有如下公式
h 1 = f ( x 1 ∗ w 1 + x 2 ∗ w 2 + b 1 ) h 2 = f ( x 1 ∗ w 3 + x 2 ∗ w 4 + b 2 ) o 1 = f ( h 1 ∗ w 5 + h 2 ∗ w 6 + b 3 ) h_1 = f(x_1 * w_1 + x_2 * w_2 + b1) \\ h_2 = f(x_1 * w_3 + x_2 * w_4 + b2) \\ o_1 = f(h_1 * w_5 + h_2 * w_6 + b3) h1=f(x1w1+x2w2+b1)h2=f(x1w3+x2w4+b2)o1=f(h1w5+h2w6+b3)
在这里插入图片描述
也就是说,对于输入 x 1 , x 2 x_1, x_2 x1,x2经过相应权重和偏差计算可以得到输出,这个输出和真实值会有差距,如何来衡量这个差距呢?一般我们会使用“均方误差损失MSE”来衡量
L = 1 n ∑ i = 1 n ( o i ′ − o i ) 2 L = \frac{1}{n}\sum_{i=1}^{n}(o_i' - o_i)^2 L=n1i=1n(oioi)2
这里假定 o i o_i oi为真实值, o i ′ o_i' oi为预测值。

2.2.2. 神经元激活代码

通过神经元组合,可以构建一个weight和bias为固定值的小网络

import numpy as np

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

class Neuron:
    def __init__(self, weight, bias):
        self.weight = weight
        self.bias = bias

    def forward(self, input):
        output = np.dot(input, self.weight) + self.bias
        return sigmoid(output)

# x1, x2 -> h1, h2 -> o1
class NeuralNetwork:
    def __init__(self):
        weight = [1, 2]
        bias = 1
        self.h1 = Neuron(weight, bias)
        self.h2 = Neuron(weight, bias)
        self.o1 = Neuron(weight, bias)

    def forward(self, input):
        a = self.h1.forward(input)
        b = self.h2.forward(input)
        return self.o1.forward([a, b])

net = NeuralNetwork()
print(net.forward(np.array([-2, -4]))) # 0.731131354942724
print(sigmoid(-9)) # 0.00012339457598623172
print(sigmoid(0.000123394 * 1 + 0.000123394 * 2 + 1)) # 0.7311313546030448

# a = sigmoid(-9) 0.000123394
# b = sigmoid(-9) 0.000123394
# o = sigmoid(0.000123394 * 1 + 0.000123394 * 2 + 1) # 0.7311313546030448

2.2.3. 反向传播

简单总结下上述的几个公式
h 1 = f ( x 1 ∗ w 1 + x 2 ∗ w 2 + b 1 ) h 2 = f ( x 1 ∗ w 3 + x 2 ∗ w 4 + b 2 ) o 1 = f ( h 1 ∗ w 5 + h 2 ∗ w 6 + b 3 ) L = 1 n ∑ i = 1 n ( o i ′ − o i ) 2 h_1 = f(x_1 * w_1 + x_2 * w_2 + b1) \\ h_2 = f(x_1 * w_3 + x_2 * w_4 + b2) \\ o_1 = f(h_1 * w_5 + h_2 * w_6 + b3) \\ L = \frac{1}{n}\sum_{i=1}^{n}(o_i' - o_i)^2 h1=f(x1w1+x2w2+b1)h2=f(x1w3+x2w4+b2)o1=f(h1w5+h2w6+b3)L=n1i=1n(oioi)2
为了找到最合适的权重和偏置,以最小化 L L L,我们可以采用梯度下降的方案来解决,通过 L L L分别对 w 1 w_1 w1或者 w 2 w_2 w2求偏导,然后使得 w 1 w_1 w1或者 w 2 w_2 w2都向着 L L L变小的方向移动一个小的step。

To be continued

3. 神经网络编程基础

本章节主要来讲解整个逻辑回归从0到1。

3.1. 基本概念

x x x: 表示一个 n x n_x nx维数据,为输入数据,维度为 ( n x , 1 ) (n_x, 1) (nx,1)
y y y:表示输出结果,取值为(0,1);
( x ( i ) , y ( i ) ) (x^{(i)}, y^{(i)}) (x(i),y(i)):表示第 i i i组数据,可能是训练数据,也可能是测试数据,此处默认为训练数据;
X = [ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ] X = [x^{(1)}, x^{(2)}, . . . , x^{(m)}] X=[x(1),x(2),...,x(m)]:表示所有的训练数据集的输入值,放在一个 n x ∗ m n_x * m nxm的矩阵中,其中 m m m表示样本数目;
Y = [ y ( 1 ) , y ( 2 ) , . . . , y ( m ) ] Y = [y^{(1)}, y^{(2)}, . . . , y^{(m)}] Y=[y(1),y(2),...,y(m)]:对应表示所有训练数据集的输出值,维度为 1 ∗ m 1 * m 1m

上述的每个样本都是一个列向量,如 x ( i ) x^{(i)} x(i)的形式,多个样本按照列顺序排布,最终形成矩阵 X X X, 其中 X X X.shape = ( n x , m ) (n_x, m) (nx,m)
在这里插入图片描述

3.2. 逻辑回归

y y y的预测值 y ^ \hat{y} y^,是 y y y的一个估计。准确来说,我们使用 y ^ \hat{y} y^来表达 y y y等于 1 1 1的概率,它是一个0到1的值。举例来说,如果 x x x是一个图片,那么 y ^ \hat{y} y^可以用来表示这张图片是一只猫的概率有多大。我们用 w w w来表示逻辑回归的参数, b b b表示偏移量,构造函数 y ^ = w T ∗ x + b \hat{y} = w^T* x + b y^=wTx+b来进行预测y值。这是线性回归中使用的函数,但是对于二元分类来说,它有个问题。要用 y ^ \hat{y} y^表示 y y y等于 1 1 1的概率值,它在0和1之间,因此我们需要再输出之前再加上一个sigmoid函数,来控制输出范围。

经过修改,逻辑回归的输出函数为
y ^ = σ ( w T x + b ) ,其中 σ ( z ) = 1 1 + e − z \hat{y} = \sigma(w^Tx + b),其中\sigma(z) = \frac{1}{1 + e^{-z}} y^=σ(wTx+b),其中σ(z)=1+ez1

对于给定的数据 { ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , . . . , ( x ( m ) , y ( m ) ) } \{(x^{(1)},y^{(1)}) , (x^{(2)}, y^{(2)}), . . . , (x^{(m)},y^{(m)})\} {(x(1),y(1)),(x(2),y(2)),...,(x(m),y(m))},期望得到 y ^ ≈ y \hat{y} \approx y y^y。为了让模型学习到 w , b w, b w,b参数,需要定义一个损失函数(代价函数)。我们用 L ( y ^ , y ) L(\hat{y}, y) L(y^,y)来表示预测输出值 y ^ \hat{y} y^和真实值 y y y到底有多接近。

一般情况下我们会使用平方误差(预测值和真实值的平差差)来作为损失函数,但在逻辑回归中一般不这样做,这是因为我们的目标不是凸优化,它可能有多个局部最优值,梯度下降可能找不到全局最优值。在逻辑回归任务重,我们一般会使用另外一个损失函数:
L ( y ^ , y ) = − y l o g ( y ^ ) − ( 1 − y ) l o g ( 1 − y ^ ) L(\hat{y}, y) = -ylog(\hat{y}) - (1-y)log(1 - \hat{y}) L(y^,y)=ylog(y^)(1y)log(1y^)
y = 1 y=1 y=1的时候,损失函数简化为 l o g ( y ^ ) log(\hat{y}) log(y^),如果要最小化损失函数 L L L,则需要将 y ^ \hat{y} y^尽可能的大,它的取值范围为0-1,所以 y ^ \hat{y} y^会无限接近于1
y = 0 y=0 y=0的时候,损失函数简化为 − l o g ( 1 − y ^ ) -log(1-\hat{y}) log(1y^),如果要 L L L尽可能的小,需要 y ^ \hat{y} y^尽可能的小,所以 y ^ \hat{y} y^会无限接近于0(注意log函数是增函数,两个负号之后, − l o g ( 1 − y ^ ) -log(1-\hat{y}) log(1y^)还是增函数)

损失函数是基于单个样本来定义的,为了衡量其在全部样本的表现,使用所有样本的损失值均值来衡量,即
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) = 1 m ∑ i = 1 m ( − y ( i ) l o g ( y ^ ( i ) ) − ( 1 − y ( i ) ) l o g ( 1 − y ^ ( i ) ) ) J(w, b) = \frac{1}{m} \sum_{i=1}^{m} {L(\hat{y}^{(i)}, y^{(i)}) } \\ = {\frac{1}{m} \sum_{i=1}^{m} (-y^{(i)}log(\hat{y}^{(i)}) - (1-y^{(i)})log(1 - \hat{y}^{(i)})) } J(w,b)=m1i=1mL(y^(i),y(i))=m1i=1m(y(i)log(y^(i))(1y(i))log(1y^(i)))

在训练模型的时候,通过调整 w , b w,b w,b的值,以使得 J ( w , b ) J(w,b) J(w,b)最小。

3.3. 梯度下降法(Gradient Descent)

3.3.1. 基础知识

在这里插入图片描述
我们以一个单参数的w和b来举例,它的二维图示如下图所示:
在这里插入图片描述
这是一个凸函数,它像一个大碗。我们需要寻找合适 ( w , b ) (w,b) (w,b),以最小化 J ( w , b ) J(w,b) J(w,b)。算法随机初始化一个点,然后随着梯度下降的方向,直到全局最优。因为它是凸函数,不管从哪里初始化,都可以到达全局最优点。
在这里插入图片描述

如果上述函数是非凸函数,则有多个最小值,梯度下降就很容易落入局部最优值。
在这里插入图片描述

3.3.2. 梯度下降的形式化说明

梯度下降的更新逻辑是
w : = w − α d J ( w ) d w w := w - \alpha \frac{dJ(w)}{d w} w:=wαdwdJ(w)
这里的 α \alpha α是学习率, d J ( w ) d w \frac{dJ(w)}{d w} dwdJ(w)为代价函数在 w w w处的梯度值

我们以单 w w w参数的函数来举例,如下图它是一个一元曲线图。下图的右半边是增函数,它的导数大于0;左半边是减函数,它的导数小于0。每次梯度更新是使用 w w w减去一个梯度值,从左边来看,因为导数小于0,则每次减法相当于增加 w w w,从右边来看,因为导数大于0,则每次减法相当于减少 w w w。也就是说,不管 w w w的初始值是在最小值左侧,还是右侧, w w w都始终向着全局最小的位置行进。
在这里插入图片描述
当有多个参数的时候,这里的导数就变成的求偏导,梯度更新是每个参数都沿着梯度下降的方向,即
w : = w − α d J ( w , b ) d w , b : = b − α d J ( w , b ) d b w := w - \alpha \frac{dJ(w,b)}{d w} , b := b - \alpha \frac{dJ(w,b)}{d b} w:=wαdwdJ(w,b),b:=bαdbdJ(w,b)

3.3.3. 复合函数梯度计算图

对于如下的流程,如果我们学过复合函数的导数公式,我们有
在这里插入图片描述
d J d b = d J d v d v d u d u d b = 3 ∗ 1 ∗ c = 3 ∗ 1 ∗ 2 = 6 \frac{dJ}{db} = \frac{dJ}{dv}\frac{dv}{du}\frac{du}{db} = 3 * 1 * c = 3*1*2 = 6 dbdJ=dvdJdudvdbdu=31c=312=6

这如何从微分意义上来大致解答呢?我们采用类似于微分增量的概念,让 b b b增加一个微小量 0.001 0.001 0.001,即从 3 3 3增加到 3.001 3.001 3.001, 我们观察 J J J的增量,即可用来近似表达 d J d b \frac{dJ}{db} dbdJ的值。
在这里插入图片描述

3.3.4. 逻辑回归的梯度下降

回顾前述的公式
y ^ = a = σ ( w T x + b ) ,其中 σ ( z ) = 1 1 + e − z L ( y ^ , y ) = − y l o g ( y ^ ) − ( 1 − y ) l o g ( 1 − y ^ ) J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) \hat{y} = a = \sigma(w^Tx + b),其中\sigma(z) = \frac{1}{1 + e^{-z}} \\ L(\hat{y}, y) = -ylog(\hat{y}) - (1-y)log(1 - \hat{y}) \\ J(w, b) = \frac{1}{m} \sum_{i=1}^{m} {L(\hat{y}^{(i)}, y^{(i)}) } y^=a=σ(wTx+b),其中σ(z)=1+ez1L(y^,y)=ylog(y^)(1y)log(1y^)J(w,b)=m1i=1mL(y^(i),y(i))
我们用 a a a代替 y ^ \hat{y} y^来计算单个样本的代价函数,则 L L L a a a的导数 d L d a \frac{dL}{da} dadL
d L d a = − ( y ∗ 1 a + ( 1 − y ) ∗ 1 1 − a ∗ ( − 1 ) ) = − ( y a + y − 1 1 − a ) = − y − a y + a y − a a ( 1 − a ) = a − y a ( 1 − a ) \begin{align*} \frac{dL}{da} &= -(y * \frac{1}{a} + (1-y) * \frac{1}{1-a} * (-1)) \\ &= -(\frac{y}{a} + \frac{y-1}{1-a}) = -\frac{y-ay + ay - a}{a(1-a)} \\ &= \frac{a-y}{a(1-a)} \\ \end{align*} dadL=(ya1+(1y)1a1(1))=(ay+1ay1)=a(1a)yay+aya=a(1a)ay
a a a z z z的导数 d a d z \frac{da}{dz} dzda
d a d z = − 1 ( 1 + e − z ) 2 ∗ e − z ∗ ( − 1 ) = e − z ∗ 1 ( 1 + e − z ) 2 = 1 − a a ∗ a 2 = a ( 1 − a ) \begin{align*} \frac{da}{dz} &= - \frac{1}{(1+e^{-z})^2} * e^{-z} * (-1) \\ &= e^{-z} * \frac{1}{(1+e^{-z})^2} \\ &= \frac{1-a}{a} * a^2 = a(1-a) \end{align*} dzda=(1+ez)21ez(1)=ez(1+ez)21=a1aa2=a(1a)

则连续求导法 d L d z \frac{dL}{dz} dzdL
d L d z = d L d a ∗ d a d z = a − y a ( 1 − a ) ∗ a ( 1 − a ) = a − y \begin{align*} \frac{dL}{dz} &= \frac{dL}{da} * \frac{da}{dz} \\ &= \frac{a-y}{a(1-a)} * a(1-a) \\ &= a-y \end{align*} dzdL=dadLdzda=a(1a)aya(1a)=ay

假设 w w w是个二元函数,满足 z = w 1 x 1 + w 2 x 2 + b z = w_1x_1 + w_2x_2 + b z=w1x1+w2x2+b。我们继续推导,求 L L L w w w b b b的导数,即计算 d L d w \frac{dL}{dw} dwdL d L d b \frac{dL}{db} dbdL的值:
d L d w 1 = d L d z d z d w 1 = ( a − y ) ∗ x 1 d L d w 2 = d L d z d z d w 2 = ( a − y ) ∗ x 2 d L d b = d L d z d z d b = ( a − y ) \begin{align*} \frac{dL}{dw_1} &= \frac{dL}{dz}\frac{dz}{dw_1} = (a-y) * x_1 \\ \frac{dL}{dw_2} &= \frac{dL}{dz}\frac{dz}{dw_2} = (a-y) * x_2 \\ \frac{dL}{db} &= \frac{dL}{dz}\frac{dz}{db} = (a-y) \end{align*} dw1dLdw2dLdbdL=dzdLdw1dz=(ay)x1=dzdLdw2dz=(ay)x2=dzdLdbdz=(ay)

3.3.5. m m m个样本的梯度下降

上述是单个样本的梯度下降,我们现在计算 m m m个样本的梯度下降,再次回顾 m m m个样本的损失函数
J ( w , b ) = 1 m ∑ i = 1 m L ( a ( i ) , y ( i ) ) J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(a^{(i)}, y^{(i)}) J(w,b)=m1i=1mL(a(i),y(i))
则求对 w 1 w_1 w1的偏导为
d J ( w , b ) d w 1 = 1 m ∑ i = 1 m d L ( a ( i ) , y ( i ) ) d w 1 1 m ∑ i = 1 m ( a ( i ) − y ( i ) ) ∗ x 1 ( i ) \frac{dJ(w,b)}{dw_1} = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{dw_1} \\ \frac{1}{m} \sum_{i=1}^{m}(a^{(i)}-y^{(i)})*x_1^{(i)} dw1dJ(w,b)=m1i=1mdw1dL(a(i),y(i))m1i=1m(a(i)y(i))x1(i)
后续为了简化起见,在某些地方会用 d w 1 dw_1 dw1来表示 d J ( w , b ) d w 1 \frac{dJ(w,b)}{dw_1} dw1dJ(w,b)

梯度下降算法
偷懒一把,直接从视频中截图出梯度下降算法的实现。其实通过前述的推导,我们已经明确知道了 d J ( w , b ) d w 1 \frac{dJ(w,b)}{dw_1} dw1dJ(w,b)是如何计算的,也就不难得到如下的算法迭代公式。

它的思想很简单

  • 初始化损失和梯度都为0(为后续的sum求和作准备)
  • 根据前述 J ( w , b ) J(w,b) J(w,b)的计算公式,计算出 J ( w , b ) J(w,b) J(w,b)
  • 然后应用前述的梯度计算方法,计算出 d J ( w , b ) d w 1 \frac{dJ(w,b)}{dw_1} dw1dJ(w,b)的值,这里的损失求和和梯度求和是在遍历样本的过程中进行累加的
  • 最后遍历完所有样本求均值
  • 在得到前述的梯度和之后,应用梯度下降的更新公式,代入学习率,即可得到新的 w w w b b b
    在这里插入图片描述

3.3.6. 梯度下降向量化

上述梯度计算的是通过两层for循环迭代,另外一种思路是采用矩阵向量运算,这样可以更好利用并行化优化。

向量化和非向量化的代码耗时

import numpy as np
import time
a = np.random.rand(1000000)
b = np.random.rand(1000000)

tic = time.time()
c = np.dot(a, b)
toc = time.time()
print(c)
print("Vectorized version:"+ str(1000*(toc-tic))+"ms")

c = 0
tic = time.time()
for i in range(1000000):
    c += a[i]*b[i]
toc = time.time()
print(c)
print("For loop:"+ str(1000*(toc-tic))+ "ms")

输出结果

250121.78770186222
Vectorized version:23.240327835083008ms
250121.7877018585
For loop:487.37192153930664ms

可以看到两者有20多倍的耗时差,这得益于向量化的能力,CPU一般可以很容易使用SIMD指令来加速矩阵运算。想写循环时候,检查 numpy 是否存在类似的内置函数,从而避免使用循环(loop)方式。

我们首先使用向量化来优化前向传播的一些计算
已知
z ( 1 ) = ( w T x ( 1 ) + b ) , z ( 2 ) = ( w T x ( 2 ) + b ) , z ( 3 ) = ( w T x ( 3 ) + b ) z^{(1)} = (w^Tx^{(1)} + b), z^{(2)} = (w^Tx^{(2)} + b), z^{(3)} = (w^Tx^{(3)} + b) z(1)=(wTx(1)+b),z(2)=(wTx(2)+b),z(3)=(wTx(3)+b)
从而,我们有
Z = [ z ( 1 ) , z ( 2 ) , . . . , z ( m ) ] = [ ( w T x ( 1 ) + b ) , ( w T x ( 2 ) + b ) , . . . , ( w T x ( m ) + b ) ] = [ w T x ( 1 ) , w T x ( 2 ) , . . . , w T x ( m ) ] + [ b , b , . . . . , b ] = w T [ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ] + [ b , b , . . . . , b ] \begin{align*} Z &= [z^{(1)}, z^{(2)}, ..., z^{(m)}] \\ &= [(w^Tx^{(1)} + b), (w^Tx^{(2)} + b), ..., (w^Tx^{(m)} + b)] \\ &= [w^Tx^{(1)}, w^Tx^{(2)}, ..., w^Tx^{(m)}] + [b,b,....,b]\\ &= w^T[x^{(1)}, x^{(2)}, ..., x^{(m)}]+[b,b,....,b] \end{align*} Z=[z(1),z(2),...,z(m)]=[(wTx(1)+b),(wTx(2)+b),...,(wTx(m)+b)]=[wTx(1),wTx(2),...,wTx(m)]+[b,b,....,b]=wT[x(1),x(2),...,x(m)]+[b,b,....,b]
显而易见,我们可以定义一个输入矩阵 X = [ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ] X = [x^{(1)}, x^{(2)}, ..., x^{(m)}] X=[x(1),x(2),...,x(m)],它是一个 n ∗ m n*m nm的矩阵,其中 n n n为单个样本的维度, m m m为样本个数,它的每列是一个样本,则
Z = w T X + [ b , b , . . . . , b ] Z = w^TX+[b,b,....,b] Z=wTX+[b,b,....,b]
在代码实现中,我们可以写 Z = n p . d o t ( w T , X ) + b Z = np.dot(w^T, X) + b Z=np.dot(wT,X)+b,用向量加上实数 b b b,python会自动将 b b b进行展开成向量,那么关于激活值 a a a呢?

我们已知
a ( 1 ) = σ ( z ( 1 ) ) , a ( 2 ) = σ ( z ( 2 ) ) , a ( 3 ) = σ ( z ( 3 ) ) a^{(1)} = \sigma(z^{(1)}), a^{(2)} = \sigma(z^{(2)}), a^{(3)} = \sigma(z^{(3)}) a(1)=σ(z(1)),a(2)=σ(z(2)),a(3)=σ(z(3))
则激活值向量为
A = [ a ( 1 ) , a ( 2 ) , . . . , a ( m ) ] = [ σ ( z ( 1 ) ) , σ ( z ( 2 ) ) , . . . , σ ( z ( m ) ) ] = σ ( Z ) \begin{align*} A &= [a^{(1)}, a^{(2)}, ..., a^{(m)}] \\ &= [\sigma(z^{(1)}), \sigma(z^{(2)}), ..., \sigma(z^{(m)})] \\ &=\sigma(Z) \end{align*} A=[a(1),a(2),...,a(m)]=[σ(z(1)),σ(z(2)),...,σ(z(m))]=σ(Z)

再次回忆代价函数 J ( w , b ) J(w,b) J(w,b),将它使用激活值来表示,则
J ( w , b ) = 1 m ∑ i = 1 m − y ( i ) l o g a ( i ) − ( 1 − y ( i ) ) l o g ( 1 − a ( i ) ) J(w, b) = \frac{1}{m} \sum_{i=1}^{m} {-y^{(i)}loga^{(i)} -(1-y^{(i)})log(1-a^{(i)})} J(w,b)=m1i=1my(i)loga(i)(1y(i))log(1a(i))
若令 Y = [ y ( 1 ) , y ( 2 ) , . . . , y ( m ) ] Y=[y^{(1)}, y^{(2)}, ..., y^{(m)}] Y=[y(1),y(2),...,y(m)],则
J ( w , b ) = 1 m ∑ i = 1 m − y ( i ) l o g a ( i ) − ( 1 − y ( i ) ) l o g ( 1 − a ( i ) ) = − Y T l o g ( A ) − ( 1 − Y ) T l o g ( 1 − A ) \begin{align*} J(w, b) &= \frac{1}{m} \sum_{i=1}^{m} {-y^{(i)}loga^{(i)} -(1-y^{(i)})log(1-a^{(i)})} \\ &=-Y^Tlog(A)-(1-Y)^Tlog(1-A) \end{align*} J(w,b)=m1i=1my(i)loga(i)(1y(i))log(1a(i))=YTlog(A)(1Y)Tlog(1A)

其次,我们使用向量化来优化两层for循环的梯度下降算法
回顾一下梯度计算算法的流程:
在这里插入图片描述
我们用 d Z = [ d z ( 1 ) , d z ( 2 ) , . . . , d z ( m ) ] dZ = [dz^{(1)}, dz^{(2)}, ..., dz^{(m)} ] dZ=[dz(1),dz(2),...,dz(m)],则我们有
d Z = [ d z ( 1 ) , d z ( 2 ) , . . . , d z ( m ) ] = [ a ( 1 ) − y ( 1 ) , a ( 2 ) − y ( 2 ) , . . . , a ( m ) − y ( m ) ] = [ a ( 1 ) , a ( 2 ) , . . . , a ( m ) ] − [ y ( 1 ) , y ( 2 ) , . . . , y ( m ) ] = A − Y \begin{align*} dZ &= [dz^{(1)}, dz^{(2)}, ..., dz^{(m)} ] \\ & = [a^{(1)} - y^{(1)}, a^{(2)} - y^{(2)}, ..., a^{(m)} - y^{(m)} ] \\ &= [a^{(1)}, a^{(2)}, ..., a^{(m)}] - [y^{(1)}, y^{(2)}, ..., y^{(m)}] \\ & = A - Y \end{align*} dZ=[dz(1),dz(2),...,dz(m)]=[a(1)y(1),a(2)y(2),...,a(m)y(m)]=[a(1),a(2),...,a(m)][y(1),y(2),...,y(m)]=AY
梯度更新的计算公式使用 d z ( i ) dz^{(i)} dz(i)来表示的话,则为
d w 1 = 1 m ∑ i = 1 m d L ( a ( i ) , y ( i ) ) d w 1 = 1 m ∑ i = 1 m x 1 ( i ) ∗ d z ( i ) d w 2 = 1 m ∑ i = 1 m d L ( a ( i ) , y ( i ) ) d w 2 = 1 m ∑ i = 1 m x 2 ( i ) ∗ d z ( i ) d b = 1 m ∑ i = 1 m d L ( a ( i ) , y ( i ) ) d b = 1 m ∑ i = 1 m d z ( i ) dw_1 = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{dw_1} = \frac{1}{m} \sum_{i=1}^{m} {x_1^{(i)} * dz^{(i)}} \\ dw_2 = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{dw_2} = \frac{1}{m} \sum_{i=1}^{m} {x_2^{(i)} * dz^{(i)} } \\ db = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{db} = \frac{1}{m} \sum_{i=1}^{m} { dz^{(i)}} dw1=m1i=1mdw1dL(a(i),y(i))=m1i=1mx1(i)dz(i)dw2=m1i=1mdw2dL(a(i),y(i))=m1i=1mx2(i)dz(i)db=m1i=1mdbdL(a(i),y(i))=m1i=1mdz(i)
d b = 1 m ∗ n p . s u m ( d Z ) db = \frac{1}{m} * np.sum(dZ) db=m1np.sum(dZ)

接下来,我们来求 d w dw dw,由于 w w w是列向量,为了便于梯度更新时候的矩阵运算,我们令 d w = [ d w 1 , d w 2 , . . . , d w n ] T dw = [dw_1, dw_2, ..., dw_n]^T dw=[dw1,dw2,...,dwn]T,也是一个列向量,则 d w dw dw可以表示为:
d w = [ d w 1 d w 2 ⋮ d w n ] = 1 m ∗ [ x 1 ( 1 ) x 1 ( 2 ) ⋯ x 1 ( m ) x 2 ( 1 ) x 2 ( 2 ) ⋯ x 2 ( m ) ⋮ ⋮ ⋮ ⋮ x n ( 1 ) x n ( 2 ) ⋯ x n ( m ) ] ∗ [ d z ( 1 ) d z ( 2 ) ⋮ d z ( m ) ] = 1 m ∗ [ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ] ∗ [ d z ( 1 ) d z ( 2 ) ⋮ d z ( m ) ] \begin{align*} dw &= \left[ \begin{matrix} dw_1 \\ dw_2 \\ \vdots \\ dw_n \\ \end{matrix}\right] \\ &= \frac{1}{m} * \left[ \begin{matrix} x_1^{(1)} & x_1^{(2)} & \cdots & x_1^{(m)} \\ x_2^{(1)} & x_2^{(2)} & \cdots & x_2^{(m)} \\ \vdots & \vdots & \vdots & \vdots \\ x_n^{(1)} & x_n^{(2)} & \cdots & x_n^{(m)} \\ \end{matrix} \right] * \left[ \begin{matrix} dz^{(1)} \\ dz^{(2)} \\ \vdots\\ dz^{(m)} \\ \end{matrix} \right] \\ &= \frac{1}{m} * [x^{(1)}, x^{(2)}, ..., x^{(m)}]*\left[ \begin{matrix} dz^{(1)} \\ dz^{(2)} \\ \vdots\\ dz^{(m)} \\ \end{matrix} \right] \end{align*} dw= dw1dw2dwn =m1 x1(1)x2(1)xn(1)x1(2)x2(2)xn(2)x1(m)x2(m)xn(m) dz(1)dz(2)dz(m) =m1[x(1),x(2),...,x(m)] dz(1)dz(2)dz(m)
回忆一下前述输入矩阵 X X X Z Z Z的表示形式, d w dw dw可以简化为
d w = 1 m ∗ X ∗ d Z T dw = \frac{1}{m} * X * dZ^T dw=m1XdZT
这可以使用代码 d w = 1 m ∗ n p . d o t ( X , d Z T ) dw = \frac{1}{m} * np.dot(X, dZ^T) dw=m1np.dot(X,dZT)。我们也可以将其写成样本展开的形式为
d w = 1 m ( x ( 1 ) d z ( 1 ) + x ( 2 ) d z ( m ) + . . . + x ( m ) d z ( m ) ) dw = \frac{1}{m} (x^{(1)}dz^{(1)} + x^{(2)}dz^{(m)} + ... + x^{(m)}dz^{(m)}) dw=m1(x(1)dz(1)+x(2)dz(m)+...+x(m)dz(m))

经过上述的矩阵简化之后,我们的前向和反向传播的公式可以简化为
Z = w T X + [ b , b , . . . . , b ] = n p . d o t ( w T , X ) + b A = [ a ( 1 ) , a ( 2 ) , . . . , a ( m ) ] = σ ( Z ) J ( w , b ) = − Y T l o g ( A ) − ( 1 − Y ) T l o g ( 1 − A ) d Z = [ d z ( 1 ) , d z ( 2 ) , . . . , d z ( m ) ] = A − Y d w = 1 m ∗ X ∗ d Z T = 1 m ∗ n p . d o t ( X , d Z T ) d b = 1 m ∗ n p . s u m ( d Z ) w : = w − α ∗ d w b : = b − α ∗ d b \begin{align*} Z &= w^TX+[b,b,....,b] = np.dot(w^T, X) + b \\ A&= [a^{(1)}, a^{(2)}, ..., a^{(m)}] =\sigma(Z) \\ J(w, b) &=-Y^Tlog(A)-(1-Y)^Tlog(1-A) \\ dZ &= [dz^{(1)}, dz^{(2)}, ..., dz^{(m)} ] = A - Y \\ dw &= \frac{1}{m} * X * dZ^T = \frac{1}{m} * np.dot(X, dZ^T) \\ db &= \frac{1}{m} * np.sum(dZ) \\ w :&= w - \alpha * dw \\ b :&= b - \alpha * db \end{align*} ZAJ(w,b)dZdwdbw:b:=wTX+[b,b,....,b]=np.dot(wT,X)+b=[a(1),a(2),...,a(m)]=σ(Z)=YTlog(A)(1Y)Tlog(1A)=[dz(1),dz(2),...,dz(m)]=AY=m1XdZT=m1np.dot(X,dZT)=m1np.sum(dZ)=wαdw=bαdb
利用前几个公式实现前向和反向传播,利用最后两个公式完成梯度更新,上述的公式没有任何for循环,全是向量运算,足够高效。

对应python代码

def sigmoid(z):
	return 1/(1 + np.exp(-z))

def compute gradient(X,y,w, b):
	m=X.shape[1]
	z=np.dot(w.T,X)+b
	a= sigmoid(z)
	dw=(1/m)*np.dot(X, (a-y).T)
	db =(1/m)*np.sum(a-y)
	return dw, db

3.4. 逻辑回归的数学解释

3.4.1. 单样本损失函数的来由

对于给定的一个样本 ( x , y ) (x, y) (x,y),对于 x x x,逻辑回归中的预测结果 y ^ \hat{y} y^表示如下
y ^ = σ ( w T x + b ) ,其中 σ ( z ) = 1 1 + e − z \hat{y} = \sigma(w^Tx + b),其中\sigma(z) = \frac{1}{1 + e^{-z}} y^=σ(wTx+b),其中σ(z)=1+ez1
这里 y ^ \hat{y} y^表示 y = 1 y=1 y=1的概率,它的输出值在 [ 0 , 1 ] [0,1] [0,1]之间。由于这是一个二分类问题,真实值 y y y只能取值 0 0 0或者 1 1 1,所以我们有:
p ( y = 1 ∣ x ) = y ^ p ( y = 0 ∣ x ) = 1 − y ^ \begin{align*} p(y=1|x) &= \hat{y} \\ p(y=0|x) &= 1 - \hat{y} \end{align*} p(y=1∣x)p(y=0∣x)=y^=1y^

上述公式是两个式子,为了简便计算,我们将其合并为一个式子来表达,即
p ( y ∣ x ) = y ^ y ∗ ( 1 − y ^ ) ( 1 − y ) p(y|x) = \hat{y}^y * (1-\hat{y})^{(1-y)} p(yx)=y^y(1y^)(1y)
我们可以代入 y = 1 y=1 y=1,满足 p ( y = 1 ∣ x ) = y ^ p(y=1|x) = \hat{y} p(y=1∣x)=y^;代入 y = 0 y=0 y=0,满足 p ( y = 0 ∣ x ) = 1 − y ^ p(y=0|x) = 1 - \hat{y} p(y=0∣x)=1y^

对于给定的一个样本 ( x , y ) (x,y) (x,y),我们的目标是最大化 p ( y ∣ x ) p(y|x) p(yx),它等价于最大化 l o g ( p ( y ∣ x ) ) log(p(y|x)) log(p(yx)),这是因为 l o g log log函数是严格递增的函数,故而我们目标是最大化
l o g ( ( p ( y ∣ x ) ) = l o g ( y ^ y ∗ ( 1 − y ^ ) ( 1 − y ) ) = y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) log((p(y|x)) = log(\hat{y}^y * (1-\hat{y})^{(1-y)}) = ylog\hat{y} + (1-y)log(1-\hat{y}) log((p(yx))=log(y^y(1y^)(1y))=ylogy^+(1y)log(1y^)

回想前述的损失函数的定义:
L ( y ^ , y ) = − y l o g ( y ^ ) − ( 1 − y ) l o g ( 1 − y ^ ) L(\hat{y}, y) = -ylog(\hat{y}) - (1-y)log(1 - \hat{y}) L(y^,y)=ylog(y^)(1y)log(1y^)
所以我们有
l o g ( ( p ( y ∣ x ) ) = − L ( y ^ , y ) log((p(y|x)) = -L(\hat{y}, y) log((p(yx))=L(y^,y)
考虑到损失函数前面有个负号,因此最大化输出概率值 p ( y ∣ x ) p(y|x) p(yx)等价于最小化损失函数 L ( y ^ , y ) L(\hat{y},y) L(y^,y),这是单个样本的损失函数表达式。

3.4.2. 推广到多样本损失函数

对于有 m m m个样本,我们的目标是寻找参数 ( w , b ) (w,b) (w,b),以最大化联合概率函数
P ( y ( 1 ) , y ( 2 ) , . . . , y ( m ) ∣ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ) P({y}^{(1)},{y}^{(2)},...,{y}^{(m)}|x^{(1)}, x^{(2)}, ..., x^{(m)}) P(y(1),y(2),...,y(m)x(1),x(2),...,x(m))
假设所有的样本满足独立同分布的特性,则联合概率密度函数等于所有样本概率密度函数的乘积,即
P ( y ( 1 ) , y ( 2 ) , . . . , y ( m ) ∣ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ) = p ( y ( 1 ) ∣ x ( 1 ) ) ∗ p ( y ( 2 ) ∣ x ( 2 ) ) ∗ . . . ∗ p ( y ( m ) ∣ x ( m ) ) = ∏ i = 1 m p ( y ( i ) ∣ x ( i ) ) \begin{align*} P({y}^{(1)},{y}^{(2)},...,{y}^{(m)}|x^{(1)}, x^{(2)}, ..., x^{(m)}) &= p({y}^{(1)} | x^{(1)}) * p({y}^{(2)} | x^{(2)}) * ... * p({y}^{(m)} | x^{(m)}) \\ & = \prod_{i=1}^m p({y}^{(i)} | x^{(i)}) \end{align*} P(y(1),y(2),...,y(m)x(1),x(2),...,x(m))=p(y(1)x(1))p(y(2)x(2))...p(y(m)x(m))=i=1mp(y(i)x(i))
同样利用 l o g log log的特性,两边求对数,以将乘积变成加法,则等同于最大化:
l o g ( ∏ i = 1 m p ( y ( i ) ∣ x ( i ) ) ) = ∑ i = 1 m l o g ( p ( y ( i ) ∣ x ( i ) ) ) = − ∑ i = 1 m L ( y ^ , y ) log(\prod_{i=1}^m p({y}^{(i)} | x^{(i)})) = \sum_{i=1}^{m}log(p({y}^{(i)} | x^{(i)})) = - \sum_{i=1}^{m} L(\hat{y}, y) log(i=1mp(y(i)x(i)))=i=1mlog(p(y(i)x(i)))=i=1mL(y^,y)
由于上面的式子最右边有个负号,因此最大化联合概率密度函数等同于最小化
∑ i = 1 m L ( y ^ , y ) \sum_{i=1}^{m} L(\hat{y}, y) i=1mL(y^,y)
前面乘以系数 1 m \frac{1}{m} m1,这就等同于我们的前述所述的多样本损失函数的定义
J ( w , b ) = 1 m ∑ i = 1 m L ( a ( i ) , y ( i ) ) J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(a^{(i)}, y^{(i)}) J(w,b)=m1i=1mL(a(i),y(i))

3.4.3. 反向思考一轮

我们期望找到参数 ( w , b ) (w,b) (w,b),它能使得 m m m个样本出现的概率最大,即最大化这些样本的联合概率密度函数
P ( y ( 1 ) , y ( 2 ) , . . . , y ( m ) ∣ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ; w , b ) P({y}^{(1)},{y}^{(2)},...,{y}^{(m)}|x^{(1)}, x^{(2)}, ..., x^{(m)}; w,b) P(y(1),y(2),...,y(m)x(1),x(2),...,x(m);w,b)

这种是我们概率论中的常考题型,参考最大似然估计一章,它详细解释了如何依赖样本值来计算出 w w w b b b

联合概率的公式不容易计算,我们一般认为样本之间满足独立同分布的特性,这就可以将联合概率密度写成单样本概率密度函数的乘积。
p ( y ( 1 ) ∣ x ( 1 ) ) ∗ p ( y ( 2 ) ∣ x ( 2 ) ) ∗ . . . ∗ p ( y ( m ) ∣ x ( m ) ) = ∏ i = 1 m p ( y ( i ) ∣ x ( i ) ) p({y}^{(1)} | x^{(1)}) * p({y}^{(2)} | x^{(2)}) * ... * p({y}^{(m)} | x^{(m)})= \prod_{i=1}^m p({y}^{(i)} | x^{(i)}) p(y(1)x(1))p(y(2)x(2))...p(y(m)x(m))=i=1mp(y(i)x(i))

概率密度的乘积同样很难计算,我们进一步通过 l o g log log对数的形式,将其转换为加法运算,即最大化
∑ i = 1 m l o g ( p ( y ( i ) ∣ x ( i ) ) ) \sum_{i=1}^{m}log(p({y}^{(i)} | x^{(i)})) i=1mlog(p(y(i)x(i)))

在概率论书籍中,到达这一步之后,我们会根据题目中给出的样本概率分布,将概率密度函数代入到 p ( y ( i ) ∣ x ( i ) ) p({y}^{(i)} | x^{(i)}) p(y(i)x(i))中,进行化简。对于本文的二元预测来说,我们定义了 y ^ \hat{y} y^ p ( y = 1 ∣ x ) p(y=1|x) p(y=1∣x),进而可以得到二元样本 ( x , y ) (x,y) (x,y)的概率分布函数(对于离散型随机变量 y y y,我们通常称之为概率分布函数)为 p ( y ∣ x ) = y ^ y ∗ ( 1 − y ^ ) ( 1 − y ) p(y|x) =\hat{y}^y * (1-\hat{y})^{(1-y)} p(yx)=y^y(1y^)(1y),代入到上面的式子,即最大化
∑ i = 1 m ( y ( i ) l o g y ^ ( i ) + ( 1 − y ( i ) ) l o g ( 1 − y ^ ( i ) ) ) \sum_{i=1}^{m}( y^{(i)}log\hat{y}^{(i)} + (1-y^{(i)})log(1-\hat{y}^{(i)}) ) i=1m(y(i)logy^(i)+(1y(i))log(1y^(i)))

又因为我们的 y ^ = σ ( w T x + b ) \hat{y} = \sigma(w^Tx+b) y^=σ(wTx+b),则可以进一步代入上式,即最大化
∑ i = 1 m ( y ( i ) l o g σ ( w T x ( i ) + b ) + ( 1 − y ( i ) ) l o g ( 1 − σ ( w T x ( i ) + b ) ) ) \sum_{i=1}^{m}( y^{(i)}log \sigma(w^Tx^{(i)}+b) + (1-y^{(i)})log(1- \sigma(w^Tx^{(i)}+b)) ) i=1m(y(i)logσ(wTx(i)+b)+(1y(i))log(1σ(wTx(i)+b)))

再重复一遍,我们的目标是求出参数 ( w , b ) (w,b) (w,b),以使得上述公式的值最大。对于概率论的书籍中,这个时候,我们会将上述公式对 w w w b b b分别求偏导,使得所有的偏导值为0,解这个多元方程组,就可以使用样本值来表达 w w w b b b。令 P P P代表上述的公式,我们有
∂ P ∂ w 1 = 0 ∂ P ∂ w 2 = 0 ∂ P ∂ b = 0 \begin{align*} \frac{\partial{P}}{\partial w_1} &= 0 \\ \frac{\partial{P}}{\partial w_2} &= 0 \\ \frac{\partial{P}}{\partial b} &= 0 \end{align*} w1Pw2PbP=0=0=0

想一想我们梯度下降在做的事情,我们初始化 ( w , b ) (w,b) (w,b)之后通过不断梯度下降的迭代,使得代价函数 J ( w , b ) J(w,b) J(w,b)最小。当其最小的时候,其实就是上述的各个偏导数为0。

4. Python编程基础

4.1. numpy广播

  • 矩阵按列求和
import numpy as np
A = np.array([[56.0,0.0,4.4,68.0],
              [1.2,104.0,52.0,8.0],
              [1.8,135.0,99.0,0.9]])
print(A)

## 按列求和
sumA = A.sum(axis=0)
print(sumA)  # 输出 [ 59.  239.  155.4  76.9]
  • 矩阵除法广播
## 求各个列中不同元素的占比
print(A/sumA)

输出
[[0.94915254 0.         0.02831403 0.88426528]
 [0.02033898 0.43514644 0.33462033 0.10403121]
 [0.03050847 0.56485356 0.63706564 0.01170351]]

一个 m ∗ n m*n mn的矩阵除以 1 ∗ m 1*m 1m的矩阵,相当于 m ∗ n m*n mn的矩阵的每一行都和该行向量对应元素相除。与之所类比,我们同样可以进行加、减、乘运算。

  • 矩阵加上一个实数
## 4.2. 每个元素都加上100
print(A+100)

输出
[[156.  100.  104.4 168. ]
 [101.2 204.  152.  108. ]
 [101.8 235.  199.  100.9]]
  • 维度延展
    广播机制首先需要判断参与计算的两个数组能否被广播机制处理?即判断是否广播兼容,规则是,比较两个数组的shape,从shape的尾部开始一一比对
    • (1) 如果两个数组的维度相同,从后向前逐个维进行比对,若轴长度相同或者其中一方的长度为1,则广播兼容:例如 m ∗ n m*n mn 1 ∗ n 1*n 1n,它们都是2维的,从后向前比对,先是 n n n,两个轴长度相同;其次是 m m m 1 1 1,其中一方为 1 1 1,故而满足广播兼容。
    • (2) 如果两个数组的维度不同,那么给低维度的数组前扩展提升一维,且扩展维的轴长度为1,然后继续形同(1)进行处理:例如 k ∗ m ∗ n k*m*n kmn 1 ∗ n 1*n 1n,它们的维度不同,需要先给 1 ∗ n 1*n 1n扩展一个维度变成 1 ∗ 1 ∗ n 1*1*n 11n,然后按照方法(1)进行处理。
      在这里插入图片描述

4.2. 使用明确的矩阵维度表示

使用明确的维度表示,不要使用一维数组,转向使用 n ∗ 1 n*1 n1维矩阵(基本上是列向量),或者 1 ∗ n 1 * n 1n 维矩阵(基本上是行向量)。

4.3 搭建神经网络

在layer L这一层中,输入是 a [ l − 1 ] a^{[l-1]} a[l1],经过权重参数 W [ l ] , b [ l ] W^{[l]}, b^{[l]} W[l],b[l],得到 z [ l ] z^{[l]} z[l]缓存下来,最后输出是 a [ l ] a^{[l]} a[l]。当反向传播的时候,从 d a [ l ] da^{[l]} da[l]经过 d z dz dz,最后到达 d a [ l − 1 ] da^{[l-1]} da[l1]

a [ 0 ] a^{[0]} a[0] a [ L ] a^{[L]} a[L],我们持续的经过上述计算,最后第 L L L层输出值为 y ^ \hat{y} y^,在这期间我们缓存了所有的 z [ l ] z^{[l]} z[l]值。
在这里插入图片描述

在这里插入图片描述

深度学习领域,有很多超参数,例如算法的学习率 α \alpha α,迭代次数,网络层数L,每层的单元数目等。应用深度学习领域,一个很大程度基于经验的过程。特别是刚开始应用于新问题的人们,去试一定范围的值看看结果如何,甚至那些用了很久的模型得学习率或是其他超参数的最优值
也有可能会改变。

超参数调试、正则化、优化

超参数调试

在机器学习中,我们通常将样本分成训练集,验证集和测试集三部分,数
据集规模相对较小,适用传统的划分比例,例如60%, 20%, 20%。数据集规模较大的,验证集和测试集要小于数据总量的 20%或 10%,例如百万条数据,验证集和测试集各1万,对应98%,1%,1%的比例。

要确保验证集和测试集的数据来自同一分布。
验证集不像训练集和测试集,它是非必需的。如果不需要调整超参数,就可以不使用验证集,直接用测试集来评估效果。
验证集评估出来的效果并非模型的最终效果,主要是用来调整超参数的,模型最终效果以测试集的评估结果为准。

偏差和方差

在这里插入图片描述

  • 训练集误差是 1%,验证集误差是 11%:训练数据数据过拟合,高方差
  • 训练集误差是 15%,验证集误差是 16%:算法偏差比较高,数据欠拟合
  • 训练集误差是 15%,验证集误差是30%:偏差相当高,但是,验证集的评估结果更糟糕,方差偏差都很糟糕的
  • 训练集误差是 0.5%,验证集误差是 1%:偏差和方差都很低。

这里面有个前提假设是“人眼进行识别几乎不会出错”,也就是说最优误差接近0%。最优误差也被称为贝叶斯误差。如果图片很模糊,即使是人眼,
或者没有系统可以准确无误地识别图片,在这种情况下,最优误差会更高。

如果训练集误差很高,那可能是欠拟合,偏差高;如果训练集误差低,但是验证集误差高,过拟合了训练数据,方差高。

  • 首先判断偏差高不高,如果偏差高,训练集都无法拟合,要做的就是选择一个新的网络,改变一些超参数值等。这可能有用,也可能没用,但是一定要不断尝试,直到解决偏差问题。至少能够拟合训练集,这是最低标准。
  • 只要图片的基本误差不高(即图片不是特别模糊到人也看不清楚),增大网络可以很好的拟合训练集,训练集偏差降低到可以接受的值,需要检查一下方差有没有问题。如果方差高,最好的办法是增加更多的数据,如果无法办到,可以尝试正则化来减少过拟合。

在深度学习的早期阶段,我们没有太多工具可以做到只减少偏差或方差却不影响到另一方。但在当前的深度学习和大数据时代

  • 只要正则适度,通常构建一个更大的网络便可以,在不影响方差的同时减少偏差
  • 采用更多数据通常可以在不过多影响偏差的同时减少方差

正则化

神经网络代价函数
J ( W [ 1 ] , b [ 1 ] , W [ 2 ] , b [ 2 ] , . . . , W [ L ] , b [ L ] ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) + λ 2 m ∑ l = 1 L ∣ ∣ W l ∣ ∣ F 2 J(W^{[1]}, b^{[1]}, W^{[2]}, b^{[2]}, ..., W^{[L]}, b^{[L]}) = \frac{1}{m}\sum_{i=1}^{m}L(\hat{y}^{(i)}, y^{(i)}) + \frac{\lambda}{2m}\sum_{l=1}^{L}||W^{l}||_F^2 J(W[1],b[1],W[2],b[2],...,W[L],b[L])=m1i=1mL(y^(i),y(i))+2mλl=1L∣∣WlF2
其中
∣ ∣ W l ∣ ∣ F 2 = ∑ i = 1 n [ L ] ∑ j = 1 n [ L − 1 ] ( W i j ) 2 W 矩阵的维度为 ( n [ L ] , n [ L − 1 ] ) ||W^{l}||_F^2 = \sum_{i=1}^{n^{[L]}} \sum_{j=1}^{n^{[L-1]}} (W_{ij})^2 \\ W矩阵的维度为(n^{[L]}, n^{[L-1]}) ∣∣WlF2=i=1n[L]j=1n[L1](Wij)2W矩阵的维度为(n[L],n[L1])

借用一个网站的图片,可以比较方便的辨识出W矩阵的维度为 W [ 1 ] W^{[1]} W[1]的形状为 4 ∗ 3 4*3 43,即 n [ 1 ] ∗ n [ 0 ] n^{[1]} * n^{[0]} n[1]n[0]。当前层的每个神经元都需要和前一层的输入 x x x相乘,故而每个神经元是 1 ∗ 3 1*3 13,当前1层有4个神经元,则构建成矩阵则为 4 ∗ 3 4*3 43
在这里插入图片描述
对上述函数对W求偏导,得到相应的参数迭代公式
W [ L ] = W [ L ] − α ∗ ( f r o m _ b a c k _ p r o g + λ m W [ L ] ) = ( 1 − λ α m ) W [ L ] − α ∗ f r o m _ b a c k _ p r o g W^{[L]} = W^{[L]} - \alpha * (from\_back\_prog + \frac{\lambda}{m} W^{[L]}) \\ = (1 - \frac{\lambda \alpha}{m} )W^{[L]} - \alpha * from\_back\_prog W[L]=W[L]α(from_back_prog+mλW[L])=(1mλα)W[L]αfrom_back_prog
有点权重衰减的意味,所以𝐿2正则化也被称为“权重衰减”。

如果正则化𝜆设置得足够大,权重矩阵𝑊被设置为接近于 0 的值,直观理解就是把多隐藏单元的权重设为 0,于是基本上消除了这些隐藏单元的许多影响,会变成一个很小的网络,小到如同一个逻辑回
归单元,可是深度却很大,它会使这个网络从过度拟合的状态更接近左图的高偏差状态。

  • W W W很小的时候, z z z的取值范围也很小,对于tanh激活函数来说,曲线会相对呈线性(双曲正切在 z z z取值小的时候呈现线性特性),整个神经网络会计算离线性函数近的值。我们知道如果每层都是线性的,那么最终网络不管多复杂,都只是一个线性网络。
    在这里插入图片描述

Dropout正则化

将一个网络的神经元按照一定的概率值随机丢弃,为何Dropout会有效:基本理解不要依赖于任何一个特征,因为该单元的输入可能随时被清除(不能完全理解)。

例如我们设置一个保留概率 k e e p − p r o b = 0.8 keep-prob = 0.8 keepprob=0.8,代表有 20 % 20\% 20%的神经元会被丢弃,使得网络结构更简化。我们以一个在第3层实施dropout来举例,对应的代码如下:

d3 = np.random.rand(a3.shape[0],a3.shape[1]) < keep-prob  # 构造一个和a3维度相同的true-false矩阵
a3 = np.multiply(a3,d3) # 将为true的位置的元素保留,为false的置为0
a3 /= keep-prob # 弥补a3的期望值

其实,对于“弥补a3的期望值”的这个说法,不是很理解。

不同层的keep-prob值要不同,矩阵 W W W的元素数目多,keep-prob可以小一些;矩阵 W W W的元素数目少,keep-prob可以大一些。

  • 我们遵守这个准则,输入层应用 dropout,keep-prob 会接近于 1
  • dropout 是一种正则化方法,它有助于预防过拟合,因此除非算法过拟合,不然我是不会使用 dropout 的
    在这里插入图片描述

dropout 一大缺点就是代价函数𝐽不再被明确定义,通常会关闭 dropout 函数,将keep-prob 的值设为 1,运行代码,确保𝐽函数单调递减。然后打开 dropout 函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值