1.4数组变形
在机器学习和深度学习中,我们常需要将处理好的数据以模型能接受的格式发送给它,由模型进行一系列运算,最终返回一个结果。但是不同模型所接收输入格式不一样,需要对数据进行一系列运算,将数据处理成符合模型要求的格式。其中最常见的是矩阵或者数组的运算,常会需要把多个向量或矩阵按某轴方向合并或展平(如在卷积或循环神经网络中,在进入全连接层之前,需要把矩阵展平)
1.4.1修改数组的形状
1.reshape 函数
import numpy as np
arr = np.arange(10)#生成数组
print(arr)
#将向量arr 变换成2行5列
print(arr.reshape(5,-1))
# 指维度时可以只指定行数或列数,其他用-1 代替
print(arr.reshape(5,-1))
print(arr.reshape(-1,5))
运行结果如下:
[0 1 2 3 4 5 6 7 8 9]
[[0 1]
[2 3]
[4 5]
[6 7]
[8 9]]
[[0 1]
[2 3]
[4 5]
[6 7]
[8 9]]
[[0 1 2 3 4]
[5 6 7 8 9]]
值得注意,reshape函数支持只指定行数或列数,其余设置-1即可,但是指定的行数或列数一定要能被整除。若将上面的代码修改为arr.reshape(3,-1)将报错,10不可以被3整除。
2.resize函数
import numpy as np
arr = np.arange(10)#生成数组
print (arr)
arr.resize(2,5)
print(arr)
运行结果如下:
[0 1 2 3 4 5 6 7 8 9]
[[0 1 2 3 4]
[5 6 7 8 9]]
3.T函数
import numpy as np
#生成一个3行4列的数组
arr = np.arange(12).reshape(3,4)
print(arr)
print(arr.T)
运行结果如下:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]
4.ravel函数
ravel函数接收一个根据C语言格式(即按照行优先排序)或者Fortran语言格式(即按列优先排序)来进行展平的参数,默认情况下是按行优先排序。
import numpy as np
#生成一个3行4列的数组
arr = np.arange(6).reshape(2,-1)
print(arr)
#按照列优先,展平
print("按照列优先,展平")
print(arr.ravel('F'))
#按照行优先,展平
print("按照行优先,展平")
print(arr.ravel())
运行结果如下:
[[0 1 2]
[3 4 5]]
按照列优先,展平
[0 3 1 4 2 5]
按照行优先,展平
[0 1 2 3 4 5]
5.flatten(order='C')函数
把矩阵转换为向量,展平方式默认是行优先(即参数默认order='C'),这种需求经常出现在卷积网络与全连接层之间。
import numpy as np
a = np.floor(10*np.random.random((3, 4)))
print(a)
print(a.flatten(order='C'))
运行结果如下:
[[8. 2. 8. 6.]
[1. 1. 6. 3.]
[1. 3. 5. 1.]]
[8. 2. 8. 6. 1. 1. 6. 3. 1. 3. 5. 1.]
flatten(展平)运算在神经网络中经常使用,一般在网络需要把2维、3维等多维数组转换为一维数组时使用
6.squeeze函数
squeeze函数主要用于降维,可以把矩阵中含1的维度去掉。
import numpy as np
arr = np.arange(3).reshape(3,1)
print(arr.shape)#(3,1)
print(arr.squeeze().shape)#(3,)
arr1 = np.arange(6).reshape(3,1,2,1)
print(arr1.shape)
print(arr1.squeeze().shape)#(3,2)
7.transpose函数
transpose函数主要用于对高维矩阵进行轴对换,常用于深度学习中,比如把图像表示颜色的RGB顺序改为GBR顺序。
import numpy as np
arr2 = np.arange(24).reshape(2,3,4)
print(arr2.shape)#(2,3,4)
print(arr2.transpose(1,2,0).shape)#(3,4,2)
1.4.2合并数组
[说明]:(1)append\concatenate以及stack函数都有一个axis参数,用于控制数组合并是按行还是列排序 (2)append和concentrate函数中待合并的数组必须有相同的行数或列数(满足一个即可)(3)stack、hstack、vstack、dstack函数中待合并的数组必须具有相同的形状。
1.append
合并一维数组:
import numpy as np
a = np.array([1,2,3])
b = np.array([4,5,6])
c = np.append(a,b)
print(c)#合并结果:[1 2 3 4 5 6]
合并多维数组:
import numpy as np
a = np.arange(4).reshape(2,2)
b = np.arange(4).reshape(2,2)
#按行合并
c = np.append(a,b,axis=0)
print("按行合并后的结果")
print(c)
print ("合并后数据维度",c.shape)
#按列合并
d = np.append(a,b,axis=1)
print("按列合并后的结果")
print(d)
运行结果如下:
按行合并后的结果
[[0 1]
[2 3]
[0 1]
[2 3]]
合并后数据维度 (4, 2)
按列合并后的结果
[[0 1 0 1]
[2 3 2 3]]
2.concatenate
沿指定轴连接数组或矩阵:
import numpy as np
a =np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
c = np.concatenate((a, b), axis=0)
print(c)
d = np.concatenate((a, b.T), axis=1)
print(d)
运行结果如下:
[[1 2]
[3 4]
[5 6]]
[[1 2 5]
[3 4 6]]
3.stack
沿指定轴堆叠数组或矩阵:
import numpy as np
a =np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print(np.stack((a, b), axis=0))
运行结果如下:
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
4.zip
zip是Python的一个内置函数,多用于张量运算中。
import numpy as np
a =np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c=c=zip(a,b)
for i,j in c:
print(i, end=",")
print(j)
运行结果如下:
[1 2],[5 6]
[3 4],[7 8]
使用zip函数组合两个向量。
a1 = [1,2,3]
b2 = [4,5,6]
c1 = zip(a1,b2)
for i,j in c1:
print(i,end=",")
print(j)
运行结果如下:
1,4
2,5
3,6
1.5批处理
在深度学习中,源数据都比较多,所以通常需要采用批处理方式。如利用批量来计算梯度的随机梯度法就是一个典型应用。
拆分批次的步骤如下:
(1)得到数据集
(2)随机打乱数据
(3)定义批大小
(4)批处理数据集
import numpy as np
#生成10000个形状为2X3的矩阵
data_train = np.random.randn(10000,2,3)
#这是一个3维矩阵,第一个维度为样本数,后两个是数据形状
print(data_train.shape)
#(10000,2,3)
#打乱这10000条数据
np.random.shuffle(data_train)
#定义批量大小
batch_size=100
#进行批处理
for i in range(0,len(data_train),batch_size):
x_batch_sum=np.sum(data_train[i:i+batch_size])
print("第{}批次,该批次的数据之和:{}".format(i,x_batch_sum))
最后8行结果如下:
1.6节省内存
1.使用X = X + Y与 X += Y的区别
这两个表达式从数学角度上是一样的,但内存开销完全不同。因为 X += Y操作可以减少内存开销。下面我们用Python的id()函数来说明,id()函数提供了内存中引用对象的确切地址。
import numpy as np
Y = np.random.randn(10,2,3)
X=np.zeros_like(Y)
print(id(X))
X=X+Y
print(id(X))
运行结果如下:
2337442210384
2337442210480
X在运行X = X + Y前后id不同,说明指向不同内存区域。
import numpy as np
Y = np.random.randn(10,2,3)
X = np.zeros_like(Y)
print(id(X))
X+=Y
print(id(X))
运行结果如下:
1775518458448
1775518458448
X与 X += Y前后id相同,说明指向同一个区域
2.X = X + Y与X[:] = X + Y的区别
import numpy as np
Y = np.random.randn(10,2,3)
X=np.zeros_like(Y)
print(id(X))
X=X+Y
print(id(X))
运行结果如下:
2382592713296
2382592713392
前后id不同,代表指向不同内存区域
import numpy as np
Y = np.random.randn(10,2,3)
X=np.zeros_like(Y)
print(id(X))
X[:]=X+Y
print(id(X))
运行结果相同:
1344073212496
1344073212496
前后id相同,说明指向一个内存区域。
1.7通用函数
Numpy提供了两个基本的对象,即ndarray与ufunc。这里来介绍ufunc,ufunc是一种能对数组的每一个元素进行操作的函数,它们比math模块中的函数的更灵活。math模块的输入一般是标量,但Numpy中函数的输入可以是向量和矩阵,而利用向量和矩阵可以避免使用循环语句,这点在机器学习、深度学习中很重要。
【说明】np.max、np.sum、np.min等函数都涉及到一个有关轴的参数axis
1.math 与numpy函数的性能比较
import time
import math
import numpy as np
x = [i * 0.001 for i in np.arange(1000000)]#[0.0,0.001,0.002....]
start = time.process_time()
#测量这段代码执行所需要的时间
for i, t in enumerate(x):
x[i] = math.sin(t)
print ("math.sin:{:.4f}".format(time.process_time() - start) )
x = [i * 0.001 for i in np.arange(1000000)]
x = np.array(x)
start = time.process_time()
np.sin(x)
print ("numpy.sin:{:.4f}".format(time.process_time() - start) )
运行结果如下:
math.sin:0.5169
numpy.sin:0.0538
由此可得,numpy.sin 比math.sin快近10倍。
2.循环与向量运算比较
充分使用Python的numpy库中的内建函数,可实现计算的向量化,从而大大提高运行速度。numpy库中的内建函数使用SIMD指令。使用向量化要比使用循环计算速度要快的多。如果使用GPU,其性能将更强大,但Numpy不支持GPU。
import time
import numpy as np
x1 = np.random.rand(1000000)
x2 = np.random.rand(1000000)
#使用循环计算向量点积
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 for loop----- Computation time = " + str(1000*(toc - tic)) + "ms")
#使用numpy函数求点积
tic = time.process_time()
dot = 0
dot = np.dot(x1,x2)
toc = time.process_time()
print ("dot = " + str(dot) + "\n verctor version---- Computation time = " + str(1000*(toc - tic)) + "ms")
运行结果如下:
由此可得,使用for循环的运行时间比使用向量运算的慢,因此,在深度学习中一般使用向量矩阵运算。
1.8广播机制
Numpy要求通用函数要求输入的数组形状是一致的,当数组的形状不相同时,则会使用广播机制。调整数组的形状时,要满足一定的规则,规则如下:
(1)让所有输入的数组都向其中最长的数组看齐,长度不足的需要在前面部分加1补齐;如a为2*3*2,b为3*2,则b向a看齐,需在b的前面加1,变为1*3*2
(2)输出数组的形状,即输入数组形状的各个轴的最大值
(3)如果输入数组的某个轴和输出数组的对应轴的长度相同或者其长度为1时,则这个数组能够用来计算,否则将出错。
(4)当输入数组的某个轴的长度为1时,沿着此轴运算时都用(或复制)此轴上的第一组值
目的:A + B,其中A为4×1矩阵,B为一维向量(3,)
要实现A、B相加,需要做如下处理:
import numpy as np
A = np.arange(0, 40,10).reshape(4, 1)
B = np.arange(0, 3)
print("A矩阵的形状:{},B矩阵的形状:{}".format(A.shape,B.shape))
C=A+B
print("C矩阵的形状:{}".format(C.shape))
print(C)
运行结果如下:
A矩阵的形状:(4, 1),B矩阵的形状:(3,)
C矩阵的形状:(4, 3)
[[ 0 1 2]
[10 11 12]
[20 21 22]
[30 31 32]]