当前位置:   article > 正文

深度学习12—VGG19实现_vgg19代码

vgg19代码

目录

VGG19实现

1.为数据打标签的generate_txt.py

2.对图像进行预处理的data_process.py

3.VGG19的网络构建代码net_VGG19.py

4.训练得到pth模型参数文件的get_pth_file.py

5.预测代码predict.py

6.预测VGG16与VGG19结果对比


VGG19实现

1.为数据打标签的generate_txt.py

这里的程序设计思想还是可以学一下的。

  1. import os
  2. from os import getcwd # 文件夹操作
  3. # 写入数据集对应的文件夹
  4. classes = ['cat','dog']
  5. sets = ['train']
  6. # 主程序执行
  7. if __name__ == "__main__":
  8. wd = getcwd() # 获取当前工作目录
  9. # 说明:当前代码的目录关系是:sets-->subset(当前只有一个“train”)-->type_name(“train”下的关于类别的文件夹,比如“dog”)-->具体样本数据
  10. # 遍历sets中的每个文件夹,当前sets中只有"train"一个文件夹
  11. for subset in sets:
  12. list_file = open("cls_"+subset+".txt",'w')
  13. # 拿到每个子文件夹的目录
  14. path_subset = subset
  15. type_names = os.listdir(path_subset) # 拿到subset文件夹下的所有动物分类文件夹type_name,存到type_names列表中
  16. # 遍历subset中的每个文件夹type_name
  17. """
  18. 它遍历名为type_names的列表中的每个元素。
  19. 代码的目的是检查每个元素是否存在于名为classes的集合中。
  20. 如果存在,代码会继续执行下一次循环,处理下一个元素;
  21. 如果不存在,代码会跳过当前循环并继续执行下一次循环。
  22. """
  23. for type_name in type_names:
  24. if type_name not in classes:
  25. continue
  26. # 打标签
  27. type_id = classes.index(type_name) # 按type_name文件夹在classes文件夹中的索引,为type_name编号
  28. # 生成每个type_name文件夹的路径
  29. type_path = os.path.join(path_subset,type_name)
  30. photo_names = os.listdir(type_path) # 拿到type_name文件夹下的所有图片,组成一个列表phto_names
  31. # 处理每一张图片
  32. """
  33. 这段代码的作用是遍历名为photos_name的列表中的每个元素,
  34. 并根据文件名的扩展名来过滤文件。代码会判断文件的扩展名是否为.jpg、.png或.jpeg,
  35. 如果不是这些扩展名之一,则跳过当前文件的处理。对于符合条件的文件,
  36. 代码会将其写入到名为list_file的文件中,并写入文件的类别ID和路径信息。
  37. """
  38. for photo_name in photo_names:
  39. """
  40. 这一行代码使用os.path.splitext()函数将文件名photo_name分成文件名部分和扩展名部分,
  41. 并将扩展名赋值给变量postfix。下划线_表示不使用文件名部分,只关注扩展名。
  42. """
  43. _,postfit = os.path.splitext(photo_name) # #该函数用于分离文件名与拓展名
  44. # 如果拓展名不在如下的列表中,则跳过当前循环;如果在,则继续
  45. if postfit not in ['.jpg','.png','.jpeg']:
  46. continue
  47. # 将文件的类别ID和完整路径信息写入到名为list_file的文件中
  48. photo_path = os.path.join(type_path,photo_name)
  49. # print(wd) # C:\Users\ZARD\PycharmProjects\pythonProject\AAA_FX\revise_VGG19
  50. # 如上可知,wd为该项目的路径
  51. list_file.write(str(type_id)+';'+'%s/%s'%(wd,photo_path))
  52. list_file.write('\n') # 这一行代码写入一个换行符,将下一个文件的记录写入到新的一行
  53. list_file.close()

2.对图像进行预处理的data_process.py

