[深度学习入门]PyTorch深度学习[数组变形、批量处理、通用函数、广播机制]


PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序

一、前言

  本文为PyTorch深度学习中[Numpy基础]的下篇,主要讲解数组变形、批量处理、通用函数和广播机制的相关知识。
  上期回顾:
  深度学习入门PyTorch深度学习——Numpy基础 (上)


二、数组变形

  在机器学习以及深度学习的任务中,通常需要将处理好的数据以模型能接收的格式输入给模型,然后由模型通过一系列的运算,最终返回一个处理结果
  然而,由于不同模型所接收的输人格式不一样,往往需要先对其进行一系列的变形和运算,从而将数据处理成符合模型要求的格式。在矩阵或者数组的运算中,经常会遇到需要 把多个向量或矩阵按某轴方向合并,或展平(如在卷积或循环神经网络中,在全连接层之前,需要把矩阵展平) 的情况。
  下面介绍几种常用的数据变形方法

2.1 更改数组的形状

  修改指定数组的形状是Numpy中最常见的操作之一,常见的方法有很多,下表列出了一些常用函数

函数 描述
arr.reshape 重新将向量arr维度进行改变,不修改向量本身
arr.resize 重新将向量arr 维度进行改变,修改向量本身
arr.T 对向量arr进行转置
arr.ravel 对向量arr进行展平,即将多维数组变成1维数组,不会产生原数组的副本
arr.flatten 对向量arr进行展平,即将多维数组变成1维数组,返回原数组的副本
arr.squeeze 只能对维数为1的维度降维。对多维数组使用时不会报错,但是不会产生任何影响
arr.transpose 对高维矩阵进行轴对换

  下面,我们来看一些实例。

2.1.1 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))

  输出结果:

[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)即为错误的。


2.1.2 resize

  改变向量的维度(修改向量本身):

import numpy as np
arr=np.arange(10)
print(arr)
#将向量arr维度变换为2行5列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]]

2.1.3 T(转置)

  向量转置:

import numpy as np
arr=np.arange(12).reshape(3,4)
#向量arr为3行4列
print(arr)
#将向量arr进行转置为4行3列
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]]

2.1.4 ravel

  向量展平:

import numpy as np
arr=np.arange(6).reshape(2,-l)
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]

2.1.5 flatten

  把矩阵转换为向量,这种需求经常出现在卷积网络与全连接层之间

import numpy as np
a=np.floor(10*np.random.random((3,4)))
print(a)
print(a.flatten())

  输出结果:

[[4. 0. 8. 5.]
 [1. 0. 4. 8.]
 [8. 2. 3. 7.]]
[4. 0. 8. 5. 1. 0. 4. 8. 8. 2. 3. 7.]

2.1.6 squeeze

  这是个主要用来降维的函数把矩阵中含1的维度去掉在PyTorch中还有一种与之相反的操作———torch.unsqueeze,这个后面再来介绍。

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(arrl.shape) #(3,1,2,1)
print(arr1.squeeze().shape) #(3,2)

2.1.7 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)

2.2 合并数组

  合并数组也是最常见的操作之一,下表列举了常见的用于数组或向量合并的方法

函数 描述
np.append 内存占用大
np.concatenate 没有内存问题
np.stack 沿着新的轴加入一系列数组
np.hstack 堆栈数组垂直顺序(行)
np.vstack 堆栈数组垂直顺序(列)
np.dstack 堆栈数组按顺序深入(沿第3维)
np.vsplit 将数组分解成垂直的多个子数组的列表

  说明:
  (1)append、concatenate 以及 stack 都有一个 axis参数用于控制数组的合并方式是按行还是按列
  (2)对于appendconcatenate待合并的数组必须有相同的行数或列数(满足一个即可)。
  (3)stack 、hstack 、 dstack 要求待合并的数组必须具有相同的形状(shape)。下面选择一些常用函数进行说明

2.2.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=l)
print('按列合并后的结果')
print(d)
print('合并后数据维度',d.shape)

  输出结果:

按行合并后的结果
[[0 1]
 [2 3]
 [0 1]
 [2 3]]
合并后数据维度 (4,2)
按列合并后的结果
[[0 1 0 1]
 [2 3 2 3]]
