CLIPでkaokoreデータセットの「化身」と「人」を見分ける（CLIP動作確認編）

### はじめに
誰が何と言おうと**「人」か「化身」か分類をしてみたい**。
前回の記事で[CLIPでkaokoreデータセットの「化身」と「人」を見分ける（CLIP説明編）](https://qiita.com/study_program/items/565cb3b07b059c4801a2)という記事を書きました。今回は試しました編です。

動作環境は、

* google colaboratory
* windows 10
です。

### 用いたデータセット

データセットは前の記事[Microsoft Lobeを顔コレデータセット（KaoKore Dataset）を用いて画像分類してみる](https://qiita.com/study_program/items/40e6fea9344efca06484)でも利用した[Kaokore Dataset](http://codh.rois.ac.jp/face/dataset/)です。

データセットの説明は前の記事に書いているので、今回は↓この4種類の画像分類に挑戦します。
![kaokore_classification.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/4f2d7d00-a608-ece3-88bf-37e00125486c.png)

画像は、こちらの[KaoKore Dataset githubのサイト](https://github.com/rois-codh/kaokore)からダウンロードして使いました。

### 早速画像分類にトライ！
なお、Roboflowさんのブログ記事中に出てくる[CLIP benchmarking Colab notebook](https://colab.research.google.com/drive/1LXla2q9MCRRI_kTjpvag2Vz-7EGLnki5#scrollTo=lOF3Feb7jrnu)
を全面的に参考にしています。このブログでは花のデイジーとタンポポの画像分類を試しています。
もともとはnpakaさんのnote記事 [CLIPを試す - OpenAIのZero-shot画像分類器](https://note.com/npaka/n/n3e3e2e56c756)がキッカケでした。読みやすくて非常に参考になります。

#### CLIPダウンロード
ダウンロードはブログ記事そのままです。

```
#installing some dependencies, CLIP was release in PyTorch
import subprocess

CUDA_version = [s for s in subprocess.check_output(["nvcc", "--version"]).decode("UTF-8").split(", ") if s.startswith("release")][0].split(" ")[-1]
print("CUDA version:", CUDA_version)

if CUDA_version == "10.0":
    torch_version_suffix = "+cu100"
elif CUDA_version == "10.1":
    torch_version_suffix = "+cu101"
elif CUDA_version == "10.2":
    torch_version_suffix = ""
else:
    torch_version_suffix = "+cu110"

!pip install torch==1.7.1{torch_version_suffix} torchvision==0.8.2{torch_version_suffix} -f https://download.pytorch.org/whl/torch_stable.html ftfy regex
```
### importなど

```
import numpy as np
import torch
import os

print("Torch version:", torch.__version__)
```
私が試した環境では

>Torch version: 1.7.1+cu110

が出力しました。

### CLIPをgit clone

```
#clone the CLIP repository
!git clone https://github.com/openai/CLIP.git
%cd CLIP
```
### 画像を保存したところにアクセス
google drive上に画像を保存していたので、マウントします。

```
from google.colab import drive
drive.mount('/content/drive')
```
ちなみにgoogle deive上では、このような感じのフォルダ構成で画像を置きました。
フォルダ名をラベルのクラス名として利用します。
![googleDriveImage.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/6cca96a0-cbc6-b317-9994-d5dbb2aea589.png)

この”_tokenization.txt”にキャプションを記載します。
こんな感じ。
![tokenization.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/f1fb4972-1959-7c2a-0e19-8d4de79b831a.png)

キャプションのリストとして読み込みます。

```
candidate_captions = []
with open('_tokenization.txt') as f:
    candidate_captions = f.read().splitlines()
```
画像分類したいクラス名をリスト化します。

```
class_names = os.listdir('.')
class_names.remove('_tokenization.txt')
```

###import CLIPなど
```
import torch
import clip
from PIL import Image
import glob

def argmax(iterable):
    return max(enumerate(iterable), key=lambda x: x[1])[0]

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

correct = []

#define our target classificaitons, you can should experiment with these strings of text as you see fit, though, make sure they are in the same order as your class names above
text = clip.tokenize(candidate_captions).to(device)
```
###精度評価！
今回は各ラベルの画像枚数を同じにするため、各ラベル535枚の画像を用いて評価をしました。
たったこれだけのコードで精度評価できるだと・・。


```
for cls in class_names:
  print(cls)
  class_correct = []
  test_imgs = glob.glob('datasets/' + cls + '/*.jpg')
  for img in test_imgs:
      #prit(img)
      image = transform(Image.open(img)).unsqueeze(0).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()

          pred = class_names[argmax(list(probs)[0])]
          #print(pred)
          if pred == cls:
              #print("img:",img, "pred:",probs)
              correct.append(1)
              class_correct.append(1)
          else:
              correct.append(0)
              class_correct.append(0)
    
  print('accuracy on class ' + cls + ' is :' + str(sum(class_correct)/len(class_correct)))
print('accuracy on all is : ' + str(sum(correct)/len(correct)))

```
結果はこんな感じでした。
![result.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/fae0dd24-f920-86ec-e30d-e128ba553126.png)
化身(incarnation)と分類できたのはわずか**2%**。
以前のMicrosoftのLobeで画像分類した時は少量の画像で精度100%だったので、
結果は**イマイチ**でした。
武士(warrior)だけ**81%**と精度が高いのは、武士に関する記事を事前に学習していたのでしょうか？

前回の記事で**CLIP は、自動車のモデル、花の種類、航空機の種類を区別するような、細かい分類を行うことができません。**と書いてあった通り、人か化身かの区別はCLIP先生にはちょっと難しかったようです。
ちなみに微妙に'_tokenization.txt'のキャプションを変更すると多少精度が変わりますが、飛躍的に精度向上することはありませんでした。

### 参考記事
ありがとうございます！

* 元論文:[Learning Transferable Visual Models From Natural Language Supervision](https://arxiv.org/abs/2103.00020)
* npakaさんのnote記事 [CLIPを試す - OpenAIのZero-shot画像分類器](https://note.com/npaka/n/n3e3e2e56c756)
* roboflowのブログ記事[How to Try CLIP: OpenAI's Zero-Shot Image Classifier](https://blog.roboflow.com/how-to-use-openai-clip/)