تحسينات WebAssembly وWebGPU لتحسين الذكاء الاصطناعي على الويب بشكل أسرع، الجزء الثاني

هذا المستند هو تكملة لـ تحسينات WebAssembly وWebGPU لتعزيز أداء الذكاء الاصطناعي على الويب، الجزء 1. ننصحك بقراءة هذه المشاركة أو مشاهدة المحادثة في مؤتمر IO 24 قبل المتابعة.

Austin Eng
Austin Eng
Deepti Gandluri
Deepti Gandluri
François Beaufort
François Beaufort

WebGPU

تمنح WebGPU تطبيقات الويب إمكانية الوصول إلى أجهزة وحدة معالجة الرسومات (GPU) في العميل لإجراء عمليات حسابية فعّالة وموازية للغاية. منذ إطلاق WebGPU في Chrome، شاهدنا عروضًا توضيحية مذهلة للذكاء الاصطناعي وتعلُّم الآلة على الويب.

على سبيل المثال، أثبتت تقنية Web Stable Diffusion إمكانية استخدام الذكاء الاصطناعي لإنشاء صور من نص في المتصفّح مباشرةً. في وقت سابق من هذا العام، نشر فريق Mediapipe في Google دعمًا تجريبيًا لاستنتاج النماذج اللغوية الكبيرة.

يعرض المخطّط المتحرك التالي Gemma، وهو النموذج اللغوي الكبير (LLM) المفتوح المصدر من Google، الذي يتم تشغيله بالكامل على الجهاز في Chrome، في الوقت الفعلي.

يُنشئ العرض التقديمي التالي من Hugging Face لنموذج Segment Anything من Meta أقنعة أشياء عالية الجودة بالكامل على العميل.

هذه ليست سوى بعض المشاريع الرائعة التي تعرض قدرة WebGPU على الذكاء الاصطناعي والتعلم الآلي. تتيح WebGPU تشغيل هذه النماذج وغيرها بشكل أسرع بكثير من السرعة التي يمكن أن تحقّقها وحدة المعالجة المركزية.

يُظهر مقياس أداء WebGPU لدمج النصوص من Hugging Face زيادةً كبيرة في السرعة مقارنةً بتنفيذ النموذج نفسه باستخدام وحدة المعالجة المركزية. على كمبيوتر محمول من طراز Apple M1 Max، كان WebGPU أسرع بأكثر من 30 مرة. أفاد آخرون أنّ WebGPU يسرع الأداء في اختبار الأداء بأكثر من 120 مرة.

تحسين ميزات WebGPU للذكاء الاصطناعي وتعلُّم الآلة

إنّ WebGPU مفيد جدًا لنماذج الذكاء الاصطناعي وتعلُّم الآلة، والتي يمكن أن تتضمّن مليارات المَعلمات، وذلك بفضل توافقها مع برامج تظليل الحوسبة. يتم تشغيل برامج تظليل الحوسبة على وحدة معالجة الرسومات وتساعد في تنفيذ عمليات الصفيفات المتوزية على كميات كبيرة من البيانات.

من بين التحسينات العديدة التي أجريناها على WebGPU في العام الماضي، واصلنا إضافة المزيد من الإمكانات لتحسين أداء الذكاء الاصطناعي والتعلم الآلي على الويب. أطلقنا مؤخرًا ميزتَين جديدتَين: النقطة العائمة 16 بت ومنتجات النقطة الصحيحة المُجمَّعة.

فاصلة عائمة 16 بت

تذكَّر أنّ أعباء العمل المتعلّقة بالذكاء الاصطناعي لا تتطلّب الدقة. ‫shader-f16 هي ميزة تتيح استخدام نوع f16 في لغة تظليل WebGPU. يشغل هذا النوع من الأرقام العشرية العائمة 16 بتًا بدلاً من 32 بتًا المعتاد. يمتلك تنسيق f16 نطاقًا أصغر ودقة أقل، ولكن هذا كافٍ للعديد من نماذج الذكاء الاصطناعي.

