Nvidia Driver، LLaMA و ChatGPT
LLaMA (نموذج اللغة الكبيرة Meta AI) هي عائلة من نماذج اللغة الكبيرة (LLMs)، تم إصدارها بواسطة Meta AI بدءًا من فبراير 2023.
لقد قمت مؤخرًا ببناء جهاز الكمبيوتر الخاص بي مع وحدة معالجة الرسومات من Nvidia. يمكنك التحقق من ذلك هنا، كيفية بناء جهاز كمبيوتر، https://lzwjava.github.io/computer.
بعد ذلك، بدأت في تشغيل مشروع LLaMA. عنوان GitHub لمشروع LLaMA هو https://github.com/facebookresearch/llama.
تثبيت برنامج تشغيل Nvidia
عندما تقوم بتنفيذ الأمر،
torchrun --nproc_per_node 1 example_text_completion.py \
--ckpt_dir llama-2-7b/ \
--tokenizer_path tokenizer.model \
--max_seq_len 128 --max_batch_size 4
يظهر الخطأ التالي: “RuntimeError: الحزمة الموزعة لا تحتوي على NCCL مدمجًا”. دعونا نتعرف على NCCL.
مكتبة الاتصال الجماعي من NVIDIA (NCCL) تنفذ بدائيات الاتصال متعددة وحدات معالجة الرسوميات (GPUs) ومتعددة العقد (multi-node) المُحسنة لوحدات معالجة الرسوميات من NVIDIA والشبكات. أنا أشير إلى المواقع التالية لتثبيت برامج تشغيل NVIDIA.
- تنزيلات CUDA Toolkit 12.2 التحديث 1، https://developer.nvidia.com/cuda-downloads
- NVIDIA NCCL، https://developer.nvidia.com/nccl
- وثائق NVIDIA Deep Learning NCCL، https://docs.nvidia.com/deeplearning/nccl/install-guide/index.html
- دليل تثبيت NVIDIA CUDA لنظام Linux، https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html
- بعد تثبيت Ubuntu، تواجه إدارة MOK، https://www.cnblogs.com/yutian-blogs/p/13019226.html
- Ubuntu 22.04 للتعلم العميق، https://gist.github.com/amir-saniyan/b3d8e06145a8569c0d0e030af6d60bea
- ملاحظات Ubuntu 22.04، https://github.com/kmcminn/thinkpad/tree/main/extreme3g
عندما نقوم بتثبيت برنامج تشغيل NVIDIA بنجاح لبطاقة الرسومات الخاصة بنا، ثم نستخدم الأمر nvidia-smi
لعرض تفاصيلها، يمكن أن تظهر المعلومات التالية.
(base) lzw@lzw-MS-7E01:~$ nvidia-smi
الخميس 17 أغسطس 2023 04:15:43
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.86.10 إصدار السائق: 535.86.10 إصدار CUDA: 12.2 |
|-----------------------------------------+----------------------+----------------------+
| GPU الاسم Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| المروحة الحرارة الأداء الطاقة: الاستخدام/السعة | استخدام الذاكرة | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 NVIDIA GeForce RTX 4070 تشغيل | 00000000:01:00.0 تشغيل | N/A |
| 0% 34C P8 9W / 215W | 666MiB / 12282MiB | 15% افتراضي |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+
+---------------------------------------------------------------------------------------+
| العمليات: |
| GPU GI CI PID النوع اسم العملية استخدام ذاكرة GPU |
| ID ID الاستخدام |
|=======================================================================================|
| 0 N/A N/A 1926 G /usr/lib/xorg/Xorg 381MiB |
| 0 N/A N/A 2065 G /usr/bin/gnome-shell 120MiB |
| 0 N/A N/A 3482 G gnome-control-center 2MiB |
| 0 N/A N/A 3803 G ...irefox/2987/usr/lib/firefox/firefox 149MiB |
+---------------------------------------------------------------------------------------+
في الواقع، من الصعب الوصول إلى هذه المرحلة. يرجى الرجوع إلى الرابط هنا بعناية، ملاحظات Ubuntu 22.04، https://github.com/kmcminn/thinkpad/tree/main/extreme3g.
تعلم LLaMA
LLaMA (Large Language Model Meta AI) هي مجموعة من نماذج اللغة الكبيرة التي طورتها Meta (المعروفة سابقًا باسم Facebook). هذه النماذج مصممة لتكون فعالة من حيث الحجم وقادرة على أداء مهام معالجة اللغة الطبيعية (NLP) بشكل جيد. إذا كنت ترغب في تعلم المزيد عن LLaMA وكيفية استخدامها، إليك بعض الخطوات التي يمكنك اتباعها:
1. فهم أساسيات LLaMA
- ابدأ بقراءة الأوراق البحثية والمستندات الرسمية التي تصف LLaMA. سيساعدك هذا على فهم كيفية عمل النموذج وما هي المهام التي يمكنه القيام بها.
- تعرف على الفرق بين LLaMA والنماذج اللغوية الأخرى مثل GPT وBERT.
2. إعداد البيئة
- تأكد من أن لديك بيئة برمجية مناسبة لتشغيل النماذج اللغوية. يمكنك استخدام Python مع مكتبات مثل PyTorch أو TensorFlow.
- قم بتثبيت المكتبات اللازمة مثل
transformers
من Hugging Face والتي تدعم تشغيل نماذج LLaMA.
3. تنزيل النموذج
- يمكنك تنزيل نماذج LLaMA من مصادر موثوقة مثل Hugging Face Model Hub.
- تأكد من أن لديك مساحة تخزين كافية وحساب GPU إذا كنت تخطط لتشغيل النموذج على جهازك.
4. التدريب والضبط
- إذا كنت ترغب في تحسين أداء النموذج لمهمة معينة، يمكنك ضبط النموذج (fine-tuning) باستخدام مجموعة بيانات خاصة بك.
- استخدم أدوات مثل
Trainer
من Hugging Face لتسهيل عملية التدريب.
5. التقييم والاختبار
- بعد تدريب النموذج، قم بتقييم أدائه باستخدام مقاييس مثل الدقة (accuracy) أو F1-score.
- قم باختبار النموذج على بيانات جديدة لضمان أنه يعمل بشكل جيد في العالم الحقيقي.
6. التطبيقات العملية
- استخدم LLaMA في تطبيقات عملية مثل توليد النصوص، الترجمة الآلية، الإجابة على الأسئلة، وغيرها.
- يمكنك أيضًا دمج LLaMA في تطبيقاتك الخاصة باستخدام واجهات برمجية (APIs).
7. الموارد الإضافية
- انضم إلى مجتمعات المطورين والمهتمين بالذكاء الاصطناعي على منصات مثل GitHub وReddit وDiscord.
- تابع الدورات التعليمية والبرامج التعليمية المتاحة على الإنترنت لتعميق فهمك.
باتباع هذه الخطوات، ستتمكن من تعلم LLaMA واستخدامها بشكل فعال في مشاريعك الخاصة.
بعد تنزيل النماذج، ومحاولة تشغيل الأمر، سنواجه الخطأ التالي،
torch.cuda.OutOfMemoryError: نفاد ذاكرة CUDA. تمت محاولة تخصيص 86.00 ميجابايت (GPU 0؛ سعة إجمالية 11.69 جيجابايت؛ تم تخصيص 9.70 جيجابايت بالفعل؛ 64.81 ميجابايت متاحة؛ 9.70 جيجابايت محجوزة إجمالًا بواسطة PyTorch). إذا كانت الذاكرة المحجوزة أكبر بكثير من الذاكرة المخصصة، حاول ضبط
max_split_size_mb
لتجنب التجزئة.
نظرًا لأن ذاكرة بطاقة الرسوميات لدينا هي 12 جيجابايت فقط، وحجم نموذج llama-2-7b يبلغ حوالي 13 جيجابايت، فإننا لا نستطيع تشغيله باستخدام بطاقة الرسوميات الخاصة بنا.
نحاول استخدام المشروع الآخر، open-llama-3b، https://huggingface.co/openlm-research/open_llama_3b.
نواجه الخطأ التالي:
RuntimeError: توقع أن تكون جميع الموترات على نفس الجهاز، ولكن تم العثور على جهازين على الأقل، cuda:0 و cpu! (عند التحقق من الوسيط index في الدالة wrapper_CUDA__index_select)
ونحن نسأل ChatGPT عن هذا.
يوفر لنا ChatGPT إصلاحًا جميلًا جدًا. نحتاج إلى إضافة الكود التالي.
input_ids = input_ids.to(model.device)
تم تحويل input_ids
إلى الجهاز الذي يعمل عليه النموذج (model.device
).
أخيرًا، يمكننا تشغيله.
(llama) lzw@lzw-MS-7E01:~/Projects/open_llama_3b$ python run.py
س: ما هو أكبر حيوان؟
ج: الحوت الأزرق.
س: ما هو أكبر حيوان؟
ج: الحوت الأزرق. إنه أكبر حيوان على وجه الأرض. وهو أيضًا
لقد جربنا أيضًا المشروع llama2.c، https://github.com/karpathy/llama2.c.
(base) lzw@lzw-MS-7E01:~/Projects/llama2.c$ ./run stories15M.bin
في يوم من الأيام، كان هناك أسد كبير وأرنب صغير في الغابة. قال الأسد الكبير: "لنتسابق إلى النهر ونشرب بعض الماء." وافق الأرنب الصغير، وبدأوا في السباق.
كان الأسد سريعًا، ولكنه كان حريصًا على ألا يؤذي الأرنب الصغير. كان الأرنب يقترب من النهر، لكن الأسد الكبير لم يره. عندما عبر الأسد النهر، انزلق وسقط في النهر.
رأى الأرنب الصغير الأسد في الماء وفكر: "يجب أن أخبر الأسد ألا يشرب." قال: "يا أسد، لا تشرب من النهر. إنه ليس آمنًا." لكن الأسد لم يستمع وشرب من النهر.
حاول الأرنب الصغير مساعدة الأسد، لكنه لم يستطع. شرب الأسد كثيرًا وبدأت معدته تؤلمه. كان الأسد الكبير حزينًا جدًا وقال: "لقد أخبرتك ألا تشرب من النهر." ذهب الأرنب الصغير إلى المنزل وهو يبكي، وما زال الأسد الكبير غير قادر على المساعدة.
تحقيق tok/s: 185.527329
فهم الكود بواسطة ChatGPT
Zhiwei: هل يمكنك شرح الأكواد التالية؟
def generate(
self,
prompt_tokens: List[List[int]],
max_gen_len: int,
temperature: float = 0.6,
top_p: float = 0.9,
logprobs: bool = False,
echo: bool = False,
) -> Tuple[List[List[int]], Optional[List[List[float]]]]:
params = self.model.params
bsz = len(prompt_tokens)
assert bsz <= params.max_batch_size, (bsz, params.max_batch_size)
min_prompt_len = min(len(t) for t in prompt_tokens)
max_prompt_len = max(len(t) for t in prompt_tokens)
assert max_prompt_len <= params.max_seq_len
total_len = min(params.max_seq_len, max_gen_len + max_prompt_len)
تم ترجمة الكود أعلاه إلى:
min_prompt_len = min(len(t) for t in prompt_tokens) # الحد الأدنى لطول النص المطلوب
max_prompt_len = max(len(t) for t in prompt_tokens) # الحد الأقصى لطول النص المطلوب
assert max_prompt_len <= params.max_seq_len # التأكد من أن الحد الأقصى لا يتجاوز الطول المسموح به
total_len = min(params.max_seq_len, max_gen_len + max_prompt_len) # الطول الكلي المقبول
pad_id = self.tokenizer.pad_id
tokens = torch.full((bsz, total_len), pad_id, dtype=torch.long, device="cuda")
for k, t in enumerate(prompt_tokens):
tokens[k, : len(t)] = torch.tensor(t, dtype=torch.long, device="cuda")
if logprobs:
token_logprobs = torch.zeros_like(tokens, dtype=torch.float)
prev_pos = 0
eos_reached = torch.tensor([False] * bsz, device="cuda")
input_text_mask = tokens != pad_id
for cur_pos in range(min_prompt_len, total_len):
logits = self.model.forward(tokens[:, prev_pos:cur_pos], prev_pos)
if logprobs:
token_logprobs[:, prev_pos + 1 : cur_pos + 1] = -F.cross_entropy(
input=logits.transpose(1, 2),
target=tokens[:, prev_pos + 1 : cur_pos + 1],
reduction="none",
ignore_index=pad_id,
)
if temperature > 0:
probs = torch.softmax(logits[:, -1] / temperature, dim=-1)
next_token = sample_top_p(probs, top_p)
else:
next_token = torch.argmax(logits[:, -1], dim=-1)
next_token = next_token.reshape(-1)
# استبدال الرمز فقط إذا تم إنشاء النص المطلوب بالفعل
next_token = torch.where(
input_text_mask[:, cur_pos], tokens[:, cur_pos], next_token
)
tokens[:, cur_pos] = next_token
eos_reached |= (~input_text_mask[:, cur_pos]) & (
next_token == self.tokenizer.eos_id
)
prev_pos = cur_pos
if all(eos_reached):
break
if logprobs:
token_logprobs = token_logprobs.tolist()
out_tokens, out_logprobs = [], []
for i, toks in enumerate(tokens.tolist()):
# قص إلى الحد الأقصى لطول التوليد
start = 0 if echo else len(prompt_tokens[i])
toks = toks[start : len(prompt_tokens[i]) + max_gen_len]
probs = None
if logprobs:
probs = token_logprobs[i][start : len(prompt_tokens[i]) + max_gen_len]
# قص إلى رمز نهاية الجملة إذا وجد
if self.tokenizer.eos_id in toks:
eos_idx = toks.index(self.tokenizer.eos_id)
toks = toks[:eos_idx]
probs = probs[:eos_idx] if logprobs else None
out_tokens.append(toks)
out_logprobs.append(probs)
return (out_tokens, out_logprobs if logprobs else None)
بالطبع، إليك شرحًا للكود المقدم:
def generate(
self,
prompt_tokens: List[List[int]],
max_gen_len: int,
temperature: float = 0.6,
top_p: float = 0.9,
logprobs: bool = False,
echo: bool = False,
) -> Tuple[List[List[int]], Optional[List[List[float]]]]:
هذه الوظيفة تُسمى generate
وتقبل عدة معاملات:
prompt_tokens
هي قائمة تحتوي على تسلسلات من الرموز (tokens) للطلبات (prompts).max_gen_len
هو الطول الأقصى للنص الذي يتم إنشاؤه.temperature
هي معلمة تُستخدم للتحكم في العشوائية أثناء عملية الإنشاء.top_p
هو عتبة احتمالية بين 0 و1 تُستخدم للتحكم في التنوع أثناء عملية أخذ العينات.logprobs
هي قيمة منطقية (boolean) تشير إلى ما إذا كان يجب تسجيل اللوغاريتمات الاحتمالية (log probabilities) لعملية الإنشاء.echo
هي قيمة منطقية (boolean) تشير إلى ما إذا كان يجب تكرار المدخلات أثناء عملية الإنشاء.
params = self.model.params
bsz = len(prompt_tokens)
assert bsz <= params.max_batch_size, (bsz, params.max_batch_size)
تم ترجمة الكود أعلاه إلى:
params = self.model.params
bsz = len(prompt_tokens)
assert bsz <= params.max_batch_size, (bsz, params.max_batch_size)
ملاحظة: الكود لم يتم ترجمته لأنه يحتوي على أسماء متغيرات ودوال بالإنجليزية، والتي عادةً ما تبقى كما هي في البرمجة.
هنا، تقوم الدالة بالحصول على معلمات النموذج وحساب حجم الدُفعة (batch size) للطلبات. تضمن الدالة أن حجم الدُفعة لا يتجاوز الحد الأقصى لحجم الدُفعة المحدد في معلمات النموذج.
min_prompt_len = min(len(t) for t in prompt_tokens) # الحد الأدنى لطول النص المطلوب
max_prompt_len = max(len(t) for t in prompt_tokens) # الحد الأقصى لطول النص المطلوب
assert max_prompt_len <= params.max_seq_len # التأكد من أن الحد الأقصى لا يتجاوز الطول المسموح به
total_len = min(params.max_seq_len, max_gen_len + max_prompt_len) # الطول الكلي المقبول
هذه الأسطر تحسب الحد الأدنى والحد الأقصى لأطوال النصوص المقدمة (prompts) وتضمن ألا يتجاوز الحد الأقصى لطول النص المقدم الحد الأقصى لطول التسلسل المحدد في معلمات النموذج. يتم حساب الطول الإجمالي بناءً على الطول الأقصى الذي يمكن إنشاؤه والطول الأقصى للنص المقدم.
pad_id = self.tokenizer.pad_id
tokens = torch.full((bsz, total_len), pad_id, dtype=torch.long, device="cuda")
for k, t in enumerate(prompt_tokens):
tokens[k, : len(t)] = torch.tensor(t, dtype=torch.long, device="cuda")
يقوم هذا القسم بتهيئة موتر tokens
برموز الحشو (padding tokens) ويملؤه بتسلسلات الرموز (token sequences) المستخرجة من النصوص المقدمة (prompts).
if logprobs:
token_logprobs = torch.zeros_like(tokens, dtype=torch.float)
إذا كانت logprobs
تساوي True
، يتم إنشاء موتر token_logprobs
لتخزين اللوغاريتمات الاحتمالية.
prev_pos = 0
eos_reached = torch.tensor([False] * bsz, device="cuda")
input_text_mask = tokens != pad_id
تم ترجمة الكود أعلاه إلى:
prev_pos = 0
eos_reached = torch.tensor([False] * bsz, device="cuda")
input_text_mask = tokens != pad_id
ملاحظة: الكود لم يتم ترجمته لأنه يحتوي على أسماء متغيرات ودوال بلغة Python، والتي لا يتم ترجمتها عادةً.
يتم تهيئة المتغيرات prev_pos
(الموضع السابق)، eos_reached
(ما إذا تم الوصول إلى رموز نهاية التسلسل)، و input_text_mask
(المواضع التي لم يتم ملؤها بالحشو).
الحلقة التالية تولد الرموز بدءًا من الحد الأدنى لطول المطالبة وصولاً إلى الطول الكلي.
for cur_pos in range(min_prompt_len, total_len):
logits = self.model.forward(tokens[:, prev_pos:cur_pos], prev_pos)
if logprobs:
token_logprobs[:, prev_pos + 1 : cur_pos + 1] = -F.cross_entropy(
input=logits.transpose(1, 2),
target=tokens[:, prev_pos + 1 : cur_pos + 1],
reduction="none",
ignore_index=pad_id,
)
if temperature > 0:
probs = torch.softmax(logits[:, -1] / temperature, dim=-1)
next_token = sample_top_p(probs, top_p)
else:
next_token = torch.argmax(logits[:, -1], dim=-1)
next_token = next_token.reshape(-1)
next_token = torch.where(
input_text_mask[:, cur_pos], tokens[:, cur_pos], next_token
)
tokens[:, cur_pos] = next_token
eos_reached |= (~input_text_mask[:, cur_pos]) & (
next_token == self.tokenizer.eos_id
)
prev_pos = cur_pos
if all(eos_reached):
break
تمت ترجمة الكود أعلاه إلى:
next_token = next_token.reshape(-1)
next_token = torch.where(
input_text_mask[:, cur_pos], tokens[:, cur_pos], next_token
)
tokens[:, cur_pos] = next_token
eos_reached |= (~input_text_mask[:, cur_pos]) & (
next_token == self.tokenizer.eos_id
)
prev_pos = cur_pos
if all(eos_reached):
break
ملاحظة: الكود لم يتم ترجمته لأنه يحتوي على أسماء ودوال محددة في لغة البرمجة Python والتي لا يتم ترجمتها عادةً.
داخل هذه الحلقة، يقوم النموذج بإنشاء توزيع احتمالي logits
للرمز التالي. إذا كان logprobs
صحيحًا، فإنه يحسب اللوغاريتمات الاحتمالية باستخدام الانتروبيا المتقاطعة. اعتمادًا على temperature
، يستخدم softmax أو argmax لتحديد الرمز التالي. ثم يقوم بتحديث موتر tokens
ويفحص رموز نهاية التسلسل.
if logprobs:
token_logprobs = token_logprobs.tolist()
إذا كانت logprobs
تساوي True
، يتم تحويل موتر احتمالات اللوغاريتمات إلى قائمة Python.
out_tokens, out_logprobs = [], []
for i, toks in enumerate(tokens.tolist()):
...
في هذه الحلقة، لكل عينة إدخال، يتم توليد تسلسل الرموز الناتج والاحتمالات اللوغاريتمية المرتبطة بها.
return (out_tokens, out_logprobs if logprobs else None)
أخيرًا، تُعيد الدالة تسلسلات الرموز المُنشأة والاحتمالات اللوغاريتمية المقابلة لها (إذا تم طلب ذلك).