T5に基づくモデルの微調整と対応するデータの導入

低構成の GPU での T5 微調整トレーニングの場合、単一のデータのみをトレーニングできます。トレーニング コードは次のとおりです。

# 使用
import datetime
import json
import os

import transformers
from torch.utils.tensorboard import SummaryWriter
from transformers import T5Tokenizer, T5ForConditionalGeneration
import torch

def preprocess(text):
  text = text.replace("\n", "\\n").replace("\t", "\\t")
  return text

def postprocess(text):
  return text.replace("\\n", "\n").replace("\\t", "\t")

def train():

  lr = 1.5e-4
  num_warmup_steps = 2000
  epochs = 3
  tb_writer = SummaryWriter(log_dir="t5/summary")
  output_dir = "t5/my_model/"
  batch_size = 1
  gradient_accumulation=1
  max_grad_norm = 1
  log_step = 1
  import pandas as pd
  colum_data = pd.read_excel("data/rewrite_train.xlsx")

  data_json_list = json.loads(colum_data.to_json(force_ascii=False, orient="records"))

  total_steps = int(len(data_json_list) / epochs/ batch_size / gradient_accumulation)

  if not os.path.exists(output_dir):
    os.mkdir(output_dir)

  tokenizer = T5Tokenizer.from_pretrained("ClueAI/ChatYuan-large-v1")
  model = T5ForConditionalGeneration.from_pretrained("ClueAI/ChatYuan-large-v1")
  # 修改colab笔记本设置为gpu,推理更快
  model.train()
  device = torch.device('cuda')
  model.to(device)

  print('calculating total steps')

  optimizer = transformers.AdamW(model.parameters(), lr=lr, correct_bias=True)

  scheduler = transformers.get_linear_schedule_with_warmup(optimizer,
                                                           num_warmup_steps=num_warmup_steps,
                                                           num_training_steps=total_steps)
  print('starting training')
  overall_step = 0
  running_loss = 0
  for epoch in range(epochs):
    print('epoch {}'.format(epoch + 1))
    now = datetime.datetime.now()
    print('time: {}'.format(now))
    import random
    random.shuffle(data_json_list)

    for step, each in enumerate(data_json_list):
      input_ids = tokenizer(preprocess(each.get("input")), return_tensors="pt").input_ids.long().to(device)
      labels = tokenizer(preprocess(each.get("label")), return_tensors="pt").input_ids.long().to(device)
      outputs = model(input_ids=input_ids, labels=labels)
      loss = outputs.loss

      if gradient_accumulation > 1:
        loss = loss / gradient_accumulation

      loss.backward()
      torch.nn.utils.clip_grad_norm_(model.parameters(), max_grad_norm)

      #  optimizer step
      if (step + 1) % gradient_accumulation == 0:
        running_loss += loss.item()
        optimizer.step()
        optimizer.zero_grad()
        scheduler.step()
        overall_step += 1
        if (overall_step + 1) % log_step == 0:
          tb_writer.add_scalar('loss', loss.item(), overall_step)
      if (overall_step + 1) % log_step == 0:
        print('now time: {}:{}. Step {} of epoch {}, loss {}'.format(
          datetime.datetime.now().hour,
          datetime.datetime.now().minute,
          step + 1,
          epoch + 1,
          running_loss / log_step))
        running_loss = 0
      if step%10==0 and step>=10:
        if not os.path.exists(output_dir + 'model_epoch{}_step{}'.format(epoch + 1,step)):
          os.mkdir(output_dir + 'model_epoch{}_step{}'.format(epoch + 1,step))
        print('saving model for epoch {}, step {}'.format(epoch + 1,step))
        model_to_save = model.module if hasattr(model, 'module') else model
        model_to_save.save_pretrained(output_dir + 'model_epoch{}_step{}'.format(epoch + 1,step))
    print('saving model for epoch {}'.format(epoch + 1))
    if not os.path.exists(output_dir + 'model_epoch{}'.format(epoch + 1)):
      os.mkdir(output_dir + 'model_epoch{}'.format(epoch + 1))
    model_to_save = model.module if hasattr(model, 'module') else model
    model_to_save.save_pretrained(output_dir + 'model_epoch{}'.format(epoch + 1))
    print('epoch {} finished'.format(epoch + 1))

    then = datetime.datetime.now()
    print('time: {}'.format(then))
    print('time for one epoch: {}'.format(then - now))

  print('training finished')
  if not os.path.exists(output_dir + 'final_model'):
    os.mkdir(output_dir + 'final_model')
  model_to_save = model.module if hasattr(model, 'module') else model
  model_to_save.save_pretrained(output_dir + 'final_model')



print("begin train now")
train()
print("train end")

あなたが裕福であれば、バッチ トレーニング方法を使用できます。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/2/27 16:39
# preference:https://github.com/Shivanandroy/T5-Finetuning-PyTorch
#数据下载:链接:https://pan.baidu.com/s/1cwKLNZD7-rsdETogacP2jw?pwd=mefc 提取码:mefc
# @Author : sparkle_code_guy
import os
from torch.utils.tensorboard import SummaryWriter
from transformers import T5Tokenizer, T5ForConditionalGeneration
import torch
from torch.utils.data import Dataset, DataLoader
from torch import cuda
import numpy as np
import pandas as pd

