深度学习(VGG模型)

欢迎大家关注我的B站:

偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com)

本程序采用百度 paddlepaddle 深度学习框架,并在百度 AI Studio 平台上运行

目录

1 实验内容

2 原始模型 CNN

2.1 原始 CNN 网络结构

2.2 原始 CNN 的定义

2.3 原始 CNN 模型的测试结果

3 VGG 模型

3.1 VGG 的介绍

3.2 VGG 的网络结构

3.3 VGG-16 网络的定义

3.4 数据增强

3.5 训练曲线与测试结果

3.6模型评价

4 总结

5 完整源程序


1 实验内容

本次实验采用简单的 CNN 模型与 VGG 模型对海洋生物进行识别,下面是数据集的介绍

2 原始模型 CNN

2.1 原始 CNN 网络结构

2.2 原始 CNN 的定义

下面是对 CNN 模型的定义代码

#定义CNN网络
import paddle
import paddle.nn.functional as F

class CNN(paddle.nn.Layer):
    def __init__(self):
        super(CNN,self).__init__()
        self.conv1 = Conv2D(in_channels=3, out_channels=20, kernel_size=5,stride=1)        
        self.pool1 = MaxPool2D(kernel_size=2, stride=2)
        self.conv2 = Conv2D(in_channels=20, out_channels=50, kernel_size=5,stride=1)
        self.pool2 = MaxPool2D(kernel_size=2, stride=2)
        self.conv3 = Conv2D(in_channels=50, out_channels=50, kernel_size=5,stride=1)
        self.pool3 = MaxPool2D(kernel_size=2, stride=2)
        self.linear1 = Linear(in_features=200, out_features=23)
    
    def forward(self, input):
        x = self.conv1(input)
        x = F.relu(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = self.pool2(x)
        x = self.conv3(x)
        x = F.relu(x)
        x = self.pool3(x)

        x = paddle.flatten(x, start_axis=1,stop_axis=-1)  
        x = self.linear1(x)
        x=F.softmax(x)
        return x

2.3 原始 CNN 模型的测试结果

经过简单的调参,下面是性能较好的超参数设置

学习率损失函数优化函数batch_sizeepoch_num
0.001cross-entropyAdam1289

下面是训练过程

在设置时可以将 epoch 适当变大,观察训练曲线或准确率离散值变化,可以在准确率变化不大时中断训练,既可以节省时间,又可以防止产生过拟合。最终的测试结果如下

最终在测试集上的准确率达到了91.55%,可见模型出现了轻微的过拟合,于是调整 epoch=9,无论在之后进行怎样的调参,准确率均变化不大,因此说明此模型的表达能力已经到了极限,需要调整网络结构,于是我采用 VGG 模型进行测试。

3 VGG 模型

3.1 VGG 的介绍

Oxford Visual Geometry Group 就是 VGG 模型的缩写,VGG模型是2014年ILSVRC竞赛的第二名。但是VGG模型在多个迁移学习任务中的表现要优于googLeNet。而且从图像中提取CNN特征,VGG模型是首选算法。但它的缺陷在于参数过多,网络结构很臃肿,计算量大,训练费时。

3.2 VGG 的网络结构

3.3 VGG-16 网络的定义

下面是代码

class ConvPool(paddle.nn.Layer):
    '''卷积+池化'''
    def __init__(self,
                 in_channels,
                 out_channels,
                 filter_size,
                 pool_size,
                 pool_stride,
                 groups,
                 conv_stride=1,
                 conv_padding=1,
                 pool_type='max'
                 ):
        super(ConvPool, self).__init__()  

        self._conv2d_list = []

        for i in range(groups):
            conv2d = self.add_sublayer(   #返回一个由所有子层组成的列表。
                'bb_%d' % i,
                paddle.nn.Conv2D(in_channels=in_channels,out_channels=out_channels,kernel_size=filter_size,padding=conv_padding)
            )
            
            in_channels = out_channels
            self._conv2d_list.append(conv2d)

        if pool_type == 'avg':
            self._pool2d = paddle.nn.AvgPool2D(
                kernel_size=pool_size,           #池化核大小
                stride=pool_stride        #池化步长
            )
        elif pool_type == 'max':
            self._pool2d = paddle.nn.MaxPool2D(
                kernel_size=pool_size,           #池化核大小
                stride=pool_stride        #池化步长
            )

    def forward(self, inputs):
        x = inputs
        for conv in self._conv2d_list:
            x = conv(x)
            x = F.relu(x)
        x = self._pool2d(x)
        return x




class VGGNet(paddle.nn.Layer):
    '''
    VGG网络
    '''
    def __init__(self):
        super(VGGNet, self).__init__()
        
        self.convpool01 = ConvPool(
            3, 64, 3, 2, 2,2)  #3:通道数,64:卷积核个数,3:卷积核大小,2:池化核大小,2:池化步长,2:连续卷积个数
        self.convpool02 = ConvPool(
            64, 128, 3, 2, 2,2)
        self.convpool03 = ConvPool(
            128, 256, 3, 2, 2,3)
        self.convpool04 = ConvPool(
            256, 512, 3, 2, 2,3)
        self.convpool05 = ConvPool(
            512, 512, 3, 2, 2,3)
       
        
        self.pool_5_shape = 512 * 7* 7
        self.fc01 = paddle.nn.Linear(self.pool_5_shape,4096)
        self.fc02 = paddle.nn.Linear(4096,4096)
        self.fc03 = paddle.nn.Linear(4096,23)
        

    def forward(self, inputs):
        # print('input shape is {}'.format(inputs.shape)) #[8, 3, 224, 224]
        """前向计算"""
        out = self.convpool01(inputs)
        # print('after convpool01')
        # print(out.shape)           #[8, 64, 112, 112]
        out = self.convpool02(out)
        # print(out.shape)           #[8, 128, 56, 56]
        out = self.convpool03(out)
        # print(out.shape)           #[8, 256, 28, 28]
        out = self.convpool04(out)
        # print(out.shape)           #[8, 512, 14, 14]
        out = self.convpool05(out)
        # print(out.shape)           #[8, 512, 7, 7]         

        out = paddle.reshape(out, shape=[-1, 512*7*7])
        out = self.fc01(out)
        out = F.relu(out)
        out = self.fc02(out)
        out = F.relu(out)
        out = self.fc03(out)
        out = F.softmax(out)
        
        return out

3.4 数据增强

通过随机裁剪大小,变换方向,使得模型能够学到关于图像的更本质的特征,减少过拟合。代码如下

if mode == 'train':
            self.transforms = T.Compose([
                T.Resize((224,224)),    # 随机裁剪大小
                T.RandomHorizontalFlip(0.5),        # 随机水平翻转
                T.ToTensor(),                       # 数据的格式转换和标准化、 HWC => CHW 
                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 图像归一化
            ])
        else:
            self.transforms = T.Compose([
                T.Resize((224,224)),                 # 图像大小修改
                T.ToTensor(),                  # 数据的格式转换和标准化 HWC => CHW
                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])   # 图像归一化
            ])

