Skip to content

[evaluation] [DRAFT] Gemma4 evaluation - #851

Draft
Torrero wants to merge 2 commits into
Samsung:mainfrom
Torrero:gemma_evaluation
Draft

[evaluation] [DRAFT] Gemma4 evaluation#851
Torrero wants to merge 2 commits into
Samsung:mainfrom
Torrero:gemma_evaluation

Conversation

@Torrero

@Torrero Torrero commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

This draft PR adds benchmarks evaluation support for Gemma4 with GPTQ quantizer.

Evaluation google/gemma-4-e2b-it

Config ID PPL mmlu vqav2 COCO (CIDEr/Bleu_4) hellaswag MMMU_Pro (vision) Llava-Bench Rel. videomme (32 frames)
FP32 in progress 0.484 0.738 0.355/0.123 0.381 0.05 81.63 48.9
PTQ_W4A16_ple8bit in progress 0.622 0.418 0.00 76.01

*For the instruction-tuned model (gemma-4-e2b-it) the perplexity evaluation in raw mode produces huge values (>1000), it should be evaluated in the chat-continuation mode, but its behavior requires clarification. (in progress).

**GPTQ mode is under evaluation.

test_Gemma_orig_eval.log
gemma_PTQ_W4A16_ple8bit.log

Run command for PTQ_W4A16_ple8bit
python -m tico.quantization.examples.quantize \
  --config tico/quantization/examples/configs/gemma4_e2b_gptq_ple8bit.yaml \
  --set model.name_or_path=google/gemma-4-e2b-it \
  --set calibration.seq_len=2048 \
  --set calibration.datasets.0.n_samples=10 \
  --set export.enabled=false \
  --set pipeline.0.enabled=false \
  --set pipeline.0.verbose=true \
  --set pipeline.0.weight_bits=4 \
  --set pipeline.0.percdamp=0.1 \
  --set pipeline.1.print_model=true \
  --set pipeline.1.enabled=true \
  --set pipeline.1.activation=int16 \
  --set pipeline.1.linear_weight=uint4 \
  --set evaluation.vlm_tasks=[vqav2] \
  --set evaluation.coco=true \
  --set evaluation.n_samples=1000 \
  --set evaluation.mmlu.enabled=false \
  --set evaluation.hellaswag.enabled=true \
  --set evaluation.hellaswag.n_samples=1000 \
  --set evaluation.mmmu.enabled=true \
  --set evaluation.mmmu.dataset=MMMU/MMMU_Pro \
  --set evaluation.mmmu.subject=vision \
  --set evaluation.mmmu.n_samples=1000 \
  --set evaluation.ppl.enabled=true \
  --set evaluation.ppl.stride=512 \
  --set evaluation.ppl.mode=chat-continuation \
  --set evaluation.llava_bench.enabled=true \
  --set evaluation.llava_bench.judge.model_id=unsloth/Llama-3.2-3B-Instruct \
  --set evaluation.llava_bench.n_samples=50 \
  --set evaluation.llava_bench.candidate_label="gemma-4-e2b-it" \
  --set evaluation.llava_bench.max_seq_len=2048 \
  --set evaluation.max_seq_len=2048

TICO-DCO-1.0-Signed-off-by: Evgenii Maltsev e.maltsev@samsung.com

This commit support benchmarks evaluation for Gemma4

Co-authored-by: Cline

TICO-DCO-1.0-Signed-off-by:  Evgenii Maltsev <e.maltsev@samsung.com>
@mhs4670go

Copy link
Copy Markdown
Contributor

From #852 (comment),