تزيد هذه الميزة من الكفاءة بعدة طرق:

  • الذاكرة المنخفضة: تشغل مصفوفات Tensor التي تحتوي على عناصر f16 نصف المساحة، ما يؤدي إلى خفض استخدام الذاكرة إلى النصف. غالبًا ما تواجه عمليات حساب وحدة معالجة الرسومات قيودًا في معدل نقل البيانات في الذاكرة، لذا يمكن أن يؤدي استخدام نصف الذاكرة إلى تشغيل أدوات التظليل بسرعة أكبر مرتين. من الناحية الفنية، لا تحتاج إلى f16 لتوفير معدل نقل البيانات في الذاكرة. من الممكن تخزين البيانات بتنسيق منخفض الدقة، ثم توسيعها إلى f32 كامل في برنامج التظليل للحساب. وتستهلك وحدة معالجة الرسومات قوة حوسبة إضافية لحزم البيانات وفك تشفيرها.

  • تقليل تحويل البيانات: يستخدم الإصدار f16 طاقة معالجة أقل من خلال تقليل تحويل البيانات. يمكن تخزين البيانات ذات الدقة المنخفضة ثم استخدامها مباشرةً بدون تحويلها.

  • زيادة التوازُف: يمكن لوحدات معالجة الرسومات الحديثة استيعاب المزيد من القيم في آنٍ واحد في وحدات تنفيذ وحدة معالجة الرسومات، ما يتيح لها إجراء عدد أكبر من العمليات الحسابية الموازية. على سبيل المثال، وحدة معالجة الرسومات التي تتيح ما يصل إلى 5 تريليون عملية بنقطة عائمة من النوع f32 في الثانية قد تتيح 10 تريليون عملية بنقطة عائمة من النوع f16 في الثانية.

لقطة شاشة لمقياس أداء WebGPU لدمج النصوص
باستخدام shader-f16، يُجري اختبار WebGPU لتضمين النصوص من Hugging Face الاختبار 3 مرات أسرع من f32 على الكمبيوتر المحمول Apple M1 Max.

WebLLM هو مشروع يمكنه تشغيل نماذج لغوية كبيرة متعددة. ويستخدم Apache TVM، وهو إطار عمل مفتوح المصدر لمجمع تعلُّم الآلة.

طلبت من WebLLM التخطيط لرحلة إلى باريس باستخدام نموذج Llama 3 الذي يتضمّن ثمانية مليارات مَعلمة. تُظهر النتائج أنّه خلال مرحلة الملء المُسبَق للنموذج، يكون تنسيق f16 أسرع بمقدار 2.1 مرة من تنسيق f32. وخلال مرحلة فك التشفير، تكون السرعة أكبر من 1.3 مرة.

يجب أن تؤكد التطبيقات أولاً أنّ محوِّل وحدة معالجة الرسومات متوافق مع f16، وإذا كان متاحًا، يجب تفعيله صراحةً عند طلب جهاز وحدة معالجة الرسومات. إذا لم يكن العنصر f16 متوافقًا، لا يمكنك طلبه في مصفوفة requiredFeatures.

// main.js

const adapter = await navigator.gpu.requestAdapter();
const supportsF16 = adapter.features.has('shader-f16');
if (supportsF16) {
  // Use f16.
  const device = await adapter.requestDevice({
    requiredFeatures: ['shader-f16'],
  });
  initApp(device);
}

بعد ذلك، في أدوات تظليل WebGPU، عليك تفعيل f16 صراحةً في أعلى الصفحة. بعد ذلك، يمكنك استخدامه في برنامج التظليل مثل أي نوع بيانات float آخر.

// my-shader.wgsl

enable f16;

struct Data {
  values : array<vec4<f16>>
}
@group(0) @binding(0) var<storage, read> data : Data;
@compute @workgroup_size(64) fn main(@builtin(global_invocation_id) gid : vec3u) {
  let value : vec4<f16> = data.values[gid.x];
  ...
}

عمليات جمع الأعداد الصحيحة المجمّعة

لا تزال العديد من النماذج تعمل بشكل جيد مع دقة 8 بت فقط (نصف f16). ويُعدّ هذا الإجراء شائعًا بين النماذج اللغوية الكبيرة والنماذج الصورية لتقسيم الصور والتعرّف على الأجسام. ومع ذلك، تنخفض جودة المخرجات للنماذج مع انخفاض الدقة، لذا فإنّ التقطيع بدقة 8 بت غير مناسب لكل تطبيق.

