فرمتهای Batch Prediction و استقرار مدل روی Edge
در Vertex AI فرمت پیشفرض ورودی Batch Prediction، JSON Lines است، اما برای نمونههای بزرگ مانند تصویر میتواند بسیار حجیم باشد. پارامتر instances_format امکان انتخاب فرمتهایی مانند csv، tf-record، tf-record-gzip، bigquery یا file-list را میدهد. در حالت file-list، یک فایل متنی شامل مسیر فایلهای ورودی به سرویس داده میشود؛ Vertex AI فایلها را باینری میخواند و به Base64 تبدیل میکند، بنابراین مدل باید لایهٔ Preprocessing مناسب مانند tf.io.decode_base64() و برای تصاویر tf.io.decode_image() یا tf.io.decode_png() داشته باشد.
بعد از پایان کار باید Jobها، Modelها و فایلهای Cloud Storage که دیگر لازم نیستند پاک شوند تا Resource و هزینهٔ اضافی باقی نماند.
استقرار مدل روی موبایل و دستگاههای نهفته
مدلهای یادگیری ماشین فقط روی Serverهای قدرتمند اجرا نمیشوند. اجرای محاسبات نزدیک منبع داده Edge Computing نام دارد؛ برای نمونه روی تلفن همراه، Fitness Tracker، کنترلر گرمایش یا سامانهٔ خودرو. این روش چند مزیت مهم دارد: حتی بدون اینترنت کار میکند، Latency را کاهش میدهد، بار Server را کم میکند و میتواند Privacy را بهتر کند چون دادهٔ خصوصی از دستگاه خارج نمیشود.
در مقابل، منابع یک Device کوچک بسیار محدودتر از Server چند-GPU است. مدل بزرگ ممکن است در Storage یا RAM جا نشود، CPU زیادی مصرف کند، Device را داغ کند و Battery را سریع تخلیه کند. بنابراین باید مدل سبک و کارآمد شود. TensorFlow Lite (TFLite) سه هدف اصلی دارد:
- کاهش اندازهٔ مدل برای کاهش زمان Download و مصرف RAM و Storage.
- کاهش تعداد محاسبات برای کمکردن Latency، مصرف انرژی و گرما.
- سازگارکردن مدل با محدودیتها و Acceleratorهای خاص Device.
تبدیل SavedModel به TFLite و FlatBuffers
TFLite Converter یک SavedModel را به فرمتی سبک مبتنی بر FlatBuffers تبدیل میکند. FlatBuffers طوری طراحی شده که داده بتواند با پردازش مقدماتی بسیار کم مستقیماً از حافظه خوانده شود. در Device، TFLite Interpreter فایل تبدیلشده را اجرا میکند.
converter = tf.lite.TFLiteConverter.from_saved_model(str(model_path))
tflite_model = converter.convert()
with open("my_converted_savedmodel.tflite", "wb") as f:
f.write(tflite_model)
میتوان یک Keras Model را نیز با tf.lite.TFLiteConverter.from_keras_model(model) مستقیماً تبدیل کرد. Converter عملیات مخصوص Train را حذف میکند، Expressionهای قابل سادهسازی را بهینه میکند و در صورت امکان چند Operation را Fuse میکند؛ برای نمونه Batch Normalization ممکن است در Operationهای لایهٔ قبلی Fold شود.
Quantization پس از آموزش
یکی از مؤثرترین راههای کمکردن اندازهٔ مدل، کاهش Bit-width است. استفاده از Float16 بهجای Float32 اندازه را تقریباً نصف میکند و معمولاً افت Accuracy اندکی دارد. TFLite میتواند Weightها را حتی به Integer هشتبیتی تبدیل کند؛ در نتیجه اندازه در مقایسه با Float32 حدود چهار برابر کمتر میشود.
شکل 19-5. Quantization پس از آموزش
در Quantization متقارن، بیشترین قدر مطلق Weight یعنی m پیدا میشود و بازهٔ -m..+m به بازهٔ Integer یعنی -127..+127 نگاشت میشود. صفر همیشه به صفر نگاشت میشود. برای فعالکردن Post-training Quantization کافی است Optimization پیشفرض را فعال کنیم:
converter.optimizations = [tf.lite.Optimize.DEFAULT]
اگر فقط Weightها Quantize شوند، اندازهٔ فایل بسیار کم میشود؛ اما در Runtime ممکن است Weightها برای محاسبات به Float بازگردند و Cache شوند، بنابراین لزوماً RAM یا زمان Inference کاهش چشمگیری پیدا نمیکند.
Quantization کامل و Quantization-Aware Training
برای کاهش واقعی Latency و مصرف انرژی بهتر است Activationها نیز Quantize شوند تا محاسبات کاملاً Integer باشند. حتی Integer با Bit-width مشابه Float معمولاً Cycle و Energy کمتری مصرف میکند؛ با Int8 بهبود بیشتر است. بعضی Acceleratorها مانند Edge TPU اساساً به Integer نیاز دارند.
Quantization Activationها به مرحلهٔ Calibration نیاز دارد. یک Representative Dataset از دادهٔ Train به Converter داده میشود تا محدودهٔ Activationها را اندازهگیری کند. اگر افت Accuracy زیاد باشد میتوان از Quantization-Aware Training استفاده کرد: Operationهای Quantization شبیهسازیشده در زمان Train وارد مدل میشوند تا Weightها با نویز Quantization سازگار شوند.
اجرای مدل در مرورگر با TensorFlow.js
اجرای مدل در Browser میتواند در سه وضعیت بسیار مفید باشد: اتصال اینترنت ناپایدار، نیاز به پاسخ بسیار سریع و نیاز به حفظ دادهٔ خصوصی در سمت Client. TensorFlow.js (TFJS) مدل را در Browser بارگذاری و Inference را بدون رفتوبرگشت به Server انجام میدهد.
import "https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest";
import "https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0";
const image = document.getElementById("image");
mobilenet.load().then(model => {
model.classify(image).then(predictions => {
for (let p of predictions) {
console.log(p.className + " : " + (p.probability * 100).toFixed(1) + "%");
}
});
});
یک Web App میتواند به PWA تبدیل شود و مانند App مستقل روی موبایل نصب شود. Service Worker میتواند Resourceها را Cache کند تا برنامه حتی Offline اجرا شود، Push Message بفرستد یا Taskهایی را در Background انجام دهد. مزیت مهم PWA نگهداری یک Codebase مشترک برای Web و Mobile است.
TFJS حتی Train کردن Model در Browser را پشتیبانی میکند و در صورت وجود GPU از WebGL استفاده میکند. Fine-tune کردن مدل بهصورت محلی روی دادهٔ کاربر میتواند Privacy را حفظ کند؛ این ایده با حوزهٔ Federated Learning ارتباط نزدیکی دارد.
استفاده از GPU برای شتابدادن محاسبات
Train کردن شبکهٔ عصبی بزرگ روی یک CPU ممکن است ساعتها یا روزها طول بکشد. GPU با Parallelism بسیار زیاد زمان را به دقیقه یا ساعت کاهش میدهد و امکان Experiment سریعتر و Retrain مکرر روی دادهٔ تازه را فراهم میکند. TensorFlow در محیطهای سازگار GPU را تشخیص میدهد و بسیاری از Operationها را بدون تغییر عمدهٔ Code روی آن اجرا میکند.
شکل 19-6. استفاده از GPU برای شتابدادن محاسبات
اگر قرار است GPU شخصی تهیه شود، مواردی مانند RAM، Memory Bandwidth، تعداد Coreها، توان، Cooling و سازگاری با CUDA باید بررسی شوند. برای Workloadهای Image و NLP مقدار VRAM کافی اهمیت زیادی دارد.
CUDA، cuDNN و پشتهٔ نرمافزاری GPU
شکل 19-7. CUDA، cuDNN و پشتهٔ نرمافزاری GPU
در سیستمهای Nvidia، TensorFlow روی CUDA و Libraryهای بهینهای مانند cuDNN تکیه میکند. Versionهای Driver، CUDA Toolkit، cuDNN و TensorFlow باید با یکدیگر سازگار باشند. Containerهای آماده یا محیطهای مدیریتشده معمولاً پیچیدگی نصب این Dependencyها را کمتر میکنند.
مشاهده و کنترل Deviceها در TensorFlow
TensorFlow فهرست Deviceهای فیزیکی و منطقی را در اختیار میگذارد و میتوان Visibility و Memory Policy را تنظیم کرد. در GPUهای مشترک لازم است از مصرف تمام حافظه جلوگیری شود یا GPU به Logical Deviceهای کوچکتر تقسیم شود.
gpus = tf.config.list_physical_devices("GPU")
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
شکل 19-8. مشاهده و کنترل Deviceها در TensorFlow
Operationها بهطور پیشفرض روی Device مناسب Place میشوند، اما با Contextهایی مانند with tf.device("/GPU:0"): میتوان Placement را کنترل کرد. انتقال Tensor بین CPU و GPU رایگان نیست و اگر Operation کوچک باشد ممکن است هزینهٔ Transfer بیشتر از منفعت GPU شود.
شکل 19-9. مشاهده و کنترل Deviceها در TensorFlow
اجرای موازی Graph روی چند Device
Operationهایی که وابستگی مستقیم ندارند میتوانند همزمان روی Deviceهای مختلف اجرا شوند. Scheduler TensorFlow وابستگیها را دنبال میکند و Nodeهای آماده را به Executor مربوط به Device میفرستد. برای رسیدن به Speedup واقعی، باید Granularity محاسبات و هزینهٔ Communication نیز در نظر گرفته شود.
شکل 19-10. اجرای موازی Graph روی چند Device
آموزش مدل روی چند Device
برای Scale کردن Training دو خانوادهٔ اصلی داریم: Model Parallelism و Data Parallelism. در Model Parallelism خود مدل میان Deviceها تقسیم میشود؛ در Data Parallelism نسخهای از مدل روی چند Worker قرار میگیرد و هر Worker Mini-batch متفاوتی را پردازش میکند.
Model Parallelism
اگر مدل آنقدر بزرگ باشد که روی یک GPU جا نشود، تقسیم Layerها یا بخشهای Graph میان Deviceها ضروری است. در مدلهای Sequential ساده، اگر Layerهای اول روی GPU0 و Layerهای بعد روی GPU1 باشند، بخش زیادی از زمان یکی از GPUها منتظر دیگری میماند؛ بنابراین Parallelism واقعی کم است.
شکل 19-11. Model Parallelism
برای شبکههایی با Branchهای مستقل، تقسیم مدل میتواند بهتر عمل کند زیرا شاخهها همزمان محاسبه میشوند. با این حال Backpropagation و Dependencyها همچنان Synchronization و Transfer ایجاد میکنند.
شکل 19-12. Model Parallelism
در RNNهای بزرگ نیز میتوان Neuronها یا Layerها را تقسیم کرد، اما Dependency زمانی باعث میشود طراحی Model Parallelism دشوار باشد. تقسیم Pipeline و Micro-batchها یکی از راههای افزایش Utilization است که در ادامهٔ فصل دوباره به آن برمیگردیم.
شکل 19-13. Model Parallelism
Data Parallelism و Mirrored Strategy
در بیشتر مدلهایی که روی یک GPU جا میشوند، Data Parallelism سادهتر و مؤثرتر است. مدل روی تمام GPUها Replica میشود. هر Replica یک Mini-batch متفاوت را Forward و Backward میکند و Gradientها در پایان Step با هم Aggregate میشوند. سپس Weightهای همهٔ Replicaها با Gradient یکسان Update میشوند.
شکل 19-14. Data Parallelism و Mirrored Strategy
در روش Mirrored، Parameterها روی تمام Deviceها کپی یکسان دارند و Updateها Synchronous هستند. چالش اصلی این است که Gradientهای تمام GPUها با سرعت بالا جمع شوند و نتیجه به همه برگردد. الگوریتمهای AllReduce برای همین هدف طراحی شدهاند. بخش بعدی Data Parallelism با Parameterهای متمرکز و Strategyهای رسمی TensorFlow را بررسی میکند.