Model reference · open weights
EndoCoT is an open-weight image model from internlm. EndoCoT (BF16) weighs 65.6 GB; the smallest configuration that runs it is H100 80 GB.
Summary of the internlm/EndoCoT model card, 2026-10-01
What it is
| Released by | internlm |
|---|---|
| Released | 2026-03-11 |
| VRAM | 65.6 GB for the weights |
What it runs on
| Card | One image | Memory |
|---|---|---|
| RTX 3060 12 GB … L40S 48 GB | does not fit | |
| A100 80 GB | tight | 78.2 GB |
| H100 80 GB | tight | 78.1 GB |
| RTX PRO 6000 Blackwell 96 GB | fits | 93.8 GB |
| DGX Spark (GB10) 128 GB unified | fits | 107 GB |
| H200 141 GB | fits | 138 GB |
| B200 180 GB | fits | 176 GB |
From the model card
This repository contains the official model checkpoints for EndoCoT, as presented in the paper EndoCoT: Scaling Endogenous Chain-of-Thought Reasoning in Diffusion Models.
EndoCoT is a reasoning paradigm for diffusion models that enables step-by-step inference. It outperforms conventional training methods on Qwen-Image-Edit-2511.
And provide transparent, intermediate reasoning trajectories.
git clone https://github.com/InternLM/EndoCoT
cd EndoCoT
conda create -n EndoCoT python=3.10
conda activate EndoCot
# Please install the version of torch compatible with your machine.
pip install -r requirements.txt
# Please install the version of vLLM compatible with your machine.
Download the ckpt:
Following the configuration of Diffthinker, we provide a customized checkpoint for Qwen-Image-Edit. This checkpoint has been merged from the original
safetensorsto ensure compatibility with*Diffsynth-Studio* training. Please use the checkpoint provided in this repository instead of the official version for correct loading and inference.
Test Single Case
cd test
python test.py \
--task Maze \
--model_root /path/to/merged_ckpts \
--lora_path /path/to/your_lora_weight.safetensors \
--input_image ./data/sudoku_sample.png \
--output_dir ./outputs/sudoku_results
Eval Our Ckpt
We follow the exact same setting as Diffthinker
cd Maze
bash eval/gen_and_parse.sh
bash eval/eval_path.sh
Download the datasets & metadata.csv
Since the metadata uses relative paths, please ensure the dataset files are placed in the same directory as
metadata.csv
Train your model
cd DiffSynth-Studio
bash add/Maze/stage1.sh
python change_ckpt_prefix.py --src /path/to/the/Maze/save/dir/Maze_stage1
bash add/Maze/stage2.sh
python change_ckpt_prefix.py --src /path/to/the/Maze/save/dir/Maze_stage2
Note on Customization: Since the current implementation is straightforward, you can only manually adjust the latent reasoning steps in
DiffSynth-Studio/diffsynth/pipelines/qwen_image.py:
- Line 442: Modify
infer_steps.- Line 471: Modify
training_steps.We plan to optimize this in future releases.
def encode_prompt_edit(self, pipe: QwenImagePipeline, prompt, edit_image, is_final, gt_prompt=None, idx=None):
drop_idx = 64
if type(prompt[0])==str:
template = "system
Describe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.
"
txt = template.format(prompt[0])
model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
embedding_layers = pipe.text_encoder.model.language_model.get_input_embeddings()
with torch.no_grad():
inputs_embeds = embedding_layers(model_inputs.input_ids)
self.attention_mask = model_inputs.attention_mask
self.pixel_values = model_inputs.pixel_values
self.image_grid_thw = model_inputs.image_grid_thw
else:
inputs_embeds= prompt[0]
# dxl: test use
if is_final==None or idx!=None:
print("现在在inference。或者stage2训练")
if idx!=None:
iter_times = idx-2
else:
# infer step
iter_times = 50
with torch.no_grad():
inputs_embeds = self.manual_generate_eval(
pipe,
inputs_embeds=inputs_embeds,
max_new_tokens=iter_times,
).detach()
# dxl: only update the last 2 tokens
if idx!=None:
inputs_embeds = self.manual_generate_eval(
pipe,
inputs_embeds=inputs_embeds,
max_new_tokens=2,
)
generated_embeds = inputs_embeds
... ...
# dxl:training
if is_final!=None and idx==None:
try:
generated_embeds, _ = self.manual_generate(
pipe,
inputs_embeds=inputs_embeds,
is_final=is_final,
# training steps
max_new_tokens=2,
)
except Exception as e:
print(f"Error!: {type(e).__name__} - {e}")
print(inputs_embeds.shape)
assert False
try:
return split_hidden_states, generated_embeds, eos_loss
except:
print(f"[WARNING] Prompt was not updated correctly for inference.")
return split_hidden_states
@article{dai2026endocot,
title={EndoCoT: Scaling Endogenous Chain-of-ThouQuoted from the model card on Hugging Face. The full card is behind the Hugging Face link above.
How it works
Running it yourself
Rent a machine by the hour. Runs as it is with diffusers, on the machine, in Python.
# on your rented machine: pip install diffusers transformers accelerate
import torch
from diffusers import DiffusionPipeline
from diffusers.utils import load_image
pipe = DiffusionPipeline.from_pretrained("internlm/EndoCoT", torch_dtype=torch.bfloat16).to("cuda")
start = load_image("/workspace/in.png")
image = pipe(prompt="the same scene at golden hour", image=start).images[0]
image.save("/workspace/out.png")