Python的矩阵传播机制&矩阵运算——消灭for循环!

Python的矩阵传播机制(Broadcasting)

我们知道在深度学习中经常要操作各种矩阵(matrix)。 回想一下,我们在操作数组(list)的时候,经常习惯于用for循环(for-loop)来对数组的每一个元素进行操作。例如:

my_list = [1,2,3,4]
new_list = []
for each in my_list:
    new_list.append(each*2)
print(new_list)  # 输出 [2,3,4,5]

如果是矩阵呢:

my_matrix = [[1,2,3,4],
             [5,6,7,8]]
new_matrix = [[],[]]
for i in range(2):    
    for j in range(4):
        new_matrix[i].append(my_matrix[i][j]*2)
print(new_matrix)# 输出 [[2, 4, 6, 8], [10, 12, 14, 16]]

实际上,上面的做法是十分的低效的!数据量小的话还不明显,如果数据量大了,尤其是深度学习中我们处理的矩阵往往巨大,那用for循环去跑一个矩阵,可能要你几个小时甚至几天。

Python考虑到了这一点,这也是本文主要想介绍的“Python的broadcasting”传播机制。 先说一句,python中定义矩阵、处理矩阵,我们一般都用numpy这个库。

下面展示什么是python的传播机制:

import numpy as np# 先定义一个3×3矩阵 A:
A = np.array(
    [[1,2,3],
     [4,5,6],
     [7,8,9]]) 

print("A:\n",A)
print("\nA*2:\n",A*2) # 直接用A乘以2
print("\nA+10:\n",A+10) # 直接用A加上10

运行结果:

A:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]

A*2:
 [[ 2  4  6]
 [ 8 10 12]
 [14 16 18]]

A+10:
 [[11 12 13]
 [14 15 16]
 [17 18 19]]

接着,再看看矩阵×(+)矩阵:

#定义一个3×1矩阵(此时也可叫向量了)
B = np.array([[10],
              [100],
              [1000]]) 
print("\nB:\n",B)
print("\nA+B:\n",A+B) 
print("\nA*B:\n",A*B)

运行结果:

B:
 [[  10]
 [ 100]
 [1000]]

A+B:
 [[  11   12   13]
 [ 104  105  106]
 [1007 1008 1009]]

A*B:
 [[  10   20   30]
 [ 400  500  600]
 [7000 8000 9000]]

可见,虽然A和B的形状不一样,一个是3×3,一个是3×1,但是我们在python中可以直接相加、相乘,相减相除也可以。

也许看到这,大家都对broadcasting有感觉了。 用一个图来示意一下:

所谓“传播”,就是把一个数或者一个向量进行“复制”,从而作用到矩阵的每一个元素上

有了这种机制,那进行向量和矩阵的运算,就太方便了! 理解了传播机制,就可以随心所欲地对矩阵进行各种便捷的操作了。

利用numpy的内置函数对矩阵进行操作:

numpy内置了很多的数学函数,例如np.log(),np.abs(),np.maximum()等等上百种。直接把矩阵丢进去,就可以算出新矩阵! 示例:

print(np.log(A))

输出把A矩阵每一个元素求log后得到的新矩阵:

array([[0.        , 0.69314718, 1.09861229],
       [1.38629436, 1.60943791, 1.79175947],
       [1.94591015, 2.07944154, 2.19722458]])

再比如深度学习中常用的ReLU激活函数,就是y=max(0,x),

也可以对矩阵直接运算:

X = np.array([[1,-2,3,-4],
              [-9,4,5,6]])
Y = np.maximum(0,X)
print(Y)

得到:

[[1 0 3 0]
 [0 4 5 6]]

更多的numpy数学函数,可以参见文档: https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.math.html

定义自己的函数来处理矩阵

其实这才是我写下本文的目的。。。前面扯了这么多,只是做个铺垫(/ω\)

我昨天遇到个问题,就是我要对ReLU函数求导,易知,y=max(0,x)的导函数是: y’ = 0 if x<0 y’ = 1 if x>0 但是这个y’(x)numpy里面没有定义,需要自己构建。 即,我需要将矩阵X中的小于0的元素变为0,大于0的元素变为1。 搞了好久没弄出来,后来在StackOverflow上看到了解决办法:

def relu_derivative(x):
    x[x<0] = 0
    x[x>0] = 1
    return x

X = np.array([[1,-2,3,-4],
              [-9,4,5,6]])

print(relu_derivative(X))

输出:

[[1 0 1 0]
 [0 1 1 1]]

