[Python/PyTorch基础] Numpy学习笔记

Numpy基础

引文

Python本身含有列表(list)和数组(array),但对于大数据来说,这些结构是有很多不足的。
list的元素可以是任何对象,因此列表中所保存的是对象的指针。
array对象可以直接保存数值,但是由于它不支持多维,在上面的函数也不多,因此也不适合做数值运算。
Numpy提供了两种基本的对象:

  • ndarray(N-dimensional Array Object)
  • ufunc(Universal Function Object)
    ndarray是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。

Numpy的主要特点

  • ndarray,快速节省空间的多维数组,提供数组化的算术运算和高级的广播功能。
  • 使用标准数学函数对整个数组的数据进行快速运算,且不需要编写循环。
  • 读取/写入磁盘上的阵列数据和操作存储器映像文件的工具。
  • 线性代数、随机数生成和傅里叶变换的能力。

Numpy主要内容

  • 如何生成Numpy数组。
  • 如何存取元素。
  • Numpy的算术运算。
  • 数组变形。
  • 批量处理。
  • Numpy的通用函数。
  • Numpy的广播机制。

生成Numpy数组

从已有数据中创建数组

直接对Python的基础数据类型(如列表、元组等)进行转换来生成ndarray:

1)将列表转换成ndarray:

import numpy as np
list1 = [2, 3, 4, 5]
nd1 = np.array(list1)

2)嵌套列表可以转换成多维ndarray:

import numpy as np
list2 = [[2, 3, 4, 5], [6, 7, 8, 9]]
nd2 = np.array(list2)

把上面示例中的列表换成元组也同样适用。

利用random模块生成数组

np.random 模块常用函数:

函数 描述
np.random.random 0到1之间随机数
np.random.uniform 均匀分布的随机数
np.random.randn 标准正态分布的随机数
np.random.normal 正态分布
np.random.randint 随机整数
np.random.shuffle 随机打乱顺序
np.random.seed 设置随机数种子
random_sample 生成随机的浮点数

创建特定形状的多维数组

参数初始化时,有时需要生成一些特殊矩阵,如全是0或1的数组或矩阵,这时我们可以利用np.zeros、np.ones、np.diag来实现。

函数 描述
np.zeros((3,4)) 创建3 x 4的元素全为0的数组
np.ones((3,4)) 创建3 x 4的元素全为1的数组
np.empty((2,3)) 创建2 x 3的空数组,但是空数据中的值不是0,是未初始化的垃圾值
np.zeros_like(ndarr) 以ndarr 相同维度创建元素全为0数组
np.ones_like(ndarr) 以ndarr 相同维度创建元素全为1数组
np.empty_like(ndarr) 以ndarr 相同维度创建空数组
np.eye(5) 创建一个5 x 5矩阵,对角线为1,其余为0
np.full((3,5), 666) 创建3 x 5的元素全为666的数组

将数据保存起来,再读取:

nd = np.random.random([5, 5])
np.savetxt(X = nd, fname = './test.txt')
nd1 = np.loadtxt('./test.txt')

利用arange、linspace函数生成数组

# arange([start,] stop[, step,], dtype=None)
print(np.arange(1, 4, 0.5))
# [1. 1.5 2. 2.5 3. 3.5]

start, stop指定范围; stop设定步长。start默认为0, step可以使小数。

np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)

linspace根据输入的指定数据范围和等份数量,自动生成一个线性等分向量。
如果retstep=True,会返回带步长的ndarray。
linspace必定会包含数据起点和重点。

获取元素

通过指定索引标签

np.random.seed(2020)
nd = np.random.random([10])
nd[3]
nd[3:6]
nd[1:6:2] # 截取固定间隔数据
nd[::-2] # 倒序取数
# 截取一个多维数组的一个区域内数据
nd2 = np.arange(25).reshape([5,5])
nd2[1:3,1:3]
# 截取一个多维数组中,数值在一个值域之内的数据
nd2[(nd2>3)&(nd2<10)]
# 截取多维数组中指定的行,如2,3行
nd2[[1,2]] # nd2[1:3,:]
# 截取多为数组中指定的列
nd2[:,1:3]

