前言
隨著bert ,gpt 等預(yù)訓(xùn)練模型的快速發(fā)展,眾多企業(yè)和學(xué)術(shù)組織在 1.預(yù)訓(xùn)練的任務(wù),2.模型的構(gòu)造,3.數(shù)據(jù)的質(zhì)量等方向進(jìn)行改進(jìn),訓(xùn)練出數(shù)以萬(wàn)計(jì)以transformer為基礎(chǔ)結(jié)構(gòu)的的berts和gpts,隨著bert,gpt的數(shù)量越來(lái)越多。Hugging face transformer屆的github 誕生了。Hugging face允許用戶(hù)上傳和下載的預(yù)訓(xùn)練的模型。這樣使得每個(gè) NLPer 都可以直接使用必須依靠大量美金才能訓(xùn)練出來(lái)的預(yù)訓(xùn)練模型。Nlper 可以輕易的在huggingface網(wǎng)站上面下載預(yù)訓(xùn)練模型在自己的數(shù)據(jù)集上進(jìn)行微調(diào)就能達(dá)到很好的效果,可謂造福大家。下方是Hugging face 中模型的搜索頁(yè)面,已經(jīng)有多達(dá)55592個(gè)預(yù)訓(xùn)練模型。

hugging face 又開(kāi)發(fā)了transformers 這個(gè)python 包,供大家一行代碼使用這些模型,十分便捷。比如可以直接 一行代碼 從 hugging face下載預(yù)訓(xùn)練模型到本地并加載到內(nèi)存,但是此法經(jīng)常碰到網(wǎng)絡(luò)練接中斷的問(wèn)題。
model = BertForSequenceClassification.from_pretrained(pretrain_Model_path)
今天筆者就記錄一下如何從https://huggingface.co 這個(gè)網(wǎng)站手動(dòng)下載模型,利用 transformers這個(gè)python 包采用本地加載模型的方式完成一次文本分類(lèi)的微調(diào)任務(wù)。
finetune前期準(zhǔn)備
1.使用下方命令安裝transformers的python包
pip install transformers
2.下載合適的預(yù)訓(xùn)練模型
這里筆者拿roberta為例,在huggingface網(wǎng)站搜索roberta,我這里找到哈工大的中文roberta,進(jìn)入詳情頁(yè)點(diǎn)files and verisons。就會(huì)看到如下方圖所示的模型文件和配置文件。

這里如果是pytorch 用戶(hù)只需要下載config.json,pytorch_model.bin和vocab.txt 即可,然后把它們放到同一個(gè)文件夾。
1.config.json 模型的結(jié)構(gòu)文件,如下方所示,定義了 dropout,hidden_size之類(lèi)的參數(shù)。
{
"architectures": [
"BertForMaskedLM"
],
"attention_probs_dropout_prob": 0.1,
"bos_token_id": 0,
"directionality": "bidi",
"eos_token_id": 2,
"hidden_act": "gelu",
"hidden_dropout_prob": 0.1,
"hidden_size": 768,
"initializer_range": 0.02,
"intermediate_size": 3072,
"layer_norm_eps": 1e-12,
"max_position_embeddings": 512,
"model_type": "bert",
"num_attention_heads": 12,
"num_hidden_layers": 12,
"output_past": true,
"pad_token_id": 0,
"pooler_fc_size": 768,
"pooler_num_attention_heads": 12,
"pooler_num_fc_layers": 3,
"pooler_size_per_head": 128,
"pooler_type": "first_token_transform",
"type_vocab_size": 2,
"vocab_size": 21128
}
2.pytorch_model.bin模型的權(quán)重
3.vocab.txt 模型所用的詞表,如下方所示。
[PAD]
[unused1]
[unused2]
[unused3]
[unused4]
[unused5]
[unused6]
[unused7]
[unused8]
...
體
佔(zhàn)
何
佗
佘
余
佚
...
finetune實(shí)戰(zhàn)部分
在模型已經(jīng)下載完成,transformers 包安裝完成后。我們就開(kāi)始在自己的數(shù)據(jù)集上進(jìn)行微調(diào)。
數(shù)據(jù)預(yù)處理
這里筆者選取來(lái)8000條新聞數(shù)據(jù),共13個(gè)分類(lèi),去除了非中文,將數(shù)據(jù)處理成下方圖片中展現(xiàn)的樣子。

import re
from sklearn.utils import shuffle
import pandas as pd
label_dic = {
'__label__affairs':0,
'__label__constellation':1,
'__label__economic':2,
'__label__edu':3,
'__label__ent':4,
'__label__fashion':5,
'__label__game':6,
'__label__home':7,
'__label__house':8,
'__label__lottery':9,
'__label__science':10,
'__label__sports':11,
'__label__stock':12}
def get_train_data(file,label_dic):
content = []
label = []
with open(file, "r", encoding="utf-8") as f:
for i in f.readlines():
c,l = i.split("\t")
content.append(re.sub('[^\u4e00-\u9fa5]',"",c))
label.append(label_dic.get(l.strip()))
return content,label
content,label = get_train_data("./news_fasttext_train.txt",label_dic)
data = pd.DataFrame({"content":content,"label":label})
data = shuffle(data)
train_data = tokenizer(data.content.to_list()[:8000], padding = "max_length", max_length = 100, truncation=True ,return_tensors = "pt")
train_label = data.label.to_list()[:8000]
預(yù)訓(xùn)練模型的載入
這里通過(guò)兩行代碼就可以初始化bert的編碼器和預(yù)訓(xùn)練模型的加載,AutoModelForSequenceClassification是專(zhuān)門(mén)為文本分類(lèi)定制的函數(shù)。
from transformers import AutoModelForSequenceClassification
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("./Robert")
model = AutoModelForSequenceClassification.from_pretrained("./robert", num_labels=13)
其中“./robert” 即是上方筆者從hugging face 網(wǎng)站上下載的模型和配置文件保存在了./robert 文件夾下。

