← Все главы

07 · Версия материала 7

Как измерять качество вероятностного прогноза: NLL и перплексия

Преобразуйте вероятности, которые модель приписала наблюдаемым целевым токенам, в среднее NLL и перплексию, явно учитывая число токенов, границы документов и оцениваемые выборки.

Начните с вероятностей продолжений, которые действительно встретились

В главе 6 мы построили сглаженную биграммную модель. Для каждого текущего токена она позволяет вычислить распределение вероятностей возможных продолжений. При оценке наблюдаемого перехода нас интересует не максимальная вероятность в этом распределении, а вероятность следующего токена, который действительно встретился.

Пусть модель приписала двум последовательным целевым токенам такие вероятности:

[1/2,1/4][1/2,1/4]

Сначала попробуйте ответить без вычислений:

  1. Какой из двух токенов оказался для модели более неожиданным?
  2. Произведение вероятностей равно 1/81/8. Почему по нему нельзя напрямую сравнивать последовательности с разным числом целевых токенов?
  3. Какую вероятность модель должна была бы приписать обоим токенам, чтобы перплексия осталась той же?

Каждой вероятности pp сопоставим меру неожиданности lnp-\ln p. Здесь ln\ln означает натуральный логарифм. Для двух токенов получаем:

1/2ln2=0.693147180560,1/4ln4=1.386294361120.\begin{aligned} 1/2 &\longmapsto \ln 2 = 0.693147180560, \\ 1/4 &\longmapsto \ln 4 = 1.386294361120. \end{aligned}

Второй токен действительно более неожиданен: чем меньше вероятность, которую модель приписала наблюдаемому продолжению, тем больше lnp-\ln p.

При заданных контекстах произведение 1/2×1/4=1/81/2\times1/4=1/8 равно правдоподобию наблюдаемой последовательности из двух целевых токенов. Отрицательный логарифм превращает это произведение в сумму: ln((1/2)×(1/4))=ln(1/2)ln(1/4)=ln2+ln4-\ln((1/2)\times(1/4))=-\ln(1/2)-\ln(1/4)=\ln 2+\ln 4. Полученная сумма — отрицательное логарифмическое правдоподобие наблюдаемой последовательности; она равна ln8=2.079441541680\ln 8=2.079441541680.

Однако эта сумма относится к двум токенам. Каждый новый токен добавляет ещё один множитель к правдоподобию и ещё одно слагаемое к сумме. Поэтому у более длинной последовательности правдоподобие обычно меньше, даже если качество прогноза на один токен не изменилось. Чтобы убрать этот эффект длины, делим сумму на число целевых токенов. Получается среднее отрицательное логарифмическое правдоподобие (NLL): 2.079441541680/2=1.0397207708402.079441541680/2=1.039720770840 ната на целевой токен.

Перплексия равна экспоненте среднего NLL; в нашем примере это 2.8284271247462.828427124746. Искомая в третьем вопросе вероятность равна величине, обратной перплексии: 1/2.8284271247460.3535533905931/2.828427124746\approx0.353553390593. Если бы модель приписала её обоим токенам, геометрическое среднее приписанных вероятностей и перплексия остались бы теми же. Перплексию можно интерпретировать как эквивалентное число равновероятных продолжений, которое давало бы ту же среднюю меру неожиданности. Это не означает, что модель буквально выбирала из 2.8284271247462.828427124746 вариантов.

Два простых случая помогают сориентироваться на шкале. Если каждому наблюдаемому токену приписана вероятность 11, то ln1=0-\ln 1=0: среднее значение NLL равно 00, а перплексия — 11. Если распределение равномерно по словарю VV, вероятность каждого токена равна 1/V1/|V|. Тогда мера неожиданности равна lnV\ln |V|, среднее значение NLL также равно lnV\ln |V|, а перплексия — V|V|. При V=5|V|=5 получаем 1.6094379124341.609437912434 ната на целевой токен и перплексию 5.0000000000005.000000000000.

Наконец, рассмотрим ошибку, которую легко допустить при работе с документами разной длины. В первом документе один целевой токен с вероятностью 11, а во втором — три токена с вероятностью 1/41/4 каждый. Выпишем все четыре меры неожиданности:

[0,ln4,ln4,ln4].[0,\ln 4,\ln 4,\ln 4].

Правильное среднее равно (0+3ln4)/4=1.039720770840(0+3\ln 4)/4=1.039720770840, а перплексия — 2.8284271247462.828427124746. Если сначала вычислить среднее для каждого документа, а затем дать двум документам одинаковый вес, получится (0+ln4)/2=0.693147180560(0+\ln 4)/2=0.693147180560 и перплексия 22. Это неверно: во втором документе оцениваются три токена, а в первом только один. Совпадение правильного результата с предыдущим примером объясняется выбранными числами и не является общим правилом.

