Datasets documentation

Proses

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Proses

🤗 Datasets menyediakan banyak alat untuk memodifikasi struktur dan konten dataset. Alat-alat ini penting untuk merapikan dataset, membuat kolom tambahan, mengonversi antara fitur dan format, serta banyak hal lainnya.

Panduan ini akan menunjukkan cara Anda:

  • Urutkan ulang baris dan bagi dataset.
  • Ganti nama dan hapus kolom, serta operasi kolom umum lainnya.
  • Terapkan fungsi pemrosesan ke setiap contoh dalam dataset.
  • Gabungkan dataset.
  • Terapkan transformasi format kustom.
  • Simpan dan ekspor dataset yang telah diproses.

Untuk detail lebih lanjut yang spesifik untuk memproses modalitas dataset lainnya, lihat panduan memproses dataset audio, panduan memproses dataset gambar, atau panduan memproses dataset teks.

Contoh dalam panduan ini menggunakan dataset MRPC, tetapi silakan muat dataset apa pun yang Anda pilih dan ikuti langkah-langkahnya!

>>> from datasets import load_dataset
>>> dataset = load_dataset("nyu-mll/glue", "mrpc", split="train")

Semua metode pemrosesan dalam panduan ini mengembalikan objek Dataset baru. Modifikasi tidak dilakukan secara in-place. Hati-hati agar tidak menimpa dataset sebelumnya!

Urutkan, acak, pilih, bagi, dan pecah

Ada beberapa fungsi untuk menyusun ulang struktur dataset. Fungsi-fungsi ini berguna untuk memilih hanya baris yang Anda inginkan, membuat pemisahan data latih dan uji, serta memecah dataset yang sangat besar menjadi potongan-potongan yang lebih kecil.

Urutkan

Gunakan sort() untuk mengurutkan nilai kolom berdasarkan nilai numeriknya. Kolom yang diberikan harus kompatibel dengan NumPy.

>>> dataset["label"][:10]
[1, 0, 1, 0, 1, 1, 0, 1, 0, 0]
>>> sorted_dataset = dataset.sort("label")
>>> sorted_dataset["label"][:10]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> sorted_dataset["label"][-10:]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Di balik layar, ini membuat daftar indeks yang diurutkan sesuai dengan nilai kolom. Pemetaan indeks ini kemudian digunakan untuk mengakses baris yang tepat di tabel Arrow yang mendasarinya.

Acak

Fungsi shuffle() menyusun ulang nilai kolom secara acak. Anda dapat menentukan parameter generator pada fungsi ini untuk menggunakan numpy.random.Generator yang berbeda jika Anda ingin memiliki kendali lebih besar atas algoritma yang digunakan untuk mengacak dataset.

>>> shuffled_dataset = sorted_dataset.shuffle(seed=42)
>>> shuffled_dataset["label"][:10]
[1, 1, 1, 0, 1, 1, 1, 1, 1, 0]

Shuffling mengambil daftar indeks [0:len(my_dataset)] dan mengacaknya untuk membuat pemetaan indeks. Namun, begitu Dataset Anda memiliki pemetaan indeks, kecepatannya bisa menjadi 10 kali lebih lambat. Hal ini terjadi karena ada langkah tambahan untuk mendapatkan indeks baris yang akan dibaca menggunakan pemetaan indeks, dan yang paling penting, Anda tidak lagi membaca potongan data yang berurutan. Untuk memulihkan kecepatan, Anda perlu menulis ulang seluruh dataset ke disk Anda lagi menggunakan Dataset.flatten_indices(), yang menghapus pemetaan indeks. Sebagai alternatif, Anda dapat beralih ke IterableDataset dan memanfaatkan pengacakan aproksimasi cepatnya IterableDataset.shuffle():

>>> iterable_dataset = dataset.to_iterable_dataset(num_shards=128)
>>> shuffled_iterable_dataset = iterable_dataset.shuffle(seed=42, buffer_size=1000)

Pilih dan Saring

Ada dua opsi untuk memfilter baris dalam dataset: select() dan filter().

  • select() mengembalikan baris berdasarkan daftar indeks:
>>> small_dataset = dataset.select([0, 10, 20, 30, 40, 50])
>>> len(small_dataset)
6
  • filter() mengembalikan baris yang cocok dengan kondisi yang ditentukan:
>>> start_with_ar = dataset.filter(lambda example: example["sentence1"].startswith("Ar"))
>>> len(start_with_ar)
6
>>> start_with_ar["sentence1"]
['Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A {@html "4.56,havingearliersetarecordhighofA 4.56 , having earlier set a record high of A "} 4.57 .',
'Arison said Mann may have been one of the pioneers of the world music movement and he had a deep love of Brazilian music .',
'Arts helped coach the youth on an eighth-grade football team at Lombardi Middle School in Green Bay .',
'Around 9 : 00 a.m. EDT ( 1300 GMT ) , the euro was at $ 1.1566 against the dollar , up 0.07 percent on the day .',
"Arguing that the case was an isolated example , Canada has threatened a trade backlash if Tokyo 's ban is not justified on scientific grounds .",
'Artists are worried the plan would harm those who need help most - performers who have a difficult time lining up shows .'
]

