⾃⼰搭建resnet18⽹络并加载torchvision⾃带权重的操作
直接搭建⽹络必须与torchvision⾃带的⽹络的权重也就是pth⽂件的结构、尺⼨和变量命名完全⼀致,否则⽆法加载权重⽂件。
此时可⽐较2个字典逐⼀加载,详见
import torch
import torchvision
import cv2 as cv
from utils.utils import letter_box
from model.backbone import ResNet18
model1 = ResNet18(1)
model2 = snet18(progress=False)
fc = model2.fc
model2.fc = Linear(512, 1)
# print(model)
model_dict1 = model1.state_dict()
model_dict2 = torch.load('resnet18.pth')
model_list1 = list(model_dict1.keys())
model_list2 = list(model_dict2.keys())
len1 = len(model_list1)
len2 = len(model_list2)
minlen = min(len1, len2)
for n in range(minlen):
if model_dict1[model_list1[n]].shape != model_dict2[model_list2[n]].shape:
continue
model_dict1[model_list1[n]] = model_dict2[model_list2[n]]
model1.load_state_dict(model_dict1)
missing, unspected = model2.load_state_dict(model_dict2)
image = cv.imread('zhn1.jpg')
image = letter_box(image, 224)
image = image[:, :, ::-1].transpose(2, 0, 1)
print('Network loading complete.')
model1.eval()
model2.eval()
_grad():
image = sor(image/256, dtype=torch.float32).unsqueeze(0)
predict1 = model1(image)
predict2 = model2(image)
print('finished')
# torch.save(model.state_dict(), 'resnet18.pth')
以上为全部程序,最终可测试原模型与加载了⾃带权重的⾃定义模型的输出是否相等。
补充:使⽤Pytorch搭建ResNet分类⽹络并基于迁移学习训练
如果stride=1,padding=1
卷积处理是不会改变特征矩阵的⾼和宽
使⽤BN层时
卷积中的参数bias置为False(有⽆偏置BN层的输出都相同),BN层放在conv层和relu层的中间
复习BN层:
Batch Norm 层是对每层数据归⼀化后再进⾏线性变换改善数据分布, 其中的线性变换是可学习的.
Batch Norm优点:减轻过拟合;改善梯度传播(权重不会过⾼或过低)容许较⾼的学习率,能够提⾼训练速度。减轻对初始化权重的强依赖,使得数据分布在激活函数的⾮饱和区域,⼀定程度上解决梯度消失问题。作为⼀种正则化的⽅式,在某种程度上减少对dropout的使⽤。
Batch Norm层摆放位置:在激活层(如 ReLU )之前还是之后,没有⼀个统⼀的定论。
BN层与 Dropout 合作:Batch Norm的提出使得dropout的使⽤减少,但是Batch Norm不能完全取代dropout,保留较⼩的dropout 率,如0.2可能效果更佳。
为什么要先normalize再通过γ,β线性变换恢复接近原来的样⼦,这不是多此⼀举吗?
在⼀定条件下可以纠正原始数据的分布(⽅差,均值变为新值γ,β),当原始数据分布⾜够好时就是恒等映射,不改变分布。如果不做BN,⽅差和均值对前⾯⽹络的参数有复杂的关联依赖,具有复杂的⾮线性。在新参数γH′ + β中仅由γ,β确定,与前边⽹络的参数⽆关,因此新参数很容易通过梯度下降来学习,能够学习到较好的分布。
迁移学习导⼊权重和下载权重:
snet#ctrl+⿏标左键点击即可下载权重
net = resnet34()#⼀开始不能设置全连接层的输出种类为⾃⼰想要的,必须先将模型参数载⼊,再修改全连接层
# 官⽅提供载⼊预训练模型的⽅法
model_weight_path = "./resnet34-pre.pth"#权重路径
missing_keys, unexpected_keys = net.load_state_dict(torch.load(model_weight_path), strict=False)#载⼊模型权重
inchannel = net.fc.in_features
net.fc = nn.Linear(inchannel, 5)#重新确定全连接层
完整代码:
model部分:
as nn
import torch
class BasicBlock(nn.Module):#对应18层和34层所对应的残差结构(既要有实线残差结构功能,也要有虚线残差结构功能)
expansion = 1#残差结构主分⽀上的三个卷积层是否相同,相同为1,第三层是⼀⼆层四倍则为4正则化权重
def __init__(self, in_channel, out_channel, stride=1, downsample=None):#downsample代表虚线残差结构选项
super(BasicBlock, self).__init__()
kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channel)
kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channel)
self.downsample = downsample
def forward(self, x):
identity = x
if self.downsample is not None:
identity = self.downsample(x)#得到捷径分⽀的输出
out = v1(x)
out = self.bn1(out)
out = lu(out)
out = v2(out)
out = self.bn2(out)
out += identity
out = lu(out)
return out#得到残差结构的最终输出
class Bottleneck(nn.Module):#对应50层、101层和152层所对应的残差结构
expansion = 4#第三层卷积核个数是第⼀层和第⼆层的四倍
def __init__(self, in_channel, out_channel, stride=1, downsample=None):
super(Bottleneck, self).__init__()
kernel_size=1, stride=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channel)
kernel_size=3, stride=stride, bias=False, padding=1)
self.bn2 = nn.BatchNorm2d(out_channel)
kernel_size=1, stride=1, bias=False)
self.bn3 = nn.BatchNorm2d(out_pansion)
self.downsample = downsample
def forward(self, x):
identity = x
if self.downsample is not None:
identity = self.downsample(x)
out = v1(x)
out = self.bn1(out)
out = lu(out)
out = v2(out)
out = self.bn2(out)
out = lu(out)
out = v3(out)
out = self.bn3(out)
out += identity
out = lu(out)
return out
class ResNet(nn.Module):#定义整个⽹络的框架部分
#blocks_num是残差结构的数⽬,是⼀个列表参数,block对应哪个残差模块
def __init__(self, block, blocks_num, num_classes=1000, include_top=True):
super(ResNet, self).__init__()
self.include_top = include_top
self.in_channel = 64#通过第⼀个池化层后所得到的特征矩阵的深度
padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(self.in_channel)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, blocks_num[0])
self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)
self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)
self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)
if self.include_top:
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # output size = (1, 1)
self.fc = nn.Linear(512 * pansion, num_classes)
for m dules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
def _make_layer(self, block, channel, block_num, stride=1):#channel:残差结构中,第⼀个卷积层所使⽤的卷积核的个数        downsample = None
if stride != 1 or self.in_channel != channel * pansion:#18层和34层会直接跳过这个if语句
downsample = nn.Sequential(
nn.Conv2d(self.in_channel, channel * pansion, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(channel * pansion))
layers = []
layers.append(block(self.in_channel, channel, downsample=downsample, stride=stride))
self.in_channel = channel * pansion
for _ in range(1, block_num):
layers.append(block(self.in_channel, channel))
return nn.Sequential(*layers)
def forward(self, x):
x = v1(x)
x = self.bn1(x)
x = lu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
if self.include_top:#默认是true
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
def resnet34(num_classes=1000, include_top=True):
return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)
def resnet101(num_classes=1000, include_top=True):
return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top)
训练部分:
import torch
as nn
from torchvision import transforms, datasets
import json
import matplotlib.pyplot as plt
import os
import torch.optim as optim
from model import resnet34, resnet101
snet#ctrl+⿏标左键点击即可下载权重
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
data_transform = {
"train": transforms.Compose([transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),#和官⽹初始化⽅法保持⼀致    "val": transforms.Compose([transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}
data_root = os.path.abspath(os.path.wd(), "../.."))  # get data root path
image_path = data_root + "/data_set/flower_data/"  # flower data set path
train_dataset = datasets.ImageFolder(root=image_path+"train",
transform=data_transform["train"])
train_num = len(train_dataset)
# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
flower_list = train_dataset.class_to_idx
cla_dict = dict((val, key) for key, val in flower_list.items())
# write dict into json file
json_str = json.dumps(cla_dict, indent=4)
with open('class_indices.json', 'w') as json_file:
json_file.write(json_str)
batch_size = 16
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size, shuffle=True,
num_workers=0)
validate_dataset = datasets.ImageFolder(root=image_path + "val",
transform=data_transform["val"])
val_num = len(validate_dataset)
validate_loader = torch.utils.data.DataLoader(validate_dataset,
batch_size=batch_size, shuffle=False,
num_workers=0)
net = resnet34()#⼀开始不能设置全连接层的输出种类为⾃⼰想要的,必须先将模型参数载⼊,再修改全连接层
# 官⽅提供载⼊预训练模型的⽅法
model_weight_path = "./resnet34-pre.pth"#权重路径
missing_keys, unexpected_keys = net.load_state_dict(torch.load(model_weight_path), strict=False)#载⼊模型权重inchannel = net.fc.in_features
net.fc = nn.Linear(inchannel, 5)#重新确定全连接层
<(device)
loss_function = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.0001)
best_acc = 0.0
save_path = './resNet34.pth'
for epoch in range(3):
# train
running_loss = 0.0
for step, data in enumerate(train_loader, start=0):
images, labels = data
<_grad()
logits = (device))
loss = loss_function(logits, (device))
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
# print train process
rate = (step+1)/len(train_loader)
a = "*" * int(rate * 50)
b = "." * int((1 - rate) * 50)
print("\rtrain loss: {:^3.0f}%[{}->{}]{:.4f}".format(int(rate*100), a, b, loss), end="")
print()
# validate
net.eval()#控制BN层状态
acc = 0.0  # accumulate accurate number / epoch
_grad():
for val_data in validate_loader:
val_images, val_labels = val_data
outputs = net((device))  # eval model only have last output layer
# loss = loss_function(outputs, test_labels)
predict_y = torch.max(outputs, dim=1)[1]
acc += (predict_y == (device)).sum().item()
val_accurate = acc / val_num
if val_accurate > best_acc:
best_acc = val_accurate
torch.save(net.state_dict(), save_path)
print('[epoch %d] train_loss: %.3f  test_accuracy: %.3f' %
(epoch + 1, running_loss / step, val_accurate))
print('Finished Training')
预测部分:
import torch
from model import resnet34
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
import json
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
data_transform = transforms.Compose(
[transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])#采⽤和训练⽅法⼀样的标准化处理# load image
img = Image.open("../aa.jpg")
plt.imshow(img)
# [N, C, H, W]
img = data_transform(img)
# expand batch dimension
img = torch.unsqueeze(img, dim=0)
# read class_indict
try:
json_file = open('./class_indices.json', 'r')
class_indict = json.load(json_file)
except Exception as e:
print(e)
exit(-1)
# create model
model = resnet34(num_classes=5)
# load model weights
model_weight_path = "./resNet34.pth"
model.load_state_dict(torch.load(model_weight_path, map_location=device))#载⼊训练好的模型参数model.eval()#使⽤eval()模式
_grad():#不跟踪损失梯度
# predict class
output = torch.squeeze(model(img))#压缩batch维度
predict = torch.softmax(output, dim=0)#通过softmax得到概率分布
predict_cla = torch.argmax(predict).numpy()#寻最⼤值所对应的索引
print(class_indict[str(predict_cla)], predict[predict_cla].numpy())#打印类别信息和概率
plt.show()
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。