BERT文本分类,代码超基础、超详细解析
生活随笔
收集整理的這篇文章主要介紹了
BERT文本分类,代码超基础、超详细解析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
聲明:
關于文章:
內容:使用bert進行新聞文本分類, 目的:熟悉預訓練模型的使用過程以及數據處理,和模型的各個接口,輸入輸出,做到對bert的簡單使用 環境:windows,pytorch,transformer,sklearn這些庫都需要自行下載, 另外,文章字不多,所有解釋都在代碼的注釋中,基本每一行都有注釋,我也手撕過bert、transformer代碼,有時間或者有用的話也寫出來分享給大家。 文章參考:https://blog.csdn.net/weixin_45552370/article/details/119788420關于本人:
本人是研一學生,知識圖譜方向,深度學習小白,對transformer以及bet等的理解并不深,這是在學習過程中寫的,一方面給自己加深印象,另一方面,我也深知中茫然不知怎么學的痛苦迷惘,希望對有需要的人能有一點點幫助。 我深知,文章中可能有些錯誤或者理解不到位的地方,倘若您能指出,我將非常感謝。 寫文章的過程中(其實是寫注釋的過程中),有過放棄的想法,心想我自己看明白了就行啊,為什么還要寫明白,分享給別人,又不能賣錢,但回想自己在學習過程中的遇到不會的地方的迷茫,看到好文章的驚喜,才讓我有了寫完的動力, 盡管只是一個小知識點,一篇不超過一萬字(而且主要是代碼)的短文,對我自己確是一種成長。 感謝那些無私分享知識的同志,這也許就是互聯網的精神吧,第一次發文,如果你看完本文能有用些許收獲。歡迎點贊評論,對我并無利益可言 只是希望能讓有需要的人看到。**自然如果我哪里寫的不正確或者有偏差,非常非常歡迎您的批評指正。**數據集:
新聞短文本數據集 鏈接: 新聞短文本數據集.
正文(代碼)
# 加深理解,注釋都是非常簡單的,非常詳細,一是希望自己能夠徹底理解處理流程, # 二是希望基礎薄弱的讀著看完能有所收獲 # 來自:https://blog.csdn.net/weixin_45552370/article/details/119788420 import torch # pytor庫,必用 import pandas as pd # pandas庫是一個處理各種數據文件的庫,類似于wps,可以打開,保存各種word,ppt等格式的文件 import torch.nn as nn #導入nn,這個庫里面是一些全連接網絡,池化層,這種神經網絡中非常基本的一些模塊,這里我們主要是用nn.linear是一個全連接層 from transformers import BertModel, BertTokenizer# transformer庫是一個把各種預訓練模型集成在一起的庫,導入之后,你就可以選擇性的使用自己想用的模型,這里使用的BERT模型。所以導入了bert模型,和bert的分詞器,這里是對bert的使用,而不是bert自身的源碼,如果有時間或者興趣的話我會在另一篇文章寫bert的源碼實現 from sklearn.model_selection import train_test_split #sklearn是一個非?;A的機器學習庫,里面都是一切基礎工具,類似于聚類算法啊,邏輯回歸算法啊,各種對數據處理的方法啊,這里我們使用的train_test_split方法,是把數據,一部分用作訓練,一部分用作測試的劃分數據的方法# 第一部分 # 加載訓練集,第一個參數文件位置,默認會以空格劃分每行的內容, # delimier參數設置的備選用制表符劃分, # 第三個參數 是不滿足要求的行舍棄掉,正常情況下,每行都是一個序號,一個標簽,一段文本, # 如果不是這樣的,我們就舍棄這一行train_set = pd.read_csv("./data/train.tsv",delimiter="\t",error_bad_lines=False) #model_name = "hfl/chinese-bert-wwm" #我們是要使用bert模型,但是bert也有很多模型,比如處理英文的,處理法語的,我們這里使用處理中文,且全mask的方法,感興趣可以看這里了解https://github.com/ymcui/Chinese-BERT-wwm,另外,如果手碼代碼出錯了,可能是因為字符串打錯了,fhl而不是hf1,是L而不是數字1 # 下面我們就獲得了bert模型中的hfl/chinese-bert-wwm模型的模型以及模型分詞方法,這里是原始模型,我們要使用微調,所以下面自寫類 # tokenizer = BertTokenizer.from_pretrained(model_name) # model = BertModel.from_pretrained(model_name)# 第二部分 # 采用bert微調策略,在反向傳播時一同調整BERT和線性層的參數,使bert更加適合分類任務 # bert微調,在這里 就是bert(類的前四行)+一個全連接層(第五行 self.fc = nn.Linear(768,15))組成一個全新模型, class BertClassfication(nn.Module):#括號里面是繼承什么,該類繼承自nn.module,因為我們的模型在根本上是神經網絡的,所以繼承nn.modelu,繼承它的基本屬性是自然的了def __init__(self):# 前四行就是bert,第五行是全連接層,即bert微調,就是本文要使用的模型super(BertClassfication,self).__init__()self.model_name = 'hfl/chinese-bert-wwm'self.model = BertModel.from_pretrained(self.model_name)self.tokenizer = BertTokenizer.from_pretrained(self.model_name)self.fc = nn.Linear(768,15) #768取決于BERT結構,2-layer, 768-hidden, 12-heads, 110M parametersdef forward(self,x):#這里的輸入x是一個list,也就是輸入文本x:RNG全隊夢游失誤頻頻不敵FW,后續淘汰賽成績引人擔憂,我這里是用一句話舉例子,實際上的數據是很多很多句話(哈哈,好不專業,很多很多)# 這句話是對文本1.進行分詞,2.進行編碼可以這里理解一下https://www.cnblogs.com/zjuhaohaoxuexi/p/15135466.html# 第一個參數x自然也就是文本了# 第二個參數add_special_token,就是說要不要加入一些特殊字符,比如標記開頭的cls,等等# 第三個參數就是最長長度,# 第四個參數是pad_to_max_len:就是說如果不夠max_len的長度,就補長到最大長度# 還有一個參數這里沒用到,就是比最長的長久減掉,第四第五個參數也就是長的減,短的補batch_tokenized = self.tokenizer.batch_encode_plus(x, add_special_tokens=True,max_length=148, pad_to_max_length=True) #tokenize、add special token、pad# 可以看到上一步的結果是好幾個維度,(建議一遍寫代碼,一遍在jupyter里面調試,看看每一步的結果或者是什么形式)# 取出input_ids:這是對漢語的編碼# attention_mask:這是對每個字是否mask的一個標記,原本的詞的位置是1,如果由于詞長不夠max_len,用pad或者其他的填充了,該位置就是0,意味著在模型中不注意他,因為該位置是填充上的沒有意義的字符,我們為什么要管他呢?input_ids = torch.tensor(batch_tokenized['input_ids'])attention_mask = torch.tensor(batch_tokenized['attention_mask'])# 把上邊兩個輸入bert模型,得到bert最后一層的隱藏層的輸出,hiden_outputs = self.model(input_ids,attention_mask=attention_mask)# bert的輸出結果有四個維度:# last_hidden_state:shape是(batch_size, sequence_length, hidden_size),hidden_size=768,它是模型最后一層輸出的隱藏狀態。# pooler_output:shape是(batch_size, hidden_size),這是序列的第一個token(classification token)的最后一層的隱藏狀態,它是由線性層和Tanh激活函數進一步處理的。(通常用于句子分類,至于是使用這個表示,還是使用整個輸入序列的隱藏狀態序列的平均化或池化,視情況而定)# hidden_states:這是輸出的一個可選項,如果輸出,需要指定config.output_hidden_states=True,它也是一個元組,它的第一個元素是embedding,其余元素是各層的輸出,每個元素的形狀是(batch_size, sequence_length, hidden_size)# attentions:這也是輸出的一個可選項,如果輸出,需要指定config.output_attentions=True,它也是一個元組,它的元素是每一層的注意力權重,用于計算self-attention heads的加權平均值。# 我們是微調模式,需要獲取bert最后一個隱藏層的輸出輸入到下一個全連接層,所以取第一個維度,也就是hiden_outputs[0]# 此時shape是(batch_size, sequence_length, hidden_size),[:,0,:]的意思是取出第一個也就是cls對應的結果,至于為什么這樣操作,我也不知道,有人告訴我因為它最具有代表性,但為什么我還是不知道,有大神能給我講一下嗎outputs = hiden_outputs[0][:,0,:] #[0]表示輸出結果部分,[:,0,:]表示[CLS]對應的結果# 把bert最后一層的結果輸入到全連接層中,全連接層是{768,15},會輸出15分類的一個結果,output = self.fc(outputs)# 這里就是返回最終地分類結果了return output# 第三部分,整理數據集 # 可以看一下tsv文件的格式,就知道下面這兩行什么意思了,一個存儲文本,一個存儲改文本對應的標簽 sentences = train_set['text_a'].values targets = train_set['label'].values train_features,test_features,train_targets,test_targets = train_test_split(sentences,targets)# 這里是把數據分為訓練集和測試集,開頭導入這個庫的方法時說了batch_size = 64 #把64句話當成一個塊來處理,相當于一段有64句話 batch_count = int(len(train_features) / batch_size) #這里就是所有的數據一共可以分為多少塊(段) batch_train_inputs, batch_train_targets = [], []# 一個列表存儲分段的文本,一個列表存儲分段的標簽 # 分段 for i in range(batch_count):batch_train_inputs.append(train_features[i*batch_size : (i+1)*batch_size])batch_train_targets.append(train_targets[i*batch_size : (i+1)*batch_size])# 第四部分,訓練 bertclassfication = BertClassfication() #實例化 lossfuction = nn.CrossEntropyLoss() #定義損失函數,交叉熵損失函數 optimizer = torch.optim.Adam(bertclassfication.parameters(),lr=2e-5)#torch.optim里面都是一些優化器,就是一些反向傳播調整參數的方法,比如梯度下降,隨機梯度下降等,這里使用ADAM,一種隨機梯度下降的改進優化方法 epoch = 5 # 訓練輪數,5輪就是所有數據跑五遍 for _ in range(epoch):los = 0 # 損失,寫在這里,因為每一輪訓練的損失都好應該重新開始計數for i in range(batch_count):#剛才說了batch_count的意思有多少塊(段),每段有64句話inputs = batch_train_inputs[i]targets = torch.tensor(batch_train_targets[i])optimizer.zero_grad()#1.梯度置零outputs= bertclassfication(inputs)#2.模型獲得結果loss = lossfuction(outputs,targets)#3.計算損失loss.backward()#4.反向傳播optimizer.step()# 5.修改參數,w,blos += loss.item() #item()返回loss的值# 下面每處理五個段,我們看一下當前損失是多少if i%5==0:print("Batch:%d,Loss %.4f" % ((i),los/5))los = 5# 第四部分 驗證 hit = 0 #用來計數,看看預測對了多少個 total = len(test_features) # 看看一共多少例子 for i in range(total):outputs = model([test_features[i]])_,predict = torch.max(outputs,1)# 這里你需要了解一下torch.max函數,詳見https://www.jianshu.com/p/3ed11362b54fif predict==test_targets[i]:# 預測對hit+=1 print('準確率為%.4f'%(hit/len(test_features)))# 小實驗,效果呈現 # 模型訓練完事之后,由于原數據集的label只有數字,沒有中文注釋,于是我自己人工注釋了0-14這15個數字對應的類別 transform_dict = {0:'文學',1:'娛樂資訊',2:'體育',3:'財經',4:'房產與住宅',5:'汽車',6:'教育',7:'科技與互聯網',8:'軍事',9:'旅游',10:'國際新聞與時政',11:'股票',12:'三農',13:'電子競技',14:'小說、故事與軼聞趣事' } result = bertclassfication(["2022年是政治之年"]) _,result = torch.max(result,1) print(transform_dict[result]) 時間太長了(個人電腦配置一般),一輪就花費了很長時間,  如果你沒有一臺非常好的電腦或者沒有實驗室的服務器可以用,建議把數據文件可以適當刪除一些。最后:
感謝您能看到這里,總結
以上是生活随笔為你收集整理的BERT文本分类,代码超基础、超详细解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 易观智库:2013年中国供应链大数据市场
- 下一篇: php中文转化为英文,json转码_解决