在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何進行MLM訓練

深度學習自然語言處理 ? 來源:CSDN ? 作者:常鴻宇 ? 2022-08-13 10:54 ? 次閱讀

1. 關于MLM

1.1 背景

作為 Bert 預訓練的兩大任務之一,MLMNSP 大家應該并不陌生。其中,NSP 任務在后續的一些預訓練任務中經常被嫌棄,例如 Roberta 中將 NSP 任務直接放棄,Albert 中將 NSP 替換成了句子順序預測。

這主要是因為 NSP 作為一個分類任務過于簡單,對模型的學習并沒有太大的幫助,而 MLM 則被多數預訓練模型保留下來。由 Roberta的實驗結果也可以證明,Bert 的主要能力應該是來自于 MLM 任務的訓練。

Bert為代表的預訓練語言模型是在大規模語料的基礎上訓練以獲得的基礎的學習能力,而實際應用時,我們所面臨的語料或許具有某些特殊性,這就使得重新進行 MLM 訓練具有了必要性。

1.2 如何進行MLM訓練

1.2.1 什么是MLM

MLM 的訓練,在不同的預訓練模型中其實是有所不同的。今天介紹的內容以最基礎的 Bert 為例。

Bert的MLM是靜態mask,而在后續的其他預訓練模型中,這一策略通常被替換成了動態mask。除此之外還有 whole word mask 的模型,這些都不在今天的討論范圍內。

所謂 mask language model 的任務,通俗來講,就是將句子中的一部分token替換掉,然后根據句子的剩余部分,試圖去還原這部分被mask的token

1.2.2 如何Mask

mask 的比例一般是15%,這一比例也被后續的多數模型所繼承,而在最初BERT 的論文中,沒有對這一比例的界定給出具體的說明。在我的印象中,似乎是知道后來同樣是Google提出的 T5 模型的論文中,對此進行了解釋,對 mask 的比例進行了實驗,最終得出結論,15%的比例是最合理的(如果我記錯了,還請指正)。

15%的token選出之后,并不是所有的都替換成[mask]標記符。實際操作是:

  • 從這15%選出的部分中,將其中的80%替換成[mask];
  • 10%替換成一個隨機的token;
  • 剩下的10%保留原來的token。

這樣做可以提高模型的魯棒性。這個比例也可以自己控制。

到這里可能有同學要問了,既然有10%保留不變的話,為什么不干脆只選擇15%*90% = 13.5%的token呢?如果看完后面的代碼,就會很清楚地理解這個問題了。

先說結論:因為 MLM 的任務是將選出的這15%的token全部進行預測,不管這個token是否被替換成了[mask],也就是說,即使它被保留了原樣,也還是需要被預測的

2. 代碼部分

2.1 背景

介紹完了基礎內容之后,接下來的內容,我將基于 transformers 模塊,介紹如何進行 mask language model 的訓練。

其實 transformers 模塊中,本身是提供了 MLM 訓練任務的,模型都寫好了,只需要調用它內置的 trainerdatasets模塊即可。感興趣的同學可以去 huggingface 的官網搜索相關教程

然而我覺得 datasets 每次調用的時候都要去寫數據集的py文件,對arrow的數據格式不熟悉的話還很容易出錯,而且 trainer 我覺得也不是很好用,任何一點小小的修改都挺費勁(就是它以為它寫的很完備,考慮了用戶的所有需求,但是實際上有一些冗余的部分)。

所以我就參考它的實現方式,把它的代碼拆解,又按照自己的方式重新組織了一下。

2.2 準備工作

首先在寫核心代碼之前,先做好準備工作。
import 所有需要的模塊:

import os
import json
import copy
from tqdm.notebook import tqdm

import torch
from torch.optim import AdamW
from torch.utils.data import DataLoader, Dataset
from transformers import BertForMaskedLM, BertTokenizerFast

然后寫一個config類,將所有參數集中起來:

class Config:
    def __init__(self):
        pass
    
    def mlm_config(
        self, 
        mlm_probability=0.15, 
        special_tokens_mask=None,
        prob_replace_mask=0.8,
        prob_replace_rand=0.1,
        prob_keep_ori=0.1,
    ):
        """
        :param mlm_probability: 被mask的token總數
        :param special_token_mask: 特殊token
        :param prob_replace_mask: 被替換成[MASK]的token比率
        :param prob_replace_rand: 被隨機替換成其他token比率
        :param prob_keep_ori: 保留原token的比率
        """
        assert sum([prob_replace_mask, prob_replace_rand, prob_keep_ori]) == 1,                 ValueError("Sum of the probs must equal to 1.")
        self.mlm_probability = mlm_probability
        self.special_tokens_mask = special_tokens_mask
        self.prob_replace_mask = prob_replace_mask
        self.prob_replace_rand = prob_replace_rand
        self.prob_keep_ori = prob_keep_ori
        
    def training_config(
        self,
        batch_size,
        epochs,
        learning_rate,
        weight_decay,
        device,
    ):
        self.batch_size = batch_size
        self.epochs = epochs
        self.learning_rate = learning_rate
        self.weight_decay = weight_decay
        self.device = device
        
    def io_config(
        self,
        from_path,
        save_path,
    ):
        self.from_path = from_path
        self.save_path = save_path

