电竞比分网-中国电竞赛事及体育赛事平台

分享

【AI大模型應(yīng)用開發(fā)】【Fine-Tuning】0. 從一個例子開始學(xué)習(xí)大模型Fine-Tuning

 小張學(xué)AI 2024-03-15 發(fā)布于山東
大家好,我是【同學(xué)小張】。持續(xù)學(xué)習(xí),持續(xù)干貨輸出,關(guān)注我,跟我一起學(xué)AI大模型技術(shù)!
公眾號內(nèi)文章一覽

有任何問題,歡迎+vx:jasper_8017,期待與志同道合的朋友一起討論,共同進步!


Fine-Tuning,即微調(diào)。我嘗試用我的理解敘述微調(diào)的含義:在原有模型的基礎(chǔ)上,通過補充一些數(shù)據(jù),用這些補充的數(shù)據(jù)對原有模型進行訓(xùn)練,訓(xùn)練的過程對原有模型的部分參數(shù)進行調(diào)整,從而使模型能在某些特定的場景下表現(xiàn)更優(yōu)。

所以,大模型微調(diào)可以提高其在特定場景下的表現(xiàn),同時,會降低大模型在通用場景下的能力。

今天,我們從一個簡單的例子入手,先來感受下Fine-Tune微調(diào)到底是什么。這個例子可以在筆記本電腦上跑,需要的配置不高。在開始本文的實踐案例前,你可以對模型訓(xùn)練一竅不通,本文將帶你跑通整個過程,同時解釋其中一些概念。

0. 環(huán)境準(zhǔn)備

使用模型訓(xùn)練利器 huggingface (http://www./)來進行模型訓(xùn)練和微調(diào)。執(zhí)行以下代碼進行安裝:

# pip安裝
pip install transformers # 安裝最新的版本
# conda安裝
conda install -c huggingface transformers  # 只4.0以后的版本

huggingface簡介:

  • 相當(dāng)于面向 NLP 模型的 Github

  • 尤其基于 transformer 的開源模型非常全

  • 封裝了模型、數(shù)據(jù)集、訓(xùn)練器等,使模型的下載、使用、訓(xùn)練都非常方便

1. 加載訓(xùn)練數(shù)據(jù)集

這里可以直接使用datasets庫中的load_dataset函數(shù)進行在線加載,只需要指定HuggingFace中的數(shù)據(jù)集名稱即可。

本文以 rotten_tomatoes 數(shù)據(jù)集為例。
這是一個對電影評論進行情感分類的數(shù)據(jù)集。

  • 輸入:電影評論

  • 輸出:['負(fù)面','正面']

save_to_disk函數(shù)用來將加載的數(shù)據(jù)集保存到本地的一個目錄下。

將加載到的數(shù)據(jù)集分成 train 部分和 validation 部分。

import datasets
from datasets import load_dataset

# 數(shù)據(jù)集名稱
DATASET_NAME = "rotten_tomatoes" 

# 加載數(shù)據(jù)集
raw_datasets = load_dataset(DATASET_NAME)

raw_datasets.save_to_disk(os.path.join("D:\\GitHub\\LEARN_LLM\\FineTune\\FineTune1\\data", DATASET_NAME))

# 訓(xùn)練集
raw_train_dataset = raw_datasets["train"]

# 驗證集
raw_valid_dataset = raw_datasets["validation"]

2. 加載模型

這里直接使用transformers庫中AutoModelForCausalLM的from_pretrained函數(shù),填入預(yù)訓(xùn)練的模型名稱,然后它就會在運行過程中自動在線加載該模型。本文使用 gpt2 模型。

from transformers import AutoModelForCausalLM

# 模型名稱
MODEL_NAME = "gpt2" 

# 加載模型 
model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, trust_remote_code=True)

3. 加載 Tokenizer

通過HuggingFace,可以指定模型名稱,運行時自動下載對應(yīng)Tokenizer。直接使用transformers庫中AutoTokenizer的from_pretrained函數(shù),填入對應(yīng)的模型名稱。

from transformers import AutoTokenizer, AutoModel

# 加載tokenizer
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
tokenizer.add_special_tokens({'pad_token''[PAD]'})
tokenizer.pad_token_id = 0

4. 處理訓(xùn)練數(shù)據(jù)集

使用datasets庫中的map函數(shù)進行數(shù)據(jù)處理。map函數(shù)參數(shù)如下:

map(
    function: Optional[Callable] = None,
    with_indices: bool = False,
    with_rank: bool = False,
    input_columns: Optional[Union[str, List[str]]] = None,
    batched: bool = False,
    batch_size: Optional[int] = 1000,
    drop_last_batch: bool = False,
    remove_columns: Optional[Union[str, List[str]]] = None,
    keep_in_memory: bool = False,
    load_from_cache_file: bool = True,
    cache_file_names: Optional[Dict[str, Optional[str]]] = None,
    writer_batch_size: Optional[int] = 1000,
    features: Optional[Features] = None,
    disable_nullable: bool = False,
    fn_kwargs: Optional[dict] = None,
    num_proc: Optional[int] = None,
    desc: Optional[str] = None,
)
  • 該函數(shù)通過一個映射函數(shù)function,處理Dataset中的每一個元素。如果不指定function,則默認(rèn)的函數(shù)為lambda x: x。

  • 參數(shù)batched表示是否進行批處理

  • 參數(shù)batch_size表示批處理的大小,也就是每次處理多少個元素,默認(rèn)為1000。

  • 參數(shù)drop_last_batch表示當(dāng)最后一批的數(shù)量小于batch_size,是否處理最后一批。

  • remove_columns表示要刪除的列的名稱,刪除列是在數(shù)據(jù)處理結(jié)束后刪除,不影響function的使用

數(shù)據(jù)處理過程代碼:

# 標(biāo)簽集
named_labels = ['neg','pos']