Сложите меры неожиданности, разделите сумму на число токенов и возьмите экспоненту

Полное правило записывается так:

=1Nt=1Nlogpt(zt),PPL=exp()\mathcal{L}=-\frac{1}{N}\sum_{t=1}^{N}\log p_t(z_t), \quad \operatorname{PPL}=\exp(\mathcal{L})

ztz_t — целевой токен, фактически наблюдавшийся на позиции tt, а pt(zt)p_t(z_t) — условная вероятность, которую модель приписала именно ему при заранее заданном правиле формирования контекста. В расчёт входит именно pt(zt)p_t(z_t), а не максимальная вероятность в строке и не энтропия всего распределения.

В этой главе log\log — натуральный логарифм, то есть logp=lnp\log p=\ln p. Для 0<p10<p\le1 значение lnp\ln p неположительно, поэтому знак минус делает меру неожиданности неотрицательной. Складываем её для всех целевых токенов во всех оцениваемых документах, после чего один раз делим сумму на NN — общее число этих токенов, а не число документов.

Величина \mathcal{L} измеряется в натах на целевой токен. Перплексия безразмерна: она получается после применения exp\exp к среднему значению NLL. Если все вероятности положительны, ту же связь можно записать так:

PPL=(t=1N1pt(zt))1/N.\operatorname{PPL}=\left(\prod_{t=1}^{N}\frac{1}{p_t(z_t)}\right)^{1/N}.

Это не независимая оценка: перплексия однозначно вычисляется из среднего NLL. Она равна геометрическому среднему обратных вероятностей, уже использованных при вычислении NLL. Эквивалентно, 1/PPL1/\operatorname{PPL} равно геометрическому среднему вероятностей, которые модель приписала наблюдаемым токенам.

Связь с кросс-энтропией требует двух уточнений. Для каждого заданного контекста определим эмпирическое распределение: наблюдавшемуся токену ztz_t оно приписывает вероятность 11, а всем остальным токенам — 00. Кросс-энтропия этого эмпирического распределения относительно распределения модели равна logpt(zt)-\log p_t(z_t). Если усреднить эти значения по тем же контекстам и тем же NN целевым токенам, получится эмпирическая кросс-энтропия, равная среднему NLL. Если эмпирическое распределение и правило формирования контекста не заданы явно, точнее говорить «среднее NLL на этих целевых токенах».

Нулевую вероятность следует отличать от некорректных входных данных. Если модель приписала наблюдаемому токену вероятность 00, она считает случившееся событие невозможным. Это не ошибка входных данных: мера неожиданности для этого токена, среднее NLL и перплексия равны положительной бесконечности. Код не заменяет ноль малым ϵ\epsilon.

Пустой список, NaN, положительная или отрицательная бесконечность во входных данных и значение вне [0,1][0,1] считаются ошибками. Реализация сначала проверяет весь список и только затем переходит к суммированию: например, для [0, NaN] она сообщит об ошибке во втором элементе, а не вернёт бесконечность из-за первого. Благодаря сглаживанию с α=1\alpha=1 биграммная модель из этой главы всегда возвращает положительные вероятности, поэтому её оценки конечны. Общее правило для p=0p=0 от этого не меняется.

Свяжите каждое обозначение с вычислением

ОбозначениеСмысл в этой главе
ttПорядковый номер наблюдаемого целевого токена, от 11 до NN. В небольшом примере t=1,2t=1,2.
ztz_tНаблюдаемый целевой токен на позиции tt, независимо от того, имеет ли он наибольшую вероятность в распределении модели.
pt(zt)p_t(z_t)Условная вероятность, которую модель приписала наблюдаемому целевому токену ztz_t при фиксированном правиле формирования контекста; это не частота токена в корпусе.
NNОбщее число оцениваемых целевых токенов во всех документах. В небольшом примере N=2N=2; это не число документов.
log\logНатуральный логарифм; в этой главе logx=lnx\log x=\ln x.
\mathcal{L}Среднее отрицательное логарифмическое правдоподобие (NLL), измеряемое в натах на целевой токен.
exp\expЭкспоненциальная функция с основанием ee, обратная натуральному логарифму.
PPL\operatorname{PPL}Перплексия, равная экспоненте среднего значения NLL.

В формуле позиции нумеруются с единицы, а в коде на Rust и трассировке — с нуля. Поэтому index=0 соответствует t=1t=1, а index=1t=2t=2. В обоих случаях речь идёт о тех же двух целевых токенах; index=0 не обозначает BOS или другой служебный маркер.

