bert4torch

repository·master·Indexed 23 days ago

https://github.com/tongjilibo/bert4torch

A comprehensive toolkit for Transformer-based models in PyTorch, offering a Keras-style training experience. It supports traditional encoder models (BERT, RoBERTa) for fine-tuning and Large Language Models (LLMs) such as Llama 1 & 2, ChatGLM, Baichuan, and Yi for inference and deployment. The library includes utilities for weight merging (delta and LoRA), SFT, DPO, and RLHF pipelines, as well as a MiniLLM project for training models from scratch.

Tokens
27.9K
Snippets
38
Records
103
Agent score
79%

What's inside bert4torch

  1. Supported LLaMA-based model architectures

    master

    This example directory provides guidance and model references for various LLaMA-based architectures supported by bert4torch, including:

    • Llama 1 & 2: Standard Facebook/Meta weights.
    • Baichuan: Note that Baichuan-7B uses the LLaMA architecture, while Baichuan-13B uses ALiBi positional encoding instead of RoPE.
    • Ziya-LLaMA: Requires weight merging via delta scripts.
    • Chinese-LLaMA-Alpaca: Requires weight conversion and LoRA merging.
    • BELLE-LLaMA: Distributed as diffs that require merging with official LLaMA weights.
    • Vicuna: LLaMA-based fine-tuned models.
    • Yi: 01-AI models.
  2. Files in the Tianchi News Classification example

    master

    The example directory contains the following scripts to facilitate the workflow:

    • convert.py: A utility script to convert TensorFlow weights from the original competition source into PyTorch weights compatible with bert4torch.
    • training.py: The main fine-tuning training script used to train the model on the news classification task.
  3. Implement RoBERTa-style dynamic masking for pretraining

    master

    To replicate RoBERTa's dynamic masking behavior, this implementation decouples data generation from model training. Instead of pre-masking a static dataset, a continuous data generation process creates new masked versions of the corpus, which the training process consumes. This ensures that the model encounters different masking patterns for the same text across different training steps.

    Workflow:

    1. Data Generation: A dedicated process reads raw .txt corpus files and continuously generates new masked training files.
    2. Model Training: A separate process reads the generated training files to perform MLM (Masked Language Model) training.

    To achieve this, you must run both the data generator and the trainer simultaneously in separate terminal sessions.

  4. Extend Callback for custom evaluation and saving

    master

    The Callback class allows you to hook into different stages of the training process. Common hooks include:

    • on_train_begin() / on_train_end()
    • on_epoch_begin() / on_epoch_end()
    • on_batch_begin() / on_batch_end()
    • on_dataloader_end()

    Use on_epoch_end() to perform validation on a separate dataloader and model.save_weights() to save the best performing model based on a metric.

    class Evaluator(Callback):
        def __init__(self):
            self.best_val_acc = 0.
    
        def on_epoch_end(self, global_step, epoch, logs=None):
            val_acc = evaluate(valid_dataloader)
            if val_acc > self.best_val_acc:
                self.best_val_acc = val_acc
                model.save_weights('best_model.pt')
            print(f'val_acc: {val_acc:.5f}, best_val_acc: {self.best_val_acc:.5f}\n')
  5. Extend Callback for Custom Evaluation

    master

    The Callback class allows you to inject logic at various stages of the training lifecycle. Common hooks include:

    • on_train_begin / on_train_end
    • on_epoch_begin / on_epoch_end (ideal for validation and saving best models)
    • on_batch_begin / on_batch_end (ideal for logging or periodic tensorboard updates)
    • on_dataloader_end (useful for re-generating dataloaders, e.g., in pre-training)
    class Evaluator(Callback):
        def __init__(self):
            self.best_val_acc = 0.
    
        def on_epoch_end(self, global_step, epoch, logs=None):
            # Perform validation logic here
            val_acc = evaluate(valid_dataloader)
            if val_acc > self.best_val_acc:
                self.best_val_acc = val_acc
                model.save_weights('best_model.pt')
  6. Prepare Triton Model Repository and config.pbtxt

    master

    Triton requires a specific directory structure for the model repository. For a sentence_classification model, the structure should look like this:

    model_repository
    └─sentence_classification
        └─1
            └─model.plan
        └─config.pbtxt

    The config.pbtxt file defines the model platform, batch size, and input/output tensor specifications. For a TensorRT engine, use platform: "tensorrt_plan".

    name: "sentence_classification"
    platform: "tensorrt_plan"
    max_batch_size: 8
    version_policy: { latest { num_versions: 1 }}
    input [
      {
        name: "input_ids"
        data_type: TYPE_INT32
        dims: [ -1 ]
      },
      {
        name: "segment_ids"
        data_type: TYPE_INT32
        dims: [ -1 ]
      }
    ]
    output [
      {
        name: "output"
        data_type: TYPE_FP32
        dims: [ -1 ]
      }
    ]
  7. Complete Modeling Workflow Example

    master

    This example demonstrates the full lifecycle of training a BERT-based model for text binary classification using bert4torch.

    Key steps include:

    1. Tokenizer Setup: Initialize Tokenizer with a dictionary path.
    2. Dataset Loading: Inherit from ListDataset and implement load_data. Use a collate_fn to return features (as a list/tuple) and labels.
    3. Model Definition: Inherit from BaseModel. Use build_transformer_model to load the BERT backbone. Note: Models returned by build_transformer_model expect inputs as a list or tuple (e.g., self.bert([token_ids, segment_ids])).
    4. Compilation: Use model.compile() to define loss, optimizer, scheduler, gradient clipping, and metrics.
    5. Evaluation: Implement a custom evaluation function and a Callback (e.g., Evaluator) to handle validation and weight saving at the end of each epoch.
    6. Training: Call model.fit() with the dataloader, epochs, and desired callbacks (e.g., Logger, Tensorboard, AdversarialTraining).
    from bert4torch.tokenizers import Tokenizer
    from bert4torch.models import build_transformer_model, BaseModel
    from bert4torch.snippets import Callback, Logger, Tensorboard, ListDataset, AdversarialTraining
    import torch.nn as nn
    import torch
    import torch.optim as optim
    from torch.utils.data import DataLoader
    
    # 1. Setup Tokenizer
    tokenizer = Tokenizer(dict_path, do_lower_case=True)
    
    # 2. Load Dataset
    class MyDataset(ListDataset):
        @staticmethod
        def load_data(filenames):
            D = []
            return D
    
    def collate_fn(batch):
        batch_token_ids, batch_segment_ids, batch_labels = [], [], []
        return [batch_token_ids, batch_segment_ids], batch_labels.flatten()
    
    train_dataloader = DataLoader(MyDataset('file_path'), batch_size=batch_size, shuffle=True, collate_fn=collate_fn) 
    
    # 3. Define Model
    class Model(BaseModel):
        def __init__(self) -> None:
            super().__init__()
            self.bert = build_transformer_model(config_path, checkpoint_path, with_pool=True)
            self.dropout = nn.Dropout(0.1)
            self.dense = nn.Linear(768, 2)
    
        def forward(self, token_ids, segment_ids):
            # build_transformer_model models accept list/tuple arguments
            hidden_states, pooled_output = self.bert([token_ids, segment_ids])
            output = self.dropout(pooled_output)
            output = self.dense(output)
            return output
    
    model = Model().to(device)
    
    # 4. Compile
    model.compile(
        loss=nn.CrossEntropyLoss(),
        optimizer=optim.Adam(model.parameters(), lr=2e-5),
        scheduler=None,
        clip_gram_norm=1.0,
        grad_accumulation_steps=2,
        metrics=['accuracy']
    )
    
    # 5. Evaluation Callback
    class Evaluator(Callback):
        def __init__(self):
            self.best_val_acc = 0.
    
        def on_epoch_end(self, global_step, epoch, logs=None):
            val_acc = evaluate(valid_dataloader)
            if val_acc > self.best_val_acc:
                self.best_val_acc = val_acc
                model.save_weights('best_model.pt')
            print(f'val_acc: {val_acc:.5f}, best_val_acc: {self.best_val_acc:.5f}\n')
    
    # 6. Fit
    if __name__ == '__main__':
        model.fit(train_dataloader, epochs=20, steps_per_epoch=100,
                  callbacks=[Evaluator(), AdversarialTraining('fgm'), Logger('./test/test.log'), Tensorboard('./test/')])
  8. Complete Modeling Workflow Example

    master

    This guide demonstrates the standard end-to-end workflow for training a model using bert4torch, including tokenizer setup, dataset definition, model architecture construction, compilation, and training with callbacks.

    from bert4torch.tokenizers import Tokenizer
    from bert4torch.models import build_transformer_model, BaseModel
    from bert4torch.snippets import ListDataset
    from bert4torch.callbacks import Callback, Logger, Tensorboard, AdversarialTraining
    import torch.nn as nn
    import torch
    import torch.optim as optim
    from torch.utils.data import DataLoader
    
    # 1. Setup Tokenizer
    tokenizer = Tokenizer(dict_path, do_lower_case=True)
    
    # 2. Define Dataset
    class MyDataset(ListDataset):
        @staticmethod
        def load_data(filenames):
            D = []
            return D
    
    def collate_fn(batch):
        batch_token_ids, batch_segment_ids, batch_labels = [], [], []
        return [batch_token_ids, batch_segment_ids], batch_labels.flatten()
    
    train_dataloader = DataLoader(MyDataset('file_path'), batch_size=batch_size, shuffle=True, collate_fn=collate_fn) 
    
    # 3. Define Model Architecture
    class Model(BaseModel):
        def __init__(self) -> None:
            super().__init__()
            self.bert = build_transformer_model(config_path, checkpoint_path, with_pool=True)
            self.dropout = nn.Dropout(0.1)
            self.dense = nn.Linear(768, 2)
    
        def forward(self, token_ids, segment_ids):
            # build_transformer_model returns [hidden_states, pooled_output] if with_pool=True
            # Input must be wrapped in a list/tuple if there is only one argument
            hidden_states, pooled_output = self.bert([token_ids, segment_ids])
            output = self.dropout(pooled_output)
            output = self.dense(output)
            return output
    
    model = Model().to(device)
    
    # 4. Compile Model
    model.compile(
        loss=nn.CrossEntropyLoss(),
        optimizer=optim.Adam(model.parameters(), lr=2e-5),
        scheduler=None,
        clip_gram_norm=1.0,
        grad_accumulation_steps=2,
        metrics=['accuracy']
    )
    
    # 5. Define Evaluation and Callbacks
    class Evaluator(Callback):
        def __init__(self):
            self.best_val_acc = 0.
    
        def on_epoch_end(self, global_step, epoch, logs=None):
            val_acc = evaluate(valid_dataloader)
            if val_acc > self.best_val_acc:
                self.best_val_acc = val_acc
                model.save_weights('best_model.pt')
            print(f'val_acc: {val_acc:.5f}, best_val_acc: {self.best_val_acc:.5f}\n')
    
    # 6. Train
    if __name__ __name__ == '__main__':
        model.fit(train_dataloader, epochs=20, steps_per_epoch=100,
                  callbacks=[Evaluator(), AdversarialTraining('fgm'), Logger('./test/test.log'), Tensorboard('./test/')])
  9. Perform single-machine multi-GPU training with DataParallel

    master

    To use DataParallel (DP) for multi-GPU training, wrap your model with BaseModelDP.

    There are two ways to implement the forward pass in DP:

    1. The forward pass returns only the logits.
    2. The forward pass directly calculates and returns the loss.

    It is recommended to use the second method (returning the loss) to help mitigate load imbalance issues across GPUs. When using model.compile(), provide a loss function that calculates the mean of the losses from multiple GPUs.

    from bert4torch.models import BaseModelDP
    
    # ===========处理数据和定义model ===========
    
    model = BaseModelDP(model)  # 指定DP模式使用多gpu
    model.compile(
        loss=lambda x, _: x.mean(),  # 多个gpu计算的loss的均值
        optimizer=optim.Adam(model.parameters(), lr=2e-5),
    )
  10. Explore advanced model tasks and research implementations

    master

    The examples/others/ directory contains specialized implementations for various NLP tasks:

    • Language Modeling:
      • Conditional Language Model using BERT + ConditionalLayerNormalization (task_conditional_language_model.py).
      • Unconditional Language Model (GPT-equivalent) by loading BERT pre-trained weights (task_language_model.py).
    • Model Compression: Implement BERT-of-Theseus for model compression (task_iflytek_bert_of_theseus.py).
    • NLU & Extraction:
      • Intent classification and entity extraction for dialogue NLU using multi-stage models (task_nlu_intent_entity.py).
      • Event extraction using gplinker (task_event_extraction_gplinker.py).
    • Specialized Tasks:
      • NL2SQL baseline for the Zhuiyi Technology 2019 challenge (task_nl2sql_baseline.py).
      • Playing Chinese Chess using a GPT-style approach (task_language_model_chinese_chess.py).