filter() juga dapat memfilter berdasarkan indeks jika Anda mengatur with_indices=True:

>>> even_dataset = dataset.filter(lambda example, idx: idx % 2 == 0, with_indices=True)
>>> len(even_dataset)
1834
>>> len(dataset) / 2
1834.0

Kecuali daftar indeks yang akan dipertahankan bersifat berurutan, metode-metode tersebut juga membuat pemetaan indeks di balik layar.

Pemisahan

Fungsi train_test_split() membuat split latih dan uji jika dataset Anda belum memilikinya. Hal ini memungkinkan Anda untuk menyesuaikan proporsi relatif atau jumlah absolut sampel di setiap split. Pada contoh di bawah, gunakan parameter test_size untuk membuat split uji yang merupakan 10% dari dataset asli:

>>> dataset.train_test_split(test_size=0.1)
{'train': Dataset(schema: {'sentence1': 'string', 'sentence2': 'string', 'label': 'int64', 'idx': 'int32'}, num_rows: 3301),
'test': Dataset(schema: {'sentence1': 'string', 'sentence2': 'string', 'label': 'int64', 'idx': 'int32'}, num_rows: 367)}
>>> 0.1 * len(dataset)
366.8

Splits diacak secara default, tetapi Anda dapat mengatur shuffle=False untuk mencegah pengacakan.

Shard

🤗 Datasets mendukung sharding untuk membagi dataset yang sangat besar menjadi sejumlah chunk yang telah ditentukan. Tentukan jumlah shard untuk membagi dataset dengan menentukan parameter num_shards di shard(). Anda juga perlu menyediakan shard yang ingin dikembalikan dengan parameter index.

Sebagai contoh, dataset stanfordnlp/imdb memiliki 25000 contoh:

>>> from datasets import load_dataset
>>> dataset = load_dataset("stanfordnlp/imdb", split="train")
>>> print(dataset)
Dataset({
    features: ['text', 'label'],
    num_rows: 25000
})

Setelah dataset dipecah menjadi empat bagian, shard pertama hanya akan memiliki 6250 contoh:

>>> dataset.shard(num_shards=4, index=0)
Dataset({
    features: ['text', 'label'],
    num_rows: 6250
})
>>> print(25000/4)
6250.0

Ganti nama, hapus, cast, dan datangkan

Fungsi-fungsi berikut memungkinkan Anda memodifikasi kolom dari sebuah dataset. Fungsi-fungsi ini berguna untuk mengganti nama atau menghapus kolom, mengubah kolom menjadi set fitur baru, serta meratakan struktur kolom bersarang.

Ganti nama

Gunakan rename_column() ketika Anda perlu mengganti nama kolom dalam dataset Anda. Fitur yang terkait dengan kolom asli sebenarnya dipindahkan ke bawah nama kolom baru, bukan sekadar mengganti kolom asli di tempatnya.

Berikan rename_column() dengan nama kolom asli, dan nama kolom baru:

>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'label', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.rename_column("sentence1", "sentenceA")
>>> dataset = dataset.rename_column("sentence2", "sentenceB")
>>> dataset
Dataset({
    features: ['sentenceA', 'sentenceB', 'label', 'idx'],
    num_rows: 3668
})

Hapus

Saat Anda perlu menghapus satu atau beberapa kolom, berikan nama kolom yang akan dihapus ke fungsi remove_columns(). Hapus lebih dari satu kolom dengan memberikan daftar nama kolom:

>>> dataset = dataset.remove_columns("label")
>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.remove_columns(["sentence1", "sentence2"])
>>> dataset
Dataset({
    features: ['idx'],
    num_rows: 3668
})

Sebaliknya, select_columns() memilih satu atau beberapa kolom untuk dipertahankan dan menghapus sisanya. Fungsi ini menerima satu nama kolom atau daftar nama kolom:

>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'label', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.select_columns(['sentence1', 'sentence2', 'idx'])
>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.select_columns('idx')
>>> dataset
Dataset({
    features: ['idx'],
    num_rows: 3668
})

Cast

Fungsi cast() mengubah tipe fitur dari satu atau beberapa kolom. Fungsi ini menerima Features baru Anda sebagai argumennya. Contoh di bawah ini menunjukkan cara mengubah fitur ClassLabel dan Value:

>>> dataset.features
{'sentence1': Value('string'),
'sentence2': Value('string'),
'label': ClassLabel(names=['not_equivalent', 'equivalent']),
'idx': Value('int32')}

>>> from datasets import ClassLabel, Value
>>> new_features = dataset.features.copy()
>>> new_features["label"] = ClassLabel(names=["negative", "positive"])
>>> new_features["idx"] = Value("int64")
>>> dataset = dataset.cast(new_features)
>>> dataset.features
{'sentence1': Value('string'),
'sentence2': Value('string'),
'label': ClassLabel(names=['negative', 'positive']),
'idx': Value('int64')}

