Classification benchmarking
Classification benchmarking evaluates how well a classification function has performed against a set of ground-truth labels.
In a typical workflow, you will first run classification and save the predictions to JSON. You can then compare those predictions against a separate JSON file containing the true labels.
Input format
Classification benchmarks compare two JSON files:
- a ground-truth file containing the true labels;
- a prediction file containing the predicted labels.
Classification saves predictions automatically to
save_dir / "classifications.json". Pass that file to the benchmark as
pred_path. The benchmark reads saved JSON and makes no model requests.
Both files should contain a list of objects with the following keys:
[
{
"img_path": "data/extracted-images/paper-1_0.png",
"label": 1
},
{
"img_path": "data/extracted-images/paper-1_1.png",
"label": 0
}
]
The benchmark expects the true and predicted files to contain the same images in the same order. The parent directories do not have to match, but the image filenames must match.
For example, this is valid:
true: data/ground-truth/paper-1_0.png
pred: data/predictions/paper-1_0.png
This is not valid:
true: data/ground-truth/paper-1_0.png
pred: data/predictions/paper-2_0.png
Binary classification benchmark
You can use
BinaryClassificationBenchmark
to benchmark a binary classification task:
from pathlib import Path
from flowde.benchmarks.classification.classification_benchmark import (
BinaryClassificationBenchmark,
)
benchmark = BinaryClassificationBenchmark(
true_path=Path("data/true-classifications.json"),
pred_path=Path("data/pred-classifications.json"),
)
The benchmark loads both JSON files, checks that they are valid, and compares the predicted labels with the true labels.
See the
BinaryClassificationBenchmark
API reference for full details of the parameters and results.
Binary benchmark results
After creating the benchmark object, you can inspect the results through its properties.
| Property | Description |
|---|---|
len(benchmark) |
Number of classified images in the benchmark. |
benchmark.result_list |
All classification results, one per image. |
benchmark.correct_predictions |
Results where pred matches true. |
benchmark.incorrect_predictions |
Results where pred does not match true. |
benchmark.accuracy |
Proportion of images where the predicted label matches the true label. |
benchmark.img_paths |
Image paths used in the benchmark. |
benchmark.trues |
True labels, in benchmark order. |
benchmark.preds |
Predicted labels, in benchmark order. |
benchmark.tp |
True positives: predicted 1, true label 1. |
benchmark.fp |
False positives: predicted 1, true label 0. |
benchmark.fn |
False negatives: predicted 0, true label 1. |
benchmark.tn |
True negatives: predicted 0, true label 0. |
benchmark.num_tp |
Number of true positives. |
benchmark.num_fp |
Number of false positives. |
benchmark.num_fn |
Number of false negatives. |
benchmark.num_tn |
Number of true negatives. |
benchmark.precision |
Of the images predicted positive, the proportion that were truly positive. |
benchmark.recall |
Of the truly positive images, the proportion predicted positive. |
benchmark.f1_score |
Harmonic mean of precision and recall. |
benchmark.tpr |
True positive rate. This is the same as recall. |
benchmark.fpr |
False positive rate: proportion of true negatives incorrectly predicted positive. |
benchmark.specificity |
True negative rate: proportion of true negatives correctly predicted negative. |
benchmark.tnr |
True negative rate. This is the same as specificity. |
For example, you can print the main benchmark metrics:
print(f"Accuracy: {benchmark.accuracy:.3f}")
print(f"Precision: {benchmark.precision:.3f}")
print(f"Recall: {benchmark.recall:.3f}")
print(f"F1 score: {benchmark.f1_score:.3f}")
You can also inspect the individual incorrect predictions:
for result in benchmark.incorrect_predictions:
print(result.img_path)
print(f"true: {result.true}")
print(f"pred: {result.pred}")
Each item in result_list, correct_predictions, incorrect_predictions,
tp, fp, fn, and tn is a
SingleClassificationResult
with:
| Attribute | Description |
|---|---|
img_path |
Path to the classified image. |
true |
Ground-truth label. |
pred |
Predicted label. |
Multiclass classification benchmark
For classifications where each image belongs to one of more than two possible
classes, you can use
MulticlassClassificationBenchmark:
from pathlib import Path
from flowde.benchmarks.classification.classification_benchmark import (
MulticlassClassificationBenchmark,
)
benchmark = MulticlassClassificationBenchmark(
true_path=Path("data/true-classifications.json"),
pred_path=Path("data/pred-classifications.json"),
labels=(
"consort_flowchart",
"other_flowchart",
"table",
"graph",
"other",
),
)
The benchmark loads both JSON files, checks that they are valid, and compares the predicted labels with the true labels.
The labels tuple declares the possible classes and must include every label
in the ground-truth and prediction files. The benchmark includes every declared
class in its counts and confusion matrix, even when no image has that label.
See the
MulticlassClassificationBenchmark
API reference for full details of the parameters and results.
Multiclass benchmark results
After creating the benchmark object, you can inspect the results through its properties.
| Property | Description |
|---|---|
len(benchmark) |
Number of classified images in the benchmark. |
benchmark.result_list |
All classification results, one per image. |
benchmark.correct_predictions |
Results where pred matches true. |
benchmark.incorrect_predictions |
Results where pred does not match true. |
benchmark.accuracy |
Proportion of images where the predicted label matches the true label. |
benchmark.img_paths |
Image paths used in the benchmark. |
benchmark.trues |
True labels, in benchmark order. |
benchmark.preds |
Predicted labels, in benchmark order. |
benchmark.labels |
Class labels supplied through labels. |
benchmark.confusion_matrix |
Number of images for each true-label and predicted-label pair. |
benchmark.num_per_true_class |
Number of images with each ground-truth label. |
benchmark.num_per_pred_class |
Number of images assigned each predicted label. |
benchmark.per_class_accuracy |
For each ground-truth class, the proportion of images classified correctly. |
For example, you can print the overall accuracy and class-level summaries:
print(f"Accuracy: {benchmark.accuracy:.3f}")
print(benchmark.confusion_matrix)
print(benchmark.num_per_true_class)
print(benchmark.num_per_pred_class)
print(benchmark.per_class_accuracy)
The confusion matrix is a nested dictionary indexed by true label, then
predicted label. For example,
benchmark.confusion_matrix["consort_flowchart"]["table"] counts CONSORT
flowcharts incorrectly classified as tables.
You can also inspect the individual incorrect predictions:
for result in benchmark.incorrect_predictions:
print(result.img_path)
print(f"true: {result.true}")
print(f"pred: {result.pred}")
Each item in result_list, correct_predictions, and incorrect_predictions
is a
SingleClassificationResult
with:
| Attribute | Description |
|---|---|
img_path |
Path to the classified image. |
true |
Ground-truth label. |
pred |
Predicted label. |
See the
MulticlassClassificationBenchmark
API reference for full details of the parameters and results.