Skip to content

fix: lower default repetition_penalty to avoid table hallucination (#240) - #244

Open
agourakis82 wants to merge 2 commits into
zai-org:mainfrom
agourakis82:fix/repetition-penalty-table-hallucination
Open

agourakis82 wants to merge 2 commits into
zai-org:mainfrom
agourakis82:fix/repetition-penalty-table-hallucination

Conversation

@agourakis82

Copy link
Copy Markdown

What

Lowers the SDK's default pipeline.page_loader.repetition_penalty from 1.1 to 1.05 in both glmocr/config.py and glmocr/config.yaml.

Why

While investigating #240, I found that the default repetition_penalty=1.1 makes GLM-OCR deterministically hallucinate a spurious extra row on table regions. On the real invoice image attached to #240, every identical call (temperature=0.0) appended a fabricated row that does not exist anywhere in the source:

...<tr><td colspan="7">Rechnungsbetrag EUR: 86,23</td></tr><tr><td colspan="7">missing in table</td></tr></tbody></table>

A repetition_penalty sweep on the same image (8 identical runs per value, fully deterministic at each) shows a clear threshold:

repetition_penalty hallucinated row
None, 1.01, 1.02, 1.05 absent
1.08, 1.1 (current default), 1.15, 1.2 present

I reproduced this identically on two independent backends/hardware:

  • Raw mlx_vlm.generate() on Apple Silicon (mlx-community/GLM-OCR-bf16)
  • The full self-hosted SDK pipeline — glmocr.server + PP-DocLayoutV3 layout detection + vllm serve zai-org/GLM-OCR — on CUDA (vLLM 0.29.0)

Both gave byte-identical output across repeated calls, with the hallucination appearing/disappearing at the same threshold in both environments.

This PR moves the default just below that threshold (1.05) rather than removing repetition suppression entirely, since some penalty is presumably useful against repeat loops (README/older reports mention repetition issues). Happy to adjust the exact value or make it task-specific (e.g. a lower penalty for table than text) if maintainers have a preference or additional data — my testing was limited to this one document/table layout.

Testing

  • pre-commit run --files glmocr/config.py glmocr/config.yaml passes (black, flake8, typos, etc.)
  • No functional code paths changed, only the default config value + a comment.
  • Full repro scripts and raw output logs are in the linked issue comments below.

References

🤖 Generated with Claude Code

agourakis82 and others added 2 commits September 21, 2026 22:30
…ai-org#240)

The SDK's default page_loader.repetition_penalty=1.1 causes the model to
deterministically hallucinate spurious trailing content on table regions:
on a real-world invoice table, every identical call under temperature=0.0
appended a fabricated extra row ('missing in table') that does not exist
in the source image.

A repetition_penalty sweep on the same real image showed a clear
threshold: the hallucination is absent at rp<=1.05 and present at
rp>=1.08 (tested 1.01, 1.02, 1.05, 1.08, 1.1, 1.15, 1.2), with 8/8
identical, deterministic outputs at every value. The hallucination was
reproduced identically on two independent backends/hardware:
mlx_vlm (Apple Silicon) and vLLM (CUDA), through both a raw
mlx_vlm.generate() call and the full SDK pipeline
(glmocr.server + PP-DocLayoutV3 layout detection + vLLM OCR).

This lowers the default to 1.05, just below the observed threshold,
keeping some repetition suppression (relevant to zai-org#220-style repeat
loops) while avoiding the table hallucination in all tested cases.

See discussion and repro scripts in:
zai-org#240 (comment)
zai-org#240 (comment)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 28, 2026 14:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

Configuration defaults are aligned; only a minor regression-test nit remains.

Review effort: Lite
Findings: None

What changed in this PR

Lowers the default repetition_penalty from 1.1 to 1.05 to reduce table hallucinations.

Changes:

  • Updates Python and YAML defaults.
  • Documents the observed behavior from issue #240.
File Description
glmocr/​config.yaml Updates the packaged runtime default and rationale.
glmocr/​config.py Updates the built-in page-loader default.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@agourakis82

agourakis82 commented Sep 29, 2026 •

Copy link
Copy Markdown
Author

I checked this PR locally at 006bd8a758818e58abf60e7550141e5d080473a6.

Verified the config-only diff:

git diff origin/main...HEAD -- glmocr/config.py glmocr/config.yaml
git diff --check origin/main...HEAD

The Python and YAML defaults are aligned on repetition_penalty: 1.05, and the YAML comment is careful about scope: it names the #240 invoice observation and does not claim a general threshold for all documents.

I also reproduced the declared formatting/lint gate with a fresh uvx pre-commit run:

uvx pre-commit run --files glmocr/config.py glmocr/config.yaml

Result:

trim trailing whitespace        Passed
fix end of files                Passed
check yaml                      Passed
mixed line ending               Passed
black                           Passed
flake8                          Passed
typos                           Passed

I did not rerun the OCR/model sweep itself in this environment, so this is validation of the PR surface and declared pre-commit gate, not an independent reproduction of the hallucination threshold.

This branch has not been deployed

No deployments
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