caf324b09d
tests / check_code_quality (push) Waiting to run
tests / tests (ubuntu-latest, 3.10) (push) Blocked by required conditions
tests / tests (ubuntu-latest, 3.11) (push) Blocked by required conditions
Deploy "method_comparison" Gradio to Spaces / deploy (push) Waiting to run
Deploy "PEFT shop" Gradio app to Spaces / deploy (push) Waiting to run
tests on transformers main / tests (push) Waiting to run
tests / tests (ubuntu-latest, 3.12) (push) Blocked by required conditions
tests / tests (ubuntu-latest, 3.13) (push) Blocked by required conditions
tests / tests (windows-latest, 3.10) (push) Blocked by required conditions
tests / tests (windows-latest, 3.11) (push) Blocked by required conditions
tests / tests (windows-latest, 3.12) (push) Blocked by required conditions
tests / tests (windows-latest, 3.13) (push) Blocked by required conditions
Secret Leaks / trufflehog (push) Waiting to run
CI security linting / zizmor latest via Cargo (push) Waiting to run
Build documentation / build (push) Failing after 0s
305 lines
9.8 KiB
Python
305 lines
9.8 KiB
Python
# Copyright 2025-present the HuggingFace Inc. team.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
import torch
|
|
from datasets import load_dataset
|
|
|
|
|
|
def load_scienceqa(num_train=1000, num_eval=200, seed=42):
|
|
"""
|
|
Load ScienceQA dataset for science question answering.
|
|
|
|
Args:
|
|
num_train: Number of training samples
|
|
num_eval: Number of evaluation samples
|
|
seed: Random seed for reproducibility
|
|
|
|
Returns:
|
|
train_dataset, eval_dataset
|
|
"""
|
|
dataset = load_dataset("derek-thomas/ScienceQA", split="train")
|
|
|
|
# Shuffle and split
|
|
dataset = dataset.shuffle(seed=seed)
|
|
train_dataset = dataset.select(range(num_train))
|
|
eval_dataset = dataset.select(range(num_train, num_train + num_eval))
|
|
|
|
return train_dataset, eval_dataset
|
|
|
|
|
|
def load_numglue(num_train=1000, num_eval=200, seed=42):
|
|
"""
|
|
Load NumGLUE dataset for mathematical reasoning.
|
|
|
|
Args:
|
|
num_train: Number of training samples
|
|
num_eval: Number of evaluation samples
|
|
seed: Random seed for reproducibility
|
|
|
|
Returns:
|
|
train_dataset, eval_dataset
|
|
"""
|
|
import json
|
|
|
|
from datasets import Dataset
|
|
from huggingface_hub import hf_hub_download
|
|
|
|
# Download the NumGLUE JSON file manually
|
|
json_path = hf_hub_download(repo_id="metaeval/num-glue", filename="NumGLUE_train.json", repo_type="dataset")
|
|
|
|
# Read and process the JSON file line by line
|
|
data = []
|
|
with open(json_path) as f:
|
|
for line in f:
|
|
if line.strip(): # Skip empty lines
|
|
item = json.loads(line)
|
|
# Extract the number from the answer JSON structure
|
|
answer = item.get("answer", "")
|
|
if isinstance(answer, dict):
|
|
# NumGLUE answers are JSON with 'number' and 'date' fields
|
|
# Extract just the number field
|
|
answer_str = answer.get("number", "")
|
|
else:
|
|
answer_str = str(answer)
|
|
|
|
data.append({"question": item.get("question", ""), "answer": answer_str})
|
|
|
|
# Create dataset from processed data
|
|
dataset = Dataset.from_list(data)
|
|
|
|
# Shuffle and split
|
|
dataset = dataset.shuffle(seed=seed)
|
|
train_dataset = dataset.select(range(min(num_train, len(dataset))))
|
|
|
|
# If not enough samples, use what's available
|
|
eval_start = min(num_train, len(dataset))
|
|
eval_end = min(num_train + num_eval, len(dataset))
|
|
eval_dataset = dataset.select(range(eval_start, eval_end))
|
|
|
|
return train_dataset, eval_dataset
|
|
|
|
|
|
def load_fomc(num_train=1000, num_eval=200, seed=42):
|
|
"""
|
|
Load FOMC dataset for financial sentiment classification.
|
|
|
|
Args:
|
|
num_train: Number of training samples
|
|
num_eval: Number of evaluation samples
|
|
seed: Random seed for reproducibility
|
|
|
|
Returns:
|
|
train_dataset, eval_dataset
|
|
"""
|
|
dataset = load_dataset("TheFinAI/finben-fomc", split="test")
|
|
|
|
# Shuffle and split
|
|
dataset = dataset.shuffle(seed=seed)
|
|
train_dataset = dataset.select(range(min(num_train, len(dataset))))
|
|
|
|
eval_start = min(num_train, len(dataset))
|
|
eval_end = min(num_train + num_eval, len(dataset))
|
|
eval_dataset = dataset.select(range(eval_start, eval_end))
|
|
|
|
return train_dataset, eval_dataset
|
|
|
|
|
|
def format_scienceqa_for_llama(examples, tokenizer, max_length=512):
|
|
"""Format ScienceQA examples for Llama instruction following."""
|
|
prompts = []
|
|
labels_text = []
|
|
|
|
for i in range(len(examples["question"])):
|
|
# Build the question with choices
|
|
question = examples["question"][i]
|
|
choices = examples["choices"][i]
|
|
|
|
# Format choices
|
|
choices_text = "\n".join([f"{chr(65 + j)}. {choice}" for j, choice in enumerate(choices)])
|
|
|
|
prompt = f"""Answer the following science question by selecting the correct option.
|
|
Question: {question}
|
|
|
|
Choices:
|
|
{choices_text}
|
|
|
|
Answer (just the letter):"""
|
|
|
|
# Get the answer (convert index to letter)
|
|
answer_idx = examples["answer"][i]
|
|
answer = chr(65 + answer_idx)
|
|
|
|
prompts.append(prompt)
|
|
labels_text.append(answer)
|
|
|
|
# Tokenize
|
|
model_inputs = tokenizer(prompts, max_length=max_length, truncation=True, padding=False)
|
|
|
|
# Tokenize labels
|
|
labels = tokenizer(labels_text, max_length=10, truncation=True, padding=False)
|
|
|
|
# Combine input and label for training
|
|
combined_input_ids = []
|
|
combined_attention_mask = []
|
|
combined_labels = []
|
|
|
|
for i in range(len(model_inputs["input_ids"])):
|
|
input_ids = model_inputs["input_ids"][i]
|
|
label_ids = labels["input_ids"][i]
|
|
|
|
# Combine input and label
|
|
combined = input_ids + label_ids + [tokenizer.eos_token_id]
|
|
combined_input_ids.append(combined)
|
|
|
|
# Attention mask
|
|
combined_attention_mask.append([1] * len(combined))
|
|
|
|
# Labels (mask the prompt part, only train on answer)
|
|
label_masked = [-100] * len(input_ids) + label_ids + [tokenizer.eos_token_id]
|
|
combined_labels.append(label_masked)
|
|
|
|
return {
|
|
"input_ids": combined_input_ids,
|
|
"attention_mask": combined_attention_mask,
|
|
"labels": combined_labels,
|
|
}
|
|
|
|
|
|
def format_numglue_for_llama(examples, tokenizer, max_length=512):
|
|
"""Format NumGLUE examples for Llama instruction following."""
|
|
prompts = []
|
|
labels_text = []
|
|
|
|
for i in range(len(examples["question"])):
|
|
question = examples["question"][i]
|
|
answer = str(examples["answer"][i])
|
|
|
|
prompt = f"""Solve the following math problem and provide just the numerical answer.
|
|
|
|
Question: {question}
|
|
|
|
Answer:"""
|
|
|
|
prompts.append(prompt)
|
|
labels_text.append(answer)
|
|
|
|
# Tokenize
|
|
model_inputs = tokenizer(prompts, max_length=max_length, truncation=True, padding=False)
|
|
labels = tokenizer(labels_text, max_length=20, truncation=True, padding=False)
|
|
|
|
combined_input_ids = []
|
|
combined_attention_mask = []
|
|
combined_labels = []
|
|
|
|
for i in range(len(model_inputs["input_ids"])):
|
|
input_ids = model_inputs["input_ids"][i]
|
|
label_ids = labels["input_ids"][i]
|
|
|
|
combined = input_ids + label_ids + [tokenizer.eos_token_id]
|
|
combined_input_ids.append(combined)
|
|
combined_attention_mask.append([1] * len(combined))
|
|
|
|
label_masked = [-100] * len(input_ids) + label_ids + [tokenizer.eos_token_id]
|
|
combined_labels.append(label_masked)
|
|
|
|
return {
|
|
"input_ids": combined_input_ids,
|
|
"attention_mask": combined_attention_mask,
|
|
"labels": combined_labels,
|
|
}
|
|
|
|
|
|
def format_fomc_for_llama(examples, tokenizer, max_length=512):
|
|
"""Format FOMC examples for Llama instruction following."""
|
|
prompts = []
|
|
labels_text = []
|
|
|
|
for i in range(len(examples["text"])):
|
|
text = examples["text"][i]
|
|
# FOMC dataset has 'answer' column with values like 'dovish', 'hawkish', 'neutral'
|
|
label = examples["answer"][i].capitalize() # Capitalize first letter
|
|
|
|
prompt = f"""Classify the sentiment of the following Federal Reserve statement as Dovish, Hawkish, or Neutral.
|
|
|
|
Statement: {text}
|
|
|
|
Sentiment:"""
|
|
|
|
prompts.append(prompt)
|
|
labels_text.append(label)
|
|
|
|
# Tokenize
|
|
model_inputs = tokenizer(prompts, max_length=max_length, truncation=True, padding=False)
|
|
labels = tokenizer(labels_text, max_length=10, truncation=True, padding=False)
|
|
|
|
combined_input_ids = []
|
|
combined_attention_mask = []
|
|
combined_labels = []
|
|
|
|
for i in range(len(model_inputs["input_ids"])):
|
|
input_ids = model_inputs["input_ids"][i]
|
|
label_ids = labels["input_ids"][i]
|
|
|
|
combined = input_ids + label_ids + [tokenizer.eos_token_id]
|
|
combined_input_ids.append(combined)
|
|
combined_attention_mask.append([1] * len(combined))
|
|
|
|
label_masked = [-100] * len(input_ids) + label_ids + [tokenizer.eos_token_id]
|
|
combined_labels.append(label_masked)
|
|
|
|
return {
|
|
"input_ids": combined_input_ids,
|
|
"attention_mask": combined_attention_mask,
|
|
"labels": combined_labels,
|
|
}
|
|
|
|
|
|
class DataCollatorForCompletionOnly:
|
|
"""Data collator that pads sequences for training."""
|
|
|
|
def __init__(self, tokenizer, max_length=512):
|
|
self.tokenizer = tokenizer
|
|
self.max_length = max_length
|
|
|
|
def __call__(self, features):
|
|
# Pad sequences
|
|
max_len = min(max(len(f["input_ids"]) for f in features), self.max_length)
|
|
|
|
input_ids = []
|
|
attention_mask = []
|
|
labels = []
|
|
|
|
for f in features:
|
|
# Truncate if needed
|
|
curr_input_ids = f["input_ids"][:max_len]
|
|
curr_attention_mask = f["attention_mask"][:max_len]
|
|
curr_labels = f["labels"][:max_len]
|
|
|
|
# Pad
|
|
padding_length = max_len - len(curr_input_ids)
|
|
curr_input_ids = curr_input_ids + [self.tokenizer.pad_token_id] * padding_length
|
|
curr_attention_mask = curr_attention_mask + [0] * padding_length
|
|
curr_labels = curr_labels + [-100] * padding_length
|
|
|
|
input_ids.append(curr_input_ids)
|
|
attention_mask.append(curr_attention_mask)
|
|
labels.append(curr_labels)
|
|
|
|
return {
|
|
"input_ids": torch.tensor(input_ids, dtype=torch.long),
|
|
"attention_mask": torch.tensor(attention_mask, dtype=torch.long),
|
|
"labels": torch.tensor(labels, dtype=torch.long),
|
|
}
|