При оценивании корпуса BOS только задаёт контекст для первого содержательного токена и в NN не входит. EOS завершает каждый документ и учитывается как последний целевой токен. Документы обрабатываются по отдельности, поэтому искусственный переход EOS→BOS не попадает ни в сумму, ни в знаменатель.

Складывайте логарифмы вместо прямого перемножения вероятностей

В статье 1948 года Шеннон связал логарифмические меры с информацией, выбором и неопределённостью. В полном тексте статьи, во введении (страницы 1–2 в просмотрщике), он поясняет, что основание логарифма определяет единицу измерения. В разделе 6 (страницы 10–11 и в просмотрщике, и в печатной нумерации) показано, что для рассматриваемой им дискретной энтропии полная определённость даёт ноль, а равномерное распределение по nn исходам достигает максимума logn\log n. Это даёт нужный нам контекст из теории информации, но Шеннон не определяет среднее NLL или перплексию языковой модели.

Бенжио, Дюшарм, Венсан и Жовен (2003) описали нейросетевую вероятностную языковую модель. В тексте статьи, в §1.1 (печатная страница 1139), они записывают вероятность последовательности слов как произведение условных вероятностей следующих слов. В разделе 2 (печатная страница 1141) перплексия описана как геометрическое среднее обратных вероятностей и как экспонента среднего отрицательного логарифмического правдоподобия. В разделе 4 (печатные страницы 1148–1149) авторы явно указывают, какие токены входят в среднее перед сравнением моделей на корпусах Brown и AP News. Результаты относятся к этим экспериментам: из них нельзя вывести универсальное ранжирование моделей или правило сравнения перплексий при разных токенизаторах.

Для расчётов в этой главе мы выбираем натуральный логарифм, усредняем по всем целевым токенам, явно задаём поведение для p=0p=0 и некорректных входных данных, сохраняем границы документов и ограничиваем интерфейс оценки биграммной модели обучающей и валидационной выборками. Эти решения не следуют напрямую ни из одной из двух статей.

Перплексии можно сравнивать только тогда, когда совпадают токенизатор, состав словаря и соответствие идентификаторов токенам, правила учёта границ документов, способ формирования контекста и набор оцениваемых целевых токенов.

Программа на Rust наглядно показывает, зачем считать в логарифмическом пространстве. При прямом умножении 20002000 множителей 1/21/2 произведение становится меньше наименьшего представимого положительного значения f64 и округляется до 0.000e0. Сумма логарифмов остаётся конечной. Переход к логарифмам не меняет того, какая последовательность правдоподобнее: большему правдоподобию соответствует меньшая сумма lnp-\ln p. Меняется только численное представление — хрупкое произведение заменяется устойчивой суммой логарифмов.

Отдельный пример показывает, чего не видно, если учитывать только argmax\arg\max. Пусть наблюдался токен B:

q = [A: 0.60, B: 0.30, C: 0.10]
r = [A: 0.60, B: 0.20, C: 0.20]

В обоих распределениях argmax\arg\max указывает на A. Но наблюдаемому токену B распределение qq приписывает вероятность 0.300.30, а rr0.200.20; соответствующие значения NLL равны 1.2039728043261.203972804326 и 1.6094379124341.609437912434. Меньшее NLL для qq означает более высокую вероятность случившегося токена. Здесь сравниваются два распределения для одного и того же наблюдаемого токена. Это лишь иллюстрация ограничения argmax\arg\max, а не историческое утверждение о том, что перплексия когда-то повсеместно заменила проверку точности.

Реализуйте метрику один раз и примените её к уже построенной модели

Функция сначала отклоняет пустой срез, а затем проверяет каждую вероятность: значение должно быть конечным и лежать в [0,1][0,1]. Только после проверки всего среза функция по очереди добавляет меры неожиданности в общий накопитель. Метод finish делит их сумму на число целевых токенов и вычисляет экспоненту среднего. Для p=0p=0 мера неожиданности равна положительной бесконечности; при p=1p=1 она в точности равна +0.0.

Проверить вероятности, сложить меры неожиданности и вычислить среднее NLL и перплексию rust/crates/llm-from-scratch/src/metrics.rs#assigned-probability-metrics
#[derive(Clone, Copy, Debug, Default)]
struct MetricAccumulator {
    total_surprise: f64,
    target_count: usize,
}

