paddlepaddle学习笔记2
前言
从之前的房价预测的实际演练中,我们发现在完成深度学习模型的过程中,第三方库numpy是极其重要的。所以在此处记录一下numpy的学习过程
ndarray数组的矢量性质
- ndarray数组中所有元素的数据类型相同、数据地址连续,批量操作数组元素时速度更快。而list列表中元素的数据类型可能不同,需要通过寻址方式找到下一个元素。
- ndarray数组支持广播机制,矩阵运算时不需要写for循环。
- NumPy底层使用C语言编写,内置并行计算功能,运行速度高于Python代码。
# Python原生的list
# 假设有两个list
a = [1, 2, 3, 4, 5]
b = [2, 3, 4, 5, 6]
# 完成如下计算
# 对a的每个元素 + 1
# a = a + 1 不能这么写,会报错
# a[:] = a[:] + 1 也不能这么写,也会报错
for i in range(5):
a[i] = a[i] + 1
如果使用ndarry实现同样的效果:
import numpy as np
a = np.array([1,2,3,4,5])
a+=1
输出结果为:array([2,3,4,5,6])
以及假如要实现两个list的相加:
# 计算 a和b中对应位置元素的和,是否可以这么写?
a = [1, 2, 3, 4, 5]
b = [2, 3, 4, 5, 6]
# 使用for循环,完成两个list对应位置元素相加
c = []
for i in range(5):
c.append(a[i] + b[i])
不难看出对于list中的任何元素都要进行for循环才能进行操作但是ndarry就可以直接利用矢量性质一步到位
# 使用numpy中的ndarray完成两个ndarray相加
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([2, 3, 4, 5, 6])
c = a + b
ndarry数组的广播机制
以下代码为例,不难看出,ndarry在进行一维和二维数组的相加的时候,会将一维数组广播到二维数组按位相加
# 自动广播机制,1维数组和2维数组相加
# 二维数组维度 2x5
# array([[ 1, 2, 3, 4, 5],
# [ 6, 7, 8, 9, 10]])
d = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# c是一维数组,维度5
# array([ 4, 6, 8, 10, 12])
c = np.array([ 4, 6, 8, 10, 12])
e = d + c
e
array([[ 5, 8, 11, 14, 17], [10, 13, 16, 19, 22]])
创建ndarry数组
创建ndarray数组最简单的方式就是使用array
函数,它接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组。下面通过实例体会下array
、arange
、zeros
、ones
四个主要函数的用法。
array
:创建嵌套序列(比如由一组等长列表组成的列表),并转换为一个多维数组。
# 导入numpy
import numpy as np
# 从list创建array
a = [1,2,3,4,5,6] # 创建简单的列表
b = np.array(a) # 将列表转换为数组
b
array([1, 2, 3, 4, 5, 6])
arange
:创建元素从0到10依次递增2的数组。np.arange(开始,结束,步长)
# 通过np.arange创建
# 通过指定start, stop (不包括stop),interval来产生一个1维的ndarray
a = np.arange(0, 10, 2)
a
array([0, 2, 4, 6, 8])
zeros
:创建指定长度或者形状的全0数组。np.zeros([一维array的数量,一个一维array中有多少个元素])
# 创建全0的ndarray
a = np.zeros([3,4])
a
array([[0., 0., 0.,0.], [0., 0., 0.,0.], [0., 0., 0.,0.]])
ones
:创建指定长度或者形状的全1数组。与上边同理,只是其中的单个元素0换为1
# 创建全1的ndarray
a = np.ones([3,3])
a
array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]])
ndarry数组的属性
ndarray的属性包括shape
、dtype
、size
和ndim
等,通过如下代码可以查看ndarray数组的属性。
shape
:数组的形状 ndarray.shape,1维数组(N, ),二维数组(M, N),三维数组(M, N, K)。dtype
:数组的数据类型。(数据类型一般有:int\float64\double等)size
:数组中包含的元素个数 ndarray.size,其大小等于各个维度的长度的乘积。(所有的元素的数量)ndim
:数组的维度大小,ndarray.ndim, 其大小等于ndarray.shape所包含元素的个数。
a = np.ones([3, 3])
print('a, dtype: {}, shape: {}, size: {}, ndim: {}'.format(a.dtype, a.shape, a.size, a.ndim))
运行结果:
a, dtype: float64, shape: (3, 3), size: 9, ndim: 2
ndarray数组的数据类型和形状
a.astype(np.int64) 强制类型转换
a.reshape([m,n]) 对元素切片为n行,每行有m个元素
#数据类型强制转换
a = np.ones([3,4])
b = a.astype(np.int64)
print('b, dtype: {}, shape: {}'.format(b.dtype,b.shape))
#改变ndarray形状
c = a.reshape([2,6])
print('c, dtype: {}, shape: {}'.format(c.dtype,c.shape))
运行结果:
b, dtype: int64, shape: (3, 4)
c, dtype: float64, shape: (2, 6)
ndarray数组的基本运算
标量和ndarray数组之间的运算
标量和ndarray数组之间的运算主要包括除法、乘法、加法和减法运算,具体代码如下所示。
# 标量除以数组,用标量除以数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
1. / arr
运行结果:
array([[1. , 0.5 , 0.33333333], [0.25 , 0.2 , 0.16666667]])
# 标量乘以数组,用标量乘以数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
2.0 * arr
运行结果:
array([[ 2., 4., 6.], [ 8., 10., 12.]])
# 标量加上数组,用标量加上数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
2.0 + arr
运行结果:
array([[3., 4., 5.], [6., 7., 8.]])
# 标量减去数组,用标量减去数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
2.0 - arr
运行结果:
array([[ 1., 0., -1.], [-2., -3., -4.]])
总结一下,标量跟ndarray的运算逻辑是:每一个元素跟标量做运算
两个ndarray数组之间的运算
两个ndarray数组之间的运算主要包括减法、加法、乘法、除法和开根号运算,具体代码如下所示。
# 数组 减去 数组, 用对应位置的元素相减
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.array([[11., 12., 13.], [21., 22., 23.]])
arr1 - arr2
array([[-10., -10., -10.], [-17., -17., -17.]])
# 数组 加上 数组, 用对应位置的元素相加
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.array([[11., 12., 13.], [21., 22., 23.]])
arr1 + arr2
array([[12., 14., 16.], [25., 27., 29.]])
# 数组 乘以 数组,用对应位置的元素相乘
arr1 * arr2
array([[ 11., 24., 39.], [ 84., 110., 138.]])
# 数组 除以 数组,用对应位置的元素相除
arr1 / arr2
array([[0.09090909, 0.16666667, 0.23076923], [0.19047619, 0.22727273, 0.26086957]])
# 数组开根号,将每个位置的元素都开根号
arr ** 0.5
array([[1. , 1.41421356, 1.73205081], [2. , 2.23606798, 2.44948974]])
总结一下,两个ndarray之间的运算逻辑是,相同维度的两组数据,相同位置的元素和对应位置的元素按照规则做运算。
ndarray数组的索引和切片
一维ndarray数组
一维数组的索引逻辑跟列表的索引是一样的
#一维数组的索引
a = np.arange(30)
print(a[10])
print(a[4:7])
a[4:7]=10
print(a[4:7])
#数组切片操作后产生的对象会直接改变原数组
#如果不想改变原数组,则需要调用np.copy函数
arr_slice = np.copy(a)
运行结果:
10
[4 5 6]
[10 10 10]
[ 0 1 2 3 10 10 10 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29]
多维ndarray数组的索引和切片
- 在多维数组中,各索引位置上的元素不再是标量而是多维数组。
- 以逗号隔开的索引列表来选取单个元素。
- 在多维数组中,如果省略了后面的索引,则返回对象会是一个维度低一点的ndarray。
多维ndarray数组的索引
接下来练习一下处理多维数组,想要处理就先创建一个多维数组:
a = np.arange(30)
arr3d = a.reshape([5,3,2])
print(arr3d)
#只有一个索引指标时,会在第0维上索引,
#后面的维度保持不变
print("arr3d[0]=",arr3d[0])
#两个索引指标
print("arr3d[0][1]=",arr3d[0][1])
print("arr3d[0,1]=",arr3d[0,1])
运行结果:
[[[ 0 1]
[ 2 3]
[ 4 5]]
[[ 6 7]
[ 8 9]
[10 11]]
[[12 13]
[14 15]
[16 17]]
[[18 19]
[20 21]
[22 23]]
[[24 25]
[26 27]
[28 29]]]
arr3d[0]=
[[0 1]
[2 3]
[4 5]]
arr3d[0][1]= [2 3]
arr3d[0,1]= [2 3]
多维ndarray数组的切片
在基于对列表的快速连贯生成方法进行多维数组的切片:[k for k in range(0,6,2)]
a = np.arange(24)
a
运行结果:array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])
# reshape成一个二维数组
a = a.reshape([6, 4])
a
运行结果:array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]])
# 使用for语句生成list
[k for k in range(0, 6, 2)]
[0, 2, 4]
# 结合上面列出的for语句的用法
# 使用for语句对数组进行切片
# 下面的代码会生成多个切片构成的list
# k in range(0, 6, 2) 决定了k的取值可以是0, 2, 4
# 产生的list的包含三个切片
# 第一个元素是a[0 : 0+2],
# 第二个元素是a[2 : 2+2],
# 第三个元素是a[4 : 4+2]
slices = [a[k:k+2] for k in range(0, 6, 2)]
slices
[array([[0, 1, 2, 3], [4, 5, 6, 7]]), array([[ 8, 9, 10, 11], [12, 13, 14, 15]]), array([[16, 17, 18, 19], [20, 21, 22, 23]])]
slices[0]
array([[0, 1, 2, 3], [4, 5, 6, 7]])
ndarray数组的统计方法
可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算。主要包括如下统计方法:
mean
:计算算术平均数,零长度数组的mean为NaN。std
和var
:计算标准差和方差,自由度可调(默认为n)。sum
:对数组中全部或某轴向的元素求和,零长度数组的sum为0。max
和min
:计算最大值和最小值。argmin
和argmax
:分别为最大和最小元素的索引。cumsum
:计算所有元素的累加。cumprod
:计算所有元素的累积。
mean:计算出限定的对象范围内的所有元素的算数平均数
arr = np.array([[1,2,3],[4,5,6],[7,8,9]])
print("arr_mean=",arr.mean())
print("arr[0]_mean=",arr[0].mean())
print("arr[2]_mean=",arr[2].mean())
运行结果:
arr_mean= 5.0
arr[0]_mean= 2.0
arr[2]_mean= 8.0
可见,mean函数的计算范围是针对每一个元素开展的。
print("arr.sum()=",arr.sum(),np.sum(arr))
print("arr.max()=",arr.max(),np.max(arr))
print("arr.min()=",arr.min(),np.min(arr))
运行结果:
arr.sum()= 45 45
arr.max()= 9 9
arr.min()= 1 1
求和求最值的函数,其运行逻辑也跟mean函数一样,也是针对ndarray数组中的每一个元素逐一操作的
接下来看一下如何针对特定的维度进行操作:
arr = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
# 指定计算的维度
# 沿着第1维求平均,也就是将[1, 2, 3]取平均等于2,[4, 5, 6]取平均等于5,[7, 8, 9]取平均等于8
print(arr.mean(axis = 1))
# 沿着第0维求和,也就是将[1, 4, 7]求和等于12,[2, 5, 8]求和等于15,[3, 6, 9]求和等于18
print(arr.sum(axis=0))
# 沿着第0维求最大值,也就是将[1, 4, 7]求最大值等于7,[2, 5, 8]求最大值等于8,[3, 6, 9]求最大值等于9
print(arr.max(axis=0))
# 沿着第1维求最小值,也就是将[1, 2, 3]求最小值等于1,[4, 5, 6]求最小值等于4,[7, 8, 9]求最小值等于7
print(arr.min(axis=1))
arr = np.array([[3,2,1],[4,5,6],[7,8,9],[10,11,12]])
# 找出最小元素的索引
print(arr.argmin(), arr.argmin(axis=0), arr.argmin(axis=1))
运行结果:
[ 2. 5. 8. 11.]
[22 26 30]
[10 11 12]
[ 1 4 7 10]
2 [0 0 0] [2 0 0 0]
第0维我们可以理解为,它的内部已经再没有[]了,也就是说它是最小的列表单元了,针对它进行操作的话,就需要竖向比较了。
随机数np.random
主要用以创建ndarray的随机数组,打乱顺序以及选取元素等相关操作
创建随机ndarray数组
设置随机数种子
# 可以多次运行,观察程序输出结果是否一致
# 如果不设置随机数种子,观察多次运行输出结果是否一致
np.random.seed(10)
a = np.random.rand(3, 3)
print(a)
多次运行的结果均相同:
[[0.77132064 0.02075195 0.63364823]
[0.74880388 0.49850701 0.22479665]
[0.19806286 0.76053071 0.16911084]]
对随机数种子可以理解为,你用一个数值给你接下来一个步骤所生成的随机数种下的种子,之后只要你的随机数种子还是这个值的话,或者你在生成随机数之前调用一下这个种子,然后你调用random函数生成的随机数都还是一样的。
均匀分布
# 生成均匀分布随机数,随机数取值范围在[0, 1)之间
a =np.random.rand(2,3)
print("a=",a)
# 生成均匀分布随机数,指定随机数取值范围和数组形状
b = np.random.uniform(low = -1.0, high = 1.0, size=(3,4))
print("b=",b)
运行结果:
a= [[0.40463757 0.63491504 0.89820407]
[0.58506969 0.37129278 0.54256925]]
b= [[-0.35235863 0.75003072 0.10550922 -0.44472179]
[-0.17675229 0.58455911 0.32316149 0.32126077]
[ 0.6812361 0.63971675 -0.80467707 -0.40779022]]
需要注意的是,random.rand()括号中的两个参数描述的是即将生成的ndarray数组的维度数据,若要指定生成的随机数的取值范围的话需要调用uniform函数,而后在uniform函数中描述清楚low 和 high的参数范围,而数组的维度数据则调用size参数直接传一个括号进去就可。
正态分布
# 生成正态分布随机数,指定均值loc和方差scale
a = np.random.normal(loc = 1.0, scale = 1.0, size = (3,3))
print(a)
运行结果:
[[-0.13470762 -0.14984214 1.04467457]
[ 2.53021305 -0.13292633 -0.59142512]
[ 1.61565544 1.19742219 0.90625467]]
随机打乱ndarray数组顺序
#生成一维数组
a = np.arange(0,30)
print("before random shuffle:",a)
#打乱一堆一维数组
np.random.shuffle(a)
print("after random shuffle:",a)
a = np.arange(0,30)
a = a.reshape(10,3)
print("before random shuffle:\n",a)
np.random.shuffle(a)
print("after random shuffle:\n",a)
观察之后不难发现关键在于shuffle函数,np.random.shuffle(ndarray数组)
随机选取元素
a = np.arange(30)
b = np.random.choice(a, size=5)
print("random choice one=",b)
运行结果:
random choice one= [22 13 15 1 21]
注意,只能随机选取部分元素,不能随机选择其他列表或者ndarray