通过函数

如random.choice从指定样本中随机抽取数据。

from numpy import random as nr
a = np.arange(1,25,dtype=float)
# 随机可重复抽取
c1 = nr.choice(a, size=(3,4))
# 随机不重复抽取
c2 = nr.choice(a, size=(3,4),replace=False) #replace默认为True,可重复抽取
#随机按制度概率抽取
c3 = nr.choice(a, size=(3,4), p=a/np.sum(a))

算数运算

  • 矩阵的对应元素相乘
np.multiply(A, B)
# 或者A*B

A, B之间的对应元素相乘遵守广播规则。
由此,数组通过一些激活函数后,输出与输入的形式一致。

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))
  • 点积运算(内积)
np.dot(x1, x2)

x1, x2对应维度的元素个数必须保持一致。

数组变形

在矩阵或者数组的运算中,经常会遇到需要把多个向量或矩阵按某轴方向合并,或展平(如在卷积或循环神经网络中,在全连接层之前,需要把矩阵展平)的情况。

  • 改变数组的形状
    numpy中改变向量形状的一些函数:
函数 描述
arr.reshape 改变向量维度,不改变向量本身
arr.resize 改变向量维度,改变向量本身
arr.T 对向量进行转置
arr.ravel 对向量展平,即将多维数组变成1维,不会产生原数组的副本
arr.flatten 对向量展平,即将多维数组变成1维,返回原数组的副本
arr.squeeze **只能对维数为1的维度降维。**对多维数组使用不会产生影响
arr.transpose 对高维矩阵队形轴兑换

注意:
reshape: 指定维度时可以只指定一个,其他用-1代替
ravel:arr.ravel(‘F’) 按照列优先展平,否则默认按行优先
flatten:把矩阵转换为向量,这种需求经常出现在卷积网络和全连接层之间。
squeeze:比如(3,1)——> (3, ) ,(3, 1, 2, 1)——> (3,2)
transpose:对高维矩阵进行轴对换,在深度学习中常使用,比如讲图片RGB顺序改成GBR。

 arr = np.arange(24).reshape(2,3,4)
 print(arr.shape) # (2,3,4)
 print(arr.transpose(1,2,0).shape) # (3,4,2)
  • 合并数组
函数 描述
np.append 内存占用大
np.concatenate 没有内存问题
np.stack 沿着新的轴加入一系列数组
np.hstack 堆栈数组垂直顺序(行)
np.vstack 堆栈数组垂直顺序(列)
np.dstack 堆栈数组按顺序深入(沿第3维)
np.vsplit 将数组分解成垂直的多个子数组的列表

注意:

  • append,concatenate以及stack都有一个axis参数,用于控制数组的合并方式是按行还是按列。
  • 对于append和concatenate,待合并的数组必须有相同的行数或列数。
  • stack,hstack,dstack要求待合并的数组要有相同形状。

示例:

  • append
  1. 合并一维数组:
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]
  1. 合并多维数组
a = np.arange(4).reshape(2,2)
b = np.arange(4).reshape(2,2)
# 按行合并
c = np.append(a, b, axis=0)
print(c)
'''[[0 1]
	[2 3]
	[0 1]
	[2 3]]
'''
d = np.append(a, b, axis=1)
print(d)
'''[[0 1 0 1]
	[2 3 2 3]]
'''
  • concatenate
  1. 沿指定轴连接数组或矩阵
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]]
'''
  • stack
    沿指定轴堆叠数组或矩阵:
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.stack((a, b), axis=0)
print(c)
print(c.shape)
'''
	[[[1 2]
	  [3 4]]
	 [[5 6]
	  [7 8]]]
	(2, 2, 2)
