大模型學習:從零到一實現一個BERT微調

目錄

一、準備階段

1.導入模塊

2.指定使用的是GPU還是CPU

3.加載數據集

二、對數據添加詞元和分詞

1.根據BERT的預訓練,我們要將一個句子的句頭添加[CLS]句尾添加[SEP]

2.激活BERT詞元分析器

3.填充句子為固定長度

代碼解釋:

三、數據處理

1.創建masks掩碼矩陣

代碼解釋:

2.拆分數據集

3.將所有的數據轉換為torch張量

4.選擇批量大小并創建迭代器

代碼解釋:

四、BERT模型配置

1.初始化一個不區分大小寫的 BERT 配置:

代碼解釋:

2.這些配置參數的作用:

3.加載模型

4.優化器分組參數

代碼解釋:

5.訓練循環的超參數

代碼解釋:

五、訓練循環

代碼解釋:

訓練圖解:

?六、使用測試數據集進行預測和評估

七、使用馬修斯相關系數(MCC)評估

2. 代碼實現:

1. 測試數據預處理與預測

2.模型預測與結果收集

3.計算MCC

到這里就完美收官咯!!!!! 大家點個贊吧!!!


本章將微調一個BERT模型來預測下游的可接受性判斷任務,如果你的電腦還沒有配置相關環境的可以去使用?Colaboratory - Colab,里面已經全部幫你配置好啦!而且還可以免費使用GPU。

一、準備階段

1.導入模塊

導入所需的預訓練相關模塊,包括用于詞元化的 BertTokenizer、用于配置 BERT 模型的 BertConfig,還有 Adam 優化器(AdamW),以及序列分類模塊(BertFo SequenceClassification):

import torch
import torch.nn as nn
from torch.utils.data import TensorDataset,DataLoader,RandomSampler,SequentialSampler
from sklearn.model_selection import train_test_split
from transformers import BertTokenizer,BertConfig
from transformers import BertForSequenceClassification,get_linear_schedule_with_warmup
# from transformers import AdamW
from torch.optim import AdamW
from tqdm import tqdm,trange
import pandas as pd
import io
import numpy as np
import matplotlib.pyplot as plt# 導入進度條
from tqdm import tqdm,trange
# 導入常用的標注python模塊
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

2.指定使用的是GPU還是CPU

使用GPU加速對我們的訓練非常又有幫助

device = torch.device("cuda" if torch.cuda.is_avsilable() else "cpu")

3.加載數據集

這里的代碼和數據是參考https://github.com/Denis2054/Transformers-for-NLP-2nd-Edition/tree/main/Chapter03

使用git倉庫拉取一下就可以得到數據了

df=pd.read_csv("你拉取的數據in_domain_train.tsv的路徑",delimiter='\t',header=None,names=['sentence_source','label','label_notes','sentence'])
# 展示數據維度
df.shape  # (8551,4)

隨機抽取十個樣本數據的看看:

df.sample(10)

?以看到數據集中的數據包含了以下四列(即.tsv文件中四個用制表符分隔的列)。

● 第1列:句子來源(用編號表示)

● 第2列:標注(0=不可接受,1= 可接受)

● 第3列:作者的標注

● 第4列;要分類的句子

二、對數據添加詞元和分詞

1.根據BERT的預訓練,我們要將一個句子的句頭添加[CLS]句尾添加[SEP]

代碼解釋:將數據中的需要分類的句子提取為sentences,循環出每個句子,在每個句子的句頭添加[CLS]句尾添加[SEP],將數據中的標簽提取為labels

sentences=df.sentence.values
sentences=["[CLS]"+ sentence+"[SEP]" for sentence in sentences]
labels=df.label.values

2.激活BERT詞元分析器

這里是初始化一個預訓練BERT詞元分析器。相比與從頭開始訓練一個詞元分析器相對,節省很多時間和資源。們選擇了一個不區分大小寫的詞元分析器,激活它,并展示對第一個句子詞元 化之后的結果:

代碼講解:tokenizer是我們初始化的詞元分析器,BertTokenizer.from_pretrained('bert-base-uncased')是使用BERT中自帶的預訓練好的參數,關于BertTokenizer.from_pretrained可以去看我的另外一章博客BertTokenizer.from_pretreined。

? ? ? ? tokenizer_texts是已經每個句子詞元分析好的一個迭代器,因為sentences中保存的句子的type為array類型,而tokenize中要傳入的是字符串類型,所以這里要強轉一下