device = 'cuda' if cuda.is_available() else 'cpu'
class YourDataSetClass(Dataset):
  """
  Creating a custom dataset for reading the dataset and
  loading it into the dataloader to pass it to the
  neural network for finetuning the model

  """

  def __init__(
          self, dataframe, tokenizer, source_len, target_len, source_text, target_text
  ):
    """
    Initializes a Dataset class

    Args:
        dataframe (pandas.DataFrame): Input dataframe
        tokenizer (transformers.tokenizer): Transformers tokenizer
        source_len (int): Max length of source text
        target_len (int): Max length of target text
        source_text (str): column name of source text
        target_text (str): column name of target text
    """
    self.tokenizer = tokenizer
    self.data = dataframe
    self.source_len = source_len
    self.rewrite_len = target_len
    self.target_text = self.data[target_text]
    self.source_text = self.data[source_text]

  def __len__(self):
    """returns the length of dataframe"""

    return len(self.target_text)

  def __getitem__(self, index):
    """return the input ids, attention masks and target ids"""

    source_text = str(self.source_text[index])
    target_text = str(self.target_text[index])

    # cleaning data so as to ensure data is in string type
    source_text = " ".join(source_text.split())
    target_text = " ".join(target_text.split())

    source = self.tokenizer.batch_encode_plus(
      [source_text],
      max_length=self.source_len,
      pad_to_max_length=True,
      truncation=True,
      padding="max_length",
      return_tensors="pt",
    )
    target = self.tokenizer.batch_encode_plus(
      [target_text],
      max_length=self.rewrite_len,
      pad_to_max_length=True,
      truncation=True,
      padding="max_length",
      return_tensors="pt",
    )

    source_ids = source["input_ids"].squeeze()
    source_mask = source["attention_mask"].squeeze()
    target_ids = target["input_ids"].squeeze()
    target_mask = target["attention_mask"].squeeze()

    return {
      "source_ids": source_ids.to(dtype=torch.long),
      "source_mask": source_mask.to(dtype=torch.long),
      "target_ids": target_ids.to(dtype=torch.long),
      "target_ids_y": target_ids.to(dtype=torch.long),
    }

def train(epoch, tokenizer, model, device, loader, optimizer,summary_writer,output_dir):

    """
    Function to be called for training with the parameters passed from main function
    """

    model.train()
    for _, data in enumerate(loader, 0):
      y = data["target_ids"].to(device, dtype=torch.long)
      y_ids = y[:, :-1].contiguous()
      lm_labels = y[:, 1:].clone().detach()
      lm_labels[y[:, 1:] == tokenizer.pad_token_id] = -100
      ids = data["source_ids"].to(device, dtype=torch.long)
      mask = data["source_mask"].to(device, dtype=torch.long)

      outputs = model(
        input_ids=ids,
        attention_mask=mask,
        decoder_input_ids=y_ids,
        labels=lm_labels,
      )
      loss = outputs[0]
      optimizer.zero_grad()
      loss.backward()
      optimizer.step()
      summary_writer.add_scalar('epoch/loss_{}'.format(epoch), loss.item(), _)

      if _%100000==0 and _ > 0:
        print(f"[Saving Model]...\n")
        # Saving the model after training
        path = os.path.join(output_dir, 'model_epoch{}_step{}'.format(epoch + 1,_))
        if not os.path.exists(path):
          os.mkdir(path)
        model.save_pretrained(path)
        tokenizer.save_pretrained(path)