impl MetricAccumulator {
    fn observe(&mut self, probability: f64) {
        self.target_count += 1;
        if probability == 0.0 {
            self.total_surprise = f64::INFINITY;
        } else if probability == 1.0 {
            // Preserve positive zero for exact learner-facing output.
            self.total_surprise += 0.0;
        } else {
            self.total_surprise += -probability.ln();
        }
    }

    fn finish(self) -> Result<MetricSummary, MetricError> {
        if self.target_count == 0 {
            return Err(MetricError::EmptyTargets);
        }

        let mean_nll = self.total_surprise / self.target_count as f64;
        Ok(MetricSummary {
            total_surprise: self.total_surprise,
            target_count: self.target_count,
            mean_nll,
            perplexity: mean_nll.exp(),
        })
    }
}

/// Scores probabilities assigned to observed targets using natural logarithms.
///
/// The complete slice is validated before accumulation. Both `0.0` and `-0.0`
/// are valid impossible-evidence values and produce positive infinity without a
/// clamp.
pub fn score_assigned_probabilities(probabilities: &[f64]) -> Result<MetricSummary, MetricError> {
    if probabilities.is_empty() {
        return Err(MetricError::EmptyTargets);
    }

    for (index, &probability) in probabilities.iter().enumerate() {
        if !probability.is_finite() || !(0.0..=1.0).contains(&probability) {
            return Err(MetricError::InvalidProbability { index, probability });
        }
    }

    let mut accumulator = MetricAccumulator::default();
    for &probability in probabilities {
        accumulator.observe(probability);
    }
    accumulator.finish()
}

Формула метрики реализована только в одном месте. Адаптер для биграммной модели принимает ссылку на уже построенную BigramModel, перебирает пары соседних токенов (windows(2)) отдельно внутри каждого закодированного документа и передаёт вероятность наблюдаемого следующего токена в тот же накопитель. Функция score_bigram_partition принимает значение ScoredPartition. В этом перечислении есть только варианты Train и Validation, поэтому функция не позволяет выбрать тестовую выборку.

Выбрать обучающую или валидационную выборку и рассчитать метрику по переходам внутри документов rust/crates/llm-from-scratch/src/metrics.rs#train-validation-scoring
/// A partition Chapter 7 is permitted to score.
///
/// The test partition is intentionally unavailable until Chapter 34.
///
/// ```compile_fail,E0599
/// use llm_from_scratch::metrics::ScoredPartition;
///
/// let _missing_variant: ScoredPartition = ScoredPartition::Test;
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ScoredPartition {
    Train,
    Validation,
}

impl ScoredPartition {
    const fn corpus_partition(self) -> Partition {
        match self {
            Self::Train => Partition::Train,
            Self::Validation => Partition::Validation,
        }
    }
}

/// Scores adjacent target transitions without refitting or joining documents.
pub fn score_bigram_partition(
    model: &BigramModel,
    partitions: &EncodedCorpusPartitions,
    partition: ScoredPartition,
) -> Result<PartitionScore, MetricError> {
    let documents = partitions.documents(partition.corpus_partition());
    let mut accumulator = MetricAccumulator::default();

    for document in documents {
        for transition in document.token_ids().windows(2) {
            let probability = model.smoothed_probability(transition[0], transition[1])?;
            accumulator.observe(probability);
        }
    }

    Ok(PartitionScore {
        partition,
        document_count: documents.len(),
        metrics: accumulator.finish()?,
    })
}

Для воспроизводимых чисел недостаточно одной формулы. Код загружает сохранённые в репозитории корпус и описание разбиения. По обучающим документам он определяет восемь правил слияния BPE и создаёт токенизатор со схемой идентификаторов версии 1 и словарём из 266 токенов. Затем документы кодируются с сохранением разбиения, а по восьми обучающим документам и 1844 переходам строится одна биграммная модель со сглаживанием α=1\alpha=1. Ниже показана эта последовательность операций; проверки контрольной суммы, идентификаторов документов и остальных параметров находятся за пределами фрагмента, в том же файле.

Загрузить корпус и разбиение, обучить BPE, закодировать документы и построить биграммную модель rust/demos/ch07-language-model-metrics/src/lib.rs#frozen-metric-fixture
/// Executes the data-to-model path before the frozen identities are checked.
fn reconstruct_frozen_metric_fixture() -> Result<ReconstructedFixture, FrozenFixtureError> {
    let corpus = Corpus::from_json(CORPUS_JSON)?;
    let manifest = SplitManifest::from_json(SPLIT_MANIFEST_SOURCE)?;
    let source_partitions = manifest.partition(&corpus)?;
    let training = BpeTrainer::new(EXPECTED_REQUESTED_MERGES).train(&source_partitions)?;
    let tokenizer = BpeTokenizer::from_training(&training)?;
    let encoded_partitions =
        EncodedCorpusPartitions::from_partitions(&source_partitions, &tokenizer);
    let model = BigramModel::fit_encoded_training_partition(
        tokenizer.layout().vocabulary_size(),
        FIT_ALPHA,
        &encoded_partitions,
    )?;

    Ok(ReconstructedFixture {
        corpus,
        manifest,
        training,
        tokenizer,
        encoded_partitions,
        model,
    })
}

