الوحدة 2 — التحويل إلى TensorFlow Lite
الميزانيّة معلَنة. الآن نحوِّل نموذجنا: مصنِّف أوراق النباتات الذي دُرِّب بـMobileNetV2 مُعاد ضبطه على PlantVillage. ما نتلقّاه من الوحدة 08 (TensorFlow وKeras) هو مجلَّد SavedModel أو ملفّ .keras؛ ما ينتظره الهاتف هو ملفّ ثنائيّ .tflite واحد. الفارق ليس بسيطًا كتغيير امتداد: صيغة .tflite مختلفة تمامًا (FlatBuffers لا Protobuf)، والقائمة العلنيّة للعوامل ليست هي نفسها، والبيانات الوصفيّة تعيش داخل الملفّ نفسه لا في مجلَّد بجانبه.
ما الذي يحدث فعلًا أثناء التحويل
TFLiteConverter يقرأ رسم الحساب من نموذج Keras أو من SavedModel، ثمّ يقوم بثلاث خطوات متتالية:
أوّلًا، يُحوِّل عوامل TensorFlow إلى عوامل TFLite. ليست كلّ عمليّة موجودة في TFLite. ال قائمة الرسميّة تحوي نحو مئة وثلاثين عاملًا فقط، مقابل ألف وخمسمئة في TensorFlow الكامل. عمليّات نادرة (مثل tf.io.decode_jpeg داخل الرسم، أو أعداد عشوائيّة داخل طبقة مخصَّصة) تُرفَض.
ثانيًا، يُحسِّن الرسم. يدمج الالتفافات مع التفعيلات (Conv + BN + ReLU → عقدة واحدة)، ويُسقط الفروع الميتة، ويُوحِّد أنماط ثابتة. النتيجة رسم أصغر وأسرع.
ثالثًا، يُخرج ملفّ FlatBuffers. هذه صيغة ثنائيّة قُرئت بلا تفسير: الملفّ نفسه في الذاكرة يُقرأ مباشرة، دون تحويل هيكل. هذا ما يجعل .tflite سريع التحميل.
أوّل تحويل: النموذج الخام
نبدأ من نموذج Keras المدرَّب. لتبسيط التنفيذ، نستعمل هنا MobileNetV2 مُعاد ضبطه سابقًا وحفظه بصيغة .keras:
import tensorflow as tf
# 1) تحميل النموذج من قرص المكتب. جاء من الدورة 08.
model = tf.keras.models.load_model("plantvillage_mobilenetv2.keras")
# 2) تجهيز المحوّل من Keras. مسار مباشر يعمل في التسعين بالمئة من الحالات.
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 3) تحويل خام، بلا أيّ تحسين. يعطي ملفًّا كبيرًا لكنّه مطابق للنموذج الأصليّ.
tflite_model = converter.convert()
with open("plantvillage_raw.tflite", "wb") as f:
f.write(tflite_model)
print(f"حجم النموذج .tflite : {len(tflite_model) / 1024 / 1024:.2f} Mo")
نموذج MobileNetV2 كامل (float32) يُنتج عادة ملفًّا بحجم 14 مِيغا. رقم أعلى بكثير من ميزانيّتنا (8 مِيغا)، وسنحلّه بالتكميم في الوحدة 3.
المسار البديل: من SavedModel لا من Keras
يوجد مسار ثانٍ أكثر عموميّة، مفيد حين لا نمتلك كائن Keras، بل مجرَّد مجلَّد SavedModel (نتيجة model.export("plantvillage_saved") مثلًا، أ و نموذج يأتي من فريق آخر):
# البديل : من مجلَّد SavedModel. يعمل مع أيّ نموذج TensorFlow، ليس Keras فقط.
converter = tf.lite.TFLiteConverter.from_saved_model("plantvillage_saved/")
tflite_model = converter.convert()
المسار من SavedModel يدعم أنواعًا أوسع من النماذج، ويحفظ التوقيعات المتعدّدة إن وُجدت. أمّا المسار من Keras فأبسط ويكفي حين يكون النموذج مبنيًّا بواجهة keras.
العوامل غير المدعومة والعوامل المُحدَّدة
إن استعمل نموذجك عاملًا نادرًا، سترى رسالة من نوع:
Some ops are not supported by the native TFLite runtime, you can enable
TF Select ops... : RandomStandardNormal, Bincount, ...
هنا خياران:
الخيار المفضَّل: أعِد كتابة الجزء المسيء بعوامل مدعومة. tf.image.resize مثلًا مدعومة، فلماذا تستعمل tf.py_function تُطلق كود بايثون؟ هذا يعيدك في الغالب إلى مراجعة معماريّة النموذج، وهو ما يجب فعله باكرًا.
الخيار الاحتياطيّ: تفعيل « العوامل المُحدَّدة » (TF Select) التي تُدمج جزءًا من TensorFlow الكامل داخل مكتبة TFLite. ثمن هذا: حجم المكتبة على الهاتف يقفز من مِيغا واحد إلى ما بين 10 و15 مِيغا، وبعض المفوَّضين (GPU خصوصًا) يتوقّفون عن العمل على العوامل المُحدَّدة. لا تستعمل هذا الحلّ إلاّ حين يفشل الحلّ الأوّل.
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# تفعيل العوامل المُحدَّدة بحذر : تضخّم المكتبة وتُعطِّل GPU
converter.target_spec.supported_ops = [
tf.lite.OpsSet.TFLITE_BUILTINS,
tf.lite.OpsSet.SELECT_TF_OPS,
]
tflite_model = converter.convert()