15 分鐘搭建一個(gè)基于XLNET的文本分類模型——keras實(shí)戰(zhàn)

今天筆者將簡(jiǎn)要介紹一下后bert 時(shí)代中一個(gè)又一比較重要的預(yù)訓(xùn)練的語(yǔ)言模型——XLNET 妥箕,下圖是XLNET在中文問(wèn)答數(shù)據(jù)集CMRC 2018數(shù)據(jù)集(哈工大訊飛聯(lián)合實(shí)驗(yàn)室發(fā)布的中文機(jī)器閱讀理解數(shù)據(jù)悦施,形式與SQuAD相同)上的表現(xiàn)。我們可以看到XLNET的實(shí)力略勝于BERT奴愉。

XLNET 的一些表現(xiàn)

這里筆者會(huì)先簡(jiǎn)單地介紹一下XLNET精妙的算法設(shè)計(jì),當(dāng)然我盡量采用通俗的語(yǔ)言去表達(dá)那些深?yuàn)W的數(shù)學(xué)表達(dá)式,整個(gè)行文過(guò)程會(huì)直接采用原論文的行文流程:Observition—>Motivition—>Contribution蜜宪。然后我會(huì)介紹一下如何用python在15分鐘之內(nèi)搭建一個(gè)基于XLNET的文本分類模型。

XLNET的原理

Observision

XLNET的原論文將預(yù)訓(xùn)練的語(yǔ)言模型分為兩類:

1. 自回歸:根據(jù)上文預(yù)測(cè)下文將要出現(xiàn)的單詞祥山,讓模型在預(yù)訓(xùn)練階段去做補(bǔ)充句子任務(wù),其中代表模型就是GPT圃验。

把句子補(bǔ)充完整。

1.秋天到了缝呕,________________________澳窑。

2 .自編碼:根據(jù)上下文去預(yù)測(cè)中間詞,其實(shí)就是讓模型在預(yù)訓(xùn)練階段去做完形填空任務(wù)岳颇,其中的代表模型就是BERT照捡,在實(shí)際預(yù)訓(xùn)練過(guò)程中用[MASK]這個(gè)字符替代要被預(yù)測(cè)的目標(biāo)單詞。

請(qǐng)選擇最合適的詞填入空缺處

1.這種夢(mèng)說(shuō)明你正在______挑戰(zhàn)话侧,但你還沒(méi)有做好準(zhǔn)備栗精。
A.面臨 B.逃避 C.參考 D.承擔(dān)

但這兩種語(yǔ)言模型都有各自的不足之處,如下圖所示:

  • 自回歸語(yǔ)言模型:由于是標(biāo)準(zhǔn)的單向語(yǔ)言模型瞻鹏,無(wú)法看到下文的信息悲立,可能導(dǎo)致信息利用不充分的問(wèn)題,實(shí)際的實(shí)驗(yàn)結(jié)果也證明利用上下文信息的BERT的效果要強(qiáng)于只利用上文信息的GPT新博。
  • 自編碼語(yǔ)言模型:1.預(yù)訓(xùn)練階段(存在[Mask] 字符)和finetuning 階段(無(wú)[Mask] 字符)文本分布不一致薪夕,會(huì)影響到下游任務(wù)的funetuning效果。2.被預(yù)測(cè)的token(詞或者字)之間彼此獨(dú)立赫悄,沒(méi)有任何語(yǔ)義關(guān)聯(lián)原献,


    兩種語(yǔ)言模型

Motivation

  • 解決自回歸語(yǔ)言模型無(wú)法獲取下文信息(預(yù)知未來(lái))的問(wèn)題
  • 優(yōu)化自編碼語(yǔ)言模型存在的兩個(gè)缺點(diǎn):1.預(yù)訓(xùn)練階段(存在[Mask] 字符)和finetuning 階段(無(wú)[Mask] 字符)文本分布不一致馏慨,2.被預(yù)測(cè)Mask詞之間彼此獨(dú)立,不符合人的認(rèn)知姑隅。

Contribution

