huggingface/datasets
多くの場合 split として test, train, validation、あれば subset として言語別
code:usage.py
import datasets
# データセット自体は読み込まず中身を確認
builder = datasets.load_dataset_builder(...)
builder.info.features
builder.info.splits
datasets.get_dataset_config_names(...) # config の列挙 ≒ subset 分かる
# データセットの読み込み
dataset = datasets.load_dataset('path')
dataset = datasets.load_dataset('path', 'name') # 大抵 name(=config) が subset に対応している
# 特定のファイル狙いうち
dataset = load_dataset("allenai/c4", data_files="multilingual/c4-ja.tfrecord-00002-of-01024.json.gz")
split='train'
streaming=True
revision=...
trust_remote_code=True
ストリーミング
code:usage.py
# iterable として一部をストリーミング
iterable_dataset = load_dataset(..., split='train', streaming=True)
for row in iterable_dataset.take(10):
print(row)
# ダウンロード済みのを dataset.to_iterable_dataset() してもよい
前処理
code:usage.py
def tokenization(example):
return tokenizer(example"text") tokenized_dataset = dataset.map(tokenization, batched=True)
code:usage.py
dataset.set_format(type='pandas')
map の後に適用される、ドキュメントにないけど type="polars" もある
カテゴリ変数の数値化
dataset = dataset.class_encode_column(col) でカテゴリの col を数値にできる
マッピングはカラム側が保持していて
dataset['test'].features['labels'].str2int('alarm') #=> 0
dataset['test'].features['labels'].int2str(0) #=> 'alarm'
データセット分割前にやること!!
DatasetDict に対して .class_encode_column("label") したら個別に実行される
分割した一方に含まれていないデータがあるとクラス数が変わってしまう
tokenizer(..., return_tensors="pt") していても map を経由すると list に変わってしまう話
データセット側で ds.set_format(type="torch", columns=[...]) するのがよさそう
データ分割
dataset["train"].filter(lambda x: len(x["text"]) < 100)
dataset['train'].shard(num_shards=10, index=0)
dataset["train"].select(range(0, 1000))
dataset['train'][0:10] してしまいがち
code:split.py
split_dataset = dataset"train".train_test_split(test_size=0.2, seed=42) train_dataset = split_dataset"train" test_dataset = split_dataset"test" データセットくっつける
code:join.py
from datasets import concatenate_datasets, interleave_datasets
# 単にくっつける
dataset = concatinate_datasets([ds1'train', ds2]) # サンプリングして取り出す
dataset = interleave_datasets(
stopping_strategy="first_exhausted", # all_exhausted なら全データセット取り出すまで少ない方からは重複して取り出される
)
ユーティリティ
KeyDataset
from transformers.pipelines.pt_utils import KeyDataset
KeyDataset(dataset, 'input') で input だけ取り出した Dataset を返す
pipeline に特定の列だけ流し込む時に便利
学習 & 推論時にモデルにデータを供給する
code:supplying.py
from datasets import Dataset
from transformers import DataCollatorWithPadding
from torch.utils.data import DataLoader
ds = Dataset(...)
def prompting(row):
return {"text": ...}
def tokenize(rows):
return tokenizer(
max_length=512,
add_special_tokens=True,
truncation=True, # truncate はする / padding は Collator に任せる
)
# 自分のユースケースでは大抵事前にまとめて tokenize しておける程度の量なので先にしておく
# GPU で学習・推論させつつ cpu で tokenize して送るには、
# 上の記事のように Collator で tokenize する / ds.map(..., batched=True)
# Loader で num_workers > 0, pin_memory=True するといい?
ds_tokenized = ds.map(prompting).map(tokenize)
dataloader = DataLoader(
# token だけでなく後で他の列参照する
batch_size=16,
shuffle=False,
num_workers=2,
pin_memory=True,
collate_fn=DataCollatorWithPadding(tokenizer=tokenizer),
)
for batch in dataloader:
...
データセットを既存の ML ライブラリのデータセットにする
code:dataset.py
dataset.set_format(type="torch", columns=...) data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
tf_dataset = dataset.to_tf_dataset(
batch_size=2,
collate_fn=data_collator,
shuffle=True
)
UserWarning: You seem to be using the pipelines sequentially on GPU. In order to maximize efficiency please use a dataset
pipe にジェネレータを渡せばよいのだが、list を含むチャットテンプレートを yield しているとうまくうごかない?