LLM:huggingface-datasets库

HuggingFace的datasets库是一个轻量级且可扩展的数据加载库,支持pandas、numpy、PyTorch和TensorFlow。它源于TensorFlowDatasets,但使用ApacheArrow进行序列化,能更有效地处理大量数据,避免一次性加载到内存。load_dataset函数用于加载数据,可以根据数据集名称、子数据集、分割等参数进行定制。此外,该库还提供了数据处理功能,如数据类型转换、过滤、排序、拆分、合并等,以及Dataset.map方法进行数据预处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        datasets是huggingface维护的一个轻量级可扩展的数据加载库,其兼容pandas、numpy、pytorch和tensorflow,使用简便。根据其官方简介:Datasets originated from a fork of the awesome TensorFlow Datasets,datasets是源自于tf.data的,两者之间的主要区别可参考这里。

        tf.data相较于pytorch的dataset/dataloader来说,(个人认为)其最强大的一点是可以处理大数据集,而不用将所有数据加载到内存中。datasets的序列化基于Apache Arrow(tf.data基于tfrecord),熟悉spark的应该对apache arrow略有了解。datasets使用的是内存地址映射的方式来直接从磁盘上来读取数据,这使得他能够处理大量的数据。用法简介可参考Quick tour。下面对datasets用法做一些简单的记录。

数据加载

load_dataset

        Datasets库使用的是 Apache Arrow 文件格式,所以你只需要加载你需要的样本到内存中就行了,不需要全量加载。

datasets.load_dataset

(   path: str,
    name: Optional[str] = None,
    data_dir: Optional[str] = None,
    data_files: Optional[Union[str, Sequence[str], Mapping[str, Union[str, Sequence[str]]]]] = None,
    split: Optional[Union[str, Split]] = None,
    cache_dir: Optional[str] = None,
    features: Optional[Features] = None,
    download_config: Optional[DownloadConfig] = None,
    download_mode: Optional[Union[DownloadMode, str]] = None,
    verification_mode: Optional[Union[VerificationMode, str]] = None,
    ignore_verifications="deprecated",
    keep_in_memory: Optional[bool] = None,
    save_infos: bool = False,
    revision: Optional[Union[str, Version]] = None,
    use_auth_token: Optional[Union[bool, str]] = None,
    task: Optional[Union[str, TaskTemplate]] = None,
    streaming: bool = False,
    num_proc: Optional[int] = None,
    storage_options: Optional[Dict] = None,
    **config_kwargs,
) -> Union[DatasetDict, Dataset, IterableDatasetDict, IterableDataset]:

参数

path (str) — Path or name of the dataset. Depending on path, the dataset builder that is used comes from a generic dataset script (JSON, CSV, Parquet, text etc.) or from the dataset script (a python file) inside the dataset directory. local数据组织方式:load_dataset(data_args.dataset_name)。data_args.dataset_name即数据的目录名,里面的数据应该是要命名成train.csv、validation.csv、test.csv,就会被自动生成DatasetDict(3个datasets)。resolve any data file that matches '['train[-._ 0-9/]**', '**[-._ 0-9/]train[-._ 0-9/]**', 'training[-._ 0-9/]**', '**[-._ 0-9/]training[-._ 0-9/]**']' with any supported extension ['csv', 'json', 'txt', 'gif', 'h5', 'hdf', 'png', 'ico', 'jpg', 'jpeg', 'mp3', 'RAW', 'zip'等很多] 

name—表示数据集中的子数据集,当一个数据集包含多个数据集时,就需要这个参数。比如"glue"数据集下就包含"sst2"、“cola”、"qqp"等多个子数据集,此时就需要指定name来表示加载哪一个子数据集。

split (Split or str) — Which split of the data to load. If None, will return a dict with all splits (typically datasets.Split.TRAIN and datasets.Split.TEST). If given, will return a single Dataset. Splits can be combined and specified like in tensorflow-datasets. split="train" 只返回1个datasets,即train的。如果只有一个train.csv,也可以通过只取前n条数据来构建train,如split="train[:5000]"表示只取前5k条数据作为train。[slice-splits]

features (Features, optional) — Set the features type to use for this dataset.

