聊聊HuggingFace如何处理大模型下海量数据集
翻译自:
Big data? Datasets to the rescue!
如今,使用大GB的数据集并不罕见,特别是从头开始预训练像BERT或GPT-2这样的Tranformer模型。在这样的情况下,甚至连加载数据都可能是一个挑战。例如,用于预训练GPT-2的WebText语料库包含超过800万份文档和40GB的文本——将其加载到电脑的RAM中,可能会使其炸掉。
幸运的是,HuggingFace DataSet 数据集的设计已经克服了这些限制,它通过将数据集视为内存映射文件(Memory-mapped file)来解决内存管理问题,并通过流(Streaming)处理语料库中的条目来解决硬盘驱动器限制。
我们将使用一个巨大的825 GB语料库(称为Pile)来探索HuggingFace数据集的这些特性。
什么是Pile?
Pile是一个英语文本语料库,由EleutherAI创建,用于训练大规模语言模型。它包括各种各样的数据集,涵盖科学文章、GitHub代码库和过滤后的web文本。训练语料库以14GB块的形式提供,你还可以下载几个单独的组件。
从PubMed Abstracts数据集开始,这是PubMed上1500万份生物医学出版物的摘要语料库。数据集是JSON行格式,并使用zstandard库压缩,所以首先我们需要安装它:
!pip install zstandard
接下来,可以使用HuggingFace提供的数据集下载方式来加载:
from datasets import load_dataset
# This takes a few minutes to run, so go grab a tea or coffee while you wait :)
data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
pubmed_dataset
Dataset({
features: ['meta', 'text'],
num_rows: 15518009
})
可以看到,这里有15518009行、2列的数据。可以查看下输出的数据集内容的第一个示例:
print pubmed_dataset[0];
# output:
{'meta': {'pmid': 11409574, 'language': 'eng'},
'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
这看起来像是一篇医学文章的摘要。 现在让我们看看我们使用了多少 RAM 来加载数据集!
memory mapping
在 Python 中测量内存使用情况的一个简单方法是使用 psutil 库,可以使用 pip 安装该库,如下所示:
!pip install psutil
它提供了一个Process类,允许我们检查当前进程的内存使用情况,如下所示:
import psutil
# Process.memory_info is expressed in bytes, so convert to megabytes
print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
# output:
RAM used: 5678.33 MB
这里的 rss 属性指的是驻留集大小,它是进程在 RAM 中占用的内存部分。 此测量还包括 Python 解释器和我们加载的库使用的内存,因此用于加载数据集的实际内存量要小一些。 为了进行比较,我们使用 dataset_size 属性查看数据集在磁盘上的大小。 由于结果像以前一样以字节表示,因此我们需要手动将其转换为千兆字节:
print(f"Number of files in dataset : {pubmed_dataset.dataset_size}")
size_gb = pubmed_dataset.dataset_size / (1024**3)
print(f"Dataset size (cache file) : {size_gb:.2f} GB")
# output
Number of files in dataset : 20979437051
Dataset size (cache file) : 19.54 GB
很好——尽管它有近 20 GB 大,但我们能够用更少的 RAM 加载和访问数据集!
如果你熟悉 Pandas,这个结果可能会让你感到惊讶,因为 Wes Kinney 著名的经验法则是,你通常需要的 RAM 是数据集大小的 5 到 10 倍。 那么HuggingFace数据集是如何解决这个内存管理问题的呢? HuggingFace Datasets 将每个数据集视为内存映射文件,它提供 RAM 和文件系统存储之间的映射,允许库访问和操作数据集的元素,而无需将其完全加载到内存中。
内存映射文件还可以在多个进程之间共享,这使得 Dataset.map() 等方法可以并行化,而无需移动或复制数据集。 在底层,这些功能都是由 Apache Arrow 内存格式和 pyarrow 库实现的,这使得数据加载和处理速度快如闪电。 为了查看实际情况,让我们通过迭代 PubMed Abstracts 数据集中的所有元素来运行一些速度测试:
import timeit
code_snippet = """batch_size = 1000
for idx in range(0, len(pubmed_dataset), batch_size):
_ = pubmed_dataset[idx:idx + batch_size]
"""
time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
print(
f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
)
# output:
'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
这里我们使用Python的timeit模块来测量code_snippet所花费的执行时间。 你通常能够以十分之几 GB/秒到几 GB/秒的速度迭代数据集。 这对于绝大多数应用程序来说都非常有效,但有时你必须使用太大而无法存储在笔记本电脑硬盘上的数据集。 例如,如果我们尝试下载整个 Pile,我们将需要 825 GB 的可用磁盘空间! 为了处理这些情况,Hugging Face Datasets 提供了流式传输功能,允许我们动态下载和访问元素,而无需下载整个数据集。
Streaming Datasets
要启用数据集流式传输,你只需将Streaming=True参数传递给load_dataset()函数。 例如,让我们再次加载 PubMed Abstracts 数据集,但采用流模式:
pubmed_dataset_streamed = load_dataset(
"json", data_files=data_files, split="train", streaming=True
)
Streaming=True 返回的对象不是我们在本章其他地方遇到的熟悉的 Dataset,而是 IterableDataset。 顾名思义,要访问 IterableDataset 的元素,我们需要对其进行迭代。 我们可以访问流数据集的第一个元素,如下所示:
next(iter(pubmed_dataset_streamed))
# output
{'meta': {'pmid': 11409574, 'language': 'eng'},
'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
可以使用IterableDataset.map()即时处理流数据集中的元素,如果你需要对输入进行标记,这在训练期间非常有用。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
next(iter(tokenized_dataset))
# output
{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
还可以使用IterableDataset.shuffle()对流式数据集进行混洗,但与 Dataset.shuffle() 不同,它仅对预定义的 buffer_size 中的元素进行混洗:
shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
next(iter(shuffled_dataset))
# output
{'meta': {'pmid': 11410799, 'language': 'eng'},
'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'}
在此示例中,我们从缓冲区中的前 10,000 个示例中随机选择了一个示例。 一旦访问了一个示例,它在缓冲区中的位置就会被语料库中的下一个示例填充(即上述情况中的第 10,001 个示例)。 还可以使用 IterableDataset.take() 和IterableDataset.skip()函数从流式数据集中选择元素,其作用方式与Dataset.select()类似。 例如,要选择 PubMed Abstracts 数据集中的前 5 个示例,我们可以执行以下操作:
dataset_head = pubmed_dataset_streamed.take(5)
list(dataset_head)
# output
[{'meta': {'pmid': 11409574, 'language': 'eng'},
'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
{'meta': {'pmid': 11409575, 'language': 'eng'},
'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'},
{'meta': {'pmid': 11409576, 'language': 'eng'},
'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."},
{'meta': {'pmid': 11409577, 'language': 'eng'},
'text': 'Oxygen concentrators and cylinders ...'},
{'meta': {'pmid': 11409578, 'language': 'eng'},
'text': 'Oxygen supply in rural africa: a personal experience ...'}]
同样,可以使用 IterableDataset.skip() 函数从打乱的数据集中创建训练和验证分割,如下所示:
# Skip the first 1,000 examples and include the rest in the training set
train_dataset = shuffled_dataset.skip(1000)
# Take the first 1,000 examples for the validation set
validation_dataset = shuffled_dataset.take(1000)
让我们通过一个常见的应用程序来完成对数据集流的探索:将多个数据集组合在一起以创建单个语料库。 Datasets 提供了 interleave_datasets() 函数,它将 IterableDataset 对象列表转换为单个 IterableDataset,其中新数据集的元素是通过源示例之间的交替获得的。 当尝试合并大型数据集时,此函数特别有用,因此我们以流式传输 Pile 的 FreeLaw 子集为例,该子集是来自美国法院的 51 GB 法律意见数据集:
law_dataset_streamed = load_dataset(
"json",
data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
split="train",
streaming=True,
)
next(iter(law_dataset_streamed))
{'meta': {'case_ID': '110921.json',
'case_jurisdiction': 'scotus.tar.gz',
'date_created': '2010-04-28T17:12:49Z'},
'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
这个数据集足够大,足以给大多数笔记本电脑的 RAM 带来压力,但我们已经能够毫不费力地加载和访问它! 现在让我们将 FreeLaw 和 PubMed Abstracts 数据集中的示例与 interleave_datasets() 函数结合起来:
from itertools import islice
from datasets import interleave_datasets
combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
list(islice(combined_dataset, 2))
[{'meta': {'pmid': 11409574, 'language': 'eng'},
'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
{'meta': {'case_ID': '110921.json',
'case_jurisdiction': 'scotus.tar.gz',
'date_created': '2010-04-28T17:12:49Z'},
'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
在这里,我们使用 Python 的 itertools 模块中的 islice() 函数从组合数据集中选择前两个示例,我们可以看到它们与两个源数据集中的第一个示例相匹配。
最后,如果想流式传输整个 825 GB 的 Pile,您可以按如下方式获取所有准备好的文件:
base_url = "https://the-eye.eu/public/AI/pile/"
data_files = {
"train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
"validation": base_url + "val.jsonl.zst",
"test": base_url + "test.jsonl.zst",
}
pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
next(iter(pile_dataset["train"]))
# output
{'meta': {'pile_set_name': 'Pile-CC'},
'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
总结
总结来看,主要是通过内存映射与流处理来实现的大数据集加载,这也是业界比较常用的方案。
聊聊HuggingFace如何处理大模型下海量数据集的更多相关文章
- 人工智能大数据,公开的海量数据集下载,ImageNet数据集下载,数据挖掘机器学习数据集下载
人工智能大数据,公开的海量数据集下载,ImageNet数据集下载,数据挖掘机器学习数据集下载 ImageNet挑战赛中超越人类的计算机视觉系统微软亚洲研究院视觉计算组基于深度卷积神经网络(CNN)的计 ...
- Windows下mnist数据集caffemodel分类模型训练及测试
1. MNIST数据集介绍 MNIST是一个手写数字数据库,样本收集的是美国中学生手写样本,比较符合实际情况,大体上样本是这样的: MNIST数据库有以下特性: 包含了60000个训练样本集和1000 ...
- 华为高级研究员谢凌曦:下一代AI将走向何方?盘古大模型探路之旅
摘要:为了更深入理解千亿参数的盘古大模型,华为云社区采访到了华为云EI盘古团队高级研究员谢凌曦.谢博士以非常通俗的方式为我们娓娓道来了盘古大模型研发的"前世今生",以及它背后的艰难 ...
- 千亿参数开源大模型 BLOOM 背后的技术
假设你现在有了数据,也搞到了预算,一切就绪,准备开始训练一个大模型,一显身手了,"一朝看尽长安花"似乎近在眼前 -- 且慢!训练可不仅仅像这两个字的发音那么简单,看看 BLOOM ...
- DeepSpeed Chat: 一键式RLHF训练,让你的类ChatGPT千亿大模型提速省钱15倍
DeepSpeed Chat: 一键式RLHF训练,让你的类ChatGPT千亿大模型提速省钱15倍 1. 概述 近日来,ChatGPT及类似模型引发了人工智能(AI)领域的一场风潮. 这场风潮对数字世 ...
- 大数据下的数据分析平台架构zz
转自http://www.cnblogs.com/end/archive/2012/02/05/2339152.html 随着互联网.移动互联网和物联网的发展,谁也无法否认,我们已经切实地迎来了一个海 ...
- 海量数据集利用Minhash寻找相似的集合【推荐优化】
MinHash 首先它是一种基于 Jaccard Index 相似度的算法,也是一种 LSH 的降维的方法,应用于大数据集的相似度检索.推荐系统.下边按我的理解介绍下MinHash 问题背景 给出N个 ...
- 我是如何处理大并发量订单处理的 KafKa部署总结
今天要介绍的是消息中间件KafKa,应该说是一个很牛的中间件吧,背靠Apache 与很多有名的中间件搭配起来用效果更好哦 ,为什么不用RabbitMQ,因为公司需要它. 网上已经有很多怎么用和用到哪的 ...
- (转载)公开的海量数据集 Public Research-Quality Datasets
转载自:http://rensanning.iteye.com/blog/1601663 海量数据数据集 海量数据(又称大数据)已经成为各大互联网企业面临的最大问题,如何处理海量数据,提供更好的解决方 ...
- 2018-02-03-PY3下经典数据集iris的机器学习算法举例-零基础
---layout: posttitle: 2018-02-03-PY3下经典数据集iris的机器学习算法举例-零基础key: 20180203tags: 机器学习 ML IRIS python3mo ...
随机推荐
- phpstudy-pikachu-字符型注入(get)
在查询栏输入1,点击查询获得查询格式 ?name=1'&submit=查询 *捷径 ' or 1=1 --+ *非捷径 ?name=1' and 1=2 --+ 2--+&submit ...
- WPF 入门笔记 - 01 - 入门基础以及常用布局
本篇为学习博客园大佬圣殿骑士的<WPF基础到企业应用系列>以及部分DotNet菜园的<WPF入门教程系列>所作笔记,对应圣殿骑士<WPF基础到企业应用系列>第 1 ...
- C# decimal double 获取一组数字 小数点后最多有几位
有一组数字,想判断一组数字中最多的有几位小数,乘以10的指定幂,转为整数,此处教大家一个高级的写法,拒接无脑for循环 decimal: decimal[] numbers = new decimal ...
- 代码随想录算法训练营Day48 动态规划
代码随想录算法训练营 代码随想录算法训练营Day48 动态规划|198.打家劫舍 213.打家劫舍II 337.打家劫舍III 198.打家劫舍 题目链接:198.打家劫舍 你是一个专业的小偷,计划偷 ...
- IntelliJ IDEA 最新激活码:2023、2022及以下版本通用(亲测有效)
分享一下 IntelliJ IDEA 2023.1 最新激活注册码,破解教程如下,可免费永久激活,亲测有效,下面是详细文档哦~ 申明:本教程 IntelliJ IDEA 破解补丁.激活码均收集于网络, ...
- 使用RSS打造你的科研资讯头条
本文章为 "生信草堂" 首发,经生信草堂授权.原作者(Steven Shen)同意转载.由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访 ...
- 20个Golang片段让我不再健忘
前言 本文使用代码片段的形式来解释在 go 语言开发中经常遇到的小功能点,由于本人主要使用 java 开发,因此会与其作比较,希望对大家有所帮助. 1. hello world 新手村的第一课,毋庸置 ...
- 尚医通day01-【项目环境搭建和医院设置详细步骤】(内附源码)
第01章-项目介绍 1.课程介绍 项目名称:尚医通预约挂号统一平台 项目原型:https://www.114yygh.com 北京市预约挂号统一平台 项目技术栈:前后端分离 后端技术:SpringBo ...
- 深度解读 Linux 内核级通用内存池 —— kmalloc 体系
本文是笔者 slab 系列的最后一篇文章,为了方便大家快速检索,先将相关的文章列举出来: <细节拉满,80 张图带你一步一步推演 slab 内存池的设计与实现> <从内核源码看 sl ...
- ASIC加速技术在ASIC加速性能优化中的新应用与挑战
目录 1. 引言 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 5. 优化与改进 1. 引言 随着计算机技术的发展,芯片的性能和面积都得到了极大的提升.为了进一步提高芯片的 ...