Datasets documentation

العملية

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v4.8.4).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

العملية

🤗 يوفر Datasets العديد من الأدوات لتعديل بنية مجموعة البيانات ومحتواها. وتُعدّ هذه الأدوات مهمة لتنظيف مجموعة البيانات، وإنشاء أعمدة إضافية، والتحويل بين الميزات والصيغ، وغيرها الكثير.

ستوضح لك هذه الدليل كيفية:

  • إعادة ترتيب الصفوف وتقسيم مجموعة البيانات.
  • إعادة تسمية الأعمدة وإزالتها، بالإضافة إلى عمليات الأعمدة الشائعة الأخرى.
  • تطبيق دوال المعالجة على كل مثال في مجموعة البيانات.
  • دمج مجموعات البيانات.
  • تطبيق تحويل تنسيق مخصص.
  • احفظ البيانات المُعالجة وصدّرها.

لمزيد من التفاصيل الخاصة بمعالجة أنماط بيانات أخرى، راجد دليل معالجة مجموعة بيانات الصوت، أو دليل معالجة مجموعة بيانات الصور، أو دليل معالجة مجموعة بيانات النص.

تستخدم الأمثلة في هذا الدليل مجموعة بيانات MRPC، لكن لا تتردد في تحميل أي مجموعة بيانات تختارها والمتابعة!

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

تعيد جميع طرق المعالجة في هذا الدليل كائن Dataset جديدًا. لا يتم التعديل في الموقع (in-place). انتبه إلى عدم تجاوز مجموعة البيانات السابقة لديك!

فرز، خلط، اختيار، تقسيم، وتجزئة

تتوفر عدة دوال لإعادة ترتيب بنية مجموعة البيانات. تُعدّ هذه الدوال مفيدة لاختيار الصفوف المطلوبة فقط، وإنشاء تقسيمات للتدريب والاختبار، وتقسيم مجموعات البيانات الكبيرة جداً إلى أجزاء أصغر.

فرز

استخدم sort() لترتيب قيم العمود وفقًا لقيمها العددية. يجب أن يكون العمود المُقدَّم متوافقًا مع 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]

في الخلفية، يُنشئ هذا قائمة من الفهارس مرتّبةً وفقاً لقيم العمود. ثم تُستخدم هذه الخريطة الفهرسية للوصول إلى الصفوف الصحيحة في جدول Arrow الأساسي.

خلط

تعيد دالة shuffle() ترتيب قيم الأعمدة بشكل عشوائي. يمكنك تحديد معامل generator في هذه الدالة لاستخدام numpy.random.Generator مختلف إذا كنت ترغب في الحصول على مزيد من التحكم في الخوارزمية المستخدمة في خلط مجموعة البيانات.

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

يقوم الخلط بأخذ قائمة الفهارس [0:len(my_dataset)] وخلطها لإنشاء تعيين للفهارس. ومع ذلك، بمجرد أن يصبح لديك تعيين للفهارس في Dataset، قد يصبح الأداء أبطأ بعشر مرات. وذلك لأن هناك خطوة إضافية للحصول على فهرس الصف الذي يجب قراءته باستخدام تعيين الفهارس، والأهم من ذلك أنك لم تعد تقرأ مقاطع متجاورة من البيانات. لإعادة استعادة السرعة، ستحتاج إلى إعادة كتابة مجموعة البيانات بأكملها على القرص مرة أخرى باستخدام Dataset.flatten_indices()، والذي يزيل تعيين الفهارس. أو يمكنك الانتقال إلى IterableDataset والاستفادة من خلطه التقريبي السريع IterableDataset.shuffle():

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

التحديد والتصفية

هناك خياران لتصفية الصفوف في مجموعة بيانات: select() وfilter().

  • يعيد select() الصفوف وفقاً لقائمة من الفهارس:
>>> small_dataset = dataset.select([0, 10, 20, 30, 40, 50])
>>> len(small_dataset)
6
  • يعيد filter() الصفوف التي تطابق شرطًا محددًا:
>>> 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() أيضًا التصفية حسب الفهارس إذا قمت بإعداد 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