詞元分析后的一條句子為:Tokenize the first sentence: ['[CLS]', 'our', 'friends', 'wo', 'n', "'", 't', 'buy', 'this', 'analysis', ',', 'let', 'alone', 'the', 'next', 'one', 'we', 'propose', '.', '[SEP]']

tokenizer=BertTokenizer.from_pretrained('bert-base-uncased')
tokenizer_texts=[tokenizer.tokenize(str(sent)) for sent in sentences]
print("Tokenize the first sentence: ")
print(tokenizer_texts[0])
# Tokenize the first sentence: 
['[CLS]', 'our', 'friends', 'wo', 'n', "'", 't', 'buy', 'this', 'analysis', ',', 'let', 'alone', 'the', 'next', 'one', 'we', 'propose', '.', '[SEP]']

3.填充句子為固定長度

上面的處理中我們不難想到,每個句子分析后的長度會隨句子的大小而改變,而在BERT微調的時候需要保證句子的長度應用,所以我們要將長度不夠的句子進行填充,我們將這個最大長度設置為128,對于長度超過128的數據我們將它截斷,保證每個句子序列的大小都為128

代碼解釋:

input_ids:里面保存的是將上面的句子分詞后的詞元列表轉化成對應數字的列表,其中將詞元一個個的循環出來后經過tokenizer.convert_tokens_to_ids,它能把分詞后的詞元轉化為整數 ID,從而讓深度學習模型能夠處理文本數據。在不同的庫中,其使用方式可能會有所不同,但核心功能是一致的。

第二個input_ids:保存的是將每個詞元序列轉化成相同大小后的迭代器,pad_sequences函數的主要作用是將多個序列填充或截斷至相同的長度,這在處理序列數據(像文本序列)時十分關鍵,因為神經網絡通常要求輸入數據具有統一的形狀

from tensorflow.keras.preprocessing.sequence import pad_sequences
MAX_LEN=128
input_ids=[tokenizer.convert_tokens_to_ids(x) for x in tokenizer_texts]
print(input_ids[0])
input_ids=pad_sequences(input_ids,maxlen=MAX_LEN,dtype='long',truncating='post',padding='post')
print(input_ids[0])

三、數據處理

1.創建masks掩碼矩陣

如果不知道為什么需要掩碼的可以去看看transformers架構

為了防止模型對填充詞元進行注意力計算,我們在前面的步驟中對序列進行了填充補齊。但是我們希望防止模型對這些填充 的詞元進行注意力計算!首先創建一個空的 attention_masks 列表,用于存儲每個序列的注意力掩碼。然后, 對于輸入序列(input_ids)中的每個序列(seq),我們遍歷其中的每個詞元。

針對每個詞元,我們判斷其索引是否大于 0。如果大于 0,則將對應位置的掩碼 值設置為1,表示該詞元是有效詞元。如果等于0,則將對應位置的掩碼值設置為0, 表示該詞元是填充詞元。最終得到的 attention_masks 列表中的每個元素都是一個與對應輸入序列長度相同的 列表,其中每個位置的掩碼值表示該位置的詞元是否有效(1表示有效,0表示填充)。

通過使用注意力掩碼,可確保在模型的注意力計算中,只有真實的詞元會被考慮, 而填充詞元則被忽略。這樣可提高計算效率,并減少模型學習無用信息的概率。

代碼解釋:

attention_masks是保存所有詞元掩碼的列表,上面我們說到input_ids保存的是將每個詞元序列轉化成相同大小后的迭代器,我們將里面的每個詞元序列遍歷為seq,判斷seq中的值是否大于0,如果大于0,那么它是有效詞元,將他對應的掩碼設置為1,反正為0

attention_masks=[]
for seq in input_ids:seq_mask=[float(i>0) for i in seq]attention_masks.append(seq_mask)
print(attention_masks[0])
"""[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]"""

2.拆分數據集

將數據拆分成訓練集和驗證集,訓練集和測試集的比例為9:1

# 拆分訓練集和驗證集 (90%訓練,10%驗證)
train_inputs, val_inputs, train_labels, val_labels = train_test_split(input_ids, labels, test_size=0.1, random_state=2025)
train_masks, val_masks, _, _ = train_test_split(attention_masks, labels, test_size=0.1, random_state=2025)  

