- 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)) 
   | 
 
參考文獻