Casting hanya berfungsi jika tipe fitur asli dan tipe fitur baru kompatibel. Misalnya, Anda dapat melakukan casting pada kolom dengan tipe fitur Value("int32") menjadi Value("bool") jika kolom asli hanya berisi angka satu dan nol.

Gunakan fungsi cast_column() untuk mengubah tipe fitur dari satu kolom. Sampaikan nama kolom dan tipe fitur barunya sebagai argumen:

>>> dataset.features
{'audio': Audio(sampling_rate=44100, mono=True)}

>>> dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))
>>> dataset.features
{'audio': Audio(sampling_rate=16000, mono=True)}

Ratakan

Kadang-kadang sebuah kolom dapat berupa struktur bersarang dari beberapa tipe. Perhatikan struktur bersarang di bawah ini dari dataset SQuAD:

>>> from datasets import load_dataset
>>> dataset = load_dataset("rajpurkar/squad", split="train")
>>> dataset.features
{'id': Value('string'),
 'title': Value('string'),
 'context': Value('string'),
 'question': Value('string'),
 'answers': {'text': List(Value('string')),
  'answer_start': List(Value('int32'))}}

Bidang answers berisi dua subbidang: text dan answer_start. Gunakan fungsi flatten() untuk mengekstrak subbidang ke dalam kolom terpisah:

>>> flat_dataset = dataset.flatten()
>>> flat_dataset
Dataset({
    features: ['id', 'title', 'context', 'question', 'answers.text', 'answers.answer_start'],
 num_rows: 87599
})

Perhatikan bahwa subfield sekarang menjadi kolom independen: answers.text dan answers.answer_start.

Peta

Beberapa aplikasi yang lebih kuat dari 🤗 Datasets berasal dari penggunaan fungsi map(). Tujuan utama map() adalah untuk mempercepat fungsi pemrosesan. Fungsi ini memungkinkan Anda menerapkan fungsi pemrosesan ke setiap contoh dalam dataset, baik secara independen maupun dalam batch. Fungsi ini bahkan dapat membuat baris dan kolom baru.

Pada contoh berikut, tambahkan awalan 'My sentence: ' pada setiap nilai sentence1 dalam dataset.

Mulai dengan membuat fungsi yang menambahkan 'My sentence: ' di awal setiap kalimat. Fungsi ini perlu menerima dan mengoutputkan dict:

>>> def add_prefix(example):
...     example["sentence1"] = 'My sentence: ' + example["sentence1"]
...     return example

Sekarang gunakan map() untuk menerapkan fungsi add_prefix ke seluruh dataset:

>>> updated_dataset = small_dataset.map(add_prefix)
>>> updated_dataset["sentence1"][:5]
['My sentence: Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
"My sentence: Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .",
'My sentence: They had published an advertisement on the Internet on June 10 , offering the cargo for sale , he added .',
'My sentence: Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A {@html "4.56,havingearliersetarecordhighofA 4.56 , having earlier set a record high of A "} 4.57 .',
]

Mari kita lihat contoh lain, namun kali ini Anda akan menghapus kolom dengan map(). Saat Anda menghapus kolom, kolom tersebut baru dihapus setelah contoh diberikan ke fungsi yang dipetakan. Hal ini memungkinkan fungsi yang dipetakan untuk menggunakan isi kolom sebelum kolom tersebut dihapus.

Tentukan kolom yang akan dihapus dengan parameter remove_columns di map():

>>> updated_dataset = dataset.map(lambda example: {"new_sentence": example["sentence1"]}, remove_columns=["sentence1"])
>>> updated_dataset.column_names
['sentence2', 'label', 'idx', 'new_sentence']

🤗 Datasets juga memiliki fungsi remove_columns() yang lebih cepat karena tidak menyalin data dari kolom yang tersisa.

Anda juga dapat menggunakan map() dengan indeks jika Anda mengatur with_indices=True. Contoh di bawah ini menambahkan indeks ke awal setiap kalimat:

>>> updated_dataset = dataset.map(lambda example, idx: {"sentence2": f"{idx}: " + example["sentence2"]}, with_indices=True)
>>> updated_dataset["sentence2"][:5]
['0: Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
 "1: Yucaipa bought Dominick 's in 1995 for {@html "693millionandsoldittoSafewayfor 693 million and sold it to Safeway for "} 1.8 billion in 1998 .",
 "2: On June 10 , the ship 's owners had published an advertisement on the Internet , offering the explosives for sale .",
 '3: Tab shares jumped 20 cents , or 4.6 % , to set a record closing high at A $ 4.57 .',
 '4: PG & E Corp. shares jumped {@html "1.63or8percentto 1.63 or 8 percent to "} 21.03 on the New York Stock Exchange on Friday .'
]

Pemrosesan Multi

Multiprocessing mempercepat pemrosesan secara signifikan dengan memparalelkan proses pada CPU. Atur parameter num_proc di map() untuk menentukan jumlah proses yang akan digunakan:

>>> updated_dataset = dataset.map(lambda example, idx: {"sentence2": f"{idx}: " + example["sentence2"]}, with_indices=True, num_proc=4)

