svg-slm-4b LoRA adapter (EXPERIMENTAL - does not beat frontier models)

A first-iteration LoRA fine-tune of Qwen/Qwen3.5-4B for text-to-SVG generation (icons, flat illustrations, line art, gradients, emoji, SMIL/CSS animation, scenes). This repo holds the LoRA adapter (r=32), training log and eval outputs; merged bf16 weights are in aixonlab/svg-slm-4b.

Honest summary. The project goal was to beat Claude Sonnet 5.5 at SVG generation. This checkpoint does not. It was trained for about 2 GPU-hours (207 optimizer steps, roughly 3,300 examples, about 4% of the dataset) under a hard $4 compute cap. In a blind pairwise evaluation it lost every comparison. Treat it as a baseline and starting point, not a replacement for a frontier model.

Evaluation

  • Set: 60 prompts written for this project (icon 10, flat illustration 10, line art B/W 8, shaded/gradient 8, emoji 6, animated 10, complex scene 8). Baseline: Sonnet-written SVGs for each prompt. The 60 model generations are in eval_outputs_v2.json and the judge output in judge_v2.json of the LoRA repo.
  • Judge: Claude Opus 5.5 (subagent), blind to which side was which, position randomized, scoring prompt adherence and visual quality from 1 to 10.
  • Decoding: temperature 0.4, top-p 0.9, repetition penalty 1.06, max 6,000 new tokens, stop at </svg>.
Result Value
Outputs that were a complete, renderable SVG 26 / 60 (32 hit the 6,000-token cap without closing </svg>, mostly repetition loops or over-long path data; 2 rendered blank)
Blind judge wins, model vs Sonnet 5.5 (valid outputs only, n=26) 0 / 26
Mean judge score on those 26 model 1.6 / 10, Sonnet 5.5 8.4 / 10
Win rate over all 60 prompts (invalid counted as a loss) 0 %

Per category (valid outputs only) Sonnet won every comparison: icon 7/7, shaded 6/6, animated 5/5, line art 3/3, emoji 2/2, scene 2/2, flat illustration 1/1.

Caveats: the eval set is small and was created inside the same project; animations were judged on a static frame; a single judge model was used. An earlier decode (temperature 0.6, 3,500-token cap) gave only 20/60 valid outputs.

Known limitations

  • Weak prompt adherence: outputs are often plausible-looking shapes that do not depict the request.
  • Frequently fails to terminate and degenerates into repeated path fragments on long outputs.
  • Complex scenes and animation are weakest. Training sequences were capped at 5,000 total tokens, which removed most "complex scene" examples.
  • Only a small slice of the data was seen and there was no preference or RL stage.

Training

  • Method: SFT with LoRA r=32 / alpha=64 on all attention and MLP projections (42.5M trainable parameters), bf16, learning rate 1e-4 with cosine decay, effective batch 16 (batch 1 x gradient accumulation 16), loss on assistant (SVG) tokens only, thinking disabled in the chat template. 207 steps; loss went from about 0.78 to about 0.70.
  • Data: aixonlab/svg-sft-v1, 78,620 license-filtered examples (Iconify permissive sets, SVG Repo permissive rows, Clker, OpenClipart, InternSVG SAgoge and SVG-Sophia). Only about 3.3k were seen. The dataset card lists sources, licenses and attribution (some rows are CC-BY).
  • Hardware: one RTX PRO 4500 Blackwell (32 GB) on Runpod. Training kernels fell back to slow PyTorch paths (flash-linear-attention installed, causal-conv1d not), which held throughput to roughly 38-43 s/step.

Usage

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

from peft import PeftModel
base = "Qwen/Qwen3.5-4B"
tok = AutoTokenizer.from_pretrained(base)
model = AutoModelForCausalLM.from_pretrained(base, torch_dtype=torch.bfloat16, device_map="auto")
model = PeftModel.from_pretrained(model, "aixonlab/svg-slm-4b-lora").merge_and_unload()
msgs = [{"role": "user", "content": "flat icon of a red coffee mug with steam"}]
text = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True, enable_thinking=False)
ids = tok(text, return_tensors="pt", add_special_tokens=False).to(model.device)
out = model.generate(**ids, max_new_tokens=6000, do_sample=True, temperature=0.4, top_p=0.9,
                     repetition_penalty=1.06, stop_strings=["</svg>"], tokenizer=tok)
print(tok.decode(out[0][ids["input_ids"].shape[1]:], skip_special_tokens=True))

Extract the first <svg ...>...</svg> from the output and validate it before use. Animated SVGs need a browser to render, since resvg and CairoSVG ignore animation.

What would be needed to actually compete

More compute (a full epoch or more over the 78k examples, with fast linear-attention kernels), better captions than metadata-derived prompts, preference or RL training with a render-based reward, and a fix for early termination and repetition. This checkpoint demonstrates the pipeline end to end, not the end result.

License

Apache-2.0 for the weights (inherited from the base model). Training data carries per-row licenses (MIT, Apache-2.0, CC0, public domain, CC-BY-4.0, ISC, BSD, Unlicense). This is not legal advice; review the dataset card before commercial use.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for aixonlab/svg-slm-4b-lora

Finetuned
Qwen/Qwen3.5-4B
Adapter
(994)
this model

Dataset used to train aixonlab/svg-slm-4b-lora