'''

批量处理

在深度学习中,由于源数据都比较大,所以通常需要用到批处理。如利用批量来计算梯度的随机梯度法(SGD)就是一个典型应用。
一次处理一条记录无法充分发挥GPU、Numpy的平行处理优势。因此,在实际使用中往往采用 批量处理(Mini-Batch) 的方法。
如何把大数据拆分成多个批次呢?
1. 得到数据集
2. 随机打乱数据
3. 定义批大小
4. 批处理数据集

示例:

# 生成10000个形状为2*3的矩阵
data = np.random.randn(10000, 2 , 3)
# 3维矩阵,第1个维度是样本数,后两个是数据形状
print(data.shape)
# (10000, 2, 3)
# 打乱数据
np.random.shuffle(data)
# 定义批量大小
batch_size = 100
# 进行批处理
for i in range(0, len(data), batch_size):
	x_batch_sum = np.sum(data[i:i+batch_size])
	print(f"第{i}批次,该批次的数据之和为{x_batch_sum}.")

批次从0开始,最后5行结果如下:

# 第9500批次,该批次的数据之和为14.58947150077857.
# 第9600批次,该批次的数据之和为42.03495509156134.
# 第9700批次,该批次的数据之和为28.81790516315437.
# 第9800批次,该批次的数据之和为20.004793932616554.
# 第9900批次,该批次的数据之和为9.329281850375956.

通用函数

Numpy的另一个对象通用函数(ufunc,universal function),它是一种能对数组的每个元素进行操作的函数。
许多ufunc函数都是用C语言级别实现的,因此它们的计算速度非常快。
此外,它们比math模块中的函数更灵活。math模块的输入一般是标量,但Numpy中的函数可以是向量或矩阵,而利用向量或矩阵可以避免使用循环语句,这点在机器学习、深度学习中非常重要。下表为Numpy中常用的几个通用函数。

函数 使用方法
sqrt 计算序列化数据的平方根
sin, cos 三角函数
abs 绝对值
dot 矩阵运算
log, log10, log2 对数
exp 指数
cumsum, cumproduct 累计求和、求积
sum 求和
mean 求均值
median 求中位数
std 求标准差
var 求方差
corrcoef 求相关系数

充分使用Python的Numpy库中的内建函数(Built-in Function)来实现计算的向量化,可大大地提高运行速度。Numpy库中的内建函数使用了SIMD指令。
如果使用GPU,其性能将更强大,不过Numpy不支持GPU。PyTorch支持GPU。在深度学习算法中,一般都使用向量化矩阵进行运算。

广播机制

Numpy的Universal functions中要求输入的数组shape是一致的,当数组的shape不相等时,则会使用广播机制。
可归纳为以下4条规则:

  1. 让所有输入数组都向其中shape最长的数组看齐,不足的部分则通过在前面加1补齐
    如:a:2×3×2,b:3×2,则b向a看齐,在b的前面加1,变为:1×3×2
  2. 输出数组的shape是输入数组shape的各个轴上的最大值
  3. 如果输入数组的某个轴和输出数组的对应轴的长度相同或者某个轴的长度为1时,这个数组能被用来计算,否则出错;
  4. 当输入数组的某个轴的长度为1时,沿着此轴运算时都用(或复制)此轴上的第1组值。

示例:
目的:A+B,其中A为4×1矩阵,B为一维向量(3,)。

  1. 由规则1,B要变成(1,3)
  2. 由规则2,输出结果为各个轴上的最大值,则输出结果应该是(4,3)矩阵
  3. 由规则4,用此轴上的第一组值进行复制(不是真的复制,费内存)
    如下图所示:在这里插入图片描述
A = np.arange(0, 40, 10).reshape(4,1) # A.shape: (4,1)
B = np.arange(0, 3) # B.shape: (3,)
C = A + B
print(C)
print(C.shape)
'''
[[0 1 2]
 [10 11 12]
 [20 21 22]
 [30 31 32]]	
'''

想了解更多内容,可登录Numpy官网(http://www.Numpy.org/)查看更多内容。
本文根据《Python深度学习:基于PyTorch》第一章内容总结。
一起加油鸭ヾ(◍°∇°◍)ノ゙

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章