I mentioned in my draft that raw PPL evaluation produces huge PPL score for original instruction-tuned model gemma-4-e2b-it (#851 (comment)). As I understand correctly the chat-continuation mode should be used for this aim.

Llama-3.2-Instruct and Qwen3-VL-Instruct are currently evaluated with the same raw WikiText PPL protocol without this issue, so I do not think instruction tuning alone is sufficient to explain the Gemma4 result.

Before switching to a different PPL protocol, I compared raw PPL/logits on the unwrapped HF model. Therefore, I ran below script and the ppls are small enough.

use_cache=True: internal_nll=2.476937, manual_nll=2.476937, ppl=11.90
use_cache=False: internal_nll=2.476937, manual_nll=2.476937, ppl=11.90
import math

import torch
import torch.nn.functional as F
import transformers
from datasets import load_dataset
from transformers import AutoModelForMultimodalLM, AutoProcessor


MODEL_ID = "google/gemma-4-E2B-it"

print("transformers:", transformers.__version__)

processor = AutoProcessor.from_pretrained(MODEL_ID)
model = AutoModelForMultimodalLM.from_pretrained(
    MODEL_ID,
    dtype=torch.bfloat16,
    device_map="auto",
)
model.eval()

dataset = load_dataset(
    "Salesforce/wikitext",
    "wikitext-2-raw-v1",
    split="test",
)

text = "\n\n".join(
    example["text"]
    for example in dataset
    if example["text"].strip()
)

input_ids = processor.tokenizer(
    text,
    return_tensors="pt",
).input_ids[:, :256]

input_device = model.get_input_embeddings().weight.device
input_ids = input_ids.to(input_device)
attention_mask = torch.ones_like(input_ids)


def evaluate_one_window(use_cache: bool) -> tuple[float, float]:
    with torch.inference_mode():
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            labels=input_ids,
            use_cache=use_cache,
            logits_to_keep=0,
        )

    logits = outputs.logits[:, :-1].float()
    targets = input_ids[:, 1:].to(logits.device)

    manual_nll = F.cross_entropy(
        logits.reshape(-1, logits.shape[-1]),
        targets.reshape(-1),
        reduction="mean",
    )

    internal_nll = float(outputs.loss)
    manual_nll_value = float(manual_nll)

    print(
        f"use_cache={use_cache}: "
        f"internal_nll={internal_nll:.6f}, "
        f"manual_nll={manual_nll_value:.6f}, "
        f"ppl={math.exp(manual_nll_value):.2f}"
    )

    return internal_nll, manual_nll_value


evaluate_one_window(use_cache=True)
evaluate_one_window(use_cache=False)

@Torrero

Torrero commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@mhs4670go

