finllm-foundry / src /data /prepare_datasets.py
finpy1789's picture
Upload folder using huggingface_hub
0418f40 verified
Raw
History Blame Contribute Delete
4.2 kB
"""Build the mixed finance instruction dataset in chat ("messages") format.
Each source dataset gets a formatter that maps its rows to
{"messages": [{"role": "user", ...}, {"role": "assistant", ...}], "source": name}
so TRL's SFTTrainer can apply any model's chat template at train time.
Sources that fail to load (renamed repo, script-based dataset on datasets>=3.0,
network) are skipped with a warning instead of killing the run.
Usage:
python -m src.data.prepare_datasets --out data/finance_sft \
[--max-per-source 20000] [--push-to-hub user/finance-sft-mix]
"""
import argparse
import random
from datasets import Dataset, concatenate_datasets, load_dataset
SYSTEM_PROMPT = (
"You are a financial analysis assistant with expertise in markets, filings, "
"accounting standards (IFRS/GAAP), and financial regulation. Be precise, cite "
"the relevant standard or filing section when applicable, and say so when you "
"are unsure. You do not give personalized investment advice."
)
def _chat(user, assistant, source):
return {
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user},
{"role": "assistant", "content": assistant},
],
"source": source,
}
def _fmt_instruction_style(row, source):
"""FinGPT-style rows: instruction / input / output."""
user = row["instruction"]
if row.get("input"):
user = f"{user}\n\n{row['input']}"
return _chat(user, row["output"], source)
def _fmt_finance_alpaca(row, source):
user = row["instruction"]
if row.get("input"):
user = f"{user}\n\n{row['input']}"
return _chat(user, row["output"], source)
def _fmt_phrasebank(row, source):
labels = {0: "negative", 1: "neutral", 2: "positive"}
user = (
"Classify the sentiment of this financial news sentence as positive, "
f"negative, or neutral:\n\n{row['sentence']}"
)
return _chat(user, labels[row["label"]], source)
# name -> (hf repo, config, split, formatter, mixture weight)
SOURCES = {
"fingpt-sentiment": ("FinGPT/fingpt-sentiment-train", None, "train", _fmt_instruction_style, 1.0),
"fingpt-fiqa-qa": ("FinGPT/fingpt-fiqa_qa", None, "train", _fmt_instruction_style, 1.5),
"fingpt-headline": ("FinGPT/fingpt-headline", None, "train", _fmt_instruction_style, 0.5),
"fingpt-finred": ("FinGPT/fingpt-finred", None, "train", _fmt_instruction_style, 0.5),
"finance-alpaca": ("gbharti/finance-alpaca", None, "train", _fmt_finance_alpaca, 1.5),
"financial-phrasebank": ("takala/financial_phrasebank", "sentences_66agree", "train", _fmt_phrasebank, 0.5),
}
def build(max_per_source, seed=42):
random.seed(seed)
parts = []
for name, (repo, config, split, fmt, weight) in SOURCES.items():
try:
ds = load_dataset(repo, config, split=split) if config else load_dataset(repo, split=split)
except Exception as e: # noqa: BLE001 - skip broken sources, keep the run alive
print(f"[skip] {name} ({repo}): {type(e).__name__}: {e}")
continue
n = min(len(ds), int(max_per_source * weight))
ds = ds.shuffle(seed=seed).select(range(n))
rows = [fmt(row, name) for row in ds]
parts.append(Dataset.from_list(rows))
print(f"[ok] {name}: {n} examples")
if not parts:
raise RuntimeError("No sources loaded — check network / dataset names.")
mixed = concatenate_datasets(parts).shuffle(seed=seed)
return mixed.train_test_split(test_size=0.02, seed=seed)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", default="data/finance_sft")
ap.add_argument("--max-per-source", type=int, default=20000)
ap.add_argument("--push-to-hub", default=None, help="e.g. user/finance-sft-mix (pushed private)")
args = ap.parse_args()
ds = build(args.max_per_source)
print(ds)
ds.save_to_disk(args.out)
print(f"Saved to {args.out}")
if args.push_to_hub:
ds.push_to_hub(args.push_to_hub, private=True)
print(f"Pushed to hub: {args.push_to_hub} (private)")
if __name__ == "__main__":
main()