ما لم تكن قائمة الفهارس التي يجب الاحتفاظ بها متتالية، فإن تلك الأساليب تنشئ أيضاً خرائط للفهارس في الخلفية.

تقسيم

تُنشئ الدالة train_test_split() تقسيمات التدريب والاختبار إذا لم تكن موجودة مسبقًا في مجموعة البيانات الخاصة بك. وهذا يتيح لك ضبط النسب النسبية أو العدد المطلق من العينات في كل تقسيم. في المثال أدناه، استخدم معامل test_size لإنشاء تقسيم اختبار يمثل 10% من مجموعة البيانات الأصلية:

>>> 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

تُخلط التقسيمات افتراضيًا، لكن يمكنك ضبط shuffle=False لمنع الخلط.

شريحة

🤗 يدعم Datasets التقسيم إلى شرائح (sharding) لتقسيم مجموعة بيانات كبيرة جدًا إلى عدد محدد مسبقًا من المقاطع. حدد معامل num_shards في shard() لتحديد عدد الشرائح التي سيتم تقسيم مجموعة البيانات إليها. كما ستحتاج إلى تحديد الشريحة التي تريد إرجاعها باستخدام معامل index.

على سبيل المثال، يحتوي مجموعة البيانات stanfordnlp/imdb على 25000 مثال:

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

بعد تقسيم مجموعة البيانات إلى أربعة أجزاء، لن يحتوي الجزء الأول إلا على 6250 مثالًا:

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

إعادة التسمية، والإزالة، والتحويل، والتسطيح

تتيح لك الدوال التالية تعديل أعمدة مجموعة البيانات. وتُعدّ هذه الدوال مفيدة لإعادة تسمية الأعمدة أو حذفها، وتغيير الأعمدة إلى مجموعة جديدة من الميزات، وتسطيح هياكل الأعمدة المتداخلة.

إعادة تسمية

استخدم rename_column() عندما تحتاج إلى إعادة تسمية عمود في مجموعة البيانات الخاصة بك. يتم نقل الميزات المرتبطة بالعمود الأصلي فعليًا تحت اسم العمود الجديد، بدلاً من مجرد استبدال العمود الأصلي في مكانه.

قدّم rename_column() باسم العمود الأصلي واسم العمود الجديد:

>>> 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
})

إزالة

عند الحاجة إلى إزالة عمود واحد أو أكثر، قم بتزويد دالة remove_columns() باسم العمود المراد إزالته. لإزالة أكثر من عمود، قم بتزويدها بقائمة من أسماء الأعمدة:

>>> 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
})

وعلى العكس من ذلك، تختار select_columns() عمودًا واحدًا أو أكثر للإبقاء عليه وتزيل الباقي. تستقبل هذه الدالة إما اسم عمود واحد أو قائمة بأسماء الأعمدة:

>>> 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() نوع الميزة لأعمدة واحدة أو أكثر. تقبل هذه الدالة Features الجديدة الخاصة بك كحجة. يوضح المثال أدناه كيفية تغيير ميزات ClassLabel و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')}

لا يعمل التحويل إلا إذا كان نوع الميزة الأصلي ونوع الميزة الجديد متوافقين. على سبيل المثال، يمكنك تحويل عمود بنوع ميزة Value("int32") إلى Value("bool") إذا كان العمود الأصلي يحتوي فقط على أرقام واحد وصفر.

استخدم دالة cast_column() لتغيير نوع الميزة لعمود واحد. مرر اسم العمود ونوع الميزة الجديد كحججين:

>>> 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)}

تسطيح

في بعض الأحيان، يمكن أن تكون العمود بنية متداخلة من عدة أنواع. انظر إلى البنية المتداخلة أدناه من مجموعة بيانات 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'))}}

يتضمن الحقل answers حقلين فرعيين: text وanswer_start. استخدم الدالة flatten() لاستخراج الحقول الفرعية إلى أعمدة مستقلة:

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

لاحظ كيف أصبحت الحقول الفرعية أعمدة مستقلة بذاتها: answers.text وanswers.answer_start.

خريطة

