Python教程

Python深度学习(一)——Numpy基础

本文主要是介绍Python深度学习(一)——Numpy基础,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

        • 1. 生成Numpy数组
          • 1.1 从已有数据中创建数组
          • 1.2 利用random模块生成数组
          • 1.3 创建特定形状的多维数组
          • 1.4 利用arange、linspace函数生成数组
        • 2.获取元素
        • 1.3 Numpy算术运算
          • 1.3.1 对应元素相乘
          • 1.3.2 点积运算
        • 1.4 数组变形
          • 1.4.1 变更数组形状
          • 1.4.2 合并数组
        • 1.5 批量处理
        • 1.6 通用函数
        • 1.7 广播机制
        • 1.8总结

在机器学习和深度学习中,图像、声音、文本等输入数据最终都要转换为数组或矩阵。如何有效地进行数组和矩阵的运算?使用Numpy。 Numpy是数据科学的通用语言,而且与PyTorch关系密切,它 是科学计算、深度学习的基石,尤其对PyTorch而言,其重要性更加明显。PyTorch中的Tensor与Numpy非常相似,之间可以非常方便地进行转换,掌握Numpy是学好PyTorch的重要基础。
为什么是Numpy?Python本身含有列表(list)和数组(array),但在深度学习中,这样结构的对象是不满足的。由于列表的元素可以是任何对象,因此列表中所保存的是对象的指针。例如为了保存一个[1,2,3],都需要有3个指针和3个整数对象。对于数值运算来说,这种结构比较浪费内存和CPU等宝贵资源。至于array对象,它可以直接保存数值,和C语言的一维数组比较类似。但不支持多维,也不适合做数值运算。
而Numpy(Numerical Python)弥补了这些不足。Numpy提供两种基本的对象: ndarray(N-dimensional Array Object)和ufunc(Universal Function Object)ndarray是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。
Numpy主要特点:

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

1. 生成Numpy数组

Numpy是Python外部库,并不再标准库中。故使用时,要先导入Numpy

import numpy as np

导入Numpy后,可通过np.+Tab键,查看可使用的函数
在这里插入图片描述
若对其中一些函数的使用不清楚,可以在对应函数+?,再运行,就可以看到如何使用函数的帮助信息。
在这里插入图片描述
在这里插入图片描述
Numpy封装了一个新的数据类型ndarray(N-dimensional Array),这是一个多维数组对象。该对象封装了许多常用的数学运算函数,方便数据处理、数据分析等。如何生成ndarray呢?这里介绍生成ndarray的几种方式,如从已有数据中创建,利用random创建,创建特定形状的多维数组,利用arange、linspace函数生成等。

1.1 从已有数据中创建数组

直接对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'>

将上述的列表换为元组同样适用。

1.2 利用random模块生成数组

深度学习中,经常需要对一些参数进行初始化,为了更有效地训练模型,提高性能,有些初始化还需要满足一定的条件,如满足正态分布或均匀分布等。这里介绍了几种常用的方法,如下列举了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'>
1.3 创建特定形状的多维数组

参数初始化时,有时需要生成一些特殊矩阵,如全是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]]
1.4 利用arange、linspace函数生成数组

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

2.获取元素

上边介绍了生成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.]]

1.3 Numpy算术运算

机器学习和深度学习中,涉及大量的数组或矩阵运算,这两种常用的运算。一种是对应元素相乘,又称为逐元乘法(Element-Wise Product),运算符为np.multiply(),或*。另一种是点积或内积元素,运算符为np.dot()

1.3.1 对应元素相乘

对应元素相乘(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)
1.3.2 点积运算

点积运算(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个维度)的元素个数必须保持一致。

1.4 数组变形

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

1.4.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.4.2 合并数组

在这里插入图片描述
1)append、concatenate以及stack都有一个axis参数,用于控制数组的合并方式是按行还是按列。
2)对于append和concatenate,待合并的数组必须有相同的行数或列数(满足一个即可)。
3)stack、hstack、dstack,要求待合并的数组必须具有相同的形状(shape)。

  • 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)
    print('合并后数据维度', d.shape)
    
    Output:
    按行合并后的结果
    [[0 1]
     [2 3]
     [0 1]
     [2 3]]
    合并后数据维度 (4, 2)
    按列合并后的结果
    [[0 1 0 1]
     [2 3 2 3]]
    合并后数据维度 (2, 4)
    
  • 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)
    
    Output:
    [[1 2]
     [3 4]
     [5 6]]
    [[1 2 5]
     [3 4 6]]
    
  • 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))
    print(np.stack((a, b), axis=1))
    
    Output:
    [[[1 2]
      [3 4]]
    
     [[5 6]
      [7 8]]]
    [[[1 2]
      [5 6]]
    
     [[3 4]
      [7 8]]]
    

1.5 批量处理

在深度学习中,由于源数据都比较大,所以通常需要用到批处理。如利用批量来计算梯度的随机梯度法(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)批处理数据集

1.6 通用函数

Numpy提供了两种基本对象,即ndarray和ufunc对象。前边介绍了ndarray,这里介绍Numpy的另一个对象通用函数(ufunc)。ufunc是universal function的缩写,它是一种能对数组的每个元素进行操作的函数。
许多ufunc函数都是用C语言级别实现的,所以计算速度非常快。此外,它们比math模块中的函数更灵活。math模块的输入一般是标量,但Numpy中的函数可以是向量或矩阵,而利用向量或矩阵可以避免使用循环语句,这点在机器学习、深度学习中非常重要。
在这里插入图片描述

  • 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。
  • 循环与向量运算的比较
    充分使用Python的Numpy库中的内建函数(Built-in Function),来实现计算的向量化,可大大地提高运行速度。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")
    
    Output:
    dot = 249746.44050395774
     for loop----- Computation time = 630.7314439999984ms
    dot = 249746.44050396432
     verctor version---- Computation time = 2.075084999997756ms
    
    可见向量运算的高效性,因此在深度学习算法中,一般都使用向量化矩阵进行运算。

1.7 广播机制

Numpy的Universal functions中要求输入的数组shape是一致的,当数组的shape不相等时,则会使用广播机制。不过,调整数组使得shape一样,需要满足一定的规则,否则将出错。

  • 让所有输入数组都向其中shape最长的数组看齐,不足的部分则通过在前面加1补齐
  • 输出数组的shape是输入数组shape的各个轴上的最大值
  • 如果输入数组的某个轴和输出数组的对应轴的长度相同或者某个轴的长度为1时,这个数组能被用来计算,否则出错;
  • 当输入数组的某个轴的长度为1时,沿着此轴运算时都用(或复制)此轴上的第一组值。
    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]]
    
    在这里插入图片描述

1.8总结

以上均为Numpy模块常用操作,尤其是矩阵的操作。更多操作可参阅Numpy官网

这篇关于Python深度学习(一)——Numpy基础的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!