Discord

Checklist OAI 2025 Stage I · Task 2

Hallucination Detection

Polish title: Wykrywanie Halucynacji

Predict whether a language model's answer to a factual question is correct, using the answer, its tokens and four higher-temperature alternative answers with token probabilities.

  • NLP
  • Tabular
  • Binary classification (hallucination detection)
  • Polish original · English translation

The task

Language models can produce answers that sound plausible but are wrong; such answers are called hallucinations. The task is to detect hallucinations in answers to factual questions generated by a large language model.

Each example contains the question, the model's main answer, the tokens of that answer, four alternative answers generated by the same model at a higher temperature, their tokens and token probabilities, and the label is_correct stating whether the main answer is correct according to a trusted source. The questions and answers themselves are in English.

The contestant implements predict_hallucinations(sample), which returns a probability for each example; a model or algorithm may be built from the training data.

Abridged and translated by SOTA from the official Polish materials. The official statement has the exact rules, and it wins wherever this summary differs.

In English

This task was published in Polish. SOTA translated its 2 files into English on 16 September 2026. Only the words changed in the notebooks: markdown, code comments, messages and printed output. The code, file names and paths are the original's, so a translated notebook runs with the original data.

Read the task notebook in English 929 words and 6 code cells

Hallucination Detection

Embedded Photo

Image generated using the DALL-E model.

Introduction

Language models help us with everyday tasks such as proofreading texts, writing code or answering questions. They are also increasingly used in fields such as medicine and education.

But how can we know whether the answers they generate are correct? Language models do not always have complete knowledge of a given topic, and yet they can formulate answers that sound credible but are in fact misleading. We call such incorrect answers hallucinations.

Task

In this task you will tackle the detection of hallucinations in answers to factual questions generated by large language models (LLMs). You will analyse a dataset that will help to assess whether the answers generated by a language model are actually correct or contain hallucinations.

Each example in the dataset contains:

  • A question, e.g. "What is the main responsibility of the US Department of Defense?"
  • The language model's answer, e.g. "The main responsibility is the defence of the country."
  • Tokens associated with generating the answer.
  • Four alternative answers generated by the same model at a higher temperature.
  • Tokens of the alternative answers generated by the same model at a higher temperature.
  • Probabilities of the alternative answers generated by the same model at a higher temperature.
  • A label (is_correct) indicating whether the main answer is correct according to a trusted source.

Example:

[
    {
        "question_id": 34,
        "question": "What is the name of the low-cost carrier that operates as a wholly owned subsidiary of Singapore Airlines?",
        "answer": "Scoot is the low-cost carrier that operates as a wholly owned subsidiary of Singapore Airlines.",
        "tokens": [" Sco", "ot", " is", ..., " Airlines", ".", "\n"],
        "supporting_answers": [
            "As a wholly owned subsidiary of Singapore Airlines, <answer> Scoot </answer> stands as a low-cost carrier that revolutionized air travel in the region.",
            "Scoot, a subsidiary of <answer> Singapore Airlines </answer> , is the low-cost carrier that operates under the same brand.",
            "<answer> Scoot </answer> is the low-cost carrier that operates as a wholly owned subsidiary of Singapore Airlines.",
            "Singapore Airlines operates a low-cost subsidiary named <answer> Scoot </answer> , offering affordable and efficient air travel options to passengers."
        ],
        "supporting_tokens": [
            [" As", " a", ..., ".", "<answer>"],
            [" Sco", "ot", ..., " brand", ".", "\n"],
            ["<answer>", " Sco", ..., ".", "\n"],
            [" Singapore", " Airlines", ..., ".", "\n"]
        ],
        "supporting_probabilities": [
            [0.0029233775567263365, 0.8621460795402527, ..., 0.018515007570385933],
            [0.42073577642440796, 0.9999748468399048, ..., 0.9166142344474792],
            [0.3258324861526489, 0.9969879984855652, ..., 0.921079695224762],
            [0.11142394691705704, 0.960810661315918, ..., 0.9557166695594788]
        ],
        "is_correct": true
    },
    .
    .
    .
]
Data

The data available to you in this task are:

  • train.json - a dataset containing 2967 questions and answers.
  • valid.json - 990 additional questions.
Scoring Criterion

