عزز أداء النموذج الخاص بك باستخدام النواة المحسنة مسبقًا، والتي يمكن تحميلها بسهولة من المركز.
اليوم، سنستكشف تطورًا مثيرًا من Hugging Face: the محور النواة! كممارسين لتعلم الآلة، نعلم أن تعظيم الأداء غالبًا ما يتضمن التعمق في التعليمات البرمجية المُحسّنة، أو نواة CUDA المخصصة، أو أنظمة البناء المعقدة. يعمل Kernel Hub على تبسيط هذه العملية بشكل كبير!
فيما يلي مثال قصير لكيفية استخدام النواة في التعليمات البرمجية الخاصة بك.
import torch
from kernels import get_kernel
activation = get_kernel("kernels-community/activation")
x = torch.randn((10, 10), dtype=torch.float16, device="cuda")
y = torch.empty_like(x)
activation.gelu_fast(y, x)
print(y)
وفي الأقسام التالية سنتناول المواضيع التالية:
- ما هو مركز النواة؟ – فهم المفهوم الأساسي.
- كيفية استخدام مركز النواة – مثال رمز سريع.
- إضافة نواة إلى نموذج بسيط – التكامل العملي باستخدام RMSNorm.
- مراجعة تأثير الأداء – قياس الفرق RMSNorm.
- حالات الاستخدام في العالم الحقيقي – أمثلة لكيفية استخدام مكتبة النواة في مشاريع أخرى.
سنقدم هذه المفاهيم سريعًا – يمكن استيعاب الفكرة الأساسية في حوالي 5 دقائق (على الرغم من أن إجراء التجارب وقياس الأداء قد يستغرق وقتًا أطول قليلاً!).
1. ما هو مركز النواة؟
يسمح Kernel Hub (التحقق من ذلك!) لمكتبات وتطبيقات Python بذلك قم بتحميل حبات الحوسبة المحسنة مباشرة من Hugging Face Hub. فكر في الأمر مثل Model Hub، ولكن بالنسبة لمقتطفات التعليمات البرمجية (النوى) ذات المستوى المنخفض وعالية الأداء التي تعمل على تسريع عمليات معينة، غالبًا على وحدات معالجة الرسومات.
تتضمن الأمثلة آليات الاهتمام المتقدمة (مثل FlashAttention للتسريع الكبير وتوفير الذاكرة). نواة تكميم مخصصة (تمكن من إجراء عمليات حسابية فعالة باستخدام أنواع بيانات أقل دقة مثل INT8 أو INT4). النوى المتخصصة المطلوبة للبنيات المعقدة مثل طبقات خليط الخبراء (MoE)، والتي تتضمن أنماط توجيه وحساب معقدة. بالإضافة إلى وظائف التنشيط وطبقات التسوية (مثل LayerNorm أو RMSNorm).
بدلاً من إدارة التبعيات المعقدة يدويًا، أو مواجهة علامات الترجمة، أو إنشاء مكتبات مثل Triton أو CUTLASS من المصدر، يمكنك استخدام kernels مكتبة لجلب وتشغيل النوى المحسنة المترجمة مسبقًا.
على سبيل المثال، لتمكين تنبيه فلاش تحتاج إلى سطر واحد فقط — بدون تصميمات أو علامات:
from kernels import get_kernel
flash_attention = get_kernel("kernels-community/flash-attn")
kernels يكتشف إصدارات Python وPyTorch وCUDA الدقيقة لديك، ثم يقوم بتنزيل الملف الثنائي المطابق المترجم مسبقًا — عادةً في ثوانٍ (أو دقيقة أو دقيقتين على اتصال بطيء).
على النقيض من ذلك، يتطلب تجميع FlashAttention بنفسك ما يلي:
- استنساخ المستودع وتثبيت كل التبعيات.
- تكوين إشارات البناء ومتغيرات البيئة.
- الحجز ~ 96 جيجابايت من ذاكرة الوصول العشوائي والكثير من نوى وحدة المعالجة المركزية.
- منتظر من 10 دقائق إلى عدة ساعات، اعتمادًا على أجهزتك. (راجع دليل التثبيت الخاص بالمشروع للحصول على التفاصيل.)
يمحو Kernel Hub كل هذا الاحتكاك: استدعاء وظيفة واحدة، وتسريع فوري.
فوائد مركز النواة:
- الوصول الفوري إلى النوى المحسنة: قم بتحميل وتشغيل النوى المحسنة لمختلف الأجهزة بدءًا من وحدات معالجة الرسومات NVIDIA وAMD، دون متاعب التجميع المحلية.
- المشاركة وإعادة الاستخدام: اكتشاف النوى ومشاركتها وإعادة استخدامها عبر المشاريع المختلفة والمجتمع.
- تحديثات سهلة: ابق على اطلاع بأحدث تحسينات kernel ببساطة عن طريق سحب الإصدار الأحدث من Hub.
- تسريع التنمية: ركز على بنية النموذج ومنطقك، وليس على تعقيدات تجميع النواة ونشرها.
- تحسين الأداء: الاستفادة من النوى التي تم تحسينها من قبل الخبراء لتسريع التدريب والاستدلال.
- تبسيط النشر: قلل من تعقيد بيئة النشر الخاصة بك عن طريق جلب النوى عند الطلب.
- تطوير ومشاركة النواة الخاصة بك: إذا قمت بإنشاء حبات محسنة، فيمكنك مشاركتها بسهولة على المركز ليستخدمها الآخرون. وهذا يشجع التعاون وتبادل المعرفة داخل المجتمع.
كما يعلم العديد من مطوري التعلم الآلي، فإن إدارة التبعيات وإنشاء تعليمات برمجية منخفضة المستوى من المصدر يمكن أن تكون عملية تستغرق وقتًا طويلاً وعرضة للأخطاء. يهدف Kernel Hub إلى تبسيط ذلك من خلال توفير مستودع مركزي لنواة الحوسبة المحسنة التي يمكن تحميلها وتشغيلها بسهولة.
اقضِ وقتًا أطول في بناء نماذج رائعة ووقتًا أقل في محاربة أنظمة البناء!
2. كيفية استخدام Kernel Hub (مثال أساسي)
تم تصميم استخدام Kernel Hub ليكون واضحًا ومباشرًا. ال kernels توفر المكتبة الواجهة الرئيسية. فيما يلي مثال سريع يقوم بتحميل نواة وظيفة تنشيط GELU المحسنة. (لاحقًا، سنرى مثالًا آخر حول كيفية دمج النواة في نموذجنا).
ملف: activation_validation_example.py
import torch
import torch.nn.functional as F
from kernels import get_kernel
DEVICE = "cuda"
torch.manual_seed(42)
activation_kernels = get_kernel("kernels-community/activation")
x = torch.randn((4, 4), dtype=torch.float16, device=DEVICE)
y = torch.empty_like(x)
activation_kernels.gelu_fast(y, x)
expected = F.gelu(x)
torch.testing.assert_close(y, expected, rtol=1e-2, atol=1e-2)
print("✅ Kernel output matches PyTorch GELU!")
print("\nInput tensor:")
print(x)
print("\nFast GELU kernel output:")
print(y)
print("\nPyTorch GELU output:")
print(expected)
print("\nAvailable functions in 'kernels-community/activation':")
print(dir(activation_kernels))
(ملحوظة: إذا كان لديك uv المثبتة، يمكنك حفظ هذا البرنامج النصي باسم script.py وتشغيل uv run script.py للتعامل مع التبعيات تلقائيًا.)
ماذا يحدث هنا؟
- يستورد
get_kernel: هذه الوظيفة هي نقطة الدخول إلى Kernel Hub عبرkernelsمكتبة. get_kernel("kernels-community/activation"): هذا الخط يبحث عنactivationمستودع النواة تحتkernels-communityمنظمة. يقوم بتنزيل وتخزين وتحميل ثنائي kernel المناسب المترجم مسبقًا.- تحضير الموترات: نقوم بإنشاء الإدخال (
x) والإخراج (y) الموترات على GPU. activation_kernels.gelu_fast(y, x): نسمي الوظيفة المحسنة المحددة (gelu_fast) المقدمة من وحدة kernel المحملة.- تَحَقّق: نحن نتحقق من الإخراج.
يوضح هذا المثال البسيط مدى سهولة جلب وتنفيذ تعليمات برمجية محسّنة للغاية. الآن دعونا نلقي نظرة على تكامل عملي أكثر باستخدام تطبيع RMS.
3. أضف نواة إلى نموذج بسيط
دعونا ندمج الأمثل تطبيع RMS النواة في النموذج الأساسي. سوف نستخدم LlamaRMSNorm التنفيذ المنصوص عليه في kernels-community/triton-layer-norm المستودع (ملاحظة: يحتوي هذا الريبو على نواة تسوية مختلفة) ومقارنته بتطبيق PyTorch الأساسي لـ RMSNorm.
أولاً، حدد وحدة RMSNorm بسيطة في PyTorch ونموذجًا أساسيًا يستخدمها:
ملف: rmsnorm_baseline.py
import torch
import torch.nn as nn
DEVICE = "cuda"
DTYPE = torch.float16
class RMSNorm(nn.Module):
def __init__(self, hidden_size, variance_epsilon=1e-5):
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size))
self.eps = variance_epsilon
self.hidden_size = hidden_size
def forward(self, x):
input_dtype = x.dtype
variance = x.to(torch.float32).pow(2).mean(-1, keepdim=True)
x = x * torch.rsqrt(variance + self.eps)
return (self.weight * x).to(input_dtype)
class BaselineModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size, eps=1e-5):
super().__init__()
self.linear1 = nn.Linear(input_size, hidden_size)
self.norm = RMSNorm(hidden_size, variance_epsilon=eps)
self.activation = nn.GELU()
self.linear2 = nn.Linear(hidden_size, output_size)
with torch.no_grad():
self.linear1.weight.fill_(1)
self.linear1.bias.fill_(0)
self.linear2.weight.fill_(1)
self.linear2.bias.fill_(0)
self.norm.weight.fill_(1)
def forward(self, x):
x = self.linear1(x)
x = self.norm(x)
x = self.activation(x)
x = self.linear2(x)
return x
input_size = 128
hidden_size = 256
output_size = 10
eps_val = 1e-5
baseline_model = (
BaselineModel(input_size, hidden_size, output_size, eps=eps_val)
.to(DEVICE)
.to(DTYPE)
)
dummy_input = torch.randn(32, input_size, device=DEVICE, dtype=DTYPE)
output = baseline_model(dummy_input)
print("Baseline RMSNorm model output shape:", output.shape)
الآن، لنقم بإنشاء إصدار باستخدام LlamaRMSNorm تم تحميل النواة عبر kernels.
ملف: rmsnorm_kernel.py
import torch
import torch.nn as nn
from kernels import get_kernel, use_kernel_forward_from_hub
from rmsnorm_baseline import BaselineModel
DEVICE = "cuda"
DTYPE = torch.float16
layer_norm_kernel_module = get_kernel("kernels-community/triton-layer-norm")
@use_kernel_forward_from_hub("LlamaRMSNorm")
class OriginalRMSNorm(nn.Module):
def __init__(self, hidden_size, variance_epsilon=1e-5):
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size))
self.eps = variance_epsilon
self.hidden_size = hidden_size
def forward(self, x):
input_dtype = x.dtype
variance = x.to(torch.float32).pow(2).mean(-1, keepdim=True)
x = x * torch.rsqrt(variance + self.eps)
return (self.weight * x).to(input_dtype)
class KernelModel(nn.Module):
def __init__(
self,
input_size,
hidden_size,
output_size,
device="cuda",
dtype=torch.float16,
eps=1e-5,
):
super().__init__()
self.linear1 = nn.Linear(input_size, hidden_size)
self.norm = OriginalRMSNorm(hidden_size, variance_epsilon=eps)
self.activation = nn.GELU()
self.linear2 = nn.Linear(hidden_size, output_size)
with torch.no_grad():
self.linear1.weight.fill_(1)
self.linear1.bias.fill_(0)
self.linear2.weight.fill_(1)
self.linear2.bias.fill_(0)
self.norm.weight.fill_(1)
def forward(self, x):
x = self.linear1(x)
x = self.norm(x)
x = self.activation(x)
x = self.linear2(x)
return x
input_size = 128
hidden_size = 256
output_size = 10
eps_val = 1e-5
kernel_model = (
KernelModel(
input_size, hidden_size, output_size, device=DEVICE, dtype=DTYPE, eps=eps_val
)
.to(DEVICE)
.to(DTYPE)
)
baseline_model = (
BaselineModel(input_size, hidden_size, output_size, eps=eps_val)
.to(DEVICE)
.to(DTYPE)
)
dummy_input = torch.randn(32, input_size, device=DEVICE, dtype=DTYPE)
output = baseline_model(dummy_input)
output_kernel = kernel_model(dummy_input)
print("Kernel RMSNorm model output shape:", output_kernel.shape)
try:
torch.testing.assert_close(output, output_kernel, rtol=1e-2, atol=1e-2)
print("\nBaseline and Kernel RMSNorm model outputs match!")
except AssertionError as e:
print("\nBaseline and Kernel RMSNorm model outputs differ slightly:")
print(e)
except NameError:
print("\nSkipping output comparison as kernel model output was not generated.")
ملاحظات هامة على KernelModel:
- وراثة النواة: ال
KernelRMSNormالطبقة ترث منlayer_norm_kernel_module.layers.LlamaRMSNorm، وهو تطبيق RMSNorm في النواة. وهذا يسمح لنا باستخدام النواة المحسنة مباشرة. - الوصول إلى الوظيفة: الطريقة الدقيقة للوصول إلى وظيفة RMSNorm (
layer_norm_kernel_module.layers.LlamaRMSNorm.forward,layer_norm_kernel_module.rms_norm_forward، أو أي شيء آخر) يعتمد كليًا على كيفية قيام منشئ النواة بتنظيم المستودع على المحور. قد تحتاج إلى فحص التحميلlayer_norm_kernel_moduleكائن (على سبيل المثال، باستخدامdir()) أو تحقق من وثائق النواة على المركز للعثور على الوظيفة/الطريقة الصحيحة وتوقيعها. لقد استخدمتrms_norm_forwardكعنصر نائب معقول ومعالجة الأخطاء المضافة. - حدود: نحن الآن نحدد فقط
rms_norm_weight(بدون تحيز)، بما يتوافق مع RMSNorm.
4. قياس تأثير الأداء
ما مدى سرعة نواة Triton RMSNorm المحسنة مقارنة بإصدار PyTorch القياسي؟ دعونا نقيس التمريرة الأمامية لمعرفة ذلك.
ملف: rmsnorm_benchmark.py
import torch
from rmsnorm_baseline import BaselineModel
from rmsnorm_kernel import KernelModel
DEVICE = "cuda"
DTYPE = torch.float16
def benchmark_model(model, input_tensor, num_runs=100, warmup_runs=10):
model.eval()
dtype = input_tensor.dtype
model = model.to(input_tensor.device).to(dtype)
for _ in range(warmup_runs):
_ = model(input_tensor)
torch.cuda.synchronize()
start_event = torch.cuda.Event(enable_timing=True)
end_event = torch.cuda.Event(enable_timing=True)
start_event.record()
for _ in range(num_runs):
_ = model(input_tensor)
end_event.record()
torch.cuda.synchronize()
elapsed_time_ms = start_event.elapsed_time(end_event)
avg_time_ms = elapsed_time_ms / num_runs
return avg_time_ms
input_size_bench = 4096
hidden_size_bench = 4096
output_size_bench = 10
eps_val_bench = 1e-5
baseline_model_bench = (
BaselineModel(
input_size_bench, hidden_size_bench, output_size_bench, eps=eps_val_bench
)
.to(DEVICE)
.to(DTYPE)
)
kernel_model_bench = (
KernelModel(
input_size_bench,
hidden_size_bench,
output_size_bench,
device=DEVICE,
dtype=DTYPE,
eps=eps_val_bench,
)
.to(DEVICE)
.to(DTYPE)
)
warmup_input = torch.randn(4096, input_size_bench, device=DEVICE, dtype=DTYPE)
_ = kernel_model_bench(warmup_input)
_ = baseline_model_bench(warmup_input)
batch_sizes = [
256,
512,
1024,
2048,
4096,
8192,
16384,
32768,
]
print(
f"'Batch Size':<12 | 'Baseline Time (ms)':<18 | 'Kernel Time (ms)':<18 | 'Speedup'"
)
print("-" * 74)
for batch_size in batch_sizes:
torch.cuda.synchronize()
bench_input = torch.randn(batch_size, input_size_bench, device=DEVICE, dtype=DTYPE)
baseline_time = benchmark_model(baseline_model_bench, bench_input)
kernel_time = -1
kernel_time = benchmark_model(kernel_model_bench, bench_input)
baseline_time = round(baseline_time, 4)
kernel_time = round(kernel_time, 4)
speedup = round(baseline_time / kernel_time, 2) if kernel_time > 0 else "N/A"
if kernel_time < baseline_time:
speedup = f"speedup:.2fx"
elif kernel_time == baseline_time:
speedup = "1.00x (identical)"
else:
speedup = f"kernel_time / baseline_time:.2fx slower"
print(f"batch_size:<12 | baseline_time:<18 | kernel_time:<18 | speedup")
النتيجة المتوقعة:
كما هو الحال مع LayerNorm، يمكن لتطبيق RMSNorm المضبوط جيدًا باستخدام Triton توفير سرعات كبيرة مقارنة بالإصدار الافتراضي لـ PyTorch — خاصة لأحمال العمل المرتبطة بالذاكرة على الأجهزة المتوافقة (على سبيل المثال، وحدات معالجة الرسومات NVIDIA Ampere أو Hopper) ومع الأنواع منخفضة الدقة مثل float16 أو bfloat16.
ضع في اعتبارك:
- قد تختلف النتائج وفقًا لوحدة معالجة الرسومات لديك وحجم الإدخال ونوع البيانات.
- يمكن أن تؤدي المعايير الدقيقة إلى تحريف الأداء في العالم الحقيقي.
- يتوقف الأداء على جودة تنفيذ النواة.
- قد لا تستفيد النوى المحسّنة من أحجام الدُفعات الصغيرة بسبب النفقات العامة.
ستعتمد النتائج الفعلية على أجهزتك وتنفيذ النواة المحددة. فيما يلي مثال لما قد تراه (على وحدة معالجة الرسومات L4):
| حجم الدفعة | الوقت الأساسي (مللي ثانية) | وقت النواة (ملي ثانية) | تسريع |
|---|---|---|---|
| 256 | 0.2122 | 0.2911 | 0.72x |
| 512 | 0.4748 | 0.3312 | 1.43x |
| 1024 | 0.8946 | 0.6864 | 1.30x |
| 2048 | 2.0289 | 1.3889 | 1.46x |
| 4096 | 4.4318 | 2.2467 | 1.97x |
| 8192 | 9.2438 | 4.8497 | 1.91x |
| 16384 | 18.6992 | 9.8805 | 1.89x |
| 32768 | 37.079 | 19.9461 | 1.86x |
| 65536 | 73.588 | 39.593 | 1.86x |
5. حالات الاستخدام في العالم الحقيقي
ال kernels المكتبة لا تزال تنمو ولكن يتم استخدامها بالفعل في العديد من مشاريع العالم الحقيقي، بما في ذلك:
- استدلال إنشاء النص: يستخدم مشروع TGI
kernelsمكتبة لتحميل النوى المحسنة لمهام إنشاء النص، وتحسين الأداء والكفاءة. - المحولات: قامت مكتبة المحولات بدمج ملف
kernelsمكتبة لاستخدام الإسقاط في الطبقات المحسنة دون الحاجة إلى أي تغييرات على رمز النموذج. يتيح ذلك للمستخدمين التبديل بسهولة بين التطبيقات القياسية والمحسنة.
البدء والخطوات التالية!
لقد رأيت مدى سهولة جلب واستخدام النوى المحسنة مع Hugging Face Kernel Hub. هل أنت مستعد لتجربتها بنفسك؟
-
تثبيت المكتبة:
pip install kernels torch numpyتأكد من تثبيت إصدار PyTorch متوافق وبرنامج تشغيل GPU.
-
تصفح المحور: استكشف النوى المتوفرة على Hugging Face Hub الموجود أسفل
kernelsعلامة أو داخل المنظمات مثلkernels-community. ابحث عن النوى ذات الصلة بعملياتك (التنشيط، والانتباه، والتطبيع مثل LayerNorm/RMSNorm، وما إلى ذلك). -
تجربة: حاول استبدال المكونات في النماذج الخاصة بك. يستخدم
get_kernel("user-or-org/kernel-name"). بشكل حاسم، قم بفحص كائن kernel الذي تم تحميله (على سبيل المثال،print(dir(loaded_kernel))) أو تحقق من وثائق مستودع Hub الخاص به لفهم كيفية استدعاء وظائفه/طرقه بشكل صحيح وما هي المعلمات (الأوزان، والتحيزات، والمدخلات، وepsilon) التي يتوقعها. -
المعيار: قم بقياس تأثير الأداء على الأجهزة وعبء العمل المحدد لديك. لا تنس التحقق من الصحة العددية (
torch.testing.assert_close). -
(متقدم) المساهمة: إذا قمت بتطوير حبات محسنة، ففكر في مشاركتها على المركز!
خاتمة
يوفر Hugging Face Kernel Hub طريقة قوية وبسيطة للوصول إلى نواة الحوسبة المحسنة والاستفادة منها. من خلال استبدال مكونات PyTorch القياسية بإصدارات محسنة لعمليات مثل تطبيع RMS، يمكنك فتح تحسينات كبيرة في الأداء دون التعقيدات التقليدية للبنيات المخصصة. تذكر التحقق من تفاصيل كل نواة على Hub للاستخدام الصحيح. جربه وشاهد كيف يمكنه تسريع سير عملك!