定義優(yōu)化器和學(xué)習(xí)率
在AutoModelForSequenceClassification.from_pretrained("./robert", num_labels=13) 這個(gè)函數(shù)中,transformer 已經(jīng)幫你定義了損失函數(shù),既13個(gè)分類(lèi)的交叉熵?fù)p失,所以下方我們只需要自己定義優(yōu)化器和學(xué)習(xí)率即可。
import torch
from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler
batch_size = 16
train = TensorDataset(train_data["input_ids"], train_data["attention_mask"], torch.tensor(train_label))
train_sampler = RandomSampler(train)
train_dataloader = DataLoader(train, sampler=train_sampler, batch_size=batch_size)
// 定義優(yōu)化器
from torch.optim import AdamW
optimizer = AdamW(model.parameters(), lr=1e-4)
//定義學(xué)習(xí)率和訓(xùn)練輪數(shù)
num_epochs = 1
from transformers import get_scheduler
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
name="linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps
)
模型訓(xùn)練
這一步就是模型的微調(diào)訓(xùn)練過(guò)程,每10步輸出一下loss。
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
for epoch in range(num_epochs):
total_loss = 0
model.train()
for step, batch in enumerate(train_dataloader):
if step % 10 == 0 and not step == 0:
print("step: ",step, " loss:",total_loss/(step*batch_size))
b_input_ids = batch[0].to(device)
b_input_mask = batch[1].to(device)
b_labels = batch[2].to(device)
model.zero_grad()
outputs = model(b_input_ids,
token_type_ids=None,
attention_mask=b_input_mask,
labels=b_labels)
loss = outputs.loss
total_loss += loss.item()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
lr_scheduler.step()
avg_train_loss = total_loss / len(train_dataloader)
print("avg_loss:",avg_train_loss)
#輸出大致如下
#step: 10 loss: 0.1410729356110096
# step: 20 loss: 0.11348817646503448
# step: 30 loss: 0.09371910033126672
# step: 40 loss: 0.07884938775096088
# step: 50 loss: 0.06864133032038808
# step: 60 loss: 0.06410953995461265
# step: 70 loss: 0.05953138789960316
# step: 80 loss: 0.05694254372501746
# step: 90 loss: 0.05430558831948373
# step: 100 loss: 0.05061182997655123
# step: 110 loss: 0.04901935522105883
模型預(yù)測(cè)
這里已經(jīng)完成了模型的微調(diào),接下來(lái)我們用微調(diào)后的模型進(jìn)行預(yù)測(cè)。輸入“過(guò)考研大綱談農(nóng)學(xué)復(fù)習(xí)之化學(xué)部分年農(nóng)學(xué)將第二次實(shí)行全國(guó)統(tǒng)一考試這使農(nóng)學(xué)考生的復(fù)習(xí)備考十分迷?!?,模型確實(shí)預(yù)測(cè)出來(lái)是屬于教育類(lèi)的新聞。
import numpy as np
test = tokenizer("通過(guò)考研大綱談農(nóng)學(xué)復(fù)習(xí)之化學(xué)部分年農(nóng)學(xué)將第二次實(shí)行全國(guó)統(tǒng)一考試這使農(nóng)學(xué)考生的復(fù)習(xí)備考十分迷茫",return_tensors="pt",padding="max_length",max_length=100)
model.eval()
with torch.no_grad():
outputs = model(test["input_ids"],
token_type_ids=None,
attention_mask=test["attention_mask"])
pred_flat = np.argmax(outputs["logits"],axis=1).numpy().squeeze()
print(pred_flat.tolist())
# label_dic = {
# '__label__affairs':0,
# '__label__constellation':1,
# '__label__economic':2,
# '__label__edu':3,
# '__label__ent':4,
# '__label__fashion':5,
# '__label__game':6,
# '__label__home':7,
# '__label__house':8,
# '__label__lottery':9,
# '__label__science':10,
# '__label__sports':11,
# '__label__stock':12}

尾聲
至此,我們就已經(jīng)學(xué)會(huì)了如何從haggface 網(wǎng)站上下載預(yù)訓(xùn)練的模型,并利用transformers 包在自己的數(shù)據(jù)集上進(jìn)行文本分類(lèi)微調(diào)和預(yù)測(cè),接下來(lái)的一篇系列文章,筆者將會(huì)介紹如何并利用transformers 包在自己的數(shù)據(jù)集上進(jìn)行實(shí)體識(shí)別的微調(diào)和預(yù)測(cè)。