# 標(biāo)簽轉(zhuǎn) token_id
label_ids = [
    tokenizer(named_labels[i],add_special_tokens=False)["input_ids"][0
    for i in range(len(named_labels))
]

MAX_LEN=32   #最大序列長度(輸入+輸出)
DATA_BODY_KEY = "text" # 數(shù)據(jù)集中的輸入字段名
DATA_LABEL_KEY = "label" #數(shù)據(jù)集中輸出字段名

# 定義數(shù)據(jù)處理函數(shù),把原始數(shù)據(jù)轉(zhuǎn)成input_ids, attention_mask, labels
def process_fn(examples):
    model_inputs = {
            "input_ids": [],
            "attention_mask": [],
            "labels": [],
        }
    for i in range(len(examples[DATA_BODY_KEY])):
        inputs = tokenizer(examples[DATA_BODY_KEY][i],add_special_tokens=False)
        label = label_ids[examples[DATA_LABEL_KEY][i]]
        input_ids = inputs["input_ids"] + [tokenizer.eos_token_id, label]

        raw_len = len(input_ids)
        input_len = len(inputs["input_ids"]) + 1

        if raw_len >= MAX_LEN:
            input_ids = input_ids[-MAX_LEN:]
            attention_mask = [1] * MAX_LEN
            labels = [-100]*(MAX_LEN - 1) + [label]
        else:
            input_ids = input_ids + [0] * (MAX_LEN - raw_len)
            attention_mask = [1] * raw_len + [tokenizer.pad_token_id] * (MAX_LEN - raw_len)
            labels = [-100]*input_len + [label] + [-100] * (MAX_LEN - raw_len)
        model_inputs["input_ids"].append(input_ids)
        model_inputs["attention_mask"].append(attention_mask)
        model_inputs["labels"].append(labels)
    return model_inputs

# 處理訓(xùn)練數(shù)據(jù)集
tokenized_train_dataset = raw_train_dataset.map(
    process_fn,
    batched=True,
    remove_columns=raw_train_dataset.column_names,
    desc="Running tokenizer on train dataset",
)

# 處理驗證數(shù)據(jù)集
tokenized_valid_dataset = raw_valid_dataset.map(
    process_fn,
    batched=True,
    remove_columns=raw_valid_dataset.column_names,
    desc="Running tokenizer on validation dataset",
)

5. 定義數(shù)據(jù)規(guī)整器

訓(xùn)練時自動將數(shù)據(jù)拆分成 Batch

# 定義數(shù)據(jù)校準(zhǔn)器(自動生成batch)
collater = DataCollatorWithPadding(
    tokenizer=tokenizer, return_tensors="pt",
)

6. 定義訓(xùn)練超參

LR=2e-5         # 學(xué)習(xí)率
BATCH_SIZE=8    # Batch大小
INTERVAL=100    # 每多少步打一次 log / 做一次 eval

# 定義訓(xùn)練參數(shù)
training_args = TrainingArguments(
    output_dir="./output",              # checkpoint保存路徑
    evaluation_strategy="steps",        # 每N步做一次eval
    overwrite_output_dir=True,
    num_train_epochs=1,                 # 訓(xùn)練epoch數(shù)
    per_device_train_batch_size=BATCH_SIZE,     # 每張卡的batch大小
    gradient_accumulation_steps=1,              # 累加幾個step做一次參數(shù)更新
    per_device_eval_batch_size=BATCH_SIZE,      # evaluation batch size
    logging_steps=INTERVAL,             # 每INTERVAL步log 一次
    save_steps=INTERVAL,                # 每INTERVAL步保存一個checkpoint
    learning_rate=LR,                   # 學(xué)習(xí)率
)
  • 學(xué)習(xí)率:是指導(dǎo)我們該如何通過損失函數(shù)的梯度調(diào)整網(wǎng)絡(luò)權(quán)重的超參數(shù),通過設(shè)置學(xué)習(xí)率控制參數(shù)更新的速度。以下公式,在更新參數(shù) w 時,其中 α 就是學(xué)習(xí)率。

  • Batch Size:是指在訓(xùn)練時,一次提供給模型的數(shù)據(jù)的數(shù)量。在訓(xùn)練時,模型需要對整個訓(xùn)練數(shù)據(jù)集進行訓(xùn)練,但是數(shù)據(jù)集通常很大,如果一次把整個數(shù)據(jù)集提供給模型訓(xùn)練,可能導(dǎo)致內(nèi)存不足或運算時間太長。因此,我們通常將數(shù)據(jù)集分成若干個Batch,每次提供一個Batch給模型訓(xùn)練。Batch Size就是指一個Batch中數(shù)據(jù)的數(shù)量。

  • 訓(xùn)練epoch數(shù):在訓(xùn)練模型時,通常會設(shè)定訓(xùn)練的epoch數(shù),這意味著模型會在訓(xùn)練數(shù)據(jù)集上訓(xùn)練多少遍。訓(xùn)練epoch數(shù)較多意味著模型會更加充分地學(xué)習(xí)訓(xùn)練數(shù)據(jù)集,但同時也會增加訓(xùn)練時間。

  • 檢查點(CheckPoints):是指通過周期性(迭代/時間)的保存模型的完整狀態(tài),在模型訓(xùn)練失敗時,可以從保存的檢查點模型繼續(xù)訓(xùn)練,以避免訓(xùn)練失敗時每次都需要從頭開始帶來的訓(xùn)練時間浪費。檢查點模式適用于模型訓(xùn)練時間長、訓(xùn)練需要提前結(jié)束、fine-tune等場景,也可以拓展到異常時的斷點續(xù)訓(xùn)場景。

7. 定義訓(xùn)練器

# 節(jié)省顯存
model.gradient_checkpointing_enable()

# 定義訓(xùn)練器
trainer = Trainer(
    model=model, # 待訓(xùn)練模型
    args=training_args, # 訓(xùn)練參數(shù)
    data_collator=collater, # 數(shù)據(jù)校準(zhǔn)器
    train_dataset=tokenized_train_dataset,  # 訓(xùn)練集
    eval_dataset=tokenized_valid_dataset,   # 驗證集
    # compute_metrics=compute_metric,         # 計算自定義評估指標(biāo)
)

8. 開始訓(xùn)練

# 開始訓(xùn)練
trainer.train()

全部的依賴包如下:

import datasets
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModel
from transformers import AutoModelForCausalLM
from transformers import TrainingArguments, Seq2SeqTrainingArguments
from transformers import Trainer, Seq2SeqTrainer
import transformers
from transformers import DataCollatorWithPadding
from transformers import TextGenerationPipeline
import torch
import numpy as np
import os, re
from tqdm import tqdm
import torch.nn as nn

運行成功后的樣子如下,會顯示當(dāng)前訓(xùn)練進度、預(yù)計耗時等:


訓(xùn)練完之后:

9. 總結(jié)

本文以Hugging face上的情感分類數(shù)據(jù)集和gpt2模型為例,展示了訓(xùn)練過程。同時針對Hugging face訓(xùn)練模型的一些接口和一些概念做了簡要介紹,零基礎(chǔ)也可以跑通該例子。

總結(jié)模型訓(xùn)練的過程:
(1)加載數(shù)據(jù)集
(2)數(shù)據(jù)預(yù)處理

  • 將輸入輸出按特定格式拼接

  • 文本轉(zhuǎn) Token IDs

  • 通過 labels 標(biāo)識出哪部分是輸出(只有輸出的 token 參與 loss 計算)

(3)加載模型
(4)加載模型的Tokenizer
(5)定義數(shù)據(jù)規(guī)整器
(6)定義訓(xùn)練超參:學(xué)習(xí)率、批次大小、…
(7)定義訓(xùn)練器
(8)開始訓(xùn)練

通過這個流程,你就能跑通模型訓(xùn)練過程。

如果覺得本文對你有幫助,麻煩點個贊和關(guān)注唄 ~~~


  • 大家好,我是同學(xué)小張,日常分享AI知識和實戰(zhàn)案例

  • 歡迎 點贊 + 關(guān)注 ??,持續(xù)學(xué)習(xí),持續(xù)干貨輸出。

公眾號內(nèi)文章一覽

有任何問題,歡迎+vx:jasper_8017,期待與志同道合的朋友一起討論,共同進步!

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多