Llama-3.1-Nemotron-Nano-4B-v1.1-banking77
A LoRA fine-tune of nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 for banking77 intent classification, with the adapter merged into the base weights.
This is a standalone causal LM — no PEFT/adapter loading required. It drops straight into
transformers, vLLM, TGI or NVIDIA NIM. It is 4.5B parameters in bfloat16 (~9 GB).
The model answers with a single banking77 intent label when given the label list in the system prompt (see Usage). Outside that prompt format it behaves like the underlying Nemotron Nano chat model.
Usage
The prompt format matters. The system prompt must contain detailed thinking off followed
by the intent list — without detailed thinking off the model may emit a <think> block
instead of a bare label, and without the label list it answers conversationally.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
REPO = "EQTYLab/Llama-3.1-Nemotron-Nano-4B-v1.1-banking77"
LABELS = [
"activate_my_card", "age_limit", "apple_pay_or_google_pay", "atm_support",
"automatic_top_up", "balance_not_updated_after_bank_transfer",
"balance_not_updated_after_cheque_or_cash_deposit", "beneficiary_not_allowed",
"cancel_transfer", "card_about_to_expire", "card_acceptance", "card_arrival",
"card_delivery_estimate", "card_linking", "card_not_working",
"card_payment_fee_charged", "card_payment_not_recognised",
"card_payment_wrong_exchange_rate", "card_swallowed", "cash_withdrawal_charge",
"cash_withdrawal_not_recognised", "change_pin", "compromised_card",
"contactless_not_working", "country_support", "declined_card_payment",
"declined_cash_withdrawal", "declined_transfer",
"direct_debit_payment_not_recognised", "disposable_card_limits",
"edit_personal_details", "exchange_charge", "exchange_rate", "exchange_via_app",
"extra_charge_on_statement", "failed_transfer", "fiat_currency_support",
"get_disposable_virtual_card", "get_physical_card", "getting_spare_card",
"getting_virtual_card", "lost_or_stolen_card", "lost_or_stolen_phone",
"order_physical_card", "passcode_forgotten", "pending_card_payment",
"pending_cash_withdrawal", "pending_top_up", "pending_transfer", "pin_blocked",
"receiving_money", "Refund_not_showing_up", "request_refund",
"reverted_card_payment?", "supported_cards_and_currencies", "terminate_account",
"top_up_by_bank_transfer_charge", "top_up_by_card_charge",
"top_up_by_cash_or_cheque", "top_up_failed", "top_up_limits", "top_up_reverted",
"topping_up_by_card", "transaction_charged_twice", "transfer_fee_charged",
"transfer_into_account", "transfer_not_received_by_recipient", "transfer_timing",
"unable_to_verify_identity", "verify_my_identity", "verify_source_of_funds",
"verify_top_up", "virtual_card_not_working", "visa_or_mastercard",
"why_verify_identity", "wrong_amount_of_cash_received",
"wrong_exchange_rate_for_cash_withdrawal",
]
SYSTEM = (
"detailed thinking off\n\n"
"Classify the customer query into exactly one of the following intents:\n"
+ ", ".join(LABELS)
)
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForCausalLM.from_pretrained(REPO, dtype=torch.bfloat16, device_map="auto")
def classify(query: str) -> str:
messages = [{"role": "system", "content": SYSTEM},
{"role": "user", "content": query}]
inputs = tok.apply_chat_template(messages, add_generation_prompt=True,
return_tensors="pt").to(model.device)
out = model.generate(inputs, max_new_tokens=16, do_sample=False,
pad_token_id=tok.pad_token_id)
return tok.decode(out[0][inputs.shape[1]:], skip_special_tokens=True).strip()
print(classify("Why was I charged an extra fee when I paid by card?"))
# -> card_payment_fee_charged
Greedy decoding (do_sample=False) is recommended; the task has one correct token sequence.
vLLM
vllm serve EQTYLab/Llama-3.1-Nemotron-Nano-4B-v1.1-banking77 \
--max-model-len 8192 --dtype bfloat16
Then call /v1/chat/completions with the same system prompt and
temperature=0, max_tokens=16.
Training
LoRA supervised fine-tuning with PEFT, then merge_and_unload() into the base weights.
| Base model | nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 |
| Method | LoRA (merged) |
| LoRA rank / alpha | 16 / 32 (scaling 2.0) |
| LoRA dropout | 0.05 |
| Target modules | q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj |
| Trainable adapter params | 448 tensors, 121.7 MB |
| Epochs | 2 |
| Batch size | 8 (grad accumulation 2, effective 16) |
| Learning rate | 1e-4 |
| Training rows | 9,993 |
| Seed | 13 |
| Precision | bfloat16 |
Evaluation
No benchmark accuracy has been measured for this checkpoint. The merged weights were verified for correctness and format compliance only:
- The merge was checked tensor-by-tensor against the raw adapter — recomputing
W + (alpha/r) · B·Aindependently reproduces the merged weights bit-exactly in bf16. - The full tensor set and total size are preserved from the base model (291 tensors, 9,025,492,992 bytes, 4,512,746,496 parameters).
- Spot-checked generation on a handful of banking queries returns clean, valid labels.
Anyone relying on this model should run their own evaluation against the banking77 test split before deploying it.
Limitations
- Output is generated text, not a constrained classifier head. The model can in principle emit a string outside the 77 labels; validate against the label list, or use constrained decoding / guided choice in vLLM.
- Sensitive to the prompt format described above. Dropping
detailed thinking offcan trigger the base model's reasoning mode and a<think>block instead of a label. - English only, and scoped to retail banking customer-support intents. Behaviour on other domains is whatever the base model does.
- The base model's biases, safety characteristics and 128K context properties carry over
unchanged; see
bias.md,safety.md,explainability.mdandprivacy.mdin this repo.
License
Derivative of nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1. Governed by the
NVIDIA Open Model License,
and, as a Llama 3.1 derivative, additionally by the
Llama 3.1 Community License.