لا تتيح سوى وحدات معالجة رسومات قليلة نسبيًا استخدام القيم الثمانية بت. وهنا يأتي دور منتجات النقطة الصحيحة المعبّأة. لقد طرحنا الإصدار DP4a من Chrome 123.

تحتوي وحدات معالجة الرسومات الحديثة على تعليمات خاصة لأخذ عددين صحيحَين من 32 بت وتفسيرهما على أنّهما 4 أعداد صحيحة من 8 بت مُجمَّعة بشكل تسلسلي وحساب المنتج النقطي بين مكوّناتهما.

ويُعدّ ذلك مفيدًا بشكل خاص للذكاء الاصطناعي وتعلُّم الآلة لأنّ نوى ضرب المصفوفات تتألف من العديد من منتجات النقط.

على سبيل المثال، لنضرب مصفوفة 4 x ‏8 بمتجه 8 x ‏1. يتضمن احتساب ذلك أخذ 4 حواصل ضرب قياسي لحساب كل قيمة من القيم في متجه الإخراج: A وB وC وD.

مخطّط بياني لمثال على ضرب المصفوفة بالمتجه

عملية احتساب كلّ من هذه النتائج هي نفسها، وسنلقي نظرة على الخطوات المُتّبعة لاحتساب أحدها. قبل إجراء أي عملية حسابية، سنحتاج أولاً إلى تحويل بيانات الأعداد الصحيحة المكونة من 8 بت إلى نوع يمكننا إجراء عمليات حسابية به، مثل f16. بعد ذلك، نُجري عملية ضرب لكل عنصر على حدة، ثم نجمع كل المنتجات معًا. في المجمل، لإجراء عملية الضرب بين المصفوفة والمتجه بالكامل، نُجري 40 عملية تحويل من الأعداد الصحيحة إلى الأعداد الكسريّة لفك تشفير البيانات و32 عملية ضرب أعداد كسرية و28 عملية جمع أعداد كسرية.

بالنسبة إلى المصفوفات الأكبر حجمًا التي تحتوي على المزيد من العمليات، يمكن أن تساعد منتجات النقط الصحيحة المعبأة في تقليل كمية العمل.

لكلّ ناتج من النواتج في متجه النتائج، نُجري عمليتَي منتج نقطي مُجمَّعَتَين باستخدام dot4U8Packed المضمّنة في WebGPU Shading Language، ثم نجمع النتائج معًا. في المجمل، لا نُجري أي عملية تحويل للبيانات في عملية الضرب بين المصفوفة والمتجه. نُجري 8 عمليات ضرب نقطي مجمّعة و4 عمليات جمع أعداد صحيحة.

مخطّط بياني لمثال على ضرب مصفوفة عددية متراصة في متجه

لقد اختبرنا منتجات النقطة الصحيحة المُجمَّعة باستخدام بيانات 8 بت على مجموعة متنوعة من وحدات معالجة الرسومات المخصّصة للمستهلكين. مقارنةً بالتنسيق العشري المتغير بسعة 16 بت، يمكننا أن نرى أنّ التنسيق العشري المتغير بسعة 8 بت أسرع من 1.6 إلى 2.8 مرة. وعندما نستخدم أيضًا عمليات الضرب النقطي للأرقام الصحيحة المُجمَّعة، يكون الأداء أفضل. وهي أسرع من 1.7 إلى 2.9 مرة.

لقطة شاشة لزيادة سرعة ضرب المصفوفة بالمتجه: f16 مقارنةً بـ u8
المخطّط 1: تسريع مصفوفة المتجهات، مقارنةً بين f16 وU8 وU8 مع dot4U8Packed

تحقّق من توافق المتصفّح مع الموقع الإلكتروني wgslLanguageFeatures. إذا لم يكن وحدة معالجة الرسومات متوافقة مع منتجات النقاط المُجمَّعة بشكلٍ أصلي، سيضيف المتصفّح عناصر polyfill إلى التنفيذ الخاص به.

// main.js

if (navigator.gpu.wgslLanguageFeatures.has('packed_4x8_integer_dot_product')) {
  // Use dot4U8Packed, dot4I8Packed builtin
  // functions in the shaders.
}