3.將所有的數據轉換為torch張量

微調模型需要使用 torch 張量,所以我們需要將數據轉換為 torch 張量:

train_inputs = torch.tensor(train_inputs)validation_inputs = torch.tensor(validation_inputs)txain_labels=torch.tensor(train_labels)validation_labels = torch.tensor(validation_labels)train_masks = torch.tensor(train_masks)validation_masks = torch.tensor(validation_masks)

4.選擇批量大小并創建迭代器

如果一股腦地將所有數據都喂進機器,會導致機器因為內存不足而崩潰。所以需 要將數據一批一批地喂給機器。這里將把批量大小(batch size)設置為 32 并創建迭代 器。然后將迭代器與 torch的 DataLoader 相結合,以批量訓練大量數據集,以免導致 機器因為內存不足而崩潰:

代碼解釋:

這里我們選的批量大小(batch_size)為32,使用TensorDataset和DataLoader創建訓練數據迭代器,如果對TensorDataset和DataLoader不清楚的可以去看看我的另外一篇博客:TensorData和DataLoader

RandomSampler 是一種隨機采樣器,從給定的數據集中隨機抽取樣本,可選擇有放回或無放回采樣。無放回采樣時,從打亂的數據集里抽取樣本;有放回采樣時,可指定抽取的樣本數量?num_samples

batch_size=32
# 訓練數據迭代器
train_data=TensorDataset(train_inputs,train_masks,train_labels)
train_sampler=RandomSampler(train_data)
train_dataloader=DataLoader(train_data,sampler=train_sampler,batch_size=batch_size)
# 測試數據迭代器validation_data=TensorDataset(validation_inputs,validation_masks,validation_label)
validation_sampler=RandomSampler(train_data)
validation_dataloader=DataLoader(validation_data,sampler=validation_sampler,batch_size=batch_size)

四、BERT模型配置

1.初始化一個不區分大小寫的 BERT 配置:

代碼解釋:

后面的代碼尋妖用到transformers這個包,如果沒有的pip安裝一下。

configuration是初始化了一個包含BERT預訓練模型中所有超參數的配置實例,如果BertConfig中不加任何的參數,那么會生成一個標準的BERT-base配置:

{"hidden_size": 768,          # 每個Transformer層的維度"num_hidden_layers": 12,     # Transformer層數(深度)"num_attention_heads": 12,    # 注意力頭的數量"intermediate_size": 3072,   # FeedForward層的中間維度"vocab_size": 30522,         # 詞表大小(需與預訓練模型一致)"max_position_embeddings": 512, # 最大序列長度...
}

?model:BertModel是根據配置生成一個隨機初始化權重的BERT模型,根據傳入的配置信息生成。

configuration是一個保存模型內部存儲的配置信息副本

try:import transformers
except:print("installing transformers")
from transformers import BertModel,BertConfig
configuration=BertConfig()model=BertModel(config=configuration)
configuration=model.config
print(configuration)
"""
輸出為:
BertConfig {"_attn_implementation_autoset": true,"attention_probs_dropout_prob": 0.1,"classifier_dropout": null,"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,"pad_token_id": 0,"position_embedding_type": "absolute","transformers_version": "4.50.0","type_vocab_size": 2,"use_cache": true,"vocab_size": 30522
}
"""

2.這些配置參數的作用:

"""

attention probs_dropout_prob:對注意力概率應用的 dropout 率,這里設置為

0.1。

● hidden_act;編碼器中的非線性激活函數,這里使用 gelu。gelu 是高斯誤差線

性單位(Gaussian Eror Linear Units)激活函數的簡稱,它對輸入按幅度加權,

使其成為非線性。

● hidden_dropout_prob:應用于全連接層的 dropout 概率。嵌入、編碼器和匯聚

器層中都有全連接。輸出不總是對序列內容的良好反映。匯聚隱藏狀態的序

第3章 微調BERT 模型

列可改善輸出序列。這里設置為0.1。

● hidden_size:編碼器層的維度,也是匯聚層的維度,這里設置為768。

● initializer_range:初始化權重矩陣時的標準偏差值,這里設置為0.02。

· intermediate_size:編碼器前饋層的維度,這里設置為3072。

● layer_norm_eps:是層規范化層的 epsilon 值,這里設置為le-12。

● max_position_embeddings:模型使用的最大長度,這里設置為512。

● model_type:模型的名稱,這里設置為 bert。

● numattention_heads:注意力頭數,這里設置為12。

· num_hidden_layers:層數,這里設置為12。

● pad_tokenid:使用0作為填充詞元的HD,以避免對填充詞元進行訓練。

57

?· type_vocab_size:token_type_ids的大小用于標識序列。例如,“the dog[SEP]

?The cat.[SEP]”可用詞元 ID [0,0,0,1,1,1]表示。

· vocab_size:模型用于表示 input_ids 的不同詞元數量。換句話說,這是模型

可以識別和處理的不同詞元或單詞的總數。在訓練過程中,模型會根據給定

的詞表將文本輸入轉換為對應的詞元序列,其中包含的詞元數量是

vocab_size。通過使用這個詞表,模型能夠理解和表示更廣泛的語言特征。這

里設置為 30522。

講解完這些參數后,接下來將加載預訓練模型。

"""

