Spaces:
Paused
Paused
File size: 6,596 Bytes
2f86d9b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | # Copyright 2026 DeepMind Technologies Limited.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""Evaluation binary for Hackable Diffusion adapter.
Runs AR sampling evaluation on a single checkpoint and reports task-specific
metrics (accuracy, BLEU, etc.) plus text sample summaries.
"""
from __future__ import annotations
from absl import app
from absl import flags
from absl import logging
from gemma.diffusion.hackable_diffusion_adapter.eval import ar_eval
import jax
from kauldron import kd
import tensorflow as tf
# pylint: disable=g-import-not-at-top
with kd.konfig.imports():
from kauldron import kd as kd_cfg # pylint: disable=reimported
from gemma.diffusion.hackable_diffusion_adapter.eval import sudoku_eval as sudoku_eval_cfg
from gemma.diffusion.hackable_diffusion_adapter.eval import pubmedqa_eval as pubmedqa_eval_cfg
# pylint: enable=g-import-not-at-top
################################################################################
# MARK: Flags
################################################################################
_CONFIG = kd.konfig.DEFINE_config_file(
"cfg",
None,
"Training configuration.",
lock_config=False,
)
_TASK = flags.DEFINE_enum(
"task",
None,
["sudoku", "pubmedqa"],
"Task to evaluate. Determines which metrics are reported.",
)
_EVAL_NAMES = flags.DEFINE_list(
"eval_names",
None,
"Evaluation(s) to run. If not set, runs all evaluators.",
)
_STEP = flags.DEFINE_integer(
"step",
None,
"The checkpoint step to evaluate. If not set, restores the latest"
" checkpoint.",
)
################################################################################
# MARK: Metrics
################################################################################
def _denoising_metrics():
"""Return metrics common to every task (denoising step counts)."""
return {
"processed_denoising_steps": kd_cfg.metrics.SingleDimension(
tensor="processed_denoising_steps", index=None
),
"processed_num_canvases": kd_cfg.metrics.SingleDimension(
tensor="processed_num_canvases", index=None
),
"average_denoising_steps_per_canvas": kd_cfg.metrics.SingleDimension(
tensor="average_denoising_steps_per_canvas", index=None
),
}
def _sudoku_metrics():
"""Return task-specific metrics for the Sudoku config."""
return {
"sudoku": sudoku_eval_cfg.SudokuAllMetrics(
tokens="samples",
ground_truth="batch.solution_tokens",
puzzle="batch.puzzle_tokens",
extraction_mode=sudoku_eval_cfg.ExtractionMode.SFT,
),
}
def _pubmedqa_metrics():
"""Return task-specific metrics for the PubMedQA config."""
return {
"pubmedqa_accuracy": pubmedqa_eval_cfg.PubMedQAAccuracy(
tokens="samples",
ground_truth="batch.short_answer_tokens",
),
"pubmedqa_bleu": pubmedqa_eval_cfg.BLEUScore(
tokens="samples",
ground_truth="batch.long_answer_tokens",
),
}
_TASK_METRICS = {
"sudoku": _sudoku_metrics,
"pubmedqa": _pubmedqa_metrics,
}
################################################################################
# MARK: Eval injection
################################################################################
def _inject_ar_evals(cfg, task):
"""Build AR evaluators with task-specific metrics and add them to ``cfg``.
Args:
cfg: The (unresolved) Kauldron trainer config.
task: Task name (``"sudoku"`` or ``"pubmedqa"``).
"""
eval_metrics = _denoising_metrics()
if task_metrics_fn := _TASK_METRICS.get(task):
eval_metrics.update(task_metrics_fn())
all_evals = ar_eval.make_ar_evals(
cfg,
gemma_network_ref=cfg.ref.model.gemma_network,
corruption_process_ref=cfg.ref.aux.corruption_process,
canvas_size_ref=cfg.ref.aux.canvas_size,
metrics=eval_metrics,
max_num_canvases=cfg.ref.aux.num_canvases,
use_early_stopping=False,
)
early_stopping_evals = ar_eval.make_ar_evals(
cfg,
gemma_network_ref=cfg.ref.model.gemma_network,
corruption_process_ref=cfg.ref.aux.corruption_process,
canvas_size_ref=cfg.ref.aux.canvas_size,
metrics=eval_metrics,
max_num_canvases=cfg.ref.aux.num_canvases,
use_early_stopping=True,
)
all_evals.update(early_stopping_evals)
cfg.evals.update(all_evals)
################################################################################
# MARK: Main
################################################################################
def main(_):
"""Run single-checkpoint evaluation."""
# Early JAX initialization to secure GPU contexts and prevent NCCL corruption.
jax.devices()
# Hide GPUs from TensorFlow to avoid memory conflicts with JAX.
tf.config.set_visible_devices([], "GPU")
cfg = _CONFIG.value
task = _TASK.value
eval_names = _EVAL_NAMES.value
step = _STEP.value
_inject_ar_evals(cfg, task)
if hasattr(cfg, "init_transform"):
cfg.init_transform = None # pyrefly: ignore[missing-attribute]
trainer: kd.train.Trainer = kd.konfig.resolve(cfg) # pyrefly: ignore[bad-assignment]
logging.info("Initializing state...")
state = trainer.init_state()
if step is None:
logging.info("Restoring latest checkpoint...")
state = trainer.checkpointer.restore(state)
step = int(state.step)
else:
logging.info("Restoring checkpoint at step %d...", step)
state = trainer.checkpointer.restore(state, step=step)
available_evals = [
name for name in trainer.evals.keys() if name != "gemma_checkpointer"
]
if not eval_names:
eval_names = available_evals
logging.info("Running evaluation(s) %s for step %d...", eval_names, step)
for name in eval_names:
if name not in trainer.evals:
raise ValueError(
f"Evaluator {name!r} not found. Available: {available_evals}"
)
evaluator = trainer.evals[name]
evaluator.evaluate(state, step=step)
logging.info("All evaluations done!")
if __name__ == "__main__":
jax.config.config_with_absl()
app.run(main)
|