يوضّح المقتطف التالي للاختلاف في التعليمات البرمجية التغييرات اللازمة لاستخدام منتجات الأعداد الصحيحة المُجمَّعة في برنامج تشويش WebGPU.

قبل: برنامج تشويش WebGPU يجمع ضربات نقطية جزئية في المتغيّر sum. في نهاية الحلقة، يحتفظ sum بالضربة النقطية الكاملة بين متجه وصف واحد من مصفوفة الإدخال.

// my-dot-product.wgsl

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid : vec3u) {
  var sum : f16;
  let start = gid.x * uniforms.dim;
  for (var i = 0u; i < uniforms.dim; i++) {
    let v1 : vec4<f16> = vector.values[i];
    let v2 : vec4<f16> = matrix.values[start + i];
    sum += dot(v1, v2);
  }
}

بعد ذلك: برنامج تشويش WebGPU مكتوب لاستخدام حاصلات الضرب النقطية للأرقام الصحيحة المُجمَّعة. يكمن الاختلاف الرئيسي في أنّ هذا المخطِّط اللوني يحمِّل عددًا صحيحًا واحدًا بسعة 32 بت بدلاً من تحميل 4 قيم عائمة من المتّجه والمصفّفة. يحتوي هذا العدد الصحيح المكوّن من 32 بت على بيانات أربع قيم صحيحة مكوّنة من 8 بت. بعد ذلك، نُطلِق dot4U8Packed لاحتساب ناتج الضرب النقطي للقيمتَين.

// my-dot-product.wgsl

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid : vec3u) {
  var sum : f32;
  let start = gid.x * uniforms.dim;
  for (var i = 0u; i < uniforms.dim; i++) {
    let v1 : u32 = vector.values[i];
    let v2 : u32 = matrix.values[start + i];
    sum += dot4U8Packed(v1, v2);
  }
}

إنّ منتجات النقطة العائمة 16 بت والمنتجات الصحيحة المُجمَّعة هي الميزات المضمّنة في Chrome التي تُسرع الذكاء الاصطناعي وتعلُّم الآلة. تتوفّر النقطة العائمة 16 بت عندما تتيحها الأجهزة، وينفّذ Chrome منتجات النقطة الصحيحة المعبأة على جميع الأجهزة.

يمكنك استخدام هذه الميزات في الإصدار الثابت من Chrome اليوم لتحقيق أداء أفضل.

الميزات المقترَحة

في المستقبل، سننظر في ميزتَين إضافيتَين: المجموعات الفرعية وضرب المصفوفات التعاوني.

تتيح ميزة المجموعات الفرعية إجراء عمليات حسابية جماعية، مثل جمع أكثر من 16 رقمًا، أو إتاحة إمكانية التواصل على مستوى SIMD. يتيح ذلك مشاركة البيانات بكفاءة بين سلاسل المحادثات. تتوفّر المجموعات الفرعية في واجهات برمجة التطبيقات الحديثة لوحدات معالجة الرسومات، بأسماء مختلفة وبأشكال مختلفة قليلاً.

لقد لخّصنا المجموعة الشائعة في اقتراح تم تقديمه إلى مجموعة WebGPU المعنيّة بالمواصفات. لقد أعددنا نماذج أولية للمجموعات الفرعية في Chrome باستخدام علامة تجريبية، وعرضنا نتائجنا الأولية في المناقشة. تتمثل المشكلة الرئيسية في كيفية ضمان السلوك المتوافق مع الأجهزة الجوّالة.

إنّ عملية ضرب المصفوفات التعاونية هي إضافة حديثة إلى وحدات معالجة الرسومات. يمكن تقسيم عملية ضرب مصفوفة كبيرة إلى عمليات ضرب مصفوفات متعددة أصغر حجمًا. تُجري عملية ضرب المصفوفة التعاونية عمليات ضرب على هذه الكتل الأصغر حجمًا والثابتة في خطوة منطقية واحدة. وفي هذه الخطوة، تتعاون مجموعة من سلاسل المحادثات بكفاءة لاحتساب النتيجة.

أجرينا استطلاعًا حول مدى توفّر الدعم في واجهات برمجة تطبيقات وحدة معالجة الرسومات الأساسية، ونخطّط لتقديم اقتراح إلى مجموعة WebGPU المعنيّة بالمواصفات. كما هو الحال مع المجموعات الفرعية، نتوقّع أن تركّز معظم المناقشة على إمكانية النقل.

