PyTorchによるCIFAR-10カラー画像分類モデルの実装

PyTorchによるCIFAR-10カラー画像分類モデルの実装

環境設定

本チュートリアルでは、PyTorchを使用してCIFAR-10データセットのカラー画像分類モデルを実装します。CIFAR-10は10クラスの物体を含む画像データセットで、機械学習の入門として広く利用されています。

開発環境:

  • Python 3.8以上
  • Jupyter Notebook
  • PyTorch(GPU対応環境推奨)

GPU環境のセットアップ

計算リソースの効率的な利用のため、GPUが利用可能な場合はGPU、そうでない場合はCPUを使用するように設定します。

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision

# デバイスの設定
compute_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用デバイス: {compute_device}")

データセットの準備

データセットのダウンロードと前処理

CIFAR-10データセットをローカル環境にダウンロードし、訓練用とテスト用に分割します。データローダーを使用して、バッチ処理が可能な形式でデータを読み込みます。

# CIFAR-10データセットのダウンロード
train_data = torchvision.datasets.CIFAR10(
    root='dataset', 
    train=True, 
    transform=torchvision.transforms.ToTensor(),
    download=True
)

test_data = torchvision.datasets.CIFAR10(
    root='dataset', 
    train=False, 
    transform=torchvision.transforms.ToTensor(),
    download=True
)
# バッチサイズの定義
batch_size = 32

# データローダーの作成
train_loader = torch.utils.data.DataLoader(
    train_data, 
    batch_size=batch_size, 
    shuffle=True
)

test_loader = torch.utils.data.DataLoader(
    test_data, 
    batch_size=batch_size
)

# データの形状を確認
sample_images, sample_labels = next(iter(train_loader))
print(f"画像バッチの形状: {sample_images.shape}")
print(f"ラベルバッチの形状: {sample_labels.shape}")

データ可視化

訓練データのサンプルを視覚的に確認することで、データの内容を把握します。

import numpy as np

# 画像表示の設定
plt.figure(figsize=(20, 5))
for i, img in enumerate(sample_images[:20]):
    # チャネル順序の変更 (C, H, W) -> (H, W, C)
    img_np = img.numpy().transpose((1, 2, 0))
    
    # 2行10列のグリッドに配置
    plt.subplot(2, 10, i + 1)
    plt.imshow(img_np)
    plt.axis('off')
plt.show()

CNNモデルの構築

畳み込みニューラルネットワーク(CNN)は、特徴抽出部と分類部で構成されます。畳み込み層で画像の特徴を抽出し、全結合層で分類を行います。

主要レイヤー解説

nn.Conv2d(二次元畳み込み層)

入力特徴マップに対して二次元畳み込み演算を適用します。

torch.nn.Conv2d(
    in_channels,      # 入力画像のチャネル数
    out_channels,     # 出力チャネル数(フィルター数)
    kernel_size,      # フィルターサイズ
    stride=1,         # ストライド
    padding=0,        # パディング
    dilation=1        # 拡張率
)

nn.MaxPool2d(二次元最大プーリング層)

入力特徴マップのローカル領域から最大値を取得し、空間サイズを縮小します。

torch.nn.MaxPool2d(
    kernel_size,      # -poolingウィンドウサイズ
    stride=None,      # ストライド(kernel_sizeと同じ場合はNoneでOK)
    padding=0         # パディング
)

nn.Linear(全結合層)

入力特徴と出力特徴の間の線形変換を実行します。

torch.nn.Linear(
    in_features,      # 入力特徴数
    out_features      # 出力特徴数
)

ネットワーク構造

以下のコードは、CIFAR-10の32×32ピクセル画像を10クラスに分類するCNNモデルを定義しています。データフローの流れは、3×32×32の入力から始まり、畳み込みとプーリングを繰り返しながら特徴次元を圧縮し、最終的に10次元ベクトル(クラス確率)が出力されます。

import torch.nn.functional as F

# クラス数の定義
num_classes = 10

class ImageClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        
        # 特徴抽出ネットワーク
        self.conv_block1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        
        self.conv_block2 = nn.Sequential(
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        
        self.conv_block3 = nn.Sequential(
            nn.Conv2d(128, 256, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        
        # 分類ネットワーク
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(256 * 4 * 4, 512),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(512, 128),
            nn.ReLU(),
            nn.Linear(128, num_classes)
        )
    
    def forward(self, x):
        x = self.conv_block1(x)
        x = self.conv_block2(x)
        x = self.conv_block3(x)
        x = self.classifier(x)
        return x
from torchinfo import summary

# モデルのインスタンス化とデバイスへの転送
network = ImageClassifier().to(compute_device)

# モデル構造の詳細表示
summary(network, input_size=(batch_size, 3, 32, 32))

モデル訓練

損失関数と最適化アルゴリズム

# クロスエントロピー損失(多クラス分類用)
loss_function = nn.CrossEntropyLoss()

# 確率的勾配降下法(SGD)
learning_rate = 0.01
optimizer = torch.optim.SGD(network.parameters(), lr=learning_rate)

訓練ループの実装

訓練関数では、バッチごとにモデルへの入力Forward計算、損失の計算、逆伝播による勾配計算、パラメータ更新を行います。

def train_model(data_loader, model, criterion, optimizer):
    dataset_size = len(data_loader.dataset)
    batch_count = len(data_loader)
    
    total_loss = 0.0
    correct_predictions = 0
    
    model.train()
    for batch_x, batch_y in data_loader:
        batch_x, batch_y = batch_x.to(compute_device), batch_y.to(compute_device)
        
        # 順伝播
        predictions = model(batch_x)
        loss = criterion(predictions, batch_y)
        
        # 逆伝播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # 統計情報の更新
        total_loss += loss.item()
        correct_predictions += (predictions.argmax(1) == batch_y).sum().item()
    
    avg_loss = total_loss / batch_count
    accuracy = correct_predictions / dataset_size
    
    return accuracy, avg_loss

テスト関数の実装

テスト時はパラメータ更新を行わないため、勾配計算を無効化してメモリ効率を向上させます。

def evaluate_model(data_loader, model, criterion):
    dataset_size = len(data_loader.dataset)
    batch_count = len(data_loader)
    
    total_loss = 0.0
    correct_predictions = 0
    
    model.eval()
    with torch.no_grad():
        for batch_x, batch_y in data_loader:
            batch_x, batch_y = batch_x.to(compute_device), batch_y.to(compute_device)
            
            predictions = model(batch_x)
            loss = criterion(predictions, batch_y)
            
            total_loss += loss.item()
            correct_predictions += (predictions.argmax(1) == batch_y).sum().item()
    
    avg_loss = total_loss / batch_count
    accuracy = correct_predictions / dataset_size
    
    return accuracy, avg_loss

訓練の実行

モデルを訓練モードと評価モード適切に切り替えながら、 指定したエポック数だけ訓練を繰り返します。

num_epochs = 10

history_train_acc = []
history_train_loss = []
history_test_acc = []
history_test_loss = []

for epoch in range(num_epochs):
    # 訓練フェーズ
    network.train()
    train_acc, train_loss = train_model(train_loader, network, loss_function, optimizer)
    
    # 評価フェーズ
    network.eval()
    test_acc, test_loss = evaluate_model(test_loader, network, loss_function)
    
    # 履歴の保存
    history_train_acc.append(train_acc)
    history_train_loss.append(train_loss)
    history_test_acc.append(test_acc)
    history_test_loss.append(test_loss)
    
    # 結果の表示
    print(f"Epoch {epoch+1:2d}: "
          f"Train Acc={train_acc*100:.1f}%, Train Loss={train_loss:.3f}, "
          f"Test Acc={test_acc*100:.1f}%, Test Loss={test_loss:.3f}")

print("訓練完了")

出力例:

Epoch  1: Train Acc=15.1%, Train Loss=2.245, Test Acc=24.0%, Test Loss=2.044
Epoch  2: Train Acc=27.2%, Train Loss=1.976, Test Acc=31.9%, Test Loss=1.854
Epoch  3: Train Acc=35.9%, Train Loss=1.763, Test Acc=39.2%, Test Loss=1.652
Epoch  4: Train Acc=41.8%, Train Loss=1.604, Test Acc=41.2%, Test Loss=1.623
Epoch  5: Train Acc=45.3%, Train Loss=1.510, Test Acc=48.4%, Test Loss=1.437
Epoch  6: Train Acc=48.5%, Train Loss=1.428, Test Acc=49.4%, Test Loss=1.413
Epoch  7: Train Acc=51.7%, Train Loss=1.347, Test Acc=47.9%, Test Loss=1.420
Epoch  8: Train Acc=54.4%, Train Loss=1.275, Test Acc=55.6%, Test Loss=1.265
Epoch  9: Train Acc=57.1%, Train Loss=1.213, Test Acc=59.0%, Test Loss=1.176
Epoch 10: Train Acc=59.3%, Train Loss=1.154, Test Acc=54.5%, Test Loss=1.270

結果の可視化

訓練过程中的精度と損失の変化をグラフ化し、モデルの学習状況を評価します。

import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")

# グラフの設定
plt.figure(figsize=(12, 4))

# 精度の推移
plt.subplot(1, 2, 1)
plt.plot(range(1, num_epochs + 1), history_train_acc, 'b-', label='訓練精度')
plt.plot(range(1, num_epochs + 1), history_test_acc, 'orange', label='テスト精度')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.title('精度の推移')

# 損失の推移
plt.subplot(1, 2, 2)
plt.plot(range(1, num_epochs + 1), history_train_loss, 'b-', label='訓練損失')
plt.plot(range(1, num_epochs + 1), history_test_loss, 'orange', label='テスト損失')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.title('損失の推移')

plt.tight_layout()
plt.show()

技術的解説

CNNの主要コンポーネント

畳み込み層

畳み込み層は、入力画像に複数のフィルターを適用することで、エッジ、テクスチャ、色などの局所的特徴を抽出します。学習可能なパラメータとしてフィルター重みが存在し、データから自動的に効果的な特徴表現を獲得します。

プーリング層

プーリング層は、特徴マップの空間サイズを縮小し、計算量を削減しながら位置変化に対する不変性を獲得します。最大プーリングはローカル領域の最大値を取得し、代表的特徴を維持しながら次元圧縮を行います。

全結合層

全結合層は、抽出された特徴を総合して最終的な分類判断を行います。出力層の次元は分類タスクのクラス数と一致し、ソフトマックス関数により確率分布に変換されます。

ドロップアウト

ドロップアウトは、訓練時にランダムに一部のニューロンを無効化することで、過学習を防止する正則化手法です。本モデルでは30%のドロップアウト率を設定しています。

Flatiningによる次元変換

三次元特徴マップ(256×4×4)を一次元ベクトル(256×4×4=4096次元)に変換することで、全結合層への入力として適合させています。この変換により、空間的に分布した特徴が分類器で処理可能な形式に変換されます。

タグ: PyTorch 深層学習 CNN 画像認識 CIFAR-10

7月19日 01:38 投稿