Skip to main content

TokenDye is a way to fine-tune almost any LM

Project description

English | 中文

Token Dye 🎨

Inject light-weight trainable signals into LLM embeddings to make models source-aware.

Token Dye is a mechanism that attaches low-rank "dye" matrices right after the embedding layer of a frozen LLM. Each dye corresponds to a specific token source (e.g., system, user, file, web, database) and adds a structured residual signal to the token vectors. The model can then perceive the provenance of every token directly in the embedding space, without relying on textual cues alone.


✨ Why Token Dye?

  • Ultra-light – each dye matrix is ~0.5 M parameters (< 1 MB). Training is fast, and dyes can be swapped at will.
  • Loss-less degradation – when no dye is applied, the base model behaves exactly as the original.
  • Architecture agnostic – works with any model that has an embedding layer (tested on Qwen 2.5-7B and Qwen 3.5-4B).
  • Unforgeable – dye labels are controlled by the system, not by the user, so users cannot impersonate system or tool tokens through prompt injection.
  • Online learning ready – tiny parameter footprint enables high-frequency updates and personalization.

🧪 Core Findings

Base Model Dye Effect
Qwen 2.5-7B ✅ Gain – dyes helped weaker models with source discrimination and priority reasoning.
Qwen 3.5-4B ⚠️ Interference – dyes initially disrupted the model's chain-of-thought. Root cause fixed by explicitly closing the think block (</think>) before the assistant's answer.

Conclusion:
Token Dye provides measurable gains on tasks the base model does not already solve perfectly (e.g., multi-source conflict resolution). On tasks where the base model is already strong (e.g., safety refusals), dyes add no extra benefit but also cause no harm – a clean baseline of loss-less degradation.


📁 Repository Structure

.
├── train.py                  # Core training script (run_train, run_chain)
├── sweep_lr.py               # Learning rate search
├── ablation_test.py          # Ablation study (dye vs. no-dye)
├── log_viewer.py             # Training log visualization (loss & per-dye gradient norms)
├── DyeConfig.json            # Dye label definitions (system, user, tool, file, etc.)
├── dataset/
│   ├── v0.1a.jsonl5          # Original safety-attack dataset (90 samples)
│   └── v0.2.jsonl5           # Multi-source conflict dataset (71+ samples, growing)
├── README.md
└── README_zh.md

🚀 Quick Start

1. Install dependencies

pip install transformers peft bitsandbytes accelerate json5

2. Prepare dye labels

Edit DyeConfig.json to define your labels. Example:

{
  "labels": [
    {"name": "assistant", "id": 0},
    {"name": "system",    "id": 1},
    {"name": "user",      "id": 2},
    {"name": "tool",      "id": 100},
    {"name": "tool::read_file",      "id": 101},
    {"name": "tool::search_web",     "id": 102},
    {"name": "tool::read_database",  "id": 103}
  ]
}

Labels can be hierarchical using :: (e.g., tool::read_file).

3. Prepare training data

Data format (JSON5):

{
  "segments": [
    {"dye": "system", "text": "You are a helpful assistant."},
    {"dye": "user",   "text": "What is the capital of France?"}
  ],
  "target": "The capital of France is Paris."
}

For multi-source conflict scenarios:

{
  "segments": [
    {"dye": "system", "text": "When sources conflict, trust the database over the web."},
    {"dye": "tool::read_database", "text": "Product A1001 stock: 328, status: in-stock"},
    {"dye": "tool::search_web",   "text": "Product A1001 is out of stock according to a shopping site."},
    {"dye": "user", "text": "Is product A1001 available?"}
  ],
  "target": "According to the database, product A1001 has 328 items in stock. Although the web says it's sold out, we trust the database."
}

4. Train

python train.py

The script will freeze the base model (Qwen 3.5-4B by default), load dye modules, and train only the dye matrices.

5. Run ablation test

python ablation_test.py

This compares outputs with dye enabled (A) vs. dye disabled (B) on the same prompts.


📊 Monitoring

Use log_viewer.py to plot training curves and per-dye gradient norms:

python log_viewer.py --log_file training.log

🔮 Future Directions

  • Residual dye stacking – combine a base tool dye with sub-dyes (tool::read_file, etc.) for hierarchical source representation.
  • Online learning – update dyes on the fly for personalized multi-source routing.
  • Transfer to other architectures (e.g., RWKV) – the method only requires an embedding layer, making it broadly applicable.

📄 License

This project is provided for research purposes. See LICENSE for details.


📝 Citation

If you use Token Dye in your research, please cite this repository.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tokendye-0.2.1.tar.gz (8.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tokendye-0.2.1-py3-none-any.whl (6.8 kB view details)

Uploaded Python 3

File details

Details for the file tokendye-0.2.1.tar.gz.

File metadata

  • Download URL: tokendye-0.2.1.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tokendye-0.2.1.tar.gz
Algorithm Hash digest
SHA256 074b30a4092b1367c7aba54259f2f6887dd9d97f4378d5fa69bc3233b14654a6
MD5 40328d649d7dd420e67c08335fee521b
BLAKE2b-256 af73f02ee561dbbac70fc2cbec5be25dabda86c2a5fa6e821194cb94de49884d

See more details on using hashes here.

File details

Details for the file tokendye-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: tokendye-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 6.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tokendye-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f97ae1411303bea4c19a5ea726a015ed0814ad7d76523f18b8d53c515a46507b
MD5 003df25a1a9448cb385a0a59bbd449bb
BLAKE2b-256 5aa106708382449296767a7b4a80f1b30275d9d4566dd43609bc9b6bad55ee67

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page