sep或者delimiter—指定数据行的分割符,用法应该是pandas的用法。

header=0——第0行是否作为列名。column_names为主,可被column_names覆盖。

column_names—指定读取数据的列名。如果不指定,就默认第一行吧。所以可以通过这个改名。column_names=['text', 'label']    指定特征column_names[#specify-features]

usecols—指定使用哪些列或列名;a valid list-like usecols parameter would be [0, 1, 2] or ['foo', 'bar', 'baz'].

[pandas小记:pandas数据输入输出_-柚子皮-的博客]cache_dir='/path/to/cache'——这个配置后,好像会卡死!数据集将被缓存到/path/to/cache目录中。如果您再次调用load_dataset并指定相同的cache_dir,则数据将从缓存中加载,而不是从原始数据源重新复制。Note:默认是会将本地数据cp到缓存目录,downloading and preparing dataset csv/chat60d to ~/.cache/huggingface/datasets/...,所以第一次会有Downloading data files。后面则是:datasets.arrow_dataset - Loading cached processed dataset at ~\.cache\huggingface\datasets\...\cache-....arrow。

load_dataset示例

示例1:

dataset_dict = load_dataset(data_args.dataset_name, sep='\t', header=0, column_names=column_names, usecols=['text', 'label'])

或者dataset_dict= load_dataset("csv", data_dir=data_args.dataset_name, delimiter='\t')

Note: 1 即使目录中只有一个train.csv时,也是返回DatasetDict对象。 

Dataset.from_list

示例:从train中取第2条数据,然后转成dataset类型

demo_data = [dataset_dict['train'][1]]
data = Dataset.from_list(demo_data)

保存、重新加载数据集save_to_disk/load_from_disk

from datasets import load_from_disk
encoded_train.save_to_disk('demo_data/')
reloaded_encoded_dataset = load_from_disk('demo_data')

[huggingface-datasets —— tf.data升级版数据加载库 - 简书]

查看datasets信息

print(dataset_dict)

DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 500
    })
    validation: Dataset({
        features: ['text', 'label'],
        num_rows: 400
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 400
    })
})

print(dataset_dict['train'].features)   #获取dataset中各个字段的数据类型
{'text': Value(dtype='string', id=None), 'label': Value(dtype='int64', id=None)}
print(dataset_dict.shape)
{'train': (500, 2), 'validation': (400, 2), 'test': (400, 2)}
print(dataset_dict['train'][:3]) # 也可以dataset_dict['train'].select(range(3))吧
{'text': ['你好', '处理服务', '能尽快帮我发货吗?'], 'label': [0, 0, 0]}
print(dataset_dict.num_rows)
{'train': 500, 'validation': 400, 'test': 400}
print(dataset_dict.column_names)
{'train': ['text', 'label'], 'validation': ['text', 'label'], 'test': ['text', 'label']}

print(dataset.format) #Dataset对象的方法

print(dataset_dict['train'].format)
{'type': None, 'format_kwargs': {}, 'columns': ['text'], 'output_all_columns': False}

# 挑选label列中的前10个进行查看
dataset[label_mask]['label'][:10]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 

数据处理

数据修改:Rename, remove, cast, and flatten

修改datasets数据类型cast_column

示例
from datasets import Value, Sequence
dataset = dataset.cast_column("input_ids", Sequence(feature=Value(dtype='int64', id=None)))
{'input_ids': Sequence(feature=Value(dtype='int32', id=None), length=-1, id=None)...} >> 
{'input_ids': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None)...}

