C语言学习网

怎么搭建resnet18网络并加载torchvision自带权重

发表于:2022-12-09 作者:安全数据网编辑
编辑最后更新 2022年12月09日,这篇文章给大家分享的是有关怎么搭建resnet18网络并加载torchvision自带权重的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。直接搭建网络必须与torchvisi

这篇文章给大家分享的是有关怎么搭建resnet18网络并加载torchvision自带权重的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

直接搭建网络必须与torchvision自带的网络的权重也就是pth文件的结构、尺寸和变量命名完全一致,否则无法加载权重文件。

pytorch加载预训练模型与自己模型不匹配的解决方案

import torchimport torchvisionimport cv2 as cvfrom utils.utils import letter_boxfrom model.backbone import ResNet18model1 = ResNet18(1)model2 = torchvision.models.resnet18(progress=False)fc = model2.fcmodel2.fc = torch.nn.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()with torch.no_grad():    image = torch.tensor(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′ + β 中仅由 γ,β 确定,与前边网络的参数无关,因此新参数很容易通过梯度下降来学习,能够学习到较好的分布。

迁移学习导入权重和下载权重:

import torchvision.models.resnet#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_featuresnet.fc = nn.Linear(inchannel, 5)#重新确定全连接层

完整代码:

model部分:

import torch.nn as nnimport torchclass 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__()        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,                               kernel_size=3, stride=stride, padding=1, bias=False)        self.bn1 = nn.BatchNorm2d(out_channel)        self.relu = nn.ReLU()        self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=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 = self.conv1(x)        out = self.bn1(out)        out = self.relu(out)        out = self.conv2(out)        out = self.bn2(out)        out += identity        out = self.relu(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__()        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,                               kernel_size=1, stride=1, bias=False)        self.bn1 = nn.BatchNorm2d(out_channel)        self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,                               kernel_size=3, stride=stride, bias=False, padding=1)        self.bn2 = nn.BatchNorm2d(out_channel)        self.conv3 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel*self.expansion,                               kernel_size=1, stride=1, bias=False)        self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)        self.relu = nn.ReLU(inplace=True)        self.downsample = downsample    def forward(self, x):        identity = x        if self.downsample is not None:            identity = self.downsample(x)        out = self.conv1(x)        out = self.bn1(out)        out = self.relu(out)        out = self.conv2(out)        out = self.bn2(out)        out = self.relu(out)        out = self.conv3(out)        out = self.bn3(out)        out += identity        out = self.relu(out)        return outclass 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#通过第一个池化层后所得到的特征矩阵的深度        self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,                               padding=3, bias=False)        self.bn1 = nn.BatchNorm2d(self.in_channel)        self.relu = nn.ReLU(inplace=True)        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 * block.expansion, num_classes)        for m in self.modules():            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 * block.expansion:#18层和34层会直接跳过这个if语句            downsample = nn.Sequential(                nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),                nn.BatchNorm2d(channel * block.expansion))        layers = []        layers.append(block(self.in_channel, channel, downsample=downsample, stride=stride))        self.in_channel = channel * block.expansion        for _ in range(1, block_num):            layers.append(block(self.in_channel, channel))        return nn.Sequential(*layers)    def forward(self, x):        x = self.conv1(x)        x = self.bn1(x)        x = self.relu(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 xdef 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 torchimport torch.nn as nnfrom torchvision import transforms, datasetsimport jsonimport matplotlib.pyplot as pltimport osimport torch.optim as optimfrom model import resnet34, resnet101import torchvision.models.resnet#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.join(os.getcwd(), "../.."))  # get data root pathimage_path = data_root + "/data_set/flower_data/"  # flower data set pathtrain_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_idxcla_dict = dict((val, key) for key, val in flower_list.items())# write dict into json filejson_str = json.dumps(cla_dict, indent=4)with open('class_indices.json', 'w') as json_file:    json_file.write(json_str)batch_size = 16train_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_featuresnet.fc = nn.Linear(inchannel, 5)#重新确定全连接层net.to(device)loss_function = nn.CrossEntropyLoss()optimizer = optim.Adam(net.parameters(), lr=0.0001)best_acc = 0.0save_path = './resNet34.pth'for epoch in range(3):    # train    net.train()#控制BN层状态    running_loss = 0.0    for step, data in enumerate(train_loader, start=0):        images, labels = data        optimizer.zero_grad()        logits = net(images.to(device))        loss = loss_function(logits, labels.to(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    with torch.no_grad():        for val_data in validate_loader:            val_images, val_labels = val_data            outputs = net(val_images.to(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 == val_labels.to(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 torchfrom model import resnet34from PIL import Imagefrom torchvision import transformsimport matplotlib.pyplot as pltimport jsondevice = 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 imageimg = Image.open("../aa.jpg")plt.imshow(img)# [N, C, H, W]img = data_transform(img)# expand batch dimensionimg = torch.unsqueeze(img, dim=0)# read class_indicttry:    json_file = open('./class_indices.json', 'r')    class_indict = json.load(json_file)except Exception as e:    print(e)    exit(-1)# create modelmodel = resnet34(num_classes=5)# load model weightsmodel_weight_path = "./resNet34.pth"model.load_state_dict(torch.load(model_weight_path, map_location=device))#载入训练好的模型参数model.eval()#使用eval()模式with torch.no_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()

感谢各位的阅读!关于"怎么搭建resnet18网络并加载torchvision自带权重"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0