3.加載模型

現在開始加載預訓練BERT模型

BertForSequenceClassification.from_pretrained?能夠讓你加載預訓練的 BERT 模型權重,并且可以根據需求調整模型以適應特定的序列分類任務。這個方法非常實用,因為借助預訓練的權重,模型通常能更快收斂,并且在特定任務上表現更優。第一個參數bert-base-uncased意思是加載BERT的默認權重,如果你有別的模型權重可以填寫它的名字或者路徑;nums_labels:表示你這個任務中的類別數,我們這個任務的label只有兩種,所以這里是2。

DataParallel:DataParallel 是一種數據并行的實現方式,其核心思想是將大規模的數據集分割成若干個較小的數據子集,然后將這些子集分配到不同的計算節點(如 GPU)上,每個節點運行相同的模型副本,但處理不同的數據子集。在每一輪訓練結束后,各節點會將計算得到的梯度進行匯總,并更新模型參數。如果不知道分布式計算的可以去看看我的另外一篇博客如何在多個GPU上訓練

model=BertForSequenceClassification.from_pretrained("bert-base-uncased",num_labels=2)
model=nn.DataParallel(model)
model.to(device)

4.優化器分組參數

在將為模型的參數初始化優化器。在進行模型微調的過程中,首先需要初始化 預訓練模型已學到的參數值。 微調一個預訓練模型時,通常會使用之前在大規模數據上訓練好的模型作為初始 模型。這些預訓練模型已通過大量數據和計算資源進行了訓練,學到了很多有用的特 征表示和參數權重。因此,我們希望在微調過程中保留這些已經學到的參數值,而不 是重新隨機初始化它們。 所以,程序會使用預訓練模型的參數值來初始化優化器,以便在微調過程中更好 地利用這些已經學到的參數。這樣可以加快模型收斂速度并提高微調效果;

代碼解釋:

這段代碼是用與為BERT模型的參數設置差異化的權重衰減策略,是訓練Transformer模型時的常用技巧

param_optimizer是以字典的方式保存模型中所有可訓練參數的名稱和值,named_parameters()函數是獲取模型中所有的參數名稱和值。

no_decay是定義無需權重衰減的參數類型,權重衰減對偏置項bias和歸一化層的weight無益,bias可能破壞模型對稱性,LayerNorm的weight需保持靈活性,正則化會抑制其適應性。

optimizer_grouped_parametes: 組1:分組設置優化策略,篩選出參數名稱中不包含bias和LayerNorm.weight的參數然后將權重衰減率設為0.1。組2:禁止權重衰減的參數,篩選出參數名稱中包含bias和LayerNorm.weight的參數將權重衰減率設為0.0

param_optimizer=list(model.named_parameters())no_decay=['bias','LayerNorm.weight']
optimizer_grouped_parametes=[{'params':[p for n,p in param_optimizer if not any(nd in n for nd in no_decay)],'weight_decay_rate':0.1},{"params":[p for n,p in param_optimizer if any(nd in n for nd in no_decay)],'weight_decay_rate':0.0}
]

5.訓練循環的超參數

訓練循環中的超參數非常重要,盡管它們看起來可能無害。例如,Adam 優化器 會激活權重衰減并經歷一個預熱階段。學習率(lr)和預熱率(warnup)應該在優化階段的早期設置為一個非常小的值,在一 定迭代次數后逐漸增加。這樣可以避免出現過大的梯度和超調問題,以更好地優化模 型目標。