接著就是設置各種配置:

config = Config()
config.mlm_config()
config.training_config(batch_size=4, epochs=10, learning_rate=1e-5, weight_decay=0, device='cuda:0')
config.io_config(from_path='/data/BERTmodels/huggingface/chinese_wwm/', 
                 save_path='./finetune_embedding_model/mlm/')

最后創建BERT模型。注意,這里的 tokenizer 就是一個普通的 tokenizer,而BERT模型則是帶了下游任務的 BertForMaskedLM,它是 transformers 中寫好的一個類,

bert_tokenizer = BertTokenizerFast.from_pretrained(config.from_path)
bert_mlm_model = BertForMaskedLM.from_pretrained(config.from_path)

2.3 數據集

因為舍棄了datasets這個包,所以我們現在需要自己實現數據的輸入了。方案就是使用 torchDataset 類。這個類一般在構建 DataLoader 的時候,會與一個聚合函數一起使用,以實現對batch的組織。而我這里偷個懶,就沒有寫聚合函數,batch的組織方法放在dataset中進行。

在這個類中,有一個 mask tokens 的方法,作用是從數據中選擇出所有需要mask 的token,并且采用三種mask方式中的一個。這個方法是從transformers 中拿出來的,將其從類方法轉為靜態方法測試之后,再將其放在自己的這個類中為我們所用。仔細閱讀這一段代碼,也就可以回答1.2.2 中提出的那個問題了。

取batch的原理很簡單,一開始我們將原始數據deepcopy備份一下,然后每次從中截取一個batch的大小,這個時候的當前數據就少了一個batch,我們定義這個類的長度為當前長度除以batch size向下取整,所以當類的長度變為0的時候,就說明這一個epoch的所有step都已經執行結束,要進行下一個epoch的訓練,此時,再將當前數據變為原始數據,就可以實現對epoch的循環了。

class TrainDataset(Dataset):
    """
    注意:由于沒有使用data_collator,batch放在dataset里邊做,
    因而在dataloader出來的結果會多套一層batch維度,傳入模型時注意squeeze掉
    """
    def __init__(self, input_texts, tokenizer, config):
        self.input_texts = input_texts
        self.tokenizer = tokenizer
        self.config = config
        self.ori_inputs = copy.deepcopy(input_texts)
        
    def __len__(self):
        return len(self.input_texts) // self.config.batch_size
    
    def __getitem__(self, idx):
        batch_text = self.input_texts[: self.config.batch_size]
        features = self.tokenizer(batch_text, max_length=512, truncation=True, padding=True, return_tensors='pt')
        inputs, labels = self.mask_tokens(features['input_ids'])
        batch = {"inputs": inputs, "labels": labels}
        self.input_texts = self.input_texts[self.config.batch_size: ]
        if not len(self):
            self.input_texts = self.ori_inputs
        
        return batch
        
    def mask_tokens(self, inputs):
        """
        Prepare masked tokens inputs/labels for masked language modeling: 80% MASK, 10% random, 10% original.
        """
        labels = inputs.clone()
        # We sample a few tokens in each sequence for MLM training (with probability `self.mlm_probability`)
        probability_matrix = torch.full(labels.shape, self.config.mlm_probability)
        if self.config.special_tokens_mask is None:
            special_tokens_mask = [
                self.tokenizer.get_special_tokens_mask(val, already_has_special_tokens=True) for val in labels.tolist()
            ]
            special_tokens_mask = torch.tensor(special_tokens_mask, dtype=torch.bool)
        else:
            special_tokens_mask = self.config.special_tokens_mask.bool()

        probability_matrix.masked_fill_(special_tokens_mask, value=0.0)
        masked_indices = torch.bernoulli(probability_matrix).bool()
        labels[~masked_indices] = -100  # We only compute loss on masked tokens

        # 80% of the time, we replace masked input tokens with tokenizer.mask_token ([MASK])
        indices_replaced = torch.bernoulli(torch.full(labels.shape, self.config.prob_replace_mask)).bool() & masked_indices
        inputs[indices_replaced] = self.tokenizer.convert_tokens_to_ids(self.tokenizer.mask_token)

        # 10% of the time, we replace masked input tokens with random word
        current_prob = self.config.prob_replace_rand / (1 - self.config.prob_replace_mask)
        indices_random = torch.bernoulli(torch.full(labels.shape, current_prob)).bool() & masked_indices & ~indices_replaced
        random_words = torch.randint(len(self.tokenizer), labels.shape, dtype=torch.long)
        inputs[indices_random] = random_words[indices_random]

        # The rest of the time (10% of the time) we keep the masked input tokens unchanged
        return inputs, labels

