零碎:
collections.OrderedDict
是 Python 标准库中的一种数据结构,有序的字典类(dictionary subclass),可以记住字典元素添加的顺序
pytorch
1. tensor的基本数据
目录:
(1)torch.tensor创建
可以接受各种数据类型作为输入,并返回一个新的张量
import torch
a = torch.FloatTensor(2,3)
b = torch.FloatTensor([2,3,4,5])
a,b
结果:
(tensor([[1.0561e-38, 1.0102e-38, 9.6429e-39],
[8.4490e-39, 9.6429e-39, 9.1837e-39]]),
tensor([2., 3., 4., 5.]))
torch.tensor()和torch.tensor([])区别
# 创建一个包含单个值的张量
tensor_a = torch.tensor(5)
print(tensor_a)
# 创建一个从列表中创建的张量
tensor_b = torch.tensor([1, 2, 3])
print(tensor_b)
import torch
# 创建一个空的张量
empty_tensor = torch.tensor([])
print(empty_tensor)
(2)torch.IntTensor整数型
用于生成数据类型为整型的Tensor
参数可以是列表,也可以是一个维度值
import torch
a = torch.IntTensor(2,3)
b = torch.IntTensor([2,3,4,5])
a,b
结果
(tensor([[0, 0, 0],
[0, 0, 0]], dtype=torch.int32), tensor([2, 3, 4, 5], dtype=torch.int32))
(3)torch.randn随机数
- 用于生成数据类型为浮点数且维度指定的随机Tensor
- 和在numpy中使用的numpy.randn类似
- 随机生成的浮点数的取值满足均值为0,方差为1的正态分布
import torch
a = torch.randn(2,3)
a
tensor([[-1.1486, -1.4540, -0.8042],
[ 0.1975, -1.0651, -1.1379]])
(4)torch.range规定范围
生成数据类型为浮点型且指定范围内的Tensor
参数有三个,起始值,结束值,步长
a = torch.range(1,20,2)
a
结果:
tensor([ 1., 3., 5., 7., 9., 11., 13., 15., 17., 19.])
(5)torch.zeros/ones/empty
-
torch.zeros :浮点型且维度指定,元素值全部为0。
-
torch.ones :生成全1的数组
-
torch.empty:创建一个未被初始化数值的tensor,tensor的大小是由size确定
这里可以是一个list 也可以是一个tuple
import torch
a = torch.zeros(3,4)
tensor([[9.1837e-39, 4.6837e-39, 9.2755e-39],
[1.0837e-38, 8.4490e-39, 9.2755e-39]])
2.tensor的计算
目录:
(1)torch.abs绝对值
返回输入参数的绝对值作为输出
import torch
a = torch.randn(2,3)
print(a)
b = torch.abs(a)
print(b)
结果:
tensor([[ 0.8940, -0.1397, 0.2802],
[ 2.1344, 1.3253, -0.8328]])
tensor([[0.8940, 0.1397, 0.2802],
[2.1344, 1.3253, 0.8328]])
(2)torch.add求和
就是求和
import torch
a = torch.randn(2,3)
b = torch.randn(2,3)
print(a)
print(b)
c = torch.add(a,b)
print(c)
结果:
tensor([[ 0.1822, 0.3149, -1.3069],
[ 1.4597, 0.8170, -0.3673]])
tensor([[-0.2853, 0.7806, 1.2908],
[-0.0537, 0.7209, -1.0830]])
tensor([[-0.1031, 1.0956, -0.0161],
[ 1.4060, 1.5380, -1.4503]])
再一个例子:对c与一个标量10相加:
e = torch.add(c,10)
print(e)
结果:
tensor([[11.3519, 13.3578, 10.6071],
[10.4409, 11.2167, 7.1050]])
(3)tensor.clamp上下限
参数:预处理张量 + 下限 + 上限
超过下限的值定为min
超过上线的值定为max
其余不改变
import torch
a = torch.randn(2,3)
b = torch.randn(2,3)
print(a)
b = torch.clamp(a,-0.2,0.2)
print(b)
结果:
tensor([[ 0.1204, 1.2552, -0.0253],
[ 0.8156, -2.3537, 0.3768]])
tensor([[ 0.1204, 0.2000, -0.0253],
[ 0.2000, -0.2000, 0.2000]])
(4)torch.div求商
就是求商
result_tensor = torch.div(input_tensor, other_tensor)
- 张量相除
import torch
a = torch.randn(2,3)
b = torch.randn(2,3)
c = torch.div(a,b)
print(a)
print(b)
print(c)
结果:
tensor([[ 0.7099, 0.4389, -1.0387],
[-0.2901, -0.6581, 0.7899]])
tensor([[-2.9549, 1.3680, 1.0147],
[-2.0475, 1.0217, -1.3405]])
tensor([[-0.2402, 0.3208, -1.0236],
[ 0.1417, -0.6441, -0.5893]])
- 张量除以标量
d = torch.div(c,2)
print(c)
print(d)
结果:
tensor([[ 0.9959, 0.2897, 0.3066],
[-0.4423, 0.6936, 1.5915]])
tensor([[ 0.4980, 0.1449, 0.1533],
[-0.2211, 0.3468, 0.7958]])
(5)torch.pow幂次方
求幂结果
import torch
b = torch.randn(2,3)
c = torch.pow(b,2)
print(b)
print(c)
结果:
tensor([[-0.1403, -0.5332, 0.2666],
[ 1.4707, -1.0248, 0.7824]])
tensor([[0.0197, 0.2843, 0.0711],
[2.1629, 1.0502, 0.6122]])
(6)torch.mm矩阵相乘
要遵循矩阵相乘的规律
import torch
a = torch.randn(2,3)
b = torch.randn(3,4)
c = torch.mm(a,b)
print(c)
(7)torch.mv(mat, vec)矩阵向量相乘
- 功能:执行矩阵与向量的乘法运算
- 参数:矩阵+向量
mat
必须是一个二维张量,vec
必须是一个一维张量,返回一个一维向量
import torch
a = torch.randn(2,3)
b = torch.randn(3)
c = torch.mv(a,b)
print(a)
print(b)
print(c)
结果:
tensor([[ 1.0841, -1.4717, -0.1817],
[ 0.7554, -0.6128, -0.2065]])
tensor([ 0.4840, -0.6109, -0.3883])
tensor([1.4943, 0.8201])
(8)torch.mul(input, other)
输入张量对应位置元素的乘积
import torch
# 创建两个输入张量
input_tensor = torch.tensor([2, 3, 4])
other_tensor = torch.tensor([5, 6, 7])
# 对两个张量进行逐元素相乘
result_tensor = torch.mul(input_tensor, other_tensor)
print(result_tensor) # 输出: tensor([10, 18, 28])
3.神经网络工具箱torch.nn
3.1 nn.Module类
1. nn.Module类
nn.Module是PyTorch提供的神经网络类,并在类中实现了网络各层的定义及前向计算与反向传播机制。在实际使用时,如果想要实现某个神经网络,只需继承nn.Module,在初始化中定义模型结构与参数,在函数forward()中编写网络前向过程即可。
(1)nn.Parameter函数
使用 nn.Parameter将一个张量标记为模型参数后,该参数将被添加到模型的参数列表中,并且在模型进行反向传播计算梯度时,PyTorch 将会自动跟踪该参数的梯度并进行更新。
(2)forward()函数与反向传播
-
)
forward()
函数:这个方法定义了输入数据如何在网络中传播,并最终得到输出结果,就是前向传播 -
)反向传播:通过反向传播算法可以计算损失函数对模型参数的梯度,从而更新模型参数以降低损失函数的数值。
在 PyTorch 中,一般通过以下几个步骤来完成反向传播:
- 利用模型的
forward()
方法计算输出 - 计算损失函数
- 调用
backward()
方法计算梯度 - 调用优化器的
step()
方法来更新模型参数
- 利用模型的
3.多个Module的嵌套
可以通过将多个 nn.Module
的子类进行嵌套来构建更复杂的神经网络模型
4.nn.Module与nn.functional库
5.nn.Sequential()模块
class MLP(nn.Module):
def __init__(self, in_dim, hid_dim1, hid_dim2, out_dim):
super(MLP, self).__init__()
# __init__ 方法:在初始化阶段,定义了 MLP 的结构。它接受输入特征维度 in_dim、两个隐藏层的维度 hid_dim1 和 hid_dim2,以及输出维度 out_dim。
self.layer = nn.Sequential(
nn.Linear(in_dim, hid_dim1),
nn.ReLU(),
nn.Linear(hid_dim1, hid_dim2),
nn.ReLU(),
nn.Linear(hid_dim2, out_dim),
)
def forward(self, x):
x = self.layer(x)
x = torch.sigmoid(x)
return x
# 定义了数据在模型中的前向传播过程。输入数据 x 首先经过 nn.Sequential 容器中的三个线性层和两个 ReLU 激活函数,最后输出预测结果,使用 Sigmoid 函数作为最后一层的激活函数可以将网络的输出解释为样本属于正类的概率
4.torch实现一个完整的神经网络
以下是例子:
model = MLP() # 创建一个MLP神经网络模型实例
criterion = get_loss() # 获得损失函数,回归问题通常会使用MSE损失,而分类问题通常会使用交叉熵损失
optimizer = optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器来更新模型参数,学习率为0.001
# 训练模型
num_epochs = 100 # 设置训练的epoch数量为100
for epoch in range(num_epochs): # 对于每一轮
# 训练
outputs = model(X_tensor) # 进行前向传播,得到模型的输出
loss = criterion(outputs, y_tensor) # 计算模型输出与真实标签之间的损失
optimizer.zero_grad() # 梯度清零,避免梯度累加
loss.backward() # 反向传播,计算梯度
optimizer.step() # 更新模型参数
if (epoch + 1) % 1 == 0: # 每训练1个epoch后
acc, recall = calculate_accuracy_recall(outputs, y_tensor) # 计算模型的准确率和召回率
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__() # 初始化神经网络模型
self.fc1 = nn.Linear(2, 64) # 输入层到第一隐藏层的全连接层
self.fc2 = nn.Linear(64, 32) # 第一隐藏层到第二隐藏层的全连接层
self.fc3 = nn.Linear(32, 2) # 第二隐藏层到输出层的全连接层
self.relu = nn.ReLU() # 激活函数ReLU
self.softmax = nn.Softmax(dim=1) # Softmax函数,用于多分类问题
def forward(self, x):
out = self.fc1(x) # 数据前向传播至第一隐藏层
out = self.relu(out) # 激活函数处理
out = self.fc2(out) # 数据前向传播至第二隐藏层
out = self.relu(out) # 激活函数处理
out = self.fc3(out) # 数据前向传播至输出层
out = self.softmax(out) # Softmax处理得到最终输出
return out # 返回输出结果
5. 简单神经网络搭建:通过继承 Module 类自定义模型
- Module 类的一些核心方法和属性
__init__(self)
: 初始化方法,用于初始化 Module 类的实例。forward(self, *input)
: 前向传播方法,定义了模型的前向计算逻辑。add_module(self, name, module)
: 添加子模块的方法。cuda(self, device=None)
: 将模型参数移动到 GPU 上。cpu(self)
: 将模型参数移动到 CPU 上。__call__(self, *input, **kwargs)
: 使实例能够像函数一样被调用,实际会调用 forward 方法。parameters(self, recurse=True)
: 返回模型的参数迭代器。named_parameters(self, prefix='', recurse=True)
: 返回模型的命名参数迭代器。children(self)
: 返回模型的子模块迭代器。named_children(self)
: 返回模型的命名子模块迭代器。modules(self)
: 返回模型及其子模块的迭代器。named_modules(self, memo=None, prefix='')
: 返回模型及其子模块的命名迭代器。train(self, mode=True)
: 设置模型为训练模式。eval(self)
: 设置模型为评估模式。zero_grad(self)
: 将模型的梯度清零。__repr__(self)
: 返回模型的可打印表示形式。__dir__(self)
: 返回模型的属性列表。
- 具有可学习参数的层(如全连接层、卷积层等)放在构造函数__init__()中
- 不具有可学习参数的层(如ReLU、dropout、BatchNormanation层)可放可不放在构造函数中,如果不放在构造函数__init__里面,则在forward方法里面可以使用nn.functional来代替
例子:
下面的是将所有的层都放在了构造函数__init__里面,但是只是定义了一系列的层,各个层之间到底是什么连接关系并没有
import torch
class MyNet(torch.nn.Module):
def __init__(self):
super(MyNet, self).__init__() # 调用父类的构造函数,初始化模型
self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1) # 创建第一个卷积层
self.relu1 = torch.nn.ReLU() # 创建第一个ReLU激活函数层
self.max_pooling1 = torch.nn.MaxPool2d(2, 1) # 创建第一个最大池化层
self.conv2 = torch.nn.Conv2d(3, 32, 3, 1, 1) # 创建第二个卷积层
self.relu2 = torch.nn.ReLU() # 创建第二个ReLU激活函数层
self.max_pooling2 = torch.nn.MaxPool2d(2, 1) # 创建第二个最大池化层
self.dense1 = torch.nn.Linear(32 * 3 * 3, 128) # 创建第一个全连接层
self.dense2 = torch.nn.Linear(128, 10) # 创建第二个全连接层
def forward(self, x):
x = self.conv1(x) # 第一个卷积层操作
x = self.relu1(x) # 第一个ReLU激活函数操作
x = self.max_pooling1(x) # 第一个最大池化操作
x = self.conv2(x) # 第二个卷积层操作
x = self.relu2(x) # 第二个ReLU激活函数操作
x = self.max_pooling2(x) # 第二个最大池化操作
x = self.dense1(x) # 第一个全连接层操作
x = self.dense2(x) # 第二个全连接层操作
return x
model = MyNet()
print(model)
'''
运行结果为:
MyNet(
(conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) # 第一个卷积层信息
(relu1): ReLU() # 第一个ReLU激活函数信息
(max_pooling1): MaxPool2d(kernel_size=2, stride=1, padding=0, dilation=1, ceil_mode=False) # 第一个最大池化层信息
(conv2): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) # 第二个卷积层信息
(relu2): ReLU() # 第二个ReLU激活函数信息
(max_pooling2): MaxPool2d(kernel_size=2, stride=1, padding=0, dilation=1, ceil_mode=False) # 第二个最大池化层信息
(dense1): Linear(in_features=288, out_features=128, bias=True) # 第一个全连接层信息
(dense2): Linear(in_features=128, out_features=10, bias=True) # 第二个全连接层信息
)
'''
将没有训练参数的层没有放在构造函数里面了,所以这些层就不会出现在model里面,但是运行关系是在forward里面通过functional的方法实现的。
import torch # 引入torch模块
import torch.nn.functional as F # 引入torch.nn.functional模块,并使用F作为别名
class MyNet(torch.nn.Module):
def __init__(self):
super(MyNet, self).__init__() # 调用父类的构造函数
self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1) # 创建第一个卷积层
self.conv2 = torch.nn.Conv2d(3, 32, 3, 1, 1) # 创建第二个卷积层
# 去掉了激活层和最大池化层,后面forward用F模块里面的
self.dense1 = torch.nn.Linear(32 * 3 * 3, 128) # 创建第一个全连接层
self.dense2 = torch.nn.Linear(128, 10) # 创建第二个全连接层
def forward(self, x):
x = self.conv1(x) # 第一个卷积层操作
x = F.relu(x) # 使用F模块中的ReLU激活函数
x = F.max_pool2d(x) # 使用F模块中的最大池化函数
x = self.conv2(x) # 第二个卷积层操作
x = F.relu(x) # 使用F模块中的ReLU激活函数
x = F.max_pool2d(x) # 使用F模块中的最大池化函数
x = self.dense1(x) # 第一个全连接层操作
x = self.dense2(x) # 第二个全连接层操作
return x
model = MyNet()
print(model)
'''
运行结果为:
MyNet(
(conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(conv2): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(dense1): Linear(in_features=288, out_features=128, bias=True)
(dense2): Linear(in_features=128, out_features=10, bias=True)
)
'''
6. module的多种实现
PyTorch 中用于构建神经网络的模块和容器有:
Container(Module)
: 现已不再使用。一般指代包含其他模块的复杂模块。Sequential(Module)
: 用于按顺序连接多个层或模块的容器。它按照添加的顺序依次执行每个模块。ModuleList(Module)
: 用于存储子模块的列表,类似 Python 列表的方式进行索引访问。ModuleDict(Module)
: 使用字典的形式存储子模块,可以通过键来访问子模块。ParameterList(Module)
: 专门用于存储参数,可以通过索引访问,并且会被视为模型的一部分。ParameterDict(Module)
: 用于存储参数,可以通过键来访问,并且会被视为模型的一部分。
简单的Sequential介绍:
例子:
import torch.nn as nn
from collections import OrderedDict
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1,20,5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20,64,5)),
('relu2', nn.ReLU())
]))
# 可以看到,能够在前面写名称
print(model)
print(model[2]) # 通过索引获取第几个层
'''运行结果为:
Sequential(
(conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(relu1): ReLU()
(conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(relu2): ReLU()
)
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''
每一个层都有了自己的名称,但不能够通过名称直接获取层,依然只能通过索引index,即
model[2] 是正确的
model[“conv2”] 是错误的
这其实是由它的定义实现的,看上面的Sequenrial定义可知,只支持index访问。
另一种实现的方式:
import torch.nn as nn
from collections import OrderedDict
model = nn.Sequential()
model.add_module("conv1",nn.Conv2d(1,20,5))
model.add_module('relu1', nn.ReLU())
model.add_module('conv2', nn.Conv2d(20,64,5))
model.add_module('relu2', nn.ReLU())
print(model)
print(model[2]) # 通过索引获取第几个层
Sequential里面并没有定义add_module()。但实际上,这个方法是定义在它的父类Module里面的,Sequential继承了而已
定义:
def add_module(self, name, module):
# 第一种:
import torch.nn as nn
from collections import OrderedDict
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.conv_block = nn.Sequential( # # 定义一个包含卷积、激活函数ReLU和最大池化层的序列
nn.Conv2d(3, 32, 3, 1, 1), # 输入通道数为3,输出通道数为32的3x3卷积层
nn.ReLU(),
nn.MaxPool2d(2))
self.dense_block = nn.Sequential( # 定义一个包含全连接层和激活函数ReLU的序列
nn.Linear(32 * 3 * 3, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
# 在这里实现层之间的连接关系,其实就是所谓的前向传播
def forward(self, x): # 通过 self 可以访问该类的成员变量和成员方法,包括 conv_block 和 dense_block 这两个成员变量,以及 forward 这个方法
conv_out = self.conv_block(x)
res = conv_out.view(conv_out.size(0), -1) # 将 conv_out 进行展平操作,将其转换为一个二维张量,以便后续输入全连接层进行处理
out = self.dense_block(res)
return out
model = MyNet()
print(model)
# 第二种: 就是多了个命名
import torch.nn as nn
from collections import OrderedDict
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.conv_block = nn.Sequential(
OrderedDict(
[
("conv1", nn.Conv2d(3, 32, 3, 1, 1)),
("relu1", nn.ReLU()),
("pool", nn.MaxPool2d(2))
]
))
self.dense_block = nn.Sequential(
OrderedDict([
("dense1", nn.Linear(32 * 3 * 3, 128)),
("relu2", nn.ReLU()),
("dense2", nn.Linear(128, 10))
])
)
def forward(self, x):
conv_out = self.conv_block(x)
res = conv_out.view(conv_out.size(0), -1)
out = self.dense_block(res)
return out
model = MyNet()
print(model)
# 第三种:就是换成add_module这种方式添加
import torch.nn as nn
from collections import OrderedDict
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.conv_block=torch.nn.Sequential()
self.conv_block.add_module("conv1",torch.nn.Conv2d(3, 32, 3, 1, 1))
self.conv_block.add_module("relu1",torch.nn.ReLU())
self.conv_block.add_module("pool1",torch.nn.MaxPool2d(2))
self.dense_block = torch.nn.Sequential()
self.dense_block.add_module("dense1",torch.nn.Linear(32 * 3 * 3, 128))
self.dense_block.add_module("relu2",torch.nn.ReLU())
self.dense_block.add_module("dense2",torch.nn.Linear(128, 10))
def forward(self, x):
conv_out = self.conv_block(x)
res = conv_out.view(conv_out.size(0), -1)
out = self.dense_block(res)
return out
model = MyNet()
print(model)
self):
super(MyNet, self).init()
self.conv_block=torch.nn.Sequential()
self.conv_block.add_module(“conv1”,torch.nn.Conv2d(3, 32, 3, 1, 1))
self.conv_block.add_module(“relu1”,torch.nn.ReLU())
self.conv_block.add_module(“pool1”,torch.nn.MaxPool2d(2))
self.dense_block = torch.nn.Sequential()
self.dense_block.add_module("dense1",torch.nn.Linear(32 * 3 * 3, 128))
self.dense_block.add_module("relu2",torch.nn.ReLU())
self.dense_block.add_module("dense2",torch.nn.Linear(128, 10))
def forward(self, x):
conv_out = self.conv_block(x)
res = conv_out.view(conv_out.size(0), -1)
out = self.dense_block(res)
return out
model = MyNet()
print(model)