Text Generation
English
finance
options-trading
market-prediction
quantitative-analysis
qlora
mistral
FinOptions-Mistral-7B / train_t4.py
Saksham7772's picture
Fix total_mem -> total_memory attribute name
2abfac3 verified
"""
Financial Options & Market Prediction Expert Model β€” T4 Optimized
"""
import os
import sys
import traceback
# Force unbuffered output so logs stream properly
os.environ["PYTHONUNBUFFERED"] = "1"
def log(msg):
print(msg, flush=True)
try:
import torch
from datasets import load_dataset, concatenate_datasets
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, prepare_model_for_kbit_training
from trl import SFTTrainer, SFTConfig
log("βœ… All imports successful")
except Exception as e:
log(f"❌ Import error: {e}")
sys.exit(1)
# ============================================================================
MODEL_ID = "mistralai/Mistral-7B-Instruct-v0.3"
HUB_MODEL_ID = "Saksham7772/FinOptions-Mistral-7B"
OUTPUT_DIR = "./finopt-mistral-7b-qlora"
SYSTEM_PROMPT = (
"You are a quantitative financial analyst and options trading expert. "
"For every analysis you provide:\n"
"1. Identify which input data features are most influential "
"(e.g., implied volatility, volume, earnings, macro indicators, sentiment)\n"
"2. Explain the directional impact of each feature on the prediction "
"(bullish/bearish/neutral and why)\n"
"3. Provide your market prediction or options strategy recommendation with clear reasoning\n"
"4. Express your confidence level and key risk factors\n"
"Think step by step before answering."
)
# ============================================================================
# DATASET PREPARATION
# ============================================================================
log("=" * 60)
log("STEP 1/5: Loading datasets...")
log("=" * 60)
try:
ds_sujet = load_dataset("sujet-ai/Sujet-Finance-Instruct-177k", split="train")
log(f" Sujet Finance: {len(ds_sujet)} rows")
def convert_sujet(example):
system = (example.get("system_prompt") or "").strip()
if not system:
system = SYSTEM_PROMPT
user = (example.get("user_prompt") or "").strip()
answer = (example.get("answer") or "").strip()
if not user or not answer:
return {"messages": None}
return {"messages": [
{"role": "system", "content": system},
{"role": "user", "content": user},
{"role": "assistant", "content": answer},
]}
ds_sujet = ds_sujet.map(convert_sujet, remove_columns=ds_sujet.column_names, num_proc=4)
ds_sujet = ds_sujet.filter(lambda x: x["messages"] is not None, num_proc=4)
log(f" Sujet after conversion: {len(ds_sujet)} rows")
ds_alpaca = load_dataset("gbharti/finance-alpaca", split="train")
log(f" Finance Alpaca: {len(ds_alpaca)} rows")
def convert_alpaca(example):
instruction = (example.get("instruction") or "").strip()
inp = (example.get("input") or "").strip()
output = (example.get("output") or "").strip()
if not instruction or not output:
return {"messages": None}
user_content = instruction + (f"\n\n{inp}" if inp else "")
return {"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_content},
{"role": "assistant", "content": output},
]}
ds_alpaca = ds_alpaca.map(convert_alpaca, remove_columns=ds_alpaca.column_names, num_proc=4)
ds_alpaca = ds_alpaca.filter(lambda x: x["messages"] is not None, num_proc=4)
log(f" Alpaca after conversion: {len(ds_alpaca)} rows")
ds_500k = load_dataset("Josephgflowers/Finance-Instruct-500k", split="train")
log(f" Finance 500K: {len(ds_500k)} rows")
def convert_500k(example):
system = (example.get("system") or "").strip()
if not system:
system = SYSTEM_PROMPT
user = (example.get("user") or "").strip()
assistant = (example.get("assistant") or "").strip()
if not user or not assistant:
return {"messages": None}
return {"messages": [
{"role": "system", "content": system},
{"role": "user", "content": user},
{"role": "assistant", "content": assistant},
]}
ds_500k = ds_500k.map(convert_500k, remove_columns=ds_500k.column_names, num_proc=4)
ds_500k = ds_500k.filter(lambda x: x["messages"] is not None, num_proc=4)
log(f" 500K after conversion: {len(ds_500k)} rows")
combined = concatenate_datasets([ds_sujet, ds_alpaca, ds_500k])
combined = combined.shuffle(seed=42)
split = combined.train_test_split(test_size=0.01, seed=42)
train_dataset = split["train"]
eval_dataset = split["test"]
log(f"βœ… COMBINED: {len(combined)} rows | Train: {len(train_dataset)} | Eval: {len(eval_dataset)}")
except Exception as e:
log(f"❌ Dataset error: {e}")
traceback.print_exc()
sys.exit(1)
# ============================================================================
# MODEL
# ============================================================================
log("=" * 60)
log("STEP 2/5: Loading model...")
log("=" * 60)
try:
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.float16,
)
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
quantization_config=bnb_config,
device_map="auto",
torch_dtype=torch.float16,
trust_remote_code=True,
)
model = prepare_model_for_kbit_training(model)
model.config.use_cache = False
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
log(f"βœ… Model loaded: {MODEL_ID} ({model.num_parameters():,} params)")
# Check GPU memory
if torch.cuda.is_available():
mem = torch.cuda.memory_allocated() / 1024**3
total = torch.cuda.get_device_properties(0).total_memory / 1024**3
log(f" GPU memory: {mem:.1f}GB / {total:.1f}GB used")
except Exception as e:
log(f"❌ Model loading error: {e}")
traceback.print_exc()
sys.exit(1)
# ============================================================================
# LoRA + SFT CONFIG
# ============================================================================
log("=" * 60)
log("STEP 3/5: Configuring training...")
log("=" * 60)
try:
peft_config = LoraConfig(
r=64, lora_alpha=128,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05, bias="none", task_type="CAUSAL_LM",
)
sft_config = SFTConfig(
output_dir=OUTPUT_DIR,
num_train_epochs=1,
per_device_train_batch_size=1,
per_device_eval_batch_size=1,
gradient_accumulation_steps=16,
optim="paged_adamw_8bit",
learning_rate=2e-4,
max_grad_norm=0.3,
weight_decay=0.001,
warmup_ratio=0.03,
lr_scheduler_type="cosine",
# NO fp16, NO bf16 β€” pure fp32 training to avoid T4 AMP issues
bf16=False,
fp16=False,
max_length=2048,
packing=False,
gradient_checkpointing=True,
gradient_checkpointing_kwargs={"use_reentrant": False},
eval_strategy="steps",
eval_steps=2000,
save_strategy="steps",
save_steps=2000,
save_total_limit=3,
load_best_model_at_end=True,
metric_for_best_model="eval_loss",
disable_tqdm=True,
logging_strategy="steps",
logging_steps=10,
logging_first_step=True,
report_to="none",
push_to_hub=True,
hub_model_id=HUB_MODEL_ID,
hub_strategy="every_save",
seed=42,
dataloader_num_workers=2,
)
log("βœ… Config created")
except Exception as e:
log(f"❌ Config error: {e}")
traceback.print_exc()
sys.exit(1)
# ============================================================================
# TRAINER
# ============================================================================
log("=" * 60)
log("STEP 4/5: Creating trainer and tokenizing...")
log("=" * 60)
try:
trainer = SFTTrainer(
model=model,
args=sft_config,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
peft_config=peft_config,
processing_class=tokenizer,
)
log(f"βœ… Trainer created")
log(f" Trainable params: {sum(p.numel() for p in trainer.model.parameters() if p.requires_grad):,}")
log(f" Total steps: {trainer.state.max_steps if hasattr(trainer.state, 'max_steps') else 'unknown'}")
# Check param dtypes
dtype_counts = {}
for name, p in trainer.model.named_parameters():
dt = str(p.dtype)
dtype_counts[dt] = dtype_counts.get(dt, 0) + 1
log(f" Param dtypes: {dtype_counts}")
if torch.cuda.is_available():
mem = torch.cuda.memory_allocated() / 1024**3
total = torch.cuda.get_device_properties(0).total_memory / 1024**3
log(f" GPU memory after trainer: {mem:.1f}GB / {total:.1f}GB")
except Exception as e:
log(f"❌ Trainer creation error: {e}")
traceback.print_exc()
sys.exit(1)
# ============================================================================
# TRAIN
# ============================================================================
log("=" * 60)
log("STEP 5/5: TRAINING...")
log(f" Model: {MODEL_ID}")
log(f" Dataset: {len(train_dataset)} examples")
log(f" Epochs: 1 | Batch: 1 | GradAccum: 16 | EffBatch: 16")
log(f" LoRA r=64 Ξ±=128 | LR: 2e-4 | fp32 training")
log(f" Push to: {HUB_MODEL_ID}")
log("=" * 60)
try:
trainer.train()
log("βœ… Training complete!")
except Exception as e:
log(f"❌ Training error: {e}")
traceback.print_exc()
# Still try to save whatever we have
try:
log("Attempting to save partial model...")
trainer.save_model(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
log("Partial model saved")
except:
log("Could not save partial model")
sys.exit(1)
# ============================================================================
# SAVE & PUSH
# ============================================================================
log("Saving final model...")
trainer.save_model(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
log(f"βœ… DONE! Model at: https://huggingface.co/{HUB_MODEL_ID}")