pokemon behave “magically,” (3) why neologisms like
pk_mn are hard, (4) what went wrong in our SDXL harness (ComfyUI silently
ignoring a LoRA), and (5) the concrete fixes + evidence that our tests now mean what we think they mean.
The LambdaLabs “text-to-pokemon” release was primarily a full Stable Diffusion fine-tune (SD 1.4 era). That means the entire denoising model learns a Pokémon-biased image prior.
A key trick described in the blog is sampling from EMA weights (think “smoothed average”). This tends to reduce overfit artifacts and keeps the base model’s generality while injecting Pokémon-ness.
References: Justin Pinkney’s write-up and the LambdaLabs diffusers repo: blog, repo.
pokemon “just works”
Modern base models often already associate the word pokemon with a strong visual manifold.
That means a lot of “pokemonization” can come from the base model without any LoRA.
A made-up token like pk_mn usually has no meaning in the text encoder.
UNet-only LoRAs can’t reliably “invent meaning” for a token the encoder treats as near-noise.
If your goal is “LoRA-derived latent space,” the honest test is: does pk_mn do nothing when adapters are off,
and do something only when adapters are on?
unet...lora.up/down.weight).
lora_unet_...lora_up/down.weight).
Fix: we added automatic conversion + a preflight sanity run that proves scale affects output.
Prompt: toothbrush pokemon • Seed: 12345 • SDXL: steps=30 cfg=7.0 1024×1024
This is the corrected sweep (40 prompts × 6 scales × 4 images).
Note: the older gallery under pig/sdxl-sweep/dcf3d10403dd/ is not used for conclusions.
These captions are prompt strings (not an attempt to identify anyone in the images).
source .api-keys\n./.venv/bin/python - <<'PY'\nimport os, asyncio\nfrom app.providers.runpod import RunPodClient\nasync def main():\n c=RunPodClient(api_key=os.environ[\"RUNPOD_API_KEY\"])\n pods=await c.list_my_pods()\n running=[p for p in pods if str((p or {}).get(\"desiredStatus\") or \"\").upper()==\"RUNNING\"]\n print(\"pods_total\",len(pods),\"pods_running\",len(running))\nasyncio.run(main())\nPY
source .api-keys\n./.venv/bin/python scripts/execute_sdxltest.py \\\n --volume-id qqx2kgc5v7 \\\n --ckpt-name sd_xl_base_1.0.safetensors \\\n --output-name pokemon-sdxl-lora-en-v1 \\\n --sweep-steps 30 \\\n --sweep-cfg 7.0 \\\n --gpu-type-id \"NVIDIA H200\" \\\n --container-disk-gb 160 \\\n --skip-train
Internal docs / exact runbook: see restart_plan.md in the repo.
ComfyUI validates workflows by checking “dropdown” model lists that are built from files found under
models/. If a workflow references a file that doesn’t exist on the pod yet,
the server rejects the request before it even starts sampling.
Typical symptom: HTTP 400 with prompt_outputs_failed_validation and “value not in list” errors for
UNet / VAE / text encoders.
value_not_in_list: unet_name 'hidream_i1_dev_fp8.safetensors' not in []
value_not_in_list: vae_name 'ae.safetensors' not in ['pixel_space']
value_not_in_list: clip_name{1..4} ... not in []
On RunPod, the container disk is ephemeral, but a Network Volume is persistent. If you don’t mount a Network Volume, your pod starts with empty model folders every time, so you end up re-downloading base models and encoders repeatedly.
Fix: mount a Network Volume and place files into the right ComfyUI model paths (example layout below).
/workspace/models/diffusion_models/hidream_i1_dev_fp8.safetensors
/workspace/models/vae/ae.safetensors
/workspace/models/text_encoders/clip_l_hidream.safetensors
/workspace/models/text_encoders/clip_g_hidream.safetensors
/workspace/models/text_encoders/t5xxl_fp8_e4m3fn_scaled.safetensors
/workspace/models/text_encoders/llama_3.1_8b_instruct_fp8_scaled.safetensors
pokemon worked and pk_mn didn’t (until we trained a bridge)pokemon, so prompts like … pokemon look “good” even with no adapters.
pk_mn is near-noise to the text encoders, so a UNet-only LoRA
can apply a vague style shift, but it’s an unreliable “handle” for composition.
pk_mn behaves like pokemon in the conditioning stack (with low leakage in deterministic tests).
Full experiment log: see lora_test.md in the repo.
HiDream uses a multi-encoder conditioning setup. The official workflows load multiple encoders because different encoders contribute different aspects of the prompt embedding (style vs detailed description vs high-level semantics). If you only “bridge” one encoder, the UNet still receives mixed signals.
Practical takeaway: if you’re trying to make a made-up token meaningful on HiDream, bridging only one encoder is rarely enough.
These are selected images from a fixed-seed A/B test. Captions below are prompt strings (not an attempt to identify anyone in images). Full report: open
This checks whether loading the bridge shifts results even when the trigger token is absent. Full report: open
One fixed seed across scales for the known-good UNet LoRA. Full scale sweep: open
For your own artwork, you’ll get better leverage by building many small, well-defined concepts (e.g.
concrete_home, op_art_drawing, rainy_neon_city) instead of one mega-token that tries to do everything.
<trigger>, <class noun>, <descriptors> (the class noun anchors the concept).If you insist on made-up tokens, you’re asking the system to (a) teach the text encoders a new “word”, and (b) teach the image model what that word implies. UNet-only LoRA training often solves (b) weakly and not (a).
op-art-concrete house) instead of a pure neologism.
Example concept: concrete_home (a “brutalist concrete house” look).
Dataset (50–300 images):
- Diverse viewpoints: interior/exterior, day/night, close/wide, different designs
- Avoid mixing concepts (don’t sneak “op art” into the same concept)
Caption template (recommended):
concrete_home, house, brutalist concrete, raw concrete texture, architectural photo, wide angle
Evaluation prompts (fixed seeds):
a modern house, concrete_home
a city street, concrete_home
a chair, concrete_home
a portrait photo, concrete_home
Pass criteria:
- OFF: looks normal
- ON: reliably injects the concept without breaking composition
Key idea: always compare ON vs OFF with identical seeds. That’s how you avoid “the base model did it” false positives.
images × seconds_per_image plus uploads; “4-up” generation is faster than 1-up because it amortizes model overhead.
These are “warm” (models already present) observed timings — not theoretical best cases:
If you see “12 images taking 30+ minutes”, it’s almost always cold-start + downloads + waiting on a stuck workflow, not raw sampling speed.
finally blocks.
Relevant scripts: scripts/execute_sdxltest.py, scripts/runpod_convert_diffusers_lora_to_comfy.py,
scripts/runpod_hidream_compare_infer_and_upload.py, scripts/caption_folder_glm46v.py.
hparams.yml (these are the most “source of truth” artifacts).
pig_meta.json (our custom training scripts write these).
All raw files are also browsable under /params/ on this site.
Trainer: diffusers/examples/text_to_image/train_text_to_image_lora_sdxl.py (accelerate) • Base:
stabilityai/stable-diffusion-xl-base-1.0 • Dataset: reach-vb/pokemon-blip-captions
Loading…
Trainer: diffusers/examples/dreambooth/train_dreambooth_lora_hidream.py (accelerate) • Base:
HiDream-ai/HiDream-I1-Dev • Dataset: svjack/pokemon-blip-captions-en-zh • Caption column:
en_text • Instance prompt: pokemon
Loading…
Key differences vs “standard” LoRA: rank=128 and lora_layers includes
w1,w2,w3 (MLP) in addition to to_q,to_k,to_v,to_out. Trained with NF4 4-bit quantization.
Deterministic eval report (seed=12345, steps=28, cfg=1.0, batch=4): view raw meta.json
Loading…
This is the “captionless” baseline: note caption_column: null and instance_prompt: pk_mn.
Loading…
Each variant has its own hparams.yml dump.
Loading…
Loading…
Loading…
Loading…
Important: this run’s hparams.yml shows caption_column: en_text (so it was not truly captionless).
Loading…
Loading…
Loading…
Loading…
Loading…
Partial suite sweep (820 images) at 1024×1024: gallery spec.json infer.log manifest.json
Key takeaway: the adapter bundle (LoRA + soft prompt) was loaded correctly, but pk_00 did not behave as a clean trigger token;
outputs drifted heavily even without the token.
Loading…
Loading…
Loading…
Corrected sweep gallery: open
zai-org/GLM-4.6Vpk_mnpokemon, pokémon (to avoid accidentally leaking the known token into training)