欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

达叔系列——神经网络编程基础

程序员文章站 2022-04-15 16:16:39
以下内容为听了达叔课程的笔记与疑惑之处,暂且都记下。如有不妥之处,尽情指出。 (1)二分类问题 目标:习得一个分类器,它以图片的特征向量作为输入,然后预测出结果 y 为 1 还是 0 。比如,我们需要预测一张图片是不是猫,输出 1 即预测是,0 则不是。 图片的特征向量:首先,计算机保存一张图片需要 ......

  以下内容为听了达叔课程的笔记与疑惑之处,暂且都记下。如有不妥之处,尽情指出

(1)二分类问题

  目标:习得一个分类器,它以图片的特征向量作为输入,然后预测出结果 y 为 1 还是 0 。比如,我们需要预测一张图片是不是猫,输出 1 即预测是,0 则不是。

  图片的特征向量:首先,计算机保存一张图片需要保存 3 个矩阵,分别对应 rgb 三个颜色通道。比如图片为 64 * 64 像素(下图故意为 5 * 4 规模),那就需要存储各个颜色通道上的像素强度。通过将 255,231...255,134...93,22... 等数字提取出来放到一个特征向量 x 中(即 (n,1 )nx = 64 * 64 * 3 = 12288,常用 n 表示特征向量维度 )。

达叔系列——神经网络编程基础

  相关符号:会在余下课程中涉及。

  • x 表示一个 nx 维数据,为输入数据,(nx , 1)
  • y 表示输出结果,取值为 1 or 0
  • (x(i) , y(i)) 表示第 i 组训练数据 or 测试数据,默认是前者
  • x = [ x(1) , x(2) ,  x(3) , x(4) ... x(m) ] 表示所有训练集的输入值,放在 nx * m 的矩阵中(在 python 中常用 x.shape 这条命令表示规模)
  • y = [ y(1) , y(2) ,  y(3) , y(4) ... y(m)] 表示输出值

(2)逻辑回归

  逻辑回归是应用于二分类问题的算法。

(1)逻辑回归的 hypothesis function (假设函数)

  对于二分类问题,当输入 x (即图片特征向量) ,我们想要一个算法预测是不是输入了一只猫,即期望输出一个预测结果 y hat ,换句话说我们想要 y hat 表示 y = 1 的可能性。

  已知 logistic 回归的参数有 w (nx 维向量,w 实际上是特征权重,维度与特征向量相同),b (实数,表偏差) 。所以给出输入 x,参数 w,b 之后,我们尝试通过 y hat = w^t * x + b,一个关于 x 的线性函数(适合用于做线性回归问题,不适合做二分类。因为 y hat 需要表示预测 y=1 的概率,所以取值属于 0~1 ,而线性回归函数取值不一定在此之内。),我们将线性回归式子作为 sigmoid 函数的自变量,将线性函数转化为非线性函数

达叔系列——神经网络编程基础

 

 

