机器学习NLP领域入门
學習路徑:自然語言處理怎么最快入門? - 知乎
Ⅰ. 統計機器學習
一、HMM隱含馬爾科夫模型
在NLP領域, HMM用來解決文本序列標注問題. 如分詞, 詞性標注, 命名實體識別都可以看作是序列標注問題。一般以文本序列數據為輸入, 以該序列對應的隱含序列為輸出。HMM屬于生成模型,是有向圖。
1. HMM的基本定義: HMM是用于描述由隱藏的狀態序列和顯性的觀測序列組合而成的雙重隨機過程
2. HMM的假設
- 馬爾科夫性假設。當前時刻的狀態值,僅依賴于前一時刻的狀態值,而不依賴于更早時刻的狀態值。(“無記憶性”)
- 齊次性假設。狀態轉移概率矩陣與時間無關。即所有時刻共享同一個狀態轉移矩陣。
- 觀測獨立性假設。當前時刻的觀察值,僅依賴于當前時刻的狀態值。
3. HMM的應用目的:通過可觀測到的數據,預測不可觀測到的數據。標注任務中,狀態值對應著標記,任務會給定觀測序列,以預測其對應的標記序列。
4. 使用HMM的三個問題:
- 概率計算問題:給定模型參數和觀測序列,計算該觀測序列的概率。是后面兩個問題的基礎。
HMM三個參數:A:狀態轉移概率矩陣。表征轉移概率,維度為N*N。B:觀測概率矩陣。表征發射概率,維度為N*M。π:初始狀態概率向量。維度為N*1。
- 學習訓練問題:給定觀測序列,估計模型參數。
HMM兩種學習方式:如果訓練數據包含觀測序列和狀態序列則為有監督(通過統計方法,求得模型的狀態轉移概率矩陣A、觀測概率矩陣B、初始狀態概率向量π),
如果訓練數據只包含觀測序列則為無監督(模型使用Baum-Welch算法來求得模型參數)
- 解碼預測問題:給定模型參數和觀測序列,求概率最大的狀態序列。(維特比算法)
二、CRF模型
CRF(Conditional Random Fields), 中文稱作條件隨機場, 同HMM一樣, 它一般也以文本序列數據為輸入, 以該序列對應的隱含序列為輸出。CRF用來解決文本序列標注問題. 如分詞, 詞性標注, 命名實體識別.
- (1)CRF是判別模型,是黑箱模型,不關心概率分布情況,只關心輸出結果。
- (2)CRF最重要的工作,是提取特征,構建特征函數。
- (3)CRF使用特征函數給不同的標注網絡打分,根據分數選出可能性最高的標注網絡。
- (4)CRF模型的計算過程,使用的是以e為底的指數。這個建模思路和深度學習輸出層的softmax是一致的。先計算各個可能情況的分數,再進行softmax歸一化操作。
學習訓練問題:CRF模型采用正則化的極大似然估計最大化概率。
預測解碼問題:和HMM完全一樣,采用維特比算法進行預測解碼
HMM與CRF模型之間差異
- HMM模型存在隱馬假設, 而CRF不存在, 因此HMM的計算速度要比CRF模型快很多, 適用于對預測性能要求較高的場合.
- 同樣因為隱馬假設, 當預測問題中隱含序列單元并不是只與上一個單元有關時, HMM的準確率會大大降低, 而CRF不受這樣限制, 準確率明顯高于HMM.
- CRF能夠獲取長文本的遠距離依賴的信息,并規避了齊次性,模型能夠獲取序列的位置信息,并且序列的位置信息會影響預測出的狀態序列。
命名實體識別任務要在特征編碼層(如RNN、CNN、BERT等 )后接CRF:
雙向的LSTM后面接softmax,但此時輸出標簽之間是沒有關系的,加了CRF后,可以建立起輸出標簽之間的關聯關系。
Ⅱ. HuggingFace實戰
多種多樣的NLP模型
BERT:處理各式NLP 任務的通用架構
NLP領域非常流行的兩階段遷移學習:
- 先以LM Pretraining 的方式預先訓練出一個對自然語言有一定「理解」的通用模型
- 再將該模型拿來做特征擷取或是fine tune 下游的(監督式)任務
預訓練BERT時讓它同時進行兩個任務:
- MLM:漏字填空(Masked Language Model)
- NSP:判斷第2個句子在原始文本中是否跟第1個句子相接(Next Sentence Prediction, NSP)
BERT中的文本處理
BERT使用當初Google NMT提出的WordPiece Tokenization,將本來的words拆成更小粒度的wordpieces,有效處理不在字典里頭的詞匯(OOV)。中文的話大致上就像是character-level tokenization,而有##前綴的tokens即為wordpieces。
以詞匯fragment來說,其可以被拆成frag與##ment兩個pieces,而一個word也可以獨自形成一個wordpiece。wordpieces可以由搜集大量文本并找出其中常見的pattern取得。
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')text = "[CLS] 等到潮水 [MASK] 了,就知道誰沒穿褲子。" tokens = tokenizer.tokenize(text) #中文斷句 ids = tokenizer.convert_tokens_to_ids(tokens) #轉換為文本id除了一般的wordpieces 以外,BERT 里頭有5 個特殊tokens 各司其職:
- [CLS]:在做分類任務時其最后一層的repr. 會被視為整個輸入序列的repr.
- [SEP]:有兩個句子的文本會被串接成一個輸入序列,并在兩句之間插入這個token 以做區隔
- [UNK]:沒出現在BERT 字典里頭的字會被這個 token 取代
- [PAD]:zero padding 遮罩,將長度不一的輸入序列補齊方便做batch 運算
- [MASK]:未知遮罩,僅在預訓練階段會用到
- 如上例所示,[CLS]一般會被放在輸入序列的最前面,而zero padding在之前的Transformer文章里已經有非常詳細的介紹。[MASK]token一般在fine-tuning或是feature extraction時不會用到
代碼實例
transformers庫設計:
- Tokenizer 類支持從預訓練模型中進行加載或者直接手動配置
- transformers 提供 一系列的Auto classes,使得快速進行模型切換非常方便。
- Head 不同于attention的head,這邊的 head 指的是下游任務的輸出層,它將模型的contextual embedding 轉化為特定任務的預測值
- Pretraining Head
- Casual Language Modeling(普通自回歸的語言模型):GPT, GPT-2,CTRL
- Masked Language Modeling(掩碼語言模型):BERT, RoBERTa
- Permuted Language Modeling(亂序重排語言模型):XLNet
- Fine-tuning Head
- Language Modeling:語言模型訓練,預測下一個詞。主要用于文本生成
- Sequence Classification:文本分類任務,情感分析任務
- Question Answering:機器閱讀理解任務,QA
- Token Classification:token級別的分類,主要用于命名實體識別(NER)任務,句法解析Tagging任務
- Multiple Choice:多選任務,主要是文本選擇任務
- Masked LM:掩碼預測,隨機mask一個token,預測該 token 是什么詞,用于預訓練
- Conditional Generation:條件生成任務,主要用于翻譯以及摘要任務。
encode和encode_plus和tokenizer的區別
Tokenizer 返回的是一個字典,里面的列表包含了int類別的數據。
encoded_input = tokenizer("Hello, I'm a single sentence!") print(encoded_input) # {'input_ids': [101, 138, 18696, 155, 1942, 3190, 1144, 1572, 13745, 1104, 159, 9664, 2107, 102], # 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}- “input_id”是對應于文本序列中每個token的索引(在vocab中的索引);
- “token_type_ids”是對應于不同的文本序列,例如在NSP(BERT及某些語言模型中的“Next Sentence Prediction”)任務中需要輸入兩個文本序列。
- “attention_mask”是對應于注意力機制的計算,各元素的值為0或1,如果當前token被mask或者是只是用來作為填充的元素,那么其不需要進行注意力機制的計算,其值為0;
tokenizer解碼:
tokenizer.decode(encoded_input["input_ids"]) # "[CLS] Hello, I'm a single sentence! [SEP]"[CLS]字符就是大多數預訓練語言模型會自動加入的特殊token。tokenizer會自動添加了模型期望的一些特殊token。可以通過傳遞add_special_tokens = False來禁用加入特殊token
tokenizer也可輸入列表:
batch_sentences = ["Hello I'm a single sentence","And another sentence","And the very very last one"] encoded_inputs = tokenizer(batch_sentences) print(encoded_inputs) # {'input_ids': [[101, 8667, 146, 112, 182, 170, 1423, 5650, 102], # [101, 1262, 1330, 5650, 102], # [101, 1262, 1103, 1304, 1304, 1314, 1141, 102]], # 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0], # [0, 0, 0, 0, 0], # [0, 0, 0, 0, 0, 0, 0, 0]], # 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1], # [1, 1, 1, 1, 1], # [1, 1, 1, 1, 1, 1, 1, 1]]}填充(padding)和截斷(truncation)
一個batch中的序列長度不一致時,
三個參數padding,truncation和max_length
encode和encode_plus的區別
1. encode僅返回input_ids
2. encode_plus返回所有的編碼信息,具體如下:
’input_ids:是單詞在詞典中的編碼
‘token_type_ids’:區分兩個句子的編碼(上句全為0,下句全為1)
‘attention_mask’:指定對哪些詞進行self-Attention操作
學習率預熱
from transformers import get_cosine_schedule_with_warmup,get_linear_schedule_with_warmupsch = get_linear_schedule_with_warmup(optimizer, #優化器 包含lr參數num_warmup_steps= 0, #預熱階段的步驟數num_training_steps, #訓練的總步驟數last_epoch=-1 #恢復訓練時最后一個epoch的索引 )Ⅲ. NLP對抗訓練
對抗訓練有兩個作用,一是提高模型對惡意攻擊的魯棒性,二是提高模型的泛化能力。
對抗訓練旨在對原始輸入樣本 x 上施加擾動 r_adv?,得到對抗樣本后用其進行訓練:
- 最大化擾動:挑選一個能使得模型產生更大損失(梯度較大)的擾動量,作為攻擊;
- 最小化損失:根據最大的擾動量,添加到輸入樣本后,朝著最小化含有擾動的損失(梯度下降)方向更新參數;
這個被構造出來的“對抗樣本”并不能具體對應到某個單詞,因此,反過來在推理階段是沒有辦法通過修改原始輸入得到這樣的對抗樣本。
常用的幾種對抗訓練方法有FGSM、FGM、PGD、FreeAT、YOPO、FreeLB、SMART。
一、FGM算法
- 首先計算輸入樣本x(通常為word embedding)的損失函數以及在x 處的梯度:
- 計算在輸入樣本的擾動量: ,其中 ? 為超參數,默認取1.0;
- 得到對抗樣本:
- 根據得到的對抗樣本,再次喂入模型中,計算損失,并累積梯度;
- 恢復原始的word embedding,接著下一個batch。
訓練的時候再添加五行:
fgm = FGM(model) # (#1)初始化 for batch_input, batch_label in data:loss = model(batch_input, batch_label) # 正常訓練loss.backward() # 反向傳播,得到正常的grad# 對抗訓練fgm.attack() # (#2)在embedding上添加對抗擾動loss_adv = model(batch_input, batch_label) # (#3)計算含有擾動的對抗樣本的lossloss_adv.backward() # (#4)反向傳播,并在正常的grad基礎上,累加對抗訓練的梯度fgm.restore() # (#5)恢復embedding參數# 梯度下降,更新參數optimizer.step()model.zero_grad()二、PGD算法
Project Gradient Descent(PGD)是一種迭代攻擊算法,相比于普通的FGM 僅做一次迭代,PGD是做多次迭代,每次走一小步,每次迭代都會將擾動投射到規定范圍內。形式化描述為:
擾動約束空間為一個球體,原始的輸入樣本對應的初識點為球心,迭代多次后,避免擾動超過球面。
import torch class PGD():def __init__(self, model):self.model = modelself.emb_backup = {}self.grad_backup = {}def attack(self, epsilon=1., alpha=0.3, emb_name='word_embeddings', is_first_attack=False):for name, param in self.model.named_parameters():if param.requires_grad and emb_name in name:if is_first_attack:self.emb_backup[name] = param.data.clone()norm = torch.norm(param.grad)if norm != 0 and not torch.isnan(norm):r_at = alpha * param.grad / normparam.data.add_(r_at)param.data = self.project(name, param.data, epsilon)def restore(self, emb_name='word_embeddings'):for name, param in self.model.named_parameters():if param.requires_grad and emb_name in name: assert name in self.emb_backupparam.data = self.emb_backup[name]self.emb_backup = {}def project(self, param_name, param_data, epsilon):r = param_data - self.emb_backup[param_name]if torch.norm(r) > epsilon:r = epsilon * r / torch.norm(r)return self.emb_backup[param_name] + rdef backup_grad(self):for name, param in self.model.named_parameters():if param.requires_grad:self.grad_backup[name] = param.grad.clone()def restore_grad(self):for name, param in self.model.named_parameters():if param.requires_grad:param.grad = self.grad_backup[name]訓練的時候添加:
pgd = PGD(model) K = 3 for batch_input, batch_label in data:# 正常訓練loss = model(batch_input, batch_label)loss.backward() # 反向傳播,得到正常的gradpgd.backup_grad()# 累積多次對抗訓練——每次生成對抗樣本后,進行一次對抗訓練,并不斷累積梯度for t in range(K):pgd.attack(is_first_attack=(t==0)) # 在embedding上添加對抗擾動, first attack時備份param.dataif t != K-1:model.zero_grad()else:pgd.restore_grad()loss_adv = model(batch_input, batch_label)loss_adv.backward() # 反向傳播,并在正常的grad基礎上,累加對抗訓練的梯度pgd.restore() # 恢復embedding參數# 梯度下降,更新參數optimizer.step()model.zero_grad()三、VAT虛擬對抗訓練
抽取一個隨機標準正態擾動,加到embedding上,并用KL散度計算擾動的梯度,然后用得到的梯度,計算對抗擾動,并進行對抗訓練,實現方法跟FGM差不多。特別提到的一點是,因為其思路也有額外的一致性損失的loss,因此可以用于半監督學習,在無監督數據集合上計算一致性的loss。
Ⅳ. NLP比賽提分技巧
1. EMA指數移動平均
EMA在深度學習的優化過程中,是t時刻的模型權重weights,是t時刻的影子權重(shadow weights)。在梯度下降的過程中,會一直維護著這個影子權重,但是這個影子權重并不會參與訓練。基本的假設是,模型權重在最后的n步內,會在實際的最優點處抖動,所以我們取最后n步的平均,能使得模型更加的魯棒。
EMA對第i步的梯度下降的步長增加了權重系數 ,相當于做了一個learning rate decay。
在保存模型或者評估模型時,會利用影子權重進行評估,如果效果比當前效果好,則保存影子權重的參數,但是之后在繼續訓練的時候會還原之前的參數進行訓練。
class EMA():def __init__(self, model, decay):self.model = modelself.decay = decayself.shadow = {}self.backup = {}def register(self):for name, param in self.model.named_parameters():if param.requires_grad:self.shadow[name] = param.data.clone()def update(self):for name, param in self.model.named_parameters():if param.requires_grad:assert name in self.shadownew_average = (1.0 - self.decay) * param.data + self.decay * self.shadow[name]self.shadow[name] = new_average.clone()def apply_shadow(self):for name, param in self.model.named_parameters():if param.requires_grad:assert name in self.shadowself.backup[name] = param.dataparam.data = self.shadow[name]def restore(self):for name, param in self.model.named_parameters():if param.requires_grad:assert name in self.backupparam.data = self.backup[name]self.backup = {}# 初始化 ema = EMA(model, 0.999) ema.register()# 訓練過程中,更新完參數后,同步update shadow weights def train():optimizer.step()ema.update()# eval前,apply shadow weights;eval之后,恢復原來模型的參數 def evaluate():ema.apply_shadow()# evaluateema.restore()2. UDPLoss, RDropLoss
在正常的監督學習損失外,增加一個一致性損失((一般是kl散度)),也有多drop層這種
Ⅴ. 實戰匯總
Kaggle 專利匹配比賽賽后總結
雜文:NLP競賽&競賽tricks整理 - 知乎
Ⅵ. 提分策略
新手入門 Kaggle NLP類比賽總結 - 知乎
SWA, Apex AMP & Interpreting Transformers in Torch | Kaggle
Utilizing Transformer Representations Efficiently | Kaggle
NLP比賽中有哪些常用的Trick? - 墨天輪
1.文本特征token:長度(Seq Length)限制進行截斷
- pre-truncate
- post-truncate
- middle-truncate (head + tail)
?2.增加特征(附加信息)
- Bert 輸入端,添加 special tokens(如類別信息 [CAT=CULTURE])
- Bert 輸出端,直接做 embedding,?然后與文本特征的 Vector Representation 進行融合
3. Pseudo-labeling
1. 訓練集上訓練得到 model1; 2. 使用 model1 在測試集上做預測得到有偽標簽的測試集; 3. 使用訓練集+帶偽標簽的測試集訓練得最終模型 model2;偽標簽數據可以作為訓練數據而被加入到訓練集中,是因為神經網絡模型有一定的容錯能力。需要注意的是偽標簽數據質量可能會很差,在使用過程中要多加小心,比如不要用在 validation set 中。
4. pooling技巧
Transformers?三種輸出形式:
- pooler output?(batch size, hidden size) : 句嵌入,即CLS token的embedding
- last hidden state?(batch size, seq Len, hidden size) 最后隱藏層
- hidden states?(n layers, batch size, seq Len, hidden size) - 所有的隱藏層
Weighted Layer Pooling
5.?隨機權重平均(SWA)
SWA通過對訓練過程中多個時間點的模型權重(checkpoint)求平均達到集成的效果,并且該方法不會為訓練增加額外的消耗,也不會增加計算量,同時該方法還可以嵌入到Pytorch中的任何優化器類中。
Stochastic Weight Averaging in PyTorch | PyTorch?(即插即用?)
6.?Multi-Sample Dropout
可以看做是對傳統dropout的一種改進,同一樣本經過多次dropout, 由于dropout具有隨機性,可以得到多個不同的樣本。基于連續的dropout,加快模型收斂,提升泛化能力。
7. 分層學習率
def get_grouped_params(args, model):no_decay = ["bias", "LayerNorm.weight"]group1 = ['layer.0.', 'layer.1.', 'layer.2.', 'layer.3.']group2 = ['layer.4.', 'layer.5.', 'layer.6.', 'layer.7.']group3 = ['layer.8.', 'layer.9.', 'layer.10.', 'layer.11.']group_all = ['layer.0.', 'layer.1.', 'layer.2.', 'layer.3.', 'layer.4.', 'layer.5.', 'layer.6.', 'layer.7.','layer.8.', 'layer.9.', 'layer.10.', 'layer.11.']optimizer_grouped_parameters = [{'params': [p for n, p in model.bert_named_params() if not any(nd in n for nd in no_decay) and not any(nd in n for nd in group_all)],'weight_decay': args.weight_decay},{'params': [p for n, p in model.bert_named_params() if not any(nd in n for nd in no_decay) and any(nd in n for nd in group1)],'weight_decay': args.weight_decay, 'lr': args.bert_lr 2},{'params': [p for n, p in model.bert_named_params() ifnot any(nd in n for nd in no_decay) and any(nd in n for nd in group2)],'weight_decay': args.weight_decay, 'lr': args.bert_lr},{'params': [p for n, p in model.bert_named_params() ifnot any(nd in n for nd in no_decay) and any(nd in n for nd in group3)],'weight_decay': args.weight_decay, 'lr': args.bert_lr * 2},{'params': [p for n, p in model.bert_named_params() ifany(nd in n for nd in no_decay) and not any(nd in n for nd in group_all)], 'weight_decay': 0.0},{'params': [p for n, p in model.bert_named_params() ifany(nd in n for nd in no_decay) and any(nd in n for nd in group1)], 'weight_decay': 0.0,'lr': args.bert_lr 2},{'params': [p for n, p in model.bert_named_params() ifany(nd in n for nd in no_decay) and any(nd in n for nd in group2)], 'weight_decay': 0.0,'lr': args.bert_lr},{'params': [p for n, p in model.bert_named_params() ifany(nd in n for nd in no_decay) and any(nd in n for nd in group3)], 'weight_decay': 0.0,'lr': args.bert_lr * 2},{'params': [p for n, p in model.base_named_params()], 'lr': args.learning_rate, "weight_decay": args.weight_decay}]return optimizer_grouped_parametersoptimizer_bert_parameters = get_grouped_params(args, model) optimizer = torch.optim.AdamW(optimizer_bert_parameters, lr=args.bert_lr, eps=args.eps)Ⅶ.模型加速
Transformers 優化方法匯總 - 知乎
- 梯度累加(Gradient Accumulation):調整更新步長,模擬大批量
- 自動混合精度(AMP):梯度縮放
推薦閱讀 / Ref
NLP硬核入門-隱馬爾科夫模型HMM - 知乎
NLP幾種常用的對抗訓練方法_華師數據學院·王嘉寧的博客-CSDN博客_nlp對抗訓練
NLP煉丹技巧合集
總結
以上是生活随笔為你收集整理的机器学习NLP领域入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab信号频率分析实验报告,信号抽
- 下一篇: matlab读取正则找科学,MATLAB