居然这么简洁就出来了!!!ミ゚Д゚彡 (゚Д゚#)

这个函数relu_derivative中最难以理解的地方,就是x[x>0]了。 于是我试了一下:

X = np.array([[1,-2,3,-4],
              [-9,4,5,6]])
print(X[X>0])
print(X[X<0])

输出:

[1 3 4 5 6]
[-2 -4 -9]

它直接把矩阵X中满足条件的元素取了出来!原来python对矩阵还有这种操作!

震惊了我好久~ 所以可以这么理解,X[X>0]相当于一个“选择器”,把满足条件的元素选出来,然后直接全部赋值。 用这种方法,我们便可以定义各种各样我们需要的函数,然后对矩阵整体进行更新操作了!

综上

可以看出,python以及numpy对矩阵的操作简直神乎其神,方便快捷又实惠。其实上面忘了写一点,那就是计算机进行矩阵运算的效率要远远高于用for-loop来运算, 不信可以用跑一跑:

# vetorization vs for loop
# define two arrays a, b:
a = np.random.rand(1000000)
b = np.random.rand(1000000)

# for loop version:
t1 = time.time()
c = 0
for i in range(1000000):
    c += a[i]*b[i]
t2 = time.time()
print(c)
print("for loop version:"+str(1000*(t2-t1))+"ms")
time1 = 1000*(t2-t1)

# vectorization version:
t1 = time.time()
c = np.dot(a,b)
t2 = time.time()
print(c)
print("vectorization version:"+str(1000*(t2-t1))+"ms")
time2 = 1000*(t2-t1)

print("vectorization is faster than for loop by "+str(time1/time2)+" times!")

运行结果:

249765.8415288075
for loop version:627.4442672729492ms
249765.84152880745
vectorization version:1.5032291412353516ms
vectorization is faster than for loop by 417.39762093576525 times!

可见,用for方法和向量化方法,计算结果是一样,但是后者比前者快了400多倍! 因此,在计算量很大的时候,我们要尽可能想办法对数据进行Vectorizing,即“向量化”,以便让计算机进行矩阵运算。

原文发布于微信公众号 - SimpleAI(SimpleAI_1)

原文发表时间:2018-10-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏算法channel

机器学习储备(7):numpy一维数组和矩阵

Numpy 是用 python封装的科学计算库,是一个精简版matlab 。 下面总结下在模拟脊回归的超参数:收缩率,与权重参数的关系时,用到的一些numpy运...

43080
来自专栏素质云笔记

聚类︱python实现 六大 分群质量评估指标(兰德系数、互信息、轮廓系数)

之前关于聚类题材的博客有以下两篇: 1、 笔记︱多种常见聚类模型以及分群质量评估(聚类注意事项、使用技巧) 2、k-means+python︱sciki...

2.4K100
来自专栏Leetcode名企之路

【Leetcode】64. 最小路径和

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

31310
来自专栏深度学习自然语言处理

基于attention的seq2seq机器翻译实践详解

理理思路 文本处理,这里我是以eng_fra的文本为例,每行是english[tab]french,以tab键分割。获取文本,清洗。 分别建立字典,一个engl...

60760
来自专栏Vamei实验室

Python标准库12 数学与随机数 (math包,random包)

我们已经在Python运算中看到Python最基本的数学运算功能。此外,math包补充了更多的函数。当然,如果想要更加高级的数学功能,可以考虑选择标准库之外的n...

28580
来自专栏互联网大杂烩

算法岗面试

快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包...

8820
来自专栏数值分析与有限元编程

广义雅可比方法

标准雅可比方法只能求解标准特征值问题。对于广义特征值问题需要采用广义雅可比方法求解。 前面已提到标准Jacobi方法的理论依据是对于实对称阵 A,必有正交阵 ...

34950
来自专栏崔庆才的专栏

自然语言处理中句子相似度计算的几种方法

24850
来自专栏数据小魔方

左手用R右手Python系列——因子变量与分类重编码

今天这篇介绍数据类型中因子变量的运用在R语言和Python中的实现。 因子变量是数据结构中用于描述分类事物的一类重要变量。其在现实生活中对应着大量具有实际意义的...

47350
来自专栏C语言及其他语言

【优质题解】题号1174:【计算直线的交点数】 (C语言描述)

题号1174,原题见下图: ? 解题思路: 将n条直线排成一个序列,直线2和直线1最多只有一个交点,直线3和直线1,2最多有两个交点,……,直线n 和其他n...

31360

扫码关注云+社区

领取腾讯云代金券