基于Alex Net的动物识别算法

介绍

当今社会,图像识别技术已经成为了人工智能领域中备受关注的研究方向之一。在这个领域中,动物识别作为一个重要的应用场景,引起了广泛的关注和研究。动物识别技术不仅能够帮助我们更好地了解动物的生态习性、行为特征,还能够在野生动物保护、农业生产、动物监测等领域发挥重要作用
而在动物识别技术中,图像分类是一个关键的环节。通过对动物图像进行分类,我们可以将动物按照种类进行区分,从而实现对不同动物的识别和分类。为了实现高效准确的动物图像分类,人们提出了各种各样的图像分类算法和模型。

在这篇博客中,我们将重点介绍基于深度学习模型的动物图像分类方法,并着重讨论使用 AlexNet 模型进行动物图像分类的背景和意义。AlexNet 是由 Alex Krizhevsky 等人在 ImageNet 大规模视觉识别挑战赛(ILSVRC)中提出的一种深度卷积神经网络模型,它以较大的卷积核和更深的网络结构为特点,在图像分类任务上取得了非常出色的表现。因此,使用 AlexNet 模型进行动物图像分类不仅能够实现高效准确的分类效果,还能够借鉴和应用深度学习在图像识别领域取得的先进成果,推动动物识别技术的发展和应用。

AlexNet 简介

论文原文AlexNet

论文简要介绍

这篇论文的全名是《ImageNet Classification with Deep Convolutional Neural Networks》,由Alex Krizhevsky, Ilya Sutskever, 和 Geoffrey E. Hinton 三位作者撰写,他们均来自多伦多大学。论文首次发表于2012年的NIPS会议。

这篇论文主要介绍了一种大型深度卷积神经网络,用于对ImageNet LSVRC-2010比赛中的1.2百万高分辨率图像进行分类,这些图像涵盖了1000个不同的类别。通过使用非饱和神经元和高效的GPU卷积运算实现,他们的模型在测试数据上达到了前所未有的低错误率,明显优于当时的最佳状态。具体来说,模型在top-1和top-5错误率上分别达到了37.5%和17.0%。

这项工作的主要贡献

  • 训练了当时最大的卷积神经网络之一,并在ILSVRC-2010和ILSVRC-2012竞赛数据集上取得了突破性的结果。
  • 实现了高度优化的GPU卷积运算。
  • 网络采用了多个新颖的特性来提高性能并缩短训练时间,比如使用ReLU非线性激活函数、多GPU训练、局部响应归一化和重叠池化等。
  • 使用了dropout方法来减少全连接层的过拟合。

AlexNet网络架构

在这里插入图片描述
这就是CNN架构示意图,明确显示了两个gpu之间的职责划分。一个GPU运行图顶部的层部件,而另一个运行图底部的层部件。

下面描述一下AlexNet的网络的架构图的概念

神经网络架构

  • 输入层: 224 x 224 x 3 图像
  • 第1卷积层: 96个11x11的卷积核, 步长4, ReLU激活
  • 最大池化层: 3x3, 步长2
  • 局部响应归一化
  • 第2卷积层: 256个5x5的卷积核, ReLU激活
  • 最大池化层: 3x3, 步长2
  • 局部响应归一化
  • 第3卷积层: 384个3x3的卷积核, ReLU激活
  • 第4卷积层: 384个3x3的卷积核, ReLU激活
  • 第5卷积层: 256个3x3的卷积核, ReLU激活
  • 最大池化层: 3x3, 步长2
  • 第1全连接层: 4096个神经元, ReLU激活
    • Dropout
  • 第2全连接层: 4096个神经元, ReLU激活
    • Dropout
  • 第3全连接层: 1000个神经元, Softmax输出
  • 输出层: 1000类预测

数据集介绍

Oxford-IIIT Pets 是一个宠物图像数据集。 该数据集涉及37 个类别(其中犬类25 类,猫类12 类),每个类别大约有200 张图片。 这些图像在比例、姿势和光照条件方面有很大的差异。 所有图像都有一个相应的ground truth 标注,包括品种、头部ROI 和像素级的trimap 分割。

下面将一步步教你如何实现基于Alex Net的动物识别。

数据的预处理

这个数据集下载下来,打开后是这样的:
在这里插入图片描述
所以我们需要对图片进行分类,不难发现同一类别下划线前面的字符都是一样的,我们可以据此为突破口写个脚本对图片进行分类。

#整理数据集,对图片进行分类
import os
import shutil

def organize_images_by_prefix(source_dir):
    """
    遍历指定的文件夹,根据图片文件名中"_"前的字符分类,将同类图片放入以该字符命名的文件夹中。
    
    参数:
    source_dir (str): 图片存放的源目录路径。
    """
    # 确保源目录存在
    if not os.path.exists(source_dir):
        print("指定的目录不存在,请检查路径。")
        return

    # 遍历源目录中的所有文件
    for filename in os.listdir(source_dir):
        if filename.endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
            # 获取文件名中"_"前的部分
            prefix = filename.split('_')[0]
            # 创建目标文件夹路径
            target_dir = os.path.join(source_dir, prefix)
            # 如果目标文件夹不存在,则创建
            if not os.path.exists(target_dir):
                os.makedirs(target_dir)
            # 构建源文件的完整路径
            source_file = os.path.join(source_dir, filename)
            # 构建目标文件的完整路径
            target_file = os.path.join(target_dir, filename)
            # 移动文件
            shutil.move(source_file, target_file)
            print(f"已移动 {filename} 到 {target_dir}")

# 指定你的源目录路径
source_directory = './datasets/images/'
organize_images_by_prefix(source_directory)

分好了就这样:在这里插入图片描述
但发现少了两类,经查看,是因为像这种被分为一类了。在这里插入图片描述
需要重新改一下脚本,但是我觉得太麻烦,少两类就少两类吧。大类是正确的就好。

按指定的比例分配图像到训练集、验证集和测试集中

#按指定的比例分配图像到训练集、验证集和测试集中
import os
import shutil
import numpy as np

def split_data(source_dir, train_dir, val_dir, test_dir, train_size=0.8, val_size=0.2):
    """
    从每个类别的源文件夹中抽取图像并按比例分配到训练集、验证集和测试集中。
    
    参数:
    source_dir (str): 包含所有类别子文件夹的根目录。
    train_dir (str): 训练集的目标根目录。
    val_dir (str): 验证集的目标根目录。
    test_dir (str): 测试集的目标根目录。
    train_size (float): 分配到训练集的比例。
    val_size (float): 分配到验证集的比例。
    """
    categories = os.listdir(source_dir)
    for category in categories:
        category_dir = os.path.join(source_dir, category)
        images = os.listdir(category_dir)
        np.random.shuffle(images)

        # 计算训练集、验证集和测试集的切分点
        n_total = len(images)
        n_train = int(n_total * train_size)
        n_val = int(n_total * val_size)
        
        # 创建目标文件夹
        os.makedirs(os.path.join(train_dir, category), exist_ok=True)
        os.makedirs(os.path.join(val_dir, category), exist_ok=True)
        os.makedirs(os.path.join(test_dir, category), exist_ok=True)
        
        # 分配图像到训练集
        for image in images[:n_train]:
            shutil.copy(os.path.join(category_dir, image), os.path.join(train_dir, category))
        
        # 分配图像到验证集
        for image in images[n_train:n_train+n_val]:
            shutil.copy(os.path.join(category_dir, image), os.path.join(val_dir, category))
        
        # 分配图像到测试集
        for image in images[n_train+n_val:]:
            shutil.copy(os.path.join(category_dir, image), os.path.join(test_dir, category))

# 设置源目录和目标目录
source_directory = './datasets/images'  # 源图像文件夹
train_directory = './datasets1/train'    # 训练集目标文件夹
val_directory = './datasets1/val'        # 验证集目标文件夹
test_directory = './datasets1/test'      # 测试集目标文件夹

# 执行数据分割
split_data(source_directory, train_directory, val_directory, test_directory)

加载数据集

import os
import torch
from torchvision import datasets, transforms
from torchvision.datasets import ImageFolder