也可以通过[process#cast]改数据类型

rename

[process#rename]

remove

[process#remove​]

set_format

dataset.set_format()方法指定各个字段的输出类型并限制输出的字段,这一般在load数据时用到。
通过reset_format()可以重置数据类型.
dataset.set_format(type='torch', columns=['label', 'gender_0_spot_id_mean'...])

Sort, shuffle, select, split, and shard

sort

sorted_dataset = dataset.sort("label")

select&shuffle

small_dataset = dataset.select([0, 10, 20, 30, 40, 50])

选取dataset的前100条
small_dataset = dataset.select(range(100))

打散后再选取前100条
dataset_dict['train'].shuffle(seed=42).select(range(100))

filter数据过滤

dataset_dict = dataset_dict.filter(lambda line: line['text'] and len(line['text']) > 0 and not line['text'].isspace())
dataset = dataset[np.array(dataset['label']) == 0]

或者在tokenized时候过滤:
tokenized_datasets = dataset_dict.map(tokenize_function,batched=True)
def tokenize_function(examples):
    examples["text"] = [line for line in examples["text"] if line and len(line) > 0 and not line.isspace()]
    return tokenizer(examples["text"], padding=padding, truncation=True, max_length=data_args.max_seq_length)

[process#select-and-filter]

数据集划分train_test_split

dataset_dict = dataset.train_test_split(test_size=0.1, seed=123)
print(dataset_dict )

DatasetDict({
    train: Dataset({
        features: ['text'],
        num_rows: 1170
    })
    test: Dataset({
        features: ['text'],
        num_rows: 130
    })
})使用shard可以将数据集划分为更小的数据集,加载时可分shard加载:
dataset_trn.shard(num_shards=50, index=1)
使用shuffle来打乱数据集:
shuffle_trn = dataset_trn.shuffle(seed=123)

[process#split][#splits]

[process#sort-shuffle-select-split-and-shard]

数据集合并concatenate_datasets

合并数据集的方法concatenate_datasets,当然合并的前提是各个数据集数据类型相同。如两个数据集来自于同一份数据抽取得到的(或者是同时读取的,如在data_files中指定不同dataset名字,或通过split指定名字),则最好先通过flatten_indices()来刷新indices,否则合并时容易报错
from datasets import concatenate_datasets
encoded_train = concatenate_datasets([encoded_trn, encoded_val])

[process#concatenate]

Dataset.map

        为了使我们的数据保持dataset的格式,我们需要使用 Dataset.map 方法。这能够使我们有更大的灵活度,我们直接给 map 函数传入一个函数,这个函数传入 dataset 的一个样本,然后返回映射后的结果。

def map(
    self,
    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: Optional[bool] = None,
    cache_file_name: 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,
    suffix_template: str = "_{rank:05d}_of_{num_proc:05d}",
    new_fingerprint: Optional[str] = None,
    desc: Optional[str] = None,
) -> "Dataset"

参数

batched (`bool`, defaults to `False`):    Provide batch of examples to `function`. 使用batched表示一次处理多行,也可将其设置为False每次仅处理一行,这在数据增强中可能更常用。因为dataset允许我们每次处理的输入行数不等于输出行数,因此数据增强如单词替换一个变多个数据时,进行将label 字段进行copy多分

batch_size (`int`, *optional*, defaults to `1000`):    Number of examples per batch provided to `function` if `batched=True`. If `batch_size <= 0` or `batch_size == None`, provide the full dataset as a single batch to `function`.

示例

def tokenize_function(examples):
    return tokenizer(examples["text"], padding=False, truncation=True, return_tensors="pt")

tokenized_dataset_dict= dataset_dict.map(
    tokenize_function,
    batched=True,
    num_proc=data_args.preprocessing_num_workers,
    remove_columns=[text_column_name],
    load_from_cache_file=not data_args.overwrite_cache,
)

Note:

1 examples的数据类型是<class 'datasets.formatting.formatting.LazyBatch'>,类继承自collections.abc.MutableMapping,是一个类似dict的类。
examples.keys()可能有这些 dict_keys(['input_ids', 'token_type_ids', 'attention_mask', 'special_tokens_mask'])
输入时examples数据是没有padding的:
len(examples['input_ids'][0])=len(examples['attention_mask'][0])=31
len(examples['input_ids'][1])=len(examples['attention_mask'][1])=31

2 map可以设置参数 batched=True,这样子我们的函数可以一下子处理多条数据,而不是每次一条一条地处理。

3 可以在 Dataset.map 中使用 num_proc 参数。如果tokenizer 使用了 use_fast=True 参数,采用的是多线程的方式处理样本。如果没有使用 fast tokenizer,则可以使用num_proc 参数提高速度。

        这里tokenizer没有使用padding,这是因为把所有的样本padding到最大的长度效率很低,通常我们会在构造batch的时候才进行padding,因为我们只需要padding到这个batch中的最大长度,而不是训练的最大长度。这个可以节省非常多的预处理时间。batch中padding在后面的DataCollatorWithPadding[参考LLM:预训练语言模型finetune下游任务]或者DataCollatorForLanguageModeling/DataCollatorForWholeWordMask[LLM:finetune预训练语言模型]。

tokenized后输出

DatasetDict({
    train: Dataset({
        features: ['input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 495
    })
    validation: Dataset({
        features: ['input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 397
    })
    test: Dataset({
        features: ['input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 397
    })
})

DatasetDict['validation'].features:

{'text': Value(dtype='string', id=None), 'label': Value(dtype='int64', id=None),'input_ids': Sequence(feature=Value(dtype='int32', id=None), length=-1, id=None), 'token_type_ids': Sequence(feature=Value(dtype='int8', id=None), length=-1, id=None), 'attention_mask': Sequence(feature=Value(dtype='int8', id=None), length=-1, id=None)}

常规保存的模型和量化后保存的模型,tokenizer应该都没变,所以最后的dataset读取出来经过tokenize后都是如上一样的数据类型。

from:-柚子皮-

ref:https://github.com/huggingface/datasets

Datasets官方文档[Datasets]

``` import json from datasets import Dataset from paddlenlp.trl import SFTConfig, SFTTrainer # 自定义 Trainer 类,增加对 dataset_text_field 参数的支持 class CustomSFTTrainer(SFTTrainer): def __init__(self, dataset_text_field="text", *args, **kwargs): # 保存自定义的目标字段 self.dataset_text_field = dataset_text_field # 删除 kwargs 中的 dataset_text_field,避免传递给父类导致报错 if "dataset_text_field" in kwargs: del kwargs["dataset_text_field"] # 初始化父类 super().__init__(*args, **kwargs) # 如果训练数据集不为空,则将指定字段重命名为训练器默认的字段(假设为 "text") if self.train_dataset is not None: if self.dataset_text_field != "text": if self.dataset_text_field in self.train_dataset.column_names: self.train_dataset = self.train_dataset.rename_column(self.dataset_text_field, "text") else: raise ValueError(f"训练数据集中不存在指定字段:{self.dataset_text_field}") # 从文件加载数据集 with open("hutao_dataset.json", "r", encoding="utf-8") as f: train_data = json.load(f) # 转换为 Hugging Face Dataset 格式 dataset = Dataset.from_list(train_data) # 配置训练参数 training_args = SFTConfig( output_dir="./toxic_coach_model", device="gpu", per_device_train_batch_size=4, num_train_epochs=3, learning_rate=5e-5, save_steps=500, logging_steps=50, ) # 初始化自定义训练器,同时指定 dataset_text_field 为 "tgt" trainer = CustomSFTTrainer( args=training_args, model="deepseek-ai/deepseek-llm-7b-base", train_dataset=dataset, dataset_text_field="text" ) # 开始微调 trainer.train() trainer.save_model() print("模型微调完成,已保存到 ./toxic_coach_model")```RuntimeError: Can't load model for 'deepseek-ai/deepseek-llm-7b-base'. Please make sure that 'deepseek-ai/deepseek-llm-7b-base' is: - a correct model-identifier of built-in pretrained models, - or a correct model-identifier of community-contributed pretrained models, - or the correct path to a directory containing relevant model files.
03-08
<think>好的,我现在需要解决用户在使用自定义的SFTTrainer进行模型微调时遇到的加载错误。用户运行代码时出现了RuntimeError,提示无法加载模型'depthseek-ai/deepseek-llm-7b-base'。我需要逐步分析可能的原因,并提供相应的解决方案。 首先,用户提供的代码中使用了paddlenlp的SFTTrainer,但错误信息可能涉及到模型名称是否正确。首先应该确认模型标识符是否正确。Hugging Face模型中的模型名称通常是正确的,但可能存在拼写错误。用户代码中的模型名称是“deepseek-ai/deepseek-llm-7b-base”,我需要检查这个名称是否存在。访问Hugging Face官网查询,发现确实存在这个模型,因此拼写没有问题。 接下来,考虑用户是否安装了必要的。PaddleNLP可能需要特定的依赖,或者用户可能混淆了不同框架的模型。例如,Hugging Face的Transformers和PaddleNLP的模型可能不兼容。用户代码中导入了paddlenlp.trl,说明使用的是PaddleNLP的SFTTrainer,但模型“deepseek-ai/deepseek-llm-7b-base”可能是一个Hugging Face格式的模型,而PaddleNLP可能无法直接加载它。这时候需要确认PaddleNLP是否支持该模型,或者是否需要转换模型格式。 另外,网络连接问题也可能导致无法下载模型。用户可能需要配置代理或检查网络设置,确保能够访问Hugging Face的模型仓。如果用户处于无法直接访问的环境,可能需要使用镜像站点或者手动下载模型。 还有一种可能性是模型文件不在预期的路径。用户可能需要手动下载模型并指定本地路径。例如,将模型下载到本地目录,然后在代码中指向该目录而不是模型名称。 此外,检查代码中的其他部分是否有错误。例如,自定义的CustomSFTTrainer是否正确处理了数据集字段的重命名,是否存在其他潜在的问题导致模型加载失败。但根据错误信息,问题更可能出在模型加载阶段,而不是训练过程中的数据处理。 综上所述,可能的原因包括:模型名称拼写错误、框架不兼容、缺少必要的依赖、网络问题或模型未正确下载。针对这些可能性,我需要建议用户逐步排查,例如尝试加载模型的最小示例代码,检查是否安装正确版本的,或者尝试使用PaddleNLP支持的模型进行测试。 最后,用户可能需要确认是否应该使用Hugging Face的Trainer而不是PaddleNLP的SFTTrainer,特别是如果模型是Hugging Face格式的。如果框架不兼容,可能需要切换到相应的。</think>这是一个使用PaddleNLP进行模型微调时遇到的模型加载问题。让我们逐步分析可能原因及解决方案: $1. 模型名称验证$ - 当前模型名称:`deepseek-ai/deepseek-llm-7b-base` - 建议访问Hugging Face官网确认该模型是否对PaddleNLP框架开放 - 尝试使用PaddleNLP官方支持的模型(如`ernie-3.0-base-zh`) $2. 框架兼容性检查$ ```python # 检查是否安装正确版本的 import paddle print(paddle.__version__) # 应 >= 2.4.0 import paddlenlp print(paddlenlp.__version__) # 应 >= 2.6.0 ``` $3. 网络连接问题$ - 尝试手动下载模型到本地: ```bash git lfs install git clone https://huggingface.co/deepseek-ai/deepseek-llm-7b-base ``` - 修改代码中模型路径: ```python model="/path/to/local/deepseek-llm-7b-base" ``` $4. 最小验证示例$ ```python from paddlenlp.transformers import AutoModelForCausalLM # 单独测试模型加载 try: model = AutoModelForCausalLM.from_pretrained("deepseek-ai/deepseek-llm-7b-base") print("√ 模型加载成功") except Exception as e: print(f"× 加载失败: {str(e)}") ``` $可能遇到的情况及解决方案$ | 问题类型 | 现象 | 解决方案 | |---------|------|---------| | 模型框架不兼容 | 出现`Weights from XXX not initialized`警告 | 改用Paddle格式的模型(如`ernie`系列) | | 显存不足 | CUDA out of memory | 减小`per_device_train_batch_size`值 | | 依赖冲突 | 出现`ImportError` | 创建干净虚拟环境重新安装:<br>`pip install --upgrade paddlenlp paddlepaddle-gpu` | 如果验证发现框架不兼容,建议改用Hugging Face原生的Trainer: ```python # 改用Hugging Face实现 from transformers import TrainingArguments, Trainer training_args = TrainingArguments( output_dir="./toxic_coach_model", per_device_train_batch_size=4, num_train_epochs=3, learning_rate=5e-5, fp16=True # 启用混合精度训练 ) trainer = Trainer( model=model, args=training_args, train_dataset=dataset ) ``` 建议先通过最小验证示例定位具体问题。如果确认是框架兼容性问题,可考虑以下替代方案: 1. 使用PaddleNLP支持的ERNIE系列模型 2. 改用Hugging Face Transformers进行训练 3. 联系模型提供方获取PaddlePaddle格式的模型权重
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值