有了上面兩個(gè)Motivation写隶,作者就提出了XLNET去解決這兩個(gè)問(wèn)題,這里我不會(huì)引入公式讲仰,只是簡(jiǎn)單的解釋一下XLNET的三個(gè)比較創(chuàng)新的設(shè)計(jì)慕趴。

  • Permutation Language Model:這個(gè)部分是XLNET最精彩的設(shè)計(jì)。主要目的就是為了解決單向語(yǔ)言模型無(wú)法利用下文信息的缺陷鄙陡,如下圖所示冕房,其實(shí)就是對(duì)句子的順序做隨機(jī)打亂后,然后作為單向語(yǔ)言模型的輸入趁矾。預(yù)訓(xùn)練時(shí)需預(yù)測(cè)當(dāng)前位置的token耙册。我們來(lái)詳細(xì)體會(huì)一下 Permutation Language Model的過(guò)程。
    1 .假設(shè)我們有一個(gè)序列[1,2,3,4]愈魏,預(yù)測(cè)目標(biāo)是3觅玻。
    2 .先對(duì)該序列進(jìn)行因式分解,對(duì)句子的順序做隨機(jī)打亂培漏,最終會(huì)有24種排列方式溪厘,下圖是其中可能的四種情況。
    3 .其中右上的圖中牌柄,3的左邊還包括了2與4畸悬,以為著我們?cè)陬A(yù)測(cè)3的時(shí)候看到3這個(gè)token上下文的信息,同時(shí)依然保持著單向的語(yǔ)言模型珊佣。

    這部分的設(shè)計(jì)就是為了解決之前的自回歸和自編碼語(yǔ)言模型的缺陷蹋宦,也就是Motivation中的兩個(gè)問(wèn)題。所以這個(gè)設(shè)計(jì)是整片文章最精妙的部分咒锻。

接下來(lái)的兩個(gè)設(shè)計(jì)主要為了解決構(gòu)建Permutation Language Model帶來(lái)的兩個(gè)問(wèn)題:
1.序列進(jìn)行因式分解使得token失去位置感冷冗。
2.序列進(jìn)行因式分解使得進(jìn)行預(yù)測(cè)時(shí)模型能夠提前看到答案。

Permutation Language Model
  • Reparameterization with positions:由于Permutation Language Model對(duì)句子順序做了隨機(jī)打亂惑艇,可能會(huì)導(dǎo)致模型失去對(duì)某個(gè)token在句子中的原始位置的感知蒿辙,導(dǎo)致模型退化成一個(gè)詞袋模型,于是作者利用Reparameterization with positions去解決這個(gè)問(wèn)題滨巴。

  • Two-stream attention
    這部分的設(shè)計(jì)依然是為了解決Permutation Language Model留下的另外一個(gè)問(wèn)題思灌,細(xì)心的讀者會(huì)發(fā)現(xiàn)序列因式分解會(huì)使得被預(yù)測(cè)的token被提起預(yù)知,回到上圖:有一個(gè)序列[1,2,3,4]恭取,預(yù)測(cè)目標(biāo)是3泰偿。可是其中有個(gè)因式分解的順序是[3,2,4,1] 導(dǎo)致預(yù)測(cè)3的時(shí)候提前拿到了3的信息蜈垮,這樣就使得預(yù)訓(xùn)練的過(guò)程變得無(wú)意義了耗跛,于是作者設(shè)計(jì)了Two-stream attention:

    1.一個(gè)query stream只編碼目標(biāo)單詞以外的上下文的信息以及目標(biāo)單詞的位置信息裕照。
    2.一個(gè)content stream既編碼目標(biāo)單詞自己的信息,又編碼上下文的信息供课兄。

    query stream 的作用就是避免了模型在預(yù)訓(xùn)練時(shí)提前預(yù)知答案牍氛。 而做下游任務(wù) fine-tune 時(shí)將query stream去掉,這樣就完美的解決了這個(gè)問(wèn)題烟阐。

下圖就是文章作者的PPT中對(duì)XLNET的總結(jié)。具體如果實(shí)作的建議讀者去看看原文和作者對(duì)XLNET的講解視頻,需要在提一嘴的是XLNET的網(wǎng)絡(luò)架構(gòu)使用了transfomer-xl紊扬,這都使得XLNET在長(zhǎng)文本的處理能力變得更強(qiáng)蜒茄。

XLNET的行文邏輯

XLNET實(shí)戰(zhàn)部分

好的,經(jīng)過(guò)了不是特別深刻的XLNET原理簡(jiǎn)介餐屎,來(lái)到我們激動(dòng)人心的實(shí)戰(zhàn)部分檀葛。(其實(shí)上文原理部分只是希望大家在感性上知道XLNET到底做了些什么,至于如何做到的還請(qǐng)拜讀原文和源代碼)腹缩。

準(zhǔn)備工作

定義一下超參數(shù)和預(yù)訓(xùn)練模型的路徑

import os
import sys
from collections import namedtuple
import numpy as np
import pandas as pd
from keras_xlnet.backend import keras
from keras_bert.layers import Extract
from keras_xlnet import Tokenizer, load_trained_model_from_checkpoint, ATTENTION_TYPE_BI
from keras_radam import RAdam

### 預(yù)訓(xùn)練模型的路徑
pretrained_path  = "/opt/developer/wp/xlnet/pretrain"
EPOCH = 10
BATCH_SIZE = 16
SEQ_LEN = 256

