Skip to main content

AIED-Unplugged Dataset SDK

Python SDK for the AIED Preview Competition: the official graders, dataset loaders, exploration helpers, and scaffolding to run a Hugging Face model on a track.

pip install aied-unplugged            # graders, loaders, submission tooling
pip install 'aied-unplugged[all]'     # + kagglehub, datasets, matplotlib, transformers

The SDK is optional. It reads the published dataset and writes the published submission format.


The tracks

Track track Predict Ranked by
Automated Essay Scoring aes, essays competence_1 … competence_5 mean QWK
Mathematical Exam Diagnostic math-diagnostic, math diagnostic macro F1
Answer Sheet Detection answer-sheet, sheets answers cell accuracy

1. Grading

from aied_unplugged import evaluate

result = evaluate("math", "my_predictions.csv", "validation_reference.csv")
result.primary  # 0.41…  the ranking metric
result.metrics  # every metric the track reports
result.per_example  # one row per item: true, pred, correct

References can be a CSV or a validation split loaded from the dataset, with the same target columns either way.

from aied_unplugged import load_track, evaluate

validation = load_track("math", "validation")
evaluate("math", predictions, validation)

Metrics

Track Primary Definition Also reported
Essays mean QWK Quadratic weighted kappa per competence, averaged over the five. Uses the full six-point scale rather than the values in the split, so scores don't shift with the split. exact_match (per competence cell), rmse pooled over the five, qwk_competence_1…_5, rmse_competence_1…_5
Math macro F1 Unweighted mean of per-class F1 over the classes present in the reference. Predicting an absent class still costs you a false negative on the correct class. accuracy
Answer sheets cell accuracy Correct cells over total reference cells, so a 26-question sheet counts more than a 16-question one. sheet_exact_match (1 only when every question on a sheet matches), cell-level macro_f1

A constant prediction zeroes the QWK expected-agreement denominator. That case scores 1.0 when the prediction is right everywhere and 0.0 otherwise, instead of NaN. Macro F1 skips classes absent from the reference: averaging over the full taxonomy hands you a guaranteed zero for classes the split never uses, and the preview sample omits two of the thirteen.

Validation checks

A grader rejects the whole file instead of scoring the rows it can read. It raises SubmissionError naming the ids and columns at fault, without quoting a target value. Rejected files include:

  • an empty file, a missing column, or an extra column
  • a blank or duplicated identifier
  • an empty prediction
  • an ungraded row, or a missing row
  • a competence score off the 0/40/80/120/160/200 grid
  • a diagnostic outside the thirteen labels
  • an answer-sheet value outside the seven
  • a question_number that is not an integer (1.0 and "1" both fail)
  • a question repeated, missing, or not on the sheet

2. Loading

Getting the dataset

download() pulls the dataset from Kaggle. Needs aied-unplugged[kaggle].

from aied_unplugged import download, load_track

download()  # into the kagglehub cache, returns the path
load_track("math")  # the loaders below now find it
load_track("math", source="kaggle")  # same, in one call
load_track("math", source="hf")  # in-memory DatasetDict from the HF Hub

Later calls reuse the cache; pass force=True to re-fetch. download() also sets the default root for the process, so explore and verify run without root=.

Both mirrors carry the same release:

To unpack one by hand, put metadata/ and schema/ under a single directory, then name that directory with root=, with AIED_UNPLUGGED_DATA, or as competition-dataset/ in your working directory. root= wins over AIED_UNPLUGGED_DATA, which wins over download(), which wins over competition-dataset/.

Loading a track

from aied_unplugged import load_track, load_metadata, open_image

splits = load_track("essays")  # local release tree if present, else the Hub
splits.train, splits.validation, splits.test

train = load_track("math", "train")  # one split
image = open_image(train.iloc[0], "equation_image")

The local loaders return pandas DataFrames with the Parquet metadata and absolute image paths in <column>_path.

