Nvidia ड्राइवर, LLaMA और ChatGPT

Home PDF

LLaMA (लार्ज लैंग्वेज मॉडल मेटा AI) एक बड़े भाषा मॉडल (LLMs) का परिवार है, जिसे मेटा AI द्वारा फरवरी 2023 से जारी किया गया है।

मैंने हाल ही में Nvidia GPU के साथ अपना कंप्यूटर बनाया है। आप यहां देख सकते हैं, कंप्यूटर कैसे बनाएं, https://lzwjava.github.io/computer

उसके बाद, मैंने LLaMA प्रोजेक्ट को चलाना शुरू किया। LLaMA प्रोजेक्ट का GitHub URL है 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: Distributed package doesn’t have NCCL built in”। आइए NCCL के बारे में जानें।

NVIDIA Collective Communication Library (NCCL) बहु-GPU और बहु-नोड संचार प्राइमिटिव्स को लागू करता है जो NVIDIA GPUs और नेटवर्किंग के लिए अनुकूलित हैं।
मैं NVIDIA ड्राइवरों को इंस्टॉल करने के लिए नीचे दिए गए वेबसाइटों का संदर्भ लेता हूँ।

जब हम अपने ग्राफिक कार्ड के लिए 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-उपयोग  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 सीखें

मॉडल डाउनलोड करने के बाद, और कमांड चलाने का प्रयास करते समय, हम नीचे दी गई त्रुटि का सामना करेंगे,

torch.cuda.OutOfMemoryError: CUDA मेमोरी खत्म हो गई। 86.00 MiB आवंटित करने का प्रयास किया गया (GPU 0; 11.69 GiB कुल क्षमता; 9.70 GiB पहले से आवंटित; 64.81 MiB मुक्त; PyTorch द्वारा कुल 9.70 GiB आरक्षित)। यदि आरक्षित मेमोरी » आवंटित मेमोरी है, तो फ़्रैगमेंटेशन से बचने के लिए max_split_size_mb सेट करने का प्रयास करें।

चूंकि हमारे ग्राफिक कार्ड की मेमोरी केवल 12 GB है, और llama-2-7b मॉडल का आकार लगभग 13GB है, इसलिए हम इसे अपने ग्राफिक कार्ड पर चलाने में सक्षम नहीं हैं।

हमने दूसरे प्रोजेक्ट, open-llama-3b, https://huggingface.co/openlm-research/open_llama_3b का उपयोग करने का प्रयास किया।

हमें नीचे दिया गया त्रुटि सामने आती है।

RuntimeError: अपेक्षा की गई थी कि सभी टेंसर एक ही डिवाइस पर होंगे, लेकिन कम से कम दो डिवाइस पाए गए, cuda:0 और cpu! (जब विधि wrapper_CUDA__index_select में तर्क index के लिए तर्क की जाँच की गई)

और हम ChatGPT से इसके बारे में पूछते हैं।

ChatGPT हमें एक बहुत ही सुंदर समाधान प्रदान करता है। हमें नीचे दिए गए कोड को जोड़ने की आवश्यकता है।

input_ids = input_ids.to(model.device)

अंत में, हम इसे चला सकते हैं।

(llama) lzw@lzw-MS-7E01:~/Projects/open_llama_3b$ python run.py 
Q: सबसे बड़ा जानवर कौन सा है?   
A: नीली व्हेल।   
Q: सबसे बड़ा जानवर कौन सा है?    
A: नीली व्हेल। यह पृथ्वी पर सबसे बड़ा जानवर है। यह भी  

हमने llama2.c प्रोजेक्ट, https://github.com/karpathy/llama2.c, को भी आज़माया।