ROC AUC (Receiver Operating Characteristic Area Under Curve) is a measure of the quality of a binary classifier. It shows the model's ability to distinguish between two classes - here, a hallucination (false) and a correct answer (true).

  • ROC (Receiver Operating Characteristic): A plot showing the relationship between the True Positive Rate (sensitivity) and the False Positive Rate (1 - specificity) at various decision thresholds.
  • AUC (Area Under Curve): The area under the ROC curve, which takes values from 0 to 1:
    • 1.0: A perfect model.
    • 0.5: A random model (no ability to distinguish between the classes).

The higher the AUC value, the better the model performs at classification.

You can score between 0 and 100 points for this task. The score will be scaled linearly depending on the ROC AUC value:

  • ROC AUC ≤ 0.7: 0 points.
  • ROC AUC ≥ 0.82: 100 points.
  • Values between 0.7 and 0.82: scaled linearly.

Scoring formula:

Points={0for ROC AUC0.7100×ROC AUC0.70.820.7for 0.7<ROC AUC<0.82100for ROC AUC0.82\text{Points} = \begin{cases} 0 & \text{for } \text{ROC AUC} \leq 0.7 \\ 100 \times \frac{\text{ROC AUC} - 0.7}{0.82 - 0.7} & \text{for } 0.7 < \text{ROC AUC} < 0.82 \\ 100 & \text{for } \text{ROC AUC} \geq 0.82 \end{cases}

Constraints

  • Your solution will be tested on the Contest Platform without internet access and in an environment without a GPU.
  • The evaluation of your final solution on the Contest Platform must not take longer than 5 minutes without a GPU.
  • List of permitted libraries: xgboost, scikit-learn, numpy, pandas, matplotlib.

Submission Files

This notebook, completed with your solution (see the predict_hallucinations function).

Evaluation

Remember that during checking the FINAL_EVALUATION_MODE flag will be set to True.

You can score between 0 and 100 points for this task. The number of points you receive will be calculated on the (secret) test set on the Contest Platform using the formula given above, rounded to an integer. If your solution does not meet the above criteria or does not run correctly, you will receive 0 points for the task.

Starter Code

In this section we initialise the environment by importing the required libraries and functions. The prepared code will make it easier for you to work with the data efficiently and to build a proper solution.

######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################

FINAL_EVALUATION_MODE = False  # While checking your solution, we will change this value to True

import os
import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn as sk
from sklearn.metrics import roc_auc_score
import xgboost as xgb
import shutil

def download_data(train=("1TGEDaxw4GKfSq0fpqSk0wRpUSc8GgZN0", "train.json"),
                  valid=("1qrr7bZk6Uct8DeC-V8Bc1qD5su56ryFd", "valid.json")):
    """Downloads the dataset from Google Drive and saves it in the 'data' folder."""
    import gdown

    # Create or reset the 'data' folder
    if not os.path.exists('data'):
        os.makedirs('data')
    else:
        shutil.rmtree('data')
        os.makedirs('data')

    GDRIVE_DATA = [train, valid]

    for file_id, file_name in GDRIVE_DATA:
        # Download the file from Google Drive and save it in the 'data' folder
        url = f'https://drive.google.com/uc?id={file_id}'
        output = f'data/{file_name}'
        gdown.download(url, output, quiet=False)

        print(f"Downloaded: {file_name}")

# Download the data only if you are not in FINAL_EVALUATION_MODE
if not FINAL_EVALUATION_MODE:
    download_data()

Loading the Data

The code below will load the data and prepare it appropriately.

######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################

def load_data(folder='data'):
    # Load the data from the files
    train_path = os.path.join(folder, 'train.json')
    valid_path = os.path.join(folder, 'valid.json')

    with open(train_path, 'r') as f:
        train = json.load(f)
    with open(valid_path, 'r') as f:
        valid = json.load(f)

    return train, valid

train, valid = load_data("data")

print(json.dumps(train[0], indent=2))

print(f"\nTotal training examples: {len(train)}")
print(f"Total validation examples: {len(valid)}")

Scoring Criterion Code

Code similar to the code below will be used to evaluate the solution on the test set.

######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################

def compute_score(roc_auc: float) -> float:
    """
    Computes the score in points from the ROC AUC value.

    :param roc_auc: A float value in the range [0.0, 1.0]
    :return: The score in points according to the specified function
    """
    if roc_auc <= 0.7:
        return 0
    elif 0.7 < roc_auc < 0.82:
        return int(round(100 * (roc_auc - 0.7) / (0.82 - 0.7)))
    else:
        return 100


def evaluate_algorithm(dataset, algorithm, verbose=False):
    """
    Evaluates a hallucination detection algorithm on the given dataset.

    Parameters
    ----------
    dataset : list
        A labelled dataset, where each element is a dictionary containing the key 'is_correct'.
    algorithm : callable
        A function that takes a single example (a dictionary) and returns the probability of a hallucination.
    verbose : bool
        If True, prints additional information for each example and a summary.

    Returns
    -------
    roc_auc : float
        The area under the ROC curve (ROC AUC) for the predictions.
    """
    predicted_ys = [] # List storing the predicted hallucination probabilities

    for i, entry in enumerate(dataset):
        # Create a copy of the sample and remove the label to obtain unlabelled input data
        sample_unlabeled = dict(entry)
        sample_unlabeled.pop('is_correct', None)

        try:
            # Predict the probability for a single sample
            pred_prob = algorithm(sample_unlabeled)
            predicted_ys.append(pred_prob)

        except Exception as e:
            # If an error occurs, we set the probability to 0.5 by default
            predicted_ys.append(0.5)
            if verbose:
                print(f"Sample {i} => Error: {e}")

    predicted_ys = np.array(predicted_ys, dtype=np.float32)
    ys = []
    for entry in dataset:
        ys.append(1 if entry.get('is_correct') else 0)
    ys = np.array(ys, dtype=np.int32)

    # Compute the ROC AUC metric
    roc_auc = roc_auc_score(ys, predicted_ys)

    # Compute the final score from the ROC AUC
    points = compute_score(roc_auc)

    if verbose:
        print(f"\nNumber of samples: {len(dataset)}")
        print(f"ROC AUC: {roc_auc:.4f}")
        print(f"Score in points: {points}")

    return points

Your Solution

Place your solution in this section. Make changes only here!

# TODO: Use the training data to create a model or algorithm here.

def predict_hallucinations(sample):
    # TODO: Run your model or algorithm on this dataset.
    # TODO: Return a list of probabilities for each example in the dataset.

    prediction = 0.5
    return prediction

Evaluation

Running the cell below will let you check how many points your solution would score on the validation data. Before submitting, make sure that the whole notebook runs from start to finish without errors and without any user intervention after choosing the "Run All" option.

if not FINAL_EVALUATION_MODE:
    roc_auc = evaluate_algorithm(valid, predict_hallucinations, verbose=True)

During checking, the model will be saved as your_model.pkl and evaluated on the test set.

######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
if FINAL_EVALUATION_MODE:
    import cloudpickle

    OUTPUT_PATH = "file_output"
    FUNCTION_FILENAME = "your_model.pkl"
    FUNCTION_OUTPUT_PATH = os.path.join(OUTPUT_PATH, FUNCTION_FILENAME)

    if not os.path.exists(OUTPUT_PATH):
        os.makedirs(OUTPUT_PATH)

    with open(FUNCTION_OUTPUT_PATH, "wb") as f:
        cloudpickle.dump(predict_hallucinations, f)

Translated by SOTA. The Polish original is the official version and wins wherever the two differ. The dataset itself is in English. In the official solution, the saved plot images still show the original Polish titles and axis labels; the plotting code has been translated, so re-running it produces English labels. If you organise this olympiad and would like the translation removed, email [email protected] and we will take it down.

At a glance

You get
train.json (2,967 questions with answers) and valid.json (990 questions), downloaded from Google Drive.
You submit
This notebook with predict_hallucinations; during checking it is saved to your_model.pkl and evaluated on the test set.
Scoring
ROC AUC. Points = 0 for ROC AUC ≤ 0.7, 100 for ROC AUC ≥ 0.82, 100 × (ROC AUC − 0.7) / (0.82 − 0.7) in between.
Rules
  • Tested without Internet access and without a GPU.
  • Evaluation must take at most 5 minutes without a GPU.
  • Allowed libraries: xgboost, scikit-learn, numpy, pandas, matplotlib.
Format
Stage I (online), 17 February – 22 March 2025; up to 100 points per task (500 in total). Evaluated automatically on the Competition Platform (Platforma Konkursowa) on a hidden test set; points are rounded to an integer, and a notebook that fails the requirements or does not run scores 0.

Details

Year
2025, Online
Round
Stage I · Task 2
Language
Polish; English translation by SOTA
License
Not stated by the source