Следующий фрагмент main.rs сначала вычисляет метрики для набора вероятностей [1/2,1/4][1/2,1/4], идеального прогноза и равномерного распределения. Здесь же проверяется поведение при нулевой вероятности и пустом входе. Затем код сопоставляет правильное усреднение с ошибочным, сравнивает два распределения с одинаковым максимальным токеном, а прямое произведение — с суммой логарифмов. В конце фрагмента одна и та же неизменная модель оценивается отдельно на обучающей и валидационной выборках. Код за пределами показанного фрагмента выводит полученные результаты. При выводе результата для 20002000 множителей 1/21/2 программа также проверяет, является ли уже вычисленная сумма мер неожиданности (total_surprise) конечным числом. Это не новый расчёт метрики: ни набор вероятностей, ни выборка повторно не оцениваются.

Вычислить учебные примеры и метрики неизменной модели на двух выборках rust/demos/ch07-language-model-metrics/src/main.rs#learner-output
    let tiny_probabilities = [0.5, 0.25];
    let tiny = score_assigned_probabilities(&tiny_probabilities)?;
    let perfect = score_assigned_probabilities(&[1.0, 1.0])?;
    let uniform = score_assigned_probabilities(&[1.0 / 5.0; 5])?;
    let impossible = score_assigned_probabilities(&[0.8, 0.0])?;
    let empty_error = match score_assigned_probabilities(&[]) {
        Err(error) => error,
        Ok(_) => return Err("empty metric input unexpectedly produced a score".into()),
    };

    let weighted_documents = score_assigned_probabilities(&[1.0, 0.25, 0.25, 0.25])?;
    // Deliberate misuse: because each document has one constant assigned
    // probability, [1.0, 0.25] represents their two geometric means. Scoring
    // that two-item pseudo-sample gives documents equal weight instead of
    // weighting all four observed targets.
    let wrong_equal_document_means = score_assigned_probabilities(&[1.0, 0.25])?;

    let q_distribution = [0.60, 0.30, 0.10];
    let r_distribution = [0.60, 0.20, 0.20];
    let q_target_b = score_assigned_probabilities(&[q_distribution[1]])?;
    let r_target_b = score_assigned_probabilities(&[r_distribution[1]])?;
    let lower = if q_target_b.mean_nll() < r_target_b.mean_nll() {
        "q"
    } else {
        "r"
    };

    let halves = vec![0.5; 2_000];
    let raw_product = halves.iter().copied().product::<f64>();
    let halves_score = score_assigned_probabilities(&halves)?;

    let fixture = frozen_metric_fixture()?;
    let train = score_bigram_partition(
        fixture.model(),
        fixture.encoded_partitions(),
        ScoredPartition::Train,
    )?;
    let validation = score_bigram_partition(
        fixture.model(),
        fixture.encoded_partitions(),
        ScoredPartition::Validation,
    )?;

Файл examples/diagram_trace.rs содержит отдельную исполняемую программу. Она вызывает функцию render_language_model_metrics_trace из src/diagram_trace.rs и выводит возвращённую строку. Показанный ниже фрагмент этой функции обращается к той же реализации метрики и к тому же набору данных и отдельно вычисляет метрики на обучающей и валидационной выборках. Кроме того, он проверяет три свойства последовательностей из этих выборок: BOS не встречается на позиции целевого токена, EOS занимает последнюю целевую позицию каждого документа, а пара EOS→BOS не добавлена. Код за пределами показанного фрагмента записывает результаты в десять строк трассировки. Значение test_selectable=no добавляется при записи, но само ограничение обеспечивается типом ScoredPartition, в котором нет варианта Test. Таким образом, все десять записей трассировки сохраняют результаты тех же функций метрики, того же набора данных и отдельных проверок границ. Для диаграммы метрика повторно не реализуется.