3.5 训练曲线与测试结果

 最终测试集的准确率达到了97.15%,可见 VGG 模型泛化能力之强。

3.6模型评价

VGG 的优点:

(1)VGG采用了同样大小的卷积核(3*3)与池化尺寸(2*2),结构十分简洁

(2)VGG采用了理论上可以的最小卷积核,至少 3*3 的尺寸才能保证能够对周围一圈的环境进行感知,采用了最小的卷积核就能够减少增加网络层数,深层的神经网络性能更好

(3)VGG 所有卷积核的步长设为1,避免了信息丢失,通过 padding=1(填充1)能够使输入输出的特征图保持尺寸一致

(4)两个3*3的卷积核的感受野与一个5*5的卷积核的感受野一样,相同的感受野但是网络更深同时参数量更少,于是性能上由于Alexnet 模型

VGG的缺点:

(1)VGG 由于全连接层神经元多,因此参数量大,计算量大,训练耗时

(2)由于参数多,占用内存大

4 总结

首先我采用了较原始的 CNN 模型,由于对海洋生物的识别对人来说有点困难,也就意味着神经网络的结构也必须更复杂,网络结构的复杂度必须与图像识别任务的难度相匹配才能取得较好的效果,因此,采用VGG 模型比较合理,最后通过简单的调参获得了较高的准确率,但是复杂的网络模型又需要丰富、复杂 、庞大的数据集去支撑,于是数据增强显得非常重要。

5 完整源程序

完整 VGG-16 海洋生物识别的源程序如下,参数自行调整,数据集自行获取,路径自行修改

#导入必要的包
import zipfile
import os
import random
import paddle
import paddle.nn.functional as F
import matplotlib.pyplot as plt
from paddle.nn import MaxPool2D,Conv2D,BatchNorm
from paddle.nn import Linear
import sys
import numpy as np
from PIL import Image
from PIL import ImageEnhance
import paddle
from multiprocessing import cpu_count
import matplotlib.pyplot as plt
import json 

