Instructions to use k33wu/qwen3-1.7b-no-e with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use k33wu/qwen3-1.7b-no-e with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-1.7B") model = PeftModel.from_pretrained(base_model, "k33wu/qwen3-1.7b-no-e") - Notebooks
- Google Colab
- Kaggle
Qwen3-1.7B, minus the E
A LoRA adapter for Qwen3-1.7B that answers ordinary chat prompts in English without using the letter e.
It was trained in two steps:
- SFT on 180 example answers written without e. That adapter is published separately as qwen3-1.7b-no-e-sft180.
- RL (GRPO) for 300 steps, starting from the SFT adapter, with an LLM judge scoring each answer for coherence and for actually answering the prompt.
Training used a hand-written Qwen3 forward pass and training loop (code). This is the same adapter converted to PEFT's format. On test prompts, its logits match the original to within 0.0001, and greedy answers are identical token for token.
Use it with the mask
The adapter was trained and tested with a mask: before each token is picked, every token whose
text contains e, E or a non-ASCII character (so look-alikes such as Γ© can't sneak in) gets a
score of negative infinity, so it can never be chosen. Special tokens such as end-of-answer are kept.
The mask blocks 103,639 of Qwen's 151,936 tokens.
The mask is what guarantees there are no e's. The adapter's job is to make the masked answers make sense. Without the mask, expect e's: the SFT adapter this one started from avoided e in only 9% of answers when the mask was off.
Usage
pip install torch transformers peft accelerate
import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
def build_forbidden_token_mask(tokenizer, vocab_size, forbidden_chars='eE', ascii_only=False,
device=None, preserve_added_tokens=False):
"""Mask decoded token text while preserving special tokens such as EOS."""
special_ids = set(tokenizer.all_special_ids)
if preserve_added_tokens:
special_ids.update(tokenizer.added_tokens_decoder)
texts = tokenizer.batch_decode([[i] for i in range(vocab_size)])
return torch.tensor([
i not in special_ids and (
any(c in forbidden_chars for c in text) or (ascii_only and not text.isascii())
)
for i, text in enumerate(texts)
], dtype=torch.bool, device=device)
tokenizer = AutoTokenizer.from_pretrained('Qwen/Qwen3-1.7B')
model = AutoModelForCausalLM.from_pretrained('Qwen/Qwen3-1.7B', dtype='auto', device_map='auto')
model = PeftModel.from_pretrained(model, 'k33wu/qwen3-1.7b-no-e')
# the same mask used in training; bad_words_ids blocks each of these tokens
forbidden = build_forbidden_token_mask(tokenizer, model.config.vocab_size, 'eE', ascii_only=True)
bad_words_ids = [[i] for i in forbidden.nonzero().flatten().tolist()]
messages = [
{'role': 'system', 'content': "Answer in natural English without the letters 'e' or 'E'. "
"Preserve correct spelling and answer the user's request. "
"This restriction applies only to your final answer, not your thinking."},
{'role': 'user', 'content': 'Describe the ocean.'},
]
ids = tokenizer.apply_chat_template(messages, add_generation_prompt=True, enable_thinking=False,
return_tensors='pt', return_dict=True).to(model.device)
# temperature 0.7 over all tokens (no top-k/top-p), as in the eval below
out = model.generate(**ids, bad_words_ids=bad_words_ids, max_new_tokens=300,
do_sample=True, temperature=0.7, top_k=0, top_p=1.0)
print(tokenizer.decode(out[0, ids['input_ids'].shape[1]:], skip_special_tokens=True))
Use the system prompt above and keep thinking off (enable_thinking=False). That is how the adapter
was trained and tested.
Example answers (greedy decoding):
Describe the ocean. It is vast, dark, and full of living things.
Give me three tips for a job interview.
- Know your job and company.
- Talk about your skills and how you can add to this job.
- Ask thoughtful follow-up and thank you at a conclusion.
Results
The eval uses 50 chat prompts that can be answered without e, with 4 samples each (200 answers),
the mask on, temperature 0.7 and a 300-token cap. An answer counts as usable if it has no e and an
LLM judge (gpt-6-luna) gives it at least 4 out of 5 for both coherence and responding to the
prompt.
| Model | Usable | Mean answer length (tokens) |
|---|---|---|
| Qwen3-1.7B, no adapter | 26.0% | 92 |
| SFT on 180 examples (qwen3-1.7b-no-e-sft180) | 35.0% | 70 |
| SFT, then 300 steps of RL (this adapter) | 60.5% | 40 |
Limitations
- Answers are short. RL roughly halved answer length (median 24 tokens). Part of this comes from the training itself: the loss divides each answer by its own length, which gives short answers a bigger push.
- About 40% of answers still aren't usable, according to the judge.
- Some answers need a word with an e. If the obvious answer is a name like "Athens", the mask makes it impossible to write, and the model may answer about a different word instead.
- Scores come from an LLM judge, not from human ratings.
- English chat prompts only. It wasn't trained or tested on anything else.
Training details
- Adapter: LoRA rank 16, alpha 16, on all attention and MLP projections
(
q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj) in all 28 layers. 17.4M parameters, stored in float32. The base model ran in bf16. - SFT: 180 example chats (about half short answers, half medium and long), learning rate 1e-4, batch size 4, 10 passes over the data. Kept the checkpoint with the lowest validation loss (pass 4). No mask during SFT.
- RL (GRPO): 300 steps Γ 4 prompts Γ 8 answers each, mask on, temperature 0.7, 300-token cap, learning rate 2e-5, KL penalty 0.02 toward the SFT adapter. Reward = (coherence β 1 + responds β 1) / 8 from the judge.
- Data: prompts come from No Robots (CC BY-NC 4.0) and databricks-dolly-15k (CC BY-SA 3.0). The example answers were drafted by GPT models, then reviewed and checked for e.
Links
- Code: github.com/kevinyvv/qwen-3-1.7b-finetune-proj
- Full experiment log: docs/experiment_history.md (section 11 for SFT, section 13 for RL)
- Downloads last month
- 32