1日100枚増える嫁との写真を自動仕分け!画像認識モデルを自作し超効率化した話。
はじめに
大量の写真を手動で仕分ける作業を自動化するため、PyTorchとResNet50を使った画像分類モデルを自作し、我が家で実運用を開始しました。本記事では、自作したモデルの結果をお伝えします。
やりたいこと
嫁と私のツーショットの写真と、ソロショットの写真のフォルダを仕分けしたい。
驚きなことに1日で100枚くらい写真が増えるほど。。なので手作業では到底追いつかないということです、しかも1年間分以上ある!(笑)
書いててアホなタイトルだなと思いましたが、本人はとても真面目に作業をしています。
なぜ自作したのか
- ローカルPCで動かせる軽量モデルだと配布できるし助かる
- VLMは大きすぎてローカル実行が難しいのと20GB程度のモデルではF1スコアが0.7くらいだった
- シンプルな二値分類で十分というユースケースのため大きなモデルは不要
- カスタマイズしやすいし、何よりも自作するのは面白い
感化された記事
→とても面白い!!!もちろんVLMも検討したが。。、我が家は計算リソースが乏しく。。苦笑
実装の概要
- フレームワーク: PyTorch
- 学習環境はGoogleColab T4 GPU
- モデル: ResNet50(ImageNet事前学習済み)
- 手法: フルファインチューニング(最初は転移学習を試したが精度があと一歩であった)
- タスク: ソロショット vs ツーショットの二値分類
モデルアーキテクチャ
ResNet50をベースに、ImageNet用の分類層を2クラス分類用に置き換えました。
- パラメータ数: 約25.6M(すべて訓練可能)
-
モデルサイズ: 約91MB
→魅力ポイントは、**VLMと比較し非常に小さいモデル(20BのVLMと比較すると約781倍小さい)**なので、CPUでも動かせるという点です。
データセット
人間のアノテーション(笑)でデータセットを作ります。これ以上は無理。。なのでデータ拡張に頼ります。
-
総画像数: 86枚
- ソロショット: 42枚
- ツーショット: 44枚
- 分割: 訓練データ 68枚(80%)、検証データ 18枚(20%)
試行錯誤の過程
最初のアプローチ: 転移学習
最初は転移学習(Transfer Learning)を試しました。転移学習は、事前学習済みモデルの重みを凍結し、最後の層のみを訓練する手法です。訓練時間が短く、メモリ使用量も少ないというメリットがあります。
# 転移学習版のコード
model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
# ベースモデルの重みを凍結(転移学習のため、ベースモデルは訓練しない)
for param in model.parameters():
param.requires_grad = False
# 最後の全結合層を置き換え
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Linear(num_features, 128),
nn.ReLU(),
nn.Dropout(0.7),
nn.Linear(128, 2)
)
# 学習率は5e-4を使用
optimizer = optim.Adam(model.parameters(), lr=5e-4)
結果: 転移学習では精度が十分に上がりませんでした。といっても検証データでの精度は約89%程度で、この時点で20B程度のVLMのF1スコアは超えている(笑)&十分なのですが、エンジニア的にもっと上を。。目指したくなりました。
改善策: フルファインチューニングへ
精度向上のため、フルファインチューニングに切り替えました。すべての層のパラメータを訓練可能にすることで、タスクに特化したモデルを構築できます。その反面、転移学習と比較すると、訓練時のメモリ消費量が大きくなります。ただし、LLMのチューニングと比較すれば圧倒的に小さいモデルなので今回のユースケースでは問題になりません。
実装の詳細
1. データ準備
# カスタムデータセットクラス
class PhotoDataset(Dataset):
def __init__(self, image_paths, labels, transform=None):
self.image_paths = image_paths
self.labels = labels
self.transform = transform
def __getitem__(self, idx):
img = Image.open(self.image_paths[idx])
if img.mode != 'RGB':
img = img.convert('RGB')
if self.transform:
img = self.transform(img)
return img, self.labels[idx]
2. データ拡張
訓練時の拡張:
- リサイズ(224×224)
- ランダム回転(±20度)
- ランダム水平反転(p=0.5)
- 色調整(明度、コントラスト、彩度、色相)
- アフィン変換(平行移動、シアー)
もちろんですが、人間の写真なので上下反転は使用していません。
3. モデル構築
# ResNet50をベースに使用
model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
# フルファインチューニングのため、すべてのパラメータを訓練可能にする
# (重みの凍結は行わない)
# 最後の全結合層を置き換え
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Linear(num_features, 128),
nn.ReLU(),
nn.Dropout(0.7),
nn.Linear(128, 2) # ソロショット(0)とツーショット(1)
)
4. 訓練設定
# 損失関数とオプティマイザー
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-4) # フルファインチューニング用の低い学習率
# 学習率スケジューラー
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
optimizer, mode='min', factor=0.5, patience=5, min_lr=1e-7
)
全層更新するため、転移学習よりも低い学習率で学習を実施しています。
結果
学習曲線

→プライベートでのデータセットのなかでは過去一よい下がり方!
転移学習 vs フルファインチューニングの比較
| 手法 | 精度 | 訓練時間 | メモリ使用量 | 備考 |
|---|---|---|---|---|
| 転移学習 | 約89% | 短い | 少ない | あと一歩物足りない |
| フルファインチューニング | 100% | 長い | 多い | 精度UPを達成 |
フルファインチューニングに切り替えることで、検証データでの精度が89%から100%に向上しました。
分類レポート(フルファインチューニング版)
precision recall f1-score
solo shot 1.00 1.00 1.00
two shot 1.00 1.00 1.00
accuracy 1.00
検証データで精度100%を達成。
👇このような形で推論されます。嫁に怒られてしまうのでモザイクをかけてあります。

モデルサイズ
- モデルサイズ: 約91MB
- ローカル実行に十分な軽量さ
→非常に軽量ですが、非常に高精度なモデルが出来上がりました。
転移学習からフルファインチューニングへの学び
フルファインチューニングのメリット
- 高い精度: 全パラメータを最適化できるため、精度向上が期待できる(実際に89%→100%に向上)
- タスク特化: データに合わせて全体を調整可能
- 学習手法として簡単か?: 転移学習やファインチューニングよりもタスク特化させる場合は経験的に簡単な気がします
今後の改善案
- データ拡張の強化: より多様な拡張を追加
- アンサンブル: 複数モデルの組み合わせ
- 分類ラベルの追加:嫁、私、ツーショットの3分類モデルを作る
まとめ
PyTorchとResNet50を使った画像分類モデルの実装を通じて、転移学習とフルファインチューニングの違いを実体験しました。
試行錯誤の結果:
- 最初は転移学習を試したが、精度が89%程度で個人的に物足りなかった(※転移学習ではこれ以上性能が上がる気がしなかった)
- フルファインチューニングに切り替えることで、精度向上!
得られた知見:
- データセットの特性や要求精度に応じて、転移学習とフルファインチューニングを使い分けることが重要かもしれない
- 最初から完璧を目指すのではなく、段階的に改善していくアプローチが有効と感じる
- VLMのような大規模モデルが不要な場合、シンプルなCNNでも十分に実用的であることも再認識させられた(次はViTでのチューニングを行ってみたい)
ローカルで動かせる軽量モデルは、プライバシーやコストの面でもメリットがあります。今回の経験から、CNN系のモデルも十分有効であることが分かりました。
そのほか
最近時様々なAI活用のご相談を受けています。
もしご興味ある方は、go.yusng@gmail.comへご連絡ください!
Discussion