load_schema("math")  # the published taxonomy, fields and metrics
graded_ids("aes")  # the ids a submission must carry
question_counts()  # questions per graded answer sheet
sample_submission("aes")  # the published placeholder file
verify()  # re-check every SHA-256 in the release

3. Exploring

Needs aied-unplugged[explore] and a local copy of the dataset, from download() or from a mirror you unpacked yourself.

from aied_unplugged import explore

explore.summary()  # rows per track and split
explore.label_distribution("math")  # counts and shares, taxonomy order
explore.plot_label_distribution("math")
explore.describe_taxonomy("math")  # labels with their Portuguese source terms

explore.show_essay("essay-1-3")  # the page, with its five competence scores
explore.show_math("math-604-01")  # question above, student working below
explore.show_sheet(0)  # a sheet with what was marked
explore.render_math("math-604-01")  # the same pair as one PIL image

Name an item by id, by position, or by passing its metadata row.


4. Running a model

Needs aied-unplugged[models]. Two strategies, either one enough for a valid submission and a baseline score.

from aied_unplugged import models, submission

# Predict the training majority for everything.
predictions = models.majority_baseline("math")

# Zero-shot with a local vision-language model.
predictions = models.predict("math", "Qwen/Qwen2.5-VL-3B-Instruct", limit=20)

submission.write("math", predictions, "submission.csv")

predict prompts the model once per item with the track's images and parses the reply into the submission schema. An unparseable reply falls back to a safe default, so a run always ends with a gradeable file.

Fine-tuning covers the math track only, as image classification over the student's working:

trainer = models.finetune(model="google/vit-base-patch16-224-in21k", epochs=3)
predictions = models.predict_with_classifier(trainer, split="test")

Not included: essay or answer-sheet fine-tuning, multi-GPU, LoRA, hyperparameter search.


5. Submissions

from aied_unplugged import build, graded_ids, question_counts, validate, write

frame = build("answer-sheet", [{"sheet_id": "s1", "answers": {1: "A", 2: "Blank"}}])
report = validate(
    frame,
    "answer-sheet",
    expected_ids=graded_ids("answer-sheet"),
    expected_questions=question_counts(),
)
if not report.valid:
    print(report)
write("answer-sheet", frame, "submission.csv")

validate checks the exact column set, the identifiers, the competence scale, the diagnostic labels, and the JSON encoding of the answer-sheet column, including integer question numbers. With expected_questions, it also checks the question count on each sheet. build takes answers as a list of records or as a {question_number: label} mapping and encodes both to the same JSON.


Development

uv run --with-editable . --with pytest pytest -q
uv run ruff check .

Tests needing the release tree skip when competition-dataset/ is absent.

MIT licensed. The dataset itself is CC BY 4.0 and is published separately.

Release files for aied-unplugged 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aied-unplugged 0.2.0
File Size Uploaded
aied_unplugged-0.2.0.tar.gz 284.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aied-unplugged 0.2.0
File Interpreter ABI Platform
aied_unplugged-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 309.8 kB

Release files / aied_unplugged-0.2.0.tar.gz

Download URL aied_unplugged-0.2.0.tar.gz
Size 284.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8d7eec44f502cc777403e6d80b2c470e589f842c7f2b017ae468e05ec85eb993
BLAKE2b-256 checksum
How to use checksums
8873ddca92a81c7c3abc85b98432dfd47aa5737e838f4c51d20e83e32cd7d0ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / aied_unplugged-0.2.0-py3-none-any.whl

Download URL aied_unplugged-0.2.0-py3-none-any.whl
Size 25.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
af1699dd43f7e55ff34cf1eef1f8585ea5abfaa7c6ddc55b75229a41cfc85608
BLAKE2b-256 checksum
How to use checksums
98cc2461b5c09c3e5459c3dd48d14455d9a32ad3a4dde5ecd26c8ab25be9c360
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page