【GoogLeNet】海洋生物识别
1. 项目准备
1.1. 问题导入
图像分类是根据图像的语义信息将不同类别图像区分开来,是计算机视觉中重要的基本问题。本实践使用卷积神经网络GoogLeNet模型构建深度学习模型,自动提取高质量的特征,来解决海洋鱼类识别的问题。
1.2. 数据集简介
本次实验使用的是台湾电力公司、台湾海洋研究所和垦丁国家公园在2010年10月1日至2013年9月30日期间,在中国台湾南湾海峡、兰屿岛和胡比湖的水下观景台收集的鱼类图像数据集。该数据集包括23类鱼种,共27370张鱼的图像,本次实验将取其中的90%作为训练集,剩下的10%作为测试集。
这是数据集的下载链接:Fish4Knowledge 23种鱼类数据集 - AI Studio
2. GoogLeNet模型
GoogLeNet模型是由Google团队在论文中提出的卷积神经网络,是2014年ILSVRC竞赛的冠军模型。相比于AlexNet模型,GoogLeNet模型的网络结构更深,共包括87层。尽管模型结构变得更复杂,但它的参数量仅为AlexNet模型参数量的1/10,这主要归功于它创新性地采用了Inception模块。如下图所示,Inception模块是一种多路并联结构,它能够提取并整合不同视野范围的特征,能极大地提升分类模型的性能。
GoogLeNet模型由多个模块串联而成,其网络结构如下图所示。以前的模型是将二维的卷积层输出的特征图直接拉成一维的全连接层输入,而在GoogLeNet模型中,作者在卷积层和全连接层之间插入一个全局平均池化层,直接生成一维的全连接层输入,这样做还能减少模型参数。需要注意的是,原始的GoogLeNet模型包含两个辅助分类器,由于辅助分类器在后续的深度卷积网络演化中并没有被再次使用,故我们去除了这两个辅助分类器。
3. 实验步骤
3.0. 前期准备
导入模块注意:本案例仅适用于PaddlePaddle 2.0+版本
import os import zipfile import random import numpy as np from PIL import Image import matplotlib.pyplot as plt import paddle from paddle import nn from paddle import metric as M from paddle.io import DataLoader, Dataset from paddle.nn import functional as F from paddle.optimizer import Adam from paddle.optimizer.lr import NaturalExpDecay 1234567891011121314 设置超参数
BATCH_SIZE = 64 # 每批次的样本数 EPOCHS = 5 # 训练轮数 LOG_GAP = 200 # 输出训练信息的间隔 INIT_LR = 3e-4 # 初始学习率 LR_DECAY = 0.5 # 学习率衰减率 SRC_PATH = "./data/data14492/fish_image23.zip" # 压缩包路径 DST_PATH = "./data" # 解压路径 DATA_PATH = DST_PATH + "/fish_image" # 实验数据集路径 INFER_LIST = [("./work/pm.jpg", "Pomacentrus moluccensis"), ("./work/ac.jpg", "Amphiprion clarkii")] # 预测数据 MODEL_PATH = "GoogLeNet.pdparams" # 模型参数保存路径 LAB_DICT = {'fish_1': 'Dascyllus reticulatus', 'fish_2': 'Plectroglyphidodon dickii', 'fish_3': 'Chromis chrysura', 'fish_4': 'Amphiprion clarkii', 'fish_5': 'Chaetodon lunulatus', 'fish_6': 'Chaetodon trifascialis', 'fish_7': 'Myripristis kuntee', 'fish_8': 'Acanthurus nigrofuscus', 'fish_9': 'Hemigymnus fasciatus', 'fish_10': 'Neoniphon sammara', 'fish_11': 'Abudefduf vaigiensis', 'fish_12': 'Canthigaster valentini', 'fish_13': 'Pomacentrus moluccensis', 'fish_14': 'Zebrasoma scopas', 'fish_15': 'Hemigymnus melapterus', 'fish_16': 'Lutjanus fulvus', 'fish_17': 'Scolopsis bilineata', 'fish_18': 'Scaridae', 'fish_19': 'Pempheris vanicolensis', 'fish_20': 'Zanclus cornutus', 'fish_21': 'Neoglyphidodon nigroris', 'fish_22': 'Balistapus undulatus', 'fish_23': 'Siganus fuscescens'} # 用于将文件名和标签相对应
12345678910111213141516171819202122232425263.1. 数据准备
解压数据集由于数据集中的数据是以压缩包的形式存放的,因此我们需要先解压数据压缩包。
if not os.path.isdir(DATA_PATH): z = zipfile.ZipFile(SRC_PATH, "r") # 打开压缩文件,创建zip对象 z.extractall(path=DST_PATH) # 解压zip文件至目标路径 z.close() print("数据集解压完成!") 12345 划分数据集
我们需要按1:9比例划分测试集和训练集,分别生成两个包含数据路径和标签映射关系的列表。
type_num, lab_dict = 0, {} # 方便动物类别在字符型和整型之间转换 train_list, test_list = [], [] # 存放数据的路径及标签的映射关系 file_folders = os.listdir(DATA_PATH) # 统计数据集下的文件夹 for folder in file_folders: lab_dict[str(type_num)] = LAB_DICT[folder] # 记录标签和数字代号的对应关系 imgs = os.listdir(os.path.join(DATA_PATH, folder)) for idx, img in enumerate(imgs): path = os.path.join(DATA_PATH, folder, img) if idx % 10 == 0: # 按照1:9的比例划分数据集 test_list.append([path, type_num]) else: train_list.append([path, type_num]) type_num += 1 1234567891011121314 数据预处理
我们需要对数据集图像进行缩放和归一化处理。
class MyDataset(Dataset): ''' 自定义的数据集类 ''' def __init__(self, label_list, transform): ''' * `label_list`: 标签与文件路径的映射列表 * `transform`:数据处理函数 ''' super(MyDataset, self).__init__() random.shuffle(label_list) # 打乱映射列表 self.label_list = label_list self.transform = transform def __getitem__(self, index): ''' 根据位序获取对应数据 ''' img_path, label = self.label_list[index] img = self.transform(img_path) return img, int(label) def __len__(self): ''' 获取数据集样本总数 ''' return len(self.label_list) def data_mapper(img_path, show=False): ''' 图像处理函数 ''' img = Image.open(img_path) if show: # 展示图像 display(img) # 将其缩放为224*224的高质量图像: img = img.resize((224, 224), Image.ANTIALIAS) # 把图像变成一个numpy数组以匹配数据馈送格式: img = np.array(img).astype("float32") # 将图像矩阵由“rgb,rgb,rbg...”转置为“rr...,gg...,bb...”: img = img.transpose((2, 0, 1)) # 将图像数据归一化,并转换成Tensor格式: img = paddle.to_tensor(img / 255.0) return img
1234567891011121314151617181920212223242526272829303132333435363738train_dataset = MyDataset(train_list, data_mapper) # 训练集 test_dataset = MyDataset(test_list, data_mapper) # 测试集 12 定义数据提供器
我们需要分别构建用于训练和测试的数据提供器,其中训练数据提供器是乱序、按批次提供数据的。
train_loader = DataLoader(train_dataset, # 训练数据集 batch_size=BATCH_SIZE, # 每批读取的样本数 num_workers=0, # 加载数据的子进程个数 shuffle=True, # 打乱训练数据集 drop_last=False) # 不丢弃不完整的样本 test_loader = DataLoader(test_dataset, # 测试数据集 batch_size=BATCH_SIZE, # 每批读取的样本数 num_workers=0, # 加载数据的子进程个数 shuffle=False, # 不打乱测试数据集 drop_last=False) # 不丢弃不完整的样本 1234567891011
3.2. 网络配置
GoogLeNet模型的网络参数设置如下表所示:
class ConvBN2d(nn.Layer): ''' Conv2D with BatchNorm2D ''' def __init__(self, in_channels: int, out_channels: int, kernel_size: int, stride=1, padding=0): ''' * `in_channels`: 输入通道数 * `out_channels`: 输出通道数 * `kernel_size`: 卷积核大小 * `stride`: 卷积运算的步长 * `padding`: 卷积填充的大小 ''' super(ConvBN2d, self).__init__() self.net = nn.Sequential( nn.Conv2D(in_channels, out_channels, kernel_size, stride, padding), nn.BatchNorm2D(out_channels) ) def forward(self, x): return self.net(x)
1234567891011121314151617181920class Inception(nn.Layer): ''' Inception v1 in GoogLeNet ''' def __init__(self, in_channels: int, c1: int, c2: tuple, c3: tuple, c4: int): ''' * `in_channels`: 输入通道数 * `c1`: 第1路卷积层的通道参数 * `c2`: 第2路卷积层的通道参数 * `c3`: 第3路卷积层的通道参数 * `c4`: 第4路卷积层的通道参数 ''' super(Inception, self).__init__() self.conv1 = nn.Sequential( ConvBN2d(in_channels, c1, 1, 1, 0), nn.ReLU() ) self.conv2 = nn.Sequential( ConvBN2d(in_channels, c2[0], 1, 1, 0), nn.ReLU(), ConvBN2d(c2[0], c2[1], 3, 1, 1), nn.ReLU() ) self.conv3 = nn.Sequential( ConvBN2d(in_channels, c3[0], 1, 1, 0), nn.ReLU(), ConvBN2d(c3[0], c3[1], 5, 1, 2), nn.ReLU() ) self.conv4 = nn.Sequential( nn.MaxPool2D(3, 1, 1), ConvBN2d(in_channels, c4, 1, 1, 0), nn.ReLU() ) def forward(self, x): y1 = self.conv1(x) y2 = self.conv2(x) y3 = self.conv3(x) y4 = self.conv4(x) y = paddle.concat([y1, y2, y3, y4], axis=1) # Depth Concat return y
12345678910111213141516171819202122232425262728293031323334353637383940414243class GoogLeNet(nn.Layer): def __init__(self, in_channels=3, n_classes=2): ''' * `in_channels`: 输入的通道数 * `n_classes`: 输出分类数量 ''' super(GoogLeNet, self).__init__() # Conv2D(输入通道数,输出通道数,卷积核大小,卷积步长,填充长度) # MaxPool2D(池化核大小,池化步长,填充长度) self.block1 = nn.Sequential( ConvBN2d(in_channels, 64, 7, 2, 3), # 64*112*112 nn.ReLU(), nn.MaxPool2D(3, 2, 1), # 64*56*56 ) self.block2 = nn.Sequential( ConvBN2d(64, 64, 1, 1, 0), # 64*56*56 nn.ReLU(), ConvBN2d(64, 192, 3, 1, 1), # 192*56*56 nn.ReLU(), nn.MaxPool2D(3, 2, 1), # 192*28*28 ) self.block3 = nn.Sequential( Inception(192, 64, (96, 128), (16, 32), 32), # 3a:256*28*28 Inception(256, 128, (128, 192), (32, 96), 64), # 3b:480*28*28 nn.MaxPool2D(3, 2, 1), # 480*14*14 ) self.block4 = nn.Sequential( Inception(480, 192, (96, 208), (16, 48), 64), # 4a:512*14*14 Inception(512, 160, (112, 224), (24, 64), 64), # 4b:512*14*14 Inception(512, 128, (128, 256), (24, 64), 64), # 4c:512*14*14 Inception(512, 112, (144, 288), (32, 64), 64), # 4d:528*14*14 Inception(528, 256, (160, 320), (32, 128), 128), # 4e:832*14*14 nn.MaxPool2D(3, 2, 1), # 832*7*7 ) self.block5 = nn.Sequential( Inception(832, 256, (160, 320), (32, 128), 128), # 5a:832*7*7 Inception(832, 384, (192, 384), (48, 128), 128), # 5b:1024*7*7 nn.AdaptiveAvgPool2D(1), # 1024*1*1 ) self.block6 = nn.Sequential( nn.Flatten(1, -1), # 1024 nn.Dropout(p=0.4), nn.Linear(1024, n_classes), # n_classes ) def forward(self, x): x = self.block1(x) x = self.block2(x) x = self.block3(x) x = self.block4(x) x = self.block5(x) y = self.block6(x) return y
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 实例化模型model = GoogLeNet(in_channels=3, n_classes=type_num) 1
3.3. 模型训练
model.train() # 开启训练模式 scheduler = NaturalExpDecay( learning_rate=INIT_LR, gamma=LR_DECAY ) # 定义学习率衰减器 optimizer = Adam( learning_rate=scheduler, parameters=model.parameters() ) # 定义Adam优化器 loss_arr, acc_arr = [], [] # 用于可视化 for ep in range(EPOCHS): for batch_id, data in enumerate(train_loader()): x_data, y_data = data y_data = y_data[:, np.newaxis] # 增加一维维度 y_pred = model(x_data) # 预测结果 acc = M.accuracy(y_pred, y_data) # 计算准确率 loss = F.cross_entropy(y_pred, y_data) # 计算交叉熵 if batch_id % LOG_GAP == 0: # 定期输出训练结果 print("Epoch:%d,Batch:%3d,Loss:%.5f,Acc:%.5f" % (ep, batch_id, loss, acc)) acc_arr.append(acc.item()) loss_arr.append(loss.item()) optimizer.clear_grad() loss.backward() optimizer.step() scheduler.step() # 每轮衰减一次学习率 paddle.save(model.state_dict(), MODEL_PATH) # 保存训练好的模型
1234567891011121314151617181920212223242526272829模型训练的结果如下:
Epoch:0,Batch: 0,Loss:3.50096,Acc:0.09375 Epoch:0,Batch:200,Loss:0.41797,Acc:0.90625 Epoch:1,Batch: 0,Loss:0.33822,Acc:0.89062 Epoch:1,Batch:200,Loss:0.04866,Acc:0.98438 Epoch:2,Batch: 0,Loss:0.04856,Acc:0.98438 Epoch:2,Batch:200,Loss:0.15580,Acc:0.96875 Epoch:3,Batch: 0,Loss:0.04368,Acc:0.98438 Epoch:3,Batch:200,Loss:0.03558,Acc:0.98438 Epoch:4,Batch: 0,Loss:0.00756,Acc:1.00000 Epoch:4,Batch:200,Loss:0.00995,Acc:1.00000 12345678910 可视化训练过程
fig = plt.figure(figsize=[10, 8]) # 训练误差图像: ax1 = fig.add_subplot(211, facecolor="#E8E8F8") ax1.set_ylabel("Loss", fontsize=18) plt.tick_params(labelsize=14) ax1.plot(range(len(loss_arr)), loss_arr, color="orangered") ax1.grid(linewidth=1.5, color="white") # 显示网格 # 训练准确率图像: ax2 = fig.add_subplot(212, facecolor="#E8E8F8") ax2.set_xlabel("Training Steps", fontsize=18) ax2.set_ylabel("Accuracy", fontsize=18) plt.tick_params(labelsize=14) ax2.plot(range(len(acc_arr)), acc_arr, color="dodgerblue") ax2.grid(linewidth=1.5, color="white") # 显示网格 fig.tight_layout() plt.show() plt.close()
12345678910111213141516171819203.4. 模型评估
model.eval() # 开启评估模式 test_costs, test_accs = [], [] for batch_id, data in enumerate(test_loader()): x_data, y_data = data y_data = y_data[:, np.newaxis] # 增加一维维度 y_pred = model(x_data) # 预测结果 acc = M.accuracy(y_pred, y_data) # 计算准确率 loss = F.cross_entropy(y_pred, y_data) # 计算交叉熵 test_accs.append(acc.item()) test_costs.append(loss.item()) test_loss = np.mean(test_costs) # 每轮测试的平均误差 test_acc = np.mean(test_accs) # 每轮测试的平均准确率 print("Eval t Loss:%.5f,Acc:%.5f" % (test_loss, test_acc)) 1234567891011121314
模型评估的结果如下:
Eval Loss:0.04160,Acc:0.99019 1
3.5. 模型预测
model.eval() # 开启评估模式 model.set_state_dict( paddle.load(MODEL_PATH) ) # 载入预训练模型参数 for idx, (img_path, truth_lab) in enumerate(INFER_LIST): image = data_mapper(img_path, show=True) # 获取预测图片 result = model(image[np.newaxis, :, :, :]) infer_lab = lab_dict[str(np.argmax(result))] # 获取推理结果 print("图%d的真实标签:%s,预测结果:%s" % (idx+1, truth_lab, infer_lab)) 12345678910
模型预测的结果如下:
图1的真实标签:Pomacentrus moluccensis,预测结果:Pomacentrus moluccensis 1
图2的真实标签:Amphiprion clarkii,预测结果:Amphiprion clarkii 1
写在最后
如果您发现项目存在问题,或者如果您有更好的建议,欢迎在下方评论区中留言讨论~这是本项目的链接:实验项目 - AI Studio,点击fork可直接在AI Studio运行~这是我的个人主页:个人主页 - AI Studio,来AI Studio互粉吧,等你哦~【友链滴滴】欢迎大家随时访问我的个人博客~相关知识
【GoogLeNet】海洋生物识别
深度学习卷积神经图像分类实现鸟类识别含训练代码和鸟类数据集(支持repVGG,googlenet, resnet, inception, mobilenet)
面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)
Pytorch实现鸟类识别(含训练代码和鸟类数据集)
112基于CNN的狗狗情感识别
海洋生物有哪些 海洋生物包括什么
Pytorch实现动物识别(含动物数据集和训练代码)
100基于卷积神经网络之鸟鸣识别鸟的种类
我的世界有哪些海洋生物 海洋生物图文介绍
搁浅的海洋生物怎么做
网址: 【GoogLeNet】海洋生物识别 https://www.mcbbbk.com/newsview683601.html
上一篇: 基于深度学习的水下鱼类检测与种类 |
下一篇: 深度学习—利用TensorFlo |
推荐分享

- 1我的狗老公李淑敏33——如何 5096
- 2南京宠物粮食薄荷饼宠物食品包 4363
- 3家养水獭多少钱一只正常 3825
- 4豆柴犬为什么不建议养?可爱的 3668
- 5自制狗狗辅食:棉花面纱犬的美 3615
- 6狗交配为什么会锁住?从狗狗生 3601
- 7广州哪里卖宠物猫狗的选择性多 3535
- 8湖南隆飞尔动物药业有限公司宠 3477
- 9黄金蟒的价格 3396
- 10益和 MATCHWELL 狗 3352