二元分类
例如分辨一张图片里面到底有没有猫:输出1(有猫) or 输出0(无猫)
特征向量思想
一张64x64的数码图片,根据RGB可以分为3个独立的亮度矩阵,每个像素都由3个不同亮度的颜色组成,取每个亮度值放进一个64x64x3维的向量中,作为输入的特征向量x,通常用小写字母n表示维度。
惯用符号
- (x,y) 表示一个样本的输入及输出,其中x∈Rnx 即x是nx 维的向量
- (x(1),y(1)) 表示样本1的输入及对应输出,若训练集由m个训练样本组成,众多对应关系表示为:(x(1),y(1)),(x(2),y(2))…(x(m),y(m))。
- 定义一个m x nx 的矩阵X,如下图:其中每一列都是一个向量x(1),x(2)…x(m),有m张图片样本,就有m个列向量。每个样本的列向量的高度为nx,即每张图片的特征数据有nx 个。
输出矩阵y的尺寸是1xm,即有m个预测结果
- python中常用X.shape获取矩阵的行和列
- 有时也使用m_train和m_test分别表示训练接和测试集的样本个数
逻辑回归
为什么叫“逻辑回归”?
这里的“逻辑”指类似逻辑电路的0-1,是一种二元分类模型,这里的“回归”指训练过程慢慢拟合“回归”到正确的函数上。
已得到一张图片X,期望得到
y
^
\widehat{y}
y
,即输入x时,得到y=1的概率。
类似数学中的经典方程:y=ax+b
逻辑回归中的2个参数为 w 和 b,其中w∈Rnx,是一个nx维的向量,b是一个实数。
逻辑回归的模型为:
y
^
\widehat{y}
y
=σ(wTx+b),要满足线性代数乘法所以将参数转置。为了完成二分类问题,需要将 wTx+b 放入sigmoid函数,将值对应在(0,1)中。
下面这个暂时不用细看(后续课程不用)
吴恩达老师对逻辑回归函数的另一种表达(不具体解释,第一课第二周第二节)
逻辑回归损失函数
我们期望每次运行算出的 y ^ \widehat{y} y (i)=y(i) ,即预测值与实际标记结果一致,但实际情况往往有一定误差,我们使用损失函数来评价误差大不大,评价本次预测结果准不准。
评价某一样本的损失函数
损失函数用来衡量
y
^
\widehat{y}
y
(i) 与实际值y(i) 的接近情况,若结果越小则两值越接近,拟合越准确。
可以简化理解为下面这个公式(仅理解损失函数的表达意义,并非此公式,原因:非凸函数有许多局部最优解不适用)
L( y ^ \widehat{y} y ,y)=1/2( y ^ \widehat{y} y –y)2
实际运用中,我们常会使用其他损失函数,一般在逻辑回归中使用下面这个函数比较合理:
L( y ^ \widehat{y} y ,y)=–(yloge y ^ \widehat{y} y +(1–y)loge(1– y ^ \widehat{y} y )) 这里的log以e为底
之所以选择它,因为如下推断
例如样本标记y为1,根据损失函数推理,只有当
y
^
\widehat{y}
y
越大时,才能使损失函数越小,而
y
^
\widehat{y}
y
的值被sigmoid函数限制在(0,1)之间,换言之,当
y
^
\widehat{y}
y
越接近1,损失函数越小,符合期待情况。因此这个函数可以满足需要,称之为交叉熵损失函数
评价全体样本的损失函数
使用函数J求所有样本损失函数的平均值,根据链式法则,参数依然是w和b,训练过程中希望J的值尽可能的小。
所以回归本源就是:获得参数w和b,计算该函数的预测值 y ^ \widehat{y} y ,然后利用损失函数,约束参数w和b情况,找到使损失值最小的w和b,即最合适的函数模型。
也可认为:成本函数Cost是所有损失函数Loss的均值
梯度下降法
数学概念回顾
方向导数:是一个数;反映 f(x,y)在某点沿某个方向的变化率。二维是导数,三维是方向导数。
梯度:是一个向量;既有大小,也有方向。梯度的方向就是函数f(x,y)在这点增长(降低)最快的方向,梯度的大小为方向导数的最大值。
梯度下降法思路
对参数任意初始化,快速根据梯度的方向找到最快下山的路,到达全局最优解。
下图为几何理解:
二维图像解释梯度下降法更新参数
首先,对参数w的更新公式如下:
其中,:= 表示“左侧更新为右侧的结果”; α表示学习率(learning rate),控制更新速度(步长); 最右边是一元函数求导,有时直接写为:“dw”,来表示函数J对参数w的求导。
一元函数画出的二维图像中,w为期待变化的参数。
- 假如初始化为红色点,该点导数斜率为正值,w更新为 “w-α斜率”,实现w的减小(左移),多次重复直到红点落到最优解处。
- 假如初始化为绿色点,该点导数斜率为负值,w更新为 “w-α斜率”,实现w的增大(右移),多次重复直到绿点落到最优解处。
逻辑回归的梯度下降
逻辑回归的两个参数w和b,均使用偏导求梯度,获取正负偏导数值。
三维图像中确定一点需要确定w和b两个参数,分别从单个参数角度向最优解进行移动,实现每一步都以最快速度下降。
一个符号的注意点
吴恩达老师在函数J(w,b)求偏导时用的是d而不是σ,我们知道多元函数偏导是用σ就行。
计算图的反向传播计算
正向传播:根据a,b,c的输入,通过某待拟合函数计算输出结果J
反向传播:本质类似于求偏导,得到输入对输出的影响情况
我们希望得到输出变量对参数变量的导数,从而根据上面设计的梯度下降法公式,得到梯度下降的方向。
链式法则
例如输出变量J的值是33,根据上面的梯度下降公式优化输入的a,需要求
d
J
d
a
\frac {dJ}{da}
dadJ的值。
众所周知使用链式法则即可:
d J d a \frac {dJ}{da} dadJ= d J d v \frac {dJ}{dv} dvdJ* d v d a \frac {dv}{da} dadv=3 ( d J d v \frac {dJ}{dv} dvdJ=3, d v d a \frac {dv}{da} dadv=1)
代码中新的表示
因为我们默认知道输出是J,所以直接用"d~" 表示输出J对 "~"的求导
如上图的红色字体:
“dv”其实就是“ d J d v \frac {dJ}{dv} dvdJ”,“da”其实就是“ d J d a \frac {dJ}{da} dadJ”
复合函数求导
当b和c同时影响u的情况下,求:
d
J
d
b
\frac {dJ}{db}
dbdJ 和
d
J
d
c
\frac {dJ}{dc}
dcdJ的值
du=3 (该表示法相当于 d J d u \frac {dJ}{du} dudJ=3)
求 d u d b \frac {du}{db} dbdu时,c视为常量2,因此 d u d b \frac {du}{db} dbdu=2
d J d b \frac {dJ}{db} dbdJ= d J d u \frac {dJ}{du} dudJ * d u d b \frac {du}{db} dbdu=3 x 2= 6
同理, d J d c \frac {dJ}{dc} dcdJ= d J d u \frac {dJ}{du} dudJ * d u d c \frac {du}{dc} dcdu=3 x 3= 9
逻辑回归中的梯度下降法
假设逻辑回归待拟合公式如下:
Z=w1x1+w2x2+b (本质还是wTx+b,只不过向量里面有2个维度,所以有下标1,2)
下面描述梯度下降法实现反向传播,得到dw1 , dw2和db,对参数w1 , w2 , b进行更新。
已知公式:
传播图:
- 先计算"da"
d L d a \frac {dL}{da} dadL= – y a \frac {y}{a} ay+ ( 1 − y ) ( 1 − a ) \frac {(1-y)}{(1-a)} (1−a)(1−y) (对a求偏导) - 链式法则计算“dz”
d L d z \frac {dL}{dz} dzdL= d L d a \frac {dL}{da} dadL* d a d z \frac {da}{dz} dzda ,别忘了
所以 d a d z \frac {da}{dz} dzda =a * (1-a)
dz = d L d z \frac {dL}{dz} dzdL= d L d a \frac {dL}{da} dadL* d a d z \frac {da}{dz} dzda =(– y a \frac {y}{a} ay+ ( 1 − y ) ( 1 − a ) \frac {(1-y)}{(1-a)} (1−a)(1−y)) x [a*(1-a)] = a–y
- 最后求dw1 , dw2和db
正常求偏导即可,偏谁谁变量,其他都视为常量。
- 根据更新变量的公式对变量进行更新,α是学习率
逻辑回归中m个样本的梯度下降法
上面解释了一个样本的反向传播和参数更新过程,至于多个样本的情况,需要使用全局损失函数。
全局损失函数正向计算:
反向求各个样本的w1 , w2 , b,例如这里求
J
(
w
,
b
)
d
w
1
\frac {J(w,b)}{dw1}
dw1J(w,b)=dw1, 其实就是把所有dw1加起来算平均:
逻辑回归中进行梯度下降的伪代码
默认有m个样本,每个样本输入维度为2(eg:位置、面积)
-
将J , dw1 , dw2和db都置为0,视作计数器。
-
使用for循环遍历m个样本
-
列出逻辑回归的标准公式: z(i) = wTx(i) + b,本例中输入x有2个维度,所以有下标1,2。
-
将输出z(i)通过sigmoid得到预测概率 a(i)
-
给变量 J 累加计算得到的损失函数值
-
计算dz
-
根据dz分别计算dw1 , dw2和db并累加到总和里
-
在for循环外面将四个累加值都除以m,得到平均损失函数值J , dw1 , dw2和db的平均值。
-
根据更新公式将参数更新
-
以上,完成了 一次 多样本(m个样本)多维度(2个维度)的梯度下降。
个人认为外层应该还有一层循环,继承上一次梯度下降的参数,投入下一次梯度下降中,不断计算平均损失梯度,直到得到使损失函数值达到最小的参数值。
向量化
代码中不再使用冗余的for循环,使用向量化处理数据
左边没有向量化:设置计数器,循环相乘并累加。
右边向量化:使用numpy库中的点乘函数直接计算,并行更加高效
对矩阵v进行指数化处理为矩阵u的两种方法,左边是for循环,右边直接采用exp函数进行处理
原则:能不用for就不用for,尽量去numpy里面找内置函数进行矩阵处理。
向量化反向传播代码
- 最开始初始化0可能会用到for循环,我们使用dw=np.zeros((n_x),1) 生成一个n行1列的全0矩阵
- dw1+=x1(i)dz(i) 和 dw2+=x2(i)dz(i)…的地方可能也会用到for循环,我们使用dw+=x(i)dz(i) 进行矩阵计算
- 最后的除以m也可以用dw/=m对矩阵进行整体除法
向量化m个样本的逻辑回归
前向传播
非向量化时需要多次计算z得到预测值a
向量化处理:
- 定义一个 (nx,m)的矩阵X,存储m个样本的输入
- 定义nx维的向量w,存储nx个参数
- 定义(1,m)的矩阵Z,使用公式z=wT+b,计算结果:并横向存储结果z(1) , z(2) , z(3)…
- 这里的b是 1xm 的矩阵
- 定义(1,m)的矩阵A,A=[a(1) , a(2) , a(3)…] = σ(z),最终得到正向传播预测结果的集合A
以上计算Z矩阵的Python代码:
Z = np.dot(w.T , X)+b
特殊的一点是Python中b是一个实数,但是自动扩展成一个1xm的矩阵,Python中称为“广播(broadcast)”
反向传播
继续沿用正向传播时定义的矩阵X、矩阵Y
- 定义(1,m)的矩阵dZ = [dz(1) , dz(2) …dz(m)],存储dz(即dz = d L d z \frac {dL}{dz} dzdL= d L d a \frac {dL}{da} dadL* d a d z \frac {da}{dz} dzda )
- 沿用正向传播时定义的存储预测结果的矩阵A
- 定义(1,m)的矩阵Y,存储实际真值结果y = [y(1) , y(2) …y(m)]
- 用矩阵计算dZ=A – Y=[a(1)–y(1) , a(2)–y(2) … a(m)–y(m)]
得到dz之后,求w和b的时候也会用到for循环
下面使用向量化优化这两步:
- db求和可以直接用numpy函数运行。db= 1 m n p . s u m ( d Z ) \frac {1}{m}np.sum(dZ) m1np.sum(dZ)
- dw则使用矩阵乘法计算(暂时先不用np.dot计算)。用(nx,m)的矩阵X 乘以 之前算出(1,m)的矩阵dZ的转置,实现乘积并求和,得到(nx,1)的矩阵
全部向量化
右边前两个对应是前向传播,后三个对应是反向传播
其中损失函数J没有输出的必要,只用于判断何时跳出训练循环,所以没有专门向量化,但每轮还是需要计算的。
Python中的广播技术
导入例子
下面是四种食材的热量值,计算以下四种食材中,碳水、蛋白、脂肪,分别占总热量的百分比。
思想:每列求和,然后每个元素除以本列的总热量即可得到所占百分比。
cal=A.sum(axis = 0) //axis=0是求列的和,axis=1是求行的和
percentage = 100*A/(cal.reshape(1,4)) //reshape将cal转换为一个1行4列的矩阵,保证形状(最好进行操作)
广播例子
黑色是计划写的值,蓝色是实际扩充后的值。
- (1,1)的矩阵100被扩充为(4,1)的矩阵
- (1,n)的矩阵被扩充为(m,n)的矩阵
- (m,1)的矩阵被扩充为(m,n)的矩阵
类似把尺寸较小的扩充为较大且方便计算的尺寸
python中numpy数组的注意事项
创建数组注意形状问题
-
可能会出问题的情况:
上面用numpy的随机函数生成5个数字放进a里面,但是读取a的shape发现是 (5, ) 形状,即秩为1的 数组,而不是矩阵。通过将a转置,发现形状没有变化,并且进行点乘也没有扩展为5x5的矩阵。 -
应该这样创建
创建数组时候,要定义成列向量a=np.random.randn(5,1)
而不是a=np.random.randn(5)
或者使用assert(a.shape(5,1))
,来声明一下,确保a是一个列向量
又或者遇到一个秩为1的数组,可以使用a=a.reshape((5,1))
来重塑为一个列向量
一个实际例子
假设img是一个具有3个颜色通道:红色、绿色和蓝色的32x32像素的图像。 如何将其重新转换为列向量?
x = img.reshape((32 * 32 * 3, 1))