I ran script from your comment (#851 (comment)) using different devices and environments but I couldn't reproduce the same low numbers for the mentioned instruction-tuned model (gemma-4-E2B-it):

use_cache=True: internal_nll=2.476937, manual_nll=2.476937, ppl=11.90
use_cache=False: internal_nll=2.476937, manual_nll=2.476937, ppl=11.90

My results for 2 different workstation with different GPU and CPU:

Python 3.10.12
Driver Version: 590.48.01      CUDA Version: 13.1 

torch==2.12.1
transformers: 5.9.0
use_cache=True: internal_nll=10.312982, manual_nll=10.312982, ppl=30121.11
use_cache=False: internal_nll=10.312982, manual_nll=10.312982, ppl=30121.11

torch==2.12.1
transformers: 5.14.1
use_cache=True: internal_nll=10.311199, manual_nll=10.311199, ppl=30067.47
use_cache=False: internal_nll=10.311199, manual_nll=10.311199, ppl=30067.47

torch==2.7.1+cu128
transformers: 5.9.0
use_cache=True: internal_nll=10.250070, manual_nll=10.250070, ppl=28284.51
use_cache=False: internal_nll=10.250070, manual_nll=10.250070, ppl=28284.51

Python 3.10.14
Driver Version: 575.57.08      CUDA Version: 12.9

torch==2.6.0+cu124
transformers: 5.5.0
use_cache=True: internal_nll=10.166949, manual_nll=10.166949, ppl=26028.55
use_cache=False: internal_nll=14.984425, manual_nll=14.984425, ppl=3218495.56

torch==2.6.0+cu124
transformers: 5.9.0
use_cache=True: internal_nll=10.166949, manual_nll=10.166949, ppl=26028.55
use_cache=False: internal_nll=10.166949, manual_nll=10.166949, ppl=26028.55

CPU 
use_cache=True: internal_nll=10.219853, manual_nll=10.219853, ppl=27442.64
use_cache=False: internal_nll=10.219853, manual_nll=10.219853, ppl=27442.64

Could you please provide information about your environment, maybe I missed something.

@mhs4670go

Copy link
Copy Markdown
Contributor

@Torrero Hmm.. it's weird.

Here's my env.

python 3.10.12
torch 2.7.1+cu128
transformers 5.9.0

Could you share the result of below scirpt?

import math

import torch
import torch.nn.functional as F
import transformers
from datasets import load_dataset
from transformers import AutoModelForMultimodalLM, AutoProcessor
import tokenizers


MODEL_ID = "/home/seongwoo.chae/models/Qwen3-VL-4B-Instruct"

print("transformers:", transformers.__version__)

processor = AutoProcessor.from_pretrained(MODEL_ID)
model = AutoModelForMultimodalLM.from_pretrained(
    MODEL_ID,
    dtype=torch.bfloat16,
    device_map="auto",
)
model.eval()

dataset = load_dataset(
    "Salesforce/wikitext",
    "wikitext-2-raw-v1",
    split="test",
)

text = "\n\n".join(
    example["text"]
    for example in dataset
    if example["text"].strip()
)

input_ids = processor.tokenizer(
    text,
    return_tensors="pt",
).input_ids[:, :256]

input_device = model.get_input_embeddings().weight.device
input_ids = input_ids.to(input_device)
attention_mask = torch.ones_like(input_ids)


def evaluate_one_window(use_cache: bool) -> tuple[float, float]:
    with torch.inference_mode():
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            labels=input_ids,
            use_cache=use_cache,
            logits_to_keep=0,
        )

    logits = outputs.logits[:, :-1].float()
    targets = input_ids[:, 1:].to(logits.device)

    manual_nll = F.cross_entropy(
        logits.reshape(-1, logits.shape[-1]),
        targets.reshape(-1),
        reduction="mean",
    )

    internal_nll = float(outputs.loss)
    manual_nll_value = float(manual_nll)

    print(
        f"use_cache={use_cache}: "
        f"internal_nll={internal_nll:.6f}, "
        f"manual_nll={manual_nll_value:.6f}, "
        f"ppl={math.exp(manual_nll_value):.2f}"
    )

    return internal_nll, manual_nll_value


evaluate_one_window(use_cache=True)
evaluate_one_window(use_cache=False)

# Test

tokenizer = processor.tokenizer

print("transformers:", transformers.__version__)
print("transformers path:", transformers.__file__)
print("tokenizers:", tokenizers.__version__)
print("tokenizer class:", type(tokenizer))
print("tokenizer path:", tokenizer.name_or_path)
print("tokenizer commit:", tokenizer.init_kwargs.get("_commit_hash"))
print("model commit:", getattr(model.config, "_commit_hash", None))

print("add_bos_token:", getattr(tokenizer, "add_bos_token", None))
print("bos_token:", tokenizer.bos_token)
print("bos_token_id:", tokenizer.bos_token_id)

print("first input ids:", input_ids[0, :10].tolist())
print(
    "first tokens:",
    tokenizer.convert_ids_to_tokens(input_ids[0, :10].tolist()),
)
print(
    "starts with BOS:",
    input_ids[0, 0].item() == tokenizer.bos_token_id,
)

input_device = model.get_input_embeddings().weight.device


@torch.inference_mode()
def score_input_ids(input_ids: torch.Tensor) -> tuple[float, float]:
    input_ids = input_ids.to(input_device)
    attention_mask = torch.ones_like(input_ids)

    outputs = model(
        input_ids=input_ids,
        attention_mask=attention_mask,
        labels=input_ids,
        use_cache=False,
        logits_to_keep=0,
    )

    logits = outputs.logits[:, :-1].float()
    targets = input_ids[:, 1:].to(logits.device)

    nll = F.cross_entropy(
        logits.reshape(-1, logits.shape[-1]),
        targets.reshape(-1),
        reduction="mean",
    )

    nll_value = float(nll)
    return nll_value, math.exp(nll_value)


