感谢中科院,感谢东南大学,感谢南京医科大,感谢江苏省人民医院以的赞助

题记

只有被ImageNet真正殴打过一次才算是真的到了深度学习的坑边,下一步才是入坑。

引用装备所兰海大佬的一句话:
能借鉴别人经验的一定要借鉴别人的经验,不要人家已经过河了,你还假装在河里摸石头,别装了。

1. 下载Imagent-1k数据集

首先,ImageNet数据集大家都可以通过百度云下载。
可以通过我这便的链接下载。

链接:https://pan.baidu.com/s/1NlenXev0cN1l55ZSVQ-_nw
提取码:d6tn
我这个链接下载下来的是迅雷的磁力链接,下载下来后用迅雷下载会很快。没办法,百度云会员太贵了。

一般来讲,直接下载下来的train数据集是每个类都在一个文件夹中的,但是val数据集是所图像在一个数据集中的,你需要自己与预处理一下。
关于预处理数据集的方法可以查看我这个博客,我已经处理好了。

2. 大家ResNet模型

在这里,我把ResNet模型的ResNet18,ResNet34,ResNet50,ResNet101等都集成到一个代码中了。很方便调用。

直接上代码。

# -*- coding: utf-8 -*-
"""
Created on Wed Dec 22 10:31:58 2021@author: DELL
"""from __future__ import print_function, division
import torch.nn.functional as F
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
from torchvision import datasets, transforms
from torchvision import models
from torch.autograd import Variable
import torchsummary as summary
import os
import csv
import codecs
import numpy as np
import time
from thop import profileos.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
'''定义超参数'''
EPOCH = 150
batch_size=512
classes_num=1000
learning_rate=1e-3'''定义Transform'''#对训练集做一个变换
train_transforms = transforms.Compose([transforms.Resize(224),transforms.RandomResizedCrop(224),       #对图片尺寸做一个缩放切割transforms.RandomHorizontalFlip(),     #水平翻转transforms.ToTensor(),                 #转化为张量transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))    #进行归一化
])
#对测试集做变换
val_transforms = transforms.Compose([transforms.Resize(224),transforms.RandomResizedCrop(224),transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])train_dir = "D:/Dateset/Alldataset/Imagenet/ILSVRC2012_img_train"           #训练集路径
#train_dir = "D:/Dateset/Alldataset/mini-imagenet/train"
#train_dir = "D:/2021year/CVPR/PermuteNet-main/CNNonMNIST/data/trainNum_T/test"
#定义数据集
train_datasets = datasets.ImageFolder(train_dir, transform=train_transforms)
#加载数据集
train_dataloader = torch.utils.data.DataLoader(train_datasets, batch_size=batch_size, shuffle=True,num_workers=8,pin_memory=True)#,num_workers=16,pin_memory=False#val_dir = "D:/Dateset/Alldataset/mini-imagenet/val"
#val_dir = "D:/2021year/CVPR/PermuteNet-main/CNNonMNIST/data/trainNum_T/val"
val_dir = "D:/Dateset/Alldataset/Imagenet/ILSVRC2012_img_val1"
val_datasets = datasets.ImageFolder(val_dir, transform=val_transforms)
val_dataloader = torch.utils.data.DataLoader(val_datasets, batch_size=batch_size, shuffle=True,num_workers=8,pin_memory=True)#,num_workers=16,pin_memory=Trueclass BasicBlock(nn.Module):'''这个函数适用于构成ResNet18和ResNet34的block'''expansion = 1def __init__(self, in_planes, planes, stride=1):super(BasicBlock, self).__init__()self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(planes)self.conv2 = nn.Conv2d(planes, planes, kernel_size=3,stride=1, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(planes)self.shortcut = nn.Sequential()if stride != 1 or in_planes != self.expansion*planes:self.shortcut = nn.Sequential(nn.Conv2d(in_planes, self.expansion*planes,kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(self.expansion*planes))def forward(self, x):out = torch.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))out += self.shortcut(x)out = torch.relu(out)return outclass Bottleneck(nn.Module):'''这个函数适用于构成ResNet50及更深层模型的的block'''expansion = 4def __init__(self, in_planes, planes, stride=1):super(Bottleneck, self).__init__()self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False)self.bn1 = nn.BatchNorm2d(planes)self.conv2 = nn.Conv2d(planes, planes, kernel_size=3,stride=stride, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(planes)self.conv3 = nn.Conv2d(planes, self.expansion *planes, kernel_size=1, bias=False)self.bn3 = nn.BatchNorm2d(self.expansion*planes)self.shortcut = nn.Sequential()if stride != 1 or in_planes != self.expansion*planes:self.shortcut = nn.Sequential(nn.Conv2d(in_planes, self.expansion*planes,kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(self.expansion*planes))def forward(self, x):out = torch.relu(self.bn1(self.conv1(x)))out = torch.relu(self.bn2(self.conv2(out)))out = self.bn3(self.conv3(out))out += self.shortcut(x)out = torch.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes=classes_num):super(ResNet, self).__init__()self.in_planes = 64self.conv1 = nn.Conv2d(3, 64, kernel_size=3,stride=2, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=False)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)self.avgpool = nn.AvgPool2d(7, stride=1)self.classifier = nn.Linear(512*block.expansion, num_classes)#def _make_layer(self, block, planes, num_blocks, stride):strides = [stride] + [1]*(num_blocks-1)layers = []for stride in strides:layers.append(block(self.in_planes, planes, stride))self.in_planes = planes * block.expansionreturn nn.Sequential(*layers)def forward(self, x):out = torch.relu(self.bn1(self.conv1(x)))out_x1 = self.relu(out)out_x2 = self.maxpool(out_x1)out1 = self.layer1(out_x2)    #56*56      4out2 = self.layer2(out1)    #28*28        4out3 = self.layer3(out2)    #14*14        4out4 = self.layer4(out3)   #(512*7*7)     4#out5 = F.avg_pool2d(out4, 4)#平均池化out5 = self.avgpool(out4 )out6 = out5.view(out5.size(0),-1)#view()函数相当于numpy中的reshapeout7 = self.classifier(out6)#平均池化全连接分类return out7def ResNet18():return ResNet(BasicBlock, [2, 2, 2, 2])
def ResNet34():return ResNet(BasicBlock, [3, 4, 6, 3])
def ResNet50():return ResNet(Bottleneck, [3, 4, 6, 3])
def ResNet101():return ResNet(Bottleneck, [3, 4, 23, 3])
def ResNet152():return ResNet(Bottleneck, [3, 8, 36, 3])
#--------------------训练过程---------------------------------
model = ResNet18()#在这里更换你需要训练的模型
summary.summary(model, input_size=(3,224,224),device="cpu")#我们选择图形的出入尺寸为(3,224,224)params = [{'params': md.parameters()} for md in model.children()if md in [model.classifier]]
#optimizer = optim.Adam(model.parameters(), lr=learning_rate, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
optimizer = optim.SGD(model.parameters(), lr=0.01,momentum=0.9, weight_decay=1e-4)
StepLR    = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)#按训练批次调整学习率,每30个epoch调整一次
loss_func = nn.CrossEntropyLoss()
#存储测试loss和acc
Loss_list = []
Accuracy_list = []
#存储训练loss和acc
train_Loss_list = []
train_Accuracy_list = []
#这俩作用是为了提前开辟一个
loss = []
loss1 = []
def train_res(model,train_dataloader,epoch):model.train()#print('epoch {}'.format(epoch + 1))# training-----------------------------train_loss = 0.train_acc = 0.for batch_x, batch_y in train_dataloader:batch_x  = Variable(batch_x).cuda()batch_y  = Variable(batch_y).cuda()optimizer.zero_grad()out = model(batch_x)loss1 = loss_func(out, batch_y)train_loss += loss1.item()pred = torch.max(out, 1)[1]train_correct = (pred == batch_y).sum()train_acc += train_correct.item()loss1.backward()optimizer.step()print('Train Loss: {:.6f}, Acc: {:.6f}'.format(train_loss / (len(train_datasets)), train_acc / (len(train_datasets))))#输出训练时的loss和acctrain_Loss_list.append(train_loss / (len(val_datasets)))train_Accuracy_list.append(100 * train_acc / (len(val_datasets)))# evaluation--------------------------------
def val(model,val_dataloader):model.eval()eval_loss= 0.eval_acc = 0.for batch_x, batch_y in val_dataloader:batch_x = Variable(batch_x, volatile=True).cuda()batch_y = Variable(batch_y, volatile=True).cuda()out = model(batch_x)loss = loss_func(out, batch_y)eval_loss += loss.item()pred = torch.max(out, 1)[1]num_correct = (pred == batch_y).sum()eval_acc += num_correct.item()print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(val_datasets)), eval_acc / (len(val_datasets))))#输出测试时的loss和accLoss_list.append(eval_loss / (len(val_datasets)))Accuracy_list.append(100 * eval_acc / (len(val_datasets)))# 保存模型的参数
#torch.save(model.state_dict(), 'ResNet18.pth')
#state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'epoch': epoch}
#torch.save(state, 'ResNet18.pth')log_dir = 'D:/2021year/CVPR/PermuteNet-main/Keras_position/Pytorch_Code/resnet18.pth'
def main():if torch.cuda.is_available():model.cuda()test_flag = False# 如果test_flag=True,则加载已保存的模型if test_flag:# 加载保存的模型直接进行测试机验证,不进行此模块以后的步骤checkpoint = torch.load(log_dir)model.load_state_dict(checkpoint['model'])optimizer.load_state_dict(checkpoint['optimizer'])start_epoch = checkpoint['epoch']val(model, val_dataloader)#如果只评估模型,则保留这个return,如果是从某个阶段开始继续训练模型,则去掉这个模型#同时把上面的False改成Truereturn# 如果有保存的模型,则加载模型,并在其基础上继续训练if os.path.exists(log_dir) and test_flag:checkpoint = torch.load(log_dir)model.load_state_dict(checkpoint['model'])optimizer.load_state_dict(checkpoint['optimizer'])start_epoch = checkpoint['epoch']print('加载 epoch {} 成功!'.format(start_epoch))else:start_epoch = 1print('无保存模型,将从头开始训练!')for epoch in range(start_epoch, EPOCH):since = time.time()print('epoch {}'.format(epoch))#显示每次训练次数train_res(model, train_dataloader, epoch)time_elapsed = time.time() - sinceprint('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))  # 输出训练和测试的时间#通过一个if语句判断,让模型每十次评估一次模型并且保存一次模型参数epoch_num = epoch/10epoch_numcl = [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0]print('epoch_num',epoch_num)if epoch_num in epoch_numcl:print('评估模型')val(model, val_dataloader)print('保存模型')state = {'model':model.state_dict(), 'optimizer':optimizer.state_dict(), 'epoch':epoch}torch.save(state, log_dir)y1 = Accuracy_listy2 = Loss_listy3 = train_Accuracy_listy4 = train_Loss_listx1 = range(len(Accuracy_list))x2 = range(len(Loss_list))x3 = range(len(train_Accuracy_list))x4 = range(len(train_Loss_list))plt.subplot(2, 1, 1)plt.plot(x1, y1,'-')plt.title('Test accuracy vs. epoches')plt.ylabel('Test accuracy')plt.subplot(2, 1, 2)plt.plot(x2, y2,'-')plt.xlabel('Test loss vs. epoches')plt.ylabel('Test loss')plt.show()plt.subplot(2, 1, 1)plt.plot(x3, y3,'-')plt.title('Train accuracy vs. epoches')plt.ylabel('Train accuracy')plt.subplot(2, 1, 2)plt.plot(x4, y4,'-')plt.xlabel('Train loss vs. epoches')plt.ylabel('Train loss')plt.show()if __name__ == '__main__':main()

