第二周:神经网络的编程基础之Python与向量化

简介: 向量化(Vectorization)就是利用矩阵运算的思想,大大提高运算速度。例如下面所示在Python中使用向量化要比使用循环计算速度快得多。

本节课我们将来探讨Python和向量化的相关知识。


1. Vectorization


深度学习算法中,数据量很大,在程序中应该尽量减少使用循环语句,而可以使用向量运算来提高程序运行速度。


向量化(Vectorization)就是利用矩阵运算的思想,大大提高运算速度。例如下面所示在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("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")


输出结果类似于:


250286.989866
Vectorized version:1.5027523040771484ms
250286.989866
For loop:474.29513931274414ms


从程序运行结果上来看,该例子使用for循环运行时间是使用向量运算运行时间的约300倍。因此,深度学习算法中,使用向量化矩阵运算的效率要高得多。


为了加快深度学习神经网络运算速度,可以使用比CPU运算能力更强大的GPU。事实上,GPU和CPU都有并行指令(parallelization instructions),称为Single Instruction Multiple Data(SIMD)。SIMD是单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集。SIMD能够大大提高程序运行速度,例如python的numpy库中的内建函数(built-in function)就是使用了SIMD指令。相比而言,GPU的SIMD要比CPU更强大一些。


2. More Vectorization Examples


应该尽量避免使用for循环而使用向量化矩阵运算。在python的numpy库中,我们通常使用np.dot()函数来进行矩阵运算。


我们将向量化的思想使用在逻辑回归算法上,尽可能减少for循环,而只使用矩阵运算。值得注意的是,算法最顶层的迭代训练的for循环是不能替换的。而每次迭代过程对J,dw,b的计算是可以直接使用矩阵运算。


3. Vectorizing Logistic Regression


整个训练样本构成的输入矩阵X的维度是(nxnx,m),权重矩阵w的维度是(nxnx,1),b是一个常数值,而整个训练样本构成的输出矩阵Y的维度为(1,m)。利用向量化的思想,所有m个样本的线性输出Z可以用矩阵表示:


381540e120bd450690b2f63d8715d340.png


在python的numpy库中可以表示为:


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


其中,w.T表示w的转置。


这样,我们就能够使用向量化矩阵运算代替for循环,对所有m个样本同时运算,大大提高了运算速度。


4. Vectorizing Logistic Regression’s Gradient Output


逻辑回归中的梯度下降算法如何转化为向量化的矩阵形式。对于所有m个样本,dZ的维度是(1,m),可表示为:


846b4939c71640ef94adbb6b1628b270.png


db可表示为:


e25269b30c3c42e79ccd2a2a72c1189c.png


对应的程序为:


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


dw可表示为:


193e1ebb72b04fb6957fe7cddcbf46f6.png


对应的程序为:


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是学习因子,决定w和b的更新速度。上述代码只是对单次训练更新而言的,外层还需要一个for循环,表示迭代次数。


5. Broadcasting in Python


下面介绍使用python的另一种技巧:广播(Broadcasting)。python中的广播机制可由下面四条表示:


  1. 让所有输入数组都向其中shape最长的数组看齐,shape中不足的部分都通过在前面加1补齐
  2. 输出数组的shape是输入数组shape的各个轴上的最大值
  3. 如果输入数组的某个轴和输出数组的对应轴的长度相同或者其长度为1时,这个数组能够用来计算,否则出错
  4. 当输入数组的某个轴的长度为1时,沿着此轴运算时都用此轴上的第一组值


简而言之,就是python中可以对不同维度的矩阵进行四则混合运算,但至少保证有一个维度是相同的。下面给出几个广播的例子,具体细节可参阅python的相关手册,这里就不赘述了。


20170926163908656.png


值得一提的是,在python程序中为了保证矩阵运算正确,可以使用reshape()函数来对矩阵设定所需的维度。这是一个很好且有用的习惯。


6. A note on python/numpy vectors


接下来我们将总结一些python的小技巧,避免不必要的code bug。


python中,如果我们用下列语句来定义一个向量:


a = np.random.randn(5)


这条语句生成的a的维度是(5,)。它既不是行向量也不是列向量,我们把a叫做rank 1 array。这种定义会带来一些问题。例如我们对a进行转置,还是会得到a本身。所以,如果我们要定义(5,1)的列向量或者(1,5)的行向量,最好使用下来标准语句,避免使用rank 1 array。


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))


7. Quick tour of Jupyter/iPython Notebooks


Jupyter notebook(又称IPython notebook)是一个交互式的笔记本,支持运行超过40种编程语言。本课程所有的编程练习题都将在Jupyter notebook上进行,使用的语言是python。


8. Explanation of logistic regression cost function(optional)


在上一节课的笔记中,我们介绍过逻辑回归的Cost function。接下来我们将简要解释这个Cost function是怎么来的。


首先,预测输出y^y^的表达式可以写成:


61f1e4cf19a945a9a03af4e6571d578c.png


其中,


6ab88c9a5b2e412cbf71b939453645f8.png


y^可以看成是预测输出为正类(+1)的概率:


f4b1cea4d85a41ee9f589b804b97e1d3.png


那么,当y=1时:


eb8bc3dd1ed94a2281c1ce6e2232abde.png