map() juga berfungsi dengan rank dari proses jika Anda mengatur with_rank=True. Ini serupa dengan parameter with_indices. Parameter with_rank dalam fungsi yang dipetakan ditempatkan setelah index jika parameter tersebut sudah ada.

>>> import torch
>>> from multiprocess import set_start_method
>>> from transformers import AutoTokenizer, AutoModelForCausalLM
>>> from datasets import load_dataset
>>>
>>> # Get an example dataset
>>> dataset = load_dataset("fka/awesome-chatgpt-prompts", split="train")
>>>
>>> # Get an example model and its tokenizer
>>> model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen1.5-0.5B-Chat").eval()
>>> tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B-Chat")
>>>
>>> def gpu_computation(batch, rank):
...     # Move the model on the right GPU if it's not there already
...     device = f"cuda:{(rank or 0) % torch.cuda.device_count()}"
...     model.to(device)
...
...     # Your big GPU call goes here, for example:
...     chats = [[
...         {"role": "system", "content": "You are a helpful assistant."},
...         {"role": "user", "content": prompt}
...     ] for prompt in batch["prompt"]]
...     texts = [tokenizer.apply_chat_template(
...         chat,
...         tokenize=False,
...         add_generation_prompt=True
...     ) for chat in chats]
...     model_inputs = tokenizer(texts, padding=True, return_tensors="pt").to(device)
...     with torch.no_grad():
...         outputs = model.generate(**model_inputs, max_new_tokens=512)
...     batch["output"] = tokenizer.batch_decode(outputs, skip_special_tokens=True)
...     return batch
>>>
>>> if __name__ == "__main__":
...     set_start_method("spawn")
...     updated_dataset = dataset.map(
...         gpu_computation,
...         batched=True,
...         batch_size=16,
...         with_rank=True,
...         num_proc=torch.cuda.device_count(),  # one process per GPU
...     )

Kasus penggunaan utama dari rank adalah untuk memparalelkan komputasi di beberapa GPU. Hal ini memerlukan pengaturan multiprocess.set_start_method("spawn"). Jika tidak, Anda akan menerima error CUDA berikut:

RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the 'spawn' start method.

Pemrosesan batch

Fungsi map() mendukung pengoperasian pada batch contoh. Operasikan pada batch dengan mengatur batched=True. Ukuran batch default adalah 1000, tetapi Anda dapat menyesuaikannya dengan parameter batch_size. Pemrosesan batch memungkinkan aplikasi menarik seperti pemecahan kalimat panjang menjadi potongan yang lebih pendek dan augmentasi data.

Pecah contoh yang panjang

Ketika contoh terlalu panjang, Anda mungkin ingin membaginya menjadi beberapa potongan yang lebih kecil. Mulailah dengan membuat fungsi yang:

  1. Memecah bidang sentence1 menjadi potongan-potongan 50 karakter.

  2. Menumpuk semua potongan untuk membuat dataset baru.

>>> def chunk_examples(examples):
...     chunks = []
...     for sentence in examples["sentence1"]:
...         chunks += [sentence[i:i + 50] for i in range(0, len(sentence), 50)]
...     return {"chunks": chunks}

Terapkan fungsi dengan map():

>>> chunked_dataset = dataset.map(chunk_examples, batched=True, remove_columns=dataset.column_names)
>>> chunked_dataset[:10]
{'chunks': ['Amrozi accused his brother , whom he called " the ',
            'witness " , of deliberately distorting his evidenc',
            'e .',
            "Yucaipa owned Dominick 's before selling the chain",
            ' to Safeway in 1998 for $ 2.5 billion .',
            'They had published an advertisement on the Interne',
            't on June 10 , offering the cargo for sale , he ad',
            'ded .',
            'Around 0335 GMT , Tab shares were up 19 cents , or',
            ' 4.4 % , at A $ 4.56 , having earlier set a record']}

Perhatikan bagaimana kalimat kini dipecah menjadi potongan yang lebih pendek, dan ada lebih banyak baris dalam dataset.

>>> dataset
Dataset({
 features: ['sentence1', 'sentence2', 'label', 'idx'],
 num_rows: 3668
})
>>> chunked_dataset
Dataset({
    features: ['chunks'],
    num_rows: 10470
})

Augmentasi data

Fungsi map() juga dapat digunakan untuk augmentasi data. Contoh berikut menghasilkan kata tambahan untuk token yang ter-mask dalam sebuah kalimat.

Muat dan gunakan model RoBERTa dalam FillMaskPipeline 🤗 Transformers:

>>> from random import randint
>>> from transformers import pipeline

>>> fillmask = pipeline("fill-mask", model="roberta-base")
>>> mask_token = fillmask.tokenizer.mask_token
>>> smaller_dataset = dataset.filter(lambda e, i: i<100, with_indices=True)

Buat fungsi untuk memilih secara acak kata yang akan di-mask dalam kalimat. Fungsi tersebut juga harus mengembalikan kalimat asli dan dua penggantian teratas yang dihasilkan oleh RoBERTA.