MODEL_NAME = 'xlnet_cls.h5'
PretrainedPaths = namedtuple('PretrainedPaths', ['config', 'model', 'vocab'])

config_path = os.path.join(pretrained_path, 'xlnet_config.json')
model_path = os.path.join(pretrained_path, 'xlnet_model.ckpt')
vocab_path = os.path.join(pretrained_path, 'spiece.model')
paths = PretrainedPaths(config_path, model_path, vocab_path)
tokenizer = Tokenizer(paths.vocab)

數(shù)據(jù)準(zhǔn)備

這里我選擇了一個(gè)語(yǔ)義相似度判斷的任務(wù)——及判斷兩個(gè)問(wèn)題是否是同一個(gè)問(wèn)題竿痰。數(shù)據(jù)集如下圖所示:


數(shù)據(jù)集

如上圖所示:數(shù)據(jù)集共2萬(wàn)條脆粥,主要任務(wù)就是判斷question1 和 question2 是否為同一問(wèn)題。label為1則是同一個(gè)問(wèn)題影涉,label為0則不是同一個(gè)問(wèn)題变隔。其中還有列標(biāo)注了問(wèn)題的類型。

在由于XLNET的輸入為單輸入,于是筆者將數(shù)據(jù)預(yù)處理成 "問(wèn)題類型:'question1是否和question2是同一個(gè)問(wèn)題蟹倾?"匣缘。舉個(gè)例了,上圖數(shù)據(jù)集的第一條數(shù)據(jù)最終變成如下格式:

  • data: aids:艾滋病窗口期會(huì)出現(xiàn)腹瀉癥狀嗎是否和頭疼腹瀉四肢無(wú)力是不是艾滋病是同一個(gè)問(wèn)題喊式?
  • label:0
# Read data
class DataSequence(keras.utils.Sequence):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __len__(self):
        return (len(self.y) + BATCH_SIZE - 1) // BATCH_SIZE

    def __getitem__(self, index):
        s = slice(index * BATCH_SIZE, (index + 1) * BATCH_SIZE)
        return [item[s] for item in self.x], self.y[s]


def generate_sequence(df):
    tokens, classes = [], []
    for _, row in df.iterrows():
        ###這里筆者將數(shù)據(jù)進(jìn)行拼接  類型+問(wèn)題1+問(wèn)題2
        text, cls = row["category"] + ":"+ row['question1'] + "是否和" + row['question2'] + "是同一個(gè)問(wèn)題孵户?", row['label']
        encoded = tokenizer.encode(text)[:SEQ_LEN - 1]
        encoded = [tokenizer.SYM_PAD] * (SEQ_LEN - 1 - len(encoded)) + encoded + [tokenizer.SYM_CLS]
        tokens.append(encoded)
        classes.append(int(cls))
    tokens, classes = np.array(tokens), np.array(classes)
    segments = np.zeros_like(tokens)
    segments[:, -1] = 1
    lengths = np.zeros_like(tokens[:, :1])
    return DataSequence([tokens, segments, lengths], classes)


### 讀取數(shù)據(jù),然后將數(shù)據(jù)
data_path = "/opt/developer/wp/xlnet/data/train.csv"
data = pd.read_csv(data_path)
test = data.sample(2000)
train = data.loc[list(set(data.index)-set(test.index))]
### 生成訓(xùn)練集和測(cè)試集
train_g = generate_sequence(train)
test_g = generate_sequence(test)

加載模型

加載事先下載好的xlnet的預(yù)訓(xùn)練語(yǔ)言模型岔留,然后再接兩個(gè)dense層和一個(gè)bn層構(gòu)建一個(gè)類別為2的分類器夏哭。

# Load pretrained model
model = load_trained_model_from_checkpoint(
    config_path=paths.config,
    checkpoint_path=paths.model,
    batch_size=BATCH_SIZE,
    memory_len=0,
    target_len=SEQ_LEN,
    in_train_phase=False,
    attention_type=ATTENTION_TYPE_BI,
)

#### 加載預(yù)訓(xùn)練權(quán)重
# Build classification model
last = model.output
extract = Extract(index=-1, name='Extract')(last)
dense = keras.layers.Dense(units=768, name='Dense')(extract)
norm = keras.layers.BatchNormalization(name='Normal')(dense)
output = keras.layers.Dense(units=2, activation='softmax', name='Softmax')(norm)
model = keras.models.Model(inputs=model.inputs, outputs=output)
model.summary()

針對(duì)下游任務(wù)fine-tuning

