Longformer

repository·master·Indexed 24 days ago

https://github.com/allenai/longformer

Transformer-based models designed for efficient processing of long documents using sliding window attention. Includes the standard Longformer and LongformerEncoderDecoder (LED) for seq2seq tasks. Supports multiple attention modes ('n2', 'tvm', and 'sliding_chunks') and provides utilities for converting RoBERTa checkpoints into Longformer models, including position embedding extension and MLM pretraining.

Tokens
3.9K
Snippets
6
Records
9
Agent score
31%

What's inside Longformer

  1. Choose an attention mode

    master

    The LongformerConfig allows you to select between three attention modes via the attention_mode attribute. Choosing the right mode depends on your hardware and task:

    • 'n2': Regular $O(n^2)$ attention.
    • 'tvm': A custom CUDA kernel implementation of sliding window attention. This is optimized for GPUs but does not work on CPU.
    • 'sliding_chunks': A PyTorch implementation of sliding window attention. This is more convenient for fine-tuning as it supports CPU, TPU, and fp16, though it uses approximately 2x more memory than the TVM implementation and does not support dilation or autoregressive attention.
  2. Use LongformerEncoderDecoder (LED)

    master

    The LongformerEncoderDecoder (LED) model supports seq2seq tasks with long inputs. With gradient checkpointing, fp16, and a 48GB GPU, input lengths can reach up to 16K tokens.

    Pretrained Models:

    1. led-base-16384
    2. led-large-16384

    Refer to scripts/summarization.py for usage examples.

  3. Configure and run Longformer

    master

    To run the model, you must initialize a LongformerConfig, set the attention_mode, and load the model. When using sliding_chunks mode, you must pad the sequence length to the nearest multiple of 512 using pad_to_window_size.

    Attention Mask Values:

    • 0: No attention
    • 1: Local attention
    • 2: Global attention (e.g., use for the <s> token in classification or question tokens in QA tasks)
    import torch
    from longformer.longformer import Longformer, LongformerConfig
    from longformer.sliding_chunks import pad_to_window_size
    from transformers import RobertaTokenizer
    
    config = LongformerConfig.from_pretrained('longformer-base-4096/') 
    # choose the attention mode 'n2', 'tvm' or 'sliding_chunks'
    config.attention_mode = 'sliding_chunks'
    
    model = Longformer.from_pretrained('longformer-base-4096/', config=config)
    tokenizer = RobertaTokenizer.from_pretrained('roberta-base')
    tokenizer.model_max_length = model.config.max_position_embeddings
    
    SAMPLE_TEXT = ' '.join(['Hello world! '] * 1000)  # long input document
    
    input_ids = torch.tensor(tokenizer.encode(SAMPLE_TEXT)).unsqueeze(0)  # batch of size 1
    
    # TVM code doesn't work on CPU. Uncomment this if `config.attention_mode = 'tvm'`
    # model = model.cuda(); input_ids = input_ids.cuda()
    
    # Attention mask values -- 0: no attention, 1: local attention, 2: global attention
    attention_mask = torch.ones(input_ids.shape, dtype=torch.long, device=input_ids.device) # initialize to local attention
    attention_mask[:, [1, 4, 21,]] =  2  # Set global attention based on the task. For example,
                                         # classification: the <s> token
                                         # QA: question tokens
    
    # padding seqlen to the nearest multiple of 512. Needed for the 'sliding_chunks' attention
    input_ids, attention_mask = pad_to_window_size(
            input_ids, attention_mask, config.attention_window[0], tokenizer.pad_token_id)
    
    output = model(input_ids, attention_mask=attention_mask)[0]
  4. Install Longformer

    master

    To set up the Longformer environment, create a new conda environment with Python 3.7, install cudatoolkit=10.0, and install the package directly from the GitHub repository.

    conda create --name longformer python=3.7
    conda activate longformer
    conda install cudatoolkit=10.0
    pip install git+https://github.com/allenai/longformer.git
  5. Convert RoBERTa to Longformer

    master

    To build a 'long' version of a pretrained RoBERTa model, you can follow the procedure described in the Longformer paper. This involves converting a standard RoBERTa checkpoint into a RobertaLong model by:

    1. Extending position embeddings: Increase the position embedding matrix from the default (e.g., 512) to a larger max_pos (e.g., 4096).
    2. Initializing additional embeddings: Crucially, initialize the new position embeddings by copying the embeddings of the first 512 positions over and over. This is vital for model performance.
    3. Replacing attention mechanisms: Replace the standard BertSelfAttention objects with LongformerSelfAttention using a specified attention_window.

    The resulting model can be used for long documents even without further pretraining, though pretraining on Masked Language Modeling (MLM) is recommended to improve performance.

    def create_long_model(save_model_to, attention_window, max_pos):
        model = RobertaForMaskedLM.from_pretrained('roberta-base')
        tokenizer = RobertaTokenizerFast.from_pretrained('roberta-base', model_max_length=max_pos)
        config = model.config
    
        # extend position embeddings
        tokenizer.model_max_length = max_pos
        tokenizer.init_kwargs['model_max_length'] = max_pos
        current_max_pos, embed_size = model.roberta.embeddings.position_embeddings.weight.shape
        max_pos += 2  # NOTE: RoBERTa has positions 0,1 reserved, so embedding size is max position + 2
        config.max_position_embeddings = max_pos
        assert max_pos > current_max_pos
        # allocate a larger position embedding matrix
        new_pos_embed = model.roberta.embeddings.position_embeddings.weight.new_empty(max_pos, embed_size)
        # copy position embeddings over and over to initialize the new position embeddings
        k = 2
        step = current_max_pos - 2
        while k < max_pos - 1:
            new_pos_embed[k:(k + step)] = model.roberta.embeddings.position_embeddings.weight[2:]
            k += step
        model.roberta.embeddings.position_embeddings.weight.data = new_pos_embed
        model.roberta.embeddings.position_ids.data = torch.tensor([i for i in range(max_pos)]).reshape(1, max_pos)
    
        # replace the `modeling_bert.BertSelfAttention` object with `LongformerSelfAttention`
        config.attention_window = [attention_window] * config.num_hidden_layers
        for i, layer in enumerate(model.roberta.encoder.layer):
            longformer_self_attn = LongformerSelfAttention(config, layer_id=i)
            longformer_self_attn.query = layer.attention.self.query
            longformer_self_attn.key = layer.attention.self.key
            longformer_self_attn.value = layer.attention.self.value
    
            longformer_self_attn.query_global = copy.deepcopy(layer.attention.self.query)
            longformer_self_attn.key_global = copy.deepcopy(layer.attention.self.key)
            longformer_self_attn.value_global = copy.deepcopy(layer.attention.self.value)
    
            layer.attention.self = longformer_self_attn
    
        logger.info(f'saving model to {save_model_to}')
        model.save_pretrained(save_model_to)
        tokenizer.save_pretrained(save_model_to)
        return model, tokenizer
  6. Pretrain Longformer on Masked Language Modeling (MLM)

    master

    To pretrain a converted Longformer model, use the pretrain_and_evaluate function.

    Key Hyperparameter Considerations:

    • Tokens per batch: It is recommended to keep the number of tokens per batch constant (e.g., $2^{18}$ tokens). Use the formula: #tokens/batch = batch_size * #gpus * gradient_accumulation * seqlen.
    • Learning Rate Scheduler: The original paper uses a polynomial decay. If training for a shorter duration (e.g., 3k steps instead of 65k), a constant learning rate scheduler (after warmup) is recommended.
    • Hardware: Pretraining can be intensive. Using fp16 and multiple GPUs is advised to speed up the process.
    • Steps: While the paper uses 65k steps, 3k steps may be sufficient for many applications.
    def pretrain_and_evaluate(args, model, tokenizer, eval_only, model_path):
        val_dataset = TextDataset(tokenizer=tokenizer,
                                  file_path=args.val_datapath,
                                  block_size=tokenizer.max_len)
        if eval_only:
            train_dataset = val_dataset
        else:
            logger.info(f'Loading and tokenizing training data is usually slow: {args.train_datapath}')
            train_dataset = TextDataset(tokenizer=tokenizer,
                                        file_path=args.train_datapath,
                                        block_size=tokenizer.max_len)
    
        data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15)
        trainer = Trainer(model=model, args=args, data_collator=data_collator,
                          train_dataset=train_dataset, eval_dataset=val_dataset, prediction_loss_only=True,)
    
        eval_loss = trainer.evaluate()
        eval_loss = eval_loss['eval_loss']
        logger.info(f'Initial eval bpc: {eval_loss/math.log(2)}')
    
        if not eval_only:
            trainer.train(model_path=model_path)
            trainer.save_model()
    
        eval_loss = trainer.evaluate()
        logger.info(f'Eval bpc after pretraining: {eval_loss/math.log(2)}')
  7. Setup environment for model conversion

    master

    To follow the conversion and pretraining guide, you need to install transformers and prepare a corpus (e.g., Wikitext103).

    # Download and unzip Wikitext103
    !wget https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-103-raw-v1.zip
    !unzip wikitext-103-raw-v1.zip
    
    # Install required transformers version
    !pip install transformers==3.0.2
  8. Use Gradient Checkpointing to reduce memory

    master

    Gradient checkpointing can reduce memory usage by approximately 5x (specifically for longformer-base-4096), allowing for longer sequences on smaller GPUs. This is available via the transformers integration.

    from transformers import LongformerModel
    model = LongformerModel.from_pretrained('allenai/longformer-base-4096', gradient_checkpointing=True)
  9. Initialize global projection layers after MLM pretraining

    master

    Because Masked Language Modeling (MLM) pretraining does not update the global projection layers, you must manually copy the local projection layers (query, key, value) to their global counterparts (query_global, key_global, value_global) after pretraining is complete. This step is necessary before fine-tuning the model on downstream tasks.

    def copy_proj_layers(model):
        for i, layer in enumerate(model.roberta.encoder.layer):
            layer.attention.self.query_global = copy.deepcopy(layer.attention.self.query)
            layer.attention.self.key_global = copy.deepcopy(layer.attention.self.key)
            layer.attention.self.value_global = copy.deepcopy(layer.attention.self.value)
        return model