在 上一篇文章 中,我們介紹了使用 NeMo 運行英-中翻譯模型的示例,并評估其性能。在這篇文章中,我們將指導您如何定制數據集,并在該數據集上微調模型。
數據收集
數據收集在模型微調中至關重要,因為它使模型適配特定任務或領域要求。
例如,我們翻譯任務是將計算機科學相關的技術文章從英文翻譯成中文,那么收集以前人工翻譯的文章作為微調數據是很有必要的。因為此類文章中包含很多這個領域中常用概念和術語,但在這些語料在預訓練數據集中極少出現。
我們建議至少采集幾千個高質量的樣本。在使用這些量身定制的數據進行微調后,模型可以在技術博客翻譯任務中取得更好的表現。
數據預處理流程
為了提高數據質量,您需要對數據進行預處理,以過濾掉無效和冗余的臟數據。NVIDIA NeMo framework 包含了 NVIDIA NeMo Curator,目前可以用于處理 LLM 預訓練中使用的語料庫。然而,NMT 的雙語數據集與單語語料庫不同,因為它們有源文本和目標文本,需要特殊的過濾方法。幸運的是,NeMo 也提供了大部分開箱即用的函數和腳本,滿足了雙語數據集處理的需求。
您可以用一個簡單的數據預處理流程來清洗英-中的翻譯數據集:
- 語言篩選
- 長度過濾
- 數據去重
- 分詞處理和標準化(僅用于 NeMo 模型微調)
- 轉換為 JSONL 格式(僅用于 ALMA 模型微調)
- 拆分數據集
除了以上這些方法,您還可以使用其他自定義的方案來過濾掉無效數據。例如,使用現有的 NMT 模型篩選出可能是錯誤的翻譯。如想了解更多數據預處理方法,請參閱 此文檔。
原始的數據格式
我們收集了英-中的翻譯對,并放在了兩個文本文件:
en_zh.en
:存儲了按行分隔的英語句子。En_zh.zh
:這個文件中每一行是相應 en_zh.en 中每一行的中文翻譯。
? | en_zh.en |
en_zh.zh |
第 1 行 | Accelerate Data Preparation | 加快完成數據準備 |
第 2 行 | An end to end, cloud native, suite of?AI and data analytics software,?optimized, certified and supported by NVIDIA. | 一款經 NVIDIA 優化、認證和支持的端到端云原生 AI 和數據分析軟件套件。 |
?
在本節中,經過每個處理數據處理步驟后輸出的文件都按以上格式記錄。
語種過濾
NeMo 通過使用 fastText 的語種識別技術提供了語種過濾的方法。
首先,從 fastText 網站下載語言分類器模型 lid.176.bin:
wget https: / / dl.fbaipublicfiles.com / fasttext / supervised - models / lid. 176.bin - O lid. 176.bin |
使用以下代碼進行語種篩選:
python / opt / NeMo / scripts / neural_machine_translation / filter_langs_nmt.py \ - - input - src en_zh.en \ - - input - tgt en_zh.zh \ - - output - src en_zh_preprocessed1.en \ - - output - tgt en_zh_preprocessed1.zh \ - - removed - src en_zh_garbage1.en \ - - removed - tgt en_zh_garbage1.zh \ - - source - lang en \ - - target - lang zh \ - - fasttext - model \ lid. 176.bin |
en_zh_preprocessed1.en
和 en_zh_preprocessed1.zh
是保留下來的有效數據,而 en_zh_garbage1.en 和 en_zh_garbage1.zh
則是廢棄數據。
以下是 en_zh_garbage1.zh
文件的一部分,這些都不是中文所以被過濾了:
NVIDIA OVX Tensor Core PROP_2.USD ConnectX - 6 Dx、ConnectX - 6 |
長度過濾
長度過濾用于去除雙語語料庫中過短或過長的文本,因為他們可能是一些臟數據。此外,我們還會根據目標句子和源句子之間的長度比進行過濾。
在運行長度篩選之前,您可以使用以下腳本計算出您收集的數據中的譯文和原文的長度比的分布,以便了解要篩選的比率閾值:
def compute_length_ratio(source_txt, target_txt, percentile): ???? len_ratios = list () ?
???? with open (source_txt, "r" ) as src, \ ???????? open (target_txt, "r" ) as tgt: ???????? for src_line, tgt_line in zip (src, tgt): ???????????? len_ratios.append( len (src_line.strip()) / len (tgt_line.strip())) ?
???? len_ratios.sort() ?
???? # compute percentile ???? ratio = len_ratios[ int ( len (len_ratios) * percentile)] ???? print (f "Length ratio @{percentile} percentile is {ratio}" ) ???? return ratio ?
compute_length_ratio( "en_zh_preprocessed1.en" , "en_zh_preprocessed1.zh" , 0.95 ) |
這個長度比在不同的數據集以及源語言和目標語言上也有所不同。
運行以下腳本以執行長度篩選。其中,--ratio 4.6
參數用于指定最大的長度比。
python / opt / NeMo / scripts / neural_machine_translation / length_ratio_filter.py \ ???? - - input - src en_zh_preprocessed1.en \ ???? - - input - tgt en_zh_preprocessed1.zh \ ???? - - output - src en_zh_preprocessed2.en \ ???? - - output - tgt en_zh_preprocessed2.zh \ ???? - - removed - src en_zh_garbage2.en \ ???? - - removed - tgt en_zh_garbage2.zh \ ???? - - min - length 10 \ ???? - - max - length 512 \ ???? - - ratio 4.6 |
同上,en_zh_preprocessed2.en
和 en_zh_preprocessed2.zh
是可以保留到下一步的數據文件。
數據去重
在這一步中,您可以使用xxhash
庫。
pip install xxhash |
運行以下 Python 腳本進行去重:
import xxhash ?
def dedup_file(input_file_lang_1, input_file_lang_2, output_file_lang_1, output_file_lang_2): ???? hashes = set () ???? with open (input_file_lang_1, 'r' ) as f_lang1, \ ???????? open (input_file_lang_2, 'r' )? as f_lang2, \ ???????? open (output_file_lang_1, 'w' ) as f_out_lang1, \ ???????? open (output_file_lang_2, 'w' ) as f_out_lang2: ???????? for line_1, line_2 in zip (f_lang1, f_lang2): ???????????? parallel_hash = xxhash.xxh64((line_1.strip()).encode( 'utf-8' )).hexdigest() ???????????? if parallel_hash not in hashes: ???????????????? hashes.add(parallel_hash) ???????????????? f_out_lang1.write(line_1.strip() + '\n' ) ???????????????? f_out_lang2.write(line_2.strip() + '\n' ) ?
dedup_file( ???? 'en_zh_preprocessed2.en' , ???? 'en_zh_preprocessed2.zh' , ???? 'en_zh_preprocessed3.en' , ???? 'en_zh_preprocessed3.zh' ) |
在此步驟中,要保留的數據文件為 en_zh_preprocessed3.en
和 en_zh_preprocessed3.zh
。
分詞處理和標準化 (僅用于 NeMo 模型微調)
如果要微調 NeMo 模型,需要進行額外的分詞處理和標準化:
- 標準化:統一規范句子中的標點符號,例如引號的格式。
分詞處理:通過在標點符號和相鄰單詞之間添加空格,以避免單詞上附加標點符號,這是 NeMo 訓練中的一項建議步驟。
python / opt / NeMo / scripts / neural_machine_translation / preprocess_tokenization_normalization.py \ ???? - - input - src en_zh_preprocessed3.en \ ???? - - input - tgt en_zh_preprocessed3.zh \ ???? - - output - src en_zh_final_nemo.en \ ???? - - output - tgt en_zh_final_nemo.zh \ ???? - - source - lang en \ ???? - - target - lang zh |
這腳本底層采用不同了的庫來處理不同的語言。例如,英語使用了 sacremoses ,對簡體中文,則使用 Jieba 和 OpenCC 。
這一步輸出的 en_zh_final_nemo.en
和 en_zh_final_nemo.zh
是 NeMo 微調所需的數據集文件。
轉換 JSONL 格式 (僅用于 ALMA 模型微調)
ALMA 訓練需要采用 JSON 行(JSONL)作為原始輸入數據,其中文件中的每一行都是一個 JSON 結構:
{ "translation" : { "en" : "Accelerate Data Preparation" , "zh" : "加快完成數據準備" }} |
如要訓練 ALMA 模型,您必須將 en_zh_preprocessed3.en
和 en_zh_preprocessed3.zh
這兩個中英文件轉換為一個 JSONL 格式的文件:
import json ?
def txt2jsonl(source_txt, target_txt, source, target, output_jsonl): ?? with open (source_txt, "r" ) as f: ?????? source_lines = f.read().splitlines() ? ??? with open (target_txt, "r" ) as f: ?????? target_lines = f.read().splitlines() ?
?? json_list = list () ?? for source_text, target_text in zip (source_lines, target_lines): ?????? json_item = { ?????????? "translation" : {source: source_text, target: target_text} ?????? } ?????? json_list.append(json_item) ?
?? with open (output_jsonl, "w" ) as f: ?????? for json_item in json_list: ?????????? f.write(json.dumps(json_item, ensure_ascii = False ) + "\n" ) ?
source_txt = "en_zh_preprocessed3.en" target_txt = "en_zh_preprocessed3.zh" source = "en" target = "zh" output_jsonl = "en_zh_final_alma.jsonl" txt2jsonl(source_txt, target_txt, source, target, output_jsonl) |
這一步輸出的 en_zh_final_alma.jsonl
是 ALMA 微調所需的數據集文件。
拆分數據集
最后一步是將數據集拆分為訓練集、驗證集和測試集。你可以使用 sklearn.model_selection 庫中的 train_test_split 方法來完成此操作。
對于 NeMo NMT 模型微調,上一步輸出的 en_zh_final_nemo.en 和 en_zh_final_nemo.zh 會被拆分為:
en_zh_final_nemo_train.en
?en_zh_final_nemo_train.zh
?en_zh_final_nemo_val.en
?en_zh_final_nemo_val.zh
en_zh_final_nemo_test.en
?en_zh_final_nemo_test.zh
?
對于 ALMA 模型微調,上一步輸出的 en_zh_final_alma.jsonl
會被拆分為:
train.zh-en.json
valid.zh-en.json
test.zh-en.json
在本節中,我們將分別演示如何微調 NeMo 和 ALMA 模型。
模型微調
在本節中,我們將分別演示如何微調 NeMo 和 ALMA 模型。
微調 NeMo NMT 模型
在進行微調之前,請參考上一個博客中 NMT 模型評估章節的介紹,把 NeMo 預訓練模型下載到 ./model/pretrained_ckpt/en_zh_24x6.nemo
。然后,您可以使用收集的數據集對 NeMo EN-ZH 模型進行微調。請注意,批處理大小將取決于 GPU 顯存的大小。
python / opt / NeMo / examples / nlp / machine_translation / enc_dec_nmt_finetune.py \ ?? model_path = model / pretrained_ckpt / en_zh_24x6.nemo \ ?? trainer.devices = 1 \ ?? trainer.max_epochs = 10 \ ?? + trainer.val_check_interval = 600 \ ?? model.train_ds.tgt_file_name = data / en_zh_final_nemo_train.zh \ ?? model.train_ds.src_file_name = data / en_zh_final_nemo_train.en \ ?? model.train_ds.tokens_in_batch = 3000 \ ?? model.validation_ds.tgt_file_name = data / en_zh_final_nemo_val.zh \ ?? model.validation_ds.src_file_name = data / en_zh_final_nemo_val.en \ ?? model.validation_ds.tokens_in_batch = 1000 \ ?? model.test_ds.tgt_file_name = data / en_zh_final_nemo_test.zh \ ?? model.test_ds.src_file_name = data / en_zh_final_nemo_test.en \ ?? + exp_manager.exp_dir = output / \ ?? + exp_manager.create_checkpoint_callback = True \ ?? + exp_manager.checkpoint_callback_params.monitor = val_sacreBLEU \ ?? + exp_manager.checkpoint_callback_params.mode = max \ ?? + exp_manager.checkpoint_callback_params.save_best_model = true \ ?? + exp_manager.checkpoint_callback_params.always_save_nemo = true \ ?? + exp_manager.checkpoint_callback_params.save_top_k = 10 |
訓練完成后,結果和權重文件將被保存到 ./output/AAYNBaseFineTune
路徑。您可以使用 TensorBoard 來可視化損失曲線或查看日志文件。
微調 ALMA NMT 模型
要微調 ALMA 模型,首先要從 GitHub 上克隆 ALMA 的倉庫,并在 NeMo framework 容器中的安裝額外的依賴項。
git clone https: / / github.com / fe1ixxu / ALMA.git cd ALMA bash install_alma.sh pip install - - upgrade pytest |
數據
您可以將處理后的數據集(train.zh-en.json
、valid.zh-en.json
、test.zh-en.json
)放置在根目錄下的 ./human_written_data/zhen
數據目錄中,其中 /zhen 子目錄專門用于存儲中英文的數據集。
配置修改
下一步是修改兩個配置文件中的參數:/runs/parallel_ft_lora.sh
和 /configs/deepspeed_train_config.yaml
。
在 /runs/parallel_ft_lora.sh
中,需要修改的字段有:
per_device_train_batch_size
:每個設備訓練批大小。gradient_accumulation_steps
:在梯度更新前的累積步數。learning_rate
:根據批大小和累積步驟數進行動態調整。
在/configs/deepspeed_train_config.yaml
文件中需要修改的字段有:
gradient_accumulation_steps
:需要和上面/runs/parallel_ft_lora.sh
中的gradient_accumulation_steps
值相同。num_processes
:用于指定 GPU 設備的數量。
微調命令
如要使用 LoRA 微調 ALMA 模型的英-中和中-英雙向翻譯能力,可在 ALMA 倉庫的根目錄中運行以下命令:
bash runs / parallel_ft_lora.sh output zh - en,en - zh |
結果文件將被存儲在 ./output 目錄:
adapter_config.json
:用于存儲 LoRA 配置。adapter_model.bin
:LoRA 模型權重。trainer_state.json
:訓練過程中的損失記錄。
微調模型評估
在上一博客中,您評估了 NeMo 和 ALMA 預訓練模型的初始性能。在這里,您可以通過使用相同的測試數據集對微調后的模型進行評估測試。
微調 NeMo 模型評估
對于 NeMo 模型可使用以下腳本在相同的測試集進行推理:
python / opt / NeMo / examples / nlp / machine_translation / nmt_transformer_infer.py \ ?? - - model $fine_tuned_nemo_path \ ?? - - srctext input_en.txt \ ?? - - tgtout nemo_ft_out_zh.txt \ ?? - - source_lang en \ ?? - - target_lang zh \ ?? - - batch_size 200 \ ?? - - max_delta_length 20 sacrebleu reference.txt - i nemo_ft_out_zh.txt - m bleu - b - w 4 |
$fine_tuned_nemo_path
:NeMo 微調后的模型路徑。input_en.txt
:輸入的英文文本。nemo_ft_out_zh.txt
:NeMo 模型微調后輸出的中文。reference.txt
: 參考譯文。
最后一行代碼計算了 BLEU 分數。
微調 ALMA 模型評估
評估 ALMA 模型時也需要對相同的測試集進行推理。以下腳本是對微調后的 ALMA 模型的推理代碼片段,其中模型加載部分與前面討論的略有不同,因為它需要讀取本地的微調 PEFT 模型和配置。
import torch from peft import PeftModel, PeftConfig from transformers import AutoModelForCausalLM from transformers import LlamaTokenizer ?
?
# Load base model and LoRA weights peft_config = PeftConfig.from_pretrained( "./output" ) model = AutoModelForCausalLM.from_pretrained(peft_config.base_model_name_or_path, torch_dtype = torch.float16, device_map = "auto" ) model = PeftModel.from_pretrained(model, "./output" ) model = model. eval () tokenizer = LlamaTokenizer.from_pretrained( "haoranxu/ALMA-7B-Pretrain" , padding_side = 'left' ) ?
# Add the source sentence into the prompt template prompt_template = "Translate this from English to Chinese:\nEnglish: {}\nChinese:" prompt = prompt_template. format ( "AI is powering change in every industry" ) ?
?
# Tokenize input_ids = tokenizer(prompt, return_tensors = "pt" , padding = True , max_length = 40 , truncation = True ).input_ids.cuda() # Inference with torch.no_grad(): ???? generated_ids = model.generate(input_ids = input_ids, num_beams = 5 , max_new_tokens = 256 , do_sample = True , temperature = 0.6 , top_p = 0.9 ) ?
outputs = tokenizer.batch_decode(generated_ids, skip_special_tokens = True ) output = outputs[ 0 ].replace(prompt, "").strip() print (output) |
您可以通過修改示例推理的腳本以對英語文本文件進行翻譯,并計算其 BLEU 分數。
結論
該 NeMo 框架容器 提供了一個方便的環境,適用于各種推理和定制任務。
- 使用 LLM 開發自定義企業生成式 AI。
- 多模態語言模型
- 計算機視覺應用
- 自動語音識別(Automatic Speech Recognition,ASR)
- 文本到語音合成(TTS)
- 神經網絡機器翻譯
NeMo framework 容器 提供了一個方便的環境,適用于各種推理和定制任務。
在本系列中,我們介紹了 NeMo NMT 模型和 ALMA NMT 模型在容器中從零開始微調的流程,涵蓋了預訓練模型推理和評估、數據收集和處理、模型微調和最終評估。
如想解更多關于構建和部署多語言 AI 對話系統的方案,請參閱 NVIDIA Riva。其中您還可以了解到更多關于實時雙語或多語言的 語音到語音翻譯(S2S) 和 語音到文本翻譯(S2T) API。
如想在 NeMo 框架中執行更多 LLM 和分布式訓練任務,請參考我們的 playbooks 和 開發者文檔。
欲了解更多關于處理數據和模型評估的方案,請參閱最近發布的 NeMo Curator 和 NVIDIA NeMo Evaluator 微服務,并申請 NeMo Microservices 早期訪問。
?