تنبع بعض التطبيقات الأكثر قوة لـ 🤗 Datasets من استخدام دالة map(). الغرض الأساسي من map() هو تسريع دوال المعالجة. فهو يتيح لك تطبيق دالة معالجة على كل مثال في مجموعة البيانات، بشكل مستقل أو على شكل دفعات. ويمكن لهذه الدالة حتى إنشاء صفوف وأعمدة جديدة.

في المثال التالي، أضف بادئة 'My sentence: ' إلى كل قيمة sentence1 في مجموعة البيانات.

ابدأ بإنشاء دالة تضيف 'My sentence: ' إلى بداية كل جملة. يجب أن تستقبل الدالة وتُخرج dict:

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

الآن استخدم map() لتطبيق دالة add_prefix على مجموعة البيانات بأكملها:

>>> 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 .',
]

لنلقِ نظرة على مثال آخر، لكن هذه المرة ستقوم بإزالة عمود باستخدام map(). عند إزالة عمود، لا تتم الإزالة إلا بعد تقديم المثال إلى الدالة المُطبَّقة. وهذا يتيح للدالة المُطبَّقة استخدام محتوى الأعمدة قبل إزالتها.

حدد العمود الذي تريد إزالته باستخدام معامل remove_columns في map():

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

🤗 Datasets also has a remove_columns() function which is faster because it doesn’t copy the data of the remaining columns.

يمكنك أيضًا استخدام map() مع الفهارس إذا قمت بإعداد with_indices=True. يضيف المثال أدناه الفهرس إلى بداية كل جملة:

>>> 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 .'
]

التعدد في المعالجة

يسرّع تعدد المعالجات (Multiprocessing) المعالجة بشكل كبير من خلال موازاة العمليات على وحدة المعالجة المركزية (CPU). اضبط معامل num_proc في map() لتحديد عدد العمليات المراد استخدامها:

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

يعمل map() أيضًا مع رتبة العملية إذا قمت بإعداد with_rank=True. وهذا يشبه معامل with_indices. يأتي معامل with_rank في الدالة المُخضعة للتحويل بعد معامل index إذا كان موجودًا بالفعل.

>>> 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
...     )

يتمثل الاستخدام الرئيسي لـ rank في موازاة الحساب عبر عدة وحدات معالجة رسومية (GPU). ويتطلب ذلك ضبط multiprocess.set_start_method("spawn"). وإذا لم تفعل ذلك، فستتلقى خطأ CUDA التالي:

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

المعالجة الدفعية

تدعم دالة map() العمل مع دفعات من الأمثلة. للتعامل مع الدفعات، اضبط batched=True. حجم الدفعة الافتراضي هو 1000، لكن يمكنك ضبطه باستخدام معامل batch_size. يتيح المعالجة بالدفعات تطبيقات مثيرة للاهتمام مثل تقسيم الجمل الطويلة إلى مقاطع أقصر وتوسيع البيانات.

تقسيم الأمثلة الطويلة

عندما تكون الأمثلة طويلة جدًا، قد ترغب في تقسيمها إلى عدة مقاطع أصغر. ابدأ بإنشاء دالة تقوم بما يلي:

  1. يقسم حقل sentence1 إلى مقاطع من 50 حرفًا.

  2. يجمع جميع المقاطع معًا لإنشاء مجموعة البيانات الجديدة.

>>> 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}

طبّق الدالة باستخدام 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']}

لاحظ كيف تم تقسيم الجمل إلى مقاطع أقصر الآن، وكيف ازداد عدد الصفوف في مجموعة البيانات.

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

تعزيز البيانات

يمكن أيضًا استخدام دالة map() لزيادة البيانات. يولّد المثال التالي كلمات إضافية للرمز المقنّع في الجملة.

حمّل واستخدم نموذج RoBERTa في 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)

أنشئ دالة لاختيار كلمة عشوائياً لإخفائها في الجملة. يجب أن تُرجع الدالة أيضاً الجملة الأصلية واستبدالين رئيسيين اثنين تولّدهما 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}

استخدم map() لتطبيق الدالة على كامل مجموعة البيانات:

>>> 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.'
]

لكل جملة أصلية، أضاف RoBERTa كلمة عشوائية مع ثلاثة بدائل. الكلمة الأصلية distorting تُستكمل بـ withholding، وsuppressing، وdestroying.