代碼解釋:

使用AdamW優化器,將模型的參數傳入,并設置初始學習率為2e-5

定義一個函數calculate_accuracy來度量準確率,用于測試結果與標注進行比較,向函數中傳入預測結果的概率分布和真實標簽,pred_flat是查找這個概率分布中的最大概率的索引,flatten是將數組一維化,labels_flat是真實結果的一維化。最后,計算預測結果展平后的數組中與展平后的標簽數組相等的元素數量占標簽數組長度的比例,并將這個比例作為結果返回。

optimizer=AdamW(optimizer_grouped_parametes,lr=2e-5)
def calculate_accuracy(preds, labels):"""計算準確率的優化版本"""preds = np.argmax(preds, axis=1).flatten()labels = labels.flatten()return np.sum(preds == labels) / len(labels)

五、訓練循環

我們的訓練循環將遵循標準的學習過程。輪數(epochs)設置為 4,并將繪制損失和 準確率的度量值。訓練循環使用 dataloader 來加載和訓練批量。我們將對訓練過程進 行度量和評估。 首先初始化 train_loss_set(用于存儲損失和準確率的數值,以便后續繪圖)。然后 開始訓練每一輪,并運行標準的訓練循環,

代碼解釋:

這段代碼實現了BERT模型的完整訓練和驗證流程,包含以下核心步驟:

  1. 初始化訓練記錄容器
  2. 循環訓練多個epoch
  3. 每個epoch包含訓練階段和驗證階段
  4. 記錄并輸出訓練指標

train_loss_history:? 儲存每個epoch的平均訓練損失

val_accuracy_history:存儲每個epoch的驗證集準確率

代碼太多了,大家在代碼中看注釋吧,這里主要說一下訓練步驟

1.初始化記錄容器

2.設置外層epoch(循環次數)循環

3.設置model為訓練模式

4.將數據挨個前向傳播和反向傳播更新參數

5.計算平均訓練損失

6.將model設置為評估階段并進行評估

7.計算平均驗證準確率

8.打印訓練信息

train_loss_history = []  # 存儲每個epoch的平均訓練損失
val_accuracy_history = []  # 存儲每個epoch的驗證集準確率for epoch_i in trange(epochs, desc="Epoch"):# ========== 訓練階段 ==========model.train()  # 設置模型為訓練模式total_train_loss = 0  # 初始化累計損失for batch in train_dataloader:# 數據轉移到GPUb_input_ids, b_input_mask, b_labels = tuple(t.to(device) for t in batch)# 梯度清零model.zero_grad()# 前向傳播outputs = model(b_input_ids,attention_mask=b_input_mask,labels=b_labels)# 多GPU處理:取平均損失loss = outputs.loss.mean()# 反向傳播loss.backward()"""在深度學習訓練時,梯度可能會變得非常大,這會導致訓練不穩定,甚至引發梯度爆炸的問題。torch.nn.utils.clip_grad_norm_ 函數通過對梯度的范數進行裁剪,避免梯度變得過大,從而讓訓練過程更加穩定。"""torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)  # 梯度裁剪# 參數更新optimizer.step()scheduler.step()total_train_loss += loss.item()# 記錄平均訓練損失avg_train_loss = total_train_loss / len(train_dataloader)train_loss_history.append(avg_train_loss)  # 記錄歷史損失# ========== 驗證階段 ==========model.eval()total_eval_accuracy = 0model.eval()  # 設置模型為評估模式total_eval_accuracy = 0  # 初始化累計準確率with torch.no_grad():  # 禁用梯度計算for batch in val_dataloader:b_input_ids, b_input_mask, b_labels = tuple(t.to(device) for t in         batch)# 前向傳播outputs = model(b_input_ids, attention_mask=b_input_mask)# .logits是將model中還沒有經過激活函數的值提取出來,因為驗證不需要激活# 這樣節省了顯存,.detach將張量從計算圖中分離,?斷開梯度追蹤。因為驗證階段不需要計算梯度,這一步可以節省內存并避免不必要的計算。# .cpu():如果張量在GPU上(例如通過.to('cuda')加載),這一步會將其移動到CPU內存中。NumPy無法直接處理GPU上的張量,必須轉移到CPU。logits = outputs.logits.detach().cpu().numpy()label_ids = b_labels.to('cpu').numpy()total_eval_accuracy += calculate_accuracy(logits, label_ids)avg_val_accuracy = total_eval_accuracy / len(val_dataloader)val_accuracy_history.append(avg_val_accuracy)# 打印訓練信息print(f"\nEpoch {epoch_i + 1}/{epochs}")print(f"Train loss: {avg_train_loss:.4f}")print(f"Validation Accuracy: {avg_val_accuracy:.4f}")

