画像分類でEfficientNetV2を選ぶと、少ないパラメータで高い精度を出しやすく、PyTorchなら数行で事前学習モデルを読み込める。この記事では、torchvisionとtimmでのモデルの選び方(S・M・L)、転移学習での分類ヘッド差し替え、クラス不均衡データへの対処までを、そのまま動くコードでまとめる。実装で最初につまずきやすい「旧来のefficientnet_pytorch(pip)はV2に対応していない」という落とし穴も先に片づける。
まとめ:EfficientNetV2をPyTorchで使う要点
- 標準はtorchvisionの
efficientnet_v2_s。事前学習の選択肢を広げたいときはtimm(ImageNet-21k重み)を使う。 - pipの
efficientnet_pytorchはEfficientNet(V1、b0〜b7)専用で、V2は扱えない。V2はtorchvisionかtimmで読み込む。 - 転移学習は
model.classifier[1]をnn.Linearで差し替え、特徴抽出層を凍結して学習する。 - クラス不均衡は
WeightedRandomSamplerで少数クラスを重点的にサンプリングして緩和する。 - 評価時の入力解像度はS=384px・M・L=480pxが目安(torchvisionの
weights.transforms()準拠)。
EfficientNetV2の特徴:EfficientNet(V1)からの3つの改良点
EfficientNetV2は、2021年にGoogleのMingxing TanとQuoc V. Leが発表した畳み込みニューラルネットワークである(論文「EfficientNetV2: Smaller Models and Faster Training」arXiv:2104.00298、ICML 2021)。2019年のEfficientNet(V1)の後継にあたり、学習速度とパラメータ効率を同時に高めた点が特徴だ。V1からの主な改良は次の3つ。
- Fused-MBConv:浅い層で「1×1拡張+depthwise畳み込み」を1つの通常の3×3畳み込みに融合し、GPU・TPUでの学習を高速化する。
- training-aware NAS:精度だけでなく学習速度も評価軸に入れてアーキテクチャを探索し、Fused-MBConvを使う位置を最適化した。
- progressive learning:学習の進行に合わせて入力画像サイズを段階的に上げ、同時にDropoutやデータ拡張などの正則化を強める。画像を大きくすると起きがちな精度低下を、正則化の調整で抑える手法だ。
結果として、同等精度のモデルと比べて最大6.8倍小さく、学習も速い。実務では「V1より速く学習でき、転移学習の初期精度も出やすい後継モデル」と捉えて問題ない。
EfficientNetV2のモデル種類とライブラリ別の呼び名
S・M・L・XLの違いと選び方
EfficientNetV2は容量の異なるバリアントを持つ。まずV2-Sで精度と速度の当たりを付け、精度が足りなければM・Lへ上げるのが実務の定石だ。XLはImageNet-21k事前学習前提の最大モデルで、timmから利用する。
| モデル | パラメータ | 評価解像度 | 提供元 | 目安の用途 |
|---|---|---|---|---|
| V2-S | 約2,150万 | 384px | torchvision・timm | まず試す標準 |
| V2-M | 約5,410万 | 480px | torchvision・timm | 精度重視 |
| V2-L | 約1億1,850万 | 480px | torchvision・timm | 高精度・大規模データ |
| V2-XL | 約2億800万 | 512px | timm(21k) | 最大精度 |
torchvision・timm・旧efficientnet_pytorchの対応
「efficientnet_pytorch」で検索して最初に出るpipパッケージ(lukemelas版)はV1専用で、V2のモデル名を渡してもエラーになる。V2を使うライブラリは実質torchvisionかtimmの2択と考えてよい。
| ライブラリ | V2対応 | 代表的な呼び出し | 備考 |
|---|---|---|---|
| torchvision | 対応 | efficientnet_v2_s(weights=…) | PyTorch標準・メンテ継続 |
| timm | 対応 | create_model(“tf_efficientnetv2_s…”) | 21k重み等バリアント豊富 |
| efficientnet_pytorch(pip) | 非対応 | EfficientNet.from_pretrained(“efficientnet-b0”) | V1のb0〜b7のみ |
PyTorch(torchvision)でEfficientNetV2を使う実装手順
事前学習モデルの読み込みと推論
torchvisionではefficientnet_v2_sに重みを渡すだけで読み込める。前処理は重みに紐づくtransforms()を使うと、学習時と同じリサイズ・正規化(V2-Sは短辺384にリサイズ後、384で中央クロップ)が自動で揃う。
import torch
from torchvision.models import efficientnet_v2_s, EfficientNet_V2_S_Weights
from PIL import Image
weights = EfficientNet_V2_S_Weights.IMAGENET1K_V1 # DEFAULT と同一
model = efficientnet_v2_s(weights=weights)
model.eval()
preprocess = weights.transforms() # resize 384, center crop 384
img = preprocess(Image.open("sample.jpg")).unsqueeze(0)
with torch.no_grad():
probs = model(img).softmax(dim=1)
label_id = probs.argmax(dim=1).item()
print(weights.meta["categories"][label_id])
転移学習:分類ヘッドの差し替えとデータ準備
自前のクラスに合わせるには、最終のclassifier[1](Linear(1280, 1000))を自分のクラス数に置き換える。データ量が少ないときはmodel.featuresを凍結し、ヘッドだけ学習させると過学習を抑えやすい。転移学習と特徴抽出・ファインチューニングの使い分けは、転移学習とは?ファインチューニング・特徴抽出との違いと実装判断を解説で判断基準を整理している。
import torch.nn as nn
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
num_classes = 5
model = efficientnet_v2_s(weights=EfficientNet_V2_S_Weights.IMAGENET1K_V1)
for p in model.features.parameters(): # 特徴抽出層を凍結
p.requires_grad = False
model.classifier[1] = nn.Linear(1280, num_classes) # 分類ヘッドを差し替え
train_tf = transforms.Compose([ # 学習側はデータ拡張あり
transforms.RandomResizedCrop(384),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
train_ds = datasets.ImageFolder("data/train", transform=train_tf)
train_loader = DataLoader(train_ds, batch_size=32, shuffle=True, num_workers=4)
学習ループと混合精度(AMP)
EfficientNetV2は入力解像度が大きくVRAMを消費するため、torch.ampによる混合精度学習でメモリと速度を両立させる。損失関数のlabel smoothingは、クラス間の紛らわしい画像が多いデータで効きやすい。
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
scaler = torch.amp.GradScaler(device)
for epoch in range(10):
model.train()
for x, y in train_loader:
x, y = x.to(device), y.to(device)
optimizer.zero_grad()
with torch.amp.autocast(device):
loss = criterion(model(x), y)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
学習の記録やマルチGPUの定型処理を減らしたいときは、PyTorch Lightningのインストール方法とpip install lightning/pytorch-lightningの違いで導入方法を確認したうえで、上記ループをLightningModuleに移すと保守しやすくなる。
timmでEfficientNetV2を使う(事前学習の選択肢を広げる)
timmはtorchvisionにないImageNet-21k事前学習の重みや、V2-XLを含む幅広いバリアントを提供する。21kで事前学習しImageNet-1kでファインチューニングしたtf_efficientnetv2_s.in21k_ft_in1kは、少量データの転移学習で1kのみの重みより初期精度が出やすい。tf_efficientnetv2_b0のような軽量版もあり、num_classesを渡すだけでヘッドが差し替わる。
import timm
model = timm.create_model(
"tf_efficientnetv2_s.in21k_ft_in1k",
pretrained=True,
num_classes=5,
)
cfg = timm.data.resolve_model_data_config(model)
transform = timm.data.create_transform(**cfg) # 重みに合った前処理を生成
クラス不均衡データでEfficientNetV2を学習させる
実データの画像分類では、クラスごとの枚数が偏ることが多い。多数クラスに引っ張られて少数クラスの精度が落ちるときは、WeightedRandomSamplerでバッチ内の出現確率をクラスの逆頻度で補正する。損失側のweight指定より、EfficientNetV2のように学習が速いモデルでは収束初期からの偏り緩和に効きやすい。
import numpy as np
from torch.utils.data import WeightedRandomSampler
targets = np.array(train_ds.targets)
class_count = np.bincount(targets)
class_weight = 1.0 / class_count # 少数クラスほど重みを大きく
sample_weight = class_weight[targets]
sampler = WeightedRandomSampler(
weights=sample_weight,
num_samples=len(sample_weight),
replacement=True,
)
train_loader = DataLoader(train_ds, batch_size=32, sampler=sampler, num_workers=4)
サンプラーを使うときはshuffle=Trueを外す(同時指定はエラー)。極端な不均衡では、サンプラーとlabel smoothing、少数クラスのデータ拡張強化を併用すると安定する。
精度と速度を両立させる実務ポイント
EfficientNetV2で伸び悩んだときに効果が大きい順に挙げる。まず試すべきは事前学習重みの選択と段階的なファインチューニングで、モデルサイズを上げるのは最後でよい。
- 段階的な凍結解除:最初はヘッドのみ学習し、損失が下げ止まったら
features後半→全体の順に凍結を解く。いきなり全層を高い学習率で回すと事前学習の表現を壊す。 - 学習と評価で解像度を変える:progressive learningの考え方どおり、学習は小さめ、評価は
weights.transforms()の推奨解像度(S=384)で推論すると精度が出やすい。 - 学習率スケジューリング:AdamW+コサイン減衰+数エポックのウォームアップが無難。ヘッドと特徴抽出層で学習率を分けると安定する。
- やり過ぎないモデル選択:数千枚規模のデータでV2-Lを使うと過学習しやすい。データが増えるまではV2-Sで十分なことが多い。
よくある質問
pipのefficientnet_pytorchでEfficientNetV2は使えますか?
使えません。efficientnet_pytorch(lukemelas版)はEfficientNet V1のb0〜b7専用で、V2のアーキテクチャや重みは含まれていません。V2を使うにはtorchvision(efficientnet_v2_sなど)かtimm(tf_efficientnetv2_sなど)を使ってください。
torchvisionとtimm、どちらを使うべきですか?
PyTorch標準で完結させたい・依存を増やしたくないならtorchvisionが第一候補です。ImageNet-21k事前学習の重みやV2-XL、より多くのバリアントが必要ならtimmを選びます。両者はモデル名と前処理の生成方法が違うだけで、学習ループは共通です。
efficientnet_v2_sの入力画像サイズはいくつですか?
torchvisionのIMAGENET1K_V1重みでは、評価時に短辺384へリサイズ後、384×384で中央クロップします。weights.transforms()を使えばこの前処理が自動で適用されるため、手動でサイズを指定する必要はありません。
EfficientNetV2とEfficientNet(V1)の違いは何ですか?
V2は浅い層にFused-MBConvを導入し、学習速度も評価軸に入れたNASと、画像サイズを段階的に上げるprogressive learningを組み合わせています。これにより、同等精度のV1より最大6.8倍小さく、学習が速くなっています。
学習したモデルを.NETなど他の環境で動かせますか?
可能です。torch.onnx.exportでONNX形式に書き出せば、ONNX RuntimeやML.NET経由で.NETアプリからも推論できます。学習はPyTorch、推論は別環境という構成が取れます。