PyTorch 実例 - 画像分類プロジェクト
画像分類はコンピュータビジョンにおける最も基本的なタスクの一つであり、その目標はコンピュータが画像内の主要な内容を認識し、それを事前に定義されたカテゴリに分類できるようにすることです。例えば、画像に猫が写っているか犬が写っているかを識別することです。
画像分類における深層学習の応用
深層学習モデル、特に畳み込みニューラルネットワーク(CNN)は、画像分類タスクの主流のソリューションとなっています。PyTorchは深層学習フレームワークとして、CNNモデルを構築・トレーニングするための完全なツールチェーンを提供します。
プロジェクトフローの概要
完全な画像分類プロジェクトには通常、以下のステップが含まれます:
- データの準備と前処理
- モデルの構築
- モデルのトレーニング
- モデルの評価
- モデルの応用
環境準備とデータ読み込み
必要なライブラリのインストール
# 安装PyTorch和torchvision !pip install torch torchvision
一般的なデータセットの読み込み
PyTorchのtorchvisionは、CIFAR-10、MNISTなど、複数の一般的なデータセットを提供しています。
例
import torch
import torchvision
import torchvision.transforms as transforms
# データ変換を定義
transform = transforms.Compose([
transforms.ToTensor(), # PIL画像をTensorに変換
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 正規化
])
# CIFAR-10トレーニングセットを読み込む
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
# CIFAR-10テストセットを読み込む
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
# クラス名を定義
classes = ('plane', 'car', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck')
import torchvision
import torchvision.transforms as transforms
# データ変換を定義
transform = transforms.Compose([
transforms.ToTensor(), # PIL画像をTensorに変換
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 正規化
])
# CIFAR-10トレーニングセットを読み込む
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
# CIFAR-10テストセットを読み込む
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
# クラス名を定義
classes = ('plane', 'car', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck')
畳み込みニューラルネットワークモデルの構築
基本的なCNN構造
典型的なCNNは、畳み込み層、プーリング層、全結合層で構成されます。
例
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 畳み込み層1:入力3チャンネル(RGB)、出力6チャンネル、5x5カーネル
self.conv1 = nn.Conv2d(3, 6, 5)
# プーリング層:2x2ウィンドウ、ストライド2
self.pool = nn.MaxPool2d(2, 2)
# 畳み込み層2:入力6チャンネル、出力16チャンネル、5x5カーネル
self.conv2 = nn.Conv2d(6, 16, 5)
# 全結合層1:入力16*5*5、出力120
self.fc1 = nn.Linear(16 * 5 * 5, 120)
# 全結合層2:入力120、出力84
self.fc2 = nn.Linear(120, 84)
# 全結合層3:入力84、出力10(10クラスに対応)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 最初の畳み込み+ReLU+プーリング
x = self.pool(F.relu(self.conv1(x)))
# 2番目の畳み込み+ReLU+プーリング
x = self.pool(F.relu(self.conv2(x)))
# 特徴マップをフラット化
x = x.view(-1, 16 * 5 * 5)
# 全結合層+ReLU
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
# 出力層
x = self.fc3(x)
return x
# ネットワークのインスタンス化
net = Net()
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 畳み込み層1:入力3チャンネル(RGB)、出力6チャンネル、5x5カーネル
self.conv1 = nn.Conv2d(3, 6, 5)
# プーリング層:2x2ウィンドウ、ストライド2
self.pool = nn.MaxPool2d(2, 2)
# 畳み込み層2:入力6チャンネル、出力16チャンネル、5x5カーネル
self.conv2 = nn.Conv2d(6, 16, 5)
# 全結合層1:入力16*5*5、出力120
self.fc1 = nn.Linear(16 * 5 * 5, 120)
# 全結合層2:入力120、出力84
self.fc2 = nn.Linear(120, 84)
# 全結合層3:入力84、出力10(10クラスに対応)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 最初の畳み込み+ReLU+プーリング
x = self.pool(F.relu(self.conv1(x)))
# 2番目の畳み込み+ReLU+プーリング
x = self.pool(F.relu(self.conv2(x)))
# 特徴マップをフラット化
x = x.view(-1, 16 * 5 * 5)
# 全結合層+ReLU
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
# 出力層
x = self.fc3(x)
return x
# ネットワークのインスタンス化
net = Net()
モデル構造の可視化

モデルのトレーニング
損失関数と最適化アルゴリズムの定義
例
import torch.optim as optim
# 交差エントロピー損失関数
criterion = nn.CrossEntropyLoss()
# 確率的勾配降下法オプティマイザ
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
# 交差エントロピー損失関数
criterion = nn.CrossEntropyLoss()
# 確率的勾配降下法オプティマイザ
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
トレーニングループ
例
for epoch in range(10): # 10エポックでトレーニング
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 入力データを取得
inputs, labels = data
# 勾配をゼロに初期化
optimizer.zero_grad()
# 順伝播
outputs = net(inputs)
# 損失を計算
loss = criterion(outputs, labels)
# 逆伝播
loss.backward()
# 重みを更新
optimizer.step()
# 統計情報を出力
running_loss += loss.item()
if i % 2000 == 1999: # 2000ミニバッチごとに出力
print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
running_loss = 0.0
print('Finished Training')
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 入力データを取得
inputs, labels = data
# 勾配をゼロに初期化
optimizer.zero_grad()
# 順伝播
outputs = net(inputs)
# 損失を計算
loss = criterion(outputs, labels)
# 逆伝播
loss.backward()
# 重みを更新
optimizer.step()
# 統計情報を出力
running_loss += loss.item()
if i % 2000 == 1999: # 2000ミニバッチごとに出力
print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
running_loss = 0.0
print('Finished Training')
モデルの評価
テストセットの精度計算
例
correct = 0
total = 0
with torch.no_grad(): # 勾配を計算しない
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Accuracy on test images: {100 * correct / total:.2f}%')
total = 0
with torch.no_grad(): # 勾配を計算しない
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Accuracy on test images: {100 * correct / total:.2f}%')
各カテゴリの精度分析
例
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1
for i in range(10):
print(f'Accuracy of {classes[i]:5s}: {100 * class_correct[i] / class_total[i]:.2f}%')
class_total = list(0. for i in range(10))
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1
for i in range(10):
print(f'Accuracy of {classes[i]:5s}: {100 * class_correct[i] / class_total[i]:.2f}%')
モデルの保存と読み込み
トレーニング済みモデルの保存
例
# モデルのパラメータを保存
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
モデルを読み込んで予測を行う
例
# モデルを読み込む
net = Net()
net.load_state_dict(torch.load(PATH))
# モデルを使用して予測を行う
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join(f'{classes[predicted[j]]:5s}' for j in range(4)))
net = Net()
net.load_state_dict(torch.load(PATH))
# モデルを使用して予測を行う
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join(f'{classes[predicted[j]]:5s}' for j in range(4)))