pixai-tagger-v1.0 / figures /plot_benchmark.py
trojblue's picture
initial commit
f33cfdb
Raw History Blame Contribute Delete
5.78 kB
"""Render the supplied common-tag benchmark with Python/Matplotlib.
Contract: quantitative grid + tradeoff scatter; 183 mm wide; point estimates
only (no uncertainty estimates supplied). Keep SVG/PDF text editable. Source
values are in benchmark.csv; no predictions or metrics are recomputed here.
"""
from pathlib import Path
import csv
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
ROOT = Path(__file__).resolve().parent
with (ROOT / 'benchmark.csv').open() as f:
rows = list(csv.DictReader(f))
plt.rcParams.update({
'font.family': 'DejaVu Sans', 'font.size': 7,
'axes.spines.top': False, 'axes.spines.right': False,
'axes.linewidth': .6, 'axes.labelcolor': '#303942',
'xtick.color': '#46515C', 'ytick.color': '#46515C',
'svg.fonttype': 'none', 'pdf.fonttype': 42,
'legend.frameon': False, 'savefig.facecolor': 'white',
})
GOLD, BLUE, GREY = '#E8B52A', '#86A7BE', '#AAA69D'
colors = [GOLD if r['model'] == 'PixAI Tagger v1.0' else BLUE if r['model'] == 'PixAI Tagger v0.9' else GREY for r in rows]
labels = [r['model'].replace('AnimeTIMM ', '') for r in rows]
def save(fig, stem):
for ext in ('png', 'svg', 'pdf'):
fig.savefig(ROOT / f'{stem}.{ext}', dpi=300)
plt.close(fig)
fig, axes = plt.subplots(1, 3, figsize=(183/25.4, 106/25.4), sharey=True)
fig.subplots_adjust(left=.28, right=.94, top=.75, bottom=.18, wspace=.40)
fig.text(.035, .94, 'General tagging on a shared vocabulary', fontsize=12, weight='bold')
fig.text(.035, .875, 'PixAI Tagger v1.0 leads all three General metrics among the eight models shown.', fontsize=7.5)
for i, (ax, key, title, limits) in enumerate(zip(axes,
['general_micro_f1','general_macro_f1','general_map'],
['Micro F1','Macro F1','mAP'],[(.55,.705),(.19,.425),(.20,.415)])):
for y, row in enumerate(rows):
value = float(row[key])
ax.scatter(value, y, s=34 if y == 0 else 23, marker='D' if y == 0 else 'o', color=colors[y], zorder=3)
ax.annotate(f'{value:.4f}', (value,y), xytext=(5,0), textcoords='offset points', va='center', fontsize=6.2, weight='bold' if y==0 else 'normal')
ax.set_title(f'{chr(97+i)} {title}', loc='left', fontsize=8, weight='bold', pad=13)
ax.set_xlim(*limits)
ax.set_ylim(len(rows)-.5,-.5)
ax.set_yticks(range(len(rows)), labels)
ax.tick_params(axis='y',length=0, pad=9)
ax.spines['left'].set_visible(False)
ax.grid(axis='x',color='#EEEAE1',linewidth=.6)
ax.set_xlabel('Score ↑')
ax.axhspan(-.45,.45,color='#FFF5D6',zorder=0)
axes[0].get_yticklabels()[0].set_weight('bold')
fig.text(.035,.06,'8,407 common General tags · 50,416 test images · thresholds selected on calibration only',fontsize=6.8,color='#46515C')
save(fig,'general_comparison')
fig, ax = plt.subplots(figsize=(183/25.4,105/25.4))
fig.subplots_adjust(left=.12,right=.95,top=.77,bottom=.21)
fig.text(.035,.94,'General quality and inference throughput',fontsize=12,weight='bold')
fig.text(.035,.875,'Tagger v1.0 has the highest General micro F1; the other measured models are faster.',fontsize=7.5)
offsets=[(9,17),(-9,9),(0,25),(-10,-23),(10,9),(0,-28),(9,0),(0,10)]
for i, (r, offset) in enumerate(zip(rows,offsets)):
x,y=float(r['b16_images_s']),float(r['general_micro_f1'])
ax.scatter(x,y,s=55 if i==0 else 30,color=colors[i],marker='D' if i==0 else 'o',zorder=3)
ax.annotate(labels[i],(x,y),xytext=offset,textcoords='offset points',
ha='left' if i in (0,4,6) else ('right' if i in (1,3) else 'center'),
va='center',fontsize=7,weight='bold' if i==0 else 'normal',
arrowprops={'arrowstyle':'-','color':'#BAC3CA','lw':.5} if i in (2,3,5) else None)
ax.set(xlim=(0,420),ylim=(.555,.69),xlabel='Batch-16 throughput (images/s) ↑',ylabel='General micro F1 ↑')
ax.grid(color='#EEEAE1',linewidth=.6)
ax.set_axisbelow(True)
fig.text(.035,.08,'H100 80 GB · forward + sigmoid + output copy to CPU · preprocessing excluded',fontsize=6.8,color='#46515C')
fig.text(.035,.035,'Configured precision differs by model. Point estimates only; uncertainty was not estimated.',fontsize=6.8,color='#46515C')
save(fig,'quality_throughput')
# Both releases use the same test split and shared tags for this evaluation.
with (ROOT / 'release_comparison.csv').open() as f:
releases = list(csv.DictReader(f))
fig, axes = plt.subplots(1, 2, figsize=(183/25.4, 90/25.4))
fig.subplots_adjust(left=.10, right=.96, top=.72, bottom=.24, wspace=.36)
fig.text(.035,.94,'PixAI Tagger: v0.9 to v1.0',fontsize=12,weight='bold')
fig.text(.035,.865,'Higher General and Character micro F1 on the same images and shared tags.',fontsize=7.5)
for ax, key, title, maximum in zip(axes,
['general_micro_f1','character_micro_f1'],['a General micro F1','b Character micro F1'],[1.0,1.0]):
values = [float(r[key]) for r in releases]
bars = ax.bar([0,1],values,color=[BLUE,GOLD],width=.48)
ax.set_xticks([0,1],['PixAI Tagger v0.9','PixAI Tagger v1.0'])
ax.set_ylim(0,maximum)
ax.set_title(title,loc='left',fontsize=8,weight='bold',pad=12)
ax.spines['left'].set_visible(False)
ax.tick_params(axis='y',length=0)
ax.grid(axis='y',color='#EEEAE1',linewidth=.6)
ax.set_axisbelow(True)
for index,(bar,v) in enumerate(zip(bars,values)):
ax.text(bar.get_x()+bar.get_width()/2,v+maximum*.035,
f'{v:.4f}',
ha='center',va='bottom',fontsize=9,weight='bold' if index==1 else 'normal')
fig.text(.035,.10,'50,416 test images · 8,407 shared General tags · 2,099 shared Character tags',fontsize=6.8,color='#46515C')
fig.text(.035,.045,'Thresholds selected on calibration only. Point estimates; no confidence intervals were estimated.',fontsize=6.8,color='#46515C')
save(fig,'release_comparison')