المعالجة غير المتزامنة

تُعدّ الدوال غير المتزامنة مفيدة لاستدعاء نقاط نهاية واجهات برمجة التطبيقات بالتوازي، على سبيل المثال لتنزيل محتوى مثل الصور أو استدعاء نقطة نهاية نموذج.

يمكنك تعريف دالة غير متزامنة باستخدام كلمات المفتاح async وawait، وفيما يلي مثال لدالة لاستدعاء نموذج محادثة من 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://huggingface.co/proxy/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"]}

تعمل الدوال غير المتزامنة بالتوازي، مما يُسرّع العملية بشكل كبير. يستغرق تنفيذ نفس الكود وقتًا أطول بكثير إذا تم تشغيله تسلسليًا، لأنه لا يؤدي أي عمل أثناء انتظار استجابة النموذج. يُوصى عمومًا باستخدام async / await عندما يتعين على دالتك انتظار استجابة من واجهة برمجة التطبيقات (API) على سبيل المثال، أو إذا كانت تقوم بتنزيل بيانات قد يستغرق ذلك بعض الوقت.

لاحظ وجود Semaphore: فهو يحدد الحد الأقصى لعدد الاستعلامات التي يمكن تنفيذها بالتوازي. يُوصى باستخدام Semaphore عند استدعاء واجهات برمجة التطبيقات لتجنب أخطاء حد المعدل.

لنستخدمه لاستدعاء نموذج microsoft/Phi-3-mini-4k-instruct وطلب منه إرجاع الموضوع الرئيسي لكل مسألة رياضية في مجموعة البيانات 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.'}

هنا، يعمل Dataset.map() على تنفيذ دالة get_topic بشكل غير متزامن، فلا يضطر إلى انتظار كل استجابة من النموذج على حدة، وهو ما كان سيستغرق وقتًا طويلاً لو تم تنفيذه بشكل تسلسلي.

بشكل افتراضي، يعمل Dataset.map() بأقصى قدر من دوال الخريطة المتوازية وعددها ألف، لذا لا تنسَ ضبط الحد الأقصى لعدد استدعاءات واجهة برمجة التطبيقات التي يمكن أن تعمل بالتوازي باستخدام Semaphore، وإلا فقد يعيد النموذج أخطاء حد المعدل أو يتجاوز طاقته. لحالات الاستخدام المتقدمة، يمكنك تغيير الحد الأقصى لعدد الاستعلامات المتوازية في datasets.config.

معالجة عدة تقسيمات

تحتوي العديد من مجموعات البيانات على تقسيمات يمكن معالجتها بالتوازي باستخدام DatasetDict.map(). على سبيل المثال، قم بتجزئة حقل sentence1 في تقسيم التدريب والاختبار من خلال:

>>> 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]
}

الاستخدام الموزّع

عند استخدامك map() في بيئة موزعة، يجب عليك أيضاً استخدام torch.distributed.barrier. يضمن ذلك أن يقوم العملية الرئيسية بتنفيذ عملية الخرائط، بينما تقوم العمليات الأخرى بتحميل النتائج، مما يتجنب تكرار العمل.

يُظهر المثال التالي كيفية استخدام torch.distributed.barrier لمزامنة العمليات:

>>> 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() تجميع العينات من مجموعة البيانات في دفعات. وهذا مفيد بشكل خاص عندما ترغب في إنشاء دفعات من البيانات للتدريب أو التقييم، لا سيما عند العمل مع نماذج التعلم العميق.

فيما يلي مثال على كيفية استخدام طريقة 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]}

يقبل أسلوب batch() المعاملات التالية:

  • batch_size (int): عدد العينات في كل دفعة.
  • drop_last_batch (bool، القيمة الافتراضية False): ما إذا كان يجب حذف الدفعة الأخيرة غير المكتملة إذا لم يكن حجم مجموعة البيانات قابلاً للقسمة على حجم الدفعة.
  • num_proc (int, اختياري، القيمة الافتراضية None): عدد العمليات المستخدمة للمعالجة المتعددة. إذا كانت القيمة None، فلن تُستخدم المعالجة المتعددة. يمكن لهذا الأمر أن يسرّع بشكل كبير من عملية تجميع الدُفعات (batching) للمجموعات البيانات الكبيرة.

