通过矢量化提高矩阵运算速度
向量点积:
向量的点乘,也叫向量的内积、数量积,对两个向量执行点乘运算,就是对这两个向量对应位一一相乘之后求和的操作,点乘的结果是一个标量。
定义: a⋅b=||a||||b||cos(θ) ,即是两个向量的模和两向量夹角余弦的乘积。
点乘的几何意义是可以用来表征或计算两个向量之间的夹角,以及在b向量在a向量方向上的投影。
矩阵内积:
内积是一种降维运算,变成一个数。矩阵的内积是每行每列的点积所组成的矩阵。所以,点积是一维向量之间的操作,内积是多维向量,即矩阵的操作。两者本质是一样的。其实,内积就是我们一般常用的矩阵乘积,行列进行相互乘积,作为结果矩阵指定位置的元素结果值。所以,要求a矩阵的列数量等于b矩阵的行数量。
示例代码:
x = numpy.array([1, 2])
y = numpy.array([10, 20])
print("Array inner:")
print(numpy.inner(x, y))
''' Output:
Array inner:
50
'''
x = numpy.mat([[1, 2], [3, 4]])
y = numpy.mat([10, 20])
print("Matrix inner:")
print(numpy.inner(x, y))
Output:
Matrix inner:
[[ 50]
[110]]
两个向量的叉乘,又叫向量积、外积、叉积,叉乘的运算结果是一个向量而不是一个标量,是向量的升维运算。并且两个向量的外积与这两个向量组成的坐标平面垂直。m维向量和n维向量的外积是m*n维矩阵。
定义:
a×b
矩阵外积:
假设a的维度是(m,n),b的维度是(p,q)
a×b=⎡⎣⎢a11...a1m.........a1m...amn⎤⎦⎥⎡⎣⎢b11...b1p.........b1q...bpq⎤⎦⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢a11∗b11...a1n∗b11a21∗b11...amn∗b11..................a11∗b1q...a1n∗b1qa21∗b1q...amn∗b1qa11∗b21...a1n∗b21a21∗b21...amn∗b21..................a11∗bpq...a1n∗bpqa21∗bpq...amn∗bpq⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥(1)
向量外积:
a=(x1,y1,z1)
b=(x2,y2,z2)
a×b=⎡⎣⎢ix1x2jy1y2kz1z2⎤⎦⎥=(y1z2−y2z1)i−(x1z2−x2z1)j+(x1y2−x2y1)k(2)
其中:
i=(1,0,0)j=(0,1,0)k=(0,0,1)
叉乘几何意义:
在三维几何中,向量a和向量b的叉乘结果是一个向量,更为熟知的叫法是法向量,该向量垂直于a和b向量构成的平面。
在二维空间中,叉乘还有另外一个几何意义就是:aXb等于由向量a和向量b构成的平行四边形的面积。
示例代码:
x = numpy.array([1, 3])
y = numpy.array([10, 20])
print("Array outer:")
print(numpy.outer(x, y))
''' Output:
Array outer:
[[10 20]
[30 60]]
'''
x = numpy.mat([[1, 2], [3, 4]])
y = numpy.mat([10, 20])
print("Matrix outer:")
print(numpy.outer(x, y))
Output:
Matrix outer:
[[10 20]
[20 40]
[30 60]
[40 80]]
定义:
a∗b
有三种情况:
a∗b=[a1…am][b1...bm](3)
a∗b=[a1…an]⎡⎣⎢⎢⎢b1b2...bm⎤⎦⎥⎥⎥ =⎡⎣⎢a1b1...a1bm.........anb1...anbm⎤⎦⎥(4)
第三种情况,就是两个矩阵尺寸相同,则是对应位置的元素乘积。
示例代码:
x = numpy.array([1, 3])
y = numpy.array([10, 20])
print("Array element-wise product:")
print(x * y)
输出:
Array element-wise product:
[10 60]
import time
x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]
#用循环来实现dot product
### CLASSIC DOT PRODUCT OF VECTORS IMPLEMENTATION ###
tic = time.process_time()
dot = 0
for i in range(len(x1)):
dot+= x1[i]*x2[i]
toc = time.process_time()
print ("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
#用循环来实现outer product
### CLASSIC OUTER PRODUCT IMPLEMENTATION ###
tic = time.process_time()
outer = np.zeros((len(x1),len(x2))) # we create a len(x1)*len(x2) matrix with only zeros
for i in range(len(x1)):
for j in range(len(x2)):
outer[i,j] = x1[i]*x2[j]
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
#用循环来实现 元素之间乘积
### CLASSIC ELEMENTWISE IMPLEMENTATION ###
tic = time.process_time()
mul = np.zeros(len(x1))
for i in range(len(x1)):
mul[i] = x1[i]*x2[i]
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
#用循环来实现泛化的点积,即内积。
### CLASSIC GENERAL DOT PRODUCT IMPLEMENTATION ###
W = np.random.rand(3,len(x1)) # Random 3*len(x1) numpy array
tic = time.process_time()
gdot = np.zeros(W.shape[0])
for i in range(W.shape[0]):
for j in range(len(x1)):
gdot[i] += W[i,j]*x1[j]
toc = time.process_time()
print ("gdot = " + str(gdot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
执行结果:
dot = 278
----- Computation time = 0.0ms
outer = [[ 81. 18. 18. 81. 0. 81. 18. 45. 0. 0. 81. 18. 45. 0.
0.]
[ 18. 4. 4. 18. 0. 18. 4. 10. 0. 0. 18. 4. 10. 0.
0.]
[ 45. 10. 10. 45. 0. 45. 10. 25. 0. 0. 45. 10. 25. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]
[ 63. 14. 14. 63. 0. 63. 14. 35. 0. 0. 63. 14. 35. 0.
0.]
[ 45. 10. 10. 45. 0. 45. 10. 25. 0. 0. 45. 10. 25. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]
[ 81. 18. 18. 81. 0. 81. 18. 45. 0. 0. 81. 18. 45. 0.
0.]
[ 18. 4. 4. 18. 0. 18. 4. 10. 0. 0. 18. 4. 10. 0.
0.]
[ 45. 10. 10. 45. 0. 45. 10. 25. 0. 0. 45. 10. 25. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0.]]
----- Computation time = 0.0ms
elementwise multiplication = [ 81. 4. 10. 0. 0. 63. 10. 0. 0. 0. 81. 4. 25. 0. 0.]
----- Computation time = 0.0ms
gdot = [ 17.02350757 18.34972831 15.85377834]
----- Computation time = 0.0ms
上述的运行结果,并没有很直观表现循环操作的耗时,这是因为测试时候选用的数据量少的缘故。
x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]
### VECTORIZED DOT PRODUCT OF VECTORS ###
tic = time.process_time()
dot = np.dot(x1,x2)
toc = time.process_time()
print ("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### VECTORIZED OUTER PRODUCT ###
tic = time.process_time()
outer = np.outer(x1,x2)
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### VECTORIZED ELEMENTWISE MULTIPLICATION ###
tic = time.process_time()
mul = np.multiply(x1,x2)
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### VECTORIZED GENERAL DOT PRODUCT ###
tic = time.process_time()
dot = np.dot(W,x1)
toc = time.process_time()
print ("gdot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
运行结果:
dot = 278
----- Computation time = 0.0ms
outer = [[81 18 18 81 0 81 18 45 0 0 81 18 45 0 0]
[18 4 4 18 0 18 4 10 0 0 18 4 10 0 0]
[45 10 10 45 0 45 10 25 0 0 45 10 25 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[63 14 14 63 0 63 14 35 0 0 63 14 35 0 0]
[45 10 10 45 0 45 10 25 0 0 45 10 25 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[81 18 18 81 0 81 18 45 0 0 81 18 45 0 0]
[18 4 4 18 0 18 4 10 0 0 18 4 10 0 0]
[45 10 10 45 0 45 10 25 0 0 45 10 25 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
----- Computation time = 0.0ms
elementwise multiplication = [81 4 10 0 0 63 10 0 0 0 81 4 25 0 0]
----- Computation time = 0.0ms
gdot = [ 17.02350757 18.34972831 15.85377834]
----- Computation time = 0.0ms
采用矢量化的方式执行,结果更加简洁高效(数据量大时候,更加明显)。且对于np.dot
是可以适用于矩阵或者矩阵向量的。对应元素的乘积,可以用np.multiply()
或者*
操作符。
import time
import numpy as np
a = np.random.rand(1000000)
b = np.random.rand(1000000)
tic = time.time()
c = np.dot(a,b)
print(c)
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("loop version:" + str(1000*(toc-tic)) + "ms")
运行结果如下:
250375.165705
vectorized version:24.0023136138916ms
250375.165705
loop version:543.0543422698975ms