文本分類上分微調技巧實戰

目錄

  • 引言
  • How to Fine-Tune BERT for Text Classification 論文
    • 微調策略
    • ITPT:繼續預訓練
  • 學術論文分類挑戰賽微調
    • huggingface工具介紹
    • bert模型介紹
    • 數據創建
    • 模型定義
    • 模型訓練與評估
    • 模型改進

How to Fine-Tune BERT for Text Classification 論文

微調策略

  1. 處理長文本 我們知道BERT 的最大序列長度爲 512,BERT 應用於文本分類的第一個問題是如何處理長度大於 512 的文本。本文嘗試了以下方式處理長文章。

Truncation methods 截斷法 文章的關鍵信息位於開頭和結尾。 我們可以使用三種不同的截斷文本方法來執行 BERT 微調。

head-only: keep the first 510 tokens 頭部510個字符,加上兩個特殊字符剛好是512 ;
tail-only: keep the last 510 tokens;尾部510個字符,同理加上兩個特殊字符剛好是512 ;
head+tail: empirically select the first 128and the last 382 tokens.:尾部結合

Hierarchical methods 層級法 輸入的文本首先被分成k = L/510個片段,喂入 BERT 以獲得 k 個文本片段的表示向量。 每個分數的表示是最後一層的 [CLS] 標記的隱藏狀態,然後我們使用均值池化、最大池化和自注意力來組合所有分數的表示。

  1. 不同層的特徵 BERT 的每一層都捕獲輸入文本的不同特徵。 文本研究了來自不同層的特徵的有效性, 然後我們微調模型並記錄測試錯誤率的性能。

我們可以看到:最後一層表徵效果最好;最後4層進行max-pooling效果最好

  1. 災難性遺忘 Catastrophic forgetting (災難性遺忘)通常是遷移學習中的常見詬病,這意味着在學習新知識的過程中預先訓練的知識會被遺忘。 因此,本文還研究了 BERT 是否存在災難性遺忘問題。 我們用不同的學習率對 BERT 進行了微調,發現需要較低的學習率,例如 2e-5,才能使 BERT 克服災難性遺忘問題。 在 4e-4 的較大學習率下,訓練集無法收斂。

這個也深有體會,當預訓練模型失效不能夠收斂的時候多檢查下超參數是否設置有問題。

  1. Layer-wise Decreasing Layer Rate 逐層降低學習率 下表 顯示了不同基礎學習率和衰減因子在 IMDb 數據集上的性能。 我們發現爲下層分配較低的學習率對微調 BERT 是有效的,比較合適的設置是 ξ=0.95 和 lr=2.0e-5

爲不同的BERT設置不同的學習率及衰減因子,BERT的表現如何?


η^l代表第幾層的學習率

ITPT:繼續預訓練

ITPT:繼續預訓練
Bert是在通用的語料上進行預訓練的,如果要在特定領域應用文本分類,數據分佈一定是有一些差距的。這時候可以考慮進行深度預訓練。

  1. Within-task pre-training:Bert在訓練語料上進行預訓練 In-domain pre-training:在同一領域上的語料進行預訓練 Cross-domain pre-training:在不同領域上的語料進行預訓練
    Within-task pretraining
    [圖片上傳失敗...(image-177ffd-1627289320036)]
    BERT-ITPT-FiT 的意思是“BERT + with In-Task Pre-Training + Fine-Tuning”,上圖表示IMDb 數據集上進行不同步數的繼續預訓練是有收益的。

  2. In-Domain 和 Cross-Domain Further Pre-Training



    我們發現幾乎所有進一步的預訓練模型在所有七個數據集上的表現都比原始 BERT 基礎模型。 一般來說,域內預訓練可以帶來比任務內預訓練更好的性能。 在小句子級 TREC 數據集上,任務內預訓練會損害性能,而在使用 Yah 的領域預訓練中。Yah. A.語料庫可以在TREC上取得更好的結果。
    這篇論文與其他模型進行了比較,結果如下表所示:


改進1 Last 4 Layers Concatenating

class LastFourModel(nn.Module):
    
    def __init__(self):
        super().__init__()
        
        config = AutoConfig.from_pretrained(PRE_TRAINED_MODEL_NAME)
        config.update({'output_hidden_states':True})
        self.model = AutoModel.from_pretrained(PRE_TRAINED_MODEL_NAME, config=config)
        self.linear = nn.Linear(4*HIDDEN_SIZE, n_classes)
        
    def forward(self, input_ids, attention_mask):
        
        outputs = self.model(input_ids, attention_mask)
        all_hidden_states = torch.stack(outputs[2])
        concatenate_pooling = torch.cat(
            (all_hidden_states[-1], all_hidden_states[-2], all_hidden_states[-3], all_hidden_states[-4]), -1
        )
        concatenate_pooling = concatenate_pooling[:,0]
        output = self.linear(concatenate_pooling)
        
        return soutput

改進2 模型層間差分學習率

def get_parameters(model, model_init_lr, multiplier, classifier_lr):
    parameters = []
    lr = model_init_lr
    for layer in range(12,-1,-1):
        layer_params = {
            'params': [p for n,p in model.named_parameters() if f'encoder.layer.{layer}.' in n],
            'lr': lr
        }
        parameters.append(layer_params)
        lr *= multiplier
    classifier_params = {
        'params': [p for n,p in model.named_parameters() if 'layer_norm' in n or 'linear' in n 
                   or 'pooling' in n],
        'lr': classifier_lr
    }
    parameters.append(classifier_params)
    return parameters
parameters=get_parameters(model,2e-5,0.95, 1e-4)
optimizer=AdamW(parameters)

改進3 ITPT 繼續預訓練

import warnings
import pandas as pd
from transformers import (AutoModelForMaskedLM,
                          AutoTokenizer, LineByLineTextDataset,
                          DataCollatorForLanguageModeling,
                          Trainer, TrainingArguments)

warnings.filterwarnings('ignore')

train_data = pd.read_csv('data/train/train.csv', sep='\t')
test_data = pd.read_csv('data/test/test.csv', sep='\t')
train_data['text'] = train_data['title'] + '.' + train_data['abstract']
test_data['text'] = test_data['title'] + '.' + test_data['abstract']
data = pd.concat([train_data, test_data])
data['text'] = data['text'].apply(lambda x: x.replace('\n', ''))

text = '\n'.join(data.text.tolist())

with open('text.txt', 'w') as f:
    f.write(text)

model_name = 'roberta-base'
model = AutoModelForMaskedLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.save_pretrained('./paper_roberta_base')

train_dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path="text.txt",  # mention train text file here
    block_size=256)

valid_dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path="text.txt",  # mention valid text file here
    block_size=256)

data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer, mlm=True, mlm_probability=0.15)

training_args = TrainingArguments(
    output_dir="./paper_roberta_base_chk",  # select model path for checkpoint
    overwrite_output_dir=True,
    num_train_epochs=5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    gradient_accumulation_steps=2,
    evaluation_strategy='steps',
    save_total_limit=2,
    eval_steps=200,
    metric_for_best_model='eval_loss',
    greater_is_better=False,
    load_best_model_at_end=True,
    prediction_loss_only=True,
    report_to="none")

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=train_dataset,
    eval_dataset=valid_dataset)

trainer.train()
trainer.save_model(f'./paper_roberta_base')
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章