original_add_bos = getattr(tokenizer, "add_bos_token", None)

try:
    for add_bos in (False, True):
        tokenizer.add_bos_token = add_bos

        test_ids = tokenizer(
            text,
            return_tensors="pt",
        ).input_ids[:, :256]

        nll, ppl = score_input_ids(test_ids)

        print(
            f"add_bos_token={add_bos}: "
            f"first_id={test_ids[0, 0].item()}, "
            f"starts_with_bos="
            f"{test_ids[0, 0].item() == tokenizer.bos_token_id}, "
            f"nll={nll:.6f}, "
            f"ppl={ppl:.2f}"
        )
finally:
    if original_add_bos is not None:
        tokenizer.add_bos_token = original_add_bos
transformers: 5.9.0
Loading weights: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 713/713 [00:01<00:00, 481.32it/s]
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (298938 > 262144). Running this sequence through the model will result in indexing errors
use_cache=True: internal_nll=2.476937, manual_nll=2.476937, ppl=11.90
use_cache=False: internal_nll=2.476937, manual_nll=2.476937, ppl=11.90
transformers: 5.9.0
transformers path: /home/seongwoo.chae/TICO/.venv/lib/python3.10/site-packages/transformers/__init__.py
tokenizers: 0.22.2
tokenizer class: <class 'transformers.models.qwen2.tokenization_qwen2.Qwen2Tokenizer'>
tokenizer path: /home/seongwoo.chae/models/Qwen3-VL-4B-Instruct
tokenizer commit: None
model commit: None
add_bos_token: False
bos_token: None
bos_token_id: None
first input ids: [284, 8397, 425, 10965, 465, 284, 14731, 8397, 425, 10965]
first tokens: ['Ġ=', 'ĠRobert', 'ĠB', 'oul', 'ter', 'Ġ=', 'ĠĊĊĊ', 'ĠRobert', 'ĠB', 'oul']
starts with BOS: False
add_bos_token=False: first_id=284, starts_with_bos=False, nll=2.476937, ppl=11.90
add_bos_token=True: first_id=284, starts_with_bos=False, nll=2.476937, ppl=11.90

@Torrero

Torrero commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@mhs4670go

This is my output:

transformers: 5.14.1
Loading weights: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████| 713/713 [00:01<00:00, 430.65it/s]
Using the latest cached version of the dataset since Salesforce/wikitext couldn't be found on the Hugging Face Hub (offline mode is enabled).
Found the latest cached dataset configuration 'wikitext-2-raw-v1' at /home/e.maltsev/SAMSUNG/huggingface_cache/datasets/Salesforce___wikitext/wikitext-2-raw-v1/0.0.0/b08601e04326c79dfdd32d625aee71d232d685c3 (last modified on Tue Aug  4 15:21:44 2026).
[transformers] Token indices sequence length is longer than the specified maximum sequence length for this model (298938 > 262144). Running this sequence through the model will result in indexing errors
use_cache=True: internal_nll=2.476499, manual_nll=2.476499, ppl=11.90
use_cache=False: internal_nll=2.476499, manual_nll=2.476499, ppl=11.90
transformers: 5.14.1
transformers path: /home/e.maltsev/SAMSUNG/.vlm_venv/lib/python3.10/site-packages/transformers/__init__.py
tokenizers: 0.22.2
tokenizer class: <class 'transformers.models.qwen2.tokenization_qwen2.Qwen2Tokenizer'>
tokenizer path: Qwen/Qwen3-VL-4B-Instruct
tokenizer commit: None
model commit: ebb281ec70b05090aa6165b016eac8ec08e71b17
add_bos_token: False
bos_token: None
bos_token_id: None
first input ids: [284, 8397, 425, 10965, 465, 284, 14731, 8397, 425, 10965]
first tokens: ['Ġ=', 'ĠRobert', 'ĠB', 'oul', 'ter', 'Ġ=', 'ĠĊĊĊ', 'ĠRobert', 'ĠB', 'oul']
starts with BOS: False
add_bos_token=False: first_id=284, starts_with_bos=False, nll=2.476499, ppl=11.90
add_bos_token=True: first_id=284, starts_with_bos=False, nll=2.476499, ppl=11.90

