Pytorch の移行学習では、猫と犬の分類を予測するためのモデル トレーニングに Resnet50 を使用します

目次

1. ResNet 残余ネットワーク

1.1 ResNetの定義

 1.2 ResNet のいくつかのネットワーク構成

 1.3 ResNet50のネットワーク構造

1.3.1 畳み込みとプーリングの最初の数層

1.3.2 残留ブロック: 深い残留ネットワークの構築

1.3.3 ResNet 本体: 複数の残差ブロックのスタック

1.4 移行学習の猫と犬の二項分類の実践

1.4.1 転移学習

1.4.2 モデルのトレーニング

1.4.3 モデルの予測


1. ResNet 残余ネットワーク

1.1 ResNetの定義

ディープラーニングは、画像分類、物体検出、音声認識などの分野で大きな進歩を遂げましたが、ネットワーク層の数が増加するにつれて、勾配の消失や勾配の爆発の問題が徐々に顕著になります。層の数が増えると、逆伝播プロセス中に勾配情報が徐々に小さくなり、ネットワークが収束することが困難になります。同時に、勾配爆発の問題により、ネットワークのパラメータ更新が大きすぎて正常に収束できなくなります。

これらの問題を解決するために、ResNet は革新的なアイデアを提案しています。それは、残差ブロック (Residual Block) の導入です。残差ブロックの設計により、ネットワークが残差マッピングを学習できるようになり、勾配消失の問題が軽減され、ネットワークのトレーニングが容易になります。

下の図は基本的な残差ブロックです。その動作は、ある層の入力をジャンプする前に次の層またはさらに深い層の活性化層に接続し、この層の出力とともに活性化関数を介して出力することです。
 

24353e89d9c84a17babbbf4ebe90630b.png

 1.2 ResNet のいくつかのネットワーク構成

以下に示すように:

 1.3 ResNet50のネットワーク構造

ResNet-50 は、50 の畳み込み層を備えた深い残差ネットワークです。そのネットワーク構造は非常に複雑ですが、次のモジュールに分割できます。

1.3.1 畳み込みとプーリングの最初の数層

import torch
import torch.nn as nn

class ResNet50(nn.Module):
    def __init__(self, num_classes=1000):
        super(ResNet50, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

1.3.2 残留ブロック: 深い残留ネットワークの構築

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1, downsample=None):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.conv3 = nn.Conv2d(out_channels, out_channels * 4, kernel_size=1, stride=1, bias=False)
        self.bn3 = nn.BatchNorm2d(out_channels * 4)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)

        return out

1.3.3 ResNet 本体: 複数の残差ブロックのスタック

ResNet-50 では、複数の残差ブロックを積み重ねてネットワーク全体を構築します。各残差ブロックは入力特徴マップを処理し、より豊富な特徴マップを出力します。複数の残差ブロックを積み重ねることにより、ネットワークは深さ方向に層ごとに情報を抽出できるため、より高いレベルの意味情報を取得できます。コードは以下のように表示されます。

class ResNet50(nn.Module):
    def __init__(self, num_classes=1000):
        # ... 前几层代码 ...

        # 4个残差块的block1
        self.layer1 = self._make_layer(ResidualBlock, 64, 3, stride=1)
        # 4个残差块的block2
        self.layer2 = self._make_layer(ResidualBlock, 128, 4, stride=2)
        # 4个残差块的block3
        self.layer3 = self._make_layer(ResidualBlock, 256, 6, stride=2)
        # 4个残差块的block4
        self.layer4 = self._make_layer(ResidualBlock, 512, 3, stride=2)

 make_layer 関数を使用して、基本的な残差ブロックのボトルネックのスタックを実現します。コードは以下のように表示されます。

def _make_layer(self, block, channel, block_num, stride=1):
    """
        block: 堆叠的基本模块
        channel: 每个stage中堆叠模块的第一个卷积的卷积核个数,对resnet50分别是:64,128,256,512
        block_num: 当期stage堆叠block个数
        stride: 默认卷积步长
    """
        downsample = None   # 用于控制shorcut路的
        if stride != 1 or self.in_channel != channel*block.expansion:   # 对resnet50:conv2中特征图尺寸H,W不需要下采样/2,但是通道数x4,因此shortcut通道数也需要x4。对其余conv3,4,5,既要特征图尺寸H,W/2,又要shortcut维度x4
            downsample = nn.Sequential(
                nn.Conv2d(in_channels=self.in_channel, out_channels=channel*block.expansion, kernel_size=1, stride=stride, bias=False), # out_channels决定输出通道数x4,stride决定特征图尺寸H,W/2
                nn.BatchNorm2d(num_features=channel*block.expansion))

        layers = []  # 每一个convi_x的结构保存在一个layers列表中,i={2,3,4,5}
        layers.append(block(in_channel=self.in_channel, out_channel=channel, downsample=downsample, stride=stride)) # 定义convi_x中的第一个残差块,只有第一个需要设置downsample和stride
        self.in_channel = channel*block.expansion   # 在下一次调用_make_layer函数的时候,self.in_channel已经x4

        for _ in range(1, block_num):  # 通过循环堆叠其余残差块(堆叠了剩余的block_num-1个)
            layers.append(block(in_channel=self.in_channel, out_channel=channel))

        return nn.Sequential(*layers)   # '*'的作用是将list转换为非关键字参数传入

