• <xmp id="om0om">
  • <table id="om0om"><noscript id="om0om"></noscript></table>
  • 生成式人工智能/大語言模型

    利用 NVIDIA NeMo Curator 整理非英語數據集以訓練 LLM

    數據管護在開發有效且公平的大型語言模型(LLMs)方面發揮著至關重要的作用。高質量、多樣化的訓練數據直接影響 LLMs 的性能,從而解決偏差、不一致和冗余等問題。通過管護高質量的數據集,我們可以確保 LLMs 準確、可靠且可泛化。

    在訓練本地化的多語種 LLM 時(尤其是針對低資源語言),像OSCAR這樣的網絡抓取數據至關重要。但是,網絡抓取數據通常包含噪聲、無關內容、重復數據和格式問題。有效的數據管護對于解決這些問題并確保高質量的 LLM 性能至關重要。

    NVIDIA 最近發布了 NVIDIA NeMo Curator 的開源版本,這是一個數據 curation 庫,專為可擴展且高效的數據集準備而設計,通過使用 Dask 和 RAPIDS 的 GPU 加速數據 curation 來提高 LLM 訓練的準確性。NeMo Curator 提供可定制的模塊化界面,可簡化流程擴展,并通過準備高質量 tokens 加速模型收斂。NeMo Curator 中的模塊使您能夠大規模地從大量未策劃的網絡語料庫和 自定義數據集中 挖掘高質量文本。

    本文以泰文維基百科為例,介紹了開源多語種數據集的數據策展流程,我們將解釋如何使用 NVIDIA NeMo Curator 構建可擴展的 GPU 加速數據策展流程。

    概述

    本教程介紹了使用 Thai Wikipedia 數據集的數據管護流程,該數據集是一個較小子集Wikipedia數據集,可在單個 GPU 上進行處理。由于Wikipedia由大型社區提供準確、結構良好的內容,因此在 LLM 預訓練中被認為是高質量的。NeMo Curator 通過檢測和過濾低質量文檔來增強這一點,確保僅使用最佳數據進行訓練。

    有關本教程的完整代碼示例,請參閱NVIDIA/NeMo-Curator GitHub 資源庫。

    預備知識

    對于使用 GPU 加速的重復數據刪除,我們建議使用以下硬件設置:

    • NVIDIA GPU:本教程使用 NVIDIA A10 24GB GPU 開發
    • CUDA 和 NVIDIA 驅動:CUDA 12.2 帶 Driver 535.154.05
    • Ubuntu 22.04
    • NVIDIA 容器工具包版本 1.14.6

    要安裝 NeMo Curator 庫,請運行以下命令:

    git clone https://github.com/NVIDIA/NeMo-Curator.git
    cd NeMo-Curator
    pip install --extra-index-url https://pypi.nvidia.com ".[cuda12x]"

    您還可以在NeMo 框架容器中運行本教程。有關更多信息,請參閱NeMo Curator README 文件

    Environment 和 Helper 函數設置

    運行以下代碼以執行必要的導入:

    !pip install jsonlines
     
    from nemo_curator.utils.distributed_utils import get_client,get_num_workers
    from nemo_curator.utils.file_utils import get_all_files_paths_under, separate_by_metadata
    from nemo_curator.utils.distributed_utils import read_data,write_to_disk
    from nemo_curator.datasets import DocumentDataset
     
    import os
    import pandas as pd
    import time
    import cudf
    import dask_cudf
    import dask
    import numpy as np
    import jsonlines
     
    os.environ["CUDA_VISIBLE_DEVICES"] = "0"
    cur_dir = os.getcwd()
    data_dir = f"{cur_dir}/workspace/"

    運行以下代碼以定義必要的助函數:

    def pre_imports():
        import cudf
     
    def check_jsonl_file(file_dir):
        for file in os.listdir(file_dir):
            if 'jsonl' not in file:
                continue
            with open(os.path.join(file_dir,file), 'r', encoding='utf-8') as f:
                first_line = f.readline()
                print(first_line)
            break
     
    def extract_lines_with_id(file_path,target_list):
        with jsonlines.open(file_path) as reader:
            for obj in reader:
                if obj.get('id') in target_list:
                    yield obj

    適用于多語言數據集的數據管護流程

    泰文維基百科數據集的數據策展流程涉及以下步驟:

    1. 從存檔中下載泰國維基百科,并將數據集提取到 JSONL 文件。
    2. 執行初步數據清理:
      1. 使用語言分隔符過濾數據集中的非泰語主要內容。
      2. 并修復文檔中的 Unicode 文本。
    3. 執行高級數據清理:
      1. 使用 GPU 加速的精確去重功能刪除相同的文檔。
      2. 使用 GPU 加速的模糊去重功能刪除近似相同的文檔。
      3. 應用預定義的啟發式過濾器過濾低質量文檔。

    數據下載

    首先,從存檔中下載泰文維基百科數據。NeMo Curator 中的下載流程包括以下類別:

    • DocumentDownloader:抽象類,用于將遠程數據下載到磁盤。
    • DocumentIterator:抽象類,用于從磁盤讀取數據集的原始記錄。
    • DocumentExtractor:抽象類,用于從磁盤上的記錄中提取文本記錄以及任何相關元數據。

    這些類別非常靈活,因此您可以修改實現以下載任何理想的數據集。NeMo Curator 還提供了下載熱門開源數據集(如 CommonCrawl、Wikipedia 和 arXiv)的實現。在本文中,請使用預定義的下載器下載 Wikipedia 數據集。

    在下載之前,請運行以下代碼以啟動 Dask 客戶端。這將在您的 CPU 上啟動 Dask LocalCluster。除了 deduplication 模塊需要 CPU 集群之外,所有模塊都可以重復使用它。

    from dask.distributed import Client, LocalCluster
    cluster = LocalCluster(n_workers=10, processes=True, memory_limit='16GB')
    client = Client(cluster)

    運行以下代碼以下載泰文維基百科數據集。這將下載泰文維基百科“20240201”快照到您的本地磁盤。要下載其他快照,您可以替換dump_date參數。要下載其他語言的維基百科數據集,您可以替換language參數。下載過程大約需要 1-2 小時。

    from nemo_curator.download import download_wikipedia
     
    download_base_path = os.path.join(data_dir,"wiki_downloads")
    download_output_path = os.path.join(download_base_path,"data")
     
    dump_date = "20240201"
    language = 'th'
     
    res = download_wikipedia(download_output_path,
                       language=language,
                       dump_date=dump_date,
                       url_limit=url_limit).df.compute()

    基本清理

    大型未標記文本語料庫通常包含多種語言。數據管護通常涉及語言特定的步驟,例如使用語言調整的啟發式算法進行質量過濾。

    數據集還可能具有未正確解碼的 Unicode 字符。標記化此類文本會傳播這些問題,可能導致不準確或無意義的標記,從而影響下游任務。

    語言分離 (可選)

    下載的泰語維基百科數據集可能包含其他語言的文檔。如果您想僅保留泰語文檔,可以執行語言分離。

    為了將文檔分類并分離為自己的語言,NeMo Curator 提供了一個預定義的啟發式過濾器 FastTextLangId,其中為每個文檔計算語言分數和語言標簽,然后通過 ScoreFilter 輔助程序將過濾過程應用到數據集。

    運行以下代碼以實現語言分離:

    from nemo_curator import ScoreFilter
    from nemo_curator.filters import FastTextLangId
     
    multilingual_data_path = f"{download_output_directory}/thwiki-20240201-pages-articles-multistream.xml.bz2.jsonl"
     
    language_base_output_path = os.path.join(data_dir,"language_sep")
    language_data_output_path = os.path.join(language_base_output_path,"data")
    language_separated_output_path = os.path.join(language_data_output_path,"language")
     
    model_path = language_base_output_path
     
    # Define key in output .jsonl files to store the language information
    language_field = "language"
     
    #Download language classification model
    !wget https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin -P {model_path}
     
    multilingual_dataset = DocumentDataset.read_json(multilingual_data_path,add_filename=True)
     
    lang_filter = FastTextLangId(os.path.join(model_path,'lid.176.bin'))
    language_id_pipeline = ScoreFilter(lang_filter, score_field=language_field, score_type='object')
    filtered_dataset = language_id_pipeline(multilingual_dataset)
     
    filtered_dataset.df[language_field] = filtered_dataset.df[language_field].apply(lambda score: score[1],meta = (language_field, 'object'))
     
    language_stats = separate_by_metadata(filtered_dataset.df, language_separated_output_path, metadata_field=language_field).compute()

    完成后,運行以下代碼以打印被識別為英語的文檔,從輸出中您可以看到文檔包含一些泰語,但大多數文檔實際上是用英語編寫的。

    check_jsonl_file(os.path.join(language_separated_output_path,'EN'))

    Unicode 重構器

    使用的另一個初步數據清理過程是統一。從互聯網抓取的數據通常包含各種 Unicode 編碼和特殊字符,這可能會導致在進一步處理中出現不一致和錯誤。在抓取的數據上運行統一有助于將文本標準化為一致的格式,使 LLM 訓練更加清晰。

    在 NeMo Curator 中,您可以使用 DocumentModifier 接口來定義數據集中的文檔應如何修改。輔助函數 Modify 接受 DocumentModifier 對象以及 DocumentDataset 對象,并根據修改器修改數據集。

    在以下代碼示例中,將使用語言分離輸出中的泰語子集,并應用預定義的UnicodeReformatter修飾符。

    from nemo_curator import Modify
    from nemo_curator.modifiers import UnicodeReformatter
     
     
    lang_sep_cleaned_data_output_path = os.path.join(language_data_output_path,"cleaned")
    target_language = "TH"
     
    lang_data_path = os.path.join(language_separated_output_path, target_language)
    lang_data = DocumentDataset.read_json(lang_data_path,add_filename=True)
     
    cleaner = Modify(UnicodeReformatter())
    cleaned_data = cleaner(lang_data)
    cleaned_data.to_json(lang_sep_cleaned_data_output_path, write_to_filename=True)

    高級清理

    數據質量無疑是 LLM 訓練性能的最重要因素之一。高級數據管護技術,如重復數據刪除和啟發式過濾,通常應用于提高數據質量。

    本節指導您如何使用 NeMo Curator 應用這些高級技術。

    準備

    在繼續之前,我們建議您為每個文檔添加一個自定義的 ID,以對數據集進行預處理,該 ID 將用作識別重復文檔或低質量文檔的跟蹤器。

    在處理多個數據集時,添加自定義 ID 也變得非常重要,因為每個數據集的原始 ID 可能重復。在這種情況下,自定義 ID 可用于區分不同的數據集。NeMo Curator 提供了一個 AddId 類,以便您以 <prefix>_<id> 的格式插入自定義 ID。

    from nemo_curator import AddId
     
    add_id_input_data_dir = lang_sep_cleaned_data_output_path
    added_id_output_path = os.path.join(data_dir,"add_id/cleaned")
     
    #Format of output ID will be <prefix>_<id>, Define prefix here
    add_ID_id_prefix="TH_wiki"
     
     
    dataset = DocumentDataset.read_json(add_id_input_data_dir,add_filename=True)
    add_id = AddId(id_field='id',id_prefix=add_ID_id_prefix,start_index=0)
    id_dataset = add_id(dataset)
     
    id_dataset.to_json(added_id_output_path, write_to_filename=True)

    完成后,運行以下代碼以檢查輸出。 ID 字段現在遵循 TH_wiki-<id> 的格式。

    check_jsonl_file(added_id_output_path)

    文檔級精確重復數據刪除

    Web 抓取的數據集通常包含跨文檔的許多逐字重復文本序列。在數據集上進行訓練時,如果存在明顯的重復,則可能會導致 LLM 從訓練數據中生成更頻繁的記憶文本,降低學習效率,并在包含訓練重復內容的保留數據上提高困惑度分數。

    在 NeMo Curator 中,the ExactDuplicates 類刪除相同的文檔。

    ExactDuplicates?類用了可用的 CUDA 設備和GPU 加速實現從?RAPIDS cuDF 庫 到高效識別重復文檔。使用 GPU 的并行處理功能獨立對每個文檔進行哈希處理,與基于 CPU 的方法相比,計算密集型重復數據刪除階段得到了顯著加速。

    運行以下代碼以停止當前運行的 CPU Dask 客戶端并啟動一個 GPU Dask 客戶端:

    client.cluster.close()
    client.shutdown()
    client = get_client(cluster_type = 'gpu', set_torch_to_use_rmm=False)
    print(f"Number of dask worker:{get_num_workers(client)}")
    client.run(pre_imports)

    運行以下代碼以進行精確的重復數據刪除:

    from nemo_curator.modules import ExactDuplicates
     
    exact_dedup_input_dataset_dir = added_id_output_path
    exact_dedup_base_output_path = os.path.join(data_dir,"exact_dedup")
    exact_dedup_log_dir = os.path.join(exact_dedup_base_output_path,'log')
    exact_dedup_cache_dir = os.path.join(exact_dedup_base_output_path,'cache')
    exact_dedup_output_dir = os.path.join(exact_dedup_base_output_path,'data')
     
    id_field="id"
     
    !mkdir -p {exact_dedup_log_dir}
    !mkdir -p {exact_dedup_cache_dir}
    !mkdir -p {exact_dedup_output_dir}
     
    input_dataset = DocumentDataset.read_json(exact_dedup_input_dataset_dir, backend='cudf')
     
    exact_dup = ExactDuplicates(
        logger=exact_dedup_log_dir,
        id_field="id",
        text_field="text",
        hash_method="md5",
        cache_dir=exact_dedup_cache_dir
    )
    duplicates = exact_dup(dataset=input_dataset)
     
    exact_docs_to_remove = duplicates.df.map_partitions(
        lambda x: x[x._hashes.duplicated(keep="first")]
    )
    result = input_dataset.df[
    ~input_dataset.df[id_field].isin(exact_docs_to_remove[id_field].compute())
    ]
    DocumentDataset(result).to_json(exact_dedup_output_dir, write_to_filename=True)

    您還可以運行以下代碼,以查看已識別的重復文檔:

    exact_dedup_res = pd.read_parquet(os.path.join(exact_dedup_cache_dir,"_exact_duplicates.parquet"))
    print(f"Number of exact duplicated document:{len(exact_dedup_res)}")
    exact_dedup_res.groupby('_hashes')['id'].agg(lambda x: ' '.join(x)).reset_index().head()

    前面的代碼示例使用 hash 鍵對重復文檔進行分組,您可以在同一組下打印文檔,看看它們是否真的相同。

    target_list =[<duplicat_document_ID1>,...,<duplicat_document_IDX>]
    for line in extract_lines_with_id(os.path.join(exact_dedup_input_dataset_dir,'thwiki-20240201-pages-articles-multistream.xml.bz2.jsonl'),target_list):
        print(line)

    文檔級模糊重復數據刪除

    精確的重復數據刪除只能刪除相同的重復文檔,但網絡抓取的數據集通常包含許多近乎重復的文檔,這些文檔存在精確匹配無法識別的細微差異。因此,需要進行模糊的重復數據刪除來查找和刪除這些近乎重復的文檔,以進一步減少數據集中的冗余。

    在 NeMo Curator 中,the FuzzyDuplicates 類用于刪除近似的文檔。與 the ExactDuplicates 類類似,the FuzzyDuplicates 類使用來自 RAPIDS cuDF 庫 的 GPU 加速實現來加速計算。

    The FuzzyDuplicates 類是 GPU 實現的 MinhashLSH 算法,這是一種快速估計集合之間相似性的技術,例如表示為帶狀痕集(n-grams)的文檔之間的相似性。它能夠以更高效的計算方式在語料庫中找到 Jaccard 相似性對。

    MinhashLSH 算法的實現包括幾個中間步驟。本教程提供了使用高級 FuzzyDuplicates 類的示例。有關每個中間步驟的更多信息,請參閱 NVIDIA/NeMo-Curator GitHub 庫。

    from nemo_curator import FuzzyDuplicates, FuzzyDuplicatesConfig
     
    fuzzy_dedup_data_path = exact_dedup_output_dir
    fuzzy_dedup_base_output_path = os.path.join(data_dir,"fuzzy_wrapper")
    fuzzy_dedup_log_dir = os.path.join(fuzzy_dedup_base_output_path,'log')
    fuzzy_dedup_cache_dir = os.path.join(fuzzy_dedup_base_output_path,'cache')
    fuzzy_dedup_output_dir = os.path.join(fuzzy_dedup_base_output_path,'data')
     
    id_field = 'id'
    text_field = 'text'
     
    !mkdir -p {fuzzy_dedup_log_dir}
    !mkdir -p {fuzzy_dedup_cache_dir}
    !mkdir -p {fuzzy_dedup_output_dir}
     
    with dask.config.set({"dataframe.backend": 'cudf'}):
             
            input_dataset = DocumentDataset.read_json(fuzzy_dedup_data_path, backend='cudf')
     
            fuzzy_dedup_config = FuzzyDuplicatesConfig(
                cache_dir=fuzzy_dedup_cache_dir,
                id_field=id_field,
                text_field=text_field,
                seed=10,
                char_ngrams=5,
                num_buckets=20,
                hashes_per_bucket=13,
                use_64_bit_hash=False,
                buckets_per_shuffle=5,
                false_positive_check=True,
                num_anchors=2,
                jaccard_threshold=0.8,
            )
            fuzzy_dup = FuzzyDuplicates(logger=fuzzy_dedup_log_dir, config=fuzzy_dedup_config)
     
            duplicates = fuzzy_dup(dataset=input_dataset)       
            duplicates.to_parquet(fuzzy_dedup_cache_dir, write_to_filename=False)
             
            fuzzy_docs_to_remove = duplicates.df.map_partitions(
                lambda x: x[x.group.duplicated(keep="first")]
            )
            result = input_dataset.df[
             ~input_dataset.df[id_field].isin(fuzzy_docs_to_remove[id_field].compute())
                ]
            DocumentDataset(result).to_json(fuzzy_dedup_output_dir, write_to_filename=True)

    您還可以運行以下代碼,以查看已識別的近乎重復的文檔:

    fuzzy_dedup_res = pd.read_parquet(f"{fuzzy_dedup_cache_dir}/part.0.parquet")
    fuzzy_dedup_res['id'] = fuzzy_dedup_res['id'].astype(str)
    fuzzy_dedup_res.groupby('group')['id'].agg(lambda x: ', '.join(x)).reset_index()

    前面的代碼示例按 group?字段對重復文檔進行分組,您可以打印同一組下的文檔,看看它們是否幾乎相同。

    target_list = [<duplicat_document_ID1>,...,<duplicat_document_IDX>]
    for line in extract_lines_with_id(os.path.join(fuzzy_dedup_data_path,'thwiki-20240201-pages-articles-multistream.xml.bz2.jsonl'),target_list):
        print(line)

    啟發式過濾

    啟發式過濾使用簡單、高效的計算規則,幫助從數據集中刪除低質量內容。通過應用精心設計的啟發式過濾器,您可以提高預訓練數據的信噪比。在發布時,NeMo Curator 為自然語言提供 24 種啟發式,為編碼語言提供 8 種啟發式。

    在本教程中,您將使用 YAML 配置文件來定義用于啟發式過濾的過濾器。該配置文件可以在 config 文件夾中找到。filter_pipeline helper 會從 config 文件中檢索過濾器設置,并構建一個順序過濾器工作流,以將每個過濾器應用到數據集。

    #Close the GPU Dask cluster and create a CPU Dask cluster
    client.cluster.close()
    client.shutdown()
    cluster = LocalCluster(n_workers=10, processes=True, memory_limit='16GB')
    client = Client(cluster)
     
    from nemo_curator.utils.config_utils import build_filter_pipeline
     
    HF_input_data_dir = fuzzy_dedup_output_dir
    HF_base_output_path = os.path.join(data_dir,'heuristic_filtering')
    kept_document_dir =  os.path.join(HF_base_output_path,'data','hq.parquet')
    filter_config_file = './config/heuristic_filter_non-en.yaml'
     
    !mkdir -p {kept_document_dir}
     
    #Load filters from config
    filter_pipeline = build_filter_pipeline(filter_config_file)
    dataset = DocumentDataset.read_json(HF_input_data_dir, backend='pandas', add_filename=True)
     
    result_data = filter_pipeline(dataset)
     
    result_data.to_parquet(kept_document_dir, write_to_filename=True)

    有關檢查每個過濾器的中間結果(例如檢查針對特定過濾器過濾的文檔)的更多信息,請參閱示例代碼在sample data curation pipeline notebook中的示例代碼。

    后續步驟

    本教程演示了如何為泰文維基百科數據構建樣本數據管護流程,為便于訪問,我們上傳了樣本數據管護流程 notebook

    除了本文中使用的資源外,NeMo Curator 還為其他高級技術提供了接口,例如基于任務的去重、任務識別和去污染、域分類和個人身份信息編輯。有關更多信息,請參閱 GitHub 上的數據整理示例集合

    在 GitHub 存儲庫添加星標,以保持最新開發成果的更新,并接收新功能、bug 修復和未來更新的通知。

    您還可以申請訪問 NVIDIA NeMo Curator 微服務,該微服務為企業提供了從任何地方開始數據管護的最簡單途徑,提供精簡的性能和可擴展性,以縮短上市時間。

    ?

    0

    標簽

    人人超碰97caoporen国产