В этой статье создается сетевая модель resnet18. Поскольку она была построена мной, официальную модель для предварительного обучения загрузить невозможно.
Главное — понять, как устроена сеть.
Адрес, по которому можно загрузить официальную предварительно обученную модель (не написан мной): модель сети resnet18, можно загрузить официальную модель сети.
1. Схема структуры сети resnet18 выглядит следующим образом:
2. Процесс:
- Сначала пройдите через слой свертки с kernel_size (7,7), а затем пройдите через слой пула с kernel_size (3,3).
- Затем выполняется серия слоев сети сверточного суммирования остатков (красный прямоугольник на рисунке).
- Затем он проходит через уровень глобального среднего пула (средний пул) и полносвязный уровень (fc).
3. Что касается анализа остаточного блока (сетевого уровня, куда добавляются сверточные остатки) (поясняется во втором красном поле):
Вы можете видеть, что в остаточном блоке есть два [3x3, 128], что указывает на то, что он прошел через два сверточных слоя 3x3. После первой свертки 3x3 размер шага равен 2, а размер шага последующей свертки 3x3 равен 1. Добавление остатков требует, чтобы размеры фигур двух добавленных объектов (векторов) были согласованными, а поскольку шаг первой свертки 3x3 равен 2, размер входных и выходных данных будет несовместимым. Поэтому необходимо скорректировать размер объекта перед добавлением остатков . В коде есть ярлык self.. Эта переменная используется для определения необходимости корректировки признаков, а затем добавляются остатки.
Первый остаточный слой в красном поле несколько отличается от последующих остаточных слоев.Первый остаточный слой подвергается двум сверткам 3x3 с размером шага 1.
Код my_resnet.py (встроенная сеть)
После создания игровой сети вы можете позвонить в созданную вами сеть для обучения.
Предыдущие статьи об обучении находятся в разделе: Обучение классификационной сети.
В следующей главе я напишу о некоторых подводных камнях, встречающихся в процессе построения сети.
import torch
import torch.nn as nn
import torch.nn.functional as F
def auto_pad(k, p=None):
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k]
return p
class Residual_2(nn.Module):
def __init__(self, in_channel, out_channel, stride=1, shortcut=False):
super(Residual_2, self).__init__()
self.shortcut = shortcut
self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=3, padding=auto_pad(3), stride=stride)
self.bn1 = nn.BatchNorm2d(out_channel)
self.relu1 = nn.ReLU()
self.conv2 = nn.Conv2d(out_channel, out_channel, kernel_size=3, padding=auto_pad(3), stride=1)
self.bn2 = nn.BatchNorm2d(out_channel)
if self.shortcut:
self.conv3 = nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride)
def forward(self, x):
y1 = self.conv1(x)
y1 = self.bn1(y1)
y1 = self.relu1(y1)
y2 = self.conv2(y1)
y2 = self.bn2(y2)
if self.shortcut:
x = self.conv3(x)
out = x + y2
return out
class HeadConv(nn.Module):
def __init__(self, in_channel, out_channel):
super(HeadConv, self).__init__()
self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=7, padding=3, stride=2)
self.bn1 = nn.BatchNorm2d(out_channel)
self.relu1 = nn.ReLU()
self.pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu1(x)
x = self.pool(x)
return x
class MainNet(nn.Module):
def __init__(self, num_classes=1000):
super(MainNet, self).__init__()
blocks_num = [2, 2, 2, 2]
channel_planes = [64, 128, 256, 512]
self.num_classes = num_classes
self.conv1 = HeadConv(3, channel_planes[0])
self.blocks = [nn.Sequential(), nn.Sequential(), nn.Sequential(), nn.Sequential()]
for i in range(len(blocks_num)):
if i == 0:
for j in range(blocks_num[i]):
# 且第一个block的stride全为1
if j == 0:
self.blocks[i].add_module("conv_block%s_branch_%s_2a" % (i + 1, j),
Residual_2(channel_planes[0], channel_planes[0], stride=1,
shortcut=True))
else:
self.blocks[i].add_module("conv_block%s_branch_%s_2b" % (i + 1, j),
Residual_2(channel_planes[0], channel_planes[0], stride=1,
shortcut=False))
else:
for j in range(blocks_num[i]):
# 且第一个block的stride全为1
if j == 0:
self.blocks[i].add_module("conv_block%s_branch_%s_2a" % (i + 1, j),
Residual_2(channel_planes[i - 1], channel_planes[i], stride=2,
shortcut=True))
else:
self.blocks[i].add_module("conv_block%s_branch_%s_2b" % (i + 1, j),
Residual_2(channel_planes[i], channel_planes[i], stride=1,
shortcut=False))
self.block1, self.block2, self.block3, self.block4 = self.blocks
self.avg = nn.AdaptiveAvgPool2d(output_size=(1, 1))
self.fc = nn.Sequential(
nn.Flatten(),
nn.Linear(512, 100),
nn.Linear(100, num_classes),
)
def forward(self, x):
x = self.conv1(x)
feat1 = self.block1(x)
feat2 = self.block2(feat1)
feat3 = self.block3(feat2)
feat4 = self.block4(feat3)
avg = self.avg(feat4)
outputs = self.fc(avg)
return outputs
if __name__ == '__main__':
x = torch.ones(size=(4, 3, 224, 224)).to("cuda")
model = MainNet(num_classes=10)
print(model)
# model = model.to("cuda")
# out = model(x)
# print(out.shape)
from torchsummary import summary
# summary(model, (3, 224, 224), device="cuda")
Код обучения:
import os
import torch
from PIL import ImageFile
import torch.optim as optim
from my_resnet import MainNet
import torch.nn.functional as F
import matplotlib.pyplot as plt
from torchvision import datasets, transforms
ImageFile.LOAD_TRUNCATED_IMAGES = True
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
def train():
running_loss = 0
for batch_idx, (data, target) in enumerate(train_data):
data, target = data.to(device), target.to(device)
out = net(data)
loss = criterion(out, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
return running_loss
def test():
correct, total = 0, 0
with torch.no_grad():
for _, (data, target) in enumerate(val_data):
data, target = data.to(device), target.to(device)
out = net(data)
out = F.softmax(out, dim=1)
prediction = out.argmax(dim=1)
# prediction = torch.max(out.data, dim=1)[1]
total += target.size(0)
correct += (prediction == target).sum().item()
print('Accuracy on test set: (%d/%d)=%d %%' % (correct, total, 100 * correct / total))
if __name__ == '__main__':
LR = 0.0001
Epoches = 200
Batch_Size = 4
num_classes = 3
best_loss = 100
Image_Size = [256, 256]
# 1.数据加载
data_dir = r'D:\Code\python\完整项目放置\classify_project\multi_classification\my_dataset1'
# 1.1 定义要对数据进行的处理
data_transform = {x: transforms.Compose([transforms.Resize(Image_Size), transforms.ToTensor()]) for x in
["train", "valid"]}
image_datasets = {x: datasets.ImageFolder(root=os.path.join(data_dir, x), transform=data_transform[x]) for x in
["train", "valid"]}
dataloader = {x: torch.utils.data.DataLoader(dataset=image_datasets[x], batch_size=Batch_Size, shuffle=True) for x
in ["train", "valid"]}
train_data, val_data = dataloader["train"], dataloader["valid"]
index_classes = image_datasets["train"].class_to_idx
print(index_classes)
example_classes = image_datasets["train"].classes
print(example_classes)
net = MainNet(num_classes)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
net.to(device)
# 5.定义损失函数,以及优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=LR)
loss_list = []
for epoch in range(Epoches):
loss = train()
loss_list.append(loss)
print("第%d轮的loss为:%5f:" % (epoch, loss))
test()
if loss < best_loss:
best_loss = loss
torch.save(net, "best.pth")
torch.save(net, "last.pth")
plt.title("Graph")
plt.plot(range(Epoches), loss_list)
plt.ylabel("loss")
plt.xlabel("epoch")
plt.show()