深度学习详细笔记 (通俗易懂,这一篇就够了 )——Python向量化方法的深入解析

1. 向量化 (Vectorization)

在深度学习的世界中,数据量经常是巨大的。在这种背景下,使用for循环来处理数据显得效率低下。一个更为高效的方法就是向量化

所谓向量化,指的是利用矩阵运算来代替传统的循环,从而极大地提高代码执行的速度。让我们通过一个Python示例来直观地感受向量化和非向量化代码之间的差异。

import numpy as np
import time

a = np.random.rand(1000000)
b = np.random.rand(1000000)

tic = time.time()
c = np.dot(a,b)
toc = time.time()

print(c)
print("向量化版本运行时间:" + 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循环版本运行时间:" + str(1000*(toc-tic)) + "ms")

可能的输出为:

250286.989866
向量化版本运行时间:1.5027523040771484ms
250286.989866
for循环版本运行时间:474.29513931274414ms

显然,使用for循环的时间是向量化版本的大约300倍。这突显了在深度学习计算中,利用向量化可以大大提高效率。

不仅如此,为了进一步提速,我们还可以使用GPU,它的计算能力远超于CPU。事实上,GPU和CPU都有并行计算的能力,这得益于它们都支持Single Instruction Multiple Data(SIMD)。简单地说,SIMDSIMDSIMD允许单一的指令同时处理多个数据点,从而大幅度提高了运算速度。特别是在做矩阵相关的运算时,GPU的性能由于其更多的并行处理单元而远超CPU。Python的numpy库的很多函数已经优化过,它们内部利用了SIMD指令,这也是为什么numpy函数如此高效的原因。

总之,无论是向量化还是利用GPU加速,目的都是为了让深度学习算法更快、更高效。

2. 更多的向量化示例 (More Vectorization Examples)

在我们之前的讨论中,已经明确提到:在计算时,应该尽量避免使用for循环,而更多地选择向量化矩阵运算,这样可以大大提高效率。如果你用过Python的numpy库,你可能已经知道我们通常使用np.dot()函数来进行这种矩阵运算。

当我们将这种向量化的方法应用于逻辑回归算法时,可以极大地减少for循环的使用,取而代之的是直接采用矩阵运算。但这里需要注意的是,算法的最外层循环(例如训练迭代)是不能被替换的。不过,我们可以在每次迭代中直接使用矩阵运算来计算J、dw和b。

3. 向量化逻辑回归 (Vectorizing Logistic Regression)

回想一下我们在《神经网络与深度学习》的课程笔记(2)中所讨论的,整个训练数据集构成的输入矩阵 X X X的维度是 ( n x , m ) (nx, m) (nx,m),权重矩阵 w w w的维度是 ( n x , 1 ) (nx, 1) (nx,1) b b b是一个常数,输出矩阵 Y Y Y的维度是 ( 1 , m ) (1, m) (1,m)。有了向量化的思想,我们可以使用矩阵表示法来计算所有 m m m个样本的线性输出 Z Z Z

Z = w T X + b Z = w^TX + b Z=wTX+b

在Python的numpy库中,这可以轻易地用以下方式表示:

Z = np.dot(w.T, X) + b
A = sigmoid(Z)

其中, w T w^T wT表示 w w w的转置。

所以,通过利用向量化矩阵运算,我们可以同时对所有 m m m个样本进行计算,从而极大地提高运算速度,而无需依赖for循环。

4. 向量化逻辑回归的梯度输出 (Vectorizing Logistic Regression’s Gradient Output)

当我们谈论机器学习,特别是逻辑回归时,效率是关键。逻辑回归中的梯度下降是一个关键的优化方法,但使用传统的循环来计算它可能会很慢。那么,我们怎么提高它的速度呢?答案是“向量化”。

向量化是一种用数组或矩阵替代传统循环的技术,可以显著提高计算速度,特别是当我们处理大数据集时。

首先,让我们考虑逻辑回归的梯度。对于所有的样本m,我们有梯度dZ,它的维度是 (1, m)。这个梯度可以简单地表示为:
d Z = A − Y dZ = A - Y dZ=AY

接下来,我们需要计算另一个重要的参数db,它代表了b的梯度。它是dZ所有元素的平均值,可以这样表示:
d b = 1 m ∑ i = 1 m d z ( i ) db = \frac{1}{m} \sum_{i=1}^{m} dz(i) db=m1i=1mdz(i)
在Python中,我们可以使用以下代码来实现它:

db = 1/m * np.sum(dZ)

然后,我们有dw,它代表了w的梯度,可以这样表示:

dw=1mX⋅dZTdw = \frac{1}{m} X \cdot dZ^Tdw=m1​X⋅dZT

在Python中,我们可以使用以下代码来实现它:

dw = 1/m * np.dot(X, dZ.T)

所以,通过向量化,我们可以避免使用传统的for循环来计算逻辑回归中的梯度。一个完整的单次迭代的梯度下降算法可以这样表示:

Z = np.dot(w.T, X) + b
A = sigmoid(Z)
dZ = A - Y
dw = 1/m * np.dot(X, dZ.T)
db = 1/m * np.sum(dZ)
w = w - alpha * dw
b = b - alpha * db

这里,alpha是学习率,它决定了wb更新的速度。请注意,上述代码只表示一个单独的训练更新。在真实的场景中,为了达到最佳的结果,我们通常会在外部添加一个for循环来多次迭代此过程。

总之,通过使用向量化技术,我们不仅提高了代码的效率,而且使它更简洁易读。

注意: "sigmoid"是逻辑函数的一种常用实现,它将任何数字映射到0和1之间。

5. Broadcasting in Python (Python中的广播)

当我们在Python中使用NumPy这样的库来处理数组时,会遇到一个非常有用的技术叫做“广播”。广播允许我们用很直观的方式处理不同维度的数组。它背后的原理可能初看起来有点复杂,但一旦你掌握了,就会觉得非常强大和实用。

广播机制可以通过以下四点来理解:

  1. 首先,比较所有输入数组的维度。不足的部分会在其维度的前面加1,从而和最长的那个维度保持一致。
    举个例子,假设我们有一个形状为 ( 3 , ) (3,) (3,)的数组和一个形状为 ( 1 , 3 ) (1,3) (1,3)的数组,当它们进行运算时,第一个数组会被看作形状为 ( 1 , 3 ) (1,3) (1,3)

  2. 输出数组的维度是输入数组各个维度上的最大值。
    如果我们把一个形状为 ( 3 , 1 ) (3,1) (3,1)的数组和一个形状为 ( 3 , 4 ) (3,4) (3,4)的数组相加,那么输出数组的形状就会是 ( 3 , 4 ) (3,4) (3,4)

  3. 只有当输入数组在某个维度上的长度与输出数组相同,或者该维度的长度为1时,该输入数组才能用于计算。否则,计算会报错。
    这意味着,一个形状为 ( 3 , 4 ) (3,4) (3,4)的数组和一个形状为 ( 4 , 3 ) (4,3) (4,3)的数组是不能进行元素级别运算的,因为它们的维度不兼容。

  4. 当输入数组的某个维度长度为1时,它会沿着这个维度复制其内容,使其与输出数组在该维度上的长度一致。
    如果你用一个形状为 ( 3 , 1 ) (3,1) (3,1)的数组加上一个形状为 ( 3 , 4 ) (3,4) (3,4)的数组,那么第一个数组会沿着其第二个维度复制内容,好像它本来就是 ( 3 , 4 ) (3,4) (3,4)那样。

为了更好地理解,你可以想象广播像是一种让不同维度的数组能够和谐共存、一起工作的魔法。并且,通过使用reshape()函数,你可以轻松地调整数组的形状,确保它们可以正常地进行广播操作。

注意: 当你在编写代码时,尤其是涉及多维数组的计算时,最好明确你的数组的形状,以避免可能出现的错误或不可预见的结果。

6. 关于Python/Numpy向量的小提示

我们接下来要探讨Python中一些关于向量的细节,帮助你避免编码中的小陷阱。

在Python的Numpy库中,当你使用以下方式创建一个向量:

a = np.random.randn(5)

这样产生的向量a的形状是(5,)。这样的向量很特别,它既不是行向量,也不是列向量,我们称它为"rank 1 array"。这种向量在实际操作中可能会带来一些麻烦。比如,你试图转置这个向量,但结果还是它本身。为了避免这样的问题,当你需要创建一个(5,1)的列向量或一个(1,5)的行向量时,建议使用以下更规范的方式:

a = np.random.randn(5,1)
b = np.random.randn(1,5)

为了确保向量或数组的形状与你预期的相符,你可以使用assert语句来进行检查:

assert(a.shape == (5,1))

这个assert语句会检查a的形状是否为(5,1)。如果不是,程序会立刻报错并停止。养成使用assert语句的习惯,可以帮你及时捕捉潜在的错误,确保代码的准确性。

最后,如果需要,你还可以使用reshape函数来调整数组的形状,使其符合你的要求:

a.reshape((5,1))

希望这些小提示能帮你更好地在Python中处理向量,避免不必要的错误!

7. Jupyter/iPython笔记本快速指南 (Quick tour of Jupyter/iPython Notebooks)

Jupyter notebook(也被称为IPython notebook)是一个能够实现编程、数学、绘图和文本全方位互动的工具,它支持超过40种编程语言,包括最受欢迎的Python。在这个课程中,我们会使用Python语言,并且所有的编程练习都会在Jupyter notebook上完成。

8. 逻辑回归成本函数的解释 (Explanation of logistic regression cost function - optional)

在之前的课程中,我们已经初步讲解了逻辑回归的成本函数。现在,我们来更深入地理解这个成本函数是如何产生的。

首先,我们有预测输出 y ^ \hat{y} y^,其公式为:

y ^ = σ ( w T x + b ) \hat{y} = \sigma(w^T x + b) y^=σ(wTx+b)

这里, σ ( z ) = 1 1 + exp ⁡ ( − z ) \sigma(z) = \frac{1}{1 + \exp(-z)} σ(z)=1+exp(z)1 表示sigmoid函数。我们可以将 y ^ \hat{y} y^理解为给定输入x时,输出为正类(y=1)的预测概率:

y ^ = P ( y = 1 ∣ x ) \hat{y} = P(y=1 | x) y^=P(y=1∣x)

如果真实的y=1,预测概率为:

p ( y ∣ x ) = y ^ p(y|x) = \hat{y} p(yx)=y^

反之,如果y=0:

p ( y ∣ x ) = 1 − y ^ p(y|x) = 1 - \hat{y} p(yx)=1y^

我们可以结合上面两个方程得到:

P ( y ∣ x ) = y ^ y ( 1 − y ^ ) ( 1 − y ) P(y|x) = \hat{y}^y (1 - \hat{y})^{(1-y)} P(yx)=y^y(1y^)(1y)

为了简化问题,我们可以对上述概率应用对数函数。得到:

log ⁡ P ( y ∣ x ) = y log ⁡ y ^ + ( 1 − y ) log ⁡ ( 1 − y ^ ) \log P(y|x) = y \log \hat{y} + (1 - y) \log (1 - \hat{y}) logP(yx)=ylogy^+(1y)log(1y^)

我们的目标是让上述的概率尽可能大,所以,将其乘以-1,我们就得到了单个样本的损失函数,即:

L = − ( y log ⁡ y ^ + ( 1 − y ) log ⁡ ( 1 − y ^ ) ) L = - (y \log \hat{y} + (1 - y) \log (1 - \hat{y})) L=(ylogy^+(1y)log(1y^))

考虑到所有的m个训练样本,如果假设所有的样本都是独立同分布的,我们的目标是让整体的概率尽可能地大。通过引入对数函数并乘以-1,我们可以得到整体的成本函数:

J ( w , b ) = − 1 m ∑ i = 1 m [ y ( i ) log ⁡ y ^ ( i ) + ( 1 − y ( i ) ) log ⁡ ( 1 − y ^ ( i ) ) ] J(w, b) = - \frac{1}{m} \sum_{i=1}^m [y^{(i)} \log \hat{y}^{(i)} + (1 - y^{(i)}) \log (1 - \hat{y}^{(i)})] J(w,b)=m1i=1m[y(i)logy^(i)+(1y(i))log(1y^(i))]

在这里, 1 m \frac{1}{m} m1 是一个正则化因子,用于对所有样本的成本函数取平均。

9. 总结 (Summary)

在这一节,我们探讨了神经网络的基础:Python和向量化。当涉及深度学习程序时,利用向量化和矩阵运算可以极大地提高执行速度,节省宝贵的时间。通过逻辑回归为例,我们学习了如何将算法流程和梯度下降转换为向量化形式。此外,我们还简要介绍了Python编程的相关方法和策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快撑死的鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值