模型訓練介紹
ModelScope提供了很多模型,這些模型可以直接在推理中使用,也可以根據用戶數據集重新生成模型的參數,這個過程叫做訓練。特別地,基於預訓練backbone進行訓練的過程叫做微調(finetune)。
一般來說,一次完整的模型訓練包含了訓練(train)和評估(evaluate)兩個過程。訓練過程使用訓練數據集,將數據輸入模型計算出loss後更新模型參數。評估過程使用評估數據集,將數據輸入模型後評估模型效果。
ModelScope提供了完整的訓練組件,其中的主要組件被稱為trainer(訓練器),這些組件可以在預訓練或普通訓練場景下使用。
PyTorch訓練流程
![](https://news.xinpengboligang.com/upload/keji/20c22445e47f5b453efe211f5909b7dc.jpeg)
ModelScope的模型訓練步驟如下:
- 使用MsDataset加載數據集
- 編寫cfg_modify_fn方法,按需修改部分參數
- 構造trainer,開始訓練
- 【訓練後步驟】進行模型評估
- 【訓練後步驟】使用訓練後的模型進行推理
PyTorch模型的訓練使用EpochBasedTrainer(及其子類),該類會根據配置文件實例化模型、預處理器、優化器、指標等模塊。因此訓練模型的重點在於修改出合理的配置,其中用到的各組件都是ModelScope的標準模塊。
trainer的重要構造參數
model: 模型id、模型本地路徑或模型實例,必填
cfg_file: 額外的配置文件,可選。如果填寫,trainer會使用這個配置進行訓練
cfg_modify_fn: 讀取配置後trainer調用這個回調方法修改配置項,可選。如果不傳就使用原始配置
train_dataset: 訓練用的數據集,調用訓練時必傳
eval_dataset: 評估用的數據集,調用評估時必傳
optimizers: 自定義的(optimizer、lr_scheduler),可選,如果傳入就不會使用配置文件中的
seed: 隨機種子
launcher: 支持使用pytorch/mpi/slurm開啟分佈式訓練
device: 訓練用設備。可選,值為cpu, gpu, gpu:0, cuda:0等,默認gpu
一個簡單的例子:文本分類
下面以一個簡單的文本分類任務為例,演示如何通過十幾行代碼,就可以端到端執行一個finetune任務。假設待訓練模型為:
# structbert的backbone,該模型沒有有效分類器,因此使用前需要finetune(微調)
model_id = 'damo/nlp_structbert_backbone_base_std'
使用MsDataset加載數據集
MsDataset提供了加載數據集的能力,包括用戶的數據和ModelScope生態數據集。下面的示例加載了ModelScope提供的afqmc(Ant Financial Question Matching Corpus,雙句相似度任務)數據集:
from modelscope.msdatasets import MsDataset
# 載入訓練數據,數據格式類似於{'sentence1': 'some content here', 'sentence2': 'other content here', 'label': 0}
train_dataset = MsDataset.load('clue', subset_name='afqmc', split='train')
# 載入評估數據
eval_dataset = MsDataset.load('clue', subset_name='afqmc', split='validation')
或者,也可以加載用戶自己的數據集:
from modelscope.msdatasets import MsDataset
# 載入訓練數據
train_dataset = MsDataset.load('/path/to/my_train_file.txt')
# 載入評估數據
eval_dataset = MsDataset.load('/path/to/my_eval_file.txt')
編寫cfg_modify_fn方法,按需修改部分參數
建議首先查看模型的配置文件,並查看需要額外修改的參數:
from modelscope.utils.hub import read_config
# 上面的model_id
config = read_config(model_id)
print(config.pretty_text)
一般的配置文件中,在訓練時需要修改的參數一般分為:
1. 預處理器參數
# 使用該模型適配的預處理器sen-sim-tokenizer
cfg.preprocessor.type='sen-sim-tokenizer'
# 預處理器輸入的dict中,句子1的key,參考上文加載數據集中的afqmc的格式
cfg.preprocessor.first_sequence = 'sentence1'
# 預處理器輸入的dict中,句子2的key
cfg.preprocessor.second_sequence = 'sentence2'
# 預處理器輸入的dict中,label的key
cfg.preprocessor.label = 'label'
# 預處理器需要的label和id的mapping
cfg.preprocessor.label2id = {'0': 0, '1': 1}
某些模態中,預處理的參數需要根據數據集修改(比如NLP一般需要修改,而CV一般不需要修改),後續可以查看ModelCard或各任務最佳實踐中各任務訓練的詳細描述。
2. 模型參數
# num_labels是該模型分類數
cfg.model.num_labels = 2
3. 任務參數
# 修改task類型為'text-classification'
cfg.task = 'text-classification'
# 修改pipeline名稱,用於後續推理
cfg.pipeline = {'type': 'text-classification'}
4. 訓練參數
一般訓練超參數的調節都在這裡進行:
# 設置訓練epoch
cfg.train.max_epochs = 5
# 工作目錄
cfg.train.work_dir = '/tmp'
# 設置batch_size
cfg.train.dataloader.batch_size_per_gpu = 32
cfg.evaluation.dataloader.batch_size_per_gpu = 32
# 設置learning rate
cfg.train.optimizer.lr = 2e-5
# 設置LinearLR的total_iters,這項和數據集大小相關
cfg.train.lr_scheduler.total_iters = int(len(train_dataset) / cfg.train.dataloader.batch_size_per_gpu) * cfg.train.max_epochs
# 設置評估metric類
cfg.evaluation.metrics = 'seq-cls-metric'
使用cfg_modify_fn將上述配置修改應用起來:
# 這個方法在trainer讀取configuration.json後立即執行,先於構造模型、預處理器等組件
def cfg_modify_fn(cfg):
cfg.preprocessor.type='sen-sim-tokenizer'
cfg.preprocessor.first_sequence = 'sentence1'
cfg.preprocessor.second_sequence = 'sentence2'
cfg.preprocessor.label = 'label'
cfg.preprocessor.label2id = {'0': 0, '1': 1}
cfg.model.num_labels = 2
cfg.task = 'text-classification'
cfg.pipeline = {'type': 'text-classification'}
cfg.train.max_epochs = 5
cfg.train.work_dir = '/tmp'
cfg.train.dataloader.batch_size_per_gpu = 32
cfg.evaluation.dataloader.batch_size_per_gpu = 32
cfg.train.dataloader.workers_per_gpu = 0
cfg.evaluation.dataloader.workers_per_gpu = 0
cfg.train.optimizer.lr = 2e-5
cfg.train.lr_scheduler.total_iters = int(len(train_dataset) / cfg.train.dataloader.batch_size_per_gpu) * cfg.train.max_epochs
cfg.evaluation.metrics = 'seq-cls-metric'
# 註意這裡需要返回修改後的cfg
return cfg
構造trainer,開始訓練
首先,配置訓練所需參數:
from modelscope.trainers import build_trainer
# 配置參數
kwargs = dict(
model=model_id,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
cfg_modify_fn=cfg_modify_fn)
trainer = build_trainer(default_args=kwargs)
trainer.train()
需要註意,數據由trainer從dataloader取數據的時候調用預處理器進行處理。
恭喜,你完成了一次模型訓練。
進行模型評估
可選地,在訓練後可以進行額外數據集的評估。用戶可以單獨調用evaluate方法對模型進行評估:
from modelscope.msdatasets import MsDataset
# 載入評估數據
eval_dataset = MsDataset.load('clue', subset_name='afqmc', split='validation')
from modelscope.trainers import build_trainer
# 配置參數
kwargs = dict(
# 由於使用的模型訓練後的目錄,因此不需要傳入cfg_modify_fn
model='/tmp/output',
eval_dataset=eval_dataset)
trainer = build_trainer(default_args=kwargs)
trainer.evaluate()
或者,也可以調用predict方法將預測結果保存下來,以供後續打榜:
from modelscope.msdatasets import MsDataset
import numpy as np
# 載入評估數據
eval_dataset = MsDataset.load('clue', subset_name='afqmc', split='test').to_hf_dataset()
from modelscope.trainers import build_trainer
def cfg_modify_fn(cfg):
# 預處理器在mini-batch中留存冗餘字段
cfg.preprocessor.val.keep_original_columns = ['sentence1', 'sentence2']
# 預測數據集沒有label,將對應key置空
cfg.preprocessor.val.label = None
return cfg
kwargs = dict(
model='damo/nlp_structbert_sentence-similarity_chinese-tiny',
work_dir='/tmp',
cfg_modify_fn=cfg_modify_fn,
# remove_unused_data會將上述keep_original_columns的列轉為attributes
remove_unused_data=True)
trainer = build_trainer(default_args=kwargs)
def saving_fn(inputs, outputs):
with open(f'/tmp/predicts.txt', 'a') as f:
# 通過attribute取冗餘值
sentence1 = inputs.sentence1
sentence2 = inputs.sentence2
predictions = np.argmax(outputs['logits'].cpu().numpy(), axis=1)
for sent1, sent2, pred in zip(sentence1, sentence2, predictions):
f.writelines(f'{sent1}, {sent2}, {pred}\n')
trainer.predict(predict_datasets=eval_dataset,
saving_fn=saving_fn)
使用訓練後的模型進行推理
訓練完成以後,文件夾中會生成推理用的模型配置,可以直接用於pipeline:
- {work_dir}/output:訓練完成後,存儲模型配置文件,及最後一個epoch/iter的模型參數(配置中需要指定CheckpointHook)
- {work_dir}/output_best:最佳模型參數時,存儲模型配置文件,及最佳的模型參數(配置中需要指定BestCkptSaverHook)
from modelscope.pipelines import pipeline
pipeline_ins = pipeline('text-classification', model='/tmp/output')
pipeline_ins(('這個功能可用嗎', '這個功能現在可用嗎'))
此外,ModelScope也會存儲*.pth文件,用於後續繼續訓練、訓練後驗證、訓練後推理。一般一次存儲會存儲兩個pth文件:
- epoch_*.pth 存儲模型的state_dict,output/output_best的bin文件是此文件的硬鏈接
- epoch_*_trainer_state.pth,存儲trainer的state_dict
在繼續訓練場景時,隻需要加載模型的pth文件,trainer的pth文件會被同時讀取。用戶也可以手動link某個pth文件到output/output_best,實現使用任意一個存儲節點的推理.
pth的文件名格式如下:
- epoch_{n}/iter_{n}.pth(如epoch_3.pth): 每interval個epoch/iter周期存儲(配置中需要指定CheckpointHook)
- best_epoch{n}_{metricname}{m}.pth(如best_iter13_accuracy22.pth):取得最佳模型參數時存儲(配置中需要指定BestCkptSaverHook)
# 用於繼續訓練
trainer.train(checkpoint_path=os.path.join(self.tmp_dir, 'iter_3.pth'))
# 用於訓練後評估
trainer.evaluate(checkpoint_path=os.path.join(self.tmp_dir, 'iter_3.pth'))
# 用於訓練後推理並通過saving_fn存儲預測的label為文件
trainer.predict(checkpoint_path=os.path.join(self.tmp_dir, 'iter_3.pth'),
predict_datasets=some_dataset,
saving_fn=some-saving-fn)