لاحظ أن Dataset.batch() يعيد Dataset جديدًا حيث يكون كل عنصر دفعة من عدة عينات من مجموعة البيانات الأصلية. إذا كنت ترغب في معالجة البيانات على شكل دفعات، فيجب عليك استخدام map() الدفعية مباشرةً، والتي تطبّق دالة على الدفعات لكن مجموعة البيانات الناتجة غير مقسمة إلى دفعات.

دمج

يمكن دمج مجموعات البيانات المنفصلة إذا كانت تشترك في نفس أنواع الأعمدة. قم بدمج مجموعات البيانات باستخدام 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])

يمكنك أيضًا دمج مجموعتي بيانات أفقيًا عن طريق تعيين axis=1 طالما أن المجموعتين تحتويان على نفس عدد الصفوف:

>>> 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_datasets(). تعمل كل من interleave_datasets() و concatenate_datasets() مع كائنات Dataset و IterableDataset العادية. راجع دليل Stream لمعرفة مثال على كيفية دمج كائنات IterableDataset بالتناوب.

يمكنك تحديد احتمالات العينة لكلٍّ من مجموعات البيانات الأصلية لتحديد كيفية تبديل مجموعات البيانات. في هذه الحالة، يتم بناء مجموعة البيانات الجديدة عن طريق أخذ الأمثلة واحدًا تلو الآخر من مجموعة بيانات عشوائية حتى تنفد العينات من إحدى مجموعات البيانات.

>>> 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]

يمكنك أيضًا تحديد stopping_strategy. الاستراتيجية الافتراضية، first_exhausted، هي استراتيجية أخذ عينات فرعية، أي أن بناء مجموعة البيانات يتوقف بمجرد نفاد العينات من إحدى مجموعات البيانات. يمكنك تحديد stopping_strategy=all_exhausted لتنفيذ استراتيجية أخذ عينات زائدة. في هذه الحالة، يتوقف بناء مجموعة البيانات بمجرد إضافة كل عينة في كل مجموعة بيانات مرة واحدة على الأقل. عمليًا، يعني ذلك أنه إذا نفدت مجموعة بيانات، فستعود إلى بداية هذه المجموعة حتى يتم تحقيق معيار التوقف. لاحظ أنه إذا لم يتم تحديد احتمالات أخذ العينات، فستحتوي مجموعة البيانات الجديدة على max_length_datasets*nb_dataset samples. يوجد أيضًا stopping_strategy=all_exhausted_without_replacement للتأكد من أن كل عينة تُرى مرة واحدة بالضبط.

>>> 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]

الصيغة

تُغيّر الدالة with_format() تنسيق عمود ليكون متوافقًا مع بعض صيغ البيانات الشائعة. حدد المخرجات المطلوبة في معامل type. كما يمكنك اختيار الأعمدة التي تريد تنسيقها باستخدام columns=. يُطبَّق التنسيق على الفور.

على سبيل المثال، أنشئ متجهات PyTorch عن طريق تعيين type="torch":

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

تُغيّر الدالة set_format() أيضًا تنسيق العمود، لكنها تعمل في الموقع:

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

إذا كنت بحاجة إلى إعادة تعيين مجموعة البيانات إلى تنسيقها الأصلي، فاضبط التنسيق على None (أو استخدم 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}

صيغ المصفوفات

تُؤيَّد صيغ متعددة للموترات أو المصفوفات. يُوصى عمومًا باستخدام هذه الصيغ بدلًا من تحويل مخرجات مجموعة البيانات إلى موترات أو مصفوفات يدويًا، وذلك لتجنّب نسخ البيانات غير الضروري وتسريع تحميل البيانات.

فيما يلي قائمة بتنسيقات الموترات أو المصفوفات المدعومة:

اطّلع على دليل استخدام مجموعات البيانات مع TensorFlow لمزيد من التفاصيل حول كيفية إنشاء مجموعة بيانات TensorFlow بكفاءة.

