はじめに
個人開発で「人が座っている」「寝ている」といった行動を検知したいと思ったことはありませんか?
通常であれば学習用データを集めてモデルを学習する必要がありますが、これはかなり大変です。
そこで注目したのが CLIP というモデルです。
CLIPは「画像」と「テキスト」を同じベクトル空間にエンコードできるため、
「a person sitting」「a person lying in bed」といった文章をラベルとして与えるだけで、学習なし(ゼロショット)で分類できます。
本記事では、このCLIPを使って簡単な行動検知を試した実験ログをまとめます。
コードをそのまま動かせる形で掲載しているので、これからCLIP/ActionCLIPに触れてみたい方の参考になれば幸いです。
Clipとは
CLIP(Contrastive Language–Image Pre-training)は、OpenAIが提案した「画像とテキストを同じベクトル空間にエンコードする」技術です。
これにより以下のようなことが可能になります。
-
Zero-shot画像分類
→ ラベルを自然文で書くだけで分類できる -
テキスト検索
→ 「犬の写真」と入力すると、それらしい画像を検索できる
今回は「zero-shot画像分類」を使って行動検知を試してみます。
仕組み
CLIPは「画像とテキストのペア」を大量に学習しています。
イメージとしては以下のような流れです。
- 画像を Image Encoder でベクトルに変換
- テキストを Text Encoder でベクトルに変換
- 同じ内容のペア(画像と説明文)の類似度を高め、それ以外を低くするように学習
これを繰り返すことで、画像とテキストを「同じ意味空間」で扱えるようになります。
→ だから「a person sitting」と入力すれば、その説明に近い画像を分類できるのです。
こちらの記事でフルスクラッチでの実装が分かりやすく解説されています。
(学習過程を簡単に説明すると以下のような感じです。画像は上記の記事から拝借。
- 画像とその画像を説明するテキストのペアをバッチサイズNでモデルに投入
- テキストはText Encoder, 画像はImage Encoderで埋め込み表現(≒多次元ベクトル)にする
- 対角成分を最大化し、それ以外を最小化するように学習する
- 正しいペアを近づけ、間違ったペアを遠ざける
- Contrasive Representation Learning
- エンコーダーは原理的にはどんなものでも良さそう
- 上記の記事ではImage Encoderはresnet, Text Encoderは4層の蒸留済みSentence Transformerを使っている
- 分類タスク
- 画像は1つ, ラベルのテキストは複数とする
- それぞれをエンコードし、埋め込み表現にする
- 正しいペア(画像とその説明のテキストの対応)が最大化するように学習している
- 出力の「1 x 入力テキストラベル数のベクトル」(をsoftmaxで確率にしたもの)の最大値が分類されたラベルとなる
)
コードと実行
こちらのコードでZero-shot分類を試すことができます。
import torch
import clip
import cv2
import numpy as np
from PIL import Image
class ClipClassifier:
def __init__(self, model_name="ViT-B/32", device=None):
self.device = device or ("cuda" if torch.cuda.is_available()
else ("mps" if torch.backends.mps.is_available() else "cpu"))
self.model, self.preprocess = clip.load(model_name, device=self.device)
def _load_image(self, image_input):
if isinstance(image_input, str):
return Image.open(image_input).convert("RGB")
elif isinstance(image_input, np.ndarray):
rgb = cv2.cvtColor(image_input, cv2.COLOR_BGR2RGB)
return Image.fromarray(rgb)
else:
raise TypeError("画像はファイルパス(str)かOpenCV画像(np.ndarray)を指定してください")
def predict(self, image_input, labels):
pil_img = self._load_image(image_input)
image = self.preprocess(pil_img).unsqueeze(0).to(self.device)
text = clip.tokenize(labels).to(self.device)
with torch.no_grad():
image_features = self.model.encode_image(image)
text_features = self.model.encode_text(text)
# softmaxは正規化しない
logits_per_image = (image_features @ text_features.T).softmax(dim=-1)
return {labels[i]: float(prob) for i, prob in enumerate(logits_per_image[0])}
if __name__ == "__main__":
classifier = ClipClassifier()
image_path="./images/reading_3.jpg"
labels = [
"a person sitting on a chair",
"a person lying in bed",
"a person reading",
"doing nothing",
]
result = classifier.predict(image_path, labels)
print("Prediction results:", result)
入力画像
実行結果
Prediction results: {
'a person sitting on a chair': 2.0325183868408203e-05,
'a person lying in bed': 0.00807952880859375,
'a person reading': 0.9482421875,
'doing nothing': 0.043670654296875
}
ラベル:a person radingがもっとも高い確率となっているので、正しく分類されているといえます。
まとめ
- CLIPを使うと、学習データを用意せずに「ゼロショット」で行動検知ができる
- ラベルを自然文で与えるだけで分類できるので、個人開発やプロトタイプにとても便利
- ラベルの表現を工夫することで精度を改善できる(具体的・英語が安定しやすい)
- 今回は静止画での実験でしたが、フレーム列に拡張すれば動画行動認識(ActionCLIP的な応用)にもつながる
ゼロショットでここまで動くのはなかなか面白いと思います。
「まずは画像1枚から」「ラベルを自由に書き換えて遊んでみる」くらいの軽い気持ちで試してみると理解が深まると思います。
参考になれば幸いです。
- 本文
- clipとは
- 画像とテキストを同じベクトル空間にエンコードする技術
- action clipはそれを時系列に拡張したもの
- ここでは解説しない
- できること
- zero shotでの画像分類
- テキストでの画像検索
- 画像とテキストを同じベクトル空間にエンコードする技術
- 仕組み
- 解説記事のリンクを貼る
- 学習
- 画像とその画像を説明するテキストのペアをバッチサイズNでモデルに投入
- テキストはText Encoder, 画像はImage Encoderで埋め込み表現(≒多次元ベクトル)にする
- 対角成分を最大化し、それ以外を最小化するように学習する
- 正しいペアを近づけ、間違ったペアを遠ざける
- Contrasive Representation Learning
- エンコーダーは原理的にはどんなものでも良さそう
- clip(風)を自作する記事ではImage Encoderはresnet, Text Encoderは4層の蒸留済みSentence Transformerを使っている
- Zero shot分類
- 画像は1つ, ラベルのテキストは複数とする
- それぞれをエンコードし、埋め込み表現にする
- 画像とその説明のテキストの対角成分が最大化するように学習している
- 出力の1 x 入力テキストラベル数のベクトル(をsoftmaxで確率に変化したもの)の最大値が分類されたラベルとなる
- ライブラリを使い動かしてみる
- コードを貼る
- コードと実行結果の簡単な解説
- clipとは
- 締め
- 学習なしで手軽に試せる強力なツール
- ラベル設計(プロンプトエンジニアリング)の工夫で精度改善可能
- 今後:動画(ActionCLIP)や応用(監視・UIトリガー等)も狙える