#解压原始数据集,将fish_image.zip解压至data目录下
src_path="/home/aistudio/data/data14492/fish_image23.zip"
target_path="/home/aistudio/data/fish_image"
if(not os.path.isdir(target_path)):
    z = zipfile.ZipFile(src_path, 'r')
    z.extractall(path=target_path)
    z.close()

#存放所有类别的信息
class_detail = []
#获取所有类别保存的文件夹名称
class_dirs = os.listdir(target_path+"/fish_image")

data_list_path="/home/aistudio/data/"

TRAIN_LIST_PATH=data_list_path + "train.txt"
EVAL_LIST_PATH=data_list_path + "eval.txt"

#每次执行代码,首先清空train.txt和eval.txt
with open(TRAIN_LIST_PATH, 'w') as f: 
    pass
with open(EVAL_LIST_PATH, 'w') as f: 
    pass

#总的图像数量
all_class_images = 0
#存放类别标签
class_label=0
# 设置要生成文件的路径
data_root_path="/home/aistudio/data/fish_image/fish_image"
#存储要写进test.txt和train.txt中的内容
trainer_list=[]
eval_list=[]
#读取每个类别,['fish_01', 'fish_02', 'fish_03']
for class_dir in class_dirs:   
    #每个类别的信息
    class_detail_list = {}
    eval_sum = 0
    trainer_sum = 0
    #统计每个类别有多少张图片
    class_sum = 0
    #获取类别路径 
    path = data_root_path + "/" + class_dir
    # 获取所有图片
    img_paths = os.listdir(path)
    for img_path in img_paths:                                  # 遍历文件夹下的每个图片
        name_path = path + '/' + img_path                       # 每张图片的路径
        if class_sum % 10 == 0:                                 # 每10张图片取一个做验证数据
            eval_sum += 1                                       # test_sum为测试数据的数目
            eval_list.append(name_path + "\t%d" % class_label + "\n")
        else:
            trainer_sum += 1 
            trainer_list.append(name_path + "\t%d" % class_label + "\n")#trainer_sum测试数据的数目
        class_sum += 1                                          #每类图片的数目
        all_class_images += 1                                   #所有类图片的数目
    class_label += 1  
    # 说明的json文件的class_detail数据
    class_detail_list['class_name'] = class_dir             #类别名称,如jiangwen
    class_detail_list['class_label'] = class_label          #类别标签
    class_detail_list['class_eval_images'] = eval_sum       #该类数据的测试集数目
    class_detail_list['class_trainer_images'] = trainer_sum #该类数据的训练集数目
    class_detail.append(class_detail_list)         
    
random.shuffle(eval_list)
with open(data_list_path + "eval.txt", 'a') as f:
    for eval_image in eval_list:
        f.write(eval_image) 
        
random.shuffle(trainer_list)
with open(data_list_path + "train.txt", 'a') as f2:
    for train_image in trainer_list:
        f2.write(train_image) 

# 说明的json文件信息
readjson = {}
readjson['all_class_name'] = data_root_path                  #文件父目录
readjson['all_class_sum'] = class_sum 
readjson['all_class_images'] = all_class_images
readjson['class_detail'] = class_detail
jsons = json.dumps(readjson, sort_keys=True, indent=4, separators=(',', ': '))
with open(data_list_path + "readme.json",'w') as f:
    f.write(jsons)
print ('生成数据列表完成!')

import paddle
import paddle.vision.transforms as T
import numpy as np
from PIL import Image


class FishDataset(paddle.io.Dataset):
    """
    23种鱼数据集类的定义
    """

    def __init__(self, mode='train'):
        """
        初始化函数
        """
        assert mode in ['train', 'eval'], 'mode is one of train, eval.'

        self.data = []

        with open('data/{}.txt'.format(mode)) as f:
            for line in f.readlines():
                info = line.strip().split('\t')

                if len(info) > 0:
                    self.data.append([info[0].strip(), info[1].strip()])

        if mode == 'train':
            self.transforms = T.Compose([
                T.Resize((224,224)),    # 随机裁剪大小
                T.RandomHorizontalFlip(0.5),        # 随机水平翻转
                T.ToTensor(),                       # 数据的格式转换和标准化、 HWC => CHW 
                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 图像归一化
            ])
        else:
            self.transforms = T.Compose([
                T.Resize((224,224)),                 # 图像大小修改
                T.ToTensor(),                  # 数据的格式转换和标准化 HWC => CHW
                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])   # 图像归一化
            ])
        
    def __getitem__(self, index):
        """
        根据索引获取单个样本
        """
        image_file, label = self.data[index]
        image = Image.open(image_file)

        if image.mode != 'RGB':
            image = image.convert('RGB')

        image = self.transforms(image)

        return image, np.array(label, dtype='int64')

    def __len__(self):
        """
        获取样本总数
        """
        return len(self.data)

