解説
F1が高いのに少ない分類を外す?AI評価のmicroとmacroを分けて読もう
3種類の架空の分類結果からmicro-F1とmacro-F1を検算。分類ごとの件数と失敗を残し、集計方法による見え方の違いを確認する。

AIの分類結果を比べたら、「F1は0.80」と書かれていた。別の表では同じ結果なのに約0.31。数字だけを見ると矛盾していそうだけれど、まとめ方が違うのかもしれないよ。
今回は適合率・再現率の基本を知っている人向けに、複数の分類を一つのF1へまとめるmicroとmacroを比べる。10件の架空データを使い、実際のAIモデルの性能を測った値ではないよ。
まず、分類ごとの失敗を残す
一つの入力にはA・B・Cのいずれか一つだけを付ける。正解ラベルは人があらかじめ用意したものとし、今回はその正しさ自体の評価は扱わない。
| 正解 | 件数 | 予測したラベル |
|---|---|---|
| A | 8 | 8件ともA |
| B | 1 | A |
| C | 1 | B |
Aは8件とも当たっているが、少ないB・Cはそれぞれ外している。全体では8/10が正解だね。この表を消して集計値だけにすると、どの分類を落としたかが見えにくくなる。
F1の材料は、拾えた件数と二種類の誤り
各ラベルについて、そのラベルを「対象」、それ以外を「対象外」と考える。
- TP:対象を対象と予測できた件数。
- FP:対象外を対象と予測した件数。
- FN:対象を対象外と予測して見落とした件数。
F1は適合率と再現率の調和平均で、件数からは 2×TP÷(2×TP+FP+FN) と計算できる。今回AはTP=8・FP=1・FN=0なので16/17、BとCのF1は0になるよ。
| ラベル | TP | FP | FN | F1 |
|---|---|---|---|---|
| A | 8 | 1 | 0 | 約0.9412 |
| B | 0 | 1 | 1 | 0 |
| C | 0 | 0 | 1 | 0 |
microは件数を合算し、macroは各分類を同じ重みで平均
micro-F1は、全ラベルのTP・FP・FNを合算してからF1を計算する。今回はTP=8・FP=2・FN=2で16/20=0.80だよ。
macro-F1は、先にラベルごとのF1を求め、それらを同じ重みで平均する。(16/17+0+0)÷3で約0.3137。件数が少ないB・Cも、それぞれAと同じ一分類として数えているんだ。
microが「間違い」でmacroが「正解」という話ではない。入力全体での出来を見たいか、少ない分類の出来も同じ重みで見たいかで、必要な情報が変わる。どちらを採用しても、分類別の件数と結果は残しておこう。
Pythonで、同じ入力から両方を出す
次は標準ライブラリだけでの検算。scikit-learnのモデルを学習・実行するコードではないよ。
from fractions import Fraction
truth = ["A"] * 8 + ["B", "C"]
pred = ["A"] * 9 + ["B"]
labels = ["A", "B", "C"]
def f1(tp, fp, fn):
denominator = 2 * tp + fp + fn
return Fraction(2 * tp, denominator) if denominator else Fraction(0)
counts = []
for label in labels:
tp = sum(t == label and p == label for t, p in zip(truth, pred))
fp = sum(t != label and p == label for t, p in zip(truth, pred))
fn = sum(t == label and p != label for t, p in zip(truth, pred))
counts.append((tp, fp, fn))
micro = f1(*(sum(values) for values in zip(*counts)))
macro = sum((f1(*values) for values in counts), Fraction(0)) / len(labels)
print(f"micro={float(micro):.4f}")
print(f"macro={float(macro):.4f}")
出力はmicro=0.8000、macro=0.3137。truthとpredは同じ長さで対応する順序も一致しているという前提だよ。実データでは長さ・IDの対応・ラベルの範囲を先に検査しよう。
このコードでは分母0のF1を0としている。存在しないラベルも集計に含めるのか、分母0をどう扱うのかは、使用する評価ツールの設定とそろえる必要がある。scikit-learnの公式資料でもaverageの種類、labels、zero_divisionを区別しているよ。
AIへ集計を頼む前に固定すること
「評価対象はA・B・Cのすべて。一件につき一ラベル。micro-F1とmacro-F1を両方求め、ラベル別TP・FP・FN・件数も残す。未知のラベルや件数の不一致は集計せず報告する」と指定すると、照合する材料が明確になるね。
今回のような全クラスを含む単一ラベル分類ではmicro-F1は正解率と一致する。複数ラベルを同時に付ける場合や、一部のラベルだけを評価する場合へ、その関係をそのまま持ち込まないようにしよう。
編集部では2026年11月1日にscikit-learnの公式仕様を確認し、上の架空例と全件正解・全件不正解・分母0のケースをPython 3.12.14で検算した。基本の適合率と再現率、別の問いである人数を重みにする平均も合わせて読めるよ。

みんなのコメント 0
感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。
最初のコメントをどうぞ。