BERT 微調與傳統機器學習的區別和聯系:
傳統機器學習流程
傳統機器學習處理文本分類通常包含以下步驟:
- 特征工程:手動設計特征(如 TF-IDF、詞袋模型)
- 模型訓練:使用分類器(如 SVM、隨機森林、邏輯回歸)
- 特征和模型調優:反復調整特征和超參數
BERT 微調流程
BERT 微調的典型流程:
- 預訓練:使用大規模無標注數據預訓練 BERT 模型
- 數據準備:將文本轉換為 BERT 輸入格式(tokenize、添加特殊標記)
- 模型微調:凍結大部分 BERT 層,只訓練分類頭(或少量 BERT 層)
- 評估與部署:在驗證集上評估,保存模型
兩者的主要區別
對比項 | 傳統機器學習 | BERT 微調 |
---|---|---|
特征表示 | 手動設計特征(如 TF-IDF) | 自動學習上下文相關表示 |
模型復雜度 | 簡單到中等(如 SVM、RF) | 非常復雜(Transformer 架構) |
數據依賴 | 需要大量標注數據 | 可以用較少數據達到好效果 |
領域適應性 | 遷移到新領域需要重新設計特征 | 可以快速適應新領域(通過微調) |
計算資源 | 通常較低 | 需要 GPU/TPU |
您代碼中的 BERT 微調關鍵點
-
數據預處理:
- 使用
BertTokenizer
將文本轉換為 token IDs - 添加特殊標記([CLS]、[SEP])
- 填充和截斷到固定長度
- 使用
-
模型架構:
- 基礎模型:BERT 預訓練模型(bert-base-chinese)
- 分類頭:在 BERT 頂部添加一個全連接層(
num_labels=6
) - 微調策略:更新整個模型的權重
-
訓練優化:
- 使用
AdamW
優化器(帶權重衰減的 Adam) - 小學習率(2e-5)避免災難性遺忘
- 批量訓練(batch_size=2)處理長序列
- 使用
-
優勢:
- 利用預訓練模型捕獲的語言知識
- 自動學習文本的上下文表示
- 對訓練數據量要求較低
- 遷移到新領域更容易
何時選擇 BERT 微調而非傳統方法?
- 當您有足夠的計算資源時
- 當任務數據量有限時
- 當需要處理復雜語義理解時
- 當需要快速適應新領域時
BERT 微調入門示例,展示了如何將預訓練語言模型應用于特定的分類任務。隨著 Transformer 架構的普及,這種方法已經成為 NLP 任務的主流解決方案。
import torch
from torch.utils.data import Dataset, DataLoader
from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd# 1. 準備數據
data = {'text': ["我想預訂明天的機票", "查詢今天的天氣", "幫我設置鬧鐘","播放周杰倫的歌曲", "今天有什么新聞", "推薦幾部科幻電影"],'label': [0, 1, 2, 3, 4, 5] # 0:訂票, 1:天氣, 2:鬧鐘, 3:音樂, 4:新聞, 5:電影
}
df = pd.DataFrame(data)# 2. 數據集劃分
train_df, val_df = train_test_split(df, test_size=0.2, random_state=42)# 3. 創建數據集類
class TextClassificationDataset(Dataset):def __init__(self, texts, labels, tokenizer, max_len=128):self.texts = textsself.labels = labelsself.tokenizer = tokenizerself.max_len = max_lendef __len__(self):return len(self.texts)def __getitem__(self, idx):text = str(self.texts[idx])label = self.labels[idx]encoding = self.tokenizer(text,add_special_tokens=True,max_length=self.max_len,return_token_type_ids=False,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')return {'input_ids': encoding['input_ids'].flatten(),'attention_mask': encoding['attention_mask'].flatten(),'label': torch.tensor(label, dtype=torch.long)}# 4. 初始化tokenizer和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese',num_labels=6
)# 5. 創建數據加載器
train_dataset = TextClassificationDataset(train_df['text'].values,train_df['label'].values,tokenizer
)
val_dataset = TextClassificationDataset(val_df['text'].values,val_df['label'].values,tokenizer
)train_loader = DataLoader(train_dataset, batch_size=2, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=2)# 6. 訓練模型
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)optimizer = AdamW(model.parameters(), lr=2e-5)
epochs = 3for epoch in range(epochs):model.train()train_loss = 0for batch in train_loader:input_ids = batch['input_ids'].to(device)attention_mask = batch['attention_mask'].to(device)labels = batch['label'].to(device)optimizer.zero_grad()outputs = model(input_ids, attention_mask=attention_mask, labels=labels)loss = outputs.losstrain_loss += loss.item()loss.backward()optimizer.step()avg_train_loss = train_loss / len(train_loader)print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_train_loss:.4f}")# 7. 評估模型
model.eval()
predictions = []
true_labels = []with torch.no_grad():for batch in val_loader:input_ids = batch['input_ids'].to(device)attention_mask = batch['attention_mask'].to(device)labels = batch['label'].to(device)outputs = model(input_ids, attention_mask=attention_mask)preds = torch.argmax(outputs.logits, dim=1)predictions.extend(preds.cpu().numpy())true_labels.extend(labels.cpu().numpy())accuracy = accuracy_score(true_labels, predictions)
print(f"Validation Accuracy: {accuracy:.4f}")# 8. 保存模型
model.save_pretrained('./intent_classifier')
tokenizer.save_pretrained('./intent_classifier')