0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

「この画像、まとめて整理できたらなぁ…」
イラストや写真をフォルダに詰め込んでいくと、検索不能なカオス地獄になりますよね。そんなときに出会ったのが SmilingWolf さんのWD EVA02-Large Tagger v3
聞くところによると「画像を食わせると、勝手にタグを山盛り付けてくれる」らしい。これは試さない手はない!


モデルって何者?

・Hugging Face 公開の 自動タグ付けAI
出力カテゴリは3つ:
 ・rating → safe / questionable / explicit
 ・character → キャラ名
 ・general → 髪色・服・背景など
・学習元はおなじみDanbooru データセット

要するに、「人間がつけるタグをほぼ自動でやってくれるスゴいやつ」です。


使ってみた

Hugging Face からモデルを取ってきて、画像をアップロード。
(フォルダに16枚放り込みました📂)
1a.jpg
2a.jpg
3a.jpg
A.png
B.png
B2.png
B3.png
C.png
いぬ.jpg
いぬ2.jpg
いぬ3.jpg
いぬ5.jpg
いぬ6.jpg
クエリ.png

前処理して → ONNX で推論 → CSV に保存。この流れを作ってスイッチオン!

処理完了のメッセージ:

results len: 16
保存完了: 400 行

おぉ……! 16枚の画像から一気に400タグ が抽出されました。

サンプルを覗くと:

    image category                tag   score
0  B2.png  general         monochrome  0.7211
1  B2.png  general   black_background  0.6906
2  B2.png  general          greyscale  0.6878
3  B2.png  general          no_humans  0.6707
4  B2.png  general  simple_background  0.6636
5  B2.png  general               solo  0.6372
6  B2.png  general            general  0.6319
7  B2.png  general         text_focus  0.6255
8  B2.png  general              1girl  0.6187
9  B2.png  general               dark  0.6041

「モノクロ」「背景黒」「1girl」…人が説明してるみたいにタグが並んでる!

気づいたこと

・rating タグが便利
→ Safe / Questionable が自動判定。フィルタリングに使える。
・general タグは超細かい
→ 髪色・構図・背景まで出る。検索用データベースにピッタリ。
・character タグはデータセット依存
→ 知ってるキャラは出るけど、新キャラはもちろん出ない。

使い道いろいろ

🔍 画像整理:何千枚あっても一瞬でタグ検索可能に
🚫 コンテンツフィルタ:safe / explicit の自動仕分け
🎨 生成AIのプロンプト支援:出力されたタグをそのまま Stable Diffusion に投げると「元画像っぽい雰囲気」で再生成できる
📊 傾向分析:頻出タグを数えると「自分の画像フォルダの趣味傾向」が丸見えになる(笑)


手順

1. モデルとタグ辞書をダウンロード

from huggingface_hub import hf_hub_download

REPO_ID = "SmilingWolf/wd-eva02-large-tagger-v3"
onnx_path = hf_hub_download(REPO_ID, "model.onnx")
tags_path = hf_hub_download(REPO_ID, "selected_tags.csv")

2. 画像をアップロード

import os, shutil
from google.colab import files

os.makedirs("images", exist_ok=True)
uploaded = files.upload()
for name in uploaded.keys():
    shutil.move(name, f"images/{name}")

3. 前処理関数

import numpy as np
from PIL import Image

IM_SIZE = 448
MEAN = np.array([0.485,0.456,0.406], dtype=np.float32)
STD  = np.array([0.229,0.224,0.225], dtype=np.float32)

def preprocess(img: Image.Image):
    img = img.convert("RGB").resize((IM_SIZE, IM_SIZE), Image.BICUBIC)
    x = np.array(img).astype(np.float32) / 255.0
    x = (x - MEAN) / STD
    return x  # (448,448,3)

4. タグ一覧の読み込み

import pandas as pd

