模型的两种写法对比
一种是在init中构造单个工具,供forward一个一个调用工具
一种是在init中构造工具集合,供forward统一调用(调用工具集合)
import torch
from torch import nn
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.conv1 = nn.Conv2d(3, 32, 5, 1, 2)
self.pool1 = nn.MaxPool2d(2)
self.conv2 = nn.Conv2d(32, 32, 5, 1, 2)
self.pool2 = nn.MaxPool2d(2)
self.conv3 = nn.Conv2d(32, 64, 5, 1, 2)
self.pool3 = nn.MaxPool2d(2)
self.flatten = nn.Flatten()
self.fc1 = nn.Linear(64 * 4 * 4, 64)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = self.pool(F.relu(self.conv3(x)))
x = self.flatten(x)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
import torch
from torch import nn
class my_model(nn.Module):
def __init__(self):
super(my_model, self).__init__()
self.model=nn.Sequential(
nn.Conv2d(3,32,5,1,2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,1,2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,1,2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4,64),
nn.Linear(64,10)
)
def forward(self,x):
x=self.model(x)
return x
完整的项目流程
model.py(模型的第一种写法,使用Sequential)
import torch
from torch import nn
class my_model(nn.Module):
def __init__(self):
super(my_model, self).__init__()
self.model=nn.Sequential(
nn.Conv2d(3,32,5,1,2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,1,2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,1,2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4,64),
nn.Linear(64,10)
)
def forward(self,x):
x=self.model(x)
return x
train-test.py
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
import time
start_time=time.time()
# ==================01、准备数据====================================
train_data=torchvision.datasets.CIFAR10(root="my_dataset",
train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
test_data=torchvision.datasets.CIFAR10(root="my_dataset",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
print(f'训练集的长度:{len(train_data)},测试集的长度:{len(test_data)}') # 训练集的长度50000,测试集的长度10000
train_loader=DataLoader(dataset=train_data,batch_size=64) #一共有64批,每批长度len=782;782x64=50048
test_loader=DataLoader(dataset=test_data,batch_size=64) #一共有64批,每批长度len=157;157x64=10048
# ===============02、创建实例模型,损失函数,优化器==================================
duixiang=my_model()
loss_func=nn.CrossEntropyLoss()
# if torch.cuda.is_available():
# duixiang=duixiang.cuda()
# loss_func=loss_func.cuda()
optimizer=torch.optim.SGD(params=duixiang.parameters(),lr=0.01) # 优化器传入实例模型的所有参数,学习率
# ===============03、设置训练初始参数================================================
epoch=10 # 训练的轮数
sum_train_step=0 # 训练集训练的总次数
sum_test_step=0 # 测试集训练的总次数
# ===============04、开始训练========================================================
for i in range(epoch): # 训练步骤包括训练和测试,分别用训练集和测试集
duixiang.train() # 训练========================================================
for data in train_loader:
imgs,label=data
# if torch.cuda.is_available():
# imgs=imgs.cuda()
# label=label.cuda()
output=duixiang(imgs)
loss=loss_func(output,label)
# 优化器设置
optimizer.zero_grad() # 梯度清零
loss.backward() # 反向传播
optimizer.step() # 更新参数
sum_train_step=sum_train_step+1 # 记录训练次数
if sum_train_step % 100 ==0:
print(f'已训练{sum_train_step}次')
print(f'这是第{i+1}轮训练,此时训练集loss值为:{loss}')
duixiang.eval() # 测试(每执行一轮测试一次)=============================================
with torch.no_grad():
sum_test_loss=0
for data in test_loader:
imgs,label=data
# if torch.cuda.is_available():
# imgs=imgs.cuda()
# label=label.cuda()
output=duixiang(imgs)
loss=loss_func(output,label)
sum_test_step=sum_test_step+1
if sum_test_step % 10 ==0:
print(f'已测试{sum_test_step}次,此时的loss值为:{loss}')
sum_test_loss=sum_test_loss+loss # 计算这一轮次的总loss值
print(f'这是第{i+1}轮测试,此时测试集loss值之和为:{sum_test_loss}')
# ========================保存模型==================================================
torch.save(duixiang,"moxing")
end_time=time.time()
print(end_time-start_time)
类中的forward函数的调用
在使用nn.Moudule时候,多常见于forward中对init中的类的调用
对象(参数)==对象.forward(参数)
对象=类(传入的是init参数)
-
区别
-
对象(参数):传入的是创建这个对象的类的forward函数
-
类(参数):传入的类中的init参数
-
-
为什么x = self.conv1(x)可以省略.forward?
- 在Python编程语言中,
x = self.conv1(x)
可以省略.forward()
方法,因为在定义类时,如果继承了torch.nn.Module
,那么该类就会自动拥有__call__()
方法。__call__()
方法会将self.conv1(x)
的计算过程封装起来,当调用该类的实例时,就会执行__call__()
方法。因此,x = self.conv1(x)
相当于调用了__call__()
方法,从而实现了前向传播的过程。
- 在Python编程语言中,
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
def forward(self, x):
x = self.conv1(x)
return x
这段代码是不是和以下代码等效?x传递给self.conv1,我们实际上是在调用该层的前向传播方法,以便对输入数据进行处理并生成输出结果。这个输出结果将作为下一层或最终的预测结果使用。
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
def forward(self, x):
x = self.conv1.forward(x)
return x
卷积操作实例
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
# 定义模型
class my_model(nn.Module):
def __init__(self):
super(my_model, self).__init__()
# 卷积层实例,输入channel=3,输出channel=6,卷积核:3x3,step=1,padding=0
# 这段代码中,卷积核的9个元素没有明确定义。在PyTorch中,卷积层的权重是通过nn.Parameter类自动初始化的。默认情况下,这些权重是随机初始化的,范围在-1到1之间。如果你想自定义权重,可以在__init__方法中使用nn.init模块进行初始化。所以这段代码的img3的张量输出结果是不一致的
# Conv2d是一个class;对象的Conv_1属性conv2d类创建的对象;
# 也可以理解为self.conv_1就是这个类创建的函数(对象),也就是工具(函数)做好了,方便forward调用
self.conv_1=Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=1)
def forward(self,input):
x=self.conv_1(input)
return x
my_CNN=my_model()
# 准备数据
img=Image.open('6aa3baf260a16ee94c11dab0d498a0c0.jpg')
trans_tensor=torchvision.transforms.ToTensor()
img2=trans_tensor(img)
print(img2.shape) # torch.Size([3, 1280, 2048])
# 传入模型
img3=my_CNN(img2)
# 输出结果
print(img3.shape) # torch.Size([6, 1280, 2048])
print(img3)