Hub documentation
Dask
Dask
Dask๋ ๊ธฐ์กด Python ๋ฐ PyData ์ํ๊ณ๋ฅผ ํ์ฅํ๋ ๋ณ๋ ฌ ๋ฐ ๋ถ์ฐ ์ปดํจํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
ํนํ Dask DataFrame๋ฅผ ์ฌ์ฉํ๋ฉด pandas ์ํฌํ๋ก๋ฅผ ํ์ฅํ ์ ์์ต๋๋ค. Dask DataFrame์ pandas๋ฅผ ๋ณ๋ ฌํํ์ฌ ๋๊ท๋ชจ ํ ํ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. pandas API๋ฅผ ๋ฐ์ ํ๊ฒ ๋ฐ์ํ๋ฏ๋ก, ๋จ์ผ ๋ฐ์ดํฐ์ ์์ ํ ์คํธํ๋ ๊ฒ์ ์ ์ฒด ๋ฐ์ดํฐ์ ์ฒ๋ฆฌ๋ก ์ ํํ๋ ๊ฒ์ด ๊ฐ๋จํฉ๋๋ค. Dask๋ Hugging Face Datasets์ ๊ธฐ๋ณธ ํ์์ธ Parquet์ ํนํ ํจ๊ณผ์ ์ผ๋ก ์๋ํ๋ฉฐ, ํ๋ถํ ๋ฐ์ดํฐ ์ ํ, ํจ์จ์ ์ธ ์ปฌ๋ผ ํํฐ๋ง, ์์ถ์ ์ง์ํฉ๋๋ค.
Dask์ ์ข์ ์ค์ฉ์ ์ฌ์ฉ ์ฌ๋ก๋ ๋ฐ์ดํฐ์ ์ ๋ํด ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋๋ ๋ชจ๋ธ ์ถ๋ก ์ ๋ถ์ฐ ๋ฐฉ์์ผ๋ก ์คํํ๋ ๊ฒ์ ๋๋ค. ์๋ฅผ ๋ค์ด, Coiled์ Hugging Face + Dask๋ฅผ ํ์ฉํ AI ๊ธฐ๋ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํ์ฅ์ ๋ํ ํ๋ฅญํ ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ์ ์ฐธ๊ณ ํ์ธ์.
์ฝ๊ธฐ ๋ฐ ์ฐ๊ธฐ
Dask์ ์๊ฒฉ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ฐ๊ธฐ ์ํด fsspec๋ฅผ ์ฌ์ฉํ๋ฏ๋ก, Hugging Face ๊ฒฝ๋ก(hf://)๋ฅผ ์ฌ์ฉํ์ฌ Hub์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ธ ์ ์์ต๋๋ค.
๋จผ์ Hugging Face ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํด์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
hf auth login๊ทธ ํ ๋ฐ์ดํฐ์ ์ ์ฅ์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
from huggingface_hub import HfApi
HfApi().create_repo(repo_id="username/my_dataset", repo_type="dataset")๋ง์ง๋ง์ผ๋ก, Dask์์ Hugging Face ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. Dask DataFrame์ Hugging Face์ Parquet์ ๋ถ์ฐ์ผ๋ก ๊ธฐ๋กํ๋ ๊ฒ์ ์ง์ํ๋ฉฐ, ์ด๋ ์ปค๋ฐ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ์ฌํญ์ ์ถ์ ํฉ๋๋ค:
import dask.dataframe as dd
df.to_parquet("hf://datasets/username/my_dataset")
# or write in separate directories if the dataset has train/validation/test splits
df_train.to_parquet("hf://datasets/username/my_dataset/train")
df_valid.to_parquet("hf://datasets/username/my_dataset/validation")
df_test .to_parquet("hf://datasets/username/my_dataset/test")์ด ๋ฐฉ์์ ํ์ผ๋ณ๋ก ์ปค๋ฐ์ ํ๋์ฉ ์์ฑํ๋ฏ๋ก, ์ ๋ก๋ ํ ํ์คํ ๋ฆฌ๋ฅผ ์ค์ฟผ์ํ๋ ๊ฒ์ด ๊ถ์ฅ๋ฉ๋๋ค:
from huggingface_hub import HfApi
HfApi().super_squash_history(repo_id=repo_id, repo_type="dataset")์ด ์์
์ Parquet ํ์์ Dask ๋ฐ์ดํฐ์
์ ํฌํจํ๋ ๋ฐ์ดํฐ์
์ ์ฅ์ username/my_dataset๋ฅผ ์์ฑํฉ๋๋ค.
๋์ค์ ๋ค์ ๋ก๋ํ ์ ์์ต๋๋ค:
import dask.dataframe as dd
df = dd.read_parquet("hf://datasets/username/my_dataset")
# or read from separate directories if the dataset has train/validation/test splits
df_train = dd.read_parquet("hf://datasets/username/my_dataset/train")
df_valid = dd.read_parquet("hf://datasets/username/my_dataset/validation")
df_test = dd.read_parquet("hf://datasets/username/my_dataset/test")Hugging Face ๊ฒฝ๋ก ๋ฐ ๊ตฌํ ๋ฐฉ์์ ๋ํ ์์ธํ ๋ด์ฉ์ HfFileSystem์ ๋ํ ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋ฐ์ดํฐ ์ฒ๋ฆฌ
Dask๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ์
์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ๋ ค๋ฉด, ๋จผ์ pandas DataFrame ๋๋ Series์ ๋ํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํจ์๋ฅผ ์ ์ํ ๋ค์, Dask์ map_partitions ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด ํจ์๋ฅผ ๋ฐ์ดํฐ์
์ ๋ชจ๋ ํํฐ์
์ ๋ณ๋ ฌ๋ก ์ ์ฉํ ์ ์์ต๋๋ค:
def dummy_count_words(texts):
return pd.Series([len(text.split(" ")) for text in texts])๋๋ pandas ๋ฌธ์์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ์ ์ฌํ ํจ์(๋ ๋น ๋ฆ):
def dummy_count_words(texts):
return texts.str.count(" ")pandas์์๋ ์ด ํจ์๋ฅผ ํ ์คํธ ์ด์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
# pandas API
df["num_words"] = dummy_count_words(df.text)๊ทธ๋ฆฌ๊ณ Dask์์๋ ์ด ํจ์๋ฅผ ๋ชจ๋ ํํฐ์ ์ ์คํํ ์ ์์ต๋๋ค:
# Dask API: run the function on every partition
df["num_words"] = df.text.map_partitions(dummy_count_words, meta=int)ํจ์์ ์ถ๋ ฅ์ธ pandas Series ๋๋ DataFrame์ ์ ํ์ธ meta๋ ์ ๊ณตํด์ผ ํ๋ค๋ ์ ์ ์ ์ํ์ธ์.
์ด๋ Dask DataFrame์ด ์ง์ฐ ํ๊ฐ(lazy) API๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ์ํฉ๋๋ค. Dask๋ .compute()๊ฐ ํธ์ถ๋ ๋๋ง ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์คํํ๋ฏ๋ก, ๊ทธ ์ฌ์ด์ ์๋ก์ด ์ด์ ์ ํ์ ํ์
ํ๊ธฐ ์ํด meta ์ธ์๊ฐ ํ์ํฉ๋๋ค.
Predicate and Projection Pushdown
Hugging Face์์ Parquet ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ๋, Dask๋ Parquet ํ์ผ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ํ์ฉํ์ฌ ํ์ํ์ง ์์ ํ์ผ์ด๋ row group ์ ์ฒด๋ฅผ ๊ฑด๋๋๋๋ค. ์๋ฅผ ๋ค์ด Parquet ํ์์ Hugging Face Dataset์ ํํฐ(predicate)๋ฅผ ์ ์ฉํ๊ฑฐ๋ ์ปฌ๋ผ์ ๋ถ๋ถ ์งํฉ(projection)์ ์ ํํ๋ ๊ฒฝ์ฐ, Dask๋ Parquet ํ์ผ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด ํ์ํ์ง ์์ ๋ถ๋ถ์ ๋ค์ด๋ก๋ํ์ง ์๊ณ ํ๊ธฐํฉ๋๋ค.
Dask DataFrame API์ ์ฌ๊ตฌํ์ ํตํด ์ฟผ๋ฆฌ ์ต์ ํ๋ฅผ ์ง์ํ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด Dask๊ฐ ๋ ๋น ๋ฅด๊ณ ๊ฒฌ๊ณ ํด์ก์ต๋๋ค.
์๋ฅผ ๋ค์ด FineWeb-Edu์ ์ด ํ์ ์งํฉ์๋ ๋ง์ Parquet ํ์ผ์ด ํฌํจ๋์ด ์์ต๋๋ค. ์ต๊ทผ CC ๋คํ์ ํ ์คํธ๋ง ์ ์งํ๋๋ก ๋ฐ์ดํฐ์ ์ ํํฐ๋งํ ์ ์๋ค๋ฉด, Dask๋ ๋๋ถ๋ถ์ ํ์ผ์ ๊ฑด๋๋ฐ๊ณ ํํฐ์ ์ผ์นํ๋ ๋ฐ์ดํฐ๋ง ๋ค์ด๋ก๋ํฉ๋๋ค:
import dask.dataframe as dd
df = dd.read_parquet("hf://datasets/HuggingFaceFW/fineweb-edu/sample/10BT/*.parquet")
# Dask will skip the files or row groups that don't
# match the query without downloading them.
df = df[df.dump >= "CC-MAIN-2023"]Dask๋ ๊ณ์ฐ์ ํ์ํ ์ด๋ง ์ฝ๊ณ ๋๋จธ์ง๋ ๊ฑด๋๋๋๋ค. ์๋ฅผ ๋ค์ด, ์ฝ๋ ํ๋ฐ๋ถ์ ํน์ ์ด์ ์ ๊ฑฐํ๋๋ผ๋ ํ์ดํ๋ผ์ธ ์ด๊ธฐ์ ํด๋น ์ด์ด ํ์ํ์ง ์๋ค๋ฉด ๋ก๋ํ์ง ์์ต๋๋ค. ์ด๋ ์ด์ ํ์ ์งํฉ์ ์กฐ์ํ๊ฑฐ๋ ๋ถ์์ ์ํํ ๋ ์ ์ฉํฉ๋๋ค:
# Dask will download the 'dump' and 'token_count' needed
# for the filtering and computation and skip the other columns.
df.token_count.mean().compute()ํด๋ผ์ด์ธํธ
dask์ ๋๋ถ๋ถ์ ๊ธฐ๋ฅ์ ํด๋ฌ์คํฐ ๋๋ ๋ก์ปฌ Client๋ฅผ ํตํด ๋ณ๋ ฌ ๊ณ์ฐ์ ์์ํ๋๋ก ์ต์ ํ๋์ด ์์ต๋๋ค:
import dask.dataframe as dd
from distributed import Client
if __name__ == "__main__": # needed for creating new processes
client = Client()
df = dd.read_parquet(...)
...๋ก์ปฌ ์ฌ์ฉ์ ๊ฒฝ์ฐ, Client๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉํฐํ๋ก์ธ์ฑ์ ์ฌ์ฉํ๋ Dask LocalCluster๋ฅผ ์ฌ์ฉํฉ๋๋ค. LocalCluster์ ๋ฉํฐํ๋ก์ธ์ฑ์ ๋ค์๊ณผ ๊ฐ์ด ์๋์ผ๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
from dask.distributed import Client, LocalCluster
cluster = LocalCluster(n_workers=8, threads_per_worker=8)
client = Client(cluster)Client ์์ด ๋ก์ปฌ์์ ๊ธฐ๋ณธ ์ค๋ ๋ ์ค์ผ์ค๋ฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ํน์ ์์
์ดํ์ DataFrame์ด ๋ ๋๋ ค์ง ์ ์์ต๋๋ค(์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ๋ฅผ ์ฐธ์กฐ).
๋ก์ปฌ ๋๋ ํด๋ผ์ฐ๋ ํด๋ฌ์คํฐ๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ธํ ๋ด์ฉ์ Dask ๋ฐฐํฌ ๋ฌธ์์์ ํ์ธํ์ธ์.
Update on GitHub