深度学习笔记_Week2
2.10 m 个样本的梯度下降(Gradient Descent on m Examples)
将2.9对一个样本的操作应用到m个训练样本上,我们要做的是计算这些微分,如我们在之前的训练样本上做的,并且求平均, 得到全局梯度值,把它直接应用到梯度下降算法中。
初始化𝐽 = 0, 𝑑𝑤1 = 0, 𝑑𝑤2 = 0, 𝑑𝑏 = 0
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 循环。这个例子中只有 2 个特征,所以𝑛等于 2 并且𝑛𝑥 等于 2。 但如果有更多特征,需要一 个 for 循环遍历所有𝑛个特征。
在代码中显式地使用 for 循环使算法很低效,同时在深度学习领域会有越来越大的数据集。所以使算法没有显式的 for 循环是十分重要的,可以适用于更大的数据集。使用向量化技术可以使得代码摆脱 for 循环。
2.11 向量化(Vectorization)
对于计算𝑧 = 𝑤𝑇𝑥 + 𝑏,𝑤、𝑥都是列向量。
如果采用非向量化方法:
z=0
for i in range(n_x)
z+=w[i]*x[i]
z+=b
采用向量化方法:
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”) #打印一下向量
化的版本的时间
#继续增加非向量化的版本
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”)#打印 for 循环的版本的时
间
在两个方法中,向量化和非向量化计算了相同的值,向量化版本花费了 1.5 毫秒,非向量化版本的 for 循环花费了大约几乎 500 毫秒,非向量化版本多花费了 300 倍时间。所以在这个例子中,仅仅是向量化你的代码,就会运行 300 倍快。这意味着如果向量化方法需要花费一分钟去运行的数据,for 循环将会花费 5 个小时去运行。
2.12 向量化的更多例子(More Examples of Vectorization)
如果你想计算向量𝑢 = 𝐴𝑣,这时矩阵乘法定义为,矩阵乘法的定义就是:𝑢𝑖 = ∑𝑗 𝐴ij𝑣𝑖
同样使用非向量化实现,𝑢 = 𝑛𝑝. 𝑧𝑒𝑟𝑜𝑠(𝑛, 1), 并且通过两层循环𝑓𝑜𝑟(𝑖): 𝑓𝑜𝑟(𝑗):,得到𝑢[𝑖] = 𝑢[𝑖] + 𝐴[𝑖] [𝑗] ∗ 𝑣[𝑗] 。现在就有了𝑖 和 𝑗 的两层循环
向量化方式就可以用𝑢 = 𝑛𝑝. 𝑑𝑜𝑡(𝐴, 𝑣),向量化实现方式,消除了两层循环,使得代码运行速度更快。
如果你已经有一个向量𝑣,并且想要对向量𝑣的每个元素做指数操作,得到向量𝑢等于𝑒的𝑣1,𝑒的𝑣2,一直到𝑒的𝑣𝑛次方。这里是非向量化的实现方式,首先你初始化了向量𝑢 = 𝑛𝑝. 𝑧𝑒𝑟𝑜𝑠(𝑛, 1),并且通过循环依次计算每个元素。但也可以通过 python 的 numpy 内置函数,import numpy as np,执行 𝑢 = 𝑛𝑝. 𝑒𝑥𝑝(𝑣) 命令。
将以上知识应用到梯度下降法中,如果有超过两个特征时,需要循环 𝑑𝑤1 、𝑑𝑤2 、𝑑𝑤3 等等。所以 𝑗 的实际值是 1、2 和 𝑛𝑥。要想消除第二循环,不用初始化 𝑑𝑤1 , 𝑑𝑤2 都等于 0,而是定义 𝑑𝑤 为一个向量,设置 𝑢 = 𝑛𝑝. 𝑧𝑒𝑟𝑜𝑠(𝑛(𝑥),1)。定义了一个𝑥行的一维向量,从而替代循环,仅仅使用了一个向量操作 𝑑𝑤 = 𝑑𝑤 + 𝑥 (𝑖)𝑑𝑧 (𝑖) 。最后得到 𝑑𝑤 = 𝑑𝑤/𝑚 ,将两层循环转成一层循环。
2.13 向量化逻辑回归(Vectorizing Logistic Regression)
如果有 𝑚 个训练样本,然后对第一个样本进行预测,需要这样计算:计算 𝑧,𝑧 (1) = 𝑤𝑇𝑥 (1) + 𝑏 。
然后计算激活函数 𝑎 (1) = 𝜎(𝑧 (1) ) ,计算第一个样本的预测值 𝑦 。
对第二个样本进行预测,需要计算 𝑧 (2) = 𝑤𝑇𝑥 (2) + 𝑏 , 𝑎 (2) = 𝜎(𝑧 (2) ) 。
对第三个样本进行预测,需要计算 𝑧 (3) = 𝑤𝑇𝑥 (3) + 𝑏 ,𝑎 (3) = 𝜎(𝑧 (3) ) ,依次类推。
如果有 𝑚 个训练样本,需要这样做 𝑚 次。
为了在一个步骤中计算 𝑧1、 𝑧2 、𝑧3 …… zm。先构建一个 1 × 𝑚 的矩阵,实际上它是一个行向量,同时准备计算 𝑧 (1), 𝑧 (2) …… 𝑧 (𝑚) 。它可以表达为 𝑤 的转置乘以大写矩阵 𝑥 然后加上向量 [𝑏𝑏. . . 𝑏] ,
[𝑏𝑏. . . 𝑏] 是一个 1 × 𝑚 的向量或者 1 × 𝑚 的矩阵或者是一个 𝑚 维的行向量。
𝑤𝑇𝑥 (1) + 𝑏 这是第一个元素,𝑤𝑇𝑥 (2) + 𝑏 这是第二个元素,𝑤𝑇𝑥 (𝑚) + 𝑏 这是第 𝑚 个 元素。
第一个元素恰好是 𝑧 (1) 的定义,第二个元素恰好是 𝑧 (2) 的定义,以此类推。所以当将不同训练样本对应的小写 𝑥 横向堆积在一起时得到大写变量 𝑋 并且将其他小写变量也用相同方法处理,将它们横向堆积起来,最终能得到大写变量 𝑍 。
numpy 命令是:
Z = np.dot(w.T,X) + b
得到 Z 后,使用
𝐴 = [𝑎(1)𝑎(2). . . 𝑎(𝑚)] = 𝜎(𝑍)
一次性计算所有 𝑎 ,完成所有 m 个训练样本的前向传播向量化计算。
2.14 向量化 logistic 回归的梯度输出(Vectorizing Logistic Regression's Gradient)
对 𝑚个 训练数据做同样的运算,可以定义一个新的变量 𝑑𝑍 = [𝑑𝑧 (1) , 𝑑𝑧 (2) . . . 𝑑𝑧 (𝑚) ] ,所有的 𝑑𝑧 变量横向排列,因此,𝑑𝑍 是一个 1 × 𝑚 的矩阵,或者说是一个 𝑚 维行向量。
在上一小节中,已经计算出 A ,即
需要找到这样的一个行向量
得
不难发现第一个元素就是 𝑑𝑧 (1),第二个元素就是 𝑑𝑧 (2) ,以此类推。
得到dZ后,尝试求出其他需要的量。首先求db:
而此时dz已经组成一个行向量dZ了,可以使用
db = 1/m * np.sum(dZ);
同理,
dw = 1/m * X * dZT
就可以避免在训练集上使用for循环。
总结所有向量化表示,可以使用以下代码:
𝑍 = 𝑤𝑇𝑋 + 𝑏 = 𝑛𝑝. 𝑑𝑜𝑡(𝑤.𝑇, 𝑋) + 𝑏
𝐴 = 𝜎(𝑍)
𝑑𝑍 = 𝐴 − 𝑌
𝑑𝑤 = 1/𝑚 ∗ 𝑋 ∗ 𝑑𝑧𝑇
𝑑𝑏 = 1/𝑚 ∗ 𝑛𝑝. 𝑠𝑢𝑚(𝑑𝑍)
𝑤: = 𝑤 − 𝑎 ∗ 𝑑𝑤
𝑏: = 𝑏 − 𝑎 ∗ 𝑑𝑏
利用前五个公式完成了前向和后向传播,也实现了对所有训练样本进行预测和求导,再利用后两个公式,梯度下降更新参数,至此,我们得到了一个高度向量化的、非常高效的逻辑回归的梯度下降算法。