1.pytorch介绍
PyTorch既是一个深度学习框架又是一个科学计算包,她在科学计算方面主要是PyTorch张量库和相关张量运算的结果。(张量是一个n维数组或者是一个n-D数组)PyTorch是一个张量库,她紧密地反映了numpy的多维数组功能,并且与numpy本身有着高度的互操作性。Pytorch中常用包的介绍
包名 | 功能 |
---|---|
torch | 包含所有包和张量库的顶级包 |
torch.nn | nn代表神经网络,主要包含类和模块,比如图层、权重和前向函数(神经网络是建立在torch.nn的基础上) |
torch.autograd | 是一个子包,主要负责处理在核心处优化神经网络权重所需的导数计算 |
torch.nn.functional | 是个功能接口,负责损失函数,激活函数和卷积运算(负责典型的深度学习函数以及算法) |
torch.optim | 负责典型的优化,像是SGD以及Adam(负责典型的深度学习函数以及算法) |
torch.utils | 是一个子包,包括数据集以及数据加载器这样的实用程序类,使得数据预处理更加容易 |
torchvision | 一个单独的包,方便访问流行的数据集,计算机视觉的模型架构以及图像转换 |
所有的深度学习框架都有两个特征:张量库(torch)以及计算导数的包(torch.autograd)。
最适合GPU的工作是可以并行完成的工作,而神经网络中的很多计算,都很容易被分解成更小的相互独立计算(例如神经网络中的卷积运算),使用CUDA。
代码:
代码语言:python代码运行次数:0复制 t=torch.tensor([1,2,3]) #定义张量,输出的话是“tensor([1,2,3])”,因为默认是使用CPU,不更改的话,后期的操作都是在CPU上执行
t=t.cuda() #将t转移到GPU上,输出的话是“tensor([1,2,3],device='cuda:0')”,后面是设备的编号“cuda:0”,pytorch支持多个GPU
Q:为什么不将所有的任务都放在GPU上?
A:因为GPU对于特定的任务来说,速度更快。但是把数据从CPU转移到GPU上的性能代价很高,所以针对较小的数据量,将其移植到GPU上反而会影响整体的性能。总之GPU对于那些可以分解成许多小任务的任务来说效果很好,如果自身已经很小了,在CPU上跑就行了。
2.张量
张量是神经网络中主要的数据结构,网络中的输入输出以及转换都使用张量来进行表示。
在计算机中,数字对应数学中的标量(0个索引),数组对应数学中的向量(1个索引),二维数组对应数学中的矩阵(2个索引)。而n维张量,意味着访问特定元素所需要的索引数量是n。张量取消了上面说的界限,只使用一个n来表示所处理维度的数量。这里秩、轴和形状是使用张量时最关注的三个张量属性,且都与索引有关。
张量中的秩是指张量中存在的维数,一个张量的秩意味着需要多少个索引来访问或引用张量数据结构中包含的特定数据元素。
张量中的轴是指张量中特定的维度,张量中的数据被认为是沿着一个轴进行运动,并会受到每个轴长度的限制。
张量中的形状是指张量的一个size(shape)
代码:
代码语言:python代码运行次数:0复制dd=[
[1,2,3],
[4,5,6],
[7,8,9]
]
t=torch.tensor(dd) #将dd转化成张量,输出t的话是“tensor(......)”
type(t) #输出的类型是“torch.Tensor”
t.shape #输出的结果是“torch.Size([3,3])”
t.reshape(1,9) #输出的结果是“tensor([[1,2,3,4,5,6,7,8,9]])”,注意重塑时分量值的乘积必须等于张量中元素的总数,保证张量数据结构中有足够位置来包含重组后的所有原始元素
t.reshape(1,9).shape #输出的结果是“torch.Size([1,9])”
Q:为什么张量会特别注意shape?
A:因为在神经网络的传递中,会有一个过程叫做reshape(重塑),即在网络中不同的点会有特定的形状,因此我们需要特别在意张量的形状,并在有能力根据需要进行重塑。
在cnn中输入的张量形状大多是以4个轴来表示,[? , ? , ? , ?]。
对于图形数据,[B , C , H , W]
在fashion图像数据集里面H和W是28*28(mnist数据集也是28*28),VGG-16神经网络中图像的大学为224*224
C代表着color,RGB图片取值为3,灰度图取值为1
B代表Batch-Size,通常使用的是批量的样本,而不是单一的样本(这个B的长度代表数据批中有多少个样本)
假设一个28*28的灰度图,张量为[1 , 1 , 28 , 28],在经过三个卷积滤波器(3个卷积滤波器代表着3个通道的输出)之后输出三个张量。输出的通道仍然由像素组成,但是像素值已经被卷积运算修改了(基于滤波器的大小,输出的高度和宽度也会发生改变)。经过输出通道,就不再需要彩色通道,但是可以当做是经过修改的颜色通道([1 , 3 , 28 , 28]),称这些通道为特征图(这些特征图是由输入颜色通道和卷积滤波器所产生的卷积的结果)。将一个输入颜色通道和一个卷积滤波器结合起来,再对其做一个卷积运算,就可以得到一个输出通道的结果,称之为特征映射(之所以用“特征”这个词,是因为输出代表了图像的特定特征,比如边缘)。当网络在训练过程中进行学习时,这些映射就会出现。
代码:
代码语言:python代码运行次数:0复制import torch
import numpy as np
t = torch.Tensor()
type(t) #输出的结果是“torch.Tensor”
print(t.dtype) #输出的结果是“torch.float32”
print(t.device) #输出的结果是“cpu”
print(t.layout) #输出的结果是“torch.strided”
device = torch.device('cuda:0')
device #输出的结果是“device(type='cuda' , index=0)”
而张量的数据类型有以下几种
每一个张量的数据类型都有CPU和GPU的版本,并且张量之间的张量运算必须在相同数据类型张量的情况下。并且在使用多设备时,张量之间的操作必须是存在于同一个设备上。
总之,张量的操作需要注意两点,一张量包含一个统一类型的数据,二张量之间的运算依赖于张量的类型以及设备。
代码:
代码语言:python代码运行次数:0复制data = np.array([1 , 2 , 3])
type(data) #输出的结果是“numpy.ndarray”
torch.Tensor(data) #输出的结果是“tensor([1. , 2. , 3.])”,因为这是一个构造类函数
torch.tensor(data) #输出的结果是“tensor([1 , 2 , 3] , dtype=torch.int32)”,因为这是一个工厂函数
torch.as_tensor(data) #输出的结果是“tensor([1 , 2 , 3] , dtype=torch.int32)”,因为这是一个工厂函数
torch.from_numpy(data) #输出的结果是“tensor([1 , 2 , 3] , dtype=torch.int32)”,因为这是一个工厂函数
torch.eye(2) #创建单位张量(对角线为1.,其余为0.),参数为行数
torch.zeros(2,2) #创建一个2*2的全是0.的张量
torch.ones(2,2) #创建一个2*2的全是1.的张量
torch.rand(2,2) #创建一个2*2的全是随机数的张量
上面的工厂函数(Factories),是指接受参数输入并返回特定类型对象(这里指的是张量对象)的函数,用于创建对象的编程概念(目的是允许更多的动态对象的创建)。而构造函数(Constructor)是用来创建类的实例。之所以产生上面的两种差别,是因为构造函数在构造一个张量时使用的是全局缺省值(通过代码“torch.get_default_dtype()”输出的结果是“torch.float32”,确实构造函数用的是全局缺省值),而工厂函数则是根据输入来推断数据的类型。另外,工厂函数有更好的文档,并且有更多的配置参数,因此目前更倾向于选择工厂函数而不是构造函数。
代码:
代码语言:python代码运行次数:0复制torch.get_default_dtype() #输出的结果是“torch.float32”,所以构造函数是用的全局缺省值
torch.tensor(np.array([1 , 2 , 3])) #输出的结果是“tensor([1 , 2 , 3] , dtype=torch.int32)”
torch.tensor(np.array([1. , 2. , 3.])) #输出的结果是“tensor([1. , 2. , 3.] , dtype=torch.float64)”
torch.tensor(np.array([1 , 2 , 3]) , dtype=torch.float64) #输出的结果是“tensor([1. , 2. , 3.] , dtype=torch.float64)”
data = np.array([1 , 2 , 3])
t1 = torch.Tensor(data) #这是在内存中创建了一个额外的输入数据副本,是一个copy
t2 = torch.tensor(data) #这是在内存中创建了一个额外的输入数据副本,是一个copy
t3 = torch.as_tensor(data) #这是在内存中共享数组的数据,是一个share
t4 = torch.from_numpy(data) #这是在内存中共享数组的数据,是一个share
data[0] = 0
data[1] = 0
data[2] = 0 #改变原始数组的值
print(t1) #输出的结果是“tensor([1. , 2. , 3.])”
print(t2) #输出的结果是“tensor([1 , 2 , 3] , dtype=torch.int32)”
print(t3) #输出的结果是“tensor([0 , 0 , 0] , dtype=torch.int32)”
print(t4) #输出的结果是“tensor([0 , 0 , 0] , dtype=torch.int32)”
在numpy和pytorch之间进行切换是非常快的,这是因为在创建新的pytorch张量时,数据是共享的,而不是后台复制的。改变张量或者数组的值,另外的值也会被改变,因此共享数据比复制数据更加有效,因为使用了更少的内存。copy data中最好用的是torch.tensor(),而share data中最好用的是torch.as_tensor()。因为torch.as_tensor函数可以接受任何Python的数组,torch.from_numpy()的调用只能接受numpy数组。
3.张量主要操作
使用张量的主要操作可以分成四大类:
重塑操作(Reshaping operations):重塑没有改变基础数据,只改变了数据的形状
元素操作(Element—wise operations)
还原(Reduction operations)
访问操作(Access operations)
代码:
代码语言:python代码运行次数:0复制t = torch.tensor([
[1 , 1 , 1 , 1] ,
[2 , 2 , 2 , 2] ,
[3 , 3 , 3 , 3]
] , dtype = torch.float32)
torch.tensor(t.shape).prob() #输出的结果是“tensor(12)”
t.numel() #输出的结果是“12”,计算的是元素的数量(在不改变秩的情况下,重塑可以是1*12,2*6,3*4,6*2和12*1)
t.reshape(2 , 2 , 3) #输出的结果是“tensor([[[1. , 1. , 1.],[1. , 2. , 2.]],[[2. , 2. , 3.],[3. , 3. , 3.]]])”(秩不同的情况下,也能重塑,用压缩squeezing和解压unsqueezing)
print(t.reshape(1 , 12)) #输出的结果是“tensor([[1. , 1. , 1. , 1. , 2. , 2. , 2. , 2. , 3. , 3. , 3. , 3.]])”
print(t.reshape(1 , 12).shape) #输出的结果是“torch.Size([1 , 12])”
print(t.reshape(1 , 12).squeeze()) #输出的结果是“tensor([1. , 1. , 1. , 1. , 2. , 2. , 2. , 2. , 3. , 3. , 3. , 3.])”
print(t.reshape(1 , 12).squeeze().shape) #输出的结果是“torch.Size([12])”
print(t.reshape(1 , 12).squeeze().unsqueeze(dim=0)) #输出的结果是“tensor([[1. , 1. , 1. , 1. , 2. , 2. , 2. , 2. , 3. , 3. , 3. , 3.]])”
print(t.reshape(1,12).squeeze().unsqueeze(dim=0).shape) #输出的结果是“torch.Size([1 , 12])”
压缩一个张量可以移除所有长度为1的轴,而解压一个张量可以增加一个长度为1的维度。这些函数实现扩大或缩小张量的秩。
在卷积层过渡到全连接层时,必须通过构建一个flatten(压扁)函数来压缩一个张量,即转换成一个轴的张量,而这里面包含了张量的所有元素。flatten函数的定义代码如下所示:
代码:
代码语言:python代码运行次数:0复制 def flatten(t):
t = t.reshape(1 , -1)
t = t.squeeze()
return t
flatten(t) #输出的结果是“tensor([1. , 1. , 1. , 1. , 2. , 2. , 2. , 2. , 3. , 3. , 3. , 3.])”
t1 = torch.tensor([
[1 , 2]
])
t2 = torch.tensor([
[3 , 4]
])
torch.cat((t1 , t2) , dim=0) #输出的结果是“tensor([[1 , 2], [3 , 4]])” 张量的连接,dim=1时,输出的结果为“tensor([[1 , 2 , 3 , 4]])”
flatten的操作是一种特殊的reshaping操作,是将所有的轴都挤压在一起。
flatten的方法:
代码:
代码语言:python代码运行次数:0复制 t.reshape(1 , -1)[0]
t.reshape(-1)
t.view(t.numel())
t.flatten()
t.flatten(start_dim=1).shape #输出的结果是“torch.Size([3 , 16])”原始是[3 , 1 , 4 , 4]
t.flatten(start_dim=1) #输出的结果是“tensor([[1 , 1 , 1 ,......] , [2 , 2 , 2 ,......] , [3 , 3 , 3 ,......]])” start_dim告诉flatten的操作从哪个轴开始,1表示从第2个轴开
利用reshape来增加维度,将(3 , 4 , 4)变成(3 , 1 , 4 , 4)
此时t[0]的结果是[[[1 , 1 , 1 , 1],
[1 , 1 , 1 , 1],
[1 , 1 , 1 , 1],
[1 , 1 , 1 , 1]]]
t[0][0]的结果是[[1 , 1 , 1 , 1],
[1 , 1 , 1 , 1],
[1 , 1 , 1 , 1],
[1 , 1 , 1 , 1]]
t[0][0][0]的结果是[1 , 1 , 1 , 1]
t[0][0][0][0]的结果是1
关于张量中的数据操作
重塑操作能够让元素组合成特定长的轴,元素的操作是对两个张量之间的元素执行操作,张量中的缩减操作是对单个张量执行操作,能够减少张量中包含的元素数量。
代码:
代码语言:python代码运行次数:0复制 import torch
import numpy as np
t = torch.tensor([
[0 , 1 , 0],
[2 , 0 , 2],
[0 , 3 , 0]
],dtype=torch.float32)
t.sum() #输出的结果是“tensor(8.)”
t[0].sum() #输出的结果是“tensor(1.)”,即为第一行的和
t.numel() #输出的结果是“9”
t.sum().numel() #输出的结果是“1”
t.sum().numel() < t.numel() #输出的结果是“True”
t.prob() #输出的结果是“tensor(0.)”
t.mean() #输出的结果是“tensor(0.8889)”
t.std() #输出的结果是“tensor(1.1667)”
t.sum(dim = 0) #输出的结果是“tensor([2 , 4 , 2])”,先竖再横,0表示竖着
t.sum(dim = 1) #输出的结果是“tensor([1 , 4 , 3])”
#例子2:
t = torch.tensor([
[1 , 0 , 0 , 2],
[0 , 3 , 3 , 0],
[4 , 0 , 0 , 5]
] , dtype=torch.float32)
t.max() #输出的结果是“tensor(5.)”
t.argmax() #输出的结果是“tensor(11)”
t.flatten() #输出的结果是“tensor([1. , 0. , 0. , 2. , 0. , 3. , 3. , 0. , 4. , 0. , 0. , 5.])”在平坦张量中5的索引就是11
t.max(dim = 0) #输出的结果是“(tensor([4. , 3. , 3. , 5.]), tensor([2 , 1 , 1 , 2]))”
t.argmax(dim = 0) #输出的结果是“tensor([2 , 1 , 1 , 2])”
t.max(dim = 1) #输出的结果是“(tensor([2. , 3. , 5.]), tensor([3 , 1 , 3]))”
t.argmax(dim = 1) #输出的结果是“tensor([3 , 1 , 3])”
t.mean() #输出的结果是“tensor(5.)”
t.mean().item() #输出的结果是“5.0”,但是item只适用于标量值张量
t.mean(dim = 0).tolist() #输出的结果是“[1.7 , 1.0 , 1.0 , 2.3]”
t.mean(dim = 0).numpy() #输出的结果是“array([1. , 1. , 1. , 2.],dtype = float32)”
4.数据的准备
数据的准备要遵循ETL过程,Extract(提取)Transform(转换)Load(加载),而pytorch内部提供了很多包,让ETL这个过程更加简单。这里由Dataset和DataLoader(都是Python的抽象类)来共同完成,Dataset类是表示数据集的抽象类,DataLoader封装数据集并提供对底层数据的访问
代码:
代码语言:python代码运行次数:0复制 import torch #torch是顶级的pytorch包和张量库
import torchvision #torchvision是一个提供对数据集、模型架构和计算机视觉的图像转换的访问的包(需要单独安装)
import torchvision.transforms as transforms #这个接口是负责访问图像处理的通用转换
#创建一个实现这些所需方法(两种)的子类来拓展数据集类,这样新self类能够传递给pytorch data loader对象构造器,并将数据集包装起来来提供额外的功能
class OHLC(Dataset):
def _init_(self , csv_file):
self.data = pd.read_csv(csv_file)
def _getitem_(self , index): #从数据集中的一个特定索引位置获取一个元素
r = self.data.iloc[index]
label = torch.tensor(r.is_up_day , dtype = torch.long)
sample = self.normalize(torch.tensor([r.open , r.high , r.low , r.close]))
return sample , label
def _len_(self):
return len(self.data) #长度方法,返回数据集长度
#使用torchvision获得一个fashion-mnist数据集的实例
train_set = torchvision.datasets.FashionMNIST(
root = './data/FashionMNIST' #这个是路径,即数据所在的硬盘位置
,train = True #一个训练的参数,意味着希望数据用于训练
,download = True #意思是如果没有出现在指定的根目录下,可以下载这些数据
,transform = transform.Compose([
transforms.ToTensor()
]) #一个变换参数,想要把图像变换成张量(为了使用在类定义中的url从web中提取出原始数据,对于转换将原始图片数据使用两个张量转换对象转换成一个张量)
)
train_loader = torch.utils.data.DataLoader(train_set) #意思是在数据加载器对象实例中封装数据,然后可以用加载器来完成一些任务(大量的批处理、线程管理和shuffle功能),数据加载器能够访问数据并提供查询功能
#也可以指定批处理的大小(默认是1)
#train_loader = torch.utils.data.DataLoader(train_set , batch_size = 10)
import numpy as np
import matplotlib.pyplot as plt
torch.set_printoptions(linewidth = 120) #只是设置一下行距
len(train_set) #查看训练集长度,长度是60000
train_set.train_labels #查看各个数据的标签(每个数字代表不同的类别),输出的结果是“tensor[9 , 0 , 0 , ... , 3 , 0 , 5]”
train_set.train_labels.bincount() #bincount可以查看一个张量内值得频率分布,输出的结果是“tensor[6000 , 6000 , 6000 , 6000 , 6000 , 6000 , 6000 , 6000 , 6000 , 6000]”,均匀分布
#把数据单独拿出来看一下
sample = next(iter(train_set)) #从训练集里面抽取数据
len(sample) #求取数据的长度,输出的结果是“2”,因为一个是图片张量一个是标签张量
type(sample) #样本是一个Python的序列类型,输出的结果是“tuple”元组
image , label = sample #使用序列解压缩的概念来分配图像和标签
image.shape #查看图片的形状,输出的结果是“torch.Size([1 , 28 , 28])”,灰度图只有一个颜色通道的图像
label.shape #查看标签的形状,输出的结果是“torch.Size([])”,因为标签是一个标量值,所以输出一个没有形状的标量张量
plt.imshow(image.squeeze(), cmap = 'gray')
print('label:' , label) #将图像和标签打印出来
batch = next(iter(train_loader)) #批量处理数据加载器
len(batch) #输出的结果是“2”
type(batch) #输出的结果是“list”
images , labels = batch #因为是批处理,所以是复数形式
images.shape #查看批量图片的形状,输出的结果是“torch.Size([10 , 1 , 28 , 28])”,灰度图只有一个颜色通道的图像
labels.shape #查看批量标签的形状,输出的结果是“torch.Size([10])”,因为标签是一个标量值,所以输出一个没有形状的标量张量
grid = torchvision.utils.make_grad(images , nrow = 10) #设置nrow是将十个图片沿着一行显示
plt.figure(figsize=(15 , 15))
plt.imshow(np.transpose(grid , (1 , 2 , 0)))
print('labels:' , labels) #输出图片以及标签
在pytorch中建立神经网络,拓展了torch.nn.module pytorch类,object和class的区别,类是一个实际对象的蓝图或描述,而对象就是事物的本身。正常会在实例中调用对象,一个给定类的所有实例都有两个核心组件,第一种是方法,第二种是属性(属性用于描述对象的特征,方法用于描述对象的行为)即可以调用的函数以及object.函数来获得属性。所以是通过创建类的实例来执行程序中的任务。例子,创建一个蜥蜴类。
代码:
代码语言:javascript复制class Lizard:
def _init_(self , name): #构造函数,self参数能够创建存储或封装在类对象的属性值
self.name = name #调用函数时,并没有通过参数来传递self参数,Python幕后自动做了
def set_name(self , name): #另外可以创建任意数量的自定义方法,然后可以调用这个方法,并为这个名称传递一个新的值
self.name = name
#调用
lizard = Lizard('deep')
print(lizard.name) #输出的结果是“deep”
lizard.set_name('lizard')
print(lizard.name) #输出的结果是“lizard”
5.搭建神经网络
ptorch的神经网络库包含了构建神经网络所需要的所有组件,比如说pytorch的神经网络库包含了构造层的类。而神经网络中的每一层都包含两个主要的组成部分,变换(代码表示)以及权重的集合(数据表示)。而神经网络可以看做是张量不断的前向传输,所有单独的层的前向通道的组成定义了网络本身的整体向前转换。对于pytorch而言,每一个pytorch nn.module都有一个前向方法(实际上的变换,通常使用nn.functional包的函数)来表示前向传输。
用pytorch构建神经网络,第一要创建一个神经网络类拓展nn.Module基类。第二在类构造函数中将网络的层定义为类属性。第三使用网络层属性以及nn.functional API的操作来定义网络的前向传输。示例:
代码:
代码语言:javascript复制class Network: #简单的神经网络
def _init_(self):
self.layer = None #构造一个虚拟层,并为前向函数提供一个虚拟的实现
def forward(self , t): #向前函数使用了张量t
t = self.layer(t) #并使用虚拟层对其进行转换
return t #在使用虚拟层进行张量变换后,再返回张量t
import torch.nn as nn
class Network(nn.Module): #指定nn.Module类,引入nn.Module来构造pytorch神经网络
def _init_(self):
super(Network , self)._init() #加入超级类构造函数的调用,可以保持追踪每个层中包含的网络权重
#添加线性层和卷积层
self.conv1 = nn.Conv2d(in_channels=1 , out_channels=6 , kernel_size=5) #参数有三
self.conv2 = nn.Conv2d(in_channels=6 , out_channels=12 , kernel_size=5)
#当从卷积层转换到线性层时,必须将张量变平
self.fc1 = nn.Linear(in_features=12*4*4 , out_features=120) #12来自上一层的输出通道数量
self.fc2 = nn.Linear(in_features=120 , out_features=60) #线性层也叫全连接层(也叫dense),所以是FC
self.out = nn.Linear(in_features=60 , out_features=10) #参数有俩
def forward(self , t): #向前函数使用了张量t
#执行前向传输,有他妈的bug
return t #在使用虚拟层进行张量变换后,再返回张量t
参数parameter和argument,两者的区别:参数(parameter)在函数定义中使用,相当于是占位符。另一方面,参数(argument)是当函数被调用时传递给函数的实际值,相当于是函数内部的局部变量,是由函数调用者从外部分配给这些变量的值。所以说名称是parameter,具体的参数值是argument。
而超参数是一个参数,他的值可以手动和任意选择的,例如上面的卷积层,kernel_size设置了在该层中使用滤波器的大小(在卷积层里面输入通道和一个卷积滤波器配对来执行卷积运算)。而滤波器可以包含输入通道(out_channels也叫做特征映射),而这个操作的结果是一个输出通道,所以一个包含输入通道的滤波器可以得到一个相应的输出通道,因此输出通道的值就是设置滤波器的数量。在线性层里面有输出特征(out_features,输出的是一个一阶张量,所以不是特征映射),输出特征是基于以后想要多少个节点。而上面代码中存在依赖于数据的超参数,即依赖于数据的超参数是在网络的开始和网络的末端,就是第一个卷积层的输入通道(依赖于构建训练集的图像内部的彩色通道的数量)以及最后一个线性层的输出特征(依赖于训练集中类的数量)。然后卷积层所有输入通道和线性层所有输入特征都依赖于上一层的数据。
可学习参数是在训练过程中学习的参数,通过网络的学习,使用迭代的方式进行更新(网络进行学习,学习的就是参数的适当值,即最小化损失函数的值),而nn.Module可以让用户直接查看权重。
接着上面的代码:
代码语言:javascript复制 class Network(nn.Module): #指定nn.Module类,引入nn.Module来构造pytorch神经网络
def _init_(self):
super(Network , self)._init() #加入超级类构造函数的调用,可以保持追踪每个层中包含的网络权重
#添加线性层和卷积层
self.conv1 = nn.Conv2d(in_channels=1 , out_channels=6 , kernel_size=5) #参数有三
self.conv2 = nn.Conv2d(in_channels=6 , out_channels=12 , kernel_size=5)
#当从卷积层转换到线性层时,必须将张量变平
self.fc1 = nn.Linear(in_features=12*4*4 , out_features=120) #12来自上一层的输出通道数量
self.fc2 = nn.Linear(in_features=120 , out_features=60) #线性层也叫全连接层(也叫dense),所以是FC
self.out = nn.Linear(in_features=60 , out_features=10) #参数有俩
def forward(self , t): #向前函数使用了张量t
net = Network()
print(network)
输出的是:
Network(
(conv1):Conv2d(1 , 6 , kernel_size=(5 , 5), stride=(1 , 1))
(conv2):Conv2d(6 , 12 , kernel_size=(5 , 5), stride=(1 , 1))
(fc1):Linear(in_features=192 , out_features=120 , bias=True)
(fc2):Linear(in_features=120 , out_features=60 , bias=True)
(out):Linear(in_features=60 , out_features=10 , bias=True)
)
以上是对pytorch基础的一部分简单介绍。有什么问题欢迎私信讨论。
邀请人:“千万别过来” 我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!