CLIP - 対照的な言語イメージの事前トレーニング

(Contrastive Language-Image Pretraining)

Created at: 2020-12-16 19:24:42
Language: Jupyter Notebook
License: MIT

クリップ

[ブログ] [論文] [モデルカード] [コラボ]

CLIP (Contrastive Language-Image Pre-Training) は、さまざまな (画像、テキスト) ペアでトレーニングされたニューラル ネットワークです。GPT-2 および 3 のゼロ ショット機能と同様に、タスクを直接最適化することなく、画像が与えられた場合に最も関連性の高いテキスト スニペットを予測するように自然言語で指示できます。CLIP は元の ResNet50 のパフォーマンスと一致することがわかりました。元の 1.28M ラベル付きの例を使用せずに ImageNet の「ゼロ ショット」を使用して、コンピューター ビジョンにおけるいくつかの主要な課題を克服しました。

アプローチ

クリップ

使用法

最初に、PyTorch 1.7.1 (またはそれ以降) と torchvision、および小さな追加の依存関係をインストールしてから、このリポジトリを Python パッケージとしてインストールします。CUDA GPU マシンでは、次のようにします。

$ conda install --yes -c pytorch pytorch=1.7.1 torchvision cudatoolkit=11.0
$ pip install ftfy regex tqdm
$ pip install git+https://github.com/openai/CLIP.git

cudatoolkit=11.0
上記を、お使いのマシンの適切な CUDA バージョンに置き換えてください。または
cpuonly
、GPU のないマシンにインストールする場合。

import torch
import clip
from PIL import Image

device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)

image = preprocess(Image.open("CLIP.png")).unsqueeze(0).to(device)
text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device)

with torch.no_grad():
    image_features = model.encode_image(image)
    text_features = model.encode_text(text)
    
    logits_per_image, logits_per_text = model(image, text)
    probs = logits_per_image.softmax(dim=-1).cpu().numpy()

print("Label probs:", probs)  # prints: [[0.9927937  0.00421068 0.00299572]]

API

CLIP モジュール

clip
は、次のメソッドを提供します。

clip.available_models()

利用可能な CLIP モデルの名前を返します。

clip.load(name, device=..., jit=False)

によって返されるモデル名で指定された、モデルと、モデルに必要な TorchVision 変換を返します

clip.available_models()
。必要に応じてモデルをダウンロードします。引数は
name
、ローカル チェックポイントへのパスにすることもできます。

モデルを実行するデバイスはオプションで指定できます。デフォルトでは、最初の CUDA デバイスがある場合はそれを使用し、そうでない場合は CPU を使用します。

jit
がの場合
False
、モデルの非 JIT バージョンがロードされます。

clip.tokenize(text: Union[str, List[str]], context_length=77)

指定されたテキスト入力のトークン化されたシーケンスを含む LongTensor を返します。これは、モデルへの入力として使用できます


によって返されるモデルは

clip.load()
、次のメソッドをサポートしています。

model.encode_image(image: Tensor)

画像のバッチを指定すると、CLIP モデルのビジョン部分によってエンコードされた画像の特徴が返されます。

model.encode_text(text: Tensor)

テキスト トークンのバッチを指定すると、CLIP モデルの言語部分によってエンコードされたテキスト機能が返されます。

model(image: Tensor, text: Tensor)

画像のバッチとテキスト トークンのバッチを指定すると、各画像とテキスト入力に対応するロジット スコアを含む 2 つのテンソルを返します。値は、対応する画像とテキストの特徴間のコサイン類似度に 100 を掛けたものです。

その他の例

ゼロショット予測

以下のコードは、論文の付録 B に示されているように、CLIP を使用してゼロ ショット予測を実行します。この例では、CIFAR-100 データセットから画像を取得し、データセットの 100 個のテキスト ラベルの中から最も可能性の高いラベルを予測します。

import os
import clip
import torch
from torchvision.datasets import CIFAR100

# Load the model
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load('ViT-B/32', device)

# Download the dataset
cifar100 = CIFAR100(root=os.path.expanduser("~/.cache"), download=True, train=False)

# Prepare the inputs
image, class_id = cifar100[3637]
image_input = preprocess(image).unsqueeze(0).to(device)
text_inputs = torch.cat([clip.tokenize(f"a photo of a {c}") for c in cifar100.classes]).to(device)

# Calculate features
with torch.no_grad():
    image_features = model.encode_image(image_input)
    text_features = model.encode_text(text_inputs)

# Pick the top 5 most similar labels for the image
image_features /= image_features.norm(dim=-1, keepdim=True)
text_features /= text_features.norm(dim=-1, keepdim=True)
similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1)
values, indices = similarity[0].topk(5)

# Print the result
print("\nTop predictions:\n")
for value, index in zip(values, indices):
    print(f"{cifar100.classes[index]:>16s}: {100 * value.item():.2f}%")

出力は次のようになります (正確な数値は、コンピューティング デバイスによって若干異なる場合があります)。

Top predictions:

           snake: 65.31%
          turtle: 12.29%
    sweet_pepper: 3.83%
          lizard: 1.88%
       crocodile: 1.75%

この例では、指定された入力のエンコードされた特徴を返す メソッド

encode_image()
とメソッドを使用していることに注意してください。
encode_text()

リニアプローブ評価

以下の例では、scikit-learnを使用して、画像の特徴に対してロジスティック回帰を実行します。

import os
import clip
import torch

import numpy as np
from sklearn.linear_model import LogisticRegression
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR100
from tqdm import tqdm

# Load the model
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load('ViT-B/32', device)

# Load the dataset
root = os.path.expanduser("~/.cache")
train = CIFAR100(root, download=True, train=True, transform=preprocess)
test = CIFAR100(root, download=True, train=False, transform=preprocess)


def get_features(dataset):
    all_features = []
    all_labels = []
    
    with torch.no_grad():
        for images, labels in tqdm(DataLoader(dataset, batch_size=100)):
            features = model.encode_image(images.to(device))

            all_features.append(features)
            all_labels.append(labels)

    return torch.cat(all_features).cpu().numpy(), torch.cat(all_labels).cpu().numpy()

# Calculate the image features
train_features, train_labels = get_features(train)
test_features, test_labels = get_features(test)

# Perform logistic regression
classifier = LogisticRegression(random_state=0, C=0.316, max_iter=1000, verbose=1)
classifier.fit(train_features, train_labels)

# Evaluate using the logistic regression classifier
predictions = classifier.predict(test_features)
accuracy = np.mean((test_labels == predictions).astype(np.float)) * 100.
print(f"Accuracy = {accuracy:.3f}")

C
値は、検証分割を使用したハイパーパラメータ スイープによって決定する必要があることに注意してください。