>>> def augment_data(examples):
...     outputs = []
...     for sentence in examples["sentence1"]:
...         words = sentence.split(' ')
...         K = randint(1, len(words)-1)
...         masked_sentence = " ".join(words[:K]  + [mask_token] + words[K+1:])
...         predictions = fillmask(masked_sentence)
...         augmented_sequences = [predictions[i]["sequence"] for i in range(3)]
...         outputs += [sentence] + augmented_sequences
...
...     return {"data": outputs}

Gunakan map() untuk menerapkan fungsi ke seluruh dataset:

>>> augmented_dataset = smaller_dataset.map(augment_data, batched=True, remove_columns=dataset.column_names, batch_size=8)
>>> augmented_dataset[:9]["data"]
['Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'Amrozi accused his brother, whom he called " the witness ", of deliberately withholding his evidence.',
 'Amrozi accused his brother, whom he called " the witness ", of deliberately suppressing his evidence.',
 'Amrozi accused his brother, whom he called " the witness ", of deliberately destroying his evidence.',
 "Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .",
 'Yucaipa owned Dominick Stores before selling the chain to Safeway in 1998 for $ 2.5 billion.',
 "Yucaipa owned Dominick's before selling the chain to Safeway in 1998 for $ 2.5 billion.",
 'Yucaipa owned Dominick Pizza before selling the chain to Safeway in 1998 for $ 2.5 billion.'
]

Untuk setiap kalimat asli, RoBERTA memperkaya kata acak dengan tiga alternatif. Kata asli distorting dilengkapi dengan withholding, suppressing, dan destroying.

Pemrosesan asinkron

Fungsi asinkron berguna untuk memanggil endpoint API secara paralel, misalnya untuk mengunduh konten seperti gambar atau memanggil endpoint model.

Anda dapat mendefinisikan fungsi asinkron menggunakan kata kunci async dan await, berikut adalah contoh fungsi untuk memanggil model chat dari Hugging Face:

>>> import aiohttp
>>> import asyncio
>>> from huggingface_hub import get_token
>>> sem = asyncio.Semaphore(20)  # max number of simultaneous queries
>>> async def query_model(model, prompt):
...     api_url = f"https://api-inference.huggingface.co/models/{model}/v1/chat/completions"
...     headers = {"Authorization": f"Bearer {get_token()}", "Content-Type": "application/json"}
...     json = {"messages": [{"role": "user", "content": prompt}], "max_tokens": 20, "seed": 42}
...     async with sem, aiohttp.ClientSession() as session, session.post(api_url, headers=headers, json=json) as response:
...         output = await response.json()
...         return {"Output": output["choices"][0]["message"]["content"]}

Fungsi asinkron berjalan secara paralel, yang mempercepat proses secara signifikan. Kode yang sama membutuhkan waktu jauh lebih lama jika dijalankan secara berurutan, karena tidak melakukan apa pun selama menunggu respons model. Secara umum, disarankan untuk menggunakan async / await ketika fungsi Anda harus menunggu respons dari API, misalnya, atau jika fungsi tersebut mengunduh data yang dapat memakan waktu beberapa saat.

Perhatikan keberadaan Semaphore: ini mengatur jumlah maksimum query yang dapat berjalan secara paralel. Disarankan untuk menggunakan Semaphore saat memanggil API untuk menghindari error batas laju (rate limit).

Mari kita gunakan untuk memanggil model microsoft/Phi-3-mini-4k-instruct dan memintanya untuk mengembalikan topik utama dari setiap soal matematika dalam dataset Maxwell-Jia/AIME_2024:

>>> from datasets import load_dataset
>>> ds = load_dataset("Maxwell-Jia/AIME_2024", split="train")
>>> model = "microsoft/Phi-3-mini-4k-instruct"
>>> prompt = 'What is this text mainly about ? Here is the text:\n\n```\n{Problem}\n```\n\nReply using one or two words max, e.g. "The main topic is Linear Algebra".'
>>> async def get_topic(example):
...     return await query_model(model, prompt.format(Problem=example['Problem']))
>>> ds = ds.map(get_topic)
>>> ds[0]
{'ID': '2024-II-4',
 'Problem': 'Let {@html "x,yx,y"} and {@html "zz"} be positive real numbers that...',
 'Solution': 'Denote $\\log_2(x) = a$, $\\log_2(y) = b$, and...,
 'Answer': 33,
 'Output': 'The main topic is Logarithms.'}

Di sini, Dataset.map() menjalankan banyak fungsi get_topic secara asinkron sehingga tidak perlu menunggu setiap respons model yang akan memakan banyak waktu jika dilakukan secara berurutan.

Secara default, Dataset.map() menjalankan hingga seribu fungsi pemetaan secara paralel, jadi jangan lupa mengatur jumlah maksimum panggilan API yang dapat berjalan secara paralel dengan Semaphore, jika tidak model dapat mengembalikan error batas laju atau kelebihan beban. Untuk kasus penggunaan lanjutan, Anda dapat mengubah jumlah maksimum kueri paralel di datasets.config.

Proses beberapa split