訓練圖解:

graph TDA[開始訓練] --> B[設置訓練模式]B --> C[遍歷訓練數據]C --> D[數據轉GPU]D --> E[梯度清零]E --> F[前向傳播]F --> G[計算損失]G --> H[反向傳播]H --> I[梯度裁剪]I --> J[參數更新]J --> K[學習率更新]K --> CC --> L[計算平均損失]L --> M[驗證模式]M --> N[遍歷驗證數據]N --> O[前向傳播]O --> P[計算準確率]P --> Q[計算平均準確率]Q --> R[記錄結果]R --> S[打印信息]S --> T[完成epoch?]T --是--> U[結束訓練]T --否--> B

?六、使用測試數據集進行預測和評估

們使用了in_domain_traintsv 數據集訓練 BERT下游模型。現在我們將使用基 于留出法!分出的測試數據集 outof_domain_dev.v 文件進行預測。我們的目標是預 測句子在語法上是否正確。 以下代碼展示了測試數據準備過程:

# 加載測試數據
test_df = pd.read_csv("out_of_domain_dev.tsv", delimiter='\t', header=None,names=['sentence_source', 'label', 'label_notes', 'sentence'])# 預處理測試數據
test_input_ids, test_attention_masks, test_labels = preprocess_data(test_df, tokenizer, max_len=128)# 創建預測DataLoader
prediction_dataset = TensorDataset(test_input_ids, test_attention_masks, test_labels)
prediction_dataloader = DataLoader(prediction_dataset, sampler=SequentialSampler(prediction_dataset),batch_size=batch_size)# 初始化存儲
predictions = []
true_labels = []model.eval()
for batch in prediction_dataloader:batch = tuple(t.to(device) for t in batch)b_input_ids, b_input_mask, b_labels = batchwith torch.no_grad():outputs = model(b_input_ids, attention_mask=b_input_mask)logits = outputs.logits.detach().cpu().numpy()label_ids = b_labels.cpu().numpy()predictions.append(logits)true_labels.append(label_ids)# 計算準確率
flat_predictions = np.concatenate(predictions, axis=0)
flat_predictions = np.argmax(flat_predictions, axis=1)
flat_true_labels = np.concatenate(true_labels, axis=0)accuracy = np.sum(flat_predictions == flat_true_labels) / len(flat_true_labels)
print(f"Test Accuracy: {accuracy:.4f}")

七、使用馬修斯相關系數(MCC)評估

?1、MCC的核心原理與優勢

馬修斯相關系數(Matthews Correlation Coefficient, MCC)是一種綜合評估二分類模型性能的指標,尤其適用于類別不平衡數據集。其優勢包括:

  1. ?全面性:同時考慮真陽性(TP)、真陰性(TN)、假陽性(FP)、假陰性(FN)。、
  2. ?魯棒性:在類別分布不均衡時仍能準確反映模型性能(例如醫學診斷中的罕見病檢測)。
  3. ?可解釋性:取值范圍為[-1, 1],1表示完美預測,0表示隨機猜測,-1表示完全錯誤。

計算公式:

2. 代碼實現:

1. 測試數據預處理與預測
# 加載測試數據(示例路徑需替換為實際路徑)
test_df = pd.read_csv("out_of_domain_dev.tsv", delimiter='\t', header=None,names=['sentence_source', 'label', 'label_notes', 'sentence'])# 預處理(復用preprocess_data函數)
test_input_ids, test_attention_masks, test_labels = preprocess_data(test_df, tokenizer, max_len=128)# 創建DataLoader
prediction_dataset = TensorDataset(test_input_ids, test_attention_masks, test_labels)
prediction_dataloader = DataLoader(prediction_dataset, sampler=SequentialSampler(prediction_dataset), batch_size=batch_size)
2.模型預測與結果收集

?

