Hub documentation
PyArrow
PyArrow
Arrow์ ์ปฌ๋ผํ ํ์์ด๋ฉฐ, ๋น ๋ฅธ ๋ฐ์ดํฐ ๊ตํ๊ณผ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ถ์์ ์ํ ๋๊ตฌ ๋ชจ์์
๋๋ค.
PyArrow๊ฐ ์๊ฒฉ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ฐ๊ธฐ ์ํด fsspec์ ์ง์ํ๋ฏ๋ก, Hugging Face ๊ฒฝ๋ก(hf://)๋ฅผ ์ฌ์ฉํ์ฌ Hub์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ธ ์ ์์ต๋๋ค.
Hugging Face์์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ํ์ผ ํ์์ด Parquet์ด๊ธฐ ๋๋ฌธ์ Parquet ๋ฐ์ดํฐ์ ํนํ ์ ์ฉํฉ๋๋ค.
์ค์ ๋ก Parquet์ ๊ตฌ์กฐ, ํ์ดํ, ๋ฉํ๋ฐ์ดํฐ ๋ฐ ์์ถ ๋๋ถ์ ํนํ ํจ์จ์ ์
๋๋ค.
ํ ์ด๋ธ ๋ก๋
๋ก์ปฌ ํ์ผ ๋๋ Hugging Face Datasets์ ๊ฐ์ ์๊ฒฉ ์ ์ฅ์์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ ์ ์์ต๋๋ค. PyArrow๋ CSV, JSON์ ํฌํจํ์ฌ Parquet ๋ฑ ๋ค์ํ ํฌ๋งท์ ์ง์ํฉ๋๋ค:
>>> import pyarrow.parquet as pq
>>> table = pq.read_table("path/to/data.parquet")Hugging Face์์ ํ์ผ์ ๋ก๋ํ๋ ค๋ฉด ๊ฒฝ๋ก๊ฐ hf://๋ก ์์ํด์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, stanfordnlp/imdb ๋ฐ์ดํฐ์
์ ์ฅ์์ ๊ฒฝ๋ก๋ hf://datasets/stanfordnlp/imdb์
๋๋ค. Hugging Face์ ๋ฐ์ดํฐ์
์๋ ์ฌ๋ฌ Parquet ํ์ผ์ด ํฌํจ๋์ด ์์ต๋๋ค. Parquet ํ์ผ ํ์์ ๋ฐ์ดํฐํ๋ ์์ ์ฝ๊ธฐ ๋ฐ ์ฐ๊ธฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๋ง๋ค๊ณ , ๋ฐ์ดํฐ ๋ถ์ ์ธ์ด ๊ฐ ๋ฐ์ดํฐ ๊ณต์ ๋ฅผ ์ฝ๊ฒ ํ๋๋ก ์ค๊ณ๋์์ต๋๋ค. plain_text/train-00000-of-00001.parquet ํ์ผ์ pyarrow Table๋ก ๋ก๋ํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค(pyarrow>=21.0 ํ์):
>>> import pyarrow.parquet as pq
>>> table = pq.read_table("hf://datasets/stanfordnlp/imdb/plain_text/train-00000-of-00001.parquet")
>>> table
pyarrow.Table
text: string
label: int64
----
text: [["I rented I AM CURIOUS-YELLOW from my video store because of all the controversy that surrounded it (... 1542 chars omitted)", ...],...,[..., "The story centers around Barry McKenzie who must go to England if he wishes to claim his inheritan (... 221 chars omitted)"]]
label: [[0,0,0,0,0,...,0,0,0,0,0],...,[1,1,1,1,1,...,1,1,1,1,1]]์ ์ฒด Parquet ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ์ง ์์ผ๋ ค๋ฉด Parquet ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ row group์ ํ๋์ฉ ๋ก๋ํ ์ ์์ต๋๋ค:
>>> import pyarrow.parquet as pq
>>> pf = pq.ParquetFile("hf://datasets/stanfordnlp/imdb/plain_text/train-00000-of-00001.parquet")
>>> pf.metadata
<pyarrow._parquet.FileMetaData object at 0x1171b4090>
created_by: parquet-cpp-arrow version 12.0.0
num_columns: 2
num_rows: 25000
num_row_groups: 25
format_version: 2.6
serialized_size: 62036
>>> for i in pf.num_row_groups:
... table = pf.read_row_group(i)
... ...Hugging Face ๊ฒฝ๋ก ๋ฐ ๊ตฌํ ๋ฐฉ์์ ๋ํ ์์ธํ ๋ด์ฉ์ HfFileSystem์ ๋ํ ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
ํ ์ด๋ธ ์ ์ฅ
pyarrow.parquet.write_table๋ฅผ ์ฌ์ฉํ์ฌ pyarrow Table์ ๋ก์ปฌ ํ์ผ ๋๋ Hugging Face์ ์ง์ ์ ์ฅํ ์ ์์ต๋๋ค.
Hugging Face์ Table์ ์ ์ฅํ๋ ค๋ฉด ๋จผ์ Hugging Face ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํด์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
hf auth login๊ทธ ํ ๋ฐ์ดํฐ์ ์ ์ฅ์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
from huggingface_hub import HfApi
HfApi().create_repo(repo_id="username/my_dataset", repo_type="dataset")๋ง์ง๋ง์ผ๋ก, PyArrow์์ Hugging Face ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
import pyarrow.parquet as pq
pq.write_table(table, "hf://datasets/username/my_dataset/imdb.parquet", use_content_defined_chunking=True)
# or write in separate files if the dataset has train/validation/test splits
pq.write_table(table_train, "hf://datasets/username/my_dataset/train.parquet", use_content_defined_chunking=True)
pq.write_table(table_valid, "hf://datasets/username/my_dataset/validation.parquet", use_content_defined_chunking=True)
pq.write_table(table_test , "hf://datasets/username/my_dataset/test.parquet", use_content_defined_chunking=True)Hugging Face์ Parquet ํ์ผ์ ์ ์ฅ ํจ์จ์ฑ์ ๋์ด๊ณ , ๋ค์ด๋ก๋ ๋ฐ ์ ๋ก๋๋ฅผ ๊ฐ์ํํ๋ฉฐ, ํจ์จ์ ์ธ ๋ฐ์ดํฐ์ ์คํธ๋ฆฌ๋ฐ ๋ฐ ํธ์ง์ ๊ฐ๋ฅํ๋๋ก ์ต์ ํ๋์ด ์์ต๋๋ค:
- Parquet Content Defined Chunking์ Hugging Face์ ์คํ ๋ฆฌ์ง ๋ฐฑ์๋์ธ Xet์ ์ต์ ํ๋ Parquet์ ๋๋ค. ์ฒญํฌ ๊ธฐ๋ฐ ์ค๋ณต ์ ๊ฑฐ๋ฅผ ํตํด ์ ๋ก๋์ ๋ค์ด๋ก๋๋ฅผ ๊ฐ์ํํ๋ฉฐ, ํจ์จ์ ์ธ ํ์ผ ํธ์ง์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
- Page index๋ ์คํธ๋ฆฌ๋ฐ ์ ํํฐ๋ฅผ ๊ฐ์ํํ๊ณ , Dataset Viewer์ ๊ฐ์ ํ๊ฒฝ์์ ํจ์จ์ ์ธ ๋๋ค ์ก์ธ์ค๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
PyArrow๋ ์ต์ ํ๋ Parquet ํ์ผ์ ์์ฑํ๋ ค๋ฉด ์ถ๊ฐ ์ธ์๊ฐ ํ์ํฉ๋๋ค:
import pyarrow.parquet as pq
pq.write_table(
table,
"hf://datasets/username/my_dataset/imdb.parquet",
# Optimize for Xet
use_content_defined_chunking=True,
write_page_index=True,
)use_content_defined_chunking=True๋ฅผ ์ค์ ํ์ฌ Parquet Content Defined Chunking์ ํ์ฑํํฉ๋๋ค. ์ค๋ณต ์ ๊ฑฐ ๋ฐ ํธ์ง์ ์ฌ์ฉ๋ฉ๋๋ค(pyarrow>=21.0ํ์).write_page_index=True๋ฅผ ์ค์ ํ์ฌ Parquet ๋ฉํ๋ฐ์ดํฐ์ ํ์ด์ง ์ธ๋ฑ์ค๋ฅผ ํฌํจํ๋ฉด ์คํธ๋ฆฌ๋ฐ ๋ฐ ๋๋ค ์ก์ธ์ค๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
Content defined chunking(CDC)์ Parquet writer๊ฐ ์ค๋ณต ๋ฐ์ดํฐ๊ฐ ๋์ผํ๊ฒ chunk๋๊ณ ์์ถ๋๋๋ก ๋ฐ์ดํฐ ํ์ด์ง๋ฅผ chunkingํฉ๋๋ค. CDC๊ฐ ์์ผ๋ฉด ํ์ด์ง๊ฐ ์์๋ก chunking๋๋ฏ๋ก, ์์ถ ๋๋ฌธ์ ์ค๋ณต ๋ฐ์ดํฐ๋ฅผ ๊ฐ์งํ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค. CDC ๋๋ถ์ ์ค๋ณต ๋ฐ์ดํฐ๊ฐ ํ ๋ฒ๋ง ์ ๋ก๋ ๋๋ ๋ค์ด๋ก๋๋๋ฏ๋ก, Hugging Face๋ก์ Parquet ์ ๋ก๋ ๋ฐ ๋ค์ด๋ก๋๊ฐ ๋ ๋นจ๋ผ์ง๋๋ค.
Xet์ ๋ํ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ์์ ํ์ธํ์ธ์.
Parquet์ Xet ์ค๋ณต ์ ๊ฑฐ ํ์ฉ
์ต์ ํ๋ Parquet ํ์ผ์ ์ค๋ณต ์ ๊ฑฐ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ Content Defined Chunking์ผ๋ก ์์ฑ๋ฉ๋๋ค. ์ด๋ก ์ธํด Hugging Face์ ์ด๋ฏธ ์กด์ฌํ๋ ๋ฐ์ดํฐ ์ฒญํฌ๋ฅผ ๋ค์ ์ ๋ก๋ํ ํ์๊ฐ ์์ด ์ ๋ก๋ ์๋๊ฐ ๋นจ๋ผ์ง๊ณ , I/O๋ฅผ ํฌ๊ฒ ์ ์ฝํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ด ์ฝ๋๋ table์ ๋ด์ฉ์ ์
๋ก๋ํ ํ edited_table์ ๊ฒฝ์ฐ ๋ณ๊ฒฝ๋ ์ฒญํฌ๋ง ์
๋ก๋ํ๋ฏ๋ก ์
๋ก๋๊ฐ ๋ ๋น ๋ฆ
๋๋ค:
import pyarrow.parquet as pq
pq.write_table(
table,
"hf://datasets/username/my_dataset/imdb.parquet",
# Optimize for Xet
use_content_defined_chunking=True,
write_page_index=True,
)
edited_table = ... # e.g. with added/modified/removed rows or columns
pq.write_table(
edited_table,
"hf://datasets/username/my_dataset/imdb.parquet",
# Optimize for Xet
use_content_defined_chunking=True,
write_page_index=True,
)Chunk์ ์ฝ 64kB์ด๋ฉฐ Parquet๋ ์ปฌ๋ผ๋ณ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ฏ๋ก, ์ค์ ๋ก Optimized Parquet ํ์ผ์ ํธ์งํ ๋ ๋ค์๊ณผ ๊ฐ์ ์ผ์ด ๋ฐ์ํฉ๋๋ค:
- ์๋ก์ด ์ด์ ์ถ๊ฐํ๋ฉด -> ์๋ก์ด ์ด์ ์ฒญํฌ๋ง ์ ๋ก๋๋ฉ๋๋ค
- add/edit/delete a row -> ์ปฌ๋ผ๋น ํ๋์ chunk๊ฐ ์ ๋ก๋๋ฉ๋๋ค
๊ทธ๋ฆฌ๊ณ ์ด ์ธ์๋ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ Parquet ํธํฐ์ ์ฒญํฌ๋ ์ ๋ก๋๋ฉ๋๋ค.
์ด๋ฏธ์ง ์ฌ์ฉ
์ด๋ฏธ์ง ์ด๋ฆ ๋๋ ๊ฒฝ๋ก ํ๋๋ฅผ ํฌํจํ๋ ๋ฉํ๋ฐ์ดํฐ ํ์ผ์ด ์๋ ํด๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ๋ก๋ํ ์ ์์ต๋๋ค:
Example 1: Example 2:
folder/ folder/
โโโ metadata.parquet โโโ metadata.parquet
โโโ img000.png โโโ images
โโโ img001.png โโโ img000.png
... ...
โโโ imgNNN.png โโโ imgNNN.png์ด๋ฏธ์ง ๊ฒฝ๋ก๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๋ณตํ ์ ์์ต๋๋ค:
from pathlib import Path
import pyarrow as pq
folder_path = Path("path/to/folder")
table = pq.read_table(folder_path + "metadata.parquet")
for file_name in table["file_name"].to_pylist():
image_path = folder_path / file_name
...๋ฐ์ดํฐ์
์ด ์ง์๋๋ ๊ตฌ์กฐ(file_name ํ๋๊ฐ ํฌํจ๋ metadata.parquet ํ์ผ)๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฏ๋ก, ์ด ๋ฐ์ดํฐ์
์ Hugging Face์ ์ ์ฅํ๋ฉด Dataset Viewer์์ ๋ฉํ๋ฐ์ดํฐ์ ์ด๋ฏธ์ง๊ฐ ๋ชจ๋ ํ์๋ฉ๋๋ค.
from huggingface_hub import HfApi
api = HfApi()
api.upload_folder(
folder_path=folder_path,
repo_id="username/my_image_dataset",
repo_type="dataset",
)Parquet ๋ด๋ถ์ ์ด๋ฏธ์ง ์๋ฒ ๋
PyArrow์๋ Arrow ํ ์ด๋ธ์ ์ด๋ฏธ์ง ๋ฐ์ดํธ๋ฅผ ์ ์ฅํ ์ ์๋ ๋ฐ์ด๋๋ฆฌ ํ์ ์ด ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ด๋ฏธ์ง(๋ฐ์ดํธ ๋ฐ ๊ฒฝ๋ก)์ ์ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ํฌํจํ๋ ๋จ์ผ Parquet ํ์ผ๋ก ๋ฐ์ดํฐ์ ์ ์ ์ฅํ ์ ์์ต๋๋ค:
import pyarrow as pa
import pyarrow.parquet as pq
# Embed the image bytes in Arrow
image_array = pa.array([
{
"bytes": (folder_path / file_name).read_bytes(),
"path": file_name,
}
for file_name in table["file_name"].to_pylist()
])
table.append_column("image", image_array)
# (Optional) Set the HF Image type for the Dataset Viewer and the `datasets` library
features = {"image": {"_type": "Image"}} # or using datasets.Features(...).to_dict()
schema_metadata = {"huggingface": {"dataset_info": {"features": features}}}
table = table.replace_schema_metadata(schema_metadata)
# Save to Parquet
# (Optional) with use_content_defined_chunking for faster uploads and downloads
pq.write_table(table, "data.parquet", use_content_defined_chunking=True)Arrow ์คํค๋ง ๋ฉํ๋ฐ์ดํฐ์ Image ์ ํ์ ์ค์ ํ๋ฉด ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ Hugging Face Dataset Viewer๊ฐ โimageโ ํ๋์ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์๋ ์ด๋ฏธ์ง๊ฐ ํฌํจ๋์ด ์์์ ์ ์ ์์ต๋๋ค.
Audios ์ฌ์ฉ
๋ฉํ๋ฐ์ดํฐ ํ์ผ์ ์ค๋์ค์ ์ด๋ฆ ๋๋ ๊ฒฝ๋ก์ ๋ํ ํ๋๊ฐ ํฌํจ๋ ํด๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ๋ก๋ํ ์ ์์ต๋๋ค:
Example 1: Example 2:
folder/ folder/
โโโ metadata.parquet โโโ metadata.parquet
โโโ rec000.wav โโโ audios
โโโ rec001.wav โโโ rec000.wav
... ...
โโโ recNNN.wav โโโ recNNN.wav์์ฑ ํ์ผ ๊ฒฝ๋ก๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๋ณตํ ์ ์์ต๋๋ค:
from pathlib import Path
import pyarrow as pq
folder_path = Path("path/to/folder")
table = pq.read_table(folder_path + "metadata.parquet")
for file_name in table["file_name"].to_pylist():
audio_path = folder_path / file_name
...๋ฐ์ดํฐ์
์ด ์ง์๋๋ ๊ตฌ์กฐ(file_name ํ๋๊ฐ ํฌํจ๋ metadata.parquet ํ์ผ)๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฏ๋ก Hugging Face์ ์ ์ฅํ ์ ์์ผ๋ฉฐ, Hub Dataset Viewer์์๋ ๋ฉํ๋ฐ์ดํฐ์ ์ค๋์ค๋ฅผ ๋ชจ๋ ํ์ธํ ์ ์์ต๋๋ค.
from huggingface_hub import HfApi
api = HfApi()
api.upload_folder(
folder_path=folder_path,
repo_id="username/my_audio_dataset",
repo_type="dataset",
)Parquet ๋ด๋ถ์ ์ค๋์ค ์๋ฒ ๋
PyArrow์๋ Arrow ํ ์ด๋ธ์ ์ค๋์ค ๋ฐ์ดํธ๋ฅผ ํฌํจํ ์ ์๋ ๋ฐ์ด๋๋ฆฌ ํ์ ์ด ์์ต๋๋ค. ๋ฐ๋ผ์ ์ค๋์ค(๋ฐ์ดํธ ๋ฐ ๊ฒฝ๋ก)์ ์ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ํฌํจํ๋ ๋จ์ผ Parquet ํ์ผ๋ก ๋ฐ์ดํฐ์ ์ ์ ์ฅํ ์ ์์ต๋๋ค:
import pyarrow as pa
import pyarrow.parquet as pq
# Embed the audio bytes in Arrow
audio_array = pa.array([
{
"bytes": (folder_path / file_name).read_bytes(),
"path": file_name,
}
for file_name in table["file_name"].to_pylist()
])
table.append_column("audio", audio_array)
# (Optional) Set the HF Audio type for the Dataset Viewer and the `datasets` library
features = {"audio": {"_type": "Audio"}} # or using datasets.Features(...).to_dict()
schema_metadata = {"huggingface": {"dataset_info": {"features": features}}}
table = table.replace_schema_metadata(schema_metadata)
# Save to Parquet
# (Optional) with use_content_defined_chunking for faster uploads and downloads
pq.write_table(table, "data.parquet", use_content_defined_chunking=True)Arrow ์คํค๋ง ๋ฉํ๋ฐ์ดํฐ์ Audio ์ ํ์ ์ค์ ํ๋ฉด ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ Hugging Face Dataset Viewer๊ฐ โaudioโ๊ฐ ๋จ์ํ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ ์ค๋์ค ๋ฐ์ดํฐ์์ ์ธ์ํ ์ ์์ต๋๋ค.
Update on GitHub