0、写在前面
之前虽然做过不少项目,但是感觉都是跑的demo,其中的语法,模型修改,算法重构自己一点也不清楚,所以准备系统的学习Pytorch,真正理解模型的创建和训练和修改。我打算对着我之前推荐的那本书来学,一步一步学习,在六月之前学习完。在寒假的时候,我就决定在正式开始深度学习,先把numpy学习完。因为在之前接触numpy和少量深度学习的知识之后,发现numpy中的矩阵和深度学习中的张量tensor十分相似,这两个之间常常会发生转化。所以我认为,学好numpy对于深度学习来说是必不可少的。
一、Numpy基础知识
我们都知道在计算机视觉中,图像的本质就是像素矩阵。说是对图像进行处理,但其实是对矩阵进行处理。而Numpy是数据科学的通用语言,它是科学计算、矩阵运算、深度学习的基石。numpy中的对象称为矩阵对象(ndarray对象),它直接保存数值,与C语言中的一维数组比较类似。
numpy主要有以下主要特点:
- 快速、节省空间,提供数组化的算术运算和高级的广播功能
- 使用标准数学函数对整个数组的数据进行快速操作,而不需要编写循环。
- 提供读写/写入磁盘上的阵列数据和操作存数器映像文件的工具(存储读取功能)
- 提供线型代数、随机数生成和傅里叶变换的能力
- 提供集成C、C++、Fortran代码的工具
我们使用Opencv库对图像进行深入探索,首先我们写一段最基本的Opencv读取图片显示的代码:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_1.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/16 18:22
"""
import cv2
image = cv2.imread("image.jpg")
cv2.imshow("image",image)
cv2.waitKey(0)
cv2.destroyAllWindows()
用type函数查看image的类型:
print(type(image))
# <class 'numpy.ndarray'>
从上面例子可以看出来,image图片的本质其实就是一个矩阵,np.ndarray。
二、数组属性
在Numpy中,维度被称为轴。np.ndarray有三个重要属性:
- nd.array.ndim:数组的维度(轴)的个数
- nd.array.shape:数组的维度,值为一个整数元组,元组的值代表对应的轴的长度。(x,y)中的x代表这个数组有几行,y代表这个数组有几列。
- nd.array.dtpye:数据类型,描述数组中元素的类型。
同样,我们以具体的例子进行讲解:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_1.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/16 18:22
"""
import numpy as np
import cv2
image = cv2.imread("image.jpg")
image_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 转成灰度图
print("image的维度为:",image.ndim)
print("image_gray的维度为:",image_gray.ndim)
print("image的大小为:",image.shape)
print("image_gray的大小为:",image_gray.shape)
print("image的数据类型为:",image.dtype)
print("image_gray的数据类型为:",image_gray.dtype)
# image的维度为: 3
# image_gray的维度为: 2
# image的大小为: (1080, 1920, 3)
# image_gray的大小为: (1080, 1920)
# image的数据类型为: uint8
# image_gray的数据类型为: uint8
ndim为维度,彩色图片通道数为3,维度为3;灰度图片通道数为1,维度为2。转化前后图片的大小不变,都为1080*1920,数据类型也不变,都为uint8。
将列表转为矩阵使用array函数:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_2.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/16 19:48
"""
import numpy as np
import cv2
# 将列表转化为ndarray
list_1 = [0,1,2,3,4,5]
image_1 = np.array(list_1)
print(image_1.ndim)
print(image_1.shape)
# 将嵌套列表转化为ndarray
list_2 = [[0,1,2,3,4],[5,6,7,8,9]]
image_2 = np.array(list_2)
print(image_2.ndim)
print(image_2.shape)
# 将嵌套列表转化为ndarray,并用cv2显示
list_3 = [[[1,2,3],[1,2,3],[1,2,3]],
[[1,2,3],[1,2,3],[1,2,3]],
[[1,2,3],[1,2,3],[1,2,3]]]
image_3 = np.array(list_3,dtype="uint8")
print(image_3.ndim)
print(image_3.shape)
print(image_3.dtype)
cv2.imshow("image",image_3)
cv2.waitKey(0)
cv2.destroyAllWindows()
用array函数能够将list列表转为ndarray格式,注意如果想用opencv进行图片显示的话,注意array转化是,将dtpye设置成“uint8”,这样才能够用opencv正常显示。
三、random模块生成数组
在深度学习时,我们常常要对参数进行初始化,这就涉及到np.random进行生成数组。接下来用一个表和一段代码介绍np.random生成数组:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_3.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/17 9:06
"""
import numpy as np
print('生成形状(4, 4),值在0-1之间的随机数:')
print(np.random.random((4, 4)), end='\n\n')
# 产生一个取值范围在[1, 50)之间的数组,数组的shape是(3, 3)
# 参数起始值(low)默认为0, 终止值(high)默认为1。
print('生成形状(3, 3),值在low-high之间的随机整数::')
print(np.random.randint(low=1, high=50, size=(3, 3)), end='\n\n')
print('产生的数组元素是均匀分布的随机数:')
print(np.random.uniform(low=1, high=3, size=(3, 3)), end='\n\n')
print('生成满足正态分布的形状为(3, 3)的矩阵:')
print(np.random.randn(3, 3))
结果为:
生成形状(4, 4),值在0-1之间的随机数:
[[1.09478767e-01 8.28542687e-01 2.29063164e-01 6.74524184e-01]
[3.95004570e-01 2.21405468e-01 7.33547514e-02 1.52782087e-01]
[9.89312618e-01 2.67019311e-01 6.23058383e-01 2.03517166e-01]
[9.67375188e-01 4.01401820e-04 8.69373856e-01 4.89925436e-01]]
生成形状(3, 3),值在low-high之间的随机整数::
[[48 29 41]
[17 29 17]
[12 30 13]]
产生的数组元素是均匀分布的随机数:
[[2.53303787 1.30406591 1.73541217]
[1.45465375 2.78644509 1.58166964]
[2.7118102 2.50493356 1.31396379]]
生成满足正态分布的形状为(3, 3)的矩阵:
[[ 1.18399228 -0.17099359 -2.02865943]
[-0.86478027 -0.68082124 0.84941314]
[-0.03038969 2.32215063 -0.57330676]]
下面进一步阐述随机数种子的含义:
用上面随机方式产生的矩阵是无法重现的,如果想两次生成相同的矩阵,就用到了随机数种子,np.random.seed()
import numpy as np
np.random.seed(10)
print("按指定随机种子,第1次生成随机数:")
print(np.random.randint(1, 5, (2, 2)))
# 想要生成同样的数组,必须再次设置相同的种子
np.random.seed(10)
print("按相同随机种子,第2次生成的数据:")
print(np.random.randint(1, 5, (2, 2)))
输出:
按指定随机种子,第1次生成随机数:
[[2 2]
[1 4]]
按相同随机种子,第2次生成的数据:
[[2 2]
[1 4]]
所以,想要生成的随机数是一样的,只需要在前面添加一个随机数种子,种子里面的代号就代表着随机数是什么,这里是10,你也可以换成其他数,代表着不同的随机数种子!
四、生成特定形状的多维数组的函数
有时,在对参数初始化时,会要求生成一些特殊的矩阵,比如全是1或者全是0的矩阵,在numpy中同样给我们留下了对应的函数,
下面我们看具体的代码:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_5.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/17 9:19
"""
import numpy as np
# 生成全是 0 的 3x3 矩阵
nd5 = np.zeros([3, 3])
# 生成与nd5形状一样的全0矩阵
# np.zeros_like(nd5)
# 生成全是 1 的 3x3 矩阵
nd6 = np.ones([3, 3])
# 生成 3 阶的单位矩阵
nd7 = np.eye(3)
# 生成 3 阶对角矩阵
nd8 = np.diag([1, 2, 3])
print("*" * 6 + "nd5" + "*" * 6)
print(nd5)
print("*" * 6 + "nd6" + "*" * 6)
print(nd6)
print("*" * 6 + "nd7" + "*" * 6)
print(nd7)
print("*" * 6 + "nd8" + "*" * 6)
print(nd8)
输出结果为:
******nd5******
[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
******nd6******
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
******nd7******
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
******nd8******
[[1 0 0]
[0 2 0]
[0 0 3]]
五、利用arange、linspace函数生成数组
在一些特定的情况中,我们还希望生成一系列有规律的矩阵,学过Python基础的同学对range函数一定不陌生,在numpy中,我们可以用arange和linspace生成特定规律的数组。
np.arange([start,]stop[,step,],dtype=None)
- start:起始数
- stop:终止数,不包括
- step:步长,可以是小数
np.linspace(start,stop,num,endpoint=True,retstep=False,dtype=None)
- start:起始数
- stop:终止数
- num:分割成多少份
- endpoint:是否包含终点
- retstep:是否带步长
- dtype:数据类型
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_6.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/17 9:57
"""
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]
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :numpy学习
@File :task_7.py
@IDE :PyCharm
@Author :咋
@Date :2023/4/17 9:58
"""
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. ]
print(np.linspace(0,1,10,retstep=True))
# ([0. , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
0.55555556, 0.66666667, 0.77777778, 0.88888889, 1. ]), 0.1111111111111111)
注意,np.linspace(0, 1, 10)生成 的是步长为0.11111111的矩阵,因为0也占了一位,步长计算为(1-0)/9=0.11111,如果想要步长为0.1,只需要将start=0改为0.1即可。
除了上面学习的arange、linspace函数,numpy还提供了logspace函数,大家也可以自己写一写代码,自行尝试一下效果!