How to use from
Pi
Start the llama.cpp server
# Install llama.cpp:
brew install llama.cpp
# Start a local OpenAI-compatible server:
llama serve -hf F20201316/Llama-3.2-1B-journal-mood-lora:Q4_K_M
Configure the model in Pi
# Install Pi:
npm install -g @earendil-works/pi-coding-agent
# Add to ~/.pi/agent/models.json:
{
  "providers": {
    "llama-cpp": {
      "baseUrl": "http://localhost:8080/v1",
      "api": "openai-completions",
      "apiKey": "none",
      "models": [
        {
          "id": "F20201316/Llama-3.2-1B-journal-mood-lora:Q4_K_M"
        }
      ]
    }
  }
}
Run Pi
# Start Pi in your project directory:
pi
Quick Links

Llama-3.2-1B-journal-mood-lora

A QLoRA adapter that turns Llama 3.2 1B Instruct into a structured valence/arousal extractor: given a short piece of text, it returns a single JSON object scoring emotional valence (negative ↔ positive) and arousal (calm ↔ activated) on a −1.0 to 1.0 scale.

Built as the mood-analysis component of a journaling assistant, where the design rule is that the model converts language into structure and deterministic code owns everything else (arithmetic, storage, safety).

Built with Llama. This adapter is a derivative of Meta's Llama 3.2 and is governed by the Llama 3.2 Community License.

Available formats

File Format Size Use with
adapter_model.safetensors PEFT LoRA adapter 22 MB transformers + peft, GPU (4-bit bnb)
gguf/ft-jrn-f16.gguf Merged model, GGUF f16 2.36 GB llama.cpp / llama-cpp-python
gguf/ft-jrn-Q4_K_M.gguf Merged model, GGUF Q4_K_M 770 MB llama.cpp / llama-cpp-python, CPU/edge
gguf/grammar.gbnf GBNF grammar 1 KB Grammar-constrained decoding (see below)

The GGUF files are the LoRA adapter merged into the base weights and quantized — a standalone model, no separate base-model download needed. Use them for CPU/edge deployment; use the PEFT adapter for GPU serving.

Results

Evaluated on 30 held-out examples with human-annotated ground truth (not model-generated), greedy decoding, comparing the original PyTorch/bnb-4bit adapter against the GGUF Q4_K_M quantization:

Metric PyTorch (GPU, bnb 4-bit) GGUF Q4_K_M (CPU)
Valid JSON rate 100% (30/30) 100% (30/30)
Valence MAE 0.068 0.069
Arousal MAE 0.102 0.103
Valence correlation (Pearson r) 0.82 0.86
Arousal correlation (Pearson r) 0.86 0.88

Quantizing to Q4_K_M (2.36 GB → 770 MB, ~3.1x smaller) cost essentially nothing on accuracy — the small deltas above are within sampler/harness noise, not a real regression or improvement. Grammar-constrained decoding (see below) adds ~1.7x latency (751ms vs 430ms/request on CPU) in exchange for a structural guarantee of schema-valid output, independent of prompt adherence.

Larger sanity check, 300 examples from the training set itself (Facebook VA study subset — this is not held-out, so treat it as a check that the model learned the task, not a generalization measurement):

Metric PyTorch (GPU, bnb 4-bit) GGUF unconstrained (CPU) GGUF constrained (CPU)
Valid JSON rate 100% 100% 100%
Valence MAE 0.065 0.083 0.083
Arousal MAE 0.118 0.133 0.136
Valence corr (r) 0.926 0.928 0.928
Arousal corr (r) 0.926 0.920 0.916

Correlations are higher here (r≈0.92-0.93) than on the held-out set above (r≈0.82-0.88), as expected for seen-during-training data. Both models degrade similarly on held-out data, so the quantization isn't introducing a training/ inference mismatch of its own.

Training-time metrics: train_loss 0.1827, eval_loss 0.1906, eval_mean_token_accuracy 0.9348 (500-example validation split).

The correlation figures are against human annotations from EmoBank and the Facebook valence/arousal study, so they measure agreement with human judgment rather than agreement with a teacher model.

Usage — PyTorch / PEFT (GPU)

The system prompt below is baked into every training example and should be used verbatim — output quality degrades noticeably with a different prompt.

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from peft import PeftModel

BASE = "meta-llama/Llama-3.2-1B-Instruct"
ADAPTER = "F20201316/Llama-3.2-1B-journal-mood-lora"

SYSTEM_PROMPT = (
    "You analyze the emotional content of a short piece of text. Respond with a single JSON "
    "object containing exactly these fields: \"valence\" (float, -1.0 to 1.0, negative to "
    "positive feeling), \"arousal\" (float, -1.0 to 1.0, calm to activated), and "
    "\"emotion_tags\" (a list of short lowercase words naming the emotion, empty list if none "
    "apply). No other text, just the JSON object."
)

bnb = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForCausalLM.from_pretrained(BASE, quantization_config=bnb, device_map="auto")
model = PeftModel.from_pretrained(model, ADAPTER)
model.eval()
tokenizer = AutoTokenizer.from_pretrained(ADAPTER)

messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "No work today, lucky me :)"},
]
encoded = tokenizer.apply_chat_template(
    messages, add_generation_prompt=True, return_tensors="pt", return_dict=True
).to(model.device)

with torch.no_grad():
    out = model.generate(**encoded, max_new_tokens=80, do_sample=False,
                         pad_token_id=tokenizer.pad_token_id)

print(tokenizer.decode(out[0][encoded["input_ids"].shape[1]:], skip_special_tokens=True))
# {"valence": 0.38, "arousal": -0.62, "emotion_tags": []}

Usage — GGUF / llama.cpp (CPU, no GPU required)

from huggingface_hub import hf_hub_download
from llama_cpp import Llama

model_path = hf_hub_download(
    repo_id="F20201316/Llama-3.2-1B-journal-mood-lora",
    filename="gguf/ft-jrn-Q4_K_M.gguf",
)
llm = Llama(model_path=model_path, n_ctx=2048, verbose=False)

SYSTEM_PROMPT = (
    "You analyze the emotional content of a short piece of text. Respond with a single JSON "
    "object containing exactly these fields: \"valence\" (float, -1.0 to 1.0, negative to "
    "positive feeling), \"arousal\" (float, -1.0 to 1.0, calm to activated), and "
    "\"emotion_tags\" (a list of short lowercase words naming the emotion, empty list if none "
    "apply). No other text, just the JSON object."
)

out = llm.create_chat_completion(
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "No work today, lucky me :)"},
    ],
    temperature=0.0,
    max_tokens=80,
)
print(out["choices"][0]["message"]["content"])
# {"valence": 0.38, "arousal": -0.62, "emotion_tags": []}

Grammar-constrained decoding

gguf/grammar.gbnf forces the output to structurally match the JSON schema at the token level, so it can't emit malformed JSON even on adversarial or out-of-distribution input (unlike prompting alone, which is a request, not a guarantee):

from huggingface_hub import hf_hub_download
from llama_cpp import LlamaGrammar

grammar_path = hf_hub_download(
    repo_id="F20201316/Llama-3.2-1B-journal-mood-lora",
    filename="gguf/grammar.gbnf",
)
grammar = LlamaGrammar.from_string(open(grammar_path).read(), verbose=False)

out = llm.create_chat_completion(
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "No work today, lucky me :)"},
    ],
    grammar=grammar,
    temperature=0.0,
    max_tokens=80,
)
print(out["choices"][0]["message"]["content"])

Limitations

emotion_tags is effectively non-functional. Only ~1% of training examples (120 of 12,319) carried non-empty tags — the public corpora supply valence and arousal but no categorical labels. The model therefore returns an empty list almost always. The field is preserved for schema stability; do not rely on it. Valence and arousal are the working outputs.

Domain mismatch with personal journaling. Training text is predominantly news sentences, fiction, and public social-media posts. It has not been validated on private diary-style writing, which is the intended downstream use.

Sarcasm and informal tone are weak spots. Observed in evaluation: heavily punctuated or ironic messages drew flatter predictions than the human annotation (e.g. "We tied South but soooo should hav won!!!!!" — human −0.25 valence, predicted 0.00). This is an expected failure mode at 1B parameters.

Predictions cluster toward the center. Scores tend to be conservative on strongly-worded text, which suppresses extremes.

Not a clinical or diagnostic tool. This produces coarse affect estimates from text. It is not a mental-health assessment, must not be used as one, and should not drive decisions about anyone's care.

Training

QLoRA fine-tune on a single RTX 5060 Laptop GPU (8GB), ~91 minutes.

Base model meta-llama/Llama-3.2-1B-Instruct
Quantization 4-bit NF4, double quant, bf16 compute
LoRA rank / alpha / dropout 16 / 32 / 0.05
Target modules q_proj k_proj v_proj o_proj gate_proj up_proj down_proj
Learning rate / schedule 2e-4, cosine, 3% warmup
Effective batch size 32 (2 × 16 gradient accumulation)
Epochs / steps 3 / 1,155
Loss completion-only (prompt tokens masked)

The GGUF files were produced afterward by merging the adapter into the base weights (peft merge_and_unload) and quantizing with llama.cpp's converter and llama-cpp-python's low-level quantization API (Q4_K_M, K-quant).

Training data

12,319 train / 500 validation examples, deduplicated by normalized text:

Source Examples Notes
EmoBank 10,062 Human-annotated VA, rescaled 1–5 → −1..1
Facebook VA study 2,894 Two annotators averaged, rescaled 1–9 → −1..1
Synthetic 125 Gemini-generated journaling-style examples

Please cite the original dataset authors if you build on this:

  • Buechel & Hahn (2017), EmoBank: Studying the Impact of Annotation Perspective and Representation Format on Dimensional Emotion AnalysisEACL 2017
  • Preoţiuc-Pietro et al. (2016), Modelling Valence and Arousal in Facebook PostsWASSA 2016

The derived training set is not redistributed here; please obtain the source corpora from their original repositories under their respective terms.

Downloads last month
80
GGUF
Model size
1B params
Architecture
llama
Hardware compatibility
Log In to add your hardware

4-bit

16-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for F20201316/Llama-3.2-1B-journal-mood-lora

Adapter
(664)
this model