1、确保矩阵或向量的形状正确
在对多个矩阵或者向量进行运算时,如果矩阵或向量的形状 (shape) 不正确,可能会出现令人摸不着头脑的错误。在编程过程中遵循以下的建议,可以有效减少一些不必要的错误:
(1)用 n 行 1 列的矩阵来表示一个列向量,用 1 行 n 列的矩阵来表示一个行向量。
import numpy as np
a = np.array([1,2,3,4])
b = np.array([[1,2,3,4]])
# 用不同的方式创建了包含相同元素的两个向量,但不建议用 a 这种方式,而是采用 b 的方式来创建。
c = np.random.rand(4)
d = np.random.rand(4,1)
# 不建议用 c 这种方式创建向量,而是采用 d 的方式来创建。
e = np.zeros(3)
f = np.zeros([3,1])
# 不建议用 e 这种方式创建向量,而是采用 f 的方式来创建。
(2)在进行矩阵或向量运算前,可以使用 assert() 函数来判断他们的形状是否满足运算的要求。
assert(A.shape[1] == B.shape[0]), "A的列数与B的行数不相同,因此矩阵A无法与矩阵B进行矩阵乘法!"
C = np.dot(A, B)
执行 assert() 函数所需的时间极少,所以可以大胆的使用 assert() 函数.。
2、使用现有函数,避免用 for 实现矩阵运算以减少运行时间
这里写了一段代码来计算 A B AB AB ,且比较了用np.dot()实现矩阵乘法以及用for循环来实现的所需时间。
import numpy as np
import time
#定义矩阵A与B的形状
m = 1
n = 10
k = 1000
#随机初始化矩阵A,B
A = np.random.rand(m,n)
B = np.random.rand(n,k)
#执行np.dot()实现矩阵乘法
time_0 = time.time() * 1000
C = np.dot(A,B)
time_1 = time.time() * 1000
#用for循环实现
time_2 = time.time() * 1000
D = np.zeros([m,k])
for i in range(m):
for j in range(k):
for h in range(n):
D[i][j] = D[i][j] + A[i][h] * B[h][j]
time_3 = time.time() * 1000
print("矩阵 A 的形状:", A.shape)
print("矩阵 B 的形状:", B.shape)
print("矩阵乘法用时:", time_1-time_0, "ms")
print("for 循环用时:", time_3-time_2, "ms")
下面通过对不同形状的 A 与 B 计算
A
B
AB
AB,可以看到,当 A 和 B 的不是太大时,用np.dot()实现矩阵乘法和 for 循环用时相差无几。
但是随着 A 和 B 元素的增多,可以明显的看到用np.dot()实现矩阵乘法的耗时始终维持在 5 毫秒左右,但是 for 循环的耗时却随之增长。尤其时最后一个测试最为明显,此时 for 循环的用时竟然达到了 10 秒左右。
这里只举了矩阵乘法这一个例子,其他矩阵运算也是如此。在编程中尽量使用 numpy 中现有的函数或技术,比如python广播、np.dot()、np.matmul()、np.sum()、np.mean()等,而避免使用 for 循环去实现矩阵运算。因为这些现有的函数经过多个版本的迭代不断被优化,其性能和效率基本上都很理想了。对于深度学习或者神经网络的训练,数据量往往都是很大的,而大量的 for 循环会成倍增加我们的训练时间。其次使用现有的函数可以让我们的代码更加精简,减少我们的编码时间和工作量。
3、数据标准化(Normalization)
数据的标准化(normalization)是将数据按比例缩放,使之落入一个小的特定区间。数据标准化方法有多种,在数据标准化方法的选择上,还没有通用的法则可以遵循。通常使用的有归一化。在执行梯度下降时,使用数据标准化可以提升模型的收敛速度。