画像分類が面白いほどわかる!ResNet18を一から徹底解剖

PR表記

※アフィリエイト広告を利用しています

どうもニコイチです。前回は「データセット作成」を行いました。今回はいよいよ、転移学習を行わない場合のモデルをどのように選定し、作成していくかを見ていきましょう。
このシリーズのゴールは、鋳造製品の画像から欠陥を検出するモデルを作ることです。今回のステップでは、まずは「転移学習なし」で使うネットワークを決めて、そのモデルを作る手順を解説します。

目次

モデルを選定する3つのポイント

  1. 既存のモデルを流用するか、新規で作成するか
    • 性能の安定性から、既存モデル(ResNet18 など)を流用するほうが簡単・確実
  2. 学習できるデータ量
    • データ量が少ない場合は、あまり層が深いモデルだと過学習しやすい
    • 比較的層の浅いモデルを選ぶほうが安定する
  3. 転移学習を行うかどうか
    • 今回は「いずれ転移学習をする」想定なので、転移学習可能なモデルを選ぶ

この3つを満たすモデルとして、ここでは ResNet18 を利用します。ResNet には 18、50、101 などいろいろな層数のバリエーションがありますが、ResNet18 は層数が少なく、比較的扱いやすいモデルです。


ResNet18 とは?

ResNet(Residual Network)は、「残差構造」を取り入れることで、層を深くしたときの学習しにくさを解消したモデルとして知られています。

  • ResNet18 はその中でも層が 18 層と、比較的浅めのネットワークです。
  • 畳み込み層(conv)を中心に、「ResidualBlock」という単位でネットワークが積み重なっています。

具体的には下記のような構造になっています(出力は省略していません)。

from torchvision import datasets, models, transforms

model_ft = models.resnet18(pretrained=False)
print(model_ft)

出力結果(抜粋)

ResNet(
 (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
 (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
 (relu): ReLU(inplace=True)
 (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
 (layer1): Sequential(
   (0): BasicBlock(
     (conv1): Conv2d(64, 64, kernel_size=(3, 3), ...
     (bn1): BatchNorm2d(64, ...
     (relu): ReLU(inplace=True)
     (conv2): Conv2d(64, 64, ...
     (bn2): BatchNorm2d(64, ...
   )
   (1): BasicBlock(
     (conv1): Conv2d(64, 64, ...
     (bn1): BatchNorm2d(64, ...
     (relu): ReLU(inplace=True)
     (conv2): Conv2d(64, 64, ...
     (bn2): BatchNorm2d(64, ...
   )
 )
 ...
 (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
 (fc): Linear(in_features=512, out_features=1000, bias=True)
)

ここでわかるように、層が順番に並んだ 4 つの大きな layer と、各 layer の中に BasicBlock(小さなブロック)が連なっている構造になっています。


欠陥検出用に最終層を変更する必要がある

ResNet18 は ImageNet という大規模データセットで事前学習された際、1000 クラス分類に対応するように最終層 (fc) が設定されています。
しかし、今回やりたいのは「正常品 or 欠陥品」という 2 クラス分類です。そのため、最終層を 2 クラス用に書き換える必要があります。

import torch.nn as nn
from torchvision import models

# 事前学習なしの ResNet18 をロード
model_ft = models.resnet18(pretrained=False)

# 最終層を 2 クラスに書き換え
model_ft.fc = nn.Linear(in_features=512, out_features=2, bias=True)

print(model_ft)

そうすると、(fc): Linear(in_features=512, out_features=2, bias=True) と表示され、
無事 2 クラス出力の ResNet18 が完成します。


転移学習なしのモデル定義を関数化する

学習しやすいように、モデルの作成部分を関数化しておくと便利です。例えば、以下のようにすると、出力クラス数を指定するだけで ResNet18 の最終層が書き換えられたモデルを簡単にロードできます。

import torch.nn as nn
from torchvision import datasets, models, transforms

device = "cpu"  # 今回はCPUで学習
def get_model(target_num, isPretrained=False):
    """ResNet18を用いてモデルを作成する関数"""
    model_ft = models.resnet18(pretrained=isPretrained)
    # 最終層を書き換える
    model_ft.fc = nn.Linear(512, target_num)
    # CPU (または GPU) に載せる
    model_ft = model_ft.to(device)
    return model_ft

これで get_model(2, isPretrained=False) とすれば、転移学習なしで 2 クラス分類の ResNet18 が作成できます。

次回予告

ここまでで、転移学習なしのモデルとして ResNet18 を選定し、最終層を書き換えるところまで完了しました。
次回は、このモデルを実際に学習させる準備として、最適化手法(optimizer)の選定loss 関数の設定を行い、その後に学習実行へ進んでいきます。

私たちはまだ学習モデル作成の道のりの途中ですが、この一歩一歩の積み重ねが必ずあなたの成長につながります。焦らず着実に進めていけば、必ず前進・成長していけるはずです。


確認用の小テスト

  1. ResNet18 の最終層を書き換える理由は?
    • A: 転移学習で重みを凍結するため
    • B: 分類対象のクラス数を変更するため
    • C: レイヤーを深くして過学習を防ぐため
  2. 転移学習を行わないとき、models.resnet18(pretrained=False) とする主な目的は?
    • A: 全く学習していない状態から重みを初期化するため
    • B: 既に学習済みのモデルでクラス数を固定するため
    • C: ResNet50 との差を明確にするため
回答
  1. B
  2. A

ここまで読んでみて、わからないことやより詳しく知りたい部分はありますか? ぜひ教えてください。皆さんの疑問に合わせて追加の情報を用意いたします!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次