3. 关于训练一开始的问题

由于一开始跑的都是很小的数据集,很多兄弟习惯了上来就看GPU的利用率。
这个时候,由于数据集数量比较少,很快啊,数据集就全被加载器加载完成了,然后开始训练,正常情况下如果有GPU的同学,都会看到一个让你满意的结果,至少利用率大于30%,或者一直在跳动,大家都可以可以接受,毕竟小数据集也不在乎这点时间了。

但是如果是大型数据集,大家就要争分夺秒了,甚至利用率为99%都觉得这块显卡是在偷懒。于是乎各种多线程操作就开始了。

在win10下面看GPU利用率的方法有两种,第一种是Ctrl+Alt+. 但是任务管理器这种方法显示的GPU利用率不准。

因此推荐在cmd终端输入
最后一个数字控制刷新时间1表示1秒刷新一次。

nvidia-smi -l 1


从上面两个图看出,这个时候的磁盘利用率直接拉满,cpu和GPU的利用率基本为0.。GPU的显存一开始也是很低,后来才慢慢拉满。

面对这种显存拉满,GPU和CPU利用率为0的情况,很多人肯定会着急,为什么会这样。然后上网开始像模像样的根据各类大神的调参经验开始调参,然后就这样一会启动,一会运行的,就是一点效果都没有。于是乎开始怀疑是不是自己设备不行。其实不是。。。。。

