mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-08-15 12:05:19 +08:00
177 lines
5.7 KiB
Python
177 lines
5.7 KiB
Python
'''
|
|
author: theblackcat102
|
|
|
|
Dataset output format from __getitem__
|
|
|
|
- question / prompt : string
|
|
|
|
- answers / rows : list of tuple pair. The first element in the tuple pair must be the positive pair (rank higher than the second element)
|
|
|
|
A list of rank based dataset for training using rank loss
|
|
|
|
Some nice features to have
|
|
|
|
[] support additional negative samples generated from other models.
|
|
|
|
For example we can use galactica-125m to generate a TLDR and assume it was
|
|
inferior than the human perference one
|
|
|
|
|
|
'''
|
|
from typing import Optional, Union
|
|
from dataclasses import dataclass
|
|
import numpy as np
|
|
from torch.utils.data import Dataset
|
|
from datasets import load_dataset
|
|
from transformers.tokenization_utils_base import PreTrainedTokenizerBase, PaddingStrategy
|
|
|
|
@dataclass
|
|
class DataCollatorForPairRank:
|
|
"""
|
|
|
|
Data collator that will dynamically pad the inputs for multiple choice received.
|
|
|
|
"""
|
|
tokenizer: PreTrainedTokenizerBase
|
|
num_choices: int = 2
|
|
padding: Union[bool, str, PaddingStrategy] = True
|
|
max_length: Optional[int] = None
|
|
pad_to_multiple_of: Optional[int] = None
|
|
drop_token_type: bool = False # galactica
|
|
|
|
def __call__(self, features):
|
|
|
|
flatten_features = []
|
|
batch_size = 0
|
|
for question, pairs in features:
|
|
for (pos, neg) in pairs:
|
|
flatten_features.append(self.tokenizer(question, pos,
|
|
truncation=True, max_length=self.max_length))
|
|
flatten_features.append(self.tokenizer(question, neg,
|
|
truncation=True, max_length=self.max_length))
|
|
batch_size += 1
|
|
|
|
batch = self.tokenizer.pad(
|
|
flatten_features,
|
|
padding=self.padding,
|
|
max_length=self.max_length,
|
|
pad_to_multiple_of=self.pad_to_multiple_of,
|
|
return_tensors="pt",
|
|
)
|
|
if self.drop_token_type:
|
|
batch.pop('token_type_ids')
|
|
# batch = {k: v.view(batch_size, self.num_choices, -1) for k, v in batch.items()}
|
|
return batch
|
|
|
|
|
|
class WebGPT(Dataset):
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
|
|
dataset = load_dataset("openai/webgpt_comparisons")
|
|
questions = {}
|
|
# using prompt as our index will allows us
|
|
# to add additional generated prompt later
|
|
self.index2question = {}
|
|
for row in dataset['train']:
|
|
question = row['question']['full_text']
|
|
if question not in self.index2question:
|
|
self.index2question[len(self.index2question)] = question
|
|
|
|
if question not in questions:
|
|
questions[question] = []
|
|
|
|
if row['score_0'] > row['score_1']:
|
|
# not going to risk it
|
|
questions[question].append((
|
|
row['answer_0'], row['answer_1']
|
|
))
|
|
else:
|
|
questions[question].append((
|
|
row['answer_1'], row['answer_0']
|
|
))
|
|
|
|
self.questions = questions
|
|
|
|
def __len__(self):
|
|
return len(self.index2question)
|
|
|
|
def __getitem__(self, index):
|
|
question = self.index2question[index]
|
|
rows = self.questions[question]
|
|
# optimize the format later
|
|
return question, rows
|
|
|
|
|
|
|
|
|
|
class HFSummary(Dataset):
|
|
'''
|
|
Human feedback data from OpenAI
|
|
https://github.com/openai/summarize-from-feedback
|
|
|
|
labeling method : pair comparison, 0 or 1
|
|
|
|
'''
|
|
def __init__(self, split='train',
|
|
conf_threshold=-1,
|
|
max_comparison_per_sample=3) -> None:
|
|
super().__init__()
|
|
assert split in ('train', 'valid1', 'valid2', 'test')
|
|
summaries = {}
|
|
# using prompt as our index will allows us
|
|
# to add additional generated prompt later
|
|
self.index2summary = {}
|
|
self.max_comparison_per_sample = max_comparison_per_sample
|
|
major_split = split if 'train' == split else 'validation'
|
|
dataset = load_dataset('Tristan/summarize_from_feedback', 'comparisons')[major_split]
|
|
for data in dataset:
|
|
if 'extra' in data and \
|
|
'confidence' in data['extra'] and \
|
|
data['extra']['confidence'] is not None and \
|
|
conf_threshold > data['extra']['confidence']:
|
|
print('skipping {}'.format(data['info']['id']))
|
|
continue
|
|
|
|
if split != 'train' and split != data['split']:
|
|
continue
|
|
|
|
if 'article' in data['info'] and \
|
|
data['info']['article'] is not None:
|
|
context = data['info']['article']
|
|
elif 'post' in data['info']:
|
|
context = data['info']['post']
|
|
|
|
|
|
if context not in self.index2summary:
|
|
self.index2summary[len(self.index2summary)] = context
|
|
|
|
if context not in summaries:
|
|
summaries[context] = []
|
|
|
|
pos, neg = (0, 1) if data['choice'] == 0 else (1, 0)
|
|
summaries[context].append((
|
|
data['summaries'][pos]['text'],
|
|
data['summaries'][neg]['text']
|
|
))
|
|
|
|
self.summaries = summaries
|
|
|
|
self.postfix_prompt = ' TLDR;'
|
|
|
|
def __len__(self):
|
|
return len(self.index2summary)
|
|
|
|
def __getitem__(self, index):
|
|
context = self.index2summary[index]
|
|
# return pairs of comparison
|
|
rows = self.summaries[context]
|
|
# pair very big
|
|
# we are going to do some sampling
|
|
# not optimal but good for now
|
|
valid_idx = np.random.choice(len(rows), self.max_comparison_per_sample)
|
|
# optimize the format later
|
|
return context+self.postfix_prompt, [ r for idx, r in enumerate(rows) if idx in valid_idx ]
|
|
|