Вычислить учебный пример и метрики неизменной модели теми же функциями, а границы документов проверить отдельно rust/demos/ch07-language-model-metrics/src/diagram_trace.rs#language-model-metrics-trace
    let tiny_probabilities = [0.5, 0.25];
    let first_target = score_assigned_probabilities(&tiny_probabilities[0..1])?;
    let second_target = score_assigned_probabilities(&tiny_probabilities[1..2])?;
    let tiny = score_assigned_probabilities(&tiny_probabilities)?;

    let fixture = frozen_metric_fixture()?;
    let train = score_bigram_partition(
        fixture.model(),
        fixture.encoded_partitions(),
        ScoredPartition::Train,
    )?;
    let validation = score_bigram_partition(
        fixture.model(),
        fixture.encoded_partitions(),
        ScoredPartition::Validation,
    )?;
    let (bos_is_target, eos_is_target, has_cross_document_pair) =
        boundary_evidence(fixture.encoded_partitions());

Запустите программу главы:

./course run cargo run --quiet --locked -p ch07-language-model-metrics

На 1844 целевых токенах обучающей выборки неизменная модель даёт среднее NLL 3.832941183107 и перплексию 46.198216022322. На 469 целевых токенах валидационной выборки среднее NLL равно 3.981939680567, а перплексия — 53.620940919077. Эти числа воспроизводятся, пока неизменны корпус, разбиение, токенизатор, модель и правила оценивания. Разница между двумя результатами относится только к этим наборам целевых токенов и сама по себе не позволяет сделать общий вывод о качестве модели на других данных. При оценке валидационной выборки биграммная таблица не изменяется и не дообучается.

Проследите, откуда взялось каждое значение на диаграмме

Рассматривая диаграмму, ответьте на пять вопросов:

  1. Какая строка трассировки соответствует математической позиции t=1t=1 и где вероятность первого токена превращается в меру неожиданности?
  2. На каком этапе используется target_count=2 и почему знаменатель показан отдельно от суммы мер неожиданности?
  3. Какой этап переводит среднее NLL на другую шкалу, не используя дополнительных вероятностей модели?
  4. Получены ли результаты для обучающей и валидационной выборок с помощью одной и той же модели или с помощью двух разных моделей?
  5. Какая запись подтверждает, что функция score_bigram_partition не позволяет выбрать тестовую выборку?
Расчёт среднего NLL и перплексии для двух токенов; метрики одной неизменной модели на двух выборках

Как из вероятностей токенов получить среднее NLL и перплексию

Сначала показан полный расчёт для двух наблюдаемых токенов, а затем — NLL и перплексия одной и той же неизменной модели на обучающей и валидационной выборках. Все числа вычислены программой на Rust.

В первой части показано, как вероятность каждого из двух наблюдаемых целевых токенов превращается в меру неожиданности, как сумма этих мер делится на два целевых токена и как из среднего NLL получается перплексия. Во второй части приведены отдельные результаты одной и той же неизменной модели на обучающей и валидационной выборках. Также показаны правила учёта границ документов и указано, что интерфейс оценки биграммной модели не позволяет выбрать тестовую выборку.

Расчёт для двух наблюдаемых токенов

Чтобы увидеть все этапы, прокрутите эту область по горизонтали. Если вы пользуетесь клавиатурой, сначала переместите фокус в эту область, а затем прокручивайте её клавишами.

Вероятность наблюдаемого токена Мера неожиданности
Индекс целевого токена в Rust (с нуля) Вероятность наблюдаемого токена Мера неожиданности
index=0 0.500000000000 0.693147180560
index=1 0.250000000000 1.386294361120

затем

Сумма мер неожиданности и знаменатель
Сумма мер неожиданности для двух токенов
2.079441541680
Число токенов, на которое делим сумму
2

затем

Среднее значение NLL

Среднее значение NLL 1.039720770840

затем

Перплексия

Перплексия 2.828427124746

Метрики одной неизменной модели на двух выборках

Модель один раз строится по обучающим документам. Затем её параметры не меняются: на обучающей и валидационной выборках вычисляются отдельные значения метрик.

Как подготовлены данные и модель
Контрольная сумма корпуса
fnv1a64:723b071980ae8a22
Способ разбиения документов
fixed-paired-document-holdout-v1
Версия схемы идентификаторов токенов
1
Заданное число правил слияния BPE
8
Число правил слияния BPE после обучения
8
Размер словаря
266
Коэффициент сглаживания
1.000000000000
Выборка, по которой построена модель
train
Число документов для построения модели
8
Число переходов для построения модели
1844
Метрики одной неизменной модели на двух выборках
Выборка, на которой вычислена метрика Число документов Число целевых токенов Сумма мер неожиданности Среднее значение NLL Перплексия
Обучающая выборка partition=train 8 1844 7067.943541648752 3.832941183107 46.198216022322
Валидационная выборка partition=validation 2 469 1867.529710185699 3.981939680567 53.620940919077
Какие токены и выборки входят в расчёт
  • BOS задаёт контекст и сам не учитывается как целевой токен. bos_target=no
  • EOS учитывается как последний целевой токен каждого документа. eos_target=yes
  • Документы обрабатываются отдельно; переход EOS→BOS не добавляется. cross_document=no
  • В интерфейсе оценки биграммной модели нельзя выбрать тестовую выборку. test_selectable=no