(base) lzw@lzw-MS-7E01:~/Projects/llama2.c$ ./run stories15M.bin 
एक दिन, एक बड़ा शेर और एक छोटा खरगोश जंगल में थे। बड़े शेर ने कहा, "चलो नदी तक दौड़ लगाते हैं और कुछ पानी पीते हैं।" छोटे खरगोश ने सहमति दी, और वे दौड़ने लगे।
शेर तेज़ था, लेकिन वह छोटे खरगोश को चोट न पहुंचाने का ध्यान रख रहा था। खरगोश नदी के करीब पहुंच रहा था, लेकिन बड़े शेर ने उसे नहीं देखा। जब शेर नदी पार कर रहा था, तो वह फिसल गया और नदी में गिर गया।
छोटे खरगोश ने शेर को पानी में देखा और सोचा, "मुझे शेर को पीने से रोकना चाहिए।" उसने कहा, "शेर, नदी से पानी मत पीओ। यह सुरक्षित नहीं है।" लेकिन शेर ने नहीं सुना और नदी से पानी पी लिया।
छोटे खरगोश ने शेर की मदद करने की कोशिश की, लेकिन वह नहीं कर सका। शेर ने बहुत ज्यादा पानी पी लिया और उसका पेट दर्द करने लगा। बड़ा शेर बहुत दुखी हुआ और बोला, "मैंने तुमसे कहा था कि नदी से पानी मत पीओ।" छोटा खरगोश रोता हुआ घर चला गया, और बड़ा शेर अभी भी मदद नहीं कर सका।
achieved tok/s: 185.527329

ChatGPT द्वारा कोड को समझें

ज़िवेई: क्या आप नीचे दिए गए कोड्स को समझा सकते हैं?

   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)

यह कोड स्निपेट निम्नलिखित कार्य करता है:

  1. min_prompt_len में prompt_tokens की सबसे छोटी लंबाई (टोकन की संख्या) को स्टोर करता है।
  2. max_prompt_len में prompt_tokens की सबसे बड़ी लंबाई (टोकन की संख्या) को स्टोर करता है।
  3. यह सुनिश्चित करता है कि max_prompt_len, params.max_seq_len से कम या बराबर है। यदि ऐसा नहीं है, तो यह एक त्रुटि (assertion error) उत्पन्न करेगा।
  4. total_len में 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()):
            # max gen len तक काटें
            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]
            # eos tok तक काटें, यदि कोई हो
            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)

ChatGPT: ज़रूर, यहां दिए गए कोड की व्याख्या है:

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 नामक है और कई पैरामीटर्स को स्वीकार करता है:

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)

(यह कोड ब्लॉक को हिंदी में अनुवाद करने की आवश्यकता नहीं है क्योंकि यह प्रोग्रामिंग कोड है और इसे अपने मूल रूप में ही रहना चाहिए।)

ये लाइन्स प्रॉम्प्ट्स की न्यूनतम और अधिकतम लंबाई की गणना करती हैं और यह सुनिश्चित करती हैं कि प्रॉम्प्ट की अधिकतम लंबाई मॉडल पैरामीटर्स में निर्दिष्ट अधिकतम अनुक्रम लंबाई से अधिक न हो। कुल लंबाई की गणना अधिकतम उत्पन्न लंबाई और अधिकतम प्रॉम्प्ट लंबाई के आधार पर की जाती है।

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 टेंसर को पैडिंग टोकन के साथ प्रारंभ करता है और इसे प्रॉम्प्ट्स से टोकन अनुक्रमों से भरता है।

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 (पिछली स्थिति), 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

इस लूप के अंदर, मॉडल अगले टोकन के लिए एक प्रायिकता वितरण logits उत्पन्न करता है। यदि logprobs True है, तो यह क्रॉस-एन्ट्रॉपी का उपयोग करके लॉग प्रायिकताओं की गणना करता है। temperature के आधार पर, यह अगले टोकन का चयन करने के लिए softmax या argmax का उपयोग करता है। फिर यह tokens टेंसर को अपडेट करता है और अनुक्रम के अंत (end-of-sequence) के टोकन की जांच करता है।

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)

(यह कोड ब्लॉक है, इसे अनुवादित नहीं किया जाना चाहिए।)

अंत में, फ़ंक्शन उत्पन्न टोकन अनुक्रम और उनके संबंधित लॉग प्रायिकताओं (यदि अनुरोध किया गया हो) को वापस करता है।


Back 2025.01.18 Donate