sigmoid 函数图像(为什么么选 sigmoid 函数作为逻辑回归函数,插眼

达叔系列——神经网络编程基础

  我们可以看到,当 z->∞,б->1,当 z->-∞,б->0。所以 z (即 y hat) 用来预测等于 1 的概率,怎么预测呢,就是不断调整训练 w,b 两个参数,控制 z ,再控制 б 的输出。 

(2)逻辑回归的代价函数(logistic regression cost function)

  为了训练 w,b 我们需要代价函数,通过代价函数来得到参数 w,b 。先看下逻辑回归函数

达叔系列——神经网络编程基础

(1)损失函数(误差函数): loss function : l(y hat, y)

  根据此函数来衡量测试输出值 y hat 和 y 的差距大小。一般我们用两者平方差,但是在逻辑回归中我们不这样做,因为会发现优化目标不是凸忧化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值(插眼

  所以定义了另一个损失函数:(为什么是这个函数呢,插眼---解释一:下文会提到 j(w,b) ,为保证 j(w, b) 函数是凸函数,即只有一个最小值)。

  达叔系列——神经网络编程基础

  我们在来看看这个 loss function 是否能满足基本取值要求:

  达叔系列——神经网络编程基础

  在这门课中很多的函数效果和以上这个类似,如果 y = 1, 我们就尽可能让 y hat 变大,如果 y = 0, 我们就尽可能让 y hat 变小。(牢记 y hat 表示的是 y = 1 的概率)

(2)代价函数

  损失函数是描述单个训练样本的,代价函数描述整个数据集的表现如何。代价函数是将所有数据集的损失函数求和再除 m (数据集大小)。

  达叔系列——神经网络编程基础

  所以,在实际中,其实我们需要关注整个训练集,而不单单关注其中一次训练。即我们最终目标是找到变量 w,b 使得整个 j(w, b) 变成最小

  

  结果表明:逻辑回归函数可以看成一个非常小的神经网络。

 

(3)梯度下降法

  我们已经有了上文提到的代价函数,那么怎么使得代价函数 j(w, b) 值最小呢?我们就通过梯度下降法实现。j(w, b) 函数可以表示成如下:其中红点处为最小值,此时的 w, b 就是我们所期盼的参数

达叔系列——神经网络编程基础

(1)怎么找到最小值

达叔系列——神经网络编程基础

  可以用图中小红点初始化,对于逻辑回归函数所有的初始化几乎都有效,因为是凸函数,无论在哪里初始化,最终最小值应该找到大致相同的点。

  所以可以从小红点开始,不断找该点梯度最大的,然后下降,不断迭代。最后找到全局或者部分最优解。

(2)进一步理解

  其实将 j(w, b) 进行切面处理,在 w , b 轴都做切面,将三维转化成二维(投影),维取到的最小值的点中必定包含着我们需要找的三位的最小值

  达叔系列——神经网络编程基础

  即 j(w) ,j(b) 只有一个参数,不断迭代:

达叔系列——神经网络编程基础

 

  其中 “:=” 表示不断更新。a 表示学习率(后文会提及如何控制 a ),用来控制步长。a*d(j(w))/dw 用来表示具体 w 的每一次减少量,直至到 j(w) 的底部。最终回到 j(w,b) 改成求偏导:

达叔系列——神经网络编程基础

(4)计算图与其求导

  在神经网络的计算中,都是按照向前或反向传播过程中组织的,紧接着进行一个反向传输操作,用来计算对应的梯度与倒数。

  那这个和计算图有什么关系呢?我们举一个比逻辑回归更简单的神经网络:(蓝色为前向传播,红色为后向传播---求导)

  达叔系列——神经网络编程基础

    

  个人觉得,计算图最大的好处就是链式求导法则会更加清晰,

达叔系列——神经网络编程基础

(5)逻辑回归中的梯度下降

  本节讨论通过计算偏导数实现逻辑回归的梯度算法。

  假设样本只有 x1,x2 ,为了计算 z ,我们需要输入 w1,w2,b 参数,即 z = w1*x1 + w2*x2 + b。

  达叔系列——神经网络编程基础

(1)假设现在只考虑单个样本情况

  单个样本呢代价函数为:

  达叔系列——神经网络编程基础

  其中 a 是逻辑回归的输出(y hat),y 是样本标签值。那么我们可以得到表示这个计算的计算图:

达叔系列——神经网络编程基础

  上图写的是前向传播计算 l(a,y) 。我们现在通过反向计算出导数(这样就能知道 l 关于 w1,w2, b 的变化率)

  编程中我们用 da 表示 d(a, y)/da (习惯写法记牢,>﹏<)。开始一步一步反向积分

  达叔系列——神经网络编程基础

  a = σ(z),所以我们可以求 dz :

达叔系列——神经网络编程基础

  再求 da/dz = a * (1 - a),过程:

  达叔系列——神经网络编程基础

  再回到 dz = dl / dz :

  达叔系列——神经网络编程基础

  好了,想想我们最终的目的是谁,是参数 w1,w2,b 关于 l(w,b) 的变化率,所以别忘了找最终单个样本的代价函数 l -- w,b  之间的关系:

  达叔系列——神经网络编程基础

  注意:l(a,y) 中 a 是复合函数,最终是关于 w1,w2,b 的函数,所以分清谁是自变量(不是 x1,x2哦),如上图,我详细写了的求导过程,但是上述都是对单个测试样本的后向传播。

  那么我们就可以根据公式调整 w1,w2,b 了:

达叔系列——神经网络编程基础

  其中的a就是学习率(步长),后文会讲解,暂按下不表

(2)考虑 m 个样本的梯度下降

  上文已经提到了单个样本怎么训练(即调整参数 w1,w2,b ),那多个样本呢?我们先回顾多个样本的代价函数 j(w, b) 的定义:  

  达叔系列——神经网络编程基础

  我们已经求得了单个样本的损失函数,代价函数无非是求和取平均值:

  达叔系列——神经网络编程基础

(3)m 个样本的梯度下降算法实现

  算法大致流程:

 1 j=0,dw_1=0,dw_2=0,db=0 # 我们初始化
 2 
 3 j=0;dw1=0;dw2=0;db=0; # 代码流程
 4 for i = 1 to m
 5     z(i) = wx(i)+b;
 6     a(i) = sigmoid(z(i));
 7     j += -[y(i)log(a(i))+(1-y(i))log(1-a(i));
 8     dz(i) = a(i)-y(i);
 9     dw1 += x1(i)dz(i);
10     dw2 += x2(i)dz(i);
11     db += dz(i);
12 j/= m;
13 dw1/= m;
14 dw2/= m;
15 db/= m;
16 w=w-alpha*dw  # alpha 就是步长
17 b=b-alpha*db

 

  算法缺点:主要是 for 效率不高。代码有两处 for : ① 就是第一行,此处因为我们只有3个变量 w1,w2,b,如果有多个呢  ② 就是第四行,需要求和 dw1,dw2,db。后续会讲解改进方法。 

(6)向量化

  向量化的目的就是处理 for 循环,当数据量较大的时候,通过 for 来处理会十分低效。那么肿么向量化呢?

  在逻辑回归中计算 z = w^t * x + b ,w/b 都是列向量,所以我们不妨将 x1,x2,x3...也当成一个向量。

  非向量化处理 w^t * x  vs  向量化处理: 

1 z=0
2 for i in range(n_x)
3    z+=w[i]*x[i]
4 z+=b
1 z=np.dot(w,x)+b  # 通过numpy函数向量化,插眼numpy函数需要继续学习,先有个向量化概念

 

   达叔说我们应该尽量避免 for ,除非迫不得已,能用向量化解决(内置函数)是最好的。

  我们现在来处理梯度下降算法,也就是通过向量化来处理:

达叔系列——神经网络编程基础

  w1,w2...变成一个 w 向量,x1,x2...变成一个 x 向量,z1,z2....变成 z 向量。这些向量都是 n 维向量。

(7)向量化逻辑回归

  本节实现通过向量化显著加快你的代码。其实我感觉就是将 for 循环的重复 x1,x2..z1z2..等等放到一个向量里取处理,理解上没啥。

  达叔系列——神经网络编程基础

(8)向量化 logistic 回归的梯度输出

  我们只需要比较原先写法和改用向量方式写法,就能更好理解了:(当然numpy 函数我还不怎么会搞)

                                                                     达叔系列——神经网络编程基础 达叔系列——神经网络编程基础

(9)python中的广播

  我们知道矩阵的加减乘法,那么如果两个举证格式按照线性代数来看不符合运算规则,那再python中呢?python会通过广播来实显示,也就是通过扩展矩阵使得可以运算:

达叔系列——神经网络编程基础