对数据做一些基本操作,可根据实际需求进行更改。

  1. import cv2
  2. import numpy as np
  3. import torch.utils.data as data
  4. from PIL import Image
  5. def preprocess_input(x):
  6. x/=127.5
  7. x-=1.
  8. return x
  9. def cvtColor(image):
  10. if len(np.shape(image))==3 and np.shape(image)[-2]==3:
  11. return image
  12. else:
  13. image=image.convert('RGB')
  14. return image
  15. class DataGenerator(data.Dataset):
  16. def __init__(self,annotation_lines,inpt_shape,random=True):
  17. self.annotation_lines=annotation_lines
  18. self.input_shape=inpt_shape
  19. self.random=random
  20. def __len__(self):
  21. return len(self.annotation_lines)
  22. def __getitem__(self, index):
  23. annotation_path=self.annotation_lines[index].split(';')[1].split()[0]
  24. image=Image.open(annotation_path)
  25. image=self.get_random_data(image,self.input_shape,random=self.random)
  26. image=np.transpose(preprocess_input(np.array(image).astype(np.float32)),[2,0,1])
  27. y=int(self.annotation_lines[index].split(';')[0])
  28. return image,y
  29. def rand(self,a=0,b=1):
  30. return np.random.rand()*(b-a)+a
  31. def get_random_data(self,image,inpt_shape,jitter=.3,hue=.1,sat=1.5,val=1.5,random=True):
  32. image=cvtColor(image)
  33. iw,ih=image.size
  34. h,w=inpt_shape
  35. if not random:
  36. scale=min(w/iw,h/ih)
  37. nw=int(iw*scale)
  38. nh=int(ih*scale)
  39. dx=(w-nw)//2
  40. dy=(h-nh)//2
  41. image=image.resize((nw,nh),Image.BICUBIC)
  42. new_image=Image.new('RGB',(w,h),(128,128,128))
  43. new_image.paste(image,(dx,dy))
  44. image_data=np.array(new_image,np.float32)
  45. return image_data
  46. new_ar=w/h*self.rand(1-jitter,1+jitter)/self.rand(1-jitter,1+jitter)
  47. scale=self.rand(.75,1.25)
  48. if new_ar<1:
  49. nh=int(scale*h)
  50. nw=int(nh*new_ar)
  51. else:
  52. nw=int(scale*w)
  53. nh=int(nw/new_ar)
  54. image=image.resize((nw,nh),Image.BICUBIC)
  55. #将图像多余的部分加上灰条
  56. dx=int(self.rand(0,w-nw))
  57. dy=int(self.rand(0,h-nh))
  58. new_image=Image.new('RGB',(w,h),(128,128,128))
  59. new_image.paste(image,(dx,dy))
  60. image=new_image
  61. #翻转图像
  62. flip=self.rand()<.5
  63. if flip: image=image.transpose(Image.FLIP_LEFT_RIGHT)
  64. rotate=self.rand()<.5
  65. if rotate:
  66. angle=np.random.randint(-15,15)
  67. a,b=w/2,h/2
  68. M=cv2.getRotationMatrix2D((a,b),angle,1)
  69. image=cv2.warpAffine(np.array(image),M,(w,h),borderValue=[128,128,128])
  70. #色域扭曲
  71. hue=self.rand(-hue,hue)
  72. sat=self.rand(1,sat) if self.rand()<.5 else 1/self.rand(1,sat)
  73. val=self.rand(1,val) if self.rand()<.5 else 1/self.rand(1,val)
  74. x=cv2.cvtColor(np.array(image,np.float32)/255,cv2.COLOR_RGB2HSV)#颜色空间转换
  75. x[...,1]*=sat
  76. x[...,2]*=val
  77. x[x[:,:,0]>360,0]=360
  78. x[:,:,1:][x[:,:,1:]>1]=1
  79. x[x<0]=0
  80. image_data=cv2.cvtColor(x,cv2.COLOR_HSV2RGB)*255
  81. return image_data

3.VGG19的网络构建代码net_VGG19.py

其实该代码可以直接去torch的官网下,而且如果想改动VGG网络结构,只需微调一下,就可以实现其代码了。

在torch官网能够下载到一些预训练的模型:

https://pytorch.org/vision/stable/models/vgg.html

比如如下的VGG16:

逐条解析该程序:

导包和下载网络权重:

import torch
import torch.nn as nn
​
model_urls = {
    "vgg19":  "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth",
}#权重下载网址,该地址在torch官网上可下载

VGG网络的类:

class VGG(nn.Module):
    def __init__(self, features, num_classes = 1000, init_weights = True, dropout = 0.5):
        #继承
        super(VGG,self).__init__()
        self.features = features
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7)) # AdaptiveAvgPool2d使处于不同大小的图片也能进行分类
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),  # 完成4096的全连接
            nn.Linear(4096, num_classes), #对 num_classes的分类
        )
        if init_weights:
            for m in self.modules():
                if isinstance(m, nn.Conv2d):
                    nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
                    if m.bias is not None:
                        nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.BatchNorm2d):
                    nn.init.constant_(m.weight, 1)
                    nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.Linear):
                    nn.init.normal_(m.weight, 0, 0.01)
                    nn.init.constant_(m.bias, 0)
​
    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

网络结构设置:

def make_layers(cfg, batch_norm = False): # make_layers对输入的cfg进行循环
    layers = []
    in_channels = 3
    for v in cfg:
        if v == "M": # 对cfg进行输入循环,取第一个v
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)] # 把输入图像进行缩小
        else:
            #v = cast(int, v)
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    return nn.Sequential(*layers)
​
​
cfgs = {
    "VGG19": [64, 64, "M", 128, 128, "M", 256, 256, 256, 256, "M", 512, 512, 512, 512, "M", 512, 512, 512, 512, "M"],
}
# 这部分代码比较讲究,可参考B站视频,地址贴在下面了
def vgg19(pretrained=False, progress=True,num_classes=2):
    model = VGG(make_layers(cfgs["VGG19"]))
    if pretrained:
        from torch.hub import load_state_dict_from_url
        state_dict = load_state_dict_from_url(model_urls['vgg19'],model_dir='./model' ,progress=progress)#预训练模型地址
        model.load_state_dict(state_dict)
    if num_classes != 1000:
        model.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),  # 随机删除一部分不合格
            nn.Linear(4096, 4096),
            nn.ReLU(True),  # 防止过拟合
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes),
        )
    return model
