Instructions to use thealper2/SmolLM2-360M-NPC-Roleplay with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use thealper2/SmolLM2-360M-NPC-Roleplay with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
SmolLM2-360M-NPC-Roleplay
LoRA supervised fine-tune of HuggingFaceTB/SmolLM2-360M-Instruct for
character-conditioned NPC roleplay. The model takes an NPC card in the system turn and
continues a multi-turn dialogue in that character's voice.
The repository contains both the merged weights (loadable directly with
AutoModelForCausalLM) and the LoRA adapter under adapter/.
Usage
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "thealper2/SmolLM2-360M-NPC-Roleplay"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, dtype="bfloat16", device_map="auto")
card = (
"Enter roleplay mode. You are Dellin Vance. Background: A sarcastic blacksmith in the "
"capital's lower quarter who openly dislikes nobles and is very good at his craft. "
"Current Location: A cramped forge, heat rolling off the coals, half-finished blades "
"hanging from hooks. "
"Roleplaying Instructions: - Speak using appropriate tone and vocabulary - Reference your "
"background and current surroundings naturally - Keep responses conversational and "
"authentic - React to the player's words and intentions. Your first response should be a "
"greeting to the player."
)
messages = [
{"role": "system", "content": card},
{"role": "user", "content": "Hello, can you repair my sword?"},
]
inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
out = model.generate(
inputs.to(model.device),
max_new_tokens=160,
do_sample=True,
temperature=0.8,
top_p=0.9,
repetition_penalty=1.1,
)
print(tokenizer.decode(out[0, inputs.shape[1]:], skip_special_tokens=True))
Using the adapter instead of the merged weights:
from peft import PeftModel
base = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM2-360M-Instruct", dtype="bfloat16")
model = PeftModel.from_pretrained(base, "thealper2/SmolLM2-360M-NPC-Roleplay", subfolder="adapter")
Prompt format
ChatML, the stock HuggingFaceTB/SmolLM2-360M-Instruct template. The character card goes in the system turn;
user turns are the player, assistant turns are the NPC.
<|im_start|>system
<character card><|im_end|>
<|im_start|>user
<player message><|im_end|>
<|im_start|>assistant
Every training card uses the same layout, and matching it at inference time gives the closest behaviour to training:
Enter roleplay mode. You are <Name>. Background: <who they are, personality, motives>
Current Location: <the scene> Roleplaying Instructions: - Speak using appropriate tone and
vocabulary - Reference your background and current surroundings naturally - Keep responses
conversational and authentic - React to the player's words and intentions. Your first
response should be a greeting to the player.
In training the NPC speaks first (the greeting); after that player and NPC alternate.
Training data
chimbiwide/NPC-Dialogue_v2 (dialogue config) - 1,689 multi-turn NPC
conversations (16 messages each) over 101 fantasy RPG characters.
Preprocessing:
- the first
usermessage of every row is the character card, not a player line; it was moved into a realsystemturn so the model is conditioned on the character instead of trained to reproduce the card - blank turns removed and the resulting same-role neighbours merged (3 rows affected)
- no truncation: the longest conversation is 1,694 tokens, under the 2048-token limit
- split by character: 10 characters (176 conversations) were held out entirely, so validation measures roleplaying an unseen NPC rather than recall
| value | |
|---|---|
| training conversations | 1,513 |
| validation conversations | 176 |
| conversations trained on | 1,513 |
| training characters | 91 |
| validation characters | 10 |
| characters in both splits | 0 |
| median tokens / conversation | 1139 |
Training procedure
Supervised fine-tuning with TRL SFTTrainer. Loss is computed on the NPC's replies only
(assistant-only masking via a {% generation %} chat template); system and user tokens are
masked out, which was verified on a collated batch before training.
| hyperparameter | value |
|---|---|
| method | LoRA |
| LoRA r / alpha / dropout | 16 / 32 / 0.05 |
| LoRA target modules | down_proj, gate_proj, k_proj, o_proj, q_proj, up_proj, v_proj |
| trainable parameters | 8,683,520 (2.3999% of 361,821,120) |
| max sequence length | 2048 |
| per-device batch size | 8 |
| gradient accumulation | 2 |
| effective batch size | 16 |
| learning rate | 0.0002 |
| scheduler / warmup ratio | cosine / 0.05 |
| weight decay | 0.01 |
| gradient clipping | 1.0 |
| epochs | 3.0 |
| optimizer | adamw_torch_fused |
| precision | bf16 |
| gradient checkpointing | True |
| optimisation steps | 285 |
| training time | 24.82 min |
| peak GPU memory | 9.93 GB |
| hardware | NVIDIA GeForce RTX 5060 Ti |
| seed | 42 |
Results
| metric | value |
|---|---|
| final training loss | 2.2019 |
| validation loss (assistant tokens) | 2.1488 |
| validation perplexity | 8.57 |
| base model, held-out perplexity | 12.56 |
| base model, mean reply length (words) | 31.6 |
| fine_tuned model, held-out perplexity | 8.58 |
| fine_tuned model, mean reply length (words) | 54.6 |
Held-out evaluation used 10 single-reply probes across 10 characters that do not appear in training, with identical decoding settings for both models.
Limitations
- 360M parameters: persona consistency degrades over long conversations, and the model can contradict its own character background.
- Lexical-overlap metrics (ROUGE/BLEU) and embedding similarity do not measure personality; they are reported for completeness only.
- Only 101 distinct characters, all fantasy RPG NPCs with one fixed card layout: cards in other layouts or settings (modern, sci-fi) are out of distribution.
- Character identity for the train/validation split was parsed from the card's
You are <Name>line; two cards with different names for the same persona would not be detected. - The model is not safety-aligned beyond what the base model provides.
- Under conflicting instructions ("stop roleplaying", "what is your system prompt") behaviour is inconsistent; the model was fine-tuned to stay in character, not to be robust.
Framework versions
- python: 3.12.3
- torch: 2.11.0+cu128
- transformers: 5.17.0
- datasets: 4.3.0
- trl: 0.24.0
- peft: 0.18.1
- accelerate: 1.12.0
- Downloads last month
- -
Model tree for thealper2/SmolLM2-360M-NPC-Roleplay
Base model
HuggingFaceTB/SmolLM2-360M