其实,这时候是在读取那128G的数据集,大概需要多久,我们也不清楚。但是,荡第一个批次之后,pytorch的下载器把数据都加载好之后,从第二个epoch开始,利用率基本不开始一直大于97%。
具体可以参考这个大佬的实验证据

其实,避免这个问题的最简单的方案就是别使用机械硬盘,直接使用固态硬盘,这样数据读取速度就直接上来了,基本上是一起动就训练了。我现在用的就是全固态系统。搞深度学习就是烧钱的,特别是imagenet,建议大家不要轻易招惹它,没啥意义。特别是没有经验也没有设备的小硕。

关于GPU利用率低的一些问题,可以参考这个大佬的无私奉献

4. 关于学习率,初始学习率,epoch,bachsize之间的关系。

4.1 如果你使用每个30批次学习率下降十倍的方法这种操作

'''
例:lr = 0.05, step_size=30, gamma=0.1
这个gamma=0.1表示学习率每次下降10倍,step_size=30表示每30个epoch变化一次。
在我的例子中就是使用这种操作的,基本上大家都是这样的。每30个批次学习率下降十倍
lr = 0.05     if epoch < 30
lr = 0.005    if 30 <= epoch < 60
lr = 0.0005   if 60 <= epoch < 90
'''

4.2 bachsize的设置与Datalodar

