Datasets documentation
استخدام مجموعات البيانات مع TensorFlow
استخدام مجموعات البيانات مع TensorFlow
هذا المستند مقدمة سريعة لاستخدام datasets مع TensorFlow، مع تركيز خاص على كيفية استخراج كائنات tf.Tensor من مجموعات البيانات لدينا، وكيفية بث البيانات من كائنات Dataset في Hugging Face إلى طرق Keras
مثل model.fit().
تنسيق مجموعة البيانات
بشكل افتراضي، تُرجع مجموعات البيانات كائنات بايثون العادية: أعداد صحيحة، وأعداد عشرية، ونصوص، وقوائم، وما إلى ذلك.
للحصول على متجهات TensorFlow بدلاً من ذلك، يمكنك تعيين صيغة مجموعة البيانات إلى tf:
>>> from datasets import Dataset
>>> data = [[1, 2],[3, 4]]
>>> ds = Dataset.from_dict({"data": data})
>>> ds = ds.with_format("tf")
>>> ds[0]
{'data': <tf.Tensor: shape=(2,), dtype=int64, numpy=array([1, 2])>}
>>> ds[:2]
{'data': <tf.Tensor: shape=(2, 2), dtype=int64, numpy=
array([[1, 2],
[3, 4]])>}يُعدّ كائن Dataset غلافًا لجدول Arrow، مما يتيح قراءة سريعة من المصفوفات في مجموعة البيانات إلى متجهات TensorFlow.
يمكن أن يكون ذلك مفيدًا لتحويل مجموعة البيانات الخاصة بك إلى قاموس من كائنات Tensor، أو لكتابة مولّد لتحميل عينات TF منها. إذا كنت ترغب في تحويل مجموعة البيانات بأكملها إلى Tensor، فما عليك سوى الاستعلام عن مجموعة البيانات الكاملة:
>>> ds[:]
{'data': <tf.Tensor: shape=(2, 2), dtype=int64, numpy=
array([[1, 2],
[3, 4]])>}مصفوفات N الأبعاد
إذا كانت مجموعة بياناتك تتكون من مصفوفات ذات أبعاد N، فستلاحظ أنه افتراضياً تُعتبر المصفوفات ذات الشكل الثابت على أنها نفس الموتر:
>>> from datasets import Dataset
>>> data = [[[1, 2],[3, 4]],[[5, 6],[7, 8]]] # fixed shape
>>> ds = Dataset.from_dict({"data": data})
>>> ds = ds.with_format("tf")
>>> ds[0]
{'data': <tf.Tensor: shape=(2, 2), dtype=int64, numpy=
array([[1, 2],
[3, 4]])>}وإلا، فإن مجموعة بيانات بتنسيق TensorFlow تُخرج RaggedTensor بدلاً من مصفوفة واحدة:
>>> from datasets import Dataset
>>> data = [[[1, 2],[3]],[[4, 5, 6],[7, 8]]] # varying shape
>>> ds = Dataset.from_dict({"data": data})
>>> ds = ds.with_format("torch")
>>> ds[0]
{'data': <tf.RaggedTensor [[1, 2], [3]]>}ومع ذلك، تتطلب هذه المنطق غالبًا مقارنات بطيئة للأشكال ونسخًا للبيانات.
لتجنب ذلك، يجب عليك استخدام نوع الميزة Array بشكل صريح وتحديد شكل متجهاتك:
>>> from datasets import Dataset, Features, Array2D
>>> data = [[[1, 2],[3, 4]],[[5, 6],[7, 8]]]
>>> features = Features({"data": Array2D(shape=(2, 2), dtype='int32')})
>>> ds = Dataset.from_dict({"data": data}, features=features)
>>> ds = ds.with_format("tf")
>>> ds[0]
{'data': <tf.Tensor: shape=(2, 2), dtype=int64, numpy=
array([[1, 2],
[3, 4]])>}
>>> ds[:2]
{'data': <tf.Tensor: shape=(2, 2, 2), dtype=int64, numpy=
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])>}أنواع ميزات أخرى
تُحوَّل بيانات ClassLabel بشكل صحيح إلى متجهات:
>>> from datasets import Dataset, Features, ClassLabel
>>> labels = [0, 0, 1]
>>> features = Features({"label": ClassLabel(names=["negative", "positive"])})
>>> ds = Dataset.from_dict({"label": labels}, features=features)
>>> ds = ds.with_format("tf")
>>> ds[:3]
{'label': <tf.Tensor: shape=(3,), dtype=int64, numpy=array([0, 0, 1])>}تُدعم أيضًا السلاسل النصية والكائنات الثنائية:
>>> from datasets import Dataset, Features
>>> text = ["foo", "bar"]
>>> data = [0, 1]
>>> ds = Dataset.from_dict({"text": text, "data": data})
>>> ds = ds.with_format("tf")
>>> ds[:2]
{'text': <tf.Tensor: shape=(2,), dtype=string, numpy=array([b'foo', b'bar'], dtype=object)>,
'data': <tf.Tensor: shape=(2,), dtype=int64, numpy=array([0, 1])>}يمكنك أيضًا تنسيق أعمدة معينة بشكل صريح وترك الأعمدة الأخرى بدون تنسيق:
>>> ds = ds.with_format("tf", columns=["data"], output_all_columns=True)
>>> ds[:2]
{'data': <tf.Tensor: shape=(2,), dtype=int64, numpy=array([0, 1])>,
'text': ['foo', 'bar']}تبقى كائنات النص والثنائية دون تغيير، لأن PyTorch يدعم الأرقام فقط.
تُدعم أنواع الميزات Image وAudio أيضًا.
لاستخدام نوع الميزة Image، ستحتاج إلى تثبيت الإضافة
visionعبر الأمرpip install datasets[vision].
>>> from datasets import Dataset, Features, Audio, Image
>>> images = ["path/to/image.png"] * 10
>>> features = Features({"image": Image()})
>>> ds = Dataset.from_dict({"image": images}, features=features)
>>> ds = ds.with_format("tf")
>>> ds[0]
{'image': <tf.Tensor: shape=(512, 512, 4), dtype=uint8, numpy=
array([[[255, 215, 106, 255],
[255, 215, 106, 255],
...,
[255, 255, 255, 255],
[255, 255, 255, 255]]], dtype=uint8)>}
>>> ds[:2]
{'image': <tf.Tensor: shape=(2, 512, 512, 4), dtype=uint8, numpy=
array([[[[255, 215, 106, 255],
[255, 215, 106, 255],
...,
[255, 255, 255, 255],
[255, 255, 255, 255]]]], dtype=uint8)>}لاستخدام نوع الميزة Audio، ستحتاج إلى تثبيت الإضافة
audioعبر الأمرpip install datasets[audio].
>>> from datasets import Dataset, Features, Audio, Image
>>> audio = ["path/to/audio.wav"] * 10
>>> features = Features({"audio": Audio()})
>>> ds = Dataset.from_dict({"audio": audio}, features=features)
>>> ds = ds.with_format("tf")
>>> ds[0]["audio"]["array"]
<tf.Tensor: shape=(202311,), dtype=float32, numpy=
array([ 6.1035156e-05, 1.5258789e-05, 1.6784668e-04, ...,
-1.5258789e-05, -1.5258789e-05, 1.5258789e-05], dtype=float32)>
>>> ds[0]["audio"]["sampling_rate"]
<tf.Tensor: shape=(), dtype=int32, numpy=44100>تحميل البيانات
على الرغم من أنه يمكنك تحميل عينات ودفعات فردية بمجرد الفهرسة في مجموعة البيانات الخاصة بك، إلا أن هذا لن يعمل إذا كنت تريد استخدام طرق Keras مثل fit() وpredict(). يمكنك كتابة دالة مولّدة تخلط وتحمّل الدفعات من مجموعة البيانات الخاصة بك ثم fit() عليها، لكن هذا يبدو كمجهود غير ضروري. بدلاً من ذلك، إذا كنت تريد بث البيانات من مجموعة البيانات الخاصة بك على نحو فوري، فننصح بتحويل مجموعة البيانات الخاصة بك إلى tf.data.Dataset باستخدام طريقة to_tf_dataset().
تغطي فئة tf.data.Dataset مجموعة واسعة من حالات الاستخدام - وغالبًا ما يتم إنشاؤها من تينسورات في الذاكرة، أو باستخدام دالة تحميل لقراءة الملفات على القرص أو التخزين الخارجي. يمكن تحويل مجموعة البيانات بشكل تعسفي باستخدام طريقة map()، أو يمكن استخدام طرق مثل batch() وshuffle() لإنشاء مجموعة بيانات جاهزة للتدريب. لا تعدّل هذه الطرق البيانات المخزنة بأي شكل من الأشكال - بل تقوم بدلاً من ذلك ببناء رسم بياني لخط أنابيب البيانات سيُنفَّذ عند التكرار على مجموعة البيانات، عادةً أثناء تدريب النموذج أو الاستدلال. وهذا يختلف عن طريقة map() الخاصة بكيانات Dataset في Hugging Face، التي تُنفّذ دالة الخريطة فورًا وتحفظ الأعمدة الجديدة أو المعدّلة.
بما أن خط أنابيب معالجة البيانات المسبقة بأكمله يمكن تجميعه في tf.data.Dataset، فإن هذا النهج يتيح تحميل البيانات والتدريب بشكل متوازٍ وغير متزامن على نطاق واسع. ومع ذلك، يمكن أن يكون شرط تجميع الرسم البياني قيدًا، لا سيما بالنسبة لمُرمّزي Hugging Face، التي عادةً ما لا يمكن (بعد!) تجميعها كجزء من رسم بياني TF. ونتيجةً لذلك، ننصح عادةً بمعالجة مجموعة البيانات المسبقة كمجموعة بيانات Hugging Face، حيث يمكن استخدام دوال Python اعتباطية، ثم التحويل إلى tf.data.Dataset بعد ذلك باستخدام to_tf_dataset() للحصول على مجموعة بيانات مُجمّعة جاهزة للتدريب. للاطلاع على أمثلة على هذا النهج، يرجى الرجوع إلى الأمثلة أو الدفاتر الخاصة بـ transformers.
استخدام to_tf_dataset()
استخدام to_tf_dataset() أمر مباشر. بمجرد معالجة مجموعة البيانات مسبقاً وجاهزيتها، ما عليك سوى استدعاؤها على النحو التالي:
>>> from datasets import Dataset
>>> data = {"inputs": [[1, 2],[3, 4]], "labels": [0, 1]}
>>> ds = Dataset.from_dict(data)
>>> tf_ds = ds.to_tf_dataset(
columns=["inputs"],
label_cols=["labels"],
batch_size=2,
shuffle=True
)الكائن tf_ds المُعاد هنا جاهز الآن بالكامل للتدريب، ويمكن تمريره مباشرة إلى model.fit(). لاحظ أنك حددت حجم الدفعة عند إنشاء مجموعة البيانات، لذا لا حاجة لتحديده عند استدعاء fit():
>>> model.fit(tf_ds, epochs=2)للحصول على وصف كامل للحجج، يرجى الاطلاع على توثيق to_tf_dataset(). في كثير من الحالات، ستحتاج أيضاً إلى إضافة collate_fn إلى استدعائك. هذه دالة تأخذ عدة عناصر من مجموعة البيانات وتدمجها في دفعة واحدة. عندما تكون جميع العناصر بنفس الطول، فإن مُجمِّع الدُفعات الافتراضي المدمج كافٍ، لكن للمهام الأكثر تعقيداً قد يكون مُجمِّعاً مخصصاً ضرورياً. على وجه الخصوص، تحتوي العديد من المهام على عينات بطول تسلسل متغير، مما سيتطلب data collator قادراً على ملء الدُفعات بشكل صحيح. يمكنك الاطلاع على أمثلة على ذلك في أمثلة ودفاتر الملاحظات الخاصة بمعالجة اللغة الطبيعية في transformers، حيث تكون أطوال التسلسل المتغيرة شائعة جداً.
إذا لاحظت أن التحميل باستخدام to_tf_dataset بطيء، يمكنك أيضًا استخدام الحجة num_workers. يقوم هذا بإطلاق عدة عمليات فرعية لتحميل البيانات بالتوازي. هذه الميزة حديثة ولا تزال تجريبية إلى حد ما - يرجى فتح مشكلة إذا واجهت أي أخطاء أثناء استخدامها!
متى تستخدم to_tf_dataset
قد يكون القارئ الحصيف قد لاحظ في هذه النقطة أننا قدّمنا نهجين لتحقيق الهدف نفسه - فإذا كنت ترغب في تمرير مجموعة بياناتك إلى نموذج TensorFlow، يمكنك إما تحويل مجموعة البيانات إلى Tensor أو dict من Tensors باستخدام .with_format('tf')، أو يمكنك تحويل مجموعة البيانات إلى tf.data.Dataset باستخدام to_tf_dataset(). يمكن تمرير أيٍّ من هذين إلى model.fit()، إذن أيهما ينبغي أن تختار؟
الشيء الأساسي الذي يجب الانتباه إليه هو أنه عند تحويل مجموعة البيانات بأكملها إلى Tensors، فإنها تصبح ثابتة ومحمّلة بالكامل في ذاكرة الوصول العشوائي (RAM). هذا الأمر بسيط ومريح، ولكن إذا كان أي مما يلي ينطبق عليك، فمن المرجح أن تستخدم to_tf_dataset() بدلاً من ذلك:
- مجموعة بياناتك كبيرة جداً بحيث لا يمكن استيعابها في الذاكرة العشوائية.
to_tf_dataset()يبث دفعة واحدة في كل مرة، لذا يمكن التعامل مع مجموعات البيانات الكبيرة جداً باستخدام هذه الطريقة. - تريد تطبيق تحويلات عشوائية باستخدام
dataset.with_transform()أوcollate_fn. هذا شائع في عدة أنماط بيانات، مثل تعزيز الصور عند تدريب نماذج الرؤية، أو الإخفاء العشوائي عند تدريب نماذج اللغة المقنّعة. استخدامto_tf_dataset()سيطبّق تلك التحويلات في اللحظة التي يُحمَّل فيها الدفعة، مما يعني أن نفس العينات ستحصل على تعزيزات مختلفة في كل مرة تُحمَّل فيها. وهذا عادةً ما تريده. - تتميز بياناتك بعد متغير، مثل نصوص الإدخال في معالجة اللغة الطبيعية التي تتكون من أعداد متغيرة من الرموز. عند إنشاء دفعة تحتوي على عينات ذات بعد متغير، تكون الحل القياسي هو ملء العينات الأقصر حتى تطابق طول أطولها. عند بث العينات من مجموعة بيانات باستخدام
to_tf_dataset، يمكنك تطبيق هذا الملء على كل دفعة عبرcollate_fnالخاص بك. ومع ذلك، إذا كنت ترغب في تحويل مجموعة بيانات كهذه إلىTensorكثيفة، فستضطر إلى ملء العينات حتى تطابق طول أطول عينة في مجموعة البيانات بأكملها! وقد يؤدي ذلك إلى كميات هائلة من الملء، مما يهدر الذاكرة ويقلل من سرعة نموذجك.
الملاحظات والقيود
حاليًا، يعيد to_tf_dataset() دائمًا مجموعة بيانات مُجمّعة - سنضيف دعمًا لمجموعات البيانات غير المُجمّعة قريبًا!