# 初始化存儲
predictions = []
true_labels = []model.eval()
for batch in prediction_dataloader:batch = tuple(t.to(device) for t in batch)b_input_ids, b_input_mask, b_labels = batchwith torch.no_grad():outputs = model(b_input_ids, attention_mask=b_input_mask)logits = outputs.logits.detach().cpu().numpy()label_ids = b_labels.cpu().numpy()predictions.append(logits)true_labels.append(label_ids)# 合并結果
flat_predictions = np.concatenate(predictions, axis=0)
flat_predictions = np.argmax(flat_predictions, axis=1)  # 將logits轉為類別(0/1)
flat_true_labels = np.concatenate(true_labels, axis=0)
3.計算MCC
from sklearn.metrics import matthews_corrcoefmcc = matthews_corrcoef(flat_true_labels, flat_predictions)
print(f"Test MCC: {mcc:.4f}")

到這里就完美收官咯!!!!! 大家點個贊吧!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/75089.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75089.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/75089.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

10組時尚復古美學自然冷色調肖像電影照片調色Lightroom預設 De La Mer – Nautical Lightroom Presets

De La Mer 預設系列包含 10 種真實的調色預設,適用于肖像、時尚和美術。為您的肖像攝影帶來電影美學和個性! De La Mer 預設非常適合專業人士和業余愛好者,可在桌面或移動設備上使用,為您的攝影項目提供輕松的工作流程。這套包括…

SDL多窗口多線程渲染技術解析

SDL多窗口多線程渲染技術解析 技術原理 SDL多線程模型與窗口管理 SDL通過SDL_Thread結構體實現跨平臺線程管理。在多窗口場景中,每個窗口需關聯獨立的渲染器,且建議遵循以下原則: 窗口與渲染器綁定:每個窗口創建時生成專屬渲染器(SDL_CreateRenderer),避免跨線程操作…

QT 跨平臺發布指南