在合理范围内,增大 Batch_Size 有何好处?
内存利用率提高了,大矩阵乘法的并行化效率提高
跑完一次epoch(全数据集)所需要的迭代次数减小,对于相同数据量的处理速度进一步加快
在一定范围内,一般来说batch size越大,其确定的下降方向越准,引起的训练震荡越小

盲目增大batch size 有什么坏处
内存利用率提高了,但是内存容量可能撑不住了
跑完一次epoch(全数据集)所需要的迭代次数减少,但是想要达到相同的精度,其所花费的时间大大增加了,从而对参数的修正也就显得更加缓慢
batch size 大到一定的程度,其确定的下降方向已经基本不再变化

首先,batch-size和num_workers。一般情况下,为了榨干GPU的每一滴精血,让GPU利用率达到100.我们都会在DataLoader中设置num_workers,一般来讲设置为8和16

train_dataloader = torch.utils.data.DataLoader(train_datasets, batch_size=batch_size,shuffle=True,num_workers=8,pin_memory=True)

设置这两个参数可以有效提高训练速度,但是很容易出现OOM(out of memory)的现象。因此这便还需要控制。
这里,继续要考虑到显存,也需要考虑到内存条的内存。

num_worker大,下一轮迭代的batch可能在上一轮/上上一轮…迭代时已经加载好了。坏处是内存(显卡内存?)开销大(开了pin memory?) ,也加重了CPU负担。显存=显卡内存(内存单词是memory),作用是用来存储显卡芯片处理过或者即将提取的渲染数据。

如同计算机的内存一样,显存是用来存储要处理的图形信息的部件。(显存和GPU的关系有点类似于内存和CPU的关系)

CPU不能直接调用存储在硬盘上的系统、程序和数据,必须首先将硬盘的有关内容存储在内存中,这样才能被CPU读取运行。因而,内存(即物理 内存,是相对于硬盘这个“外存”而言)作为硬盘和CPU的“中转站”,对电脑运行速度有较大影响。

https://zhuanlan.zhihu.com/p/31558973

pin memory: 开了就在GPU上面预留一片固定的内存区域,以加速传输。

如果开了pin memory: 增大num_workers的数量,内存占用也会增加。因为每个worker都需要缓存一个batch的数据。此时,batch size和num_workers的增大不可兼得,否则cuda out of memory。
开pin memory不觉得能快多少,反倒是牺牲了batch size。我是16G显存24G运存,训练Imagenet时batch size= 512和num_workers = 8.这样没啥问题

