- Transformers 核心庫: 模型加載 模型訓練
- Tokenizer 分詞器: 對數據進行預處理,將文本轉換成模型可以理解的格式(Token序列)
- Datasets 數據集庫: 下載公開的數據集,並且進行預處理
- Evaluate 評估函示: 像是準確率 F1, BLEU, ROUGE 等等
- PEFT 高校微調方法: 只訓練一小部分參數,而不是全部重新訓練,提供常用的微調方法
- Accelerate 分布式訓練: 對訓練過程進行加速,提供了分布式訓練的方法
- Optimum 優化加速庫: 提供了一些優化方法,比如混合精度訓練,對訓練過程進行加速
- Gradio 可視化庫: 提供了一些可視化的方法,透過幾行程式達到快速外部交互的效果
前置環境
需要安裝以下套件:
- python
- conda
- pytorch
- 以我的狀況為例,我是cuda支援的版本是12.4,這代表小於12.4的版本都可以支援cuda,建議使用pip進行安裝
- 30xx 或是 40xx 顯卡要安裝 cu11 以上的版本
最後測試是否安裝成功
1 2 3 4 5
| C:\Users\Name>python >>> import torch >>> torch.cuda.is_available() True >>>
|
確認cuda版本
- 進入 nvdia 控制面板
- 點選系統信息
- 查看 cuda 版本
執行以下指令安裝 transformers
1 2
| pip install transformers datasets evaluate peft accelerate gradio optimum sentencepiece pip install jupyterlab scikit-learn pandas matplotlib tensorboard nltk rouge
|
關於Pipeline
這是一個簡單的工具,可以幫助我們快速的使用模型進行預測,包含三個階段:
- Tokenization 數據處理: 將文本轉換成模型可以理解的格式(Token序列)
- Raw Text (
"This is apple"
) -> Tokenization -> Token IDs ([100, 101, 2708]
)
- Model 模型調用: 使用模型進行預測
- Post-processing 結果後處理: 將模型的輸出轉換成我們需要的格式
Pisitive: 99.89%, Negative: 0.11%
有很多不同 Pipeline 支援的任務,可以透過以下程式碼查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| from transformers.pipelines import SUPPORTED_TASKS for k, v in SUPPORTED_TASKS.items(): print(k, v["type"])
audio-classification | audio automatic-speech-recognition | multimodal text-to-audio | text feature-extraction | multimodal text-classification | text token-classification | text question-answering | text table-question-answering | text visual-question-answering | multimodal document-question-answering | multimodal fill-mask | text summarization | text translation | text text2text-generation | text text-generation | text zero-shot-classification | text zero-shot-image-classification | multimodal zero-shot-audio-classification | multimodal conversational | text image-classification | image image-feature-extraction | image image-segmentation | multimodal image-to-text | multimodal object-detection | multimodal zero-shot-object-detection | multimodal depth-estimation | image video-classification | video mask-generation | multimodal image-to-image | image
|
快速上手
1 2 3 4
| from transformers import *
pipe = pipeline("text-classification") pip("This is a good day")
|
使用指定模型
可以前往 https://huggingface.co/models 找到模型名稱建立相對應的Pipeline。
指定任務 -> 再指定模型 -> 創建基於指定模型的 Pipeline
1 2 3 4
| from transformers import *
pipe = pipeline("text-classification", model="uer/roberta-base-finetuned-dianping-chinese") pipe("今天是好天氣")
|
預先加載模型,再創建Pipeline
一定要使用模型相對應的 Tokenizer,否則會出現錯誤
1 2 3 4 5 6
| from transformers import *
model = AutoModelForSequenceClassification.from_pretrained("uer/roberta-base-finetuned-dianping-chinese") toeknizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese") pipe = pipeline("text-classification", model=model, tokenizer=tokenizer) pipe("今天是好天氣")
|
使用GPU
1 2 3 4
| from transformers import *
pipe = pipeline("text-classification") print(pipe.model.device)
|
關於Tokenizer
Tokenizer 是將文本轉換成模型可以理解的格式(Token序列),這是模型的第一步,因為模型只能理解數字,而不能理解文本。因此 Tokenizer 是非常重要的一個環節。
基本使用
- 加載保存
from_pretrained
: 從預訓練模型中加載 Tokenizer
save_pretrained
: 將 Tokenizer 保存到本地
- 句子分詞
tokenize
: 句子分詞
encode
: 句子轉換成 ID
- 查看字典
- 索引轉換
convert_tokens_to_ids
: 將 Token 轉換成 ID
convert_ids_to_tokens
: 將 ID 轉換成 Token
- 填充截斷
padding
: 對文本進行填充
truncation
: 對文本進行截斷
快速上手
- 直接使用 tokenizer(sen, padding=“max_length”, max_length=10) 可以會傳以下資訊
offset_mapping
: 會顯示每個 token 在原始文本中的位置,通常會合 word_ids 一起使用
- 會長這樣
[(0, 0), (0, 2), (2, 5), (5, 8), (8, 11), (0, 0)]
word_ids
: 會顯示每個 token 對應的原始文本中的單詞
- 會長這樣
[None, 0, 1, 2, 3, None]
input_ids
: 會顯示每個 token 對應的 ID
- 會長這樣
[101, 0, 1, 2, 3, 102]
attention_mask
: 會顯示每個 token 對應的注意力遮罩,哪些部分是有效的哪些是填充的
- 會長這樣
[1, 1, 1, 1, 1, 1, 0, 0]
token_type_ids
: 會顯示每個 token 對應的 token type
- 會長這樣
[0, 0, 0, 0, 0, 0, 0, 0]
add_special_tokens
: 是否加入特殊的 token,像是 [CLS], [SEP] 等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("user/tokenizer-name") tokenizer.save_pretrained("tokenizer-name")
tokenizer = toeknizer("今天是好天氣") print(tokenizer)
print(tokenizer.vocab) print(tokenizer.vocab_size)
ids = tokenizer.convert_tokens_to_ids("今天是好天氣") print(ids) str_sen = tokenizer.convert_ids_to_tokens([0, 1, 2, 3]) print(str_sen)
ids = tokenizer.encode("今天是好天氣", add_special_tokens=True) print(ids) str_sen = tokenizer.decode([101, 0, 1, 2, 3, 102]) print(str_sen)
ids = tokenizer.encode("今天是好天氣", padding="max_length", max_length=10) print(ids)
ids = tokenizer.encode("今天是好天氣", truncation=True, max_length=4) print(ids)
inputs = tokenizer("今天是好天氣", padding="max_length", max_length=10, return_offsets_mapping=True) print(inputs) inputs.word_ids()
sens = ["今天是好天氣", "明天是好天氣"] inputs = tokenizer(sens, padding="max_length", max_length=15) print(inputs)
|
特殊Tokenizer的加載
huggingface 上有很多模型不是官方的,有可能我們下載的模型,他的 Tokenizer 不在官方的 Tokenizer 中,這時候我們可以使用 trust_remote_code=True
來加載 Tokenizer,表示我們相信這個 Tokenizer 是安全的。以 https://huggingface.co/THUDM/chatglm-6b/tree/main 為例,他所使用的 Tokenizer 是 tokenization_chatglm.py
他自己實現的。
1 2 3 4 5 6 7
| from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
tokenizer.save_pretrained("chatglm-6b")
tokenizer = AutoTokenizer.from_pretrained("chatglm-6b", trust_remote_code=True)
|
關於 Model
模型的類型
- 只有 Encoder: 每個詞都能看到完整上下文
- 常用預訓練模型: BERT, RoBERTa, ALBERT
- 適合任務:文本分類、命名實體識別、閱讀理解
- 只有 Decoder: 每個詞能看到上文,但是不能看到下文
- 常用預訓練模型: GPT, LLaMA
- 適合任務:文本生成
- Encoder+Decoder: 前面的token只能看後面的,但是到了後面的token就是純粹的解碼器模型了
- 常用預訓練模型: MART, T5, GLM, Marian, mBART
- 適合任務:文本摘要, 翻譯
Model Head
- 通常是連接在模型後的層,通常為1個或多個fully connected layer
- 他負責將模型的輸出轉換成我們需要的格式,以解決不同類型的任務
- 常見的像是 ForMaskedLMHead (遮住某個字來猜), ForSeq2SeqLMHead (翻譯), ForQuestionAnsweringHead (問答), ForSequenceClassification (分類)
快速上手
這邊主要會介紹 Model 的基本使用方法
- 在線加載:
AutoModel.from_pretrained
- 模型下載
- 離線加載
- 模型參數下載
模型調用
- 不帶 model head 的模型調用
- 帶 model head 的模型調用
下載模型 AutoModel
方法1: 線上下載
直接透過 AutoModel.from_pretrained()
1 2 3
| from transformers import AutoConfig, AutoModel, AutoTokenizer
model = AutoModel.from_pretrained("hf1/rbt3")
|
方法2: 模型下載
或是可以透過git的方式,甚至是去huggingface自己下載.bin
檔案
1 2
| !git clone "https://huggingface.co/hf1/rbt3" !git lfs clone "https://huggingface.co/hf1/rbt3" --include="*.bin"
|
然後你就可以在本地加載了:
1 2 3
| from transformers import AutoConfig, AutoModel, AutoTokenizer
model = AutoModel.from_pretrained("本地資料夾位置")
|
模型參數加載 AutoConfig
可以透過AutoConfig查看模型相關參數
1 2 3 4
| from transformers import AutoConfig, AutoModel, AutoTokenizer
config = AutoConfig.from_pretrained("rbt3") config.[想看的參數]
|
模型調用 AutoTokenizer + AutoModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from transformers import AutoConfig, AutoModel, AutoTokenizer
sen = "今天天氣很好今天天氣很好" tokenizer = AutoTokenizer.from_pretrained("rbt3") inputs = tokenizer(sen, return_tensors="pt") print(inputs)
model = AutoModel.from_pretrained("rbt3") output = model(**inputs) print(output) print(output.last_hidden_state) print(output.last_hidden_state.size()) print(len(inputs["input_ids"][0]))
|
Model Head的模型調用
Model Head 有點像是模型的最後一層,他負責將模型的輸出轉換成我們需要的格式,以解決不同類型的任務。
我們用 AutoModelForSequenceClassification 文本分類為例:
1 2 3 4 5 6 7 8 9 10
| from transformers import AutoModelForSequenceClassification
clz_model = AutoModelForSequenceClassification.from_pretrained("rbts") print(clz_model.config.num_labels) print(clz_model(**inputs))
clz_model = AutoModelForSequenceClassification.from_pretrained("rbts", num_labels=10) print(clz_model.config.num_labels) print(clz_model(**inputs))
|
模型微調範例
step 1 加載數據
先去下載 ChnSentiCorp_ht1_all.csv
預測資料存在本地
1 2 3 4
| from transformers import AutoConfig, AutoModel, AutoTokenizer
import pandas as pd data = pd.read_csv("./ChnSentiCorp_ht1_all.csv").dropna()
|
Step 2 創建 dataset
後面Dataloader會根據整批的dataset
- 進行 90:10 的訓練和測試資料分割
- 進行 tokenizer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import torch.utils.data import Dataset
class MyDataset(Dataset): def __init__(self) -> None: super().__init__() self.data = pd.read_csv("./ChnSentiCorp_ht1_all.csv").dropna() def __getitem__(self, index): return self.data.iloc[index]["review"], self.data.iloc[index]["label"]
def __len__(self): return len(self.data)
dataset = MyDataset() for i in range(5): print(dataset[i])
|
Step3 劃分數據集
接下來我們可以使用 random_split
劃分數據:
1 2 3 4
| from torch.utils.data import random_split
trainset, validset = random_split(dataset, lengths=[0.9, 0.1]) print(len(trainset, len(validset)))
|
Step 4 創建Dataloader
開始處理 batch 資料,進行 batch 的 tokenizer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from torch.utils.data import DataLoader
tokenizer = AutoTokenizer.from_pretrained("rbt3")
def collate_func(batch): texts, labels = [], [] for item in batch: texts.append(item[0]) labels.append(item[1]) inputs = tokenizer.batch_encode_plus(texts, max_length=512, padding="max_length", truncation=True, return_tensors="pt") inputs["labels"] = torch.tensor(labels) return inputs
trainloader = DataLoader(trainset, batch_size=32, shuffle=True, collate_fn=collate_func) validloader = DataLoader(validset, batch_size=32, shuffle=True, collate_fn=collate_func) print(next(enumerate(trainloader))[1])
|
Step 5 創建模型及優化器
1 2 3 4 5 6 7 8 9
| from torch.optim import Adam from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("./rbt3/") optimizer = Adam(model.parameters(), lr=2e-5)
if torch.cuda.is_available(): model = model.cuda()
|
Step 6 訓練與驗證
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| def evaluate(): model.eval() acc_num = 0 with torch.inference_mode(): for batch in validloader: if torch.cuda.is_available(): batch = {k: v.cuda() for k, v in batch.items()} ouput = model(**batch) pred = torch.argmax(ouput.logits, dim=-1) acc_num += (pred.long() == batch["labels"]).float().sum() return acc_num / len(validset)
def train(epoch=3, log_step=100): global_step = 0 for ep in range(epoch): model.train() for batch in trainloader: if torch.cuda.is_available(): batch = {k: v.cuda() for k, v in batch.items()} optimizer.zero_grad() output = model(**batch) output.loss.backword() optimizer.step() if global_step % log_step == 0: print(f"ep: {ep}, global_step: {global_step}, loss:{output.loss.item()}") global_step += 1 acc = evaluate() print(f"ep: {ep}, acc: {acc}")
train()
|
Step 9 進行預測
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| sen = "我覺得這家酒店不錯 飯很好吃!" id2_label = {0: "差評", 1:"好評"} with torch.inference_mode(): inputs = tokenizer(sen, return_tensors="pt") inputs = {k: v.cuda() for k, v in inputs.items()} logits = model(**inputs).logits pred = torch.argmax(logits, dim=-1) print(f"輸入: {sen} \n模型預測結果:{id2_label(pred.item())}")
from transformer import pipeline
pipe = pipeline("text-classification", model=model, tokenizer=tokenizer, device=0) print(pipe(sen))
model.config.id2lable = id2_label print(pipe(sen))
|
參考文獻