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/? ∗ ??. ???(??)
?: = ? − ? ∗ ??
?: = ? − ? ∗ ??
利用前五个公式完成了前向和后向传播,也实现了对所有训练样本进行预测和求导,再利用后两个公式,梯度下降更新参数,至此,我们得到了一个高度向量化的、非常高效的逻辑回归的梯度下降算法。