仮想「データサイエンティスト採用試験」作って採点してみる

# はじめに

仮想です。
今の所弊社ではこの「業務未経験可のデータサイエンティスト求人」及び「それ用の試験」は実施しておりません、ご了承ください。
そのうちするかもしれないけども。


### 動機
- 異業種からの転職組・実務経験なし組用にスキルチェック課題を作りたい
- 試しに作ってみると採用云々関係なく、基礎の理解に役立ちそうな練習問題になりそうなので公開しておきます
- よさそうなら本当に試験として使いたいなぁ

### 試験目的
- 設定された実務っぽい課題に対して、適切に「仮説の設定・方針の設計・分析・検証・考察」の流れが組めるかを確認する
    - 明確な答えがある問題ではありません
- 精度やモデルの高度さは度外視
	- うまく精度出ていなくてもいい
	- もっと言うとプログラム組めてなくても流れが説明できていれば減点はない

・・・という旨を課題を提示したときに先に説明する想定


↓↓↓作ってみた試験　ここから↓↓↓

# 仮想試験問題

## 問題設定


データサイエンティストであるあなたに、以下のような相談が持ち掛けられました。
相談者はWebコンテンツ作成を行っており、コンテンツ作成に利用するための動物画像の素材を多く保持しています。
最近この素材の量が増えてきて、利用したい画像を探す手間が増えてきたため、「素材画像の整理を自動でできないか」という相談でした。

要望をまとめると

- 要望１：類似の素材をまとめて3つのフォルダに分類して管理したい
- 要望２：今後も素材が増えていくため、新しく入手した素材の分類も自動化したい

となりました。

