# پردازش داده

{#if fw === 'pt'}

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "گوگل کولَب", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/fa/chapter3/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/fa/chapter3/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "گوگل کولَب", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/fa/chapter3/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/fa/chapter3/section2_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}

در این بخش در ادامه مثال [فصل قبل](/course/chapter2)، نحوه تعلیم مدل‌های دسته‌بندی کننده رشته‌ها را در یک بَتچ با استفاده از پایتورچ شرح می‌دهیم:


<div dir="ltr">

```python
import torch
from torch.optim import AdamW
from transformers import AutoTokenizer, AutoModelForSequenceClassification

# Same as before
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")

# This is new
batch["labels"] = torch.tensor([1, 1])

optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()
```

</div>

{:else}

در این بخش در ادامه مثال [فصل قبل](/course/chapter2)، نحوه تعلیم مدل‌های دسته‌بندی کننده رشته‌ها را در یک بَتچ با استفاده از تِنسورفلو شرح می‌دهیم:

<div dir="ltr">

```python
import tensorflow as tf
import numpy as np
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

# Same as before
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))

# This is new
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
labels = tf.convert_to_tensor([1, 1])
model.train_on_batch(batch, labels)
```

</div>

{/if}

البته تعلیم با استفاده از دو جمله به نتایج چشم‌گیری منتهی نخواهد شد. برای به دست آوردن نتایج بهتر نیاز به آماده‌سازی دیتاسِت بزرگتری خواهید داشت.

در این بخش ما از دیتاسِت MRPC[^1] که در یک [مقاله](https://www.aclweb.org/anthology/I05-5002.pdf)، نوشته‌ی ویلیام بی دالن و کریس براکت، معرفی شده به عنوان یک مثال استفاده خواهیم کرد. این دیتاسِت شامل ۵۸۰۱ جفت جمله و یک برچسب می‌باشد که برچسب نشان دهنده متناظر بودن جملات می‌باشد (به عنوان مثال اینکه آیا دو جمله معنی یکسانی دارند یا خیر). علت انتخاب این دیتاسِت این است که دیتاسِت کوچکی است و تجربه تعلیم روی آن آسان است.

### بارگذاری دیتاسِت‌ها از هاب

{#if fw === 'pt'}
<Youtube id="_BZearw7f0w"/>
{:else}
<Youtube id="W_gMJF0xomE"/>
{/if}

هاب تنها شامل مدل‌ها نمی‌باشد؛ بلکه شامل دیتاسِت‌های متعدد در بسیاری از زبان‌های مختلف می‌باشد. شما می‌توانید دیتاسِت‌ها را در این [لینک](https://huggingface.co/datasets) جستجو کنید و پیشنهاد می‌کنیم پس از اتمام این بخش یک دیتاسِت جدید را دریافت و پردازش کنید (بخش مستندات عمومی را در [اینجا](https://huggingface.co/docs/datasets/loading) مشاهده کنید). اما اجازه بدهید اکنون روی دیتاسِت MRPC تمرکز کنیم! این یکی از ۱۰ دیتاسِت [GLUE benchmark](https://gluebenchmark.com/) است که یک محک تهیه شده در محیط دانشگاهی جهت اندازه گیری کارکرد مدل‌های یادگیری ماشینی در ۱۰ مسئله دسته‌بندی متن مختلف می‌باشد.

کتابخانه دیتاسِت هاگینگ‌فِیس یک دستور بسیار ساده جهت دانلود و انبار کردن یک دیتاسِت در هاب ارائه می‌کند. ما می‌توانیم دیتاسِت MRPC را به روش زیر دانلود کنیم:

<div dir="ltr">

<Tip>
⚠️ **هشدار** مطمئن شوید که `datasets` نصب شده است. برای اطمینان، دستور `pip install datasets` را اجرا کنید. سپس، مجموعه داده MRPC را بارگذاری کنید و آن را چاپ کنید تا ببینید چه چیزی در آن وجود دارد.
</Tip> 
```py
from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")
raw_datasets
```

</div>

<div dir="ltr">

```python out
DatasetDict({
    train: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 408
    })
    test: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 1725
    })
})
```

</div>

همانطور که می‌بینید یک شیء `DatasetDict` بدست می‌آوریم که شامل مجموعه `training`، مجموعه `validation` و مجموعه `test` می‌باشد. هر یک از این‌ها شامل چندین ستون (`label`، `sentence2`، `sentence1` و `idx`) و تعداد متغیری سطر که عناصر هر مجموعه را تشکیل می‌دهند می‌باشد. (بنابراین، ۳۶۶۸ جفت جمله در مجموعه `training` وجود دارد، ۴۰۸ تا در مجموعه `validation` و ۱۷۲۵ تا در مجموعه `test`).

 این دستور دیتاسِت را دانلود و به صورت پیش‌فرض در پوشه‌ <span dir="ltr">*~/.cache/huggingface/dataset*</span> انبار می‌کند. از فصل ۲ به یاد داشته باشید که می‌توانید پوشه‌ انبار کردن‌تان را با تنظیم متغیر محیطی `HF_HOME` به دلخواه تغییر دهید.

ما می‌توانیم به هر جفت از جملات در شئ `raw_datasets` با استفاده از اندیس, مانند یک دیکشنری دسترسی پیدا کنیم: 

<div dir="ltr">

```py
raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]
```

</div>

<div dir="ltr">

```python out
{'idx': 0,
 'label': 1,
 '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 .'}
```

</div>

می‌بینیم که برچسب‌ها از پیش اعداد صحیح هستند، بنابراین لازم نیست هیچ پیش‌پردازشی روی آنها انجام دهیم. برای این که بدانیم کدام مقدار عددی صحیح به کدام برچسب مربوط می‌شود، می‌توانیم `features` از ‌`raw_train_dataset`‌مان را بررسی کنیم. این کار نوع هر ستون را به ما خواهد گفت.

<div dir="ltr">

```py
raw_train_dataset.features
```

</div>

<div dir="ltr">

```python out
{'sentence1': Value(dtype='string', id=None),
 'sentence2': Value(dtype='string', id=None),
 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
 'idx': Value(dtype='int32', id=None)}
```

</div>

در پشت صحنه، `label` از نوع `ClassLabel` می‌باشد، و نگاشت اعداد صحیح به نام برچسب در پوشه‌ *names* ذخیره شده است. `0` مربوط به `not_equivalent` و `1` مربوط به `equivalent` می‌باشد.

<Tip>
✏️ **امتحان کنید!** عنصر شماره ۱۵ از مجموعه `training` و عنصر شماره ۸۷ از مجموعه `validation` را مشاهده کنید. برچسب‌های آنها چیست؟
</Tip>

### پیش‌پردازش دیتاسِت‌‌ها

{#if fw === 'pt'}
<Youtube id="0u3ioSwev3s"/>
{:else}
<Youtube id="P-rZWqcB6CE"/>
{/if}

به منظور پیش‌پردازش دیتاسِت‌، لازم است متن را به اعدادی قابل پردازش برای مدل تبدیل کنیم. همانطور که در[فصل قبل](/course/chapter2) مشاهده کردید، این کار با استفاده از یک توکِنایزر انجام می‌شود. ما می‌توانیم یک یا چند جمله را به توکِنایزر بدهیم، در نتیجه می‌توانیم به طور مستقیم تمام جملات اول و دوم هر جفت جمله را به صورت زیر توکِن کنیم:

<div dir="ltr">

```py
from transformers import AutoTokenizer

checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
```

</div>

با این حال، نمی‌توانیم دو جمله را به مدل ارسال کنیم تا پیش‌بینی کند که متناظر هستند یا خیر. ما نیاز داریم با دو رشته به صورت یک جفت برخورد کنیم و پیش‌پردازش مناسب را به آن اعمال کنیم. خوشبختانه، توکِنایزر می‌تواند یک جفت رشته را دریافت کند و آنرا به گونه‌ای که مدل BERT ما انتظار دارد آماده‌سازی کند:

<div dir="ltr">

```py
inputs = tokenizer("This is the first sentence.", "This is the second one.")
inputs
```

</div>

<div dir="ltr">

```python out
{ 
  'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
  'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
  'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
```

</div>

در [فصل ۲](/course/chapter2) در مورد کلیدهای `input_ids` و `attention_mask` بحث کردیم، اما از گفتگو در مورد `token_type_ids` اجتناب کردیم. در این مثال این همان چیزی است که به مدل می‌گوید کدام بخش از ورودی جمله اول و کدام بخش جمله دوم است.

<Tip>

✏️ **امتحان کنید!** عنصر شماره ۱۵ از مجموعه `training` را بردارید و دو جمله را به صورت جداگانه و جفت توکِن کنید. تفاوت دو نتیجه چیست؟

</Tip>

اگر شناسه‌های داخل `input_ids` را به کلمات کدگشایی کنیم:

<div dir="ltr">

```py
tokenizer.convert_ids_to_tokens(inputs["input_ids"])
```

</div>

خواهیم داشت:

<div dir="ltr">

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
```

</div>

بنابراین می‌بینیم که مدل انتظار دارد وقتی که دو جمله داریم ورودی‌ها به صورت <span dir="ltr">`[CLS] sentence1 [SEP] sentence2 [SEP]`</span> باشند.

<div dir="ltr">

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
[      0,      0,    0,     0,       0,          0,   0,       0,      1,    1,     1,        1,     1,   1,       1]
```

</div>

همانطور که می‌بینید، بخش‌هایی از ورودی که مربوط به <span dir="ltr">`[CLS] sentence1 [SEP]`</span> هستند اندیس نشان دهنده نوع توکِن آنها `0` و بخش‌هایی که مربوط به <span dir="ltr">`sentence2 [SEP]`</span> هستند اندیس نشان دهنده نوع توکِن‌شان `1` می‌باشد.

توجه داشته باشید که اگر نقطه تعلیم متفاوتی را انتخاب کنید، در ورودی‌ها لزوما `token_type_ids` نخواهید داشت (به عنوان مثال، اگر از یک DistilBERT استفاده کنید آنها بازگردانده نخواهند شد). آنها فقط زمانی بازگردانده می‌شوند که مدل می‌داند با آنها چکار کند، به این خاطر که آنها را در زمان پیش‌تعلیم دیده است.

در اینجا، مدل BERT با شناسه‌هایی که نشان دهنده نوع توکِن هستند از پیش‌ تعلیم دیده و علاوه بر هدف تکمیل جاهای خالی متن که در [فصل ۱](/course/chapter1) در مورد آن صحبت کردیم وظیفه‌ دیگری تحت عنوان _پیش‌بینی جمله‌ بعدی_ بر عهده دارد. هدف از این وظیفه مدل کردن رابطه بین جملات جفتی می‌باشد.

 در پیش‌بینی جمله بعدی، لیستی از جمله‌های جفت شده (با کلماتی که به طور تصادفی پنهان شده‌اند) به مدل داده می‌شوند و از مدل خواسته می‌شود پیش‌بینی کند که آیا جمله دوم در ادامه‌ جمله‌ اول قرار دارد یا خیر. برای سخت‌تر کردن مسئله، در نیمی از حالت‌ها دو جمله در متن اصلی به دنبال هم آمده‌، و در نیمی دیگر از دو متن متفاوت می‌آیند.

در مجموع، نیازی نیست نگران وجود یا عدم وجود `token_type_ids` در ورودی‌های توکِن شده خود باشید: مادامی که از نقطه تعلیم یکسان برای توکِنایزر و مدل استفاده کنید، همه چیز خوب پیش خواهد رفت چرا که توکِنایزر می‌داند چه چیزی برای مدل فراهم کند.

اکنون که مشاهده کردیم چگونه توکِن کننده ما می‌تواند با دو جمله برخورد کند، می‌توانیم آن را برای توکِن کردن کل دیتاسِت‌مان به کار ببریم: مانند [فصل قبل](/course/chapter2)، ما می‌توانیم توکِنایزر را با لیستی از جفت جمله‌ها، با دادن لیست جملات اول و سپس لیست جملات دوم، تغذیه کنیم. این روش همچنین با گزینه‌های `padding` و `truncation` که در [فصل ۲](/course/chapter2) مشاهده کردیم سازگاری دارد. بنابراین، یک روش برای پیش‌پردازش دیتاسِت `training` اینگونه می‌باشد:

<div dir="ltr">

```py
tokenized_dataset = tokenizer(
    raw_datasets["train"]["sentence1"],
    raw_datasets["train"]["sentence2"],
    padding=True,
    truncation=True,
)
```

</div>

این روش به خوبی کار می‌کند، اما مشکل‌اش این است که دیکشنری (از کلیدهای ما شامل، `input_ids`, `attention_mask` و `token_type_ids` و مقادیر آنها که لیست‌هایی از لیست‌ها هستند) برمی‌گرداند. همچنین این روش فقط زمانی کار می‌کند که حافظه موقت کافی جهت ذخیره‌سازی کل دیتاسِت در حین توکِن کردن داشته باشید (در حالی که دیتاسِت‌های موجود در کتابخانه `Datatasets` از هاگینگ‌فِیس فایل‌هایی از نوع [Apache Arrow](https://arrow.apache.org/) هستند که روی دیسک ذخیره شده‌اند، بنابراین شما فقط نمونه‌هایی را که جهت ذخیره در حافظه درخواست کرده‌اید نگه‌ می‌دارید).

به منظور نگه داشتن داده به صورت یک دیتاسِت، از تابع <span dir="ltr">[`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map)</span> استفاده می‌کنیم. چنانچه به پیش‌پردازش‌های بیشتری علاوه‌ بر توکِن کردن نیاز داشته باشیم این روش انعطاف‌پذیری لازم را به ما می‌دهد. تابع <span dir="ltr">`map()`</span> با اعمال کردن یک عملیات روی هر عنصر دیتاسِت عمل می‌کند، بنابراین اجازه دهید تابعی تعریف کنیم که ورودی‌ها را توکِن کند:

<div dir="ltr">

```py
def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
```

</div>

این تابع دیکشنری (مثل اقلام داخل دیتاسِت) دریافت می‌کند و دیکشنری دیگری با کلیدهای `input_ids`، `attention_mask` و `token_type_ids` برمی‌گرداند. توجه داشته باشید از آنجایی که توکِنایزر روی لیست‌هایی از دو جمله‌ها کار می‌کند، همان‌طور که قبلا مشاهده کردیم، این تابع نیز در صورتی که دیکشنری `example` شامل چندین نمونه (هر کلید به عنوان لیستی از جمله‌ها) باشد کار می‌کند. این به ما این امکان را خواهد داد که از گزینه `batched=True` در فراخوانی تابع <span dir="ltr">`map()`</span> استفاده کنیم که توکِنایزر را به میزان زیادی سریع‌تر خواهد کرد. این `tokenizer` با توکِنایزری در کتابخانه [Tokenizers](https://github.com/huggingface/tokenizers) از هاگینگ‌فِیس که به زبان برنامه‌‌نویسی Rust نوشته شده پشتیبانی می‌شود. این توکِنایزر می‌تواند بسیار سریع باشد، اما فقط به شرطی که ورودی‌های زیادی را به صورت یک جا به آن بدهیم.

توجه داشته باشید که ما آرگومان هم‌طول‌سازی را در تابع توکِن کننده‌مان نادیده گرفته‌ایم. این به این خاطر است که هم‌طول‌سازی روی همه نمونه‌ها برای بیشترین طول به صرفه نیست: بهتر است که نمونه‌ها را زمانی که در حال ساختن بَتچ هستیم هم‌طول کنیم، در این صورت فقط نیاز داریم نمونه‌ها را به اندازه بزرگترین طول همان بَتچ و نه بیشترین طول در سرتاسر دیتاسِت‌ هم‌طول کنیم. این روش زمانی که ورودی‌ها دارای طول‌های بسیار متغیری هستند وقت و انرژی زیادی را صرفه‌جویی خواهد کرد. 

در اینجا نشان می‌دهیم چگونه تابع تولید توکِن را روی کل دیتاسِت به یکباره اعمال می‌کنیم. ما از `batched=True` در فراخوانی تابع `map` استفاده می‌کنیم بنابر این تابع ما به جای اینکه روی هر عنصر به صورت جداگانه عمل کند روی چندین عنصر از دیتاسِت به یکباره عمل می‌کند. این کار اجازه می‌دهد که پیش‌پردازش سریع‌تر انجام گیرد:

<div dir="ltr">

```py
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets
```

</div>

کتابخانه `Datasets` از هاگینگ‌فِیس این پیش‌پردازش را با افزودن -فیلدهای- جدید به دیتاسِت‌ها، یکی به اِزای هر کلید در -دیکشنری- که توسط تابع پیش‌پردازش بازگردانده می‌شوند، اعمال می‌کند:

<div dir="ltr">

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 408
    })
    test: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 1725
    })
})
```

</div>

شما حتی می‌توانید زمانی که تابع پیش‌پردازش خود را اعمال می‌کنید، با ارسال آرگومان `num_proc` در تابع <span dir="ltr">`map()`</span> از چندپردازشی استفاده کنید. در اینجا ما این کار را انجام ندادیم چرا که کتابخانه `Tokenizers` هاگینگ‌فِیس از پیش، از چندین رشته پردازشی برای توکِن کردن سریع‌تر نمونه‌های ما استفاده می‌کند، اما اگر شما از یک توکِنایزر سریع که با این کتابخانه پشتیبانی شود استفاده نمی‌کنید، این روش می‌تواند پیش‌پردازش شما را سریع‌تر کند.


تابع `tokenize_function` ما یک دیکشنری شامل کلیدهای `input_ids`، `attention_mask` و `token_type_ids` برمی‌گرداند به گونه‌ای که این کلیدها به صورت سه فیلد جدید به همه بخش‌های دیتاسِت افزوده گردند. توجه داشته باشید اگر تابع پیش‌پردازش ما برای یک کلید موجود در دیتاسِت مقدار جدیدی بازمی‌گرداند ما می‌توانستیم فیلدهای موجود در دیتاسِتی که تابع <span dir="ltr">`map()`</span> به آن اعمال می‌شود را نیز تغییر دهیم.

آخرین کاری که باید انجام دهیم این است که هنگامی که عناصر را با هم در یک بَتچ قرار می‌دهیم، طول همه عناصر را به اندازه بلندترین عنصر برسانیم - تکنیکی که ما به آن *هم‌طول‌سازی پویا* می‌گوییم.


### هم‌طول‌سازی پویا

<Youtube id="7q5NyFT8REg"/>

{#if fw === 'pt'}

تابعی که مسئول کنار هم گذاشتن نمونه‌ها در یک بَتچ می‌باشد *تابع ترکیب کننده* خوانده می‌شود. شما می‌توانید این تابع را که در حالت پیش‌ فرض نمونه‌های شما را به تِنسور پایتورچ تبدیل کرده و به هم الحاق می‌کند (اگر عناصر شما لیست، تاپِل یا دیکشنری باشند این کار به صورت بازگشتی انجام می‌گیرد) هنگام ساختن `DataLoader` به داخل آن ارسال کنید. از آنجایی که ورودی‌های ما هم‌طول نخواهند بود استفاده از این تابع برای ما امکان‌پذیر نیست. ناهم‌طولی ورودی‌ها به این خاطر است که ما فرایند هم‌طول‌سازی را عمدا به تعویق انداختیم تا فقط در زمان نیاز آن را روی هر بَتچ اجرا کنیم و از داشتن ورودی‌های بیش از اندازه طولانی با مقدار زیادی هم‌طول‌سازی پیش‌گیری کنیم. این روش، فرایند تعلیم را تا اندازه‌ای سرعت می‌بخشد، اما توجه داشته باشید که اگر شما در حال تعلیم روی TPU هستید این کار می‌تواند مشکل ساز باشد چرا که TPU اشکال معین را ترجیح می‌دهد، حتی اگر نیاز به هم‌طول‌سازی اضافه داشته باشد.

{:else}

تابعی که مسئول کنار هم گذاشتن نمونه‌ها در یک بَتچ می‌باشد *تابع ترکیب کننده* خوانده می‌شود. تابع ترکیب کننده پیش‌فرض تابعی است که فقط نمونه‌های شما را به `tf.Tensor` تبدیل کرده و آنها را به هم الحاق می‌کند (اگر عناصر شما لیست، تاپِل یا دیکشنری باشند این کار به صورت بازگشتی انجام می‌گیرد). از آنجایی که ورودی‌های ما هم‌طول نخواهند بود استفاده از این تابع برای ما امکان پذیر نیست. ناهم‌طولی ورودی‌ها به این خاطر است که ما فرایند هم‌طول‌سازی را عمدا به تعویق انداختیم تا فقط در زمان نیاز آن را روی هر بَتچ اجرا کنیم و از داشتن ورودی‌های بیش از اندازه طولانی با مقدار زیادی هم‌طول‌سازی پیش‌گیری کنیم. این روش، فرایند تعلیم را تا اندازه‌ای سرعت می‌بخشد، اما توجه داشته باشید که اگر شما در حال تعلیم روی TPU هستید این کار می‌تواند مشکل ساز باشد چرا که TPU اشکال معین را ترجیح می‌دهد، حتی اگر نیاز به هم‌طول‌سازی اضافه داشته باشد.

{/if}

برای انجام این کار در عمل، ما باید یک تابع ترکیب کننده تعریف کنیم که میزان درستی از هم‌طول‌سازی را به آیتم‌های دیتاسِت‌هایی که ما می‌خواهیم باهم در یک بَتچ قرار دهیم اعمال کند. خوشبختانه، کتابخانه ترنسفورمرهای هاگینگ‌فِیس چنین قابلیتی را توسط کلاس `DataCollatorWithPadding` به ما می‌دهد. به محض این که شیء‌ای از این کلاس را تعریف کنیم (یعنی تعیین کنیم چه توکِنی برای هم‌طول‌سازی استفاده کند و مدل انتظار هم‌طول‌سازی از سمت چپ یا راست ورودی‌ها را داشته باشد) یک توکِنایزر را برداشته و هر کاری را که لازم دارید انجام می‌دهد:

{#if fw === 'pt'}

<div dir="ltr">

```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

</div>

{:else}

<div dir="ltr">

```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
```

</div>

{/if}

اجازه دهید چند نمونه از مجموعه `training` را که می‌خواهیم باهم در یک بَتچ قرار دهیم برداریم تا این ابزار جدید را امتحان کنیم. در اینجا ستون‌های `idx`، `sentence1` و `sentence2` را حذف می‌کنیم چرا که احتیاج نخواهند شد و شامل رشته‌های متنی می‌شوند (که ما نمی‌توانیم تنسورهایی از رشته‌های متنی ایجاد کنیم) و سپس نگاهی می‌اندازیم به طول هر ورودی در هر بَتچ:

<div dir="ltr">

```py
samples = tokenized_datasets["train"][:8]
samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
[len(x) for x in samples["input_ids"]]
```

</div>

<div dir="ltr">

```python out
[50, 59, 47, 67, 59, 50, 62, 32]
```

</div>

تعجبی ندارد که نمونه‌هایی با طول‌های متغییر، از ۳۲ تا ۶۷ بدست می‌آوریم. هم‌طول‌سازی پویا به این معنی است که نمونه‌های موجود در این بَتچ باید همگی با طول ۶۷، که بزرگترین طول داخل بَتچ می‌باشد، هم‌طول شده باشند. بدون هم‌طول‌سازی پویا، همه نمونه‌ها در کل دیتاسِت باید به اندازه بزرگ‌ترین طول یا بزرگ‌ترین طول قابل پذیرش برای مدل، هم‌طول شوند. اجازه دهید بررسی کنیم آیا `data_collator` ما بَتچ را به درستی هم‌طول می‌کند:

<div dir="ltr">

```py
batch = data_collator(samples)
{k: v.shape for k, v in batch.items()}
```

</div>

{#if fw === 'tf'}

<div dir="ltr">

```python out
{'attention_mask': TensorShape([8, 67]),
 'input_ids': TensorShape([8, 67]),
 'token_type_ids': TensorShape([8, 67]),
 'labels': TensorShape([8])}
```

</div>

{:else}

<div dir="ltr">

```python out
{'attention_mask': torch.Size([8, 67]),
 'input_ids': torch.Size([8, 67]),
 'token_type_ids': torch.Size([8, 67]),
 'labels': torch.Size([8])}
```

</div>

به نظر خوب می‌آید! اکنون که از متن خالص به بَتچ‌هایی رسیده‌ایم که مدل‌مان می‌تواند با آنها کار کند، آماده کوک‌ کردن مدل هستیم:

{/if}

<Tip>

✏️ **امتحان کنید!** پروسه پیش‌پردازش را روی دیتاسِت GLUE SST-2 باز تکرار کنید. از آنجایی که این مجموعه به جای دو جمله‌ها شامل تک جمله‌ها می‌باشد این کار کمی متفاوت است، اما بقیه کارهایی که انجام داده‌ایم باید یکسان به نظر برسند. برای یک چالش مشکل‌تر، سعی کنید تابع پیش‌پردازشی بنویسید که برای همه مسئله‌های GLUE کار کند.

</Tip>

{#if fw === 'tf'}

اکنون که دیتاسِت‌مان و یک `collator` داده در اختیار داریم، نیاز داریم که آنها را باهم بکار ببریم. ما می‌توانستیم بَتچ‌ها را دستی لود کرده و آنها را `collate` کنیم، اما این روش کار زیادی می‌برد و احتمالا خیلی هم بهینه نخواهد بود. در عوض، تابعی ساده وجود دارد که راه حل بهینه‌ای برای این مسئله ارائه می‌کند: <span dir="ltr">`to_tf_dataset()`</span>. این تابع یک `tf.data.Dataset` شامل پارامتری اختیاری برای تابع `collation` را دور دیتاسِت‌تان می‌پیچد. `tf.data.Dataset` یک فرمت بومی تِنسورفلو است که کِراس می‌تواند برای <span dir="ltr">`model.fit()`</span> استفاده کند، در نتیجه همین یک تابع می‌تواند یک دیتاسِت هاگینگ‌فِیس را به سرعت به فرمت آماده برای تعلیم تبدیل کند. اجازه دهید آنرا در عمل با دیتاسِت‌مان مشاهده کنیم!

<div dir="ltr">

```py
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)

tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=False,
    collate_fn=data_collator,
    batch_size=8,
)
```

</div>

این هم از این! حالا می‌توانیم این دیتاسِت‌ها را به درس بعدی ببریم، جایی که تعلیم پس از همه سختی‌های  پیش‌پردازش به طرز خوشایندی سرراست خواهد بود.

{/if}

[^1]: Microsoft Research Paraphrase Corpus

</div>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/fa/chapter3/2.mdx" />