В первой части диаграммы показан основной пример. Строки index=0 и index=1 содержат вероятности двух токенов и соответствующие меры неожиданности. Блок суммирования отдельно показывает сумму и знаменатель — число целевых токенов, равное 2. После деления получается среднее NLL, а после применения экспоненты — перплексия. Последний этап применяет exp\exp к уже вычисленному среднему NLL. Новые вероятности модели для этого не нужны; перплексия и среднее NLL однозначно определяются друг через друга.

Во второй части в разделе общих сведений описана подготовка данных и модели. И для обучающей, и для валидационной выборки используется модель, построенная только по обучающим документам. Диаграмма также явно показывает правила учёта границ: BOS задаёт контекст, EOS учитывается как целевой токен, документы не склеиваются, а интерфейс оценки биграммной модели позволяет выбрать только обучающую или валидационную выборку.

Сначала предскажите результат, затем проверьте рассуждение

Решите задачи на бумаге. В задачах об усреднении явно указывайте знаменатель, а в задачах о последовательностях — правила учёта границ документов.

  1. Для вероятностей [1/2,1/4][1/2,1/4] вычислите обе меры неожиданности, их сумму, число целевых токенов, среднее значение NLL и перплексию.
  2. Выведите среднее значение NLL и перплексию для идеального прогноза и для равномерного распределения по словарю размера V|V|.
  3. Предскажите результат для [0.8,0][0.8,0]. Почему замена нуля на ϵ\epsilon меняет метрику, а не просто делает вычисление устойчивее?
  4. В одном документе один целевой токен с p=1p=1, в другом — три токена с p=1/4p=1/4. Исправьте алгоритм, который сначала вычисляет среднее для каждого документа, а затем усредняет два полученных значения.
  5. Для последовательности [BOS,A,B,EOS] назовите все оцениваемые целевые токены и объясните роль BOS.
  6. Склейте [BOS,A,EOS] и [BOS,B,EOS]. Какой искусственный переход появится в месте соединения?
  7. Наблюдался токен B. Сравните q=[0.60,0.30,0.10]q=[0.60,0.30,0.10] и r=[0.60,0.20,0.20]r=[0.60,0.20,0.20]. Почему значения NLL различаются, хотя в обоих случаях argmax=A\arg\max=A?
  8. Можно ли напрямую сравнивать перплексии, полученные с разными токенизаторами или на разных наборах целевых токенов? Перечислите все условия, которые должны совпадать.
  9. Почему метрики сглаженной биграммной модели с α=1\alpha=1 конечны, хотя общая функция расчёта метрики возвращает положительную бесконечность при нулевой вероятности?