画像は[こちら](https://drive.google.com/file/d/1SADtXF-dhcmNpF-Zp0RmAMElH0mAqy0r/view?usp=sharing)からどうぞ。

↓内容はこんな感じ。
![Pasted image 20230917193448.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83530/50a4392e-cc80-1b7f-2e29-ed46ca2289fe.png)

（試験用に加工前なのでファイル名など微妙に異なります。そしてRefinerと名前が付いているのでピンと来る方がいるかもしれませんが、Stable Diffusion XL v1.0で作成しています。）

## 【課題１】方針の検討
まず要望1の「3つのフォルダに分類」について考えます。

どのような分類の仕方が適切かを考え、その分類方法のアイディアを記載してください。
なぜそのように考えたかも教えてください。


## 【課題２】実装
課題１の分類アイディアに基づいて、実際に「dataset.zip」の画像を分類するプログラムを作成してください。
この時、要望２の「新しい画像も自動的に分類」しなければいけないことに注意してください。


## 【課題３】考察
実際にあなたがこのプロジェクトを担当したとして、注意する点や相談者に進言する点があれば教えてください。

例：「・・・という点は相談者に確認する必要がある」「自動化を実装する際は・・・に注意する必要がある」


↑↑↑問題ここまで↑↑↑

提出の形式は、文章で説明＋プログラムの提出あたりにするか、Colaboratoryでテンプレート作っておいてこれで提出してね、にするかあたり。



# 想定解と採点ポイント

ある意味ここから本編。

## 課題１：方針

### 出題意図
- 素材画像は一つの基準で３分割にはしにくい意地悪な構成にしています。
- なんらか理由をつけ、それに沿って分け方が定義できていればどんな分け方でも問題なし

※実務ではこの時点で疑問点や確認事項をクライアントとすり合わせたりが重要になりますが、今回は課題３に回します。

### 回答サンプル

- ３つに分類する方法としてイラストで描かれている対象が「１匹の猫」か「１匹の犬」か「その他」かで分ける方法を提案します

- 理由として、Webコンテンツ作成時にイラストを探すことを想定すると、何の動物のイラストを使うかをまず考え、その後画風の選択などがあるだろうと考えました。そのため最初の分類としては動物の種類で分けておくのがよいだろうと判断しました。
	- また複数の動物が映っている場合は種類が混ざってしまう可能があるため「その他」としました
- この分け方を適用した場合、目視で判断したところ
	- １匹の猫・・・24枚
	- １匹の犬・・・29枚
	- その他　・・・20枚
	となり、大きな偏りも生まれません

- ただしこの分け方が本当に適切かは利用者に確認が必要と考えます → 課題３

### 想定解
他にも以下のような観点で３つに分類することが考えられます

- 映っている動物の種類により分類
	- 犬・猫・判別不能　など
- 映っている動物の数により分類
	- １・２・３以上　など
- 画像のサイズで分類
	- ５種類あるが、小・中・大に分ける　など
- 画風で分類
	- リアルな写真・イラスト調・３Dにデフォルメ　など
- 色合いや背景で分類
	- 無地背景・朝っぽい・夕方っぽい　など

どれが良い悪いはなく、依頼者の活用シーンを考え定義していれば問題なし。

### 採点ポイント
採点は加点法を想定。何ができていないとダメというのではなく、考慮できているものが多いほど点数高くしていきます。（想定解で思いついてないのもあるだろうし）


|点数(最大)|評価ポイント|
|---|---|
|10|「なぜそのように分けたか」が説明されており論理が通っている|
|10|実際に相談者が活用する場面を想定できているか|
|5|そのように考えた過程を数値やグラフを使って説明しているか|

３つ目はオプションぐらいの扱い。画像サイズの分布とか犬猫が何枚あったとか。

### NGな例
加点法とはいえこれは困る、の例

- 条件無視
	- ３分割は適さないので以降４分割として考えます　など　※
- 画像見てない
	- ファイル名で並べて順番に３つに振り分けていきます　など

※気持ちはわからんでもないですが、試験ではやめてね。課題３に続く。


### スニペット？
ここQiitaなのでなんらかプログラムも書いておこう。

Colabratory上で画像を任意のサイズで表示するの、案外めんどくさいのでメモしておきます。


```python:OpenCVバージョン
# ds_test/ 内のpngファイルを全部表示
import cv2
import pathlib
from IPython.display import Image, display


def imshow(img):
	img = cv2.imencode('.png', img)[1]
	display(Image(img))

img_path = pathlib.Path('dataset')

for p in img_path.glob("*.png"):
	img = cv2.imread(str(p))
	imshow(cv2.resize(img, dsize=(200, 200)))

```

```python:Pillowバージョン
import PIL.Image

for p in img_path.glob("*.png"):
	img = PIL.Image.open(p)
	display(img.resize((200,200)))

```


## 課題２：実装
### 出題意図

- 実装力と分析手順の理解を見る
- 分類精度によって減点はしない（加点はありえる）
- プログラムが完成していなかった場合も大きな減点はしない
	- 採用側としては「手順が妥当であり、時間をかければor有識者の多少のサポートがあれば完成できる」と判断できる場合は戦力になると考える（と思う）

### 回答サンプル

- 画像に映る動物が「１匹の犬」か「１匹の猫」か「その他」かを判別するためにCLIPを利用したゼロショット学習を用いる
	- 対象画像の殆どは1or2匹の猫or犬で構成されているため、まずは以下の５種類に分けた上で、「１匹の猫」「１匹の犬」「その他」の３分類にする。
		- "one dog", "one cat", "two dogs", "two cats", "other animals"

（以下のプログラムはColaboratoryで実行を想定）
```python: 初期設定色々
# Colabratoryで動作を想定
# ファイルを解凍しておく
!unzip dataset.zip

# CLIPの導入
!pip install open_clip_torch

import torch
from PIL import Image
import open_clip

import glob, pathlib
from IPython.display import display

import pandas as pd
from sklearn.metrics import confusion_matrix
from sklearn.metrics import f1_score

# モデルのロード
model, _, preprocess = open_clip.create_model_and_transforms('ViT-B-32', pretrained='laion2b_s34b_b79k')
tokenizer = open_clip.get_tokenizer('ViT-B-32')
```

```python: clipのzeroshot
img_path = pathlib.Path('dataset')

# 分類を実施する5つのラベルを定義
labels = ["one dog", "one cat", "two dogs", "two cats", "other animals"]

for p in img_path.glob("*.jpg"):
	img = Image.open(p)
	
	image = preprocess(img).unsqueeze(0)
	text = tokenizer(labels)
	
	with torch.no_grad(), torch.cuda.amp.autocast():
		image_features = model.encode_image(image)
		text_features = model.encode_text(text)
		image_features /= image_features.norm(dim=-1, keepdim=True)
		text_features /= text_features.norm(dim=-1, keepdim=True)
		
		text_probs = (100.0 * image_features @ text_features.T).softmax(dim=-1)

	# 最もスコアが高いラベルを特定
	idx = text_probs[0].tolist().index(max(text_probs[0]))
	
	display(img.resize((200,200)))
	print("Label probs:", labels[idx])
```

シンプルなゼロショット分類ならほぼ[CLIPのサンプルコード](https://github.com/openai/CLIP)そのままでいけます。
こんな感じで結構きっちり分類してくれます。

![Pasted image 20230917222247.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83530/971b7f01-d121-b0bb-9ca0-17448446611e.png)

５分類したものを今回定義した３分類に集約し直して、評価と考察に移ります。

- 評価
	- 独自に学習は行っていないため、学習/テスト画像に分けておらず全てテスト画像として扱う
	- 最初に目視で"one dog", "one cat", "other"をラベル付けしておき、どれだけ一致しているかを混同行列とF1スコアで評価する
        - 予測した結果をデータフレームにまとめると、、、
            - `pred` : 上のプログラムの５分類
            - `pred_3ctg` : ３つに集約
            - `ans` : 目視で付けた答え
        ![Pasted image 20230917223409.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83530/e9ef3f97-9347-e491-33e4-5ffb98160307.png)
        こんな感じ

評価結果はこう↓

```python: 評価
display(confusion_matrix(df["ans"], df["pred_3ctg"]))
f1_score(df["ans"], df["pred_3ctg"],average='micro')

↓

array([[22,  2,  0],
       [ 5, 24,  0],
       [ 0,  0, 20]])

0.9041095890410958
```
（F1スコア0.9ってすごいな。CLIPすごいなぁ。）

- 考察
	- 答えを外している画像をリストップして確認します
	- 犬か猫が区別つきにくいものや改めて見ると答えの方が間違ってそうだと思えるものなどが多数
	- ![Pasted image 20230917224547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83530/fb8fc439-8af1-4f59-cd3a-12bd535ebc0d.png)


（ちなみに画像を作る際もわざと「犬と猫の合体」とか「猫と狐の合体」などを指定して、わかりにくいものを混ぜています。）

### 想定解
分け方をどうするかによっても手法は大きく変わるのでこの限りではないが、思いつくものをリストアップ。

- 教師あり＆独自学習型アプローチ
	- おそらく最も多いであろうアプローチ方法
	- 事前準備
		- *簡単のため、ここでは「犬・猫・その他」に分けると仮定*
		- 画像に対して「犬・猫・その他」のラベル付けを行い、正解データを作っておく
		- 画像を学習用・テスト用に分けておき、「新しく追加される画像への対応」を検証できる状態にしておく	
	 - 基本的な流れ
		- 分類問題として解く
		- 学習用データでモデルを学習
		- テスト用データを当てはめ分類結果を出力
		- 分類結果の精度を評価
			- 混同マトリックス
			- F1スコアなどなんらか
	- モデルいろいろ
		- CNNとかでもいいし、シンプルに１次元ベクトルにしてロジスティック回帰に入れるだけとかでも
- 教師なしアプローチ
	- あまり多くなさそうだが教師なしで考えるアプローチもあり得る
	- 「画像の色合いで分類する」とした場合、どのような色合いの画像が多いか・どう３分類すればいいかをクラスタリングしてみて基準をつくる、など
- 既存の学習済みモデル活用型のアプローチ
	- 既存モデルを活用して解くパターン
		- *実務的には工数・精度面を考えるとこれがいんじゃなかろうかと個人的には思っている*
	- 何らかの分類モデルに入れてその結果を使う、ファインチューニングする
	- YOLOなどを利用して、動物の数や種類を特定して利用する
	- CLIPなどを利用して、ZeroShot学習により任意のラベル付けを行って利用する　※回答サンプルの
	- 追記：この記事を書いてる途中に少し話題になっていた[Heron](https://www.itmedia.co.jp/news/articles/2309/07/news175.html)みたいな、画像に対して問い合わせできるモデルとか使うとさらにスマートかもしれない
- その他
	- 外部のAPIに投げて情報を得る・分類させる、など
		- モデルの学習なりプログラムなりやってほしい気持ちはあるものの、なくはないなと


|点数(最大)|評価ポイント|
|---|---|
|5|1-1. MLを利用しないが実現できている（画像サイズのみから機械的に分類、など。絶対MLやれとは書いてないので・・・）|
|15|1-2. MLを利用して実現できている|
|5|妥当な分類結果となっている。精度ではなく課題１の回答と整合性があるか。|
|10|学習用・テスト用を分け正しい手順で検証しているか|
|5|2-1. 最低限目視で評価を行っているか。|
|10|2-2. 何らかの精度指標や数値を用いて評価しているか|
|10|結果に対する考察があるか。課題３に回していてもOK|

※1-1と1-2、2-1と2-2はどちらかを選ぶ。2重加点はしない。


## 課題３：考察

### 出題意図
- 実際のプロジェクトを想定して、相談者の要望の深堀やより良くするための提案・システム化の懸念点などが意識できているかを問う問題
- 指摘の中身もそうだが、どういった項目が多いかでその人の性質も見えてきそうな期待あり（システム作り好きそうとかコンサル向きそうとか）

### 想定解
- 前提条件の確認
	- 3つに分類するのは必須か
	- 分類数を将来的に増やしたり、変更したりする可能性はあるか
	- 分類精度はどの程度必要か
- データに関する確認
	- 対象の動物は増えるか
	- 画像のサイズや縦横比は増えるか
	- 素材はどのぐらいのペースで増えていくか
- システム化に関する確認
	- コストの制約はあるか
	- 実行速度の制約はあるか
	- 実行環境はどこか（相談者のローカルで行う必要はあるか）
	- 相談者以外に利用者はいるか（素材は共用か）
- 提案
	- フォルダをN個にするとより良いのではないか
	- フォルダ分けではなくタグ管理はどうか
など

### 採点ポイント

ここはもう都度検討が多くなりそうではある。

|点数(最大)|評価ポイント|
|---|---|
|1.5 x 件数|妥当な指摘数によって加点。最大15点|
|5|大項目（前提の確認・データ自体の確認・システム化/自動化に関する確認）をすべて網羅している|
|5|適切な提案（進言）がなされているか|
|-|想定外のものは都度検討|

# まとめ

長すぎた。

さて、仮想試験作ってみたものの、ちゃんと機能するのか・難易度どうなのかはこれから検証していかないといけないなというフェーズ。

ただデータサイエンスというか分析系の業務はこういう感じで進んでいくのものも多いので、特に異業種から移ろうと思ってる方々は一つの練習課題として遊んでみてはどうでしょうか。