当y=0时:


71024bfc2e0e40ab989dc555753cb8e6.png


我们把上面两个式子整合到一个式子中,得到:


070965da42374bca8f3b2af827816053.png


由于log函数的单调性,可以对上式P(y|x)进行log处理:


bfcbd63ac4234e06b993c2393873ce2b.png


我们希望上述概率P(y|x)越大越好,对上式加上负号,则转化成了单个样本的Loss function,越小越好,也就得到了我们之前介绍的逻辑回归的Loss function形式。


47cc652574b84143a1cd15c4b3c61bad.png


如果对于所有m个训练样本,假设样本之间是独立同分布的(iid),我们希望总的概率越大越好:


27c6e770ea5f4cbcb3bb6e9647d3d0be.png


同样引入log函数,加上负号,将上式转化为Cost function:


657c6703d14441ff825151fcbcc1eb84.png


上式中,1/m表示对所有m个样本的Cost function求平均,是缩放因子。


9. Summary


本节课我们主要介绍了神经网络基础——python和向量化。在深度学习程序中,使用向量化和矩阵运算的方法能够大大提高运行速度,节省时间。以逻辑回归为例,我们将其算法流程包括梯度下降转换为向量化的形式。同时,我们也介绍了python的相关编程方法和技巧。

目录
相关文章
|
3天前
|
数据采集 存储 API
在信息时代,Python爬虫用于自动化网络数据采集,提高效率。
【7月更文挑战第5天】在信息时代,Python爬虫用于自动化网络数据采集,提高效率。基本概念包括发送HTTP请求、解析HTML、存储数据及异常处理。常用库有requests(发送请求)和BeautifulSoup(解析HTML)。基本流程:导入库,发送GET请求,解析网页提取数据,存储结果,并处理异常。应用案例涉及抓取新闻、商品信息等。
16 2
|
1天前
|
开发者 Python
元类,Python中的隐藏BOSS?掌握它,让你的编程之路畅通无阻
【7月更文挑战第7天】Python的元类是创建类的类,如同编程的“大BOSS”。它们让开发者在类创建时干预过程,添加功能,如自动注册、修改属性。元类通过`__new__`方法动态创建类,如示例中MetaClass得到Meta元类附加的属性。虽然使用需谨慎,以免增加复杂性,但元类提供了超越常规类的强大力量,解锁高级编程技术。
10 2
|
1天前
|
开发者 Python
Python元类实战:打造你的专属编程魔法,让代码随心所欲变化
【7月更文挑战第7天】Python的元类是编程的变形师,用于创建类的“类”,赋予代码在构建时的变形能力。
10 1
|
1天前
|
程序员 数据库连接 Python
解锁Python新姿势:上下文管理器的自定义技巧,让你的编程之路更顺畅
【7月更文挑战第7天】Python上下文管理器简化资源管理,确保异常时资源正确释放。通过实现`__enter__`和`__exit__`或使用`contextmanager`装饰器自定义管理器。示例展示了类定义和装饰器方法。自定义管理器提升代码可读性,防止资源泄露,是高效编程的关键。**
|
1天前
|
Python
Python编程实战:利用闭包与装饰器优化日志记录功能
【7月更文挑战第7天】Python的闭包和装饰器简化了日志记录。通过定义如`log_decorator`的装饰器,可以在不修改原函数代码的情况下添加日志功能。当@log_decorator用于`add(x, y)`函数时,调用时自动记录日志。进一步,`timestamp_log_decorator`展示了如何创建特定功能的装饰器,如添加时间戳。这些技术减少了代码冗余,提高了代码的可维护性。
8 1
|
1天前
|
设计模式 存储 Python
Python元类大揭秘:从理解到应用,一步步构建你的编程帝国
【7月更文挑战第6天】Python元类是创建类的对象的基石,允许控制类的生成过程。通过自定义元类,可在类定义时动态添加方法或改变行为。
11 0
|
1天前
|
设计模式 开发者 Python
惊呆了!Python元类竟能如此玩转,你的编程世界将不再有界限
【7月更文挑战第6天】Python元类是类的类,用于控制类的创建。它们让开发者能自定义类的行为,如添加方法、改变继承或实例化过程。例如,定义一个元类`my_metaclass`,它会在创建类时自动添加新方法。元类广泛应用在单例、插件系统和ORM等高级场景,拓展了Python代码的灵活性和威力。掌握元类,揭开编程魔法的面纱,为代码解锁更多可能。
7 0
|
1天前
|
存储 Python
逆天改命?Python元类:从菜鸟到大师,一键升级你的编程认知
【7月更文挑战第6天】Python的元类是类的构造器,允许控制类的创建。元类`Meta`通过`__new__`方法动态添加属性,如示例所示,创建`MyClass`时添加`new_attr`。元类还能实现高级功能,如单例模式,`SingletonMeta`元类确保同一类的所有实例相等。元类是进阶技术,能提升代码的灵活性和创造力。
|
3天前
|
存储 机器学习/深度学习 关系型数据库
Python 金融编程第二版(四)(5)
Python 金融编程第二版(四)
9 0
|
3天前
|
存储 SQL 数据库
Python 金融编程第二版(四)(4)
Python 金融编程第二版(四)
10 0