Проверьте ответы и ход вычислений
  1. Меры неожиданности равны ln(1/2)=ln2=0.693147180560-\ln(1/2)=\ln 2=0.693147180560 и ln(1/4)=ln4=1.386294361120-\ln(1/4)=\ln 4=1.386294361120. Их сумма — ln8=2.079441541680\ln 8=2.079441541680. Делим её на два целевых токена: 2.079441541680/2=1.0397207708402.079441541680/2=1.039720770840 ната на целевой токен. После применения exp\exp получаем PPL=2.828427124746\operatorname{PPL}=2.828427124746.
  2. При идеальном прогнозе каждый раз p=1p=1, поэтому ln1=0-\ln 1=0, среднее значение равно 00, а PPL=exp(0)=1\operatorname{PPL}=\exp(0)=1. Для равномерного распределения p=1/Vp=1/|V|, следовательно, каждое слагаемое равно lnV\ln |V|. Среднее остаётся равным lnV\ln |V|, а PPL=exp(lnV)=V\operatorname{PPL}=\exp(\ln |V|)=|V|.
  3. Для 0.8 мера неожиданности конечна. Вероятность 0, приписанная фактически наблюдавшемуся токену, даёт положительную бесконечность. Поэтому сумма, среднее NLL и перплексия также равны положительной бесконечности; нулевая вероятность не считается ошибкой входных данных. Если заменить ноль на ϵ\epsilon, событию будет приписана ненулевая вероятность, а результат станет конечным и зависящим от ϵ\epsilon. Тем самым изменится сама метрика, а не только устойчивость вычисления.
  4. Нужно выписать все четыре слагаемых: [ln1,ln(1/4),ln(1/4),ln(1/4)][-\ln 1,-\ln(1/4),-\ln(1/4),-\ln(1/4)]. Складываем их и делим на четыре целевых токена: (0+3ln4)/4=1.039720770840(0+3\ln 4)/4=1.039720770840, поэтому перплексия равна 2.8284271247462.828427124746. Если сначала вычислить среднее для каждого документа, а затем усреднить два результата, оба документа получат одинаковый вес. Это ошибочно даёт (0+ln4)/2=0.693147180560(0+\ln 4)/2=0.693147180560 и перплексию 22.
  5. Целевые токены — A, B и EOS: это вторые элементы переходов BOS→A, A→B и B→EOS. BOS задаёт контекст для первого целевого токена, но сам целью не является. Этот документ увеличивает NN на 33; в сумму входят три слагаемых.
  6. После склеивания получится BOS,A,EOS,BOS,B,EOS. В середине возникнет переход EOS→BOS. Из-за него маркер BOS второго документа ошибочно станет целевым токеном после EOS первого документа.
  7. В обоих распределениях наибольшая вероятность 0.600.60 относится к A, но в формулу подставляется вероятность наблюдаемого B. Для qq получаем ln0.30=1.203972804326-\ln 0.30=1.203972804326, для rrln0.20=1.609437912434-\ln 0.20=1.609437912434. Распределение qq даёт меньшее NLL, потому что приписывает фактически наблюдавшемуся токену большую вероятность.
  8. Нет. Для прямого сравнения должны совпадать токенизатор, состав словаря и соответствие идентификаторов токенам, правило учёта границ документов, способ формирования контекста и точный набор оцениваемых целевых токенов. Если меняется хотя бы одно из этих условий, меняются слагаемые, знаменатель или сам смысл сравнения.
  9. Аддитивное сглаживание с α=1\alpha=1 делает каждую вероятность, возвращаемую этой биграммной моделью, положительной, поэтому все её меры неожиданности конечны. Общая функция метрики применима и к другим моделям: если какая-либо из них приписывает наблюдаемому токену вероятность ровно 00, функция возвращает положительную бесконечность и не подменяет ноль другим значением. Сглаживание изменяет распределение вероятностей именно этой биграммной модели. Сама общая функция метрики не ограничивает вероятности снизу и не заменяет ноль на ϵ\epsilon.

Проверка понимания: перплексия и среднее NLL не являются двумя независимыми показателями качества. Перплексия в точности равна exp()\exp(\mathcal{L}), а обратная ей величина 1/PPL1/\operatorname{PPL} — геометрическому среднему вероятностей, которые модель приписала наблюдаемым токенам. Перплексия помогает интерпретировать результат, но не содержит новых сведений по сравнению с исходными вероятностями и числом целевых токенов.

Используйте одну и ту же метрику по мере усложнения модели

Теперь мы можем воспроизводимо вычислить среднее NLL и перплексию модели из главы 6 на обучающей и валидационной выборках. В обоих случаях используется одна и та же неизменная биграммная модель, а в формулу подставляются вероятности фактически наблюдавшихся целевых токенов. При оценивании счётчики переходов в биграммной таблице не меняются и обучение не повторяется. Интерфейс оценки биграммной модели в главе 7 позволяет выбрать только обучающую или валидационную выборку.

В главе 8 начнётся реализация численного ядра. Значения тензора мы разместим в плоском Vec<f64>, зададим его форму и шаг по каждому измерению (stride), а затем реализуем проверяемое преобразование координат в смещение. В главах 8–22 появятся тензорные операции, дифференцирование и оптимизация, с помощью которых последующие модели смогут снижать NLL.

В главе 33 мы обучим готовый декодер и выберем одно из его состояний, используя только значение функции потерь на валидационной выборке. В главе 34 мы зафиксируем выбранное состояние и покажем одну локальную оценку фиксированного учебного примера после завершения выбора модели. Затем сравним выбранный декодер с сохранённой биграммной моделью. Это порядок действий внутри одного запуска, а не утверждение о том, как часто фиксированный результат использовался при разработке репозитория. Чтобы сравнение было корректным, для обеих моделей должны совпадать токенизатор, состав словаря и соответствие идентификаторов токенам, правила учёта границ документов, способ формирования контекста, исходные данные и точный набор оцениваемых целевых токенов. Глава 7 задаёт способ измерения; последующие главы покажут, как улучшать модель и честно проводить итоговое сравнение.