لتقييم أداء عمليات المجموعات الفرعية، دمجنا ميزة تجريبية للمجموعات الفرعية في MediaPipe واختبرناها باستخدام النموذج الأولي لعمليات المجموعات الفرعية في Chrome في تطبيق حقيقي.

لقد استخدمنا مجموعات فرعية في نوى وحدة معالجة الرسومات (GPU) لمرحلة الملء المُسبَق للنموذج اللغوي الكبير، لذا أُبلغ فقط عن السرعة التي تمّ تحقيقها في مرحلة الملء المُسبَق. في وحدة معالجة الرسومات Intel، نلاحظ أنّ المجموعات الفرعية تحقّق أداءً أسرع بمقدار مرتين ونصف مقارنةً بالأداء الأساسي. ومع ذلك، لا تكون هذه التحسينات متسقة في وحدات معالجة الرسومات المختلفة.

لقطة شاشة لتسريع معالجة المجموعات الفرعية في الاستنتاج باستخدام نموذج تعلُّم ضخم للغة (LLM) في MediaPipe
رسم بياني 2. تؤدي المجموعات الفرعية إلى تسريع عملية الملء المُسبَق بمقدار 2.5 مرة على وحدة معالجة الرسومات Intel Tiger Lake GT2، مع إتاحة ميزة تجريبية في Chrome وMediapipe.

يعرض الرسم البياني التالي نتائج تطبيق المجموعات الفرعية لتحسين اختبار الأداء المجهري لضرب المصفوفات على مستوى وحدات معالجة الرسومات المتعددة المخصّصة للمستهلكين. يُعدّ ضرب المصفوفات من العمليات الأكثر كثافة في النماذج اللغوية الكبيرة. توضّح البيانات أنّ المجموعات الفرعية تزيد السرعة في العديد من وحدات معالجة الرسومات بمقدار ضعفين أو خمسة أضعاف أو حتى ثلاثة عشر ضعفًا من السرعة الأساسية. ومع ذلك، يُرجى ملاحظة أنّ المجموعات الفرعية ليست أفضل كثيرًا في وحدة معالجة الرسومات الأولى.

لقطة شاشة لزيادة سرعة المجموعة الفرعية لضرب المصفوفات
الرسم البياني 3- قد يؤدي تطبيق مجموعات فرعية على ضرب المصفوفات إلى تحسين الأداء بشكلٍ أكبر.

من الصعب تحسين أداء وحدة معالجة الرسومات.

في النهاية، تعتمد أفضل طريقة لتحسين وحدة معالجة الرسومات على وحدة معالجة الرسومات التي يوفّرها العميل. لا يؤدي استخدام ميزات وحدة معالجة الرسومات الجديدة والمميّزة إلى تحقيق النتائج المتوقّعة دائمًا، لأنّه قد تكون هناك الكثير من العوامل المعقدة المعنيّة. قد لا تكون أفضل استراتيجية تحسين على وحدة معالجة رسومات واحدة هي أفضل استراتيجية على وحدة معالجة رسومات أخرى.

إذا كنت تريد تقليل عرض النطاق الترددي للذاكرة مع استخدام سلاسل المهام الحسابية لوحدة معالجة الرسومات بالكامل

يمكن أن تكون أنماط الوصول إلى الذاكرة مهمة جدًا أيضًا. تميل وحدات معالجة الرسومات إلى تحقيق أداء أفضل بكثير عندما تصل سلاسل عمليات الحساب إلى الذاكرة في نمط مثالي للأجهزة. ملاحظة مهمة: من المتوقّع أن تختلف خصائص الأداء على أجهزة وحدة معالجة الرسومات المختلفة. قد تحتاج إلى تنفيذ عمليات تحسين مختلفة حسب وحدة معالجة الرسومات.

في الرسم البياني التالي، استخدمنا خوارزمية ضرب المصفوفات نفسها، ولكن أضفنا سمة أخرى لإظهار تأثير استراتيجيات التحسين المختلفة والتعقيد والتباين على مستوى وحدات معالجة الرسومات المختلفة. لقد طرحنا أسلوبًا جديدًا هنا، سنُطلق عليه اسم "Swizzle". تعمل ميزة Swizzle على تحسين أنماط الوصول إلى الذاكرة لتكون أكثر ملاءمةً للأجهزة.