一、Windows 平臺發布 1. 使用 windeployqt 工具 windeployqt --release --no-compiler-runtime your_app.exe 2. 需要包含的文件 應用程序 .exe 文件 Qt5Core.dll, Qt5Gui.dll, Qt5Widgets.dll 等 Qt 庫 platforms/qwindows.dll 插件 styles/qwindowsvistastyle.dll (如果使…

L2-037 包裝機 (分數25)(詳解)

題目鏈接——L2-037 包裝機 問題分析 這個題目就是模擬了物品在傳送帶和筐之間的傳送過程。傳送帶用隊列模擬,筐用棧模擬。 輸入 3 4 4 GPLT PATA OMSA 3 2 3 0 1 2 0 2 2 0 -1輸出 根據上述操作,輸出的物品順序是: MATA樣例分析 初始…

機器學習的一百個概念(4)下采樣

前言 本文隸屬于專欄《機器學習的一百個概念》,該專欄為筆者原創,引用請注明來源,不足和錯誤之處請在評論區幫忙指出,謝謝! 本專欄目錄結構和參考文獻請見[《機器學習的一百個概念》 ima 知識庫 知識庫廣場搜索&…

qt6下配置qopengl

qt部件選擇 Qt 6:需要手動選擇 Qt Shader Tools 和 Qt 5 Compatibility Module(如果需要兼容舊代碼) cmake文件 cmake_minimum_required(VERSION 3.16) # Qt6 推薦最低 CMake 3.16 project(myself VERSION 0.1 LANGUAGES CXX)set(CMAKE_A…

數據安全系列4:密碼技術的應用-接口調用的身份識別

傳送門 數據安全系列1:開篇 數據安全系列2:單向散列函數概念 數據安全系列3:密碼技術概述 什么是認證? 一談到認證,多數人的反應可能就是"用戶認證" 。就是應用系統如何識別用戶的身份,直接…

STL之map和set

1. 關聯式容器 vector、list、deque、 forward_list(C11)等,這些容器統稱為序列式容器,因為其底層為線性序列的數據結構,里面存儲的是元素本身。 關聯式容器也是用來存儲數據的,與序列式容器不同的是,其里面存儲的是結…

Vue3 其它API Teleport 傳送門

Vue3 其它API Teleport 傳送門 在定義一個模態框時,父組件的filter屬性會影響子組件的position屬性,導致模態框定位錯誤使用Teleport解決這個問題把模態框代碼傳送到body標簽下

C++練習

1.將File練習題&#xff0c;內部的FILE*描述符&#xff0c;改成int描述符 2。寫一個類Fifo管道類。提高難度&#xff0c;什么都不提示。只要求&#xff1a;使用自己編寫的Fifo類對象&#xff0c;實現2個終端之間互相聊天 file.cpp #include <iostream> #include <c…

《Python Web網站部署應知應會》No4:基于Flask的調用AI大模型的高性能博客網站的設計思路和實戰(上)

基于Flask的調用AI大模型的高性能博客網站的設計思路和實戰&#xff08;上&#xff09; 摘要 本文詳細探討了一個基于Flask框架的高性能博客系統的設計與實現&#xff0c;該系統集成了本地AI大模型生成內容的功能。我們重點關注如何在高并發、高負載狀態下保持系統的高性能和…

實現一個簡易版的前端監控 SDK

【簡易版的前端監控系統】 1、Promise的錯誤如何監控&#xff1f;–promise不是所有都是接口請求 2、接口的報錯如何監控&#xff1f;–全局監控sdk&#xff0c;不改動公共的請求方法、不改動業務代碼&#xff1b;一般接口使用axios請求 3、資源的報錯如何監控&#xff1f; 4、…

【操作系統】軟中斷vs硬中斷

在操作系統中&#xff0c;中斷&#xff08;Interrupt&#xff09; 是 CPU 響應外部事件的重要機制&#xff0c;分為 硬中斷&#xff08;Hardware Interrupt&#xff09; 和 軟中斷&#xff08;Software Interrupt&#xff09;。它們的核心區別在于 觸發方式 和 處理機制。 1. 硬…

力扣刷題-熱題100題-第27題(c++、python)

21. 合并兩個有序鏈表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常規法 創建一個新鏈表&#xff0c;遍歷list1與list2&#xff0c;將新鏈表指向list1與list2…

Python包下載路徑 Chrome用戶數據 修改到非C盤

查看 site-packages 是否能通過命令行完成&#xff1f; 可以&#xff0c;使用以下命令&#xff08;不需寫腳本&#xff09;&#xff1a; python -m site輸出包含&#xff1a; sys.path site-packages 路徑&#xff08;全局和用戶級&#xff09; 如果只想看安裝路徑&#…

【鴻蒙5.0】鴻蒙登錄界面 web嵌入(隱私頁面加載)

在鴻蒙應用中嵌入 Web 頁面并加載隱私頁面&#xff0c;可借助 WebView 組件來實現。以下是一個完整示例&#xff0c;展示如何在鴻蒙 ArkTS 里嵌入 Web 頁面并加載隱私政策頁面。 在 HarmonyOS 應用開發中&#xff0c;如果你希望嵌入一個網頁&#xff0c;并且特別關注隱私頁面加…

AI加Python的文本數據情感分析流程效果展示與代碼實現

本文所使用數據來自于梯田景區評價數據。 一、數據預處理 數據清洗 去除重復值、空值及無關字符(如表情符號、特殊符號等)。 提取中文文本,過濾非中文字符。 統一文本格式(如全角轉半角、繁體轉簡體)。 中文分詞與去停用詞 使用 jieba 分詞工具進行分詞。 加載自定義詞…

Microi吾碼界面設計引擎之基礎組件用法大全【內置組件篇·上】

&#x1f380;&#x1f380;&#x1f380; microi-pageengine 界面引擎系列 &#x1f380;&#x1f380;&#x1f380; 一、Microi吾碼&#xff1a;一款高效、靈活的低代碼開發開源框架【低代碼框架】 二、Vue3項目快速集成界面引擎 三、Vue3 界面設計插件 microi-pageengine …

【多線程】單例模式和阻塞隊列

目錄 一.單例模式 1. 餓漢模式 2. 懶漢模式 二.阻塞隊列 1. 阻塞隊列的概念 2. BlockingQueue接口 3.生產者-消費者模型 4.模擬生產者-消費者模型 一.單例模式 單例模式&#xff08;Singleton Pattern&#xff09;是一種常用的軟件設計模式&#xff0c;其核心思想是確保…

終值定理的推導與理解

終值定理的推導與理解 終值定理是控制理論和信號處理中的一個重要工具&#xff0c;它通過頻域的拉普拉斯變換來分析時間域函數的最終穩態值。具體來說&#xff0c;終值定理提供了一個簡便的方法&#xff0c;利用 F ( s ) F(s) F(s)&#xff08; f ( t ) f(t) f(t) 的拉普拉斯…