train_dataset=FishDataset(mode='train')
eval_dataset=FishDataset(mode='eval')

class ConvPool(paddle.nn.Layer):
    '''卷积+池化'''
    def __init__(self,
                 in_channels,
                 out_channels,
                 filter_size,
                 pool_size,
                 pool_stride,
                 groups,
                 conv_stride=1,
                 conv_padding=1,
                 pool_type='max'
                 ):
        super(ConvPool, self).__init__()  

        self._conv2d_list = []

        for i in range(groups):
            conv2d = self.add_sublayer(   #返回一个由所有子层组成的列表。
                'bb_%d' % i,
                paddle.nn.Conv2D(in_channels=in_channels,out_channels=out_channels,kernel_size=filter_size,padding=conv_padding)
            )
            
            in_channels = out_channels
            self._conv2d_list.append(conv2d)

        if pool_type == 'avg':
            self._pool2d = paddle.nn.AvgPool2D(
                kernel_size=pool_size,           #池化核大小
                stride=pool_stride        #池化步长
            )
        elif pool_type == 'max':
            self._pool2d = paddle.nn.MaxPool2D(
                kernel_size=pool_size,           #池化核大小
                stride=pool_stride        #池化步长
            )

    def forward(self, inputs):
        x = inputs
        for conv in self._conv2d_list:
            x = conv(x)
            x = F.relu(x)
        x = self._pool2d(x)
        return x




class VGGNet(paddle.nn.Layer):
    '''
    VGG网络
    '''
    def __init__(self):
        super(VGGNet, self).__init__()
        
        self.convpool01 = ConvPool(
            3, 64, 3, 2, 2,2)  #3:通道数,64:卷积核个数,3:卷积核大小,2:池化核大小,2:池化步长,2:连续卷积个数
        self.convpool02 = ConvPool(
            64, 128, 3, 2, 2,2)
        self.convpool03 = ConvPool(
            128, 256, 3, 2, 2,3)
        self.convpool04 = ConvPool(
            256, 512, 3, 2, 2,3)
        self.convpool05 = ConvPool(
            512, 512, 3, 2, 2,3)
       
        
        self.pool_5_shape = 512 * 7* 7
        self.fc01 = paddle.nn.Linear(self.pool_5_shape,4096)
        self.fc02 = paddle.nn.Linear(4096,4096)
        self.fc03 = paddle.nn.Linear(4096,23)
        

    def forward(self, inputs):
        # print('input shape is {}'.format(inputs.shape)) #[8, 3, 224, 224]
        """前向计算"""
        out = self.convpool01(inputs)
        # print('after convpool01')
        # print(out.shape)           #[8, 64, 112, 112]
        out = self.convpool02(out)
        # print(out.shape)           #[8, 128, 56, 56]
        out = self.convpool03(out)
        # print(out.shape)           #[8, 256, 28, 28]
        out = self.convpool04(out)
        # print(out.shape)           #[8, 512, 14, 14]
        out = self.convpool05(out)
        # print(out.shape)           #[8, 512, 7, 7]         

        out = paddle.reshape(out, shape=[-1, 512*7*7])
        out = self.fc01(out)
        out = F.relu(out)
        out = self.fc02(out)
        out = F.relu(out)
        out = self.fc03(out)
        out = F.softmax(out)
        
        return out

import paddle
from paddle import Model

model= Model(VGGNet())

# 配置模型
model.prepare(paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()), 
              paddle.nn.CrossEntropyLoss(), 
              paddle.metric.Accuracy())

model.evaluate(eval_dataset, batch_size=64,verbose=1)

# 进行预测操作
predict_result = model.predict(eval_dataset)
import pandas as pd
data=pd.read_csv('./data/eval.txt',header=None,sep='\t')
# 定义画图方法
from PIL import Image

def show_img(img, predict):
    plt.figure()
    plt.title(predict)
    plt.imshow(img, cmap=plt.cm.binary)
    plt.show()

# 抽样展示
for i in range(10):
    img_path=data[0][i]
    real_class=data[1][i]
    predict_class= np.argmax(predict_result[0][i])
    img=Image.open(img_path)
    title=str(i) +'   '+ 'real_class: ' +str(real_class)+'  '+ 'predict_class: ' + str(predict_class)
    show_img(img, title)
model.save('inference_model', training=False)  # save for inference

  • 12
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无意2121

创作不易,多多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值