孩子们,懒大王回来了!
正如标题所说,今天我们继续开始新的篇章,我们要开始高强度学习深度学习的相关内容,这个专栏内容较多、全是干货,我们还会在合适的地方进行拓展一些额外的语法或者别的相关知识,并且保证会持续更新,大家坐稳了这就出发!由于不方便插入jupyter代码,所以可能会包含很多图片。
首先,预先善其事必先利其器,所以我们第一章从张量开始讲起。 需要事先说明的一点是我们这里主要使用的是pytorch的张量,实际上通用的深度学习框架都有张量这一数据结构,里面numpy中的array就很类似,但是由于只支持一些基础的函数功能,所以这里我们还是选择pytorch。
第一步当然是导入torch包,如果你不想再转到终端输入,我们也可以直接在jupyter notebook中输入下面命令,%pip
是在 Jupyter Notebook 中使用的一种特殊命令,它是一个 魔法命令。
%pip
会确保安装的包直接作用于当前 Jupyter Notebook 环境,不会影响到其他系统环境。- 使用
%pip
可以避免一些pip
在 Jupyter Notebook 中运行时可能出现的路径或环境问题。%pip install torch
1.张量创建
PyTorch 提供的张量创建函数(如 torch.tensor()
)实际上可以接受多种类型的输入,具体来说,它支持:
- 列表(list)
- 元组(tuple)
- NumPy 数组(numpy.ndarray)
- 标量(scalar)
- 其他张量(tensor)
#通过列表创建
torch.tensor([1,3])
#通过元组创建
torch.tensor((1,3))
#通过numpy创建
import numpy as np
a = np.array((1,2))
t = torch.tensor(a)
这里我们不列举出所有的方式了,需要说明的一点是这种多种创建方式并不是通过函数重载实现的,而是通过 参数类型的多态性 和 Python 内部的动态类型机制 来支持的。这里我们拓展一下,具体谈一下实现方式。
1.类型检查:PyTorch 会根据传入的参数类型(如列表、元组、NumPy 数组等)来选择合适的张量构造方式。
2.内部转化:具体来说,对于列表和元组,PyTorch 会将其转换为张量,转换的过程是类似的。元组和列表在 Python 中都是可以迭代的序列类型,因而可以共用同样的创建张量的逻辑。
动态类型检查的原理:
动态类型检查 是指在程序运行时检查一个对象的类型,而不是在编译时。Python 是一种 动态类型 语言,这意味着变量不需要声明类型,类型检查发生在程序执行时,而不是在编译阶段进行检查。
在 Python 中,所有对象都包含一个指向其类型的指针。这意味着我们可以在运行时通过 type()
函数来获取对象的类型。例如:
a = [1, 2, 3]
print(type(a)) # <class 'list'>
b = (1, 2, 3)
print(type(b)) # <class 'tuple'>
在运行时,Python 会根据变量 a
和 b
的实际值来确定它们的类型。
当你传递一个参数到函数或方法中时,Python 可以动态地检查该参数的类型,进而决定如何处理该参数。例如,torch.tensor()
会检查传入的参数类型,并根据类型选择适当的处理方式。
下面是pytorch如何运作的一个简化版示例:
import torch
def create_tensor(data):
if isinstance(data, list):
print("数据是一个列表")
return torch.tensor(data)
elif isinstance(data, tuple):
print("数据是一个元组")
return torch.tensor(data)
elif isinstance(data, torch.Tensor):
print("数据是一个张量")
return data.clone() # 克隆现有张量
else:
raise TypeError("不支持该类型的输入")
# 测试
tensor_from_list = create_tensor([1, 2, 3])
tensor_from_tuple = create_tensor((4, 5, 6))
补充一下:isinstance()
是 Python 的一个内置函数,用于检查一个对象是否是某个特定类或类的子类的实例。这个函数通常用于动态类型检查,它返回一个布尔值:如果对象是指定类型或其子类的实例,返回 True
;否则返回 False
。
2.张量的类型
如果我们运行最后一个通过numpy创建的代码会发现,多了个dtype,如下图:
这是因为如果用前两种方法创建tensor默认是长整型int64,这里使用numpy变成了int32所以显示出来了。我们可以通过.dtype来查看类型,会发现一个规律:整数型array创建默认32位,tensor默认64位,浮点数array默认float是64位,tensor默认float是32位。
所以我们也可以在创建的时候设置类型参数:
t0 = torch.tensor([1.14,5.14],dtype = torch.float64)
需要特别注意的一点是当我们用numpy创建的时候张量类型会和里面的numpy的类型一致,如图:
这里tensor默认是32位,但是由于array默认是64位,所以最后输出还是float64
除此之外我们还可以创建bool类型:
t1 = torch.tensor([True,False])
t1.dtype
3.张量类型的转化
如上图输出所示,三种常用类型之间都是可以互相转化的,而且默认会朝着数据量更多的那个方向转化。当然我们也可以直接进行转化,如下图:
我们会发现,当我们转化之后并没有改变t的属性,这是因为返回的是t的一个副本,并没有对原张量进行修改,如果你想对原张量进行修改也可以使用加上 _
的方法,比如 .float_()
或 .double_()
4.张量的维度
我们可以通过调用常用的方法和属性来查看张量的维度大小维度等信息,如下图:
需要注意的是在pytorch中和numpy不同,size方法和shape属性返回的结果是一样的
这里的len函数也可以返回维度信息,而numel返回底层有多少个元素
高维张量:
如上图所示,我们创建了一个t3张量包括了两个numpy数组,我们这里主要解释一下shape属性
shape返回的[2,2,3]意思是包含了两个小矩阵,每个小矩阵是二行三列
维度的形变
如图所示,常用到的就flatten和reshape两个方法
flatten方法顾名思义就是将一个张量拉平,reshape可以将张量改变成你想要的形状,需要注意的是我们可以看到reshape输入的参数和返回的size方法结果一致。说白了就是你想要返回什么样的size就输入什么参数,因此这里我们也可以调用reshape来打到flatten的作用。
5.常见特殊张量的创建
6.不同类型的转化
需要注意一点的是使用list函数转化为列表,里面包含的是0维张量而不是数值,而item方法就是把0维张量转化为数值。
7.张量的拷贝
我们通过修改可以发现t4和t5是指向同一个地址空间的,也就是常说的浅拷贝,如果我们要指向不同的地址空间的深拷贝,调用clone方法即可:
8.张量的索引
1.一维张量索引
和python的索引用法几乎一样,如下图:
如果冒号前后没有数字表明索引这一整片区域,唯一需要注意的是这里的步长不能为负数。
2.二维张量索引
在搞懂二维之后,三维甚至高维都是同样的索引方法,所以这里就不再写三维的了。
3.张量的函数索引
9.view方法
view顾名思义就是视图的意思,把原对象以另一种方式表达就叫视图。需要注意的是视图和原对象共用同一片存储空间,也就是前面说的浅拷贝。我们发现当我们修改了原对象之后,视图也发生了改变,这就是浅拷贝的证明。
10.张量的切分
1.chunk方法
chunk方法可以在指定维度上进行等量切分的操作,例如将t3二等分为两个小tensor,如果不能完全等分可能会返回意外的结果,例如下面这个不能三等分,返回的是二等分的。需要额外注意的是这里返回的同样也是视图。
2.split方法
如图,split方法可以完成自定义切分,如果第二个参数只有一个数字表示每段的大小,也可以是一个列表,例如最后那样表示第一份一个,第二份两个。
11张量的合并
如上图所示合并操作有两种,一种是拼接,一种是堆叠。堆叠的要求更加高一点,要求两个张量形状一样,并且放到一个更高一维的张量中。