然后取一些用于訓練的語料,格式很簡單,就是把所有文本放在一個list里邊,注意長度不要超過512個token,不然多出來的部分就浪費掉了。可以做適當的預處理。

[
"這是一條文本",
"這是另一條文本",
...,
]

然后構建dataloader:

train_dataset = TrainDataset(training_texts, bert_tokenizer, config)
train_dataloader = DataLoader(train_dataset)

2.4 訓練

構建一個訓練方法,輸入參數分別是我們實例化好的待訓練模型,數據集,還有config:

def train(model, train_dataloader, config):
    """
    訓練
    :param model: nn.Module
    :param train_dataloader: DataLoader
    :param config: Config
    ---------------
    ver: 2021-11-08
    by: changhongyu
    """
    assert config.device.startswith('cuda') or config.device == 'cpu', ValueError("Invalid device.")
    device = torch.device(config.device)
    
    model.to(device)
    
    if not len(train_dataloader):
        raise EOFError("Empty train_dataloader.")
        
    param_optimizer = list(model.named_parameters())
    no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]
    optimizer_grouped_parameters = [
        {"params": [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], "weight_decay": 0.01},
        {"params": [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], "weight_decay": 0.0}]
    
    optimizer = AdamW(params=optimizer_grouped_parameters, lr=config.learning_rate, weight_decay=config.weight_decay)
    
    for cur_epc in tqdm(range(int(config.epochs)), desc="Epoch"):
        training_loss = 0
        print("Epoch: {}".format(cur_epc+1))
        model.train()
        for step, batch in enumerate(tqdm(train_dataloader, desc='Step')):
            input_ids = batch['inputs'].squeeze(0).to(device)
            labels = batch['labels'].squeeze(0).to(device)
            loss = model(input_ids=input_ids, labels=labels).loss
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            model.zero_grad()
            training_loss += loss.item()
        print("Training loss: ", training_loss)

調用它訓練幾輪:

train(model=bert_mlm_model, train_dataloader=train_dataloader, config=config)

2.5 保存和加載

使用過預訓練模型的同學應該都了解,普通的bert有兩項輸出,分別是:

  • 每一個token對應的768維編碼結果;
  • 以及用于表征整個句子的句子特征。

其中,這個句子特征是由模型中的一個 Pooler 模塊對原句池化得來的。可是這個Pooler的訓練,并不是由 MLM 任務來的,而是由 NSP任務中來的。

由于沒有 NSP 任務,所以無法對 Pooler 進行訓練,故而沒有必要在模型中加入 Pooler。所以在保存的時候需要分別保存 embedding和 encoder, 加載的時候也需要分別讀取 embedding 和 encoder,這樣訓練出來的模型拿不到 CLS 層的句子表征。如果需要的話,可以手動pooling 。

torch.save(bert_mlm_model.bert.embeddings.state_dict(), os.path.join(config.save_path, 'bert_mlm_ep_{}_eb.bin'.format(config.epochs)))
torch.save(bert_mlm_model.bert.encoder.state_dict(), os.path.join(config.save_path, 'bert_mlm_ep_{}_ec.bin'.format(config.epochs)))

加載的話,也是實例化完bert模型之后,用bert的 embedding 組件和 encoder 組件分別讀取這兩個權重文件即可。

到這里,本期內容就全部結束了,希望看完這篇博客的同學,能夠對 Bert 的基礎原理有更深入的了解。

審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 模型
    +關注

    關注

    1

    文章

    3298

    瀏覽量

    49112
  • 語言模型
    +關注

    關注

    0

    文章

    538

    瀏覽量

    10320
  • mask
    +關注

    關注

    0

    文章

    10

    瀏覽量

    2925

原文標題:2. 代碼部分