Banyak dataset memiliki split yang dapat diproses secara bersamaan dengan DatasetDict.map(). Misalnya, tokenisasi field sentence1 pada split train dan test dengan:

>>> from datasets import load_dataset

# load all the splits
>>> dataset = load_dataset('nyu-mll/glue', 'mrpc')
>>> encoded_dataset = dataset.map(lambda examples: tokenizer(examples["sentence1"]), batched=True)
>>> encoded_dataset["train"][0]
{'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
'label': 1,
'idx': 0,
'input_ids': [  101,  7277,  2180,  5303,  4806,  1117,  1711,   117,  2292, 1119,  1270,   107,  1103,  7737,   107,   117,  1104,  9938, 4267, 12223, 21811,  1117,  2554,   119,   102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}

Penggunaan terdistribusi

Saat Anda menggunakan map() dalam pengaturan terdistribusi, Anda juga harus menggunakan torch.distributed.barrier. Hal ini memastikan proses utama melakukan pemetaan, sementara proses lain memuat hasilnya, sehingga menghindari pekerjaan yang duplikat.

Contoh berikut menunjukkan cara Anda dapat menggunakan torch.distributed.barrier untuk menyinkronkan proses:

>>> from datasets import Dataset
>>> import torch.distributed

>>> dataset1 = Dataset.from_dict({"a": [0, 1, 2]})

>>> if training_args.local_rank > 0:
...     print("Waiting for main process to perform the mapping")
...     torch.distributed.barrier()

>>> dataset2 = dataset1.map(lambda x: {"a": x["a"] + 1})

>>> if training_args.local_rank == 0:
...     print("Loading results from main process")
...     torch.distributed.barrier()

Batch

Metode batch() memungkinkan Anda mengelompokkan sampel dari dataset menjadi batch. Hal ini sangat berguna ketika Anda ingin membuat batch data untuk pelatihan atau evaluasi, terutama saat bekerja dengan model deep learning.

Berikut adalah contoh cara menggunakan metode batch():

>>> from datasets import load_dataset
>>> dataset = load_dataset("cornell-movie-review-data/rotten_tomatoes", split="train")
>>> batched_dataset = dataset.batch(batch_size=4)
>>> batched_dataset[0]
{'text': ['the rock is destined to be the 21st century\'s new " conan " and that he\'s going to make a splash even greater than arnold schwarzenegger , jean-claud van damme or steven segal .',
        'the gorgeously elaborate continuation of " the lord of the rings " trilogy is so huge that a column of words cannot adequately describe co-writer/director peter jackson\'s expanded vision of j . r . r . tolkien\'s middle-earth .',
        'effective but too-tepid biopic',
        'if you sometimes like to go to the movies to have fun , wasabi is a good place to start .'],
'label': [1, 1, 1, 1]}

Metode batch() menerima parameter berikut:

  • batch_size (int): Jumlah sampel dalam setiap batch.
  • drop_last_batch (bool, defaultnya False): Apakah batch terakhir yang tidak lengkap akan dibuang jika ukuran dataset tidak habis dibagi ukuran batch.
  • num_proc (int, opsional, default None): Jumlah proses yang digunakan untuk multiprocessing. Jika None, multiprocessing tidak digunakan. Hal ini dapat mempercepat batching secara signifikan untuk dataset besar.

Perhatikan bahwa Dataset.batch() mengembalikan Dataset baru di mana setiap item adalah batch dari beberapa sampel dari dataset asli. Jika Anda ingin memproses data dalam batch, Anda harus menggunakan map() yang di-batch secara langsung, yang menerapkan fungsi ke batch tetapi dataset output tidak di-batch.

Concatenate

Dataset yang terpisah dapat digabungkan jika mereka memiliki tipe kolom yang sama. Gabungkan dataset dengan concatenate_datasets():

>>> from datasets import concatenate_datasets, load_dataset

>>> stories = load_dataset("ajibawa-2023/General-Stories-Collection", split="train")
>>> stories = stories.select_columns(["text"])  # only keep the 'text' column
>>> wiki = load_dataset("wikimedia/wikipedia", "20231101.en", split="train")
>>> wiki = wiki.select_columns(["text"])  # only keep the 'text' column

>>> assert stories.features.type == wiki.features.type
>>> bert_dataset = concatenate_datasets([stories, wiki])

Anda juga dapat menggabungkan dua dataset secara horizontal dengan mengatur axis=1 selama kedua dataset memiliki jumlah baris yang sama:

>>> from datasets import Dataset
>>> stories_ids = Dataset.from_dict({"ids": list(range(len(stories)))})
>>> stories_with_ids = concatenate_datasets([stories, stories_ids], axis=1)

Interleave

Anda juga dapat mencampur beberapa dataset dengan mengambil contoh secara bergantian dari masing-masing dataset untuk membuat dataset baru. Hal ini dikenal sebagai interleaving, yang diaktifkan oleh fungsi interleave_datasets(). Baik interleave_datasets() maupun concatenate_datasets() bekerja dengan objek Dataset dan IterableDataset biasa. Lihat panduan Stream untuk contoh cara melakukan interleaving pada objek IterableDataset.

Anda dapat mendefinisikan probabilitas sampling untuk masing-masing dataset asli untuk menentukan cara menginterleaving dataset. Dalam kasus ini, dataset baru dibangun dengan mengambil contoh satu per satu dari dataset acak hingga salah satu dataset kehabisan sampel.

>>> from datasets import Dataset, interleave_datasets
>>> seed = 42
>>> probabilities = [0.3, 0.5, 0.2]
>>> d1 = Dataset.from_dict({"a": [0, 1, 2]})
>>> d2 = Dataset.from_dict({"a": [10, 11, 12, 13]})
>>> d3 = Dataset.from_dict({"a": [20, 21, 22]})
>>> dataset = interleave_datasets([d1, d2, d3], probabilities=probabilities, seed=seed)
>>> dataset["a"]
[10, 11, 20, 12, 0, 21, 13]

Anda juga dapat menentukan stopping_strategy. Strategi default, first_exhausted, adalah strategi subsampling, yaitu konstruksi dataset dihentikan begitu salah satu dataset kehabisan sampel. Anda dapat menentukan stopping_strategy=all_exhausted untuk menjalankan strategi oversampling. Dalam kasus ini, konstruksi dataset dihentikan begitu setiap sampel di setiap dataset telah ditambahkan setidaknya sekali. Secara praktis, ini berarti jika sebuah dataset kehabisan sampel, dataset tersebut akan kembali ke awal hingga kriteria berhenti tercapai. Perhatikan bahwa jika probabilitas sampling tidak ditentukan, dataset baru akan memiliki max_length_datasets*nb_dataset samples. Tersedia juga stopping_strategy=all_exhausted_without_replacement untuk memastikan setiap sampel dilihat tepat satu kali.

>>> d1 = Dataset.from_dict({"a": [0, 1, 2]})
>>> d2 = Dataset.from_dict({"a": [10, 11, 12, 13]})
>>> d3 = Dataset.from_dict({"a": [20, 21, 22]})
>>> dataset = interleave_datasets([d1, d2, d3], stopping_strategy="all_exhausted")
>>> dataset["a"]
[0, 10, 20, 1, 11, 21, 2, 12, 22, 0, 13, 20]

Format

Fungsi with_format() mengubah format kolom agar kompatibel dengan beberapa format data umum. Tentukan output yang diinginkan pada parameter type. Anda juga dapat memilih kolom yang ingin diformat menggunakan columns=. Pemformatan diterapkan secara langsung.

Sebagai contoh, buat tensor PyTorch dengan mengatur type="torch":

>>> dataset = dataset.with_format(type="torch")

Fungsi set_format() juga mengubah format kolom, kecuali ia berjalan secara in-place:

>>> dataset.set_format(type="torch")

Jika Anda perlu mengatur ulang dataset ke format aslinya, atur format menjadi None (atau gunakan reset_format()):

>>> dataset.format
{'type': 'torch', 'format_kwargs': {}, 'columns': [...], 'output_all_columns': False}
>>> dataset = dataset.with_format(None)
>>> dataset.format
{'type': None, 'format_kwargs': {}, 'columns': [...], 'output_all_columns': False}

Format tensor

Beberapa format tensor atau array didukung. Secara umum, disarankan untuk menggunakan format-format ini alih-alih mengonversi output dataset ke tensor atau array secara manual, guna menghindari penyalinan data yang tidak perlu dan mempercepat pemuatan data.

Berikut adalah daftar format tensor atau array yang didukung:

Lihat panduan Menggunakan Datasets dengan TensorFlow untuk detail lebih lanjut tentang cara membuat dataset TensorFlow secara efisien.

Ketika dataset diformat dalam format tensor atau array, semua data diformat sebagai tensor atau array (kecuali tipe yang tidak didukung, seperti string, misalnya untuk PyTorch):

>>> ds = Dataset.from_dict({"text": ["foo", "bar"], "tokens": [[0, 1, 2], [3, 4, 5]]})
>>> ds = ds.with_format("torch")
>>> ds[0]
{'text': 'foo', 'tokens': tensor([0, 1, 2])}
>>> ds[:2]
{'text': ['foo', 'bar'],
 'tokens': tensor([[0, 1, 2],
         [3, 4, 5]])}

Format tabular

Anda dapat menggunakan format dataframes atau tabel untuk mengoptimalkan pemuatan dan pemrosesan data, karena format tersebut umumnya menawarkan operasi zero-copy dan transformasi yang ditulis dalam bahasa tingkat rendah.

Berikut adalah daftar format dataframe atau tabel yang didukung:

Ketika dataset diformat dalam format dataframe atau tabel, setiap baris dataset atau kumpulan baris diformat sebagai dataframe atau tabel, dan kolom dataset diformat sebagai series atau array:

>>> ds = Dataset.from_dict({"text": ["foo", "bar"], "label": [0, 1]})
>>> ds = ds.with_format("pandas")
>>> ds[:2]
  text  label
0  foo      0
1  bar      1

Format-format tersebut memungkinkan iterasi data lebih cepat dengan menghindari penyalinan data, serta memungkinkan pemrosesan data yang lebih cepat di map() atau filter():

>>> ds = ds.map(lambda df: df.assign(upper_text=df.text.str.upper()), batched=True)
>>> ds[:2]
  text  label upper_text
0  foo      0        FOO
1  bar      1        BAR

Transform format kustom

Fungsi with_transform() menerapkan transform format kustom secara langsung. Fungsi ini menggantikan format yang sebelumnya ditentukan. Sebagai contoh, Anda dapat menggunakan fungsi ini untuk melakukan tokenisasi dan padding token secara langsung. Tokenisasi hanya diterapkan ketika contoh diakses:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
>>> def encode(batch):
...     return tokenizer(batch["sentence1"], batch["sentence2"], padding="longest", truncation=True, max_length=512, return_tensors="pt")
>>> dataset = dataset.with_transform(encode)
>>> dataset.format
{'type': 'custom', 'format_kwargs': {'transform': <function __main__.encode(batch)>}, 'columns': ['idx', 'label', 'sentence1', 'sentence2'], 'output_all_columns': False}

Ada juga set_transform() yang melakukan hal yang sama tetapi berjalan secara in-place.

Anda juga dapat menggunakan fungsi with_transform() untuk decoding kustom pada Features.

Contoh di bawah ini menggunakan paket pydub sebagai alternatif untuk decoding torchcodec:

>>> import numpy as np
>>> from pydub import AudioSegment

>>> audio_dataset_amr = Dataset.from_dict({"audio": ["audio_samples/audio.amr"]})

>>> def decode_audio_with_pydub(batch, sampling_rate=16_000):
...     def pydub_decode_file(audio_path):
...         sound = AudioSegment.from_file(audio_path)
...         if sound.frame_rate != sampling_rate:
...             sound = sound.set_frame_rate(sampling_rate)
...         channel_sounds = sound.split_to_mono()
...         samples = [s.get_array_of_samples() for s in channel_sounds]
...         fp_arr = np.array(samples).T.astype(np.float32)
...         fp_arr /= np.iinfo(samples[0].typecode).max
...         return fp_arr
...
...     batch["audio"] = [pydub_decode_file(audio_path) for audio_path in batch["audio"]]
...     return batch

>>> audio_dataset_amr.set_transform(decode_audio_with_pydub)

Simpan

Setelah dataset Anda siap, Anda dapat menyimpannya sebagai Dataset Hugging Face dalam format Parquet dan menggunakannya kembali nanti dengan load_dataset().

Simpan dataset Anda dengan memberikan nama repositori dataset di Hugging Face yang ingin Anda tuju ke push_to_hub():

encoded_dataset.push_to_hub("username/my_dataset")

Anda dapat menggunakan beberapa proses untuk mengunggahnya secara paralel. Ini sangat berguna jika Anda ingin mempercepat prosesnya:

dataset.push_to_hub("username/my_dataset", num_proc=8)

Gunakan fungsi load_dataset() untuk memuat ulang dataset (dalam mode streaming atau tidak):

from datasets import load_dataset
reloaded_dataset = load_dataset("username/my_dataset", streaming=True)

Sebagai alternatif, Anda dapat menyimpannya secara lokal dalam format Arrow di disk. Dibandingkan dengan Parquet, Arrow tidak dikompresi sehingga memuat ulang datanya jauh lebih cepat, yang sangat cocok untuk penggunaan lokal di disk dan penyangga sementara. Namun, karena ukurannya lebih besar dan metadatanya lebih sedikit, proses unggah/unduh/kueri lebih lambat daripada Parquet dan kurang cocok untuk penyimpanan jangka panjang.

Gunakan fungsi save_to_disk() dan load_from_disk() untuk memuat ulang dataset dari disk Anda:

>>> encoded_dataset.save_to_disk("path/of/my/dataset/directory")
>>> # later
>>> from datasets import load_from_disk
>>> reloaded_dataset = load_from_disk("path/of/my/dataset/directory")

Ekspor

🤗 Datasets mendukung ekspor sehingga Anda dapat bekerja dengan dataset Anda di aplikasi lain. Tabel berikut menunjukkan format file yang saat ini didukung untuk diekspor:

Jenis fileMetode ekspor
CSVDataset.to_csv()
JSONDataset.to_json()
ParquetDataset.to_parquet()
SQLDataset.to_sql()
Objek Python di memoriDataset.to_pandas(), Dataset.to_polars() or Dataset.to_dict()

Sebagai contoh, ekspor dataset Anda ke file CSV seperti ini:

>>> encoded_dataset.to_csv("path/of/my/dataset.csv")

Gunakan jalur hf:// untuk mengekspor ke Repositori Dataset atau Storage Bucket di Hugging Face:

>>> encoded_dataset.to_csv("hf://datasets/username/dataset_name/path/of/my/dataset.csv")
>>> encoded_dataset.to_csv("hf://buckets/username/raw_data_bucket/path/of/my/dataset.csv")
Update on GitHub