عندما يكون مجموعة البيانات مُنسّقة بتنسيق المصفوفات (tensors) أو المصفوفات (arrays)، فإن جميع البيانات تكون مُنسّقة كمصفوفات (tensors) أو مصفوفات (arrays) (باستثناء الأنواع غير المدعومة، مثل النصوص على سبيل المثال في 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]])}

الصيغ الجدولية

يمكنك استخدام صيغة dataframes أو الجداول لتحسين تحميل البيانات ومعالجتها، إذ توفر هذه الصيغ بشكل عام عمليات تحويل بدون نسخ (zero-copy) وعمليات تحويل مكتوبة بلغات منخفضة المستوى.

فيما يلي قائمة بتنسيقات إطارات البيانات أو الجداول المدعومة:

عندما يكون مجموعة البيانات مُنسّقة بصيغة إطار بيانات (dataframe) أو جدول، تُنسّق كل صف من صفوف مجموعة البيانات أو دفعات من الصفوف على شكل إطار بيانات أو جدول، وتُنسّق أعمدة مجموعة البيانات على شكل سلسلة أو مصفوفة:

>>> 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

تتيح هذه الصيغ تكرار العمل على البيانات بشكل أسرع من خلال تجنب نسخ البيانات، كما تُمكّن من معالجة بيانات أسرع في map() أو 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

تحويل التنسيق المخصص

تطبّق الدالة with_transform() تحويل تنسيق مخصص بشكل فوري. تحلّ هذه الدالة محلّ أي تنسيق محدد سابقاً. على سبيل المثال، يمكنك استخدام هذه الدالة لتجزئة الرموز وتعبئتها بشكل فوري. لا تُطبَّق التجزئة إلا عند الوصول إلى الأمثلة:

>>> 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}

يوجد أيضاً set_transform() الذي يؤدي نفس الوظيفة لكنه يعمل في الموقع.

يمكنك أيضًا استخدام دالة with_transform() لفك الترميز المخصص على Features.

يستخدم المثال أدناه حزمة pydub كبديل لفك ترميز 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)

حفظ

بمجرد أن يصبح مجموعة بياناتك جاهزة، يمكنك حفظها كمجموعة بيانات Hugging Face بتنسيق Parquet وإعادة استخدامها لاحقًا باستخدام load_dataset().

احفظ مجموعة البيانات الخاصة بك من خلال تقديم اسم مستودع مجموعة البيانات على Hugging Face الذي ترغب في حفظها فيه إلى push_to_hub():

encoded_dataset.push_to_hub("username/my_dataset")

يمكنك استخدام عمليات متعددة لرفعها بالتوازي. وهذا مفيد بشكل خاص إذا كنت ترغب في تسريع العملية:

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

استخدم دالة load_dataset() لإعادة تحميل مجموعة البيانات (في وضع البث أو لا):

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

بديلًا عن ذلك، يمكنك حفظه محليًا على القرص بتنسيق Arrow. مقارنةً بـ Parquet، لا يتم ضغط Arrow مما يجعل إعادة تحميله أسرع بكثير، وهو أمر ممتاز للاستخدام المحلي على القرص والتخزين المؤقت المؤقت. ولكن نظرًا لكونه أكبر حجمًا وأقل في البيانات الوصفية، فهو أبطأ في الرفع/التحميل/الاستعلام مقارنةً بـ Parquet، وهو أقل ملاءمة للتخزين طويل الأمد.

Use the save_to_disk() and load_from_disk() function to reload the dataset from your disk:

>>> 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")

تصدير

🤗 يدعم Datasets التصدير أيضًا، مما يتيح لك العمل بمجموعتك من البيانات في تطبيقات أخرى. تُظهر الجدول التالي صيغ الملفات المدعومة حاليًا التي يمكنك التصدير إليها:

نوع الملفطريقة التصدير
CSVDataset.to_csv()
JSONDataset.to_json()
ParquetDataset.to_parquet()
SQLDataset.to_sql()
كائن بايثون في الذاكرةDataset.to_pandas(), Dataset.to_polars() or Dataset.to_dict()

على سبيل المثال، قم بتصدير مجموعة البيانات الخاصة بك إلى ملف CSV على النحو التالي:

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

استخدم مسار hf:// للتصدير إلى مستودع Dataset أو Storage Bucket على 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