現場で画像検索エンジンを設計する際、必ず直面するのが「何をもって『似ている』と定義するか」という問題です。製造業の外観検査からECサイトのレコメンドエンジンに至るまで、画像認識技術を用いたシステム開発において、実用的な精度と速度を両立するモデル設計は常に重要なテーマとなります。
「同じブランドの服を見つけたい」のか、「似たようなシルエットの服を探したい」のか、あるいは「柄が似ているもの」なのか。ユーザーの意図によって最適なアルゴリズムは変わります。これまで画像特徴量の抽出といえばResNetに代表されるCNN(畳み込みニューラルネットワーク)が主流でしたが、近年はVision Transformer(ViT)が強力な選択肢となっています。
しかし、最新の論文がViTの最高精度(SOTA)を謳っているからといって、実際のシステムにViTが最適だとは限りません。推論速度、学習データの規模、そして何より「どのような特徴を捉えたいか」という仮説に基づいて、選択すべきモデルは変わるからです。
今回は、アルゴリズムの原理からPyTorchを用いた実装までを段階的に解説し、CNNとViTを同一条件下で比較します。データから仮説を立て、実験で検証するサイクルを通じて、精度とスピードのトレードオフを評価するための材料を提供します。
1. 画像特徴量ベース推薦のメカニズムとECにおける課題
ECサイト、特にアパレルやインテリアの分野では、テキスト情報(メタデータ)だけでは商品の魅力を表現しきれません。「ふんわりした」「シュッとした」といったニュアンスは、タグ付けする人の主観に依存するため、テキスト検索では限界があります。ここで画像特徴量ベースの推薦が真価を発揮します。
テキスト検索では拾えない「視覚的類似性」
画像検索(Content-Based Image Retrieval: CBIR)の基本は、画像を人間には解読不能な数値の列(ベクトル)に変換し、そのベクトル同士の距離(近さ)を計算することです。このベクトルを「特徴量」と呼びます。
例えば、赤い花柄のワンピースがあったとします。
- テキスト検索: 「ワンピース」「赤」「花柄」でヒットしますが、花の大きさやシルエットまでは絞り込めません。
- 画像検索: 画像そのものを入力とするため、言葉にしにくい「雰囲気」や「形状」の類似性を捉えることができます。
CNN(局所特徴)とViT(大域特徴)のアプローチの違い
ここで重要なのが、CNNとViTの「目の付け所」の違いです。画像認識モデルを選定する際、精度と推論スピードのトレードオフを数値的に評価し、要件に合わせた判断を下す必要があります。
CNN (ResNetなど):
畳み込み演算を用います。これは、画像のごく一部(局所)を見て、エッジやテクスチャを検出し、層を重ねるごとに徐々に広い範囲の特徴を組み合わせていくボトムアップ型のアプローチです。そのため、「柄」「素材感」「局所的な形状」を捉えるのが得意な傾向があります。一方で、「帰納バイアス(Inductive Bias)」として「平行移動不変性(対象が画像のどこにいても認識できる性質)」を強く持っています。
なお、ResNet50は提案から年月が経過していますが、現在でもPyTorch Image Models (timm)ライブラリ等で継続的にサポートされており、推論速度と精度のバランスの良さから、画像分類の主要なバックボーンネットワークとして安定して利用されています。Vision Transformer (ViT):
画像をパッチ(例えば16x16ピクセル)に分割し、それらを単語のように扱ってSelf-Attention機構にかけます。最初から画像全体のパッチ間の関係性を計算できるため、「大域的なシルエット」「離れた場所にある要素同士の関係」を捉えるのが得意です。CNNのような強い帰納バイアスを持たないため、大量のデータで学習しないと性能が出にくい側面がありますが、事前学習済みモデルを使うことでこの課題はクリアされつつあります。
本記事で実装するパイプラインの全体像
今回は以下のフローで実装を進めます。Hugging Face Transformersの最新版では内部設計が大きく刷新され、PyTorchを中心に最適化されたモジュールアーキテクチャへと移行しています。旧来のTensorFlowやFlaxベースのサポートは終了しているため、公式の移行ガイドに従い、PyTorchベースの最新APIを活用してパイプラインを構築します。
- データ準備: 商品画像をPyTorchのTensorに変換。
- モデル構築: timmやHugging Faceの最新APIを利用し、ResNet50とViT-b-16の事前学習済みモデルを用意。
- 特徴量抽出: 画像をモデルに通し、高次元ベクトルを取得。
- 類似度計算: コサイン類似度でランキングを作成。
これらの手順を通じて、モデルのアーキテクチャが特徴量空間にどのような違いをもたらすのかを検証します。
2. 実装環境の構築とデータセットの準備
実装環境としてPyTorchをベースに据え、Vision Transformer(ViT)の取り回しを容易にするHugging Faceのtransformersライブラリを導入します。また、2026年現在もResNet50などのCNNモデルは主要なバックボーンとして広く利用されており、これらを効率よく扱うためにtimm(PyTorch Image Models)ライブラリを併用するアプローチも実用的です。特徴量ベクトルの計算にはscikit-learn、結果の可視化にはmatplotlibを使用します。
必要なライブラリ
# 必要なライブラリのインストール
# pip install torch torchvision transformers timm scikit-learn matplotlib pillow
import torch
import torch.nn as nn
import torchvision.models as models
import torchvision.transforms as transforms
from transformers import ViTModel, ViTImageProcessor
import timm
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# デバイスの設定(GPUが利用可能な場合は自動適用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
商品画像データの前処理と正規化
モデルへ入力するにあたり、画像を適切なサイズへリサイズし、テンソル化と正規化を行う必要があります。ここで留意すべきは、CNNとViTで推奨される前処理プロセスが異なる点です。
ResNet50に代表されるImageNet事前学習済みCNNモデルは、長年アーキテクチャ自体に変更はなく、現在も画像分類の強力なベースラインとして機能しています。通常は224x224へのリサイズと、ImageNetの統計値を用いた正規化を適用します。一方、ViTも基本的な考え方は共通していますが、Hugging FaceのImageProcessorを活用することで、各モデルのアーキテクチャに最適化された前処理を自動で適用できるため、手動設定によるヒューマンエラーを防ぐことができます。
# 画像パスのリスト(検証用のダミーパス)
image_paths = ["item1.jpg", "item2.jpg", "item3.jpg"]
# CNN (ResNet等) 用の前処理パイプライン
cnn_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# 画像ロード関数の定義
def load_image(path):
return Image.open(path).convert('RGB')
# CNN用のバッチ作成処理
def prepare_images_cnn(paths):
images = [load_image(p) for p in paths]
batch = torch.stack([cnn_transform(img) for img in images]).to(device)
return batch
3. ResNet50(CNN)による特徴量抽出の実装
画像分類のバックボーンネットワークとして広く知られるResNet50を使用して、特徴量を抽出します。提案から年月が経過していますが、PyTorch Image Models(timm)などの主要ライブラリで継続的にサポートされており、現在でも実用的な画像認識タスクにおいて極めて信頼性の高い選択肢です。
特にエッジ推論環境や最新のAI専用チップにおける推論最適化が進んでおり、精度と処理スピードのバランスに優れたモデルとして、依然として多くのプロジェクトで採用されています。
通常、画像分類タスクでは最終層(Fully Connected Layer)の出力をクラス分類に使いますが、類似画像検索では最終層の「一つ手前」の出力を使用します。ResNet50の場合、最終層の直前にあるGlobal Average Pooling層の出力(2048次元)が、画像の局所的な形状やテクスチャといった視覚的特徴を最もよく表していると考えられています。
事前学習済みモデルのロードと最終層の削除
torchvisionのモデルをロードし、torch.nn.Sequentialを用いて最終層を除いたモデルを再構築します。
class ResNetFeatureExtractor(nn.Module):
def __init__(self):
super(ResNetFeatureExtractor, self).__init__()
# ImageNetで学習済みのResNet50をロード
resnet = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
# 最終層(fc)を取り除く
# ResNetの構造: [conv1, bn1, relu, maxpool, layer1, layer2, layer3, layer4, avgpool, fc]
# 最後から1つ目まで(fc以外)を取得
self.features = nn.Sequential(*list(resnet.children())[:-1])
def forward(self, x):
# 推論モードで勾配計算を無効化
with torch.no_grad():
x = self.features(x)
# 出力形状は (Batch, 2048, 1, 1) なので (Batch, 2048) に平坦化
x = torch.flatten(x, 1)
return x
# モデルのインスタンス化
resnet_extractor = ResNetFeatureExtractor().to(device)
resnet_extractor.eval() # 推論モードへ
print("ResNet50 extractor ready.")
特徴ベクトルの抽出
画像から2048次元のベクトルを取り出す準備が整いました。抽出されたこのベクトルが、画像を数値化した「指紋」として機能し、後続の類似度計算に用いられます。
# 実行例
cnn_batch = prepare_images_cnn(image_paths)
resnet_features = resnet_extractor(cnn_batch)
print(f"ResNet Feature Shape: {resnet_features.shape}")
# 出力例: torch.Size([3, 2048])
4. Vision Transformer(ViT)による特徴量抽出の実装
Vision Transformer(ViT)は、画像を固定サイズのパッチに分割し、自然言語処理と同様の系列データとして扱うアプローチをとります。入力系列の先頭には「CLSトークン」と呼ばれる特殊なベクトルが追加され、Self-Attention機構を備えたTransformer層を通過する過程で、画像内の各パッチ間の関係性が計算されます。最終的に、このCLSトークンの出力ベクトルが、画像全体の大域的な文脈(シルエットや雰囲気など)を集約した特徴表現として抽出されます。
ViT-b-16モデルのロードと設定
Hugging Faceのtransformersライブラリを利用することで、CNNベースのモデルとは異なる前処理パイプラインもシンプルに記述できます。ここではベースモデルとして広く使われるアーキテクチャを採用します。
class ViTFeatureExtractorWrapper(nn.Module):
def __init__(self):
super(ViTFeatureExtractorWrapper, self).__init__()
# Hugging Faceからモデルとプロセッサをロード
self.model = ViTModel.from_pretrained('google/vit-base-patch16-224')
self.processor = ViTImageProcessor.from_pretrained('google/vit-base-patch16-224')
def get_features(self, images):
# プロセッサで前処理(リサイズ、正規化など)
inputs = self.processor(images=images, return_tensors="pt").to(device)
with torch.no_grad():
outputs = self.model(**inputs)
# last_hidden_stateの形状: (Batch, SequenceLength, HiddenSize)
# SequenceLengthの0番目が[CLS]トークン
# ViT-BaseのHiddenSizeは768次元
cls_token = outputs.last_hidden_state[:, 0, :]
return cls_token
# モデルのインスタンス化
vit_extractor = ViTFeatureExtractorWrapper().to(device)
vit_extractor.model.eval()
print("ViT extractor ready.")
ViTの特徴抽出実行
ViTの実装では、専用のプロセッサがパッチ分割や正規化を内包しているため、画像オブジェクトのリストを直接渡す処理フローとなります。CNNを使用する場合の手動でのテンソル変換や標準化処理と比較して、コードの記述が簡略化されます。
# 画像オブジェクトのリストを作成
pil_images = [load_image(p) for p in image_paths]
# 特徴量抽出
vit_features = vit_extractor.get_features(pil_images)
print(f"ViT Feature Shape: {vit_features.shape}")
# 出力例: torch.Size([3, 768])
抽出された特徴量の形状を確認すると、ViT-Baseモデルは768次元を出力します。ここで、比較対象となるResNetの出力(多くの場合2048次元)との違いが明確になります。
ResNet50は現在でも画像分類の主要なバックボーンネットワークとして広く利用されており、PyTorch Image Models (timm) などのライブラリで継続的にサポートされています。さらに最新のAIチップやエッジデバイスにおけるハードウェア最適化の恩恵を受けやすく、推論速度の大幅な向上が報告されています。
一方でViTは、出力次元数が低いことによるベクトル検索時の計算速度やメモリ効率の高さが利点となります。局所的なテクスチャの抽出に長け、ハードウェアレベルでの高速化が進むResNetと、Attention Mapを通じて画像全体の大域的な関係性を捉えることに優れたViT。これら特徴量空間の性質の違いと、精度・スピードのトレードオフを数値で評価し、システム要件に合わせたモデル選定を行うことが重要です。
5. 類似度計算と検索精度の比較検証
特徴量が抽出できたら、コサイン類似度を用いて類似度を計算します。ベクトル $A$ と $B$ のコサイン類似度は以下の式で表されます。
$$ \text{Cosine Similarity} = \frac{A \cdot B}{|A| |B|} $$
値は-1から1の範囲をとり、1に近いほど似ていることを意味します。
コサイン類似度によるランキング生成
from sklearn.metrics.pairwise import cosine_similarity
def calculate_similarity(features):
# CPUに移動してnumpy配列化
features_np = features.cpu().numpy()
# コサイン類似度行列を計算
sim_matrix = cosine_similarity(features_np)
return sim_matrix
# ResNetの類似度
resnet_sim = calculate_similarity(resnet_features)
# ViTの類似度
vit_sim = calculate_similarity(vit_features)
print("ResNet Similarity Matrix:\n", resnet_sim)
print("ViT Similarity Matrix:\n", vit_sim)
ResNetとViTの検索結果の違い(事例比較)
アパレル画像を対象とした類似度検索の評価において、各モデルのアーキテクチャに起因する顕著な傾向が一般的に確認されています。
柄・テクスチャ重視のResNet:
- ボーダー柄のTシャツをクエリにすると、「形は違うがボーダー柄の服(ポロシャツや長袖)」が上位に来やすい傾向があります。
- 細かい刺繍や素材感が似ているものを拾うのが得意です。
- 畳み込み処理(CNN)の性質上、局所的な特徴を捉えやすいため、背景が複雑な場合は背景のテクスチャもノイズとして拾ってしまうケースが報告されています。
- なお、ResNet50は現在でもPyTorch Image Models(timm)などの主要ライブラリで継続的にサポートされており、画像分類の強力なバックボーンとして広く利用され続けています。アーキテクチャ自体に変更はありませんが、最新の専用AIチップ環境では推論速度が大幅に向上している事例もあり、実運用における信頼性は依然として高いと言えます。
形状・構造重視のViT:
- ボーダー柄のTシャツをクエリにすると、「無地でも形の似ているTシャツ」が上位に来やすい傾向があります。
- 全体のシルエット(ロング丈、袖の形など)を捉えるのが得意です。
- Self-Attentionメカニズムにより、画像全体の文脈から主要なオブジェクト(商品)に注目しやすく、背景ノイズに強い特性を持っています。
アパレル画像における「柄」vs「形」の判定差
検索精度を高めるための正解は、最終的に「ユーザーが定義する『似ている』の基準」に依存します。
例えば、ユーザーが「同じ柄のシリーズを探したい」「特定の素材感のアイテムを見つけたい」という意図を持っている場合、局所的な特徴抽出に長けたResNetのほうが満足度の高い結果を返す可能性が高いと考えます。反対に、「このスタイルの服が好き」「全体のシルエットを合わせたい」という抽象的なニーズには、大局的な構造を捉えるViTが適しています。
実際のシステム開発では、データから仮説を立て、これらのモデルが持つ特徴量空間の特性を実験で検証した上で、要件に合わせて単独で採用するか、あるいは両者の特徴量をアンサンブルして精度を底上げするアプローチが有効です。
6. 実運用に向けた最適化とハイブリッド戦略
ECサイトで扱う商品数は数万から数百万点に及ぶことが一般的です。推論された特徴量に対して単純な総当たり計算を行うと、応答速度が著しく低下し、ユーザー体験を損ないます。実運用環境に耐えうるシステムを構築するには、検索の高速化と精度向上の両面からアプローチする必要があります。
Faissによるベクトル検索の高速化
大規模なベクトル検索において、Meta社が開発したFaissライブラリの導入は非常に有効な手段です。GPUを活用した高速な近傍探索を実装できます。
import faiss
# Faiss用にデータを準備 (float32にする必要あり)
features_np = vit_features.cpu().numpy().astype('float32')
dimension = features_np.shape[1] # 768
# インデックスの作成 (L2距離の場合)
index = faiss.IndexFlatL2(dimension)
index.add(features_np) # ベクトルを登録
# 検索 (自分自身を含む上位3件)
k = 3
D, I = index.search(features_np, k) # D: 距離, I: インデックス
print("Search Results (Indices):\n", I)
数百万件規模のデータセットを扱う場合、IndexFlatL2(全探索)ではなく、IndexIVFFlat(転置インデックス)などを用いて探索空間をクラスタリングし、検索範囲を絞り込むことで、処理速度を劇的に向上させることが可能です。
特徴量の連結(Concatenation)による精度向上
「商品の柄(テクスチャ)も、全体のシルエット(形状)も同時に考慮したい」という複雑な検索要件に対しては、ハイブリッド戦略が効果を発揮します。局所的な特徴抽出に優れたResNetと、大域的な文脈を捉えるViTの特徴量を結合(Concat)し、より表現力の高い新しい特徴ベクトルを生成します。
# 特徴量の結合
# 次元数は 2048 + 768 = 2816 になる
hybrid_features = torch.cat((resnet_features, vit_features), dim=1)
print(f"Hybrid Feature Shape: {hybrid_features.shape}")
ここで注意すべきは「次元の呪い」です。次元数が増大すると、距離計算の精度低下やメモリ消費量の増加を招きます。そのため、PCA(主成分分析)などの次元圧縮手法を適用し、情報量を保持しつつ計算コストを最適化する工程が不可欠です。
まとめ:ECサイトに最適な画像検索モデルの選択
CNNとViTのどちらを採用すべきかについて、すべてのユースケースに当てはまる単一の正解はありません。システムの要件とユーザーの検索意図に合わせて、適切なアーキテクチャを選択することが重要です。
- ResNet (CNN): テクスチャや柄の類似性を重視し、計算リソースを抑えたい場合に適しています。現在でも主要な画像分類のバックボーンネットワークとして広く利用されており、最新のハードウェア環境では推論性能の大幅な向上も報告されるなど、極めて安定した選択肢です。
- ViT (Transformer): 商品の全体的なシルエットや雰囲気を捉えたい場合や、大規模データセットによる事前学習モデルの恩恵を最大限に活用できる環境で真価を発揮します。
- ハイブリッド: 両者の強みを掛け合わせたい場合に有効ですが、モデルの複雑化に伴うエンジニアリングコストと推論コストの増加を許容できるインフラ基盤が求められます。
実際のシステム開発においては、オフラインでの精度評価だけでなく、A/Bテストを実施してCTR(クリック率)やCVR(コンバージョン率)といったビジネス指標への影響を数値で比較検証することが推奨されます。まずは紹介した実装例を参考に、対象データを用いたプロトタイプを作成し、仮説検証のサイクルを回すことから始めるのが効果的です。
要件が複雑化し、推論速度と精度のトレードオフが課題となるケースは珍しくありません。そのような場合は、対象のデータセットに特化したファインチューニングや、距離学習(Metric Learning)といった高度なアプローチを視野に入れることで、より洗練された検索体験の提供が可能になります。
コメント