593b94c120
pytest / Unit Tests (push) Has been cancelled
pytest / Integration (integration_tests_a) (push) Has been cancelled
pytest / Integration (integration_tests_b) (push) Has been cancelled
pytest / Integration (integration_tests_c) (push) Has been cancelled
pytest / Integration (integration_tests_d) (push) Has been cancelled
pytest / Integration (integration_tests_e) (push) Has been cancelled
pytest / Integration (integration_tests_f) (push) Has been cancelled
pytest / Integration (integration_tests_g) (push) Has been cancelled
pytest / Integration (integration_tests_h) (push) Has been cancelled
pytest / Integration (integration_tests_i) (push) Has been cancelled
pytest / Integration (integration_tests_j) (push) Has been cancelled
pytest / Distributed (distributed_a) (push) Has been cancelled
pytest / Distributed (distributed_b) (push) Has been cancelled
pytest / Distributed (distributed_c) (push) Has been cancelled
pytest / Distributed (distributed_d) (push) Has been cancelled
pytest / Distributed (distributed_e) (push) Has been cancelled
pytest / Distributed (distributed_f) (push) Has been cancelled
pytest / Minimal Install (push) Has been cancelled
pytest / Event File (push) Has been cancelled
pytest (slow) / py-slow (push) Has been cancelled
Publish JSON Schema / publish-schema (push) Has been cancelled
133 lines
4.6 KiB
Python
133 lines
4.6 KiB
Python
"""Prepare the Anthropic HH-RLHF dataset for Ludwig alignment training.
|
|
|
|
Downloads `Anthropic/hh-rlhf` from HuggingFace and converts it into the
|
|
column format expected by Ludwig's DPO, KTO, and ORPO trainers.
|
|
|
|
DPO / ORPO output: train.csv, test.csv
|
|
Columns: prompt, chosen, rejected
|
|
|
|
KTO output: train_kto.csv, test_kto.csv
|
|
Columns: prompt, response, label (label is True for chosen, False for rejected)
|
|
|
|
The HH-RLHF dataset stores full multi-turn conversations as raw text with the
|
|
pattern:
|
|
"\n\nHuman: <turn>\n\nAssistant: <turn>\n\nHuman: ...\n\nAssistant: ..."
|
|
|
|
We extract the last Human turn as the prompt and the final Assistant turn as
|
|
the response. For DPO we do this for both `chosen` and `rejected` columns.
|
|
"""
|
|
|
|
import argparse
|
|
import re
|
|
|
|
import pandas as pd
|
|
from datasets import load_dataset
|
|
|
|
|
|
def extract_last_human_turn(conversation: str) -> str:
|
|
"""Return the last Human turn from a raw HH-RLHF conversation string."""
|
|
human_turns = re.findall(r"\n\nHuman: (.*?)(?=\n\nAssistant:|\Z)", conversation, re.DOTALL)
|
|
if human_turns:
|
|
return human_turns[-1].strip()
|
|
return conversation.strip()
|
|
|
|
|
|
def extract_last_assistant_turn(conversation: str) -> str:
|
|
"""Return the last Assistant turn from a raw HH-RLHF conversation string."""
|
|
assistant_turns = re.findall(r"\n\nAssistant: (.*?)(?=\n\nHuman:|\Z)", conversation, re.DOTALL)
|
|
if assistant_turns:
|
|
return assistant_turns[-1].strip()
|
|
return ""
|
|
|
|
|
|
def convert_split(split_data) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
"""Convert one HF dataset split into DPO and KTO DataFrames."""
|
|
rows_dpo = []
|
|
rows_kto = []
|
|
|
|
for example in split_data:
|
|
chosen_conv = example["chosen"]
|
|
rejected_conv = example["rejected"]
|
|
|
|
prompt = extract_last_human_turn(chosen_conv)
|
|
chosen_response = extract_last_assistant_turn(chosen_conv)
|
|
rejected_response = extract_last_assistant_turn(rejected_conv)
|
|
|
|
if not prompt or not chosen_response or not rejected_response:
|
|
continue
|
|
|
|
rows_dpo.append(
|
|
{
|
|
"prompt": prompt,
|
|
"chosen": chosen_response,
|
|
"rejected": rejected_response,
|
|
}
|
|
)
|
|
|
|
# KTO: expand each pair into two rows with a boolean label
|
|
rows_kto.append({"prompt": prompt, "response": chosen_response, "label": True})
|
|
rows_kto.append({"prompt": prompt, "response": rejected_response, "label": False})
|
|
|
|
return pd.DataFrame(rows_dpo), pd.DataFrame(rows_kto)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Prepare Anthropic HH-RLHF for Ludwig alignment training.")
|
|
parser.add_argument("--output_dir", default=".", help="Directory to write CSV files into.")
|
|
parser.add_argument(
|
|
"--max_train_samples",
|
|
type=int,
|
|
default=None,
|
|
help="Cap the number of training examples (useful for quick experiments).",
|
|
)
|
|
parser.add_argument(
|
|
"--max_test_samples",
|
|
type=int,
|
|
default=None,
|
|
help="Cap the number of test examples.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
print("Downloading Anthropic/hh-rlhf …")
|
|
dataset = load_dataset("Anthropic/hh-rlhf")
|
|
|
|
train_split = dataset["train"]
|
|
test_split = dataset["test"]
|
|
|
|
if args.max_train_samples:
|
|
train_split = train_split.select(range(min(args.max_train_samples, len(train_split))))
|
|
if args.max_test_samples:
|
|
test_split = test_split.select(range(min(args.max_test_samples, len(test_split))))
|
|
|
|
print(f"Converting {len(train_split)} train examples …")
|
|
train_dpo, train_kto = convert_split(train_split)
|
|
|
|
print(f"Converting {len(test_split)} test examples …")
|
|
test_dpo, test_kto = convert_split(test_split)
|
|
|
|
import os
|
|
|
|
os.makedirs(args.output_dir, exist_ok=True)
|
|
|
|
train_path = os.path.join(args.output_dir, "train.csv")
|
|
test_path = os.path.join(args.output_dir, "test.csv")
|
|
train_kto_path = os.path.join(args.output_dir, "train_kto.csv")
|
|
test_kto_path = os.path.join(args.output_dir, "test_kto.csv")
|
|
|
|
train_dpo.to_csv(train_path, index=False)
|
|
test_dpo.to_csv(test_path, index=False)
|
|
train_kto.to_csv(train_kto_path, index=False)
|
|
test_kto.to_csv(test_kto_path, index=False)
|
|
|
|
print(f"\nDPO dataset: {len(train_dpo)} train rows -> {train_path}")
|
|
print(f" {len(test_dpo)} test rows -> {test_path}")
|
|
print(f"KTO dataset: {len(train_kto)} train rows -> {train_kto_path}")
|
|
print(f" {len(test_kto)} test rows -> {test_kto_path}")
|
|
print("\nColumns in DPO files: prompt, chosen, rejected")
|
|
print("Columns in KTO files: prompt, response, label")
|
|
print("\nDone.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|