论文题目: 嗯- ImageNet Classification with Deep Convolutional Neural Networks -嗯
个人理解: 这边论文虽然已经过时了,里面的很多技术也都改进了。但是作为以后所有高级模型的改进,新手还是可以看一下的。
自我解读
摘要:
主要讲作者们训练了一个大型的深度卷积神经网络,将ImageNet LSVRC-2010竞赛中的120万张高分辨率图像分类为1000个不同的类别。测试一下数据上,我们实现了37.5% 和17.0% 的前1和前5错误率,这比以前的最新技术要好得多。神经网络具有6000万的参数和650,000的神经元,由五个卷积层组成,其中一些是max池层,以及三个具有最终1000 softmax的全连接层。为了使训练更快,我们使用了非饱和神经元和卷积操作的非常有效的GPU实现。为了减少全连接层中的过拟合,我们采用了最近开发的称为 “dropout” 的正则化方法,该方法被证明非常有效。
Top-1:预测的label中,概率向量最大的作为预测的结果,分类结果正确则正确,错误则错误
Top-5: 预测的label中,概率向量最大的前5个最为预测结果只有五个全部预测错误,则分类错误
对数据集的解释
ImageNet是属于大致22,000个类别的超过1500万个标记的高分辨率图像的数据集。大致有120万训练图像、50,000验证图像和150,000测试图像。因为之前跑过一个最近发表的论文代码,他的数据集是自己打标签的。但是作为AlexNet的标签则是以文件名命名,然后在文件里面存放大量的这种类别的图片。我自己训练的时候图片比较少,然后最后测试效果也不咋滴。
一个B站Up主对于数据集的理解:训练集可以看成“小测” 。 验证集可以看成:“模拟考” 。测试集可以看成:‘高考”
网络结构
一个GPU训练如下:
字有点丑,见谅:
减少过拟合的方法:
- 数据增强:从原始图片中生成转化后的图像,转换后的图像在CPU中的python代码直接生成,GPU此刻正在处理前一批图像
- Dropout激活: 减少神经元之间的联合依赖;随机掐死一片神经元。
代码解析:
MyModel.py
import torch from torch import nn class MyAlexNet(nn.Module): def __init__(self): super(MyAlexNet, self).__init__() self.model=nn.Sequential( nn.Conv2d(3,48,11,stride=4,padding=2), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(48,128,5,stride=1,padding=2), nn.ReLU(), nn.Conv2d(128,192,3,stride=1,padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(192,192,3,stride=1,padding=1), nn.ReLU(), nn.Conv2d(192,128,3,stride=1,padding=1), nn.ReLU(), nn.MaxPool2d(2) ) self.classfiler=nn.Sequential( nn.Linear(128*6*6,2048), nn.Dropout(p=0.5), nn.Linear(2048,2048), nn.Dropout(p=0.5), nn.Linear(2048,1000), nn.Dropout(p=0.5), nn.Linear(1000,10) ) def forward(self,x): x=self.model(x) x = torch.flatten(x, start_dim=1) #按照个数推平 x=self.classfiler(x) return x if __name__ == '__main__': input=torch.ones([32,3,224,224]) aiy=MyAlexNet() output=aiy(input) print(output.shape)
然后是训练集,train.py
import os import torchvision from matplotlib import pyplot as plt from torchvision.datasets import ImageFolder from torchvision import transforms from torch import nn from torch.utils.data import dataset, DataLoader from MyModel import * #画图中文的乱码解决问题 plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus']=False #数据集 train_root=r"data/train" test_root=r"data/val" #数据预处理 #1、归一化 normalize=transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5]) #三通道图像 train_transforms=transforms.Compose([ #将数据变换包装起来依次执行 transforms.Resize((224,224)), transforms.RandomHorizontalFlip(),#随机垂直翻转,相对于以后的打乱 transforms.ToTensor(), #归一化到【0-1】 normalize, #标准化到【-1,1】 ] ) test_transforms=transforms.Compose([ transforms.Resize((224,224)), transforms.ToTensor(), #归一化到【0-1】 normalize, #标准化到【-1,1】 ]) #默认数据集已经自觉按照要分配的类型分成了不同的文件夹,一种类型的文件夹下面只存放一种类型的图片 # train_data=ImageFolder('../data/train',transform=train_transforms) train_data=torchvision.datasets.CIFAR10(root='./dataset',train=True, transform=train_transforms,download=True) # test_data=ImageFolder("../data/val",transform=test_transforms) #测试 test_data=torchvision.datasets.CIFAR10(root='./dataset',train=False, transform=test_transforms,download=True) # print(test_data.classes) #根据分的文件夹的名字来确定的类别 train_dataloader=DataLoader(train_data,batch_size=32,shuffle=True) test_dataloader=DataLoader(test_data,batch_size=32,shuffle=False) device='cuda' if torch.cuda.is_available() else 'cpu' #引入模型 model=MyAlexNet().to(device) #创建损失函数 loss_fn=nn.CrossEntropyLoss().to(device) #定义优化器 optimizer=torch.optim.SGD(model.parameters(),lr=0.001) #params (iterable) – 待优化参数的iterable(w和b的迭代) 或者是定义了参数组的dict # lr (float) – 学习率 # momentum (float, 可选) – 动量因子(默认:0) #让学习率每10轮减半 # lr_scheduler=lr_scheduler.StepLR(optimizer,s) #定义训练次数 total_train_step=0 #定义测试次数 total_test_step=0 #定义一个画图函数 def matplot_loss(train_loss,val_loss): plt.plot(train_loss,label='train_loss') plt.plot(val_loss,label='val_loss') plt.legend(loc='best') plt.xlabel('loss') plt.ylabel('epoch') plt.title("训练集和验证集的loss值对比图") plt.show() #定义一个画图函数 def matplot_acc(train_acc,val_acc): plt.plot(train_acc,label='train_acc') plt.plot(val_acc,label='val_acc') plt.legend(loc='best') plt.xlabel('acc') plt.ylabel('epoch') plt.title("训练集和验证集的acc值对比图") plt.show() #训练的次数 epoch=10 loss_train=[] acc_train=[] loss_val=[] acc_val=[] min_vcc=0.0 for i in range(epoch): print("-----正在进行第{}轮训练------".format(i + 1)) model.train() total_train_loss=0 total_train_acc=0 m=0.0 for data in train_dataloader: images,targets=data images=images.to(device) targets=targets.to(device) # print(images.shape) output=model(images) loss=loss_fn(output,targets) total_train_loss=total_train_loss+loss.item() accuracy = (output.argmax(1) == targets).sum() / output.shape[0] # 1代表横向,0代码代表纵向 total_train_acc=total_train_acc+accuracy.item() optimizer.zero_grad() loss.backward() optimizer.step() #调整参数 total_train_step=total_train_step+1 if total_train_step % 100 ==0 : print("训练次数:{},loss={}".format(total_test_step,loss)) m=m+1 #模型转化为验证模式 model.eval() train_loss = total_train_loss / m train_acc = total_train_acc / m print("整体测试集上的loss:{}".format(train_loss)) print("整体测试集上的正确率:{}".format(train_acc)) loss_train.append(train_loss) acc_train.append(train_acc) total_test_loss=0 total_accuracy=0 ''' 在使用pytorch时,并不是所有的操作都需要进行计算图的生成(计算过程的构建,以便梯度反向传播等操作)。 而对于tensor的计算操作,默认是要进行计算图的构建的,在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建。 ''' #每次运行完之后进行一次测试,类似于参数已经更新完毕,来测试一下 with torch.no_grad(): n=0.0 for data in test_dataloader: images, targets = data images = images.to(device) targets = targets.to(device) output = model(images) loss=loss_fn(output,targets) total_test_loss=total_test_loss+loss.item() ''' 1.item()取出张量具体位置的元素元素值 2.并且返回的是该位置元素值的高精度值 3.保持原元素类型不变;必须指定位置 ''' accuracy=(output.argmax(1)==targets).sum()/output.shape[0] #1代表横向,0代码代表纵向 total_accuracy=total_accuracy+accuracy.item() n=n+1 val_loss=total_test_loss/n val_acc=total_accuracy/n print("整体测试集上的loss:{}".format(val_loss)) print("整体测试集上的正确率:{}".format(val_acc)) loss_val.append(val_loss) acc_val.append(val_acc) #保存最好的模型权重 if val_acc > min_vcc: folder='save_model' if not os.path.exists(folder): os.mkdir('save_model') min_vcc=val_acc print("save best model,在第{}轮".format(i)) torch.save(model.state_dict(),'save_model/best_model.pth') if i==epoch-1: print("保存最后一轮权重:") torch.save(model.state_dict(),'save_model/last_model.pth') matplot_loss(loss_train,loss_val) matplot_loss(acc_train,acc_val) print("目前没有problem")
最后是测试集,这个我读取每一张图片是用的OpenCV,后续会参考其他大佬的写法
import torch from PIL import Image from torch import nn from torchvision.transforms import transforms, ToPILImage from torch.autograd import Variable from MyAlexNet.MyModel import MyAlexNet import os import cv2 directory_name=r"F:/NetWork/AlexNet/data_test/" def read_directory(directory_name): for filename in os.listdir(directory_name): #os.listdir()方法用于返回指定文件夹包含的文件或文件夹的名字的列表 img = cv2.imread(directory_name + "/" + filename) trans = transforms.Compose([transforms.ToTensor(),transforms.Resize((224, 224))]) classes = ["ants", "bees"] # 自己是几种,这里就改成自己种类的字符数组 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print("using {} device.".format(device)) model=MyAlexNet().to(device) model.load_state_dict(torch.load("F:/NetWork/AlexNet/MyAlexNet/save_model/best_model.pth")) model.eval() for filename in os.listdir(directory_name): # os.listdir()方法用于返回指定文件夹包含的文件或文件夹的名字的列表 image = cv2.imread(directory_name + "/" + filename) # image = Image.open(img) # image = image.convert("RGB") image = trans(image) # show=ToPILImage() # show(image).show() image=Variable(torch.unsqueeze(image,dim=0).float()).to(device) image=torch.tensor(image).to(device) pred=model(image) print(pred) predicted=classes[torch.argmax(pred,dim=1)] print("prdeicted:{}".format(predicted))