diffusion_baseline / tests /test_conditional_evaluation.py
jyliuAI's picture
Add files using upload-large-folder tool
784f572 verified
Raw History Blame Contribute Delete
4.45 kB
import math
import pytest
from dlb.schema import ConditionalSampleRecord
from evaluation.conditional_perplexity import (
compute_conditional_gen_ppl,
conditional_texts,
)
class SourceTokenizer:
def decode(self, token_ids, **kwargs):
del kwargs
row = list(token_ids)
if len(row) == 64 and set(row) == {1}:
return "prompt"
if len(row) == 64 and set(row) == {2}:
return "generated"
if len(row) == 64 and set(row) == {3}:
return "reference"
if len(row) == 128 and row[:64] == [1] * 64 and row[64:] == [2] * 64:
return "prompt generated"
if len(row) == 960 and set(row) == {2}:
return "full generated suffix"
raise ValueError(f"unexpected decoded row length {len(row)}")
class WhitespaceSuffixTokenizer:
def decode(self, token_ids, **kwargs):
del kwargs
row = list(token_ids)
if len(row) == 64 and set(row) == {1}:
return "prompt"
if len(row) == 64 and set(row) == {4}:
return " \n\t"
if len(row) == 64 and set(row) == {3}:
return "reference"
if len(row) == 128 and row[:64] == [1] * 64 and row[64:] == [4] * 64:
return "prompt \n\t"
raise ValueError(f"unexpected decoded row length {len(row)}")
class ScorerTokenizer:
def __call__(self, texts, **kwargs):
rows = {
"prompt": [9, 8],
"prompt generated": [9, 8, 2, 3],
}
encoded = [rows[text][: kwargs["max_length"]] for text in texts]
width = max(map(len, encoded))
return {
"input_ids": [row + [0] * (width - len(row)) for row in encoded],
"attention_mask": [[1] * len(row) + [0] * (width - len(row)) for row in encoded],
}
class PromptExcludedScorer:
def __init__(self) -> None:
self.eval_called = False
def eval(self):
self.eval_called = True
return self
def score_prompt_excluded_batch(self, input_ids, attention_mask, target_mask):
del attention_mask
total = 0.0
count = 0
for row, targets in zip(input_ids, target_mask, strict=True):
for token, target in zip(row[1:], targets[1:], strict=True):
if target:
assert token in {2, 3}
total += math.log(4)
count += 1
return total, count
def record(*, suffix_length: int = 64) -> ConditionalSampleRecord:
prefix = [1] * 64
continuation = [2] * suffix_length
return ConditionalSampleRecord(
sample_id=0,
prompt_id=0,
completion_id=0,
source_index=10,
prefix_token_ids=prefix,
continuation_token_ids=continuation,
reference_token_ids=[3] * 64,
full_token_ids=prefix + continuation,
prefix_text="prompt",
continuation_text="generated",
reference_text="reference",
full_text="prompt generated",
seed=42,
generation_seconds=0.0,
prefix_exact_match=True,
)
def test_conditional_ppl_excludes_prompt_tokens_from_nll() -> None:
"""Catch prompt tokens being included in conditional generative perplexity."""
result = compute_conditional_gen_ppl(
[record()],
PromptExcludedScorer(),
ScorerTokenizer(),
SourceTokenizer(),
batch_size=1,
)
assert result.valid_token_count == 2
assert result.perplexity == pytest.approx(4.0)
def test_conditional_texts_decode_only_evaluation_suffix_for_long_canvas() -> None:
"""Catch OWT's 960-token generated suffix leaking into main quality metrics."""
decoded = conditional_texts([record(suffix_length=960)], SourceTokenizer())
assert decoded[0].generated_suffix == "generated"
assert decoded[0].reference_suffix == "reference"
assert decoded[0].prefix_and_generated_suffix == "prompt generated"
def test_conditional_texts_preserves_whitespace_only_suffix() -> None:
"""Catch valid whitespace token suffixes being rejected as empty text."""
value = record()
value.continuation_token_ids[:] = [4] * 64
value.full_token_ids[:] = value.prefix_token_ids + value.continuation_token_ids
decoded = conditional_texts([value], WhitespaceSuffixTokenizer())
assert decoded[0].generated_suffix == " \n\t"
assert decoded[0].prefix_and_generated_suffix == "prompt \n\t"