يمكنك ملاحظة أنّ ميزة تبديل الذاكرة لها تأثير كبير، وفي بعض الأحيان يكون تأثيرها أكبر من تأثير المجموعات الفرعية. في وحدة معالجة الرسومات 6، توفّر تقنية swizzle تسريعًا بمقدار 12 مرة، في حين توفّر المجموعات الفرعية تسريعًا بمقدار 13 مرة. وبذلك، تحقّق هذه التقنيات زيادةً في السرعة بمقدار 26 مرة. بالنسبة إلى وحدات معالجة الرسومات الأخرى، يحقّق أحيانًا الجمع بين أسلوب swizzle والمجموعات الفرعية أداءً أفضل من أيّ منهما وحده. وعلى وحدات معالجة الرسومات الأخرى، يُحقّق استخدام swizzle حصريًا أفضل أداء.

لقطة شاشة لزيادة السرعة في استراتيجيات ضرب المصفوفات
رسم بياني 4.

قد تتطلّب عملية ضبط خوارزميات وحدة معالجة الرسومات وتحسينها كي تعمل بشكل جيد على كلّ جهاز، الكثير من الخبرة. لحسن الحظ، هناك عدد كبير من الأعمال الموهوبة التي تُجرى في إطار عمل المكتبات ذات المستوى الأعلى، مثل Mediapipe وTransformers.js وApache TVM وONNX Runtime Web وغيرها.

إنّ المكتبات والأُطر هي الخيار الأمثل للتعامل مع تعقيد إدارة التصاميم المتنوعة لوحدة معالجة الرسومات، وإنشاء رمز خاص بالنظام الأساسي يعمل بشكل جيد على الجهاز العميل.

الخلاصات

يواصل فريق Chrome المساعدة في تطوير معايير WebAssembly وWebGPU لتحسين منصة الويب لأحمال عمل تعلُّم الآلة. نحن نستثمر في أساسيات الحوسبة الأسرع وإمكانية التشغيل التفاعلي بشكل أفضل على مستوى معايير الويب، ونحرص على أن تكون النماذج الكبيرة والصغيرة قادرة على العمل بكفاءة على جميع الأجهزة.

هدفنا هو تحسين إمكانات المنصة إلى أقصى حدّ مع الحفاظ على أفضل ما في الويب، أي مدى الوصول إلى الجمهور وسهولة الاستخدام وإمكانية النقل. ولن نفعل ذلك بمفردنا. نحن نعمل بالتعاون مع مورّدي المتصفّحات الآخرين في W3C، والعديد من شركاء التطوير.

ننصحك بتذكُّر ما يلي أثناء العمل مع WebAssembly وWebGPU:

  • تتوفّر ميزة الاستنتاج بالذكاء الاصطناعي الآن على الويب وعلى جميع الأجهزة. ويمنح ذلك ميزة التشغيل على أجهزة العملاء، مثل خفض تكلفة الخادم وانخفاض زمن الاستجابة وزيادة الخصوصية.
  • على الرغم من أنّ العديد من الميزات التي تمت مناقشتها ذات صلة بمؤلفي إطار العمل في المقام الأول، يمكن أن تستفيد تطبيقاتك منها بدون أيّ تكلفة إضافية.
  • إنّ معايير الويب سائلة ومتغيّرة، ونحن نبحث دائمًا عن الملاحظات. شارِك تجاربك حول WebAssembly وWebGPU.

الشكر والتقدير

نريد أن نشكر فريق الرسومات على الويب في Intel، الذي كان له دور أساسي في تطوير WebGPU f16 وميزات المنتج النقطي للعدد الصحيح المُجمَّع. نشكر الأعضاء الآخرين في مجموعتَي العمل WebAssembly وWebGPU في W3C، بما في ذلك مورّدي المتصفّحات الآخرين.

نشكر فِرق الذكاء الاصطناعي والتعلم الآلي في Google وفي منتدى البرامج المفتوحة على شراكتهم الرائعة. ونشكر أيضًا جميع زملائنا الذين ساهموا في تحقيق كل ذلك.