if __name__ == '__main__':
    in_data = torch.ones(1, 3, 224, 224)
    net = vgg19(pretrained=False, progress=True, num_classes=2)
    out = net(in_data)
    print(out)

4.训练得到pth模型参数文件的get_pth_file.py

生成训练集和测试集:

'''数据集'''
annotation_path = 'cls_train.txt' # 读取数据集生成的文件
with open(annotation_path,'r') as f:
    lines = f.readlines() # 拿到所有图片数据的地址,lines的数据类型是一个列表,其中存下了所有图片地址
    #print(type(lines)) # <class 'list'>
    
import numpy as np
np.random.seed(10101) # 函数用于生成指定随机数
np.random.shuffle(lines) # 数据打乱
num_val = int(len(lines)*0.2) # 用做测试的数据数量
#print(num_val) #-->266
num_train = len(lines)-num_val # 训练的数据的数量
#输入图像大小
input_shape=[224,224]   #导入图像大小
# 生成数据
from AAA_FX.revise_VGG19.data_process import DataGenerator
​
train_data = DataGenerator(lines[:num_train],input_shape,True)
val_data = DataGenerator(lines[num_train:],input_shape,False)
​
val_len=len(val_data)
print(val_len)#返回测试集长度

加载数据:

# 取黑盒子工具
"""加载数据"""
from torch.utils.data import DataLoader#工具取黑盒子,用函数来提取数据集中的数据(小批次)

gen_train=DataLoader(train_data,batch_size=4)#训练集batch_size读取小样本,规定每次取多少样本
gen_test=DataLoader(val_data,batch_size=4)#测试集读取小样本

构建网络:

'''构建网络'''
from net_VGG19 import vgg19

device=torch.device('cuda'if torch.cuda.is_available() else "cpu")#电脑主机的选择
net=vgg19(True, progress=True,num_classes=2)#定于分类的类别
net.to(device)

选择优化器和学习率的调整方法:

'''选择优化器和学习率的调整方法'''
lr=0.0001#定义学习率
optim=torch.optim.Adam(net.parameters(),lr=lr)#导入网络和学习率
sculer=torch.optim.lr_scheduler.StepLR(optim,step_size=1)#步长为1的读取

训练:

'''训练'''
epochs=20#读取数据次数,每次读取顺序方式不同
for epoch in range(epochs):
    total_train=0 #定义总损失
    for data in gen_train:
        img,label=data
        with torch.no_grad():
            img =img.to(device)
            label=label.to(device)
        optim.zero_grad()
        output=net(img)
        train_loss=nn.CrossEntropyLoss()(output,label).to(device)
        train_loss.backward()#反向传播
        optim.step()#优化器更新
        total_train+=train_loss #损失相加
    sculer.step()
    total_test=0#总损失
    total_accuracy=0#总精度
    for data in gen_test:
        img,label =data #图片转数据
        with torch.no_grad():
            img=img.to(device)
            label=label.to(device)
            optim.zero_grad()#梯度清零
            out=net(img)#投入网络
            test_loss=nn.CrossEntropyLoss()(out,label).to(device)
            total_test+=test_loss#测试损失,无反向传播
            accuracy=((out.argmax(1)==label).sum()).clone().detach().cpu().numpy()#正确预测的总和比测试集的长度,即预测正确的精度
            total_accuracy+=accuracy
    print("训练集上的损失:{}".format(total_train))
    print("测试集上的损失:{}".format(total_test))
    print("测试集上的精度:{:.1%}".format(total_accuracy/val_len))#百分数精度,正确预测的总和比测试集的长度

    torch.save(net.state_dict(),"DogandCat{}.pth".format(epoch+1))
    print("模型已保存")

5.预测代码predict.py

导入图像:

from PIL import Image
test_pth='.\\train\cat\cat.6.jpg'#设置可以检测的图像
test=Image.open(test_pth)

处理图片:

'''处理图片'''
from torchvision import transforms

transform = transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor()])
image=transform(test)

加载网络:

'''加载网络'''
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")#CPU与GPU的选择
net =vgg19()#输入网络

model=torch.load("./DogandCat8.pth",map_location=device)#已训练完成的结果权重输入
net.load_state_dict(model)#模型导入
net.eval()#设置为推测模式
image=torch.reshape(image,(1,3,224,224))#四维图形,RGB三个通
with torch.no_grad():
    out=net(image)
out=F.softmax(out,dim=1)#softmax 函数确定范围
out=out.data.cpu().numpy()
print(out)
a=int(out.argmax(1))#输出最大值位置
plt.figure()
list=['Cat','Dog']
plt.suptitle("Classes:{}:{:.1%}".format(list[a],out[0,a]))#输出最大概率的道路类型
plt.imshow(test)
plt.show()

VGG16的道理是一样的,这里略。 

6.预测VGG16与VGG19结果对比

VGG19的预测结果:

VGG16的预测结果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/340856
推荐阅读
  

闽ICP备14008679号