接下來(lái)只需要定義好優(yōu)化器,學(xué)習(xí)率献联,損失函數(shù)竖配,評(píng)估函數(shù)何址,以及一些回調(diào)函數(shù),就可以開(kāi)始針對(duì)語(yǔ)義相似度判斷任務(wù)進(jìn)行模型微調(diào)了进胯。

# 定義優(yōu)化器用爪,loss和metrics
model.compile(
    optimizer=RAdam(learning_rate=1e-5),
    loss='sparse_categorical_crossentropy',
    metrics=['sparse_categorical_accuracy'],
)
### 定義callback函數(shù),只保留val_sparse_categorical_accuracy 得分最高的模型
from keras.callbacks import ModelCheckpoint
checkpoint = ModelCheckpoint("./model/best_xlnet.h5", monitor='val_sparse_categorical_accuracy', verbose=1, save_best_only=True,
                            mode='max')

模型訓(xùn)練
model.fit_generator(
    generator=train_g,
    validation_data=test_g,
    epochs=EPOCH,
    callbacks=[checkpoint],
)

下發(fā)截圖是模型訓(xùn)練過(guò)程胁镐,看起來(lái)還不錯(cuò)偎血,loss下降和sparse_categorical_accuracy得分上升都很穩(wěn)定。

模型訓(xùn)練

至于最后效果盯漂,我只和bert做了一個(gè)簡(jiǎn)單的對(duì)比颇玷。在這個(gè)任務(wù)上,xlnet相較于bert稍微弱了2個(gè)百分點(diǎn)就缆,可能是由于文本過(guò)短的原因或者我自己打開(kāi)的方式不對(duì)帖渠,對(duì)應(yīng)xlnet的finetune還是需要好好研究。最后的感覺(jué)就是深度學(xué)習(xí)的煉丹之路任重而道遠(yuǎn)竭宰,大家且行且珍惜空郊。

結(jié)語(yǔ)

預(yù)訓(xùn)練的語(yǔ)言模型成為NLP的熱點(diǎn),通過(guò)無(wú)監(jiān)督的預(yù)訓(xùn)練讓模型學(xué)習(xí)領(lǐng)域基礎(chǔ)知識(shí)切揭,之后專注在這個(gè)領(lǐng)域的某個(gè)下游任務(wù)上才能有所建樹(shù)狞甚。這個(gè)過(guò)程和我們大學(xué)教育培養(yǎng)模式很像,大學(xué)四年海納百川式的學(xué)習(xí)一些基本的科學(xué)知識(shí)伴箩,研究生之后專注于某個(gè)方向深入研究入愧。所以這個(gè)過(guò)程make sense。筆者認(rèn)為以下三個(gè)方向是預(yù)訓(xùn)練模型還可以更進(jìn)一步的方向:

  • 更好嗤谚,更難的的無(wú)監(jiān)督預(yù)訓(xùn)練的任務(wù)(算法)
  • 更精妙的網(wǎng)絡(luò)設(shè)計(jì)(算法)
  • 更大棺蛛,更高質(zhì)量的數(shù)據(jù)(算力)

參考文獻(xiàn)

xlnet原文
https://github.com/CyberZHG/keras-xlnet
https://github.com/ymcui/Chinese-PreTrained-XLNet
https://www.bilibili.com/video/av67474438?from=search&seid=12927135467023026176

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巩步,隨后出現(xiàn)的幾起案子旁赊,更是在濱河造成了極大的恐慌,老刑警劉巖椅野,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件终畅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡竟闪,警方通過(guò)查閱死者的電腦和手機(jī)离福,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)炼蛤,“玉大人妖爷,你說(shuō)我怎么就攤上這事±砼螅” “怎么了絮识?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵绿聘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我次舌,道長(zhǎng)熄攘,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任彼念,我火速辦了婚禮挪圾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘国拇。我一直安慰自己洛史,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布酱吝。 她就那樣靜靜地躺著,像睡著了一般土思。 火紅的嫁衣襯著肌膚如雪务热。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天己儒,我揣著相機(jī)與錄音崎岂,去河邊找鬼。 笑死闪湾,一個(gè)胖子當(dāng)著我的面吹牛冲甘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播途样,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼江醇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了何暇?” 一聲冷哼從身側(cè)響起陶夜,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裆站,沒(méi)想到半個(gè)月后条辟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宏胯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年羽嫡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肩袍。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杭棵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出了牛,到底是詐尸還是另有隱情颜屠,我是刑警寧澤辰妙,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站甫窟,受9級(jí)特大地震影響密浑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粗井,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一尔破、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浇衬,春花似錦懒构、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至醉冤,卻和暖如春秩霍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚁阳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工铃绒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人螺捐。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓颠悬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親定血。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赔癌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容