文章出處:【微信號:zenRRan,微信公眾號:深度學習自然語言處理】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    何進行電源供應設計 – 第 4 部分

    電子發燒友網站提供《如何進行電源供應設計 – 第 4 部分.pdf》資料免費下載
    發表于 09-09 10:34 ?0次下載
    如<b class='flag-5'>何進行</b>電源供應設計 – 第 4 部分

    何進行電源供應設計

    電子發燒友網站提供《如何進行電源供應設計.pdf》資料免費下載
    發表于 09-09 10:33 ?0次下載
    如<b class='flag-5'>何進行</b>電源供應設計

    何進行電源設計–第5部分

    電子發燒友網站提供《如何進行電源設計–第5部分.pdf》資料免費下載
    發表于 09-07 11:11 ?0次下載
    如<b class='flag-5'>何進行</b>電源設計–第5部分

    何進行電源設計-第1部分

    電子發燒友網站提供《如何進行電源設計-第1部分.pdf》資料免費下載
    發表于 09-07 11:10 ?0次下載
    如<b class='flag-5'>何進行</b>電源設計-第1部分

    何進行電源設計–第2部分

    電子發燒友網站提供《如何進行電源設計–第2部分.pdf》資料免費下載
    發表于 09-07 11:09 ?0次下載
    如<b class='flag-5'>何進行</b>電源設計–第2部分

    何進行電源設計–第3部分

    電子發燒友網站提供《如何進行電源設計–第3部分.pdf》資料免費下載
    發表于 09-07 11:08 ?0次下載
    如<b class='flag-5'>何進行</b>電源設計–第3部分

    何進行電源設計–第6部分

    電子發燒友網站提供《如何進行電源設計–第6部分.pdf》資料免費下載
    發表于 09-06 15:05 ?0次下載
    如<b class='flag-5'>何進行</b>電源設計–第6部分

    何進行電源設計–第4部分

    電子發燒友網站提供《如何進行電源設計–第4部分.pdf》資料免費下載
    發表于 09-06 15:04 ?0次下載
    如<b class='flag-5'>何進行</b>電源設計–第4部分

    何進行電源供應設計-第3部分

    電子發燒友網站提供《如何進行電源供應設計-第3部分.pdf》資料免費下載
    發表于 08-30 09:16 ?0次下載
    如<b class='flag-5'>何進行</b>電源供應設計-第3部分

    請問PGA308能溫度補償嗎?如何進行呢?

    請問PGA308能溫度補償嗎?如何進行呢?
    發表于 08-22 08:25

    何進行IP檢測

    排查網絡連接問題,并及時的防范潛在的網絡攻擊。 那么,如何進行 IP 地址檢測呢?接下來我將進行圖示哦~ 使用操作系統自帶的工具 ① Windows 系統中,按win+R,輸入“ipconfig”命令。 ② Mac 系統中,則可以在“系統偏好設置”中的“網絡”
    的頭像 發表于 07-26 14:09 ?643次閱讀
    如<b class='flag-5'>何進行</b>IP檢測

    請問IR615如何進行端口映射?

    IR615 如何進行端口映射
    發表于 07-24 07:53

    如何利用Matlab進行神經網絡訓練

    ,使得神經網絡的創建、訓練和仿真變得更加便捷。本文將詳細介紹如何利用Matlab進行神經網絡訓練,包括網絡創建、數據預處理、訓練過程、參數調整以及仿真預測等步驟。
    的頭像 發表于 07-08 18:26 ?2023次閱讀

    何進行RF PA Ruggedness的測試和評估呢?

    關于PA ruggedness設計測試問題,先介紹一下原理,如何進行ruggedness的測試和評估。
    的頭像 發表于 03-27 10:19 ?2755次閱讀
    如<b class='flag-5'>何進行</b>RF PA Ruggedness的測試和評估呢?

    TC375如何進行校準?

    初次使用 TC375,在使用 EVADC時,當輸入電壓是 0 時,轉換結果是 16,應該是沒有校準原因。 請問如何進行校準。
    發表于 01-31 06:49
    主站蜘蛛池模板: 最新在线网址 | 国产四虎| 激情五月激情综合网 | 久久精品成人免费网站 | 中文字幕在线天堂 | 天天操精品视频 | 免费看黄色录像 | 色视频免费在线观看 | 国产高清色视频免费看的网址 | 黄a级免费 | 久久大香线蕉综合爱 | 永久免费看mv网站入口 | 五月婷婷激情综合 | 大色综合色综合资源站 | 免费无遮挡很爽很污很黄 | 天堂中文字幕 | 五月婷婷天 | 一级做a爱片特黄在线观看免费看 | 亚洲国内精品久久 | 午夜欧美性欧美 | 男人操女人在线观看 | 在线黄视频 | 国产色婷婷精品免费视频 | 性夜影院爽黄a爽免费看网站 | 免费网站啪啪大全 | 91大神免费视频 | 91人成网站色www免费 | 日韩毛片免费在线观看 | 激情五月激情综合色区 | 99视频网站| 中文字幕一区二区三区在线观看 | 久久精品伊人波多野结 | 天天做天天添婷婷我也去 | 大又大粗又爽又黄少妇毛片 | 特级黄视频 | 国产日本在线观看 | 五月激情网站 | tube性欧美另类 | 1024人成网站色 | 高清视频黄色录像免费 | 国产美女视频黄a视频免费全过程 |