def T5Trainer(
        dataframe, source_text, target_text, model_params, output_dir="./outputs/"
):
  """
  T5 trainer
  """
  if not os.path.exists(output_dir):
    os.mkdir(output_dir)
  # Set random seeds and deterministic pytorch for reproducibility
  torch.manual_seed(model_params["SEED"])  # pytorch random seed
  np.random.seed(model_params["SEED"])  # numpy random seed

  # logging
  print(f"""[Model]: Loading {model_params["MODEL"]}...\n""")

  # tokenzier for encoding the text
  tokenizer = T5Tokenizer.from_pretrained(model_params["MODEL"])

  # Defining the model. We are using t5-base model and added a Language model layer on top for generation of Summary.
  # Further this model is sent to device (GPU/TPU) for using the hardware.
  model = T5ForConditionalGeneration.from_pretrained(model_params["MODEL"])
  model = model.to(device)

  # logging
  print(f"[Data]: Reading data...\n")

  # Importing the raw dataset
  dataframe = dataframe[[source_text, target_text]]

  # Creation of Dataset and Dataloader
  # Defining the train size. So 80% of the data will be used for training and the rest for validation.
  train_size = 1
  train_dataset = dataframe.sample(frac=train_size, random_state=model_params["SEED"])
  train_dataset = train_dataset.reset_index(drop=True)

  print(f"FULL Dataset: {dataframe.shape}")
  print(f"TRAIN Dataset: {train_dataset.shape}")

  # Creating the Training and Validation dataset for further creation of Dataloader
  training_set = YourDataSetClass(
    train_dataset,
    tokenizer,
    model_params["MAX_SOURCE_TEXT_LENGTH"],
    model_params["MAX_TARGET_TEXT_LENGTH"],
    source_text,
    target_text,
  )

  # Defining the parameters for creation of dataloaders
  train_params = {
    "batch_size": model_params["TRAIN_BATCH_SIZE"],
    "shuffle": True,
    "num_workers": 0,
  }


  # Creation of Dataloaders for testing and validation. This will be used down for training and validation stage for the model.
  training_loader = DataLoader(training_set, **train_params)

  # Defining the optimizer that will be used to tune the weights of the network in the training session.
  optimizer = torch.optim.Adam(
    params=model.parameters(), lr=model_params["LEARNING_RATE"]
  )

  # Training loop
  print(f"[Initiating Fine Tuning]...\n")

  for epoch in range(model_params["TRAIN_EPOCHS"]):
    summary_writer = SummaryWriter(log_dir="t5/summary_task")
    train(epoch, tokenizer, model, device, training_loader, optimizer,summary_writer,output_dir)
    print(f"[Saving Model]...\n")
    # Saving the model after training
    path = os.path.join(output_dir, 'model_epoch{}'.format(epoch + 1))
    if not os.path.exists(path):
      os.mkdir(path)
    model.save_pretrained(path)
    tokenizer.save_pretrained(path)
  print(
    f"""[Model] Model saved @ {os.path.join(output_dir, "model_files")}\n"""
  )


if __name__ == '__main__':
  model_params = {
    "MODEL": "ClueAI/ChatYuan-large-v1",  # model_type: t5-base/t5-large
    "TRAIN_BATCH_SIZE": 8,  # training batch size
    "TRAIN_EPOCHS": 3,  # number of training epochs
    "LEARNING_RATE": 1e-4,  # learning rate
    "MAX_SOURCE_TEXT_LENGTH": 768,  # max length of source text
    "MAX_TARGET_TEXT_LENGTH": 512,  # max length of target text
    "SEED": 42,  # set seed for reproducibility
  }
  train_dataframe = pd.read_csv("data/new_data.txt", sep='\t')
  T5Trainer(train_dataframe, "input", "label", model_params)

トレーニング データセット:

リンク: https://pan.baidu.com/s/1-aaKVBB80afYVuueJ2niPw?pwd=nrb9

抽出コード:nrb9

モデルの適用部分について:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/2/27 16:39
# @Author : sparkle_code_guy
from transformers import T5Tokenizer, T5ForConditionalGeneration

tokenizer = T5Tokenizer.from_pretrained("ClueAI/ChatYuan-large-v1")
model = T5ForConditionalGeneration.from_pretrained("ClueAI/ChatYuan-large-v1")
import torch

# 修改colab笔记本设置为gpu,推理更快
device = torch.device('cuda')
model.to(device)

def preprocess(text):
    text = text.replace("\n", "\\n").replace("\t", "\\t")
    return text

def postprocess(text):
    return text.replace("\\n", "\n").replace("\\t", "\t")

def answer(text, sample=True, top_p=1, temperature=0.7):
    '''sample:是否抽样。生成任务,可以设置为True;
    top_p:0-1之间,生成的内容越多样'''
    text = preprocess(text)
    print(len(text))
    encoding = tokenizer(text=[text], truncation=True, padding=True, max_length=768, return_tensors="pt").to(device)
    if not sample:
        out = model.generate(**encoding, return_dict_in_generate=True, output_scores=False, max_new_tokens=512,
                             num_beams=1, length_penalty=0.6)
    else:
        out = model.generate(**encoding, return_dict_in_generate=True, output_scores=False, max_new_tokens=512,
                             do_sample=True, top_p=top_p, temperature=temperature, no_repeat_ngram_size=3)
    out_text = tokenizer.batch_decode(out["sequences"], skip_special_tokens=True)
    return postprocess(out_text[0])

def rewrite_message(input):
    print("query message:",input)
    answer_message_list=[]
    for each in range(4):
        answer_message_list.append("方案{0}:".format(each) + answer_message(input) )

    return "\n\n".join(answer_message_list)

def answer_message(input):
    input_format = input.replace("\n", "。")
    input_text = "用户:" + input_format + "\n小智:"
    output_text = answer(input_text)
    return f"{output_text}"

import gradio as gr

examples_list = [
                 "example1"]

synthesis_interface = gr.Interface(rewrite_message,
                                   inputs=gr.components.Textbox(lines=10,interactive=True,placeholder="enter your question ..."),
                                   outputs=gr.components.Textbox(lines=10,interactive=False),
                                   cache_examples=False,
                                   title="问答",
                                   examples_per_page=5,
                                   examples=examples_list,
                                   live=False)
synthesis_interface.launch(share=False,server_name='0.0.0.0',server_port=7860)

おすすめ

転載: blog.csdn.net/sslfk/article/details/129387874