后续更新…

Windows下,Pytorch使用Imagenet-1K训练ResNet的经验(有代码)相关推荐

  1. Windows下基于python3使用word2vec训练中文维基百科语料资料汇总

    Windows下基于python3使用word2vec训练中文维基百科语料(一):https://www.cnblogs.com/gaofighting/p/9055674.html Windows下 ...

  2. Windows下使用VS Code编译和构建LoRaWan开源节点代码

    Windows下使用VS Code编译和构建LoRaWan开源节点代码 1.下载LoRaWan节点端开源代码 2.构建LoRaMac-node的先决条件 2.1添加环境变量 3.使用VS Code构建 ...

  3. C++在windows下获取本地主机ipv4地址和ipv6地址的代码

    把内容过程中经常用的内容段记录起来,下面内容段是关于C++在windows下获取本地主机ipv4地址和ipv6地址的内容,应该是对码农有所用处. #include <Winsock2.h> ...

  4. Windows下搭建ARM11裸机开发环境(3):Eclipse+JLink调试代码

    By: Ailson Jack Date: 2019.05.17 个人博客:http://www.only2fire.com/ 本文在我博客的地址是:http://www.only2fire.com/ ...

  5. Windows下基于python3使用word2vec训练中文维基百科语料(一)

    在进行自然语言处理之前,首先需要一个语料,这里选择维基百科中文语料,由于维基百科是 .xml.bz2文件,所以要将其转换成.txt文件,下面就是相关步骤: 步骤一:下载维基百科中文语料 https:/ ...

  6. Yolov5 windows下的环境搭建及训练

    说明:此环境可以和Yolov5-3.0.4.0.5.0.6.0.YoloX等网络共享. 首先下载博主为大家提供的yolov5-4.0的安装包(之所以选择4.0是为了和接下来的win10下的C++推理做 ...

  7. Windows下基于python3使用word2vec训练中文维基百科语料(三)

    对前两篇获取到的词向量模型进行使用: 代码如下: 1 import gensim 2 model = gensim.models.Word2Vec.load('wiki.zh.text.model') ...

  8. windows下,linux下c++生成文件夹

    windows下方法 方法1:使用system()函数调用 mkdir 命令 代码如下 #include <string> using namespace std;int main() { ...

  9. 乐视三合一体感摄像头--windows下的开发2

    乐视三合一体感摄像头--windows下的开发 Introduction 环境配置过程 参考资料 后言 Introduction 解决上篇教程乐视三合一体感摄像头–基本信息及windows下部分开发: ...

最新文章

  1. css3-transform
  2. R语言绘制空白图实战
  3. 多线程题目 2019.06.02 晚
  4. 利用Keras构建自动编码器
  5. java.lang.NoClassDefFoundError: org/springframework/dao/support/DaoSupport
  6. 雷讯和pix_青海叶陇沟金矿地质地球化学特征及找矿方向
  7. Android Relativelayout
  8. 中南大学在线考试答案计算机基础,中南大学《计算机基础》在线考试题库(267题)(有答案).doc...
  9. [Oracle]Oracle表权限小结
  10. 迁移学习全面指南:概念、应用、优势、挑战
  11. python学习之-- redis模块基本介绍
  12. Spring源码的学习方法和知识地图
  13. win8.1安装马上6,连不上
  14. Ubuntu远程连接,远程桌面
  15. php中lpush(),lPush 命令/方法/函数
  16. matlab角度和弧度的互换_MATLAB弧度与角度转换 -
  17. Anaconda Clean命令
  18. 网页视频的下载方法,电脑如何下载网页视频
  19. Echarts Map地图类型使用
  20. 工作15年码农总结:学编程难吗?那只是你觉得难!

热门文章

  1. OpenBCI_GUI部件指南
  2. 四种情况会造成内存的泄漏:
  3. 米拓1元体验建站正式上线
  4. kettle开发篇-缓存机制-Day7
  5. python字典排序并输出_对Python的字典进行排序
  6. 怎样快速掌握WORD的常用功能,看这里就够了,常见的一些word使用小技巧
  7. 什么是TCP三次握手和四次挥手【重点学习系列---干货十足--一文详解】
  8. 考研成长日记day5
  9. 单神经元自适应PID控制SCL程序
  10. Glastopf蜜罐月报分析