tags_df = pd.read_csv(tags_path)
# category が数値のこともあるので変換
cat_map = {0:"general",1:"character",2:"rating"}
tag_names = tags_df["name"].tolist()
tag_cats = [cat_map.get(int(c), str(c)) for c in tags_df["category"].tolist()]

5. 推論準備(ONNX Runtime)

import onnxruntime as ort

providers = ["CUDAExecutionProvider","CPUExecutionProvider"]
sess = ort.InferenceSession(onnx_path, providers=[p for p in providers if p in ort.get_available_providers()])
input_name = sess.get_inputs()[0].name
output_name = sess.get_outputs()[0].name

6. タグ抽出関数

THRESH = {"rating":0.35, "character":0.50, "general":0.35}
TOPK = 25

def pick_tags(probs_vec, want_cat):
    idxs = [j for j,c in enumerate(tag_cats) if c==want_cat]
    sub  = [(tag_names[j], float(probs_vec[j])) for j in idxs]
    picked = [t for t in sub if t[1] >= THRESH[want_cat]]
    if not picked:
        sub.sort(key=lambda x:x[1], reverse=True)
        picked = sub[:TOPK]
    else:
        picked.sort(key=lambda x:x[1], reverse=True)
        picked = picked[:TOPK]
    return picked

7. 推論ループ

import glob
from tqdm import tqdm

results = []
batch, names = [], []
BATCH = 8
image_paths = sorted(glob.glob("images/*"))

def emit_batch(batch, names):
    if not batch: return
    x = np.stack(batch, axis=0)
    logits = sess.run([output_name], {input_name: x})[0]
    probs = 1/(1+np.exp(-logits))
    for i, fname in enumerate(names):
        p = probs[i]
        results.append({
            "image": fname,
            "rating":    pick_tags(p,"rating"),
            "character": pick_tags(p,"character"),
            "general":   pick_tags(p,"general"),
        })

for fp in tqdm(image_paths):
    img = Image.open(fp)
    batch.append(preprocess(img))
    names.append(os.path.basename(fp))
    if len(batch) >= BATCH:
        emit_batch(batch, names)
        batch, names = [], []
emit_batch(batch, names)

print("results:", len(results), "images processed")

8. 保存

rows = []
for r in results:
    for cat in ("rating","character","general"):
        for tag,score in r[cat]:
            rows.append({"image":r["image"],"category":cat,"tag":tag,"score":round(score,4)})

df = pd.DataFrame(rows)
df.to_csv("wd_eva02_results.csv", index=False)

import json
with open("wd_eva02_results.json","w",encoding="utf-8") as f:
    json.dump(results, f, ensure_ascii=False, indent=2)

print("保存完了:", len(df), "行")
print(df.head(10))

実験してみた感想

正直、「16枚で400タグ」 って数字に驚きました。
タグの粒度が細かいので、ただ整理するだけじゃなく「AIに自分の絵柄を言語化させる」感覚もあります。
やっていて面白いのは、自分では気にしてなかった特徴をタグ化してくれるところ。「あ、この絵は背景黒で見栄えしてたんだ!」みたいな再発見がありました。

まとめ

WD EVA02-Large Tagger v3は「自動タグ職人」
・Colabで動かすのは簡単
・小規模実験でも「大量のタグ」が出てきてワクワクする
・整理・検索・生成AI連携まで用途は広い


フリーランスエンジニアです。
AIや画像生成の記事色々書いているのでプロフィール見てみてください。

もし以下のようなご要望をお持ちでしたらお気軽にご相談ください。
AIサービスを開発したい、ビジネスにAIを組み込んで効率化したい、AIを使ったスマホアプリを開発したい、
ARを使ったアプリケーションを作りたい、スマホアプリを作りたいけどどこに相談したらいいかわからない…

いずれも中間コストを省いたリーズナブルな価格でお請けできます。

お仕事のご相談はこちらまで
rockyshikoku@gmail.com

機械学習やAR技術を使ったアプリケーションを作っています。
機械学習/AR関連の情報を発信しています。

Twitter
Medium
GitHub

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?