ndarray(N-dimensional Array Object)和ufunc(Universal Function Object)
。
ndarray是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。
Numpy是Python外部库,并不再标准库中。故使用时,要先导入Numpy
import numpy as np
导入Numpy后,可通过np.+Tab键,查看可使用的函数
若对其中一些函数的使用不清楚,可以在对应函数+?,再运行,就可以看到如何使用函数的帮助信息。
Numpy封装了一个新的数据类型ndarray(N-dimensional Array)
,这是一个多维数组对象。该对象封装了许多常用的数学运算函数,方便数据处理、数据分析等。如何生成ndarray呢?这里介绍生成ndarray的几种方式,如从已有数据中创建,利用random创建,创建特定形状的多维数组,利用arange、linspace函数生成等。
直接对Python基础数据类型(如列表、元组等)进行转换来生成ndarray:
1)将列表转化为ndarray
import numpy as np lst1=[3,4,5,6,1,2] nd1=np.array(lst1) print(nd1) print(type(nd1))
Output:
[3 4 5 6 1 2] <class 'numpy.ndarray'>
2)将嵌套列表转化为多维ndarray
import numpy as np lst2=[[2,3,4,5],[9,8,7,6]] nd2=np.array(lst2) print(nd2) print(type(nd2))
Output:
[[2 3 4 5] [9 8 7 6]] <class 'numpy.ndarray'>
将上述的列表换为元组同样适用。
深度学习中,经常需要对一些参数进行初始化,为了更有效地训练模型,提高性能,有些初始化还需要满足一定的条件,如满足正态分布或均匀分布等。这里介绍了几种常用的方法,如下列举了np.random
模块常用的函数。
import numpy as np nd3=np.random.random([4,4]) print(nd3) print("nd3的形状:",nd3.shape)
Output:
[[0.53403198 0.98447249 0.00533233 0.65173917] [0.87977903 0.16099692 0.18023754 0.97709347] [0.04435656 0.98775115 0.59948141 0.51385331] [0.6364136 0.6397407 0.2583276 0.18612369]] nd3的形状: (4, 4) nd3类型: <class 'numpy.ndarray'>
为了每次生成同一份数据,指定一个随机种子,使用shuffle函数打乱生成的随机数。
import numpy as np np.random.seed(100) nd4 = np.random.randn(2,3) print(nd4) np.random.shuffle(nd4) print("随机打乱后数据:") print(nd4) print(type(nd4))
Output:
[[-1.74976547 0.3426804 1.1530358 ] [-0.25243604 0.98132079 0.51421884]] 随机打乱后数据: [[-1.74976547 0.3426804 1.1530358 ] [-0.25243604 0.98132079 0.51421884]] <class 'numpy.ndarray'>
参数初始化时,有时需要生成一些特殊矩阵,如全是0或1的数组或矩阵,这时可以利用np.zeros、np.ones、np.diag来实现。如下:
示例如下:
import numpy as np # 生成全是 0 的 3x3 矩阵 nd5 =np.zeros([3, 3]) print(nd5) #生成与nd5形状一样的全0矩阵 #np.zeros_like(nd5) # 生成全是 1 的 3x3 矩阵 nd6 = np.ones([3, 3]) print(nd6) # 生成 3 阶的单位矩阵 nd7 = np.eye(3) print(nd7) # 生成 3 阶对角矩阵 nd8 = np.diag([1, 2, 3]) print(nd8)
有时还可能需要把生成的数据暂时保存起来,以备后续使用。
import numpy as np nd9 =np.random.random([5, 5]) np.savetxt(X=nd9, fname='./test1.txt') nd10 = np.loadtxt('./test1.txt') print(nd10)
Output:
[[0.87014264 0.06368104 0.62431189 0.52334774 0.56229626] [0.00581719 0.30742321 0.95018431 0.12665424 0.07898787] [0.31135313 0.63238359 0.69935892 0.64196495 0.92002378] [0.29887635 0.56874553 0.17862432 0.5325737 0.64669147] [0.14206538 0.58138896 0.47918994 0.38641911 0.44046495]]
arange是numpy模块中的函数,其格式为:
arange([start,] stop[, step,], dtype=None)
其中start与stop用来指定范围,step用来设定步长。在生成一个ndarray时,start默认为0,步长step可为小数。与Python内置函数range类似。
import numpy as np print(np.arange(10)) # [0 1 2 3 4 5 6 7 8 9] print(np.arange(0, 10)) # [0 1 2 3 4 5 6 7 8 9] print(np.arange(1, 4, 0.5)) # [1. 1.5 2. 2.5 3. 3.5] print(np.arange(9, -1, -1)) # [9 8 7 6 5 4 3 2 1 0]
linspace
也是numpy模块中常用的函数,其格式为:
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
linspace可以根据输入的指定数据范围以及等份数量,自动生成一个线性等分向量,其中endpoint(包含终点)默认为True,等分数量num默认为50。如果将retstep设置为True,则会返回一个带步长的ndarray。
import numpy as np print(np.linspace(0,1,10)) #[0. 0.11111111 0.22222222 0.33333333 0.44444444 0.55555556 # 0.66666667 0.77777778 0.88888889 1.
除了上面介绍到的arange和linspace,Numpy还提供了logspace
函数,该函数的使用方法与linspace的使用方法一样.
import numpy as np print(np.logspace(0.1,1,10)) #[ 1.25892541 1.58489319 1.99526231 2.51188643 3.16227766 3.98107171 # 5.01187234 6.30957344 7.94328235 10. ]
上边介绍了生成ndarray
的几种方法。在数据生成后,又该如何读取所需要的数据呢?
import numpy as np np.random.seed(2019) nd11 = np.random.random([10]) #获取指定位置的数据,获取第4个元素 nd11[3] #截取一段数据 nd11[3:6] #截取固定间隔数据 nd11[1:6:2] #倒序取数 nd11[::-2] #截取一个多维数组的一个区域内数据 nd12=np.arange(25).reshape([5,5]) nd12[1:3,1:3] #截取一个多维数组中,数值在一个值域之内的数据 nd12[(nd12>3)&(nd12<10)] #截取多维数组中,指定的行,如读取第2,3行 nd12[[1,2]] #或nd12[1:3,:] ##截取多维数组中,指定的列,如读取第2,3列 nd12[:,1:3]
获取数组中的部分元素除了通过指定索引标签来实现外,还可以通过使用一些函数来实现,如通过random.choice
函数从指定的样本中随机抽取数据。
import numpy as np from numpy import random as nr a=np.arange(1,25,dtype=float) c1=nr.choice(a,size=(3,4)) #size指定输出数组形状 c2=nr.choice(a,size=(3,4),replace=False) #replace缺省为True,即可重复抽取。 #下式中参数p指定每个元素对应的抽取概率,缺省为每个元素被抽取的概率相同。 c3=nr.choice(a,size=(3,4),p=a / np.sum(a)) print("随机可重复抽取") print(c1) print("随机但不重复抽取") print(c2) print("随机但按制度概率抽取") print(c3)
Output:
随机可重复抽取 [[19. 6. 24. 22.] [21. 17. 16. 2.] [23. 15. 13. 11.]] 随机但不重复抽取 [[ 5. 14. 23. 15.] [ 8. 24. 6. 18.] [13. 10. 21. 11.]] 随机但按制度概率抽取 [[19. 21. 18. 11.] [10. 21. 23. 19.] [18. 19. 11. 20.]]
机器学习和深度学习中,涉及大量的数组或矩阵运算,这两种常用的运算。一种是对应元素相乘,又称为逐元乘法(Element-Wise Product),运算符为np.multiply(),或*
。另一种是点积或内积元素,运算符为np.dot()
。
对应元素相乘(Element-Wise Product)是两个矩阵中对应元素乘积。np.multiply
函数用于数组或矩阵对应元素相乘,输出与相乘数组或矩阵的大小一致,格式如下:
multiply(x1, x2, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj])
其中x1、x2之间的对应元素相乘遵守广播规则,以下通过一些示例说明。
import numpy as np A = np.array([[1, 2], [-1, 4]]) B = np.array([[2, 0], [3, 4]]) A*B
Output:
array([[ 2, 0], [-3, 16]])
Numpy数组不仅可以和数组进行对应元素相乘,还可以和单一数值(或称为标量)进行运算。运算时,Numpy数组中的每个元素都和标量进行运算,其间会用到广播机制.
推而广之,数组通过一些激活函数后,输出与输入形状一致。
X=np.random.rand(2,3) def softmoid(x): return 1/(1+np.exp(-x)) def relu(x): return np.maximum(0,x) def softmax(x): return np.exp(x)/np.sum(np.exp(x)) print("输入参数X的形状:",X.shape) print("激活函数softmoid输出形状:",softmoid(X).shape) print("激活函数relu输出形状:",relu(X).shape) print("激活函数softmax输出形状:",softmax(X).shape)
Output:
输入参数X的形状: (2, 3) 激活函数softmoid输出形状: (2, 3) 激活函数relu输出形状: (2, 3) 激活函数softmax输出形状: (2, 3)
点积运算(Dot Product)又称为内积,在Numpy用np.dot
表示,其一般格式为:
numpy.dot(a, b, out=None)
import numpy as np X1=np.array([[1,2],[3,4]]) X2=np.array([[5,6,7],[8,9,10]]) X3=np.dot(X1,X2) print(X3)
Output:
[[21 24 27] [47 54 61]]
矩阵X1和矩阵X2进行点积运算,其中X1和X2对应维度(即X1的第2个维度与X2的第1个维度)的元素个数必须保持一致。
在机器学习以及深度学习的任务中,通常需要将处理好的数据以模型能接收的格式输入给模型,然后由模型通过一系列的运算,最终返回一个结果。然而,由于不同模型所接收的输入格式不一样,往往需要先对其进行一系列的变形和运算,从而将数据处理成符合模型要求的格式。在矩阵或者数组的运算中,经常会遇到需要把多个向量或矩阵按某轴方向合并,或展平(如在卷积或循环神经网络中,在全连接层之前,需要把矩阵展平)的情况。以下是几种常用的数据变形方法。
修改指定数组的形状是Numpy中最常见的操作之一,方法有很多。
reshape
import numpy as np arr=np.arange(10) print(arr) # 将向量 arr 维度变换为2行5列 print(arr.reshape(2, 5)) # 指定维度时可以只指定行数或列数, 其他用 -1 代替 print(arr.reshape(5, -1)) print(arr.reshape(-1, 5))
Output:
[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是必要的。且所指定行数或列数一定要能被整除。
resize
import numpy as np arr=np.arange(10) print(arr) # 将向量 arr 维度变换为2行5列 arr.resize(2,5) print(arr)
Output:
[0 1 2 3 4 5 6 7 8 9] [[0 1 2 3 4] [5 6 7 8 9]]
T(向量转置)
import numpy as np arr=np.arange(15).reshape(3,5) print(arr) # 将向量 arr 转置为5行3列 print(arr.T)
Output:
[[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] [[ 0 5 10] [ 1 6 11] [ 2 7 12] [ 3 8 13] [ 4 9 14]]
ravel(向量展平)
import numpy as np arr=np.arange(6).reshape(2,-1) print(arr) #按照列优先,展平 print("按照列优先,展平") print(arr.ravel('F')) # 按照行优先,展平 print("按照行优先,展平") print(arr.ravel())
Output:
[[0 1 2] [3 4 5]] 按照列优先,展平 [0 3 1 4 2 5] 按照行优先,展平 [0 1 2 3 4 5]
flatten(矩阵转化为向量,常出现在卷积网络与全连接层之间)
import numpy as np a =np.floor(10*np.random.random((3,4))) print(a) print(a.flatten())
Output:
[[3. 3. 7. 5.] [6. 4. 1. 2.] [4. 5. 9. 6.]] [3. 3. 7. 5. 6. 4. 1. 2. 4. 5. 9. 6.]
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) #(3, 1, 2, 1) print(arr1.squeeze().shape) #(3,2)
transpose(对高维矩阵进行轴对换)
import numpy as np arr=np.arange(24).reshape(2,3,4) print(arr.shape) #(2,3,4) print(arr.transpose().shape) #(4,3,2)
1)append、concatenate
以及stack
都有一个axis
参数,用于控制数组的合并方式是按行还是按列。
2)对于append和concatenate,待合并的数组必须有相同的行数或列数(满足一个即可)。
3)stack、hstack、dstack,要求待合并的数组必须具有相同的形状(shape)。
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) print('合并后数据维度', d.shape)Output:
按行合并后的结果 [[0 1] [2 3] [0 1] [2 3]] 合并后数据维度 (4, 2) 按列合并后的结果 [[0 1 0 1] [2 3 2 3]] 合并后数据维度 (2, 4)
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)Output:
[[1 2] [3 4] [5 6]] [[1 2 5] [3 4 6]]
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)) print(np.stack((a, b), axis=1))Output:
[[[1 2] [3 4]] [[5 6] [7 8]]] [[[1 2] [5 6]] [[3 4] [7 8]]]
在深度学习中,由于源数据都比较大,所以通常需要用到批处理。如利用批量来计算梯度的随机梯度法(SGD)就是一个典型应用。深度学习的计算一般比较复杂,并且数据量一般比较大,如果一次处理整个数据,较大概率会出现资源瓶颈。为更有效地计算,一般将整个数据集分批次处理。与处理整个数据集相反的另一个极端是每次只处理一条记录,这种方法也不科学,一次处理一条记录无法充分发挥GPU、Numpy的平行处理优势。因此,在实际使用中往往采用批量处理(Mini-Batch)方法。如何把大数据拆分成多个批次呢?可采用如下步骤:
import numpy as np #生成10000个形状为2X3的矩阵 data_train = np.random.randn(10000,2,3) #这是一个3维矩阵,第1个维度为样本数,后两个是数据形状 print(data_train.shape) #(10000,2,3) #打乱这10000条数据 np.random.shuffle(data_train) #定义批量大小 batch_size=1000 #进行批处理 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))Output:
(10000, 2, 3) 第0批次,该批次的数据之和:-18.08598468837612 第1000批次,该批次的数据之和:167.26905751590118 第2000批次,该批次的数据之和:-39.48224416996322 第3000批次,该批次的数据之和:-74.24661472350503 第4000批次,该批次的数据之和:10.274965250772805 第5000批次,该批次的数据之和:-56.32505188423596 第6000批次,该批次的数据之和:-113.44731706106604 第7000批次,该批次的数据之和:-82.65170444631626 第8000批次,该批次的数据之和:-51.83454832338842 第9000批次,该批次的数据之和:-5.972071452288738
得到数据集
2)随机打乱数据
3)定义批大小
4)批处理数据集
Numpy提供了两种基本对象,即ndarray和ufunc
对象。前边介绍了ndarray,这里介绍Numpy的另一个对象通用函数(ufunc)。ufunc是universal function的缩写,它是一种能对数组的每个元素进行操作的函数。
许多ufunc函数都是用C语言级别实现的,所以计算速度非常快。此外,它们比math模块中的函数更灵活。math模块的输入一般是标量,但Numpy中的函数可以是向量或矩阵,而利用向量或矩阵可以避免使用循环语句,这点在机器学习、深度学习中非常重要。
import time import math import numpy as np x = [i * 0.001 for i in np.arange(1000000)] start = time.clock() for i, t in enumerate(x): x[i] = math.sin(t) print ("math.sin:", time.clock() - start ) x = [i * 0.001 for i in np.arange(1000000)] x = np.array(x) start = time.clock() np.sin(x) print ("numpy.sin:", time.clock() - start )Output:
math.sin: 0.3137710000000027 numpy.sin: 0.019892999999999716可见,numpy运算要显著快于math。
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")Output:
dot = 249746.44050395774 for loop----- Computation time = 630.7314439999984ms dot = 249746.44050396432 verctor version---- Computation time = 2.075084999997756ms可见向量运算的高效性,因此在深度学习算法中,一般都使用向量化矩阵进行运算。
Numpy的Universal functions中要求输入的数组shape是一致的,当数组的shape不相等时,则会使用广播机制。不过,调整数组使得shape一样,需要满足一定的规则,否则将出错。
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)Output:
A矩阵的形状:(4, 1),B矩阵的形状:(3,) C矩阵的形状:(4, 3) [[ 0 1 2] [10 11 12] [20 21 22] [30 31 32]]
以上均为Numpy模块常用操作,尤其是矩阵的操作。更多操作可参阅Numpy官网