文章目錄 學習視頻地址 項目地址 數據集的下載 模型微調的基本概念與流程 加載數據集 制作Dataset vocab字典操作 下游任務模型設計 模型訓練與保存 最終效果評估與測試
學習視頻地址
項目地址
數據集的下載
下載數據集,然后進行文件調整,調整結果如下圖所示
from datasets import load_dataset
import os
os. environ[ "HF_HUB_DISABLE_SYMLINKS_WARNING" ] = "1"
dataset_name = "lansinuote/ChnSentiCorp"
cache_dir = "data/lansinuote/ChnSentiCorp"
dataset = load_dataset( dataset_name, cache_dir= cache_dir, trust_remote_code= True
) print ( f"數據集已下載到: { cache_dir} " )
print ( "\n第一條數據樣例:" )
print ( dataset[ "train" ] [ 0 ] )
測試:加載數據,并在終端輸出
from datasets import Dataset
test_set = Dataset. from_file( "./data/lansinuote/ChnSentiCorp/chn_senti_corp-test.arrow" )
print ( test_set)
for data in test_set: print ( data)
train_set = Dataset. from_file( ".//data/lansinuote/ChnSentiCorp/chn_senti_corp-train.arrow" )
print ( train_set)
for data in train_set: print ( data)
validation_set = Dataset. from_file( "./data/lansinuote/ChnSentiCorp/chn_senti_corp-validation.arrow" )
print ( validation_set)
for data in validation_set: print ( data)
模型微調的基本概念與流程
微調指在預訓練模型的基礎上,通過進一步的訓練來適應特定的下游任務 。BERT模型通過預訓練來學習語言的通用模式,然后通過微調來適應特定任務,如情感分析、命名實體識別等。微調過程中,通常凍結BERT的預訓練層,只訓練與下游任務相關的層。
加載數據集
情感分析任務的數據通常包括文本及其對應的情感標簽。使用HuggingFace的datasets庫可以輕松地加載和處理數據集。
from datasets import load_dataset
dataset = load_dataset( 'csv' ,data_files= "data/chnsenticorp.csv" )
print ( dataset)
數據集格式
Hugging Face 的 datasets 庫支持多種數據集格式,如 CSV、JSON、TFRecord 等。在本案例中,使用CSV格式,CSV文件應包含兩列:一列是文本數據,另一列是情感標簽。
數據集信息
加載數據集后,可以查看數據集的基本信息,如數據集大小、字段名稱等。
制作Dataset
加載數據集后,需要對其進行處理以適應模型的輸入格式。這包括數據清洗、格式轉換等操作。
from torch. utils. data import Dataset, DataLoader
from datasets import Dataset as HFDataset class MyDataset ( Dataset) : def __init__ ( self, split) : if split == "train" : data_path = "./data/train/chn_senti_corp-train.arrow" elif split == "test" : data_path = "./data/test/chn_senti_corp-test.arrow" elif split == "validation" : data_path = "./data/validation/chn_senti_corp-validation.arrow" else : raise ValueError( "無效的數據集類型,必須是 train/test/validation" ) self. hf_dataset = HFDataset. from_file( data_path) def __len__ ( self) : return len ( self. hf_dataset) def __getitem__ ( self, idx) : text = self. hf_dataset[ idx] [ "text" ] label = self. hf_dataset[ idx] [ "label" ] return text, labelif __name__ == "__main__" : dataset = MyDataset( "train" ) dataloader = DataLoader( dataset, batch_size= 1 , shuffle= True ) for data in dataloader: print ( data)
數據集字段
在制作Dataset時,需定義數據集的字段。在本案例中,定義了兩個字段:text(文本)和labe1(情感標簽)。每個字段都需要與模型的輸入和輸出匹配。
數據集信息
制作Dataset后,可以通過dataset.info等方法查看其大小、字段名稱等信息,以確保數據集的正確性和完整性。
vocab字典操作
詞匯表
BERT模型使用詞匯表(vocab)將文本轉換為模型可以理解的輸入格式。詞匯表包含所有模型已知的單詞及其對應的索引。確保數據集中的所有文本都能找到對應的詞匯索引是至關重要的。
from transformers import BertTokenizer
model_name= "./model/google-bert/bert-base-chinese/models--google-bert--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f"
token= BertTokenizer. from_pretrained( model_name)
'''
sents=["酒店太舊,大堂感覺像三星級水平,在鄭州這樣的酒店水平,絕對算不上四星水平,早餐走了兩圈也沒有可以吃的,太差","已經貼完了,又給小區的媽媽買了一套,值得推薦","屏幕大,本子薄。自帶數字小鍵盤,比較少見,聲音很好,usb接口也很多,性價比高","酒店環境很好,就是有一點偏僻,交通不是很好,不太好打車,酒店應該想辦法解決一下"]
'''
'''
out=token.batch_encode_plus(batch_text_or_text_pairs=[sents[0],sents[1]],add_special_tokens=True,truncation=True, # 句子過長截斷padding="max_length", # 一律補零到max_length長度max_length=30,return_tensors=None, # 默認返回listreturn_attention_mask=True, # 返回 attention_maskreturn_token_type_ids=False,return_special_tokens_mask=True, #特殊符號標識return_length=True, # 返回length,標識長度)
print(out)
'''
token. add_tokens( new_tokens= [ "陽光" , "大志" ] )
vocab= token. get_vocab( )
print ( "大志" in vocab)
res= token. encode( text= "陽光照在大地上[EOS]" , text_pair= None , truncation= True , padding= "longest" , longest= 10 , add_special_tokens= True , return_tensors= None )
print ( res)
print ( token. decode( res) )
文本轉換
使用tokenizer將文本分割成詞匯表中的單詞,并轉換為相應的索引l。此步驟需要確保文本長度、特殊字符處理等都與BERT模型的預訓練設置相一致。
下游任務模型設計
在微調BERT模型之前,需要設計一個適應情感分析任務的下游模型結構。通常包括一個或多個全連接層,用于將BERT輸出的特征向量轉換為分類結果。
from transformers import BertModel
import torch
DEVICE= torch. device( 'cuda' if torch. cuda. is_available( ) else 'cpu' )
model_dir= "./model/google-bert/bert-base-chinese/models--google-bert--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f"
pretrained= BertModel. from_pretrained( model_dir) . to( DEVICE)
class Model ( torch. nn. Module) : def __init__ ( self) : super ( ) . __init__( ) self. fc = torch. nn. Linear( 768 , 2 ) def forward ( self, input_ids, attention_mask, token_type_ids) : with torch. no_grad( ) : out= pretrained( input_ids= input_ids, attention_mask= attention_mask, token_type_ids= token_type_ids) output= self. fc( out. last_hidden_state[ : 0 ] ) output= output. softmax( dim= 1 ) return output;
模型訓練與保存
數據加載
使用DataLoader實現批量數據加載。DataLoader自動處理數據的批處理和隨機打亂,確保訓l練的高效性和數據的多樣性。
優化器
Adamw是一種適用于BERT模型的優化器,結合了Adam和權重衰減的特點,能夠有效地防止過擬合。
訓練循環
訓練循環包含前向傳播(forward pass)、損失計算(loss calculation)、反向傳播(backward pass)、參數更新(parameter update)等步驟。每個epoch 都會對整個數據集進行一次遍歷,更新模型參數。通常訓練過程中會跟蹤損失值的變化,以判斷模型的收斂情況。
import torch
from torch. optim import AdamWfrom MyData import MyDataset
from torch. utils. data import DataLoader
from Net import Model
from transformers import BertTokenizer
DEVICE = torch. device( 'cuda' if torch. cuda. is_available( ) else 'cpu' )
EPOCHS = 100
BATCH_SIZE = 32
model_name = "./model/google-bert/bert-base-chinese/models--google-bert--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f"
token = BertTokenizer. from_pretrained( model_name)
def collate_fn ( data) : sente = [ i[ 0 ] for i in data] label = [ i[ 1 ] for i in data] data = token. batch_encode_plus( batch_text_or_text_pairs= sente, truncation= True , padding= 'max_length' , max_length= 300 , return_tensors= 'pt' , return_length= True ) input_ids = data[ 'input_ids' ] attention_mask = data[ 'attention_mask' ] token_type_ids = data[ 'token_type_ids' ] labels = torch. LongTensor( label) return input_ids, attention_mask, token_type_ids, labels
train_dataset = MyDataset( "train" )
train_loader = DataLoader( dataset= train_dataset, batch_size= BATCH_SIZE, shuffle= True , drop_last= True , collate_fn= collate_fn
) if __name__ == '__main__' : print ( DEVICE) model = Model( ) . to( DEVICE) optimizer = AdamW( model. parameters( ) , lr= 5e - 4 ) loss_func= torch. nn. CrossEntropyLoss( ) model. train( ) for epoch in range ( EPOCHS) : for i, ( input_ids, attention_mask, token_type_ids, labels) in enumerate ( train_loader) : input_ids, attention_mask, token_type_ids, labels= input_ids. to( DEVICE) , attention_mask. to( DEVICE) , token_type_ids. to( DEVICE) , labels. to( DEVICE) out= model( input_ids, attention_mask, token_type_ids) loss = loss_func( out, labels) optimizer. zero_grad( ) loss. backward( ) optimizer. step( ) if i% 20 == 0 : out= out. argmax( dim= 1 ) acc= ( out== labels) . sum ( ) / len ( labels) print ( epoch, i, loss. item( ) , acc) torch. save( model. state_dict( ) , f"./params/ { epoch} bert.pt" ) print ( epoch)
最終效果評估與測試
在模型訓練完成后,需要評估其在測試集上的性能。通常使用準確率、精確率、召回率和F1分數等指標來衡量模型的效果。 準確率是衡量分類模型整體性能的基本指標,計算公式為正確分類的樣本數量除以總樣本數量。 精確率、召回率是分類模型的另兩個重要指標,分別反映模型在正例預測上的精確性和召回能力。 F1分數是精確率和召回率的調和平均數,通常用于不均衡數據集的評估。
import torch
from Net import Model
from transformers import BertTokenizer
from MyData import MyDataset
from torch. utils. data import DataLoaderDEVICE = torch. device( "cuda:0" if torch. cuda. is_available( ) else "cpu" )
model_name = "./model/google-bert/bert-base-chinese/models--google-bert--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f"
token = BertTokenizer. from_pretrained( model_name)
def collate_fn ( data) : sente = [ i[ 0 ] for i in data] label = [ i[ 1 ] for i in data] data = token. batch_encode_plus( batch_text_or_text_pairs= sente, truncation= True , padding= 'max_length' , max_length= 300 , return_tensors= 'pt' , return_length= True ) input_ids = data[ 'input_ids' ] attention_mask = data[ 'attention_mask' ] token_type_ids = data[ 'token_type_ids' ] labels = torch. LongTensor( label) return input_ids, attention_mask, token_type_ids, labels
train_dataset = MyDataset( "train" )
train_loader = DataLoader( dataset= train_dataset, batch_size= 32 , shuffle= True , drop_last= True , collate_fn= collate_fn
) if __name__ == "__main__" : acc= 0 total= 0 model= Model( ) . to( DEVICE) model. load_state_dict( torch. load( "./params/1bert.pt" ) ) model. eval ( ) for i, ( input_ids, attention_mask, token_type_ids, labels) in enumerate ( train_loader) : input_ids, attention_mask, token_type_ids, labels = input_ids. to( DEVICE) , attention_mask. to( DEVICE) , token_type_ids. to( DEVICE) , labels. to( DEVICE) out = model( input_ids, attention_mask, token_type_ids) out= out. argmax( dim= 1 ) acc+= ( out== labels) . sum ( ) . item( ) total+= len ( labels) print ( acc/ total)
模型加載和測試
import torch
from transformers import BertTokenizerfrom emotionAnalysis. Net import ModelDEVICE = torch. device( 'cuda' if torch. cuda. is_available( ) else 'cpu' )
print ( DEVICE) values= [ "負向評價" , "正向評價" ]
model= Model( ) . to( DEVICE)
model_name = "./model/google-bert/bert-base-chinese/models--google-bert--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f"
token = BertTokenizer. from_pretrained( model_name)
def collate_fn ( data) : sente = [ data] data = token. batch_encode_plus( batch_text_or_text_pairs= sente, truncation= True , padding= 'max_length' , max_length= 300 , return_tensors= 'pt' , return_length= True ) input_ids = data[ 'input_ids' ] attention_mask = data[ 'attention_mask' ] token_type_ids = data[ 'token_type_ids' ] return input_ids, attention_mask, token_type_idsdef test ( ) : model. load_state_dict( torch. load( "./params/1bert.pt" ) ) model. eval ( ) while True : data= input ( "請輸入測試數據(輸入‘q’退出):" ) if data== "q" : print ( "測試結束" ) break input_ids, attention_mask, token_type_ids= collate_fn( data) input_ids, attention_mask, token_type_ids= input_ids. to( DEVICE) , attention_mask. to( DEVICE) , token_type_ids. to( DEVICE) with torch. no_grad( ) : out= model( input_ids= input_ids, attention_mask= attention_mask, token_type_ids= token_type_ids) out= out. argmax( dim= 1 ) print ( "模型判定:" , values[ out] , "\n" ) if __name__== "__main__" : test( )
cuda
請輸入測試數據(輸入‘q’退出):酒店服務不錯,環境也還比較好!特別是好像是48樓那個旋轉餐廳滿有特色,至少我覺得住這個酒店還滿滿意!
模型判定: 正向評價 請輸入測試數據(輸入‘q’退出):東西很差
模型判定: 負向評價 請輸入測試數據(輸入‘q’退出):東西很好,我要再買一個
模型判定: 正向評價 請輸入測試數據(輸入‘q’退出):我真的會謝謝你
模型判定: 正向評價 請輸入測試數據(輸入‘q’退出):真的很栓Q
模型判定: 負向評價