1.4 移行学習の猫と犬の二項分類の実践

1.4.1 転移学習

転移学習は、あるタスクから学習した知識や機能を別の関連タスクに転送できるようにする機械学習および深層学習の手法であり、これによりモデルのトレーニングが高速化され、パフォーマンスが向上します。転移学習では通常、完全に新しいモデルを最初からトレーニングするのではなく、大規模なデータセットで事前トレーニングされたモデル (ソース タスク モデルと呼ばれる) を取得し、その重みを新しいタスク (ターゲット タスクと呼ばれる) に適用します。

転移学習の核となる考え方は、新しいタスクを解決する前に、まず学習したタスクからいくつかの共通の特徴や知識を取得し、これらの特徴や知識を新しいタスクに転送できるということです。この利点は、ソース タスク モデルが大規模なデータセットで完全にトレーニングされており、エッジ検出やテクスチャなど、多くのタスクに役立つ多くの共通機能を学習していることです。

1.4.2 モデルのトレーニング

まず、猫と犬を二項分類するためのデータセットを準備する必要があります。データセットは Kaggle からダウンロードできます。そこには猫や犬の写真が多数含まれています。

データセットをダウンロードした後、データセットをトレーニング セットとテスト セットに分割する必要があります。トレーニング セット フォルダーの名前は train で、猫と犬という 2 つのフォルダーが作成され、各フォルダーには対応するカテゴリの写真が保存されます。テスト セットには同様に test という名前が付けられます。次に、ResNet50 ネットワーク モデルを使用し、GPU を使用してモデルをトレーニングしてコンピューターに保存し、トレーニングの完了後にテスト セットでモデル予測の精度を検証します。

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
from torch.utils.data import DataLoader, Dataset
from torchvision.datasets import ImageFolder
from torchvision.models import resnet50

# 设置随机种子
torch.manual_seed(42)

# 定义超参数
batch_size = 32
learning_rate = 0.001
num_epochs = 10

# 定义数据转换
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 加载数据集
train_dataset = ImageFolder("train", transform=transform)
test_dataset = ImageFolder("test", transform=transform)

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size)

# 加载预训练的ResNet-50模型
model = resnet50(pretrained=True)
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 2)  # 替换最后一层全连接层,以适应二分类问题

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate, momentum=0.9)

# 训练模型
total_step = len(train_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):
        images = images.to(device)
        labels = labels.to(device)

        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (i + 1) % 100 == 0:
            print(f"Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], Loss: {loss.item()}")
torch.save(model,'model/c.pth')
# 测试模型
model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.to(device)
        labels = labels.to(device)
        outputs = model(images)
        print(outputs)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        break

    print(f"Accuracy on test images: {(correct / total) * 100}%")

1.4.3 モデルの予測

まず保存したモデルをロードします。ここでは単一の画像を予測し、予測結果をログに出力します。

import cv2 as cv
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import torchvision.transforms as transforms
import  torch
from PIL import Image
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model=torch.load('model/c.pth')
print(model)
model.to(device)

test_image_path = 'test/dogs/dog.4001.jpg'  # Replace with your test image path
image = Image.open(test_image_path)
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
input_tensor = transform(image).unsqueeze(0).to(device)  # Add a batch dimension and move to GPU

# Set the model to evaluation mode
model.eval()


with torch.no_grad():
    outputs = model(input_tensor)
    _, predicted = torch.max(outputs, 1)
    predicted_label = predicted.item()


label=['猫','狗']
print(label[predicted_label])
plt.axis('off')
plt.imshow(image)
plt.show()

スクリーンショットを実行する

ここまででこの記事は終わりです。

おすすめ

転載: blog.csdn.net/qq_43649937/article/details/131870303