語義匹配(二)搜狐文本匹配大賽BaseLine比較:P-tuning和Conditional_LN實現多任務文本匹配
- 比賽簡介
- 即插即用的多任務模塊
- P-tuning
- Conditional Layer Normalization
- Conditional Layer Normalization多任務分類實現
- 實驗結果
- 總結
比賽簡介
關鍵詞:文本匹配、多任務匹配與我們接觸的文本語意匹配任務相似:給定兩個句子,判斷兩個句子語義是否相似。本次比賽在原本的語義匹配的基礎上將語義是否相似分成了【主題】一致判斷、【事件】一致判斷,并針對這兩種判斷標準給出了短文-短文、短文-長文、長文-長文匹配三種類型的語料。
數據樣例
{"source": "英國倫敦,20/21賽季英超第20輪,托特納姆熱刺VS利物浦。熱刺本賽季18輪聯賽是9勝6平3負,目前積33分排名聯賽第5位。利物浦本賽季19輪聯賽是9勝7平3負,目前積34分排名聯賽第4位。從目前的走勢來看,本場比賽從熱刺的角度來講,是非常被動的。最終,本場比賽的比分為托特納姆熱刺1-3利","target": " 北京時間1月29日凌晨4時,英超聯賽第20輪迎來一場強強對話,熱刺坐鎮主場迎戰利物浦。 熱刺vs利物浦,比賽看點如下: 第一:熱刺能否成功復仇?雙方首回合,熱刺客場1-2被利物浦絕殺,賽后穆里尼奧稱最好的球隊輸了,本輪熱刺主場迎戰利物浦,借著紅軍5輪不勝的低迷狀態,能否成功復仇? 第二:利物浦近","labelA": "1"
}
{"source": "英國倫敦,20/21賽季英超第20輪,托特納姆熱刺VS利物浦。熱刺本賽季18輪聯賽是9勝6平3負,目前積33分排名聯賽第5位。利物浦本賽季19輪聯賽是9勝7平3負,目前積34分排名聯賽第4位。從目前的走勢來看,本場比賽從熱刺的角度來講,是非常被動的。最終,本場比賽的比分為托特納姆熱刺1-3利","target": " 北京時間1月29日凌晨4時,英超聯賽第20輪迎來一場強強對話,熱刺坐鎮主場迎戰利物浦。 熱刺vs利物浦,比賽看點如下: 第一:熱刺能否成功復仇?雙方首回合,熱刺客場1-2被利物浦絕殺,賽后穆里尼奧稱最好的球隊輸了,本輪熱刺主場迎戰利物浦,借著紅軍5輪不勝的低迷狀態,能否成功復仇? 第二:利物浦近","labelB": "0"
}
任務解讀
如蘇神在其博客中理解:一般來說,完成這個任務至少需要兩個模型,畢竟A、B兩種類型的分類標準是不一樣的,如果要做得更精細的話,應該還要做成6個模型。但問題是,如果獨立地訓練6個模型,那么往往比較費力,而且不同任務之間不能相互借鑒來提升效果,所以很自然地我們應該能想到共享一部分參數變成一個多任務學習問題。如果通過構建一個模型來提取文本語義,并將不同判斷條件甚至是不同長度匹配作為額外的信息加入到模型中去來控制模型的判斷標準。
即插即用的多任務模塊
本文意在探索目前比較流行的基于P-tuning的任務提示模塊,和蘇神在其博客中介紹的Conditional Layer Normalization,探究這兩個模塊在文本多任務分類中的指揮作用,并進行比較。WHY P-tuning/Conditional LN: 作為兩個可在任何語言模型中即插即用的輔助模塊,P-tuning 和 Conditional LN可以讓我們快速實現指揮模型并且可以完整利用預訓練模型權重,只需要經過簡單的微調即可。(這讓我想到了關于可控文本生成中PPLM的圖,我們的兩個模塊就像大象頭上的那只老鼠,牽引著模型向我們期望的方向前進)
P-tuning
相關論文鏈接:GPT Understands, Too
P-tuning,原本用于解決 傳統的人工提示(prompt) 來控制GPT的生成文本,無法隨著模型訓練進行獨立優化,而且依賴大量的人工設計和嘗試。P-tuning將傳統的人工提示Token替換為可訓練的獨立的Embedding,使其可以隨著模型訓練進行優化。這相當于讓模型在訓練的過程中自動計算最優的Prompt,從而在提高生產效果的同時減少了人工的實驗成本。對于BERT等MLM模型,我們可以利用其預訓練預測掩碼的任務特性,同樣達到一些zero-shot的效果。如下圖,我們通過設置“下面是MM新聞”作為人工提示,讓模型對文本生產文本的類別名稱,從而實現文本分類。
我們同樣可以使用P-tuning的方法,如下圖我們設置了u1-u6六個提示token,來替換上述的人工提示token,并在微調時同時對這6個token進行更新,從而最大化maxpromptP(x分類∣prompt,text)max_{prompt}P(x_{分類}|prompt,text)maxprompt?P(x分類?∣prompt,text)。
P-tuning多任務分類實現
根據不同的分類標準我們設置不同的prompt,目前開放的中文預訓練模型bert-base,bert-wwm等都提供了多個 [unused][unused][unused] 的備用token,這類token在模型預訓練時并未參與訓練,利用這類token我們可以根據不同的分類標準構建不同的任務前綴token
{
"短短匹配A類": [1, 2, 3, 4, 9, 9],
"短短匹配B類": [5, 6, 7, 8, 9, 9],
"短長匹配A類": [1, 2, 3, 4, 9, 10],
"短長匹配B類": [5, 6, 7, 8, 9, 10],
"長長匹配A類": [1, 2, 3, 4, 10, 10],
"長長匹配B類": [5, 6, 7, 8, 10, 10]A類匹配標準:
[1,2,3,4]
B類匹配標準:
[5,6,7,8]
短文:
[9]
長文:
[10]
}
chinese-bert-wwm-ext中token_id 1-99均為未使用的 [unused][unused][unused] token,在prompt并沒有唯一標準,此處思路為:不同的評判標準之間的差異較大,且對于模型擬合來說重要性更強,因此各占4個token,而文本長度信息本身就包含在input_legth的padding中,因此僅使用簡單的兩個token。剩余的與我們熟悉的句子對數據構建方法一致,模型的輸入為prompt[cls]sentence1[sep]sentence2[sep]prompt [cls] sentence1 [sep] sentence2 [sep]prompt[cls]sentence1[sep]sentence2[sep],最后通過[CLS]接全連接來輸出最后的得分,亦或是對所有token作avgpooling后接全連接。這兩種方法的效果本質上沒有太大差異,后者在操作時記得要mask掉[pad]token。P-tuning具有極高的便捷性,我們甚至不需要修改任何模型結構,只需要在輸入側增加相應的prompt即可。
Conditional Layer Normalization
Conditional Layer Normalization這一方法我們比較熟悉,在我之前的三元信息抽取中提到了通過Conditional Layer Normalization將主體的hidden state經過兩次線性變換與Layer Normalization的weight和bias相加,以此信息傳遞給下游去識別與改主體相關的客體,對該結構感興趣的朋友可以通過基于Conditional Layer Normalization的條件文本生成了解更多細節。在本文的任務中,我們需要使用Conditional Layer Normalization將多任務信息傳遞給模型即可,至于這個信息如何產生,我們只需要對每一種任務類型初始化一個embedding向量,隨著模型訓練一起更新即可。
Conditional Layer Normalization多任務分類實現
本人魔改了torch原生的Layer Normalization,并保存為一個新的類ConditionalLayerNorm,方便之后隨時調用,具體代碼如下,值得注意的是其中reset_parameters這一步比較關鍵,如果按torch的默認初始化方法初始化weight_dense和bias_dense,會讓模型一開始的訓練非常不穩定,我理解會是影響了預訓練模型的參數結構。
import torch
import numbers
from torch
.nn
.parameter
import Parameter
from torch
.nn
import Module
from torch
.nn
import init
from torch
import nn
from torch
import Tensor
, Size
from typing
import Union
, List_shape_t
= Union
[int, List
[int], Size
]class ConditionalLayerNorm(Module
):__constants__
= ['normalized_shape', 'eps', 'elementwise_affine']normalized_shape
: _shape_teps
: floatelementwise_affine
: booldef __init__(self
, normalized_shape
: _shape_t
, conditional_size
: int ,eps
: float = 1e-5) -> None:super(ConditionalLayerNorm
, self
).__init__
()if isinstance(normalized_shape
, numbers
.Integral
):normalized_shape
= (normalized_shape
,)self
.normalized_shape
= tuple(normalized_shape
)self
.eps
= epsself
.weight
= Parameter
(torch
.Tensor
(*normalized_shape
))self
.bias
= Parameter
(torch
.Tensor
(*normalized_shape
))self
.conditional_size
= conditional_sizeself
.weight_dense
= nn
.Linear
(conditional_size
,self
.normalized_shape
[0],bias
=False)self
.bias_dense
= nn
.Linear
(conditional_size
, self
.normalized_shape
[0],bias
=False)self
.reset_parameters
()def reset_parameters(self
) -> None:init
.ones_
(self
.weight
)init
.zeros_
(self
.bias
)init
.zeros_
(self
.weight_dense
.weight
)init
.zeros_
(self
.bias_dense
.weight
)def forward(self
, input: Tensor
,condition
: Tensor
) -> Tensor
:'''condition.shape = (batch_size,conditional_size)'''condition
= torch
.unsqueeze
(condition
, 1)'''condition.shape = (batch_size,1,conditional_size)'''add_weight
= self
.weight_dense
(condition
)add_bias
= self
.bias_dense
(condition
)'''condition經過兩次線性變換進行對齊與Layer Normalization的weight和bias相加'''weight
= self
.weight
+ add_weightbias
= self
.bias
+ add_biasoutputs
= inputmean
= torch
.mean
(outputs
, dim
=-1, keepdim
=True)outputs
= outputs
- meanvariance
= torch
.mean
(torch
.square
(outputs
), dim
=-1, keepdim
=True)std
= torch
.sqrt
(variance
+ self
.eps
)outputs
= outputs
/ stdoutputs
= outputs
* weightoutputs
= outputs
+ bias
return outputs
模型部分比較簡單,當我們需要在Bert中使用Conditional Layer Normalization,我們只需要將原生的Layer Normalization的實例化替換為上述的ConditionalLayerNorm即可,在傳入input時,我們額外設置一個輸入Condition。
class NeuralNetwork(nn
.Module
):def __init__(self
,model_path
):super(NeuralNetwork
, self
).__init__
()self
.linear_relu_stack
= nn
.Sequential
(nn
.Linear
(768, 512),nn
.ReLU
(),nn
.Linear
(512, 2))self
.standerembed
= nn
.Embedding
(6,128)'''六個子任務,初始化6個128緯的embedding向量'''self
.bert
= BertModel
.from_pretrained
(model_path
,config
=Config
)def forward(self
, input_ids
, attention_mask
,token_type_ids
,c
):'''將任務類別作為一個輸入傳入,獲取對應的condition'''condition
= self
.standerembed
(c
)x1
= self
.bert
(input_ids
, attention_mask
=attention_mask
,token_type_ids
=token_type_ids
,conditional
=condition
)x2
= x1
.last_hidden_statelogits
= self
.linear_relu_stack
(x2
[:, 0])return logits
實驗結果
方法模型線下F1線上F1
| P-tuning | chinese-bert-wwm-extinput=512_{input=512}input=512? | \ | 0.7166 |
| Conditional_LN | chinese-bert-wwm-extinput=512_{input=512}input=512? | 0.7244 | 0.7261 |
| Conditional_LN | wobert-baseinput=512_{input=512}input=512? | 0.7353 | 0.7279 |
| Conditional_LN | nezha-base-wwminput=1024_{input=1024}input=1024? | 0.7367 | 0.7259 |
總結
Conditional_LN和P-tuning作為兩種接插即用的多任務掛件,方便我們在不改變模型結構的前提下對龐大的預訓練模型進行快速微調,經過實驗兩者的收斂速度相差無幾,對于本文提到的分類任務性能上Conditional_LN稍優于P-tuning,直覺上Conditional_LN影響了模型每一層的輸出分布,相比P-tuning僅在輸入環節加入信息來說,前者更容易擬合。利用NEZHA相對位置編碼增大輸入的長度并不能給我們來帶提升。P-tuning作為比較新的idea,不僅給GPT等生產模型的控制帶來了方便,也給MLM模型提供了更多可能。本次比賽目前的Top1分數為0.748,于作者實現的Conditional_LN差0.022,在這個baseline的基礎上通過對數據進行進一步清洗,并進行K-fold + ensamble模型融合,能進一步提高分數。
參考資料:
蘇劍林. (Apr. 16, 2021). 《搜狐文本匹配:基于條件LayerNorm的多任務baseline 》[Blog post]. Retrieved from https://kexue.fm/archives/8337《GPT Understands, Too》https://arxiv.org/abs/2103.10385
代碼開源
https://github.com/zhengyanzhao1997/NLP-model/tree/main/model/model/Torch_model/Souhu_TextMatch
總結
以上是生活随笔為你收集整理的语义匹配(二)搜狐文本匹配大赛BaseLine比较:P-tuning和Conditional_LN实现多任务语义匹配的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。