合并后数据维度 (2,4)

2.1.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)

  输出结果:

[[12]
 [3 4]
 [5 6]]
[[1 2 5]
 [3 4 6]]

2.1.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]]]

三、批量处理

  在深度学习中,由于源数据都比较大,所以通常需要用到批处理如利用批量来计算梯度的随机梯度法(SGD)就是一个典型应用。深度学习的计算一般比较复杂,并且数据量一般比较大如果一次处理整个数据,较大概率会出现资源瓶颈
  为了更有效地计算,一般将整个数据集分批次处理。与处理整个数据集相反的另一个极端是每次只处理一条记录。这种方法也不科学一次处理一条记录无法充分发挥GPU、Numpy的平行处理优势
  因此,在实际使用中往往采用批量处理(Mini-Batch)的方法。
  如何把大数据拆分成多个批次呢? 可采用如下步骤:
  (1)得到数据集
  (2)随机打乱数据
  (3)定义批大小
  (4)批处理数据集
  下面我们通过一个示例来具体说明:

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=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))

  最后5行结果:

9500批次,该批次的数据之和:17.637025804380929600批次,该批次的数据之和:-1.3609246073683879700批次,该批次的数据之和:-25.9122262392664459800批次,该批次的数据之和:32.0181369578358149900批次,该批次的数据之和:2.9002576614446935

  【说明】批次从0开始,所以最后一个批次是9900。


四、通用函数

  Numpy提供了两种基本的对象,即 ndarrayufunc 对象。前文已经介绍了ndarray,本节将介绍Numpy的另一个对象通用函数(ufunc)
  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 计算相关系数

4.1 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)]=np.array(x)
start=time.clock()np.sin(x)
print("numpy.sin:",time.clock()-start)

  打印结果:

math.sin:0.5169950000000005
numpy.sin:0.05381199999999886

4.2 循环与向量运算比较

  充分使用Python的 Numpy库中的内建函数(Built-in Function),来实现计算的向量化,可大大地提高运行速度。Numpy库中的内建函数使用了SIMD指令。如下使用的向量化要比使用循环计算速度快得多。如果使用GPU,其性能将更强大,不过Numpy不支持GPU
  PyTorch支持GPU,后面有机会将介绍PyTorch 如何使用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(xl)):
	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")

  输出结果:

dot=250215.601995
  for loop----- Computation time=798.3389819999998 ms
  dot=250215.601995
verctor version---- Computation time=1.885051999999554 ms

  从运行结果上来看,使用for 循环的运行时间大约是向量运算的400倍。因此,在深度学习算法中,一般都使用向量化矩阵进行运算。


五、广播机制

  Numpy 的 Universal functions 中要求输人的数组shape是一致的当数组的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时,沿着此轴运算时都用(或复制)此轴上的第一组值
  广播在整个Numpy中用于决定如何处理形状迥异的数组,涉及的算术运算包括(+,-,*,/…)。这些规则说得很严谨,但不直观,下面我们结合图形与代码来进一步说明。
  目的:A+B,其中A为4×1矩阵,B为一维向量( 3,)。
要相加,需要做如下处理:
  根据规则1,B需要向看齐,把B变为(1,3 )
  根据规则2,输出的结果为各个轴上的最大值,即输出结果应该为(4,3)矩阵,那么A如何由(4,1)变为(4,3)矩阵?B又如何由(1,3)变为(4,3)矩阵?
  根据规则4,用此轴上的第一组值(要主要区分是哪个轴),进行复制(但在实际处理中不是真正复制,否则太耗内存,而是采用其他对象如ogrid对象,进行网格处理)即可,详细处理过程如下图所示。
在这里插入图片描述
  代码实现:

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]]

六、小结

  本章主要介绍了Numpy模块的常用操作,尤其涉及对矩阵的操作,这些操作在后续程序中经常使用。Numpy内容很丰富,这里只列了一些主要内容,如果你想了解更多内容,可登录Numpy的官网(http://www.Numpy.org/)查看更多内容。
  最近在学习深度学习的优化算法,不知道大家想最先看哪一个…请在下方投个票吧!

猜你喜欢

转载自blog.csdn.net/m0_65748531/article/details/132147279