def get_data_loaders(data_dir, batch_size=32):
    # 定义图像的转换操作
    transform = transforms.Compose([
        transforms.Resize((227, 227)),  # 将图像调整为227x227
        transforms.ToTensor(),          # 转换为torch.Tensor并归一化到[0,1]
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),  # 归一化
    ])

    # 定义一个简单的文件验证函数,排除非图像文件和.ipynb_checkpoints目录
    def is_valid_file(x):
        return x.endswith(('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp')) and not '.ipynb_checkpoints' in x

    # 定义训练集、验证集和测试集的文件夹路径
    train_dir = os.path.join(data_dir, 'train')
    val_dir = os.path.join(data_dir, 'val')
    test_dir = os.path.join(data_dir, 'test')

    # 使用ImageFolder加载数据集,加入is_valid_file函数过滤文件
    train_dataset = ImageFolder(train_dir, transform=transform, is_valid_file=is_valid_file)
    val_dataset = ImageFolder(val_dir, transform=transform, is_valid_file=is_valid_file)
    test_dataset = ImageFolder(test_dir, transform=transform, is_valid_file=is_valid_file)

    # 创建DataLoader
    train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
    val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
    test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

    return train_loader, val_loader, test_loader

# 设置数据目录和批量大小
data_dir = './datasets1'  
batch_size = 128

# 获取数据加载器
train_loader, val_loader, test_loader = get_data_loaders(data_dir, batch_size=batch_size)

但是这段代码运行的时候,出现了如下错误:
在这里插入图片描述
.ipynb_checkpoints问题:是因为Jupyter Notebook在保存文件时会自动创建这个目录来存储备份,导致ImageFolder尝试将它作为一个类别目录加载。所以,不急,写两行代码,干掉他们:

import os
import shutil

def remove_unwanted_dirs(directory, unwanted_dirs=['.ipynb_checkpoints']):
    """
    从给定目录中递归删除不需要的文件夹。
    
    参数:
    directory (str): 要清理的根目录。
    unwanted_dirs (list): 不需要的目录名称列表。
    """
    for root, dirs, files in os.walk(directory, topdown=False):
        for name in dirs:
            if name in unwanted_dirs:
                dir_path = os.path.join(root, name)
                shutil.rmtree(dir_path)
                print(f"Removed directory: {dir_path}")

# 设置数据目录
data_dir = './datasets1'

# 删除不需要的目录
remove_unwanted_dirs(data_dir)

再运行上面代码,即可解决问题。

定义神经网络

下面就简简单单根据论文来搭建神经网络:

#定义神经网络
import torch
import torch.nn as nn