@mhs4670go

Copy link
Copy Markdown
Contributor

@Torrero Then, the ppl seems small enough now.

@Torrero

Torrero commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@mhs4670go But this is result for Qwen3-vl (#851 (comment))

for gemma4-e2b-it results still enough big:

transformers: 5.14.1
Loading weights: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1951/1951 [00:01<00:00, 1043.92it/s]
Using the latest cached version of the dataset since Salesforce/wikitext couldn't be found on the Hugging Face Hub (offline mode is enabled).
Found the latest cached dataset configuration 'wikitext-2-raw-v1' at /home/e.maltsev/SAMSUNG/huggingface_cache/datasets/Salesforce___wikitext/wikitext-2-raw-v1/0.0.0/b08601e04326c79dfdd32d625aee71d232d685c3 (last modified on Tue Aug  4 15:21:44 2026).
use_cache=True: internal_nll=10.311199, manual_nll=10.311199, ppl=30067.47
use_cache=False: internal_nll=10.311199, manual_nll=10.311199, ppl=30067.47
transformers: 5.14.1
transformers path: /home/e.maltsev/SAMSUNG/.vlm_venv/lib/python3.10/site-packages/transformers/__init__.py
tokenizers: 0.22.2
tokenizer class: <class 'transformers.models.gemma.tokenization_gemma.GemmaTokenizer'>
tokenizer path: google/gemma-4-E2B-it
tokenizer commit: None
model commit: 3e22461f65e89153144f8adb70e3b8c2cc9845a7
add_bos_token: False
bos_token: <bos>
bos_token_id: 2
first input ids: [578, 9877, 151936, 589, 578, 236743, 109, 9877, 151936, 589]
first tokens: ['▁=', '▁Robert', '▁Boul', 'ter', '▁=', '▁', '\n\n\n', '▁Robert', '▁Boul', 'ter']
starts with BOS: False
add_bos_token=False: first_id=578, starts_with_bos=False, nll=10.311199, ppl=30067.47
add_bos_token=True: first_id=2, starts_with_bos=True, nll=5.217113, ppl=184.40

@mhs4670go

Copy link
Copy Markdown
Contributor

@Torrero Sorry for confusion. It's my bad that I used a different model.

Thanks for checking this across multiple environments.

My previous low-PPL result was caused by a mistake on my side. I reran the
test and reproduced the high raw PPL in the following environment:

  • Python 3.10.12
  • torch 2.7.1+cu128
  • torchvision 0.22.1
  • transformers 5.9.0
  • tokenizers 0.22.2

Results:

add_bos_token=False:
  NLL=10.282091, PPL=29204.88

add_bos_token=True:
  NLL=5.214510, PPL=183.92

The missing BOS token explains a large part of the extremely high raw PPL, but adding BOS alone is still not enough to obtain a reasonable value.

The gemma-4-E2B-it tokenizer has add_bos_token=False, while its chat template inserts BOS and the user/model turn tokens itself. This confirms that bare raw-text tokenization is not the intended input format for this instruction-tuned checkpoint as you thought.

I therefore agree that Gemma4 IT should use a separately labelled chat-formatted PPL mode rather than the current raw mode.

One remaining detail is terminology: the current implementation renders a fixed user instruction and scores WikiText as assistant-side text. This is more precisely a chat-prefixed or assistant-response PPL, rather than a true context/target continuation split. It is still suitable for comparing FP32 and quantized Gemma4 models though.

Therefore, please proceed what you was going to merge. I'll review them. Thank you again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants