- Safetensors هو تنسيق ملفٍ لتخزين أوزان نماذج التعلُّم الآلي، ويمنع ثغرات تنفيذ التعليمات البرمجية التعسفية الموجودة في التنسيقات القائمة على pickle.
- يتم تحميله بسرعة تصل إلى ٢–١٠ مرات أسرع من ملفات PyTorch ذات الامتداد .bin، وذلك باستخدام خرائط الذاكرة دون نسخ (zero-copy memory mapping)، ويدعم التحميل الكسول (lazy loading) للنماذج التي يتجاوز حجمها عدة جيجابايت.
- ثبِّته باستخدام الأمر
pip install safetensorsواستخدِمsafe_open()لتحميل الملف أوsave_file()لحفظ التنسورات. - مدعومٌ على نطاق واسع من قِبل Hugging Face وOllama و LM Studio, ComfyUI، وجميع إطارات التعلُّم الآلي الرئيسية.
Safetensors هو تنسيق ملف ثنائي لتخزين وأخذ أوزان نماذج التعلُّم الآلي، طوَّرته شركة Hugging Face. وهو يعالج ثغرات أمنية بالغة الخطورة موجودة في التنسيقات القائمة على pickle (مثل ملفات PyTorch ذات الامتداد .bin أو .pt)، وذلك باستخدام بنية بسيطة يمكن تحليلها ثابتًا (statically parseable)، ولا تسمح بتنفيذ أي تعليمات برمجية تعسفية أثناء عملية فك التسلسل (deserialization). ويحقِّق هذا التنسيق أوقات تحميل أسرع بكثير عبر خرائط الذاكرة والعمليات دون نسخ، ما يجعله التنسيق المفضَّل لتوزيع وتحميل نماذج الذكاء الاصطناعي في عام ٢٠٢٦.
ما هو Safetensors؟
يُخزِّن Safetensors التنسورات (المصفوفات متعددة الأبعاد للأعداد التي تمثِّل أوزان الشبكات العصبية) بصيغة ثنائية بسيطة تحتوي على رأس بصيغة JSON. وعلى عكس التنسيقات القائمة على pickle والتي استُخدمت تاريخيًّا في PyTorch، لا يحتوي Safetensors سوى على بيانات التنسور الأولية والمعلومات الوصفية (metadata) — دون أي كود بايثون، أو تعريفات للطبقات (classes)، أو تعليمات قابلة للتنفيذ.
ويتكوَّن هيكل الملف من الآتي:
- رأس بطول ٨ بايت يحتوي على طول قسم المعلومات الوصفية.
- قسم معلومات وصفية بصيغة JSON يحدِّد أسماء التنسورات وأشكالها وأنواع بياناتها وانحرافاتها (offsets) بالبايت.
- بيانات التنسور الأولية المخزَّنة بشكل متجاور في كتل ذاكرة مُحاذاة.
وتتيح هذه البساطة تحميل الملف عبر خرائط الذاكرة مباشرةً: حيث تقوم نظام التشغيل بربط الملف مباشرةً في ذاكرة العملية، مما يسمح بالوصول الفوري إلى بيانات التنسور دون نسخ جيجابايت عديدة إلى الذاكرة العشوائية (RAM). وبذلك، فإن وقت تحميل نموذج بحجم ٧ مليار معامل (7B parameter model) المحفوظ بصيغة Safetensors يُقاس بالملي ثانية بدلًا من الثواني.
لماذا أُنشئ Safetensors؟
إن تنسيق Python pickle، الذي يستخدم افتراضيًّا في دالة PyTorch torch.save() و torch.load()، يمكنه تنفيذ أي كود بايثون تعسفي أثناء عملية فك التسلسل. ويمكن لمهاجم ضار أن يُهيِّئ ملفًا ذا امتداد .bin أو .pt ليُنفِّذ برمجيات خبيثة عند استدعاء الدالة torch.load(). وهذه ليست ثغرة نظرية فقط — بل سُجِّلت حالات عدَّة في الواقع أظهرت ملفات نماذج مُسلَّحة بالفعل.
وبالإضافة إلى الجوانب الأمنية، تعاني التنسيقات القائمة على pickle من مشكلات أداء، منها:
| المشكلة | القائمة على pickle (.bin، .pt) | Safetensors |
|---|---|---|
| تنفيذ تعليمات برمجية تعسفية | نعم، وهي سمة جوهرية في pickle | لا، لأنه تنسيق ثابت |
| وقت التحميل لنماذج بحجم ٧ مليار معامل | ٥–١٥ ثانية | ٠٫٥–٢ ثانية |
| الاستهلاك الإضافي للذاكرة أثناء التحميل | ضعف حجم النموذج (يتطلَّب نسخًا) | حوالي ضعف الحجم نفسه (عبر خرائط الذاكرة) |
| إمكانية النقل بين الإطارات | محددة بـ Python/PyTorch | متوفرة لأي لغة تمتلك روابط (bindings) |
| دعم التحميل الكسول (Lazy loading) | لا | نعم |
فعند تحميل ملف pickle، يجب على بايثون فك تسلسل الهيكل الكامل إلى الذاكرة، وإعادة بناء كائنات بايثون، ثم نسخ بيانات التنسور إلى الصيغة الأصلية الخاصة بالإطار المستخدم. أما Safetensors فيلغي كل هذه الخطوات عبر ربط البيانات الثنائية للتنسور مباشرةً.
تثبيت Safetensors واستخدامه
ثبِّت مكتبة بايثون:
pip install safetensorsتحميل ملفات Safetensors
الاستخدام safe_open() للتحميل الكسول عبر خرائط الذاكرة:
from safetensors import safe_open
with safe_open("model.safetensors", framework="pt", device="cpu") as f:
# الحصول على قائمة بأسماء التنسورات
tensor_names = f.keys()
# تحميل تنسور معيَّن (تحميل كسول — يحمل هذا التنسور فقط)
embedding_weights = f.get_tensor("model.embed_tokens.weight")
# الحصول على المعلومات الوصفية للتنسور دون تحميله
metadata = f.metadata()الـ framework تحدد هذه المعلَّمة الإطار المستهدف: "pt" لـ PyTorch، "tf" لـ TensorFlow. "np" لـ NumPy، أو "jax" لـ JAX. وتحكّم معلَّمة الجهاز في مكان وضع المتجهات (tensors): "cpu", "cuda:0"، أو مُعرِّفات أجهزة أخرى.
لتحميل جميع المتجهات دفعة واحدة:
from safetensors.torch import load_file
tensors = load_file("model.safetensors")
# يُعيد قاموسًا: {"layer.weight": tensor، "layer.bias": tensor، ...}حفظ ملفات Safetensors
احفظ قاموسًا من المتجهات:
from safetensors.torch import save_file
import torch
tensors = {
"embedding.weight": torch.randn(50000, 768)،
"layer1.weight": torch.randn(768, 768)،
"layer1.bias": torch.randn(768)
}
save_file(tensors، "model.safetensors")تضمين بيانات وصفية مخصصة:
save_file(
tensors،
"model.safetensors"،
metadata={"model_type": "bert"، "vocab_size": "50000"}
)تحميل النماذج من Hugging Face
مكتبة transformers Hugging Face تستخدم تنسيق safetensors تلقائيًّا عند توافره:
from transformers import AutoModel
# يقوم تلقائيًّا بتنزيل النموذج وتحميله بصيغة .safetensors إذا كانت متاحة
model = AutoModel.from_pretrained("bert-base-uncased")إجبار استخدام تنسيق safetensors:
model = AutoModel.from_pretrained(
"bert-base-uncased"،
use_safetensors=True # يفشل إذا لم تكن safetensors متوفرة
)معظم النماذج على Hugging Face Hub تتضمّن الآن كلاً من model.safetensors و وpytorch_model.bin والتي تُفضِّل المكتبة تنسيق safetensors عند وجود كليهما.
التحويل بين التنسيقات
تحويل ملفات PyTorch (.bin) إلى Safetensors
from safetensors.torch import save_file
import torch
# تحميل نقطة التحقق (checkpoint) الخاصة بـ PyTorch
state_dict = torch.load("pytorch_model.bin"، map_location="cpu")
# حفظها بصيغة safetensors
save_file(state_dict، "model.safetensors")تحويل ملفات Safetensors إلى ملفات PyTorch (.bin)
from safetensors.torch import load_file
import torch
tensors = load_file("model.safetensors")
torch.save(tensors، "pytorch_model.bin")برنامج تحويل Hugging Face
الـ transformers تتضمن المكتبة أداة تحويل:
python -m transformers.convert_safetensors_to_pytorch
--model_name_or_path .\/model_folder
--output_dir .\/convertedالتفاصيل التقنية لتنسيق الملف
يتّسم ملف safetensors بالهيكل التالي:
- الرأس (8 بايت): عدد صحيح غير مُوقَّع بطول 64 بت، مُرتَّب وفق ترتيب البايت الصغير (little-endian)، ويحتوي على طول البيانات الوصفية (metadata) بالبايت مُعبَّرًا عنها بصيغة JSON
- البيانات الوصفية (متغيِّرة الطول): كائن JSON يتبع هذا المخطط:
{ "layer_name": { "dtype": "F32"، \/\/ نوع البيانات: F32، F16، BF16، I64، I32، إلخ. "shape": [768، 768]، \/\/ أبعاد المتجه "data_offsets": [0، 2359296] \/\/ الموقع البايتي الابتدائي والنهائي في قسم البيانات }، "__metadata__": { \/\/ بيانات وصفية مخصصة اختيارية "key": "value" } } - قسم البيانات: البايتات الأولية للمتجهات، المخزَّنة وفق الترتيب المتصل C (row-major)، ومُحاذاة على حدود 8 بايت
يدعم التنسيق أنواع البيانات التالية: F64، F32، F16، BF16، I64، U64، I32، U32، I16، U16، I8، U8، BOOL. ويتم تحديد النطاق البايتي لكل متجه في البيانات الوصفية، ما يسمح بتحميل انتقائي دون الحاجة إلى تحليل الملف بأكمله.
دعم النظام البيئي
يتم دعم safetensors عبر نظام الذكاء الاصطناعي بأكمله:
| الأداة/الإطار | مستوى الدعم | ملاحظات |
|---|---|---|
| Hugging Face Transformers | السكان الأصليين | التنسيق الافتراضي بدءًا من الإصدار v4.30 |
| Hugging Face Diffusers | السكان الأصليين | يُستخدم في نماذج Stable Diffusion |
| PyTorch | عبر المكتبة | يتطلب حزمة safetensors package |
| TensorFlow | عبر المكتبة | مدعوم عبر الروابط البرمجية (bindings) |
| JAX | عبر المكتبة | مدعوم عبر الروابط البرمجية (bindings) |
| Ollama | السكان الأصليين | يقوم بتحويلها داخليًّا إلى GGUF |
| LM Studio | السكان الأصليين | يحمل ملفات safetensors مباشرة |
| ComfyUI | السكان الأصليين | التنسيق الأساسي للنماذج المخصصة |
| AUTOMATIC1111 | السكان الأصليين | يدعم واجهة Stable Diffusion WebUI |
| llama.cpp | عبر التحويل | تحويل إلى تنسيق GGUF |
| في إل إل إم | السكان الأصليين | دعم خادم الاستنتاج (Inference server support) |
| TGI | السكان الأصليين | دعم استنتاج توليد النصوص (Text Generation Inference) |
عند تقييم ما إذا كان النموذج سيتّسع في ذاكرة GPU الخاصة بك، استخدم حاسبة الذاكرة VRAM لتقدير المتطلبات استنادًا إلى عدد المُعاملات ومستوى التكمين. ولا يؤثر تنسيق الملف نفسه على استهلاك الذاكرة VRAM — فملفات safetensors وملفات pickle لنفس النموذج تستهلك كمية متطابقة من ذاكرة GPU بعد تحميلها.
الخصائص الأداءية
نتائج الاختبارات على نظام مزوَّد بقرص SSD من نوع NVMe وذاكرة RAM سعة 64 جيجابايت، عند تحميل نموذج مكوَّن من 7 مليار معامل:
| الصيغة | زمن التحميل | أقصى استخدام لذاكرة RAM | حجم الملف |
|---|---|---|---|
| ملف PyTorch (.bin) | 8.2 ثانية | 28 جيجابايت | 13.5 جيجابايت |
| ملف safetensors (باستخدام الدالة load_file) | 1.1 ثانية | ١٤ غيغابايت | 13.5 جيجابايت |
| ملف safetensors (بالتحميل الكسول safe_open lazy) | 0.08 ثانية | 0.5 جيجابايت | 13.5 جيجابايت |
وتُعد طريقة التحميل الكسول باستخدام safe_open() مُفيدةً بشكل خاص عندما تحتاج إلى فحص بنية النموذج أو استخراج طبقات محددة أو تحميل نماذج تتجاوز سعة الذاكرة RAM المتاحة، وذلك عبر تحميل التنسورات بشكل انتقائي.
ويدعم تنسيق safetensors بالنسبة للنماذج المكمَّنة جميع أنواع البيانات القياسية، ومنها FP16 وBF16 وINT8 وINT4. ويشمل قاعدة بيانات نماذج الذكاء الاصطناعي أحجام ملفات safetensors ومتطلبات ذاكرة VRAM لـ 37 نموذجًا شائعًا عبر مستويات تكمين مختلفة.
النماذج المجزَّأة (Sharded Models)
غالبًا ما تُوزَّع النماذج الأكبر من بضعة جيجابايت على هيئة عدة ملفات safetensors (التقسيم أو sharding). فقد يُقسَّم نموذج مكوَّن من 70 مليار معامل إلى 8 أجزاء (shards) على النحو التالي:
model-00001-of-00008.safetensors
model-00002-of-00008.safetensors
...
model-00008-of-00008.safetensors
model.safetensors.index.jsonويُطابق ملف الفهرسة (index file) أسماء التنسورات مع أسماء الملفات المُقسَّمة:
{
"metadata": {"total_size": 141123453952},
"weight_map": {
"model.embed_tokens.weight": "model-00001-of-00008.safetensors",
"model.layers.0.self_attn.q_proj.weight": "model-00001-of-00008.safetensors",
"model.layers.40.mlp.gate_proj.weight": "model-00005-of-00008.safetensors"
}
}وتتعامل مكتبات Hugging Face تلقائيًّا مع التحميل المُقسَّم. أما التحميل اليدوي فيتم كما يلي:
import json
from safetensors import safe_open
with open("model.safetensors.index.json") as f:
index = json.load(f)
weight_map = index["weight_map"]
# تحميل تنسور معيَّن بالبحث عن الجزء (shard) الذي يحتويه
tensor_name = "model.layers.20.mlp.down_proj.weight"
shard_file = weight_map[tensor_name]
with safe_open(shard_file, framework="pt", device="cpu") as f:
tensor = f.get_tensor(tensor_name)روابط اللغات (Language Bindings)
ورغم أن التنفيذ المرجعي مكتوب بلغة Python، فإن لـ safetensors روابط (bindings) متعددة للغات برمجية أخرى:
- Rust: التنفيذ الأساسي بلغة Rust لتحقيق الأداء والأمان
- Python: روابط رسمية عبر PyPI
- JavaScript/Node.js:
@huggingface/safetensorsحزمة npm - C/C++: متاحة عبر واجهة الربط الخارجية FFI مع مكتبة Rust
- Go: توجد تنفيذات مجتمعية متاحة
ويُعتبر التنفيذ بلغة Rust هو المرجع الرسمي. أما الروابط بلغة Python فهي تغلف هذا التنفيذ وتتوارث خصائصه في الأداء.
الأسئلة الشائعة
هل يمكنني استخدام safetensors مع نماذج PyTorch الحالية دون إجراء أي تغييرات في الكود؟
نعم، إذا كنت تستخدم مكتبات Hugging Face. أما بالنسبة للنماذج المخصصة، فستحتاج إلى تغيير torch.load() في عميل OpenAI الخاص بك إلى load_file() من حزمة safetensors و torch.save() في عميل OpenAI الخاص بك إلى save_file()أما بيانات التنسور وبُنية النموذج فتبقى مطابقة تمامًا — والفرق الوحيد هو تنسيق التسلسل (serialization format). ويُعد تحويل نقاط التفتيش (checkpoints) الحالية عملية لمرة واحدة تستغرق بضع ثوانٍ فقط.
هل تعمل ملفات safetensors عبر إصدارات مختلفة من PyTorch؟
نعم. فعلى عكس ملفات pickle التي قد تتوقف عن العمل عند تغيُّر إصدار Python أو PyTorch، فإن ملفات safetensors تخزن البيانات الثنائية الأولية دون أي تسلسل يعتمد على الإصدار. وبالتالي، فإن ملف safetensors الذي أُنشئ باستخدام PyTorch 1.12 يُحمَّل بشكل صحيح في إصدار PyTorch 2.x والعكس صحيح. وهذا يجعل safetensors خيارًا أفضل لأرشفة النماذج وتوزيعها على المدى الطويل.
لماذا لا تزال بعض النماذج على Hugging Face Hub تُوزَّع على هيئة ملفات .bin؟
قد تحتوي النماذج الأقدم التي تم رفعها قبل عام 2023 فقط على ملفات تعتمد على pickle. وتقوم Hugging Face تدريجيًّا بتحويل مكتبة النماذج، لكن بعض النماذج تظل تعتمد على pickle فقط إن لم يوفِّر مُرفِعها الأصلي إصدارات بتنسيق safetensors. وعند وجود كلا التنسيقين، يُفضَّل تنسيق safetensors تلقائيًّا. ويمكنك التحويل محليًّا باستخدام الطريقة الموضحة في قسم التحويل أعلاه.
هل يؤدي استخدام safetensors إلى زيادة حجم الملف مقارنةً بملف PyTorch (.bin)؟
لا. فحجم الملفات يكون عادةً متطابقًا أو يختلف بنسبة 1–2% فقط، لأن كلا التنسيقين يخزنان نفس البيانات الثنائية الأولية للتنسورات. ويضيف تنسيق safetensors هامشًا ضئيلًا جدًّا (رأس JSON بضعة كيلوبايت)، بينما يضيف تنسيق pickle هامشًا ناتجًا عن تسلسل كائنات Python. فعلى سبيل المثال، ينتج كلا التنسيقين لنموذج مكوَّن من 7 مليار معامل ملفات بحجم 13–14 جيجابايت تقريبًا. والفرق الحقيقي يكمن في سرعة التحميل والأمان، وليس في كفاءة التخزين.
هل يمكنني فحص ملفات safetensors دون تحميل النموذج الكامل في الذاكرة؟
نعم، وهذه إحدى المزايا الأساسية لـ safetensors. استخدم safe_open() لقراءة البيانات الوصفية (metadata) والتحميل الانتقائي لتينسورات محددة. ويمكنك سرد أسماء جميع التنسورات، والتحقق من أشكالها وأنواع بياناتها، واستخراج طبقات فردية دون تحميل الملف الكامل الذي قد يبلغ حجمه عدة جيجابايت. وهذه الميزة مفيدة جدًّا في تحليل النماذج وتصحيح الأخطاء واستخراج مكوناتها.
هل يُعادل GGUF وsafetensors الشيء نفسه؟
لا. فـ GGUF (GPT-Generated Unified Format) هو تنسيق مختلف يستخدمه llama.cpp أساسًا في عمليات الاستنتاج المكمَّنة. ويتضمَّن GGUF خطط تكمين مُحسَّنة للاستنتاج على وحدة المعالجة المركزية (CPU) لا يدعمها safetensors. أما safetensors فهو مصمَّم للتدريب والتوزيع العام للنماذج، بينما يركِّز GGUF على تحقيق كفاءة عالية في الاستنتاج على الأجهزة الاستهلاكية. وتقبل العديد من الأدوات مثل Ollama ملفات safetensors كمدخلات ثم تقوم داخليًّا بتحويلها إلى GGUF للاستنتاج.