class AlexNet(nn.Module):
    def __init__(self, num_classes=35):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=0),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(96, 256, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(256, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096), 
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

alexnet = AlexNet(num_classes=35)
#尝试不同的权重初始化策略:
# 初始化权重
def initialize_weights(m):
    if isinstance(m, nn.Conv2d):
        nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
    elif isinstance(m, nn.Linear):
        nn.init.xavier_normal_(m.weight)
        nn.init.constant_(m.bias, 0)

alexnet.apply(initialize_weights)
print(alexnet)

设置损失函数和优化器

#设置损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(alexnet.parameters(), lr=0.001)  # 使用Adam优化器

开始训练

#训练并且过程可视化
import torch
import matplotlib.pyplot as plt

def train_model(model, criterion, optimizer, train_loader, val_loader, num_epochs=100):
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model.to(device)
    
    train_losses, val_losses = [], []
    for epoch in range(num_epochs):
        model.train()  # 设置模型为训练模式
        running_loss = 0.0
        for inputs, labels in train_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            
            optimizer.zero_grad()  # 梯度归零
            outputs = model(inputs)  # 前向传播
            loss = criterion(outputs, labels)
            loss.backward()  # 反向传播
            optimizer.step()  # 更新权重
            
            running_loss += loss.item() * inputs.size(0)

        epoch_loss = running_loss / len(train_loader.dataset)
        train_losses.append(epoch_loss)

        # 验证阶段
        model.eval()  # 设置模型为评估模式
        with torch.no_grad():
            running_loss = 0.0
            for inputs, labels in val_loader:
                inputs, labels = inputs.to(device), labels.to(device)
                
                outputs = model(inputs)
                loss = criterion(outputs, labels)
                
                running_loss += loss.item() * inputs.size(0)
            
            epoch_val_loss = running_loss / len(val_loader.dataset)
            val_losses.append(epoch_val_loss)

        print(f'Epoch {epoch+1}/{num_epochs}, Train Loss: {epoch_loss:.4f}, Validation Loss: {epoch_val_loss:.4f}')
    
    return train_losses, val_losses

# 运行训练函数并可视化结果
train_losses, val_losses = train_model(alexnet, criterion, optimizer, train_loader, val_loader, num_epochs=100)

# 绘制损失曲线
plt.figure(figsize=(10, 5))
plt.plot(train_losses, label='Training Loss')
plt.plot(val_losses, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

# 保存模型权重
torch.save(alexnet.state_dict(), 'alexnet_trained.pth')
print("Saved trained model weights to 'alexnet_trained.pth'.")

在这里插入图片描述

模型评估

#模型评估
def evaluate_model(model, test_loader):
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model.to(device)
    model.eval()

    correct = 0
    total = 0
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    accuracy = 100 * correct / total
    print(f'Accuracy on test set: {accuracy:.2f}%')

# 加载模型权重
alexnet.load_state_dict(torch.load('alexnet_trained.pth'))
evaluate_model(alexnet, test_loader)

模型预测

from PIL import Image
from torchvision.transforms import ToTensor

def predict_image(image_path, model):
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model.to(device)
    model.eval()

    img = Image.open(image_path)
    img = ToTensor()(img).unsqueeze(0)  # 图像预处理和添加批次维度
    img = img.to(device)
    
    with torch.no_grad():
        outputs = model(img)
        _, predicted = torch.max(outputs, 1)
    return predicted.item()

# 示例用法
image_path = 'your_image.jpg'
prediction = predict_image(image_path, alexnet)
print(f'Predicted class index: {prediction}')

总结

AlexNet虽然在当时是一个划时代的模型,但其参数效率较低,模型较大,需要较多的计算资源。反正给我的感觉就是训练起来挺考验个人经验的。后续会为大家介绍一种更小的模型且能达到更快的训练速度。

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
卷积神经网络(Convolutional Neural Network,CNN)目标识别算法分类如下: 1. 基于传统CNN的目标识别算法:使用传统的卷积神经网络结构,如LeNet、AlexNet、VGG等,对图像进行特征提取和分类。 2. 基于深度CNN的目标识别算法:使用深度卷积神经网络结构,如ResNet、Inception等,对图像进行特征提取和分类。这些算法通常使用更深的网络结构和更多的卷积层,能够提取更复杂的特征,提高准确率。 3. 基于循环神经网络(Recurrent Neural Network,RNN)的目标识别算法:使用循环神经网络结构,如LSTM、GRU等,对图像序列进行特征提取和分类。这些算法通常用于视频目标识别和行为识别。 4. 基于注意力机制的目标识别算法:使用注意力机制,对图像中的重要部分进行特征提取和分类。这些算法通常用于图像中有多个目标或复杂背景的情况。 举例: 基于传统CNN的目标识别算法AlexNetAlexNet是一种使用卷积神经网络进行图像分类的算法,由Alex Krizhevsky等人于2012年提出。它使用了5个卷积层和3个全连接层,能够识别1000种不同的物体,准确率达到了84.7%。 基于深度CNN的目标识别算法:ResNet。ResNet是一种使用深度卷积神经网络进行图像分类的算法,由Microsoft Research Asia的Kaiming He等人于2015年提出。它使用了残差连接的方式,可以训练更深的网络结构,有效解决了深度网络的梯度消失和过拟合问题。在ImageNet数据集上,ResNet-152取得了3.57%的Top-5错误率,创下了当时的最好成绩。 基于循环神经网络的目标识别算法:LRCN。LRCN是一种使用循环神经网络进行视频分类的算法,由Donahue等人于2015年提出。它将卷积神经网络提取的特征序列输入到LSTM中,对视频进行分类。在UCF101数据集上,LRCN取得了87.6%的准确率。 基于注意力机制的目标识别算法:Squeeze-and-Excitation Networks(SENet)。SENet是一种使用注意力机制进行图像分类的算法,由Jie Hu等人于2018年提出。它通过学习每个通道的重要性,自适应地调整卷积神经网络中的特征图,提高了特征的判别能力和泛化能力。在ImageNet数据集上,SENet-154取得了2.42%的Top-5错误率,超过了当时的所有模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值