| import sys |
| import torch |
| import random |
| import numpy as np |
| import transformers |
| from typing import Dict |
|
|
| from transformers.trainer_pt_utils import LabelSmoother |
| from sdlm.conversation import get_conv_template |
|
|
| IGNORE_TOKEN_ID = LabelSmoother.ignore_index |
|
|
|
|
| """ |
| data preprocess for sft |
| """ |
| def preprocess_text_sft( |
| template_name, |
| sources, |
| tokenizer: transformers.PreTrainedTokenizer, |
| ds_name: str = None, |
| ) -> Dict: |
| assert len(sources) == 1, 'process only the first conversations' |
| conversations = sources[0] |
|
|
| if conversations[0]['from'] == 'system': |
| system_prompt = conversations[0]['value'] |
| conversations = conversations[1:] |
| else: |
| conv = get_conv_template(template_name) |
| system_prompt = conv.system_message |
|
|
| batches, roles = [], [] |
| if system_prompt is not None: |
| batches.append(f'<|im_start|>system\n{system_prompt}<|im_end|>\n') |
| roles.append('system') |
| for conversation in conversations: |
| if conversation['from'] == 'human': |
| batches.append(f'<|im_start|>user\n{conversation["value"]}<|im_end|>\n') |
| roles.append('human') |
| elif conversation['from'] == 'gpt': |
| batches.append(f'<|im_start|>assistant\n{conversation["value"]}<|im_end|>\n') |
| roles.append('gpt') |
| elif conversation['from'] == 'function': |
| batches.append(f'<|im_start|>function\n{conversation["value"]}<|im_end|>\n') |
| roles.append('function') |
| else: |
| raise NotImplementedError |
|
|
| add_bos_token = getattr(tokenizer, 'add_bos_token', False) |
| if add_bos_token: |
| batches[0] = tokenizer.bos_token + batches[0] |
|
|
| |
| input_ids = tokenizer( |
| batches, |
| return_tensors='np', |
| padding=False, |
| max_length=tokenizer.model_max_length, |
| truncation=False, |
| ).input_ids |
|
|
| if add_bos_token: |
| input_ids = [item[1:] for item in input_ids] |
|
|
| final_input_ids, final_targets = [], [] |
| ignore_ids = tokenizer('<|im_start|>assistant\n', return_tensors='np').input_ids[0] |
| ignore_len = ignore_ids.shape[0] - 1 if add_bos_token else ignore_ids.shape[0] |
| for role, input_id in zip(roles, input_ids): |
| if role == 'system' or role == 'human' or role == 'function': |
| final_input_ids.append(input_id) |
| final_targets.append(np.full(input_id.shape, IGNORE_TOKEN_ID)) |
| elif role == 'gpt': |
| target = input_id.copy() |
| target[:ignore_len] = IGNORE_TOKEN_ID |
| target[-1:] = IGNORE_TOKEN_ID |
| final_input_ids.append(input_id) |
| final_targets.append(target) |
| else: |
| raise NotImplementedError |
| input_ids = torch.tensor(np.concatenate(final_input_ids)) |
| targets = torch.tensor(np.concatenate(final_targets)) |
|
|
| assert len(input_ids) == len(targets) |
|
|
| current_length = input_ids.size(0) |
| if current_length > tokenizer.model_max_length: |
| input_ids = input_ids[:tokenizer.model_max_length] |
| targets = targets[:tokenizer.model_max_length] |
| print( |
| f'WARNING: Too long and truncated: {current_length} vs. {tokenizer.model_max_length}.' |
| f' This dataset is {ds_name}.' |
| ) |
| sys.stdout.flush() |
|
|
| input_ids = input_ids.unsqueeze(0) |
| targets = targets.unsqueeze(0) |
|
|
| |
| |
| |
|
|
| return dict( |
| input_ids=input_ids, |
| labels=targets, |
| attention_mask=input_ids.ne(tokenizer.pad_token_id), |
| ) |
|
|