二、神经网络的编程基础
2.1 二分类(Binary Classification)
-
前向暂停\传播(forward pause\propagation)/:数据从输入层传入,经过隐藏层,最终到达输出层的过程;
反向暂停\传播(backward pause\propagation):复合函数的链式求导法则,能够优化代价函数,这也是训练神经网络的目标;
- 逻辑回归(logistic regression):是一个用于二分类(binary classification)的算法;
-
例子:假如你有一张图片作为输入,比如这只猫,如果识别这张图
片为猫,则输出标签 1 作为结果;如果识别出不是猫,那么输出标签 0 作为结果。用y作为输出的结果标签,就有了下面的关系:
现在看看图片在计算机中的表示方法:为了保存一张图片,需要保存三个矩阵,它们分别对应图片中的红、绿、蓝三种颜色通道,如下所示:
以三个小矩阵为例,规模为 5x4,分别对应图片中红、绿、蓝三种像素的强度值
为了把这些像素值放到一个特征向量中,我们需要把这些像素值提取出来,然后放入一
个特征向量x,为此需要像下面这样定义一个特征向量 𝑥 来表示这张图片,那么共有64643个的特征向量,也就是输入特征向量的维度
——>在二分类问题中,我们的目标就是习得一个分类器,它以图片的特征向量作为输入,然后预测输出结果𝑦为 1 还是 0,也就是预测图片中是否有猫;
- 其他符号定义:
- x:表示一个𝑛𝑥维数据,为输入数据,维度为(𝑛𝑥, 1);
- 𝑦:表示输出结果,取值为(0,1);
- (𝑥(𝑖), 𝑦(𝑖)):表示第𝑖组数据,可能是训练数据,也可能是测试数据,此处默认为训练数据;
- 𝑋 = [𝑥(1), 𝑥(2), . . . , 𝑥(𝑚)]:表示所有的训练数据集的输入值,放在一个 𝑛𝑥 × 𝑚的矩阵中,其中𝑚表示样本数目;
- 𝑌 = [𝑦(1), 𝑦(2), . . . , 𝑦(𝑚)]:对应表示所有训练数据集的输出值,维度为1 × m;
神经网络中更喜欢以列的形式来构建特征矩阵,记为X。相应的y也组成一个对应的(1,m)的向量。
2.2 逻辑回归(Logistic Regression)
对于二元分类问题来讲,给定一个输入特征向量𝑋,它可能对应一张图片,你想识别这
张图片识别看它是否是一只猫或者不是一只猫的图片,你想要一个算法能够输出预测,你只能称之为𝑦^ ,也就是你对实际值 𝑦 的估计——>逻辑回归用线性表达式来联系x和y,称为sigmoid函数:
如果𝑧非常大那么𝑒−𝑧将会接近于 0,关于𝑧的 sigmoid 函数将会近似等于 1 除 以 1 加上某个非常接近于 0 的项,因为𝑒 的指数如果是个绝对值很大的负数的话,这项将会接近于 0,所以如果𝑧很大的话那么关于𝑧的 sigmoid 函数会非常接近 1。相反地,如果𝑧非常小或者说是一个绝对值很大的负数,那么关于𝑒−𝑧这项会变成一个很大的数,你可以认为这
是 1 除以 1 加上一个非常非常大的数,所以这个就接近于 0
对于参数ω以及b可以整合为θi(i=0,1,2...)来表示,其中θ0表示b,其余的就是ω;
2.3 逻辑回归的代价函数(Logistic Regression Cost Function)
- 损失函数/误差函数:用来衡量算法的运行情况,来衡量预测输出值和实际值有多接近。在逻辑回归中用到的损失函数是:𝐿(𝑦^ , 𝑦) = −𝑦log(𝑦^) − (1 − 𝑦)log(1 − 𝑦^);
损失函数只适用于像这样的单个训练样本,而代价函数是参数的总代价,所以在训练逻辑回归模型时候,我们需要找到合适的𝑤和𝑏,来让代价函数 𝐽 的总代价降到最低;
2.4 梯度下降法(Gradient Descent)
在你测试集上,通过最小化代价函数(成本函数)𝐽(𝑤, 𝑏)来训练的参数𝑤和b。在第二行给出和之前一样的逻辑回归算法的代价函数
- 其形式化表达:
横轴表示你的空间参数𝑤和𝑏,在实践中,𝑤可以是更高的维度,但是为了更好地绘图,我们定义𝑤和𝑏,都是单一实数,代价函数(成本函数)𝐽(𝑤, 𝑏)是在水平轴𝑤和 𝑏上的曲面,因此曲面的高度就是𝐽(𝑤, 𝑏)在某一点的函数值
在图上任意一点通过不断迭代来达到图像最低点(最优点)——>全局最优解
假定代价函数(成本函数)𝐽(𝑤) 只有一个参数𝑤,即用一维曲线代替多维曲线,这样可
以更好画出图像:
那么迭代就是不断重复做上面这个公式。: =表示更新参数,𝑎 表示学习率
就像下面这样:
2.5 逻辑回归中的梯度下降(Logistic Regression Gradient Descent)
- 相关的计算图:
也就是说只需要知道𝑑𝑧 = (𝑎 − 𝑦)已经计算好了,进而求w和b的导数再用导数去求最优解就好:
然后: 更新𝑤1 = 𝑤1 − 𝑎𝑑𝑤1, 更新𝑤2 = 𝑤2 − 𝑎𝑑𝑤2, 更新𝑏 = 𝑏 − 𝛼𝑑𝑏。这就是关于单个样本实例的梯度下降算法中参数更新一次的步骤;
2.6 m 个样本的梯度下降(Gradient Descent on m Examples)
在有m个样本时的损失函数为:
- 代码实现:
J=0;dw1=0;dw2=0;db=0;
for i = 1 to m
z(i) = wx(i)+b;
a(i) = sigmoid(z(i));
J += -[y(i)log(a(i))+(1-y(i))log(1-a(i));
dz(i) = a(i)-y(i);
dw1 += x1(i)dz(i);
dw2 += x2(i)dz(i);
db += dz(i);
J/= m;
dw1/= m;
dw2/= m;
db/= m;
w=w-alpha*dw
b=b-alpha*db
- 但是这个算法有个缺点:应用此方法在逻辑回归上你需要编写两个 for 循环:第一个 for 循环是一个小循环遍历𝑚个训练样本,第二个 for 循环是一个遍历所有特征的 for循环;
但是调用for循环会导致效率低下,所以引进了向量化;
2.7 向量化(Vectorization)
向量化是非常基础的去除代码中 for 循环的技术。在深度学习中经常用到。向量化实现将会非常直接计算𝑤𝑇𝑥,代码如下:
z=np.dot(w,x)+b
通过如下代码来比较两者的差距:
import numpy as np #导入 numpy 库
a = np.array([1,2,3,4]) #创建一个数据 a
print(a)
# [1 2 3 4]
import time #导入时间库
a = np.random.rand(1000000)
b = np.random.rand(1000000) #通过 round 随机得到两个一百万维度的数组
tic = time.time() #现在测量一下当前时间
#向量化的版本
c = np.dot(a,b)
toc = time.time()
print(“Vectorized version:” + str(1000*(toc-tic)) +”ms”) #打印一下向量化的版本的时间
#Vectorized version:1.0118484497070312ms
#继续增加非向量化的版本
c = 0
tic = time.time()
for i in range(1000000):
c += a[i]*b[i]
toc = time.time()
print(c)
#250151.68277642858
print(“For loop:” + str(1000*(toc-tic)) + “ms”)#打印 for 循环的版本的时间
#For loop:555.5386543273926ms
可以很明显看出二者的差距。总之,和 for 循环相比,向量化可以快速得到结果;
2.8 向量化逻辑回归(Vectorizing Logistic Regression)
现在就可以把向量化带入到逻辑回归中来提升其运行速度了。
[𝑧(1)𝑧(2). . . 𝑧(𝑚)] = 𝑤𝑇𝑋 + [𝑏𝑏. . . 𝑏] = [𝑤𝑇𝑥(1) + 𝑏, 𝑤𝑇𝑥(2) + 𝑏. . . 𝑤𝑇𝑥(𝑚) + 𝑏],其实也就是WTX+[𝑏𝑏. . . 𝑏],这在python中的代码就是𝑍 = 𝑛𝑝. 𝑑𝑜𝑡(𝑤. 𝑇,𝑋) + b。在python中可以通过广播的方法把实数b扩展为1* m的向量,还可以通过sigmoid函数来计算变量Z进而输出A,通过这样一行代码就能实现:Z = np.dot(w.T,X) +b
2.9 向量化 logistic 回归的梯度输出(Vectorizing Logistic Regression's Gradient)
- 现在来将遍历训练集的for循环用向量化去掉:
利用前五个公式完成了前向和后向传播,也实现了对所有训练样本进行预测和求导,再利用后两个公式,梯度下降更新参数
2.10 Python 中的广播(Broadcasting in Python)
在 numpy 中,当一个 4 × 1的列向量与一个常数做加法时,实际上会将常数扩展为一个 4 × 1的列向量,然后两者做逐元素加法。这种广播机制对于行向量和列向量均可以使用;
用一个 2 × 3的矩阵和一个 1 × 3 的矩阵相加,其泛化形式是 𝑚 × 𝑛 的矩阵和 1 × 𝑛的矩阵相加。在执行加法操作时,其实是将 1 × 𝑛 的矩阵复制成为 𝑚 × 𝑛 的矩阵,然后两者做逐元素加法得到结果。针对这个具体例子,相当于在矩阵的第一列加 100,第二列加 200,第三列加 300
- 广播机制的一般原则如下:广播会在轴长度为 1 的维度进行,轴长度为 1 的维度对应 axis=0,即垂直方向,矩阵 cal1,4 沿 axis=0(垂直方向)复制成为 cal_temp3,4 ,之后两者进行逐元素除法运算
- numpy 广播机制:如果两个数组的后缘维度的轴长度相符或其中一方的轴长度为 1,则认为它们是广播兼容的。广播会在缺失维度和轴长度为 1 的维度上进行(后缘维度的轴长度:A.shape[-1] 即矩阵维度元组中的最后一个位置的值);
总结起来就是下面这张图:
小提示?在神经网络最好不要构建一维数组,这样的话会影响向量运算;可以通过𝑛𝑝. 𝑟𝑎𝑛𝑑𝑜𝑚. 𝑟𝑎𝑛𝑑𝑛(x,y)来随机生成一个x×y的数组;