LLaMA 3 adalah salah satu model sumber terbuka paling menjanjikan setelah Mistral, yang menyelesaikan berbagai tugas. Saya sebelumnya menulis blog di Medium tentang membuat LLM dengan lebih dari 2,3 juta parameter dari awal menggunakan arsitektur LLaMA. Sekarang LLaMA-3 telah dirilis, kami akan membuatnya kembali dengan cara yang lebih sederhana.
Kami tidak akan menggunakan GPU untuk blog ini, tetapi Anda memerlukan setidaknya 17 GB RAM karena kami akan memuat beberapa file yang berukuran lebih dari 15 GB. Jika ini merupakan masalah Anda, Anda dapat menggunakan Kaggle sebagai solusinya. Karena kami tidak memerlukan GPU, Kaggle menawarkan RAM 30 GB dan hanya menggunakan inti CPU sebagai akselerator.
Berikut ini tautan blog yang memandu Anda tentang cara membuat 2,3+ juta parameter LLM dari awal: 2,3+ Juta Parameter LLM Dari Awal
Bagian baiknya adalah kita tidak akan menggunakan pengkodean pemrograman berorientasi objek (OOP), hanya pemrograman Python biasa. Namun, Anda harus memiliki pemahaman dasar tentang jaringan saraf dan arsitektur Transformer. Ini adalah dua prasyarat yang diperlukan untuk mengikuti blog.
Topik | Link |
---|---|
Teori Transformator | Tautan Video |
Teori Jaringan Syaraf Tiruan | Tautan Video |
Dasar-dasar piton | Tautan Video |
Sebelum melihat detail teknisnya, hal pertama yang harus Anda ketahui adalah bahwa keseluruhan arsitektur LLaMA 3 sama dengan LLaMA 2. Jadi, jika Anda belum mempelajari detail teknis LLaMA 3, itu tidak akan terjadi. masalah bagi Anda untuk mengikuti blog ini. Bahkan jika Anda tidak memiliki pemahaman tentang arsitektur LLaMA 2, jangan khawatir, kami juga akan melihat ikhtisar tingkat tinggi mengenai detail teknisnya. Blog ini dirancang untuk Anda.
Berikut beberapa poin penting tentang LLaMA 2 dan LLaMA 3. Jika Anda sudah familiar dengan arsitekturnya:
FITUR | Lama 3 | Lama 2 |
---|---|---|
Tokenizer | Tiktoken (dikembangkan oleh OpenAI) | Potongan Kalimat |
Jumlah Parameter | 8B, 70B | 70B, 13B, 7B |
Data Pelatihan | 15T token | 2.2T token |
Panjang Konteks | 8192 token | 4096 token |
Mekanisme Perhatian | Perhatian kueri yang dikelompokkan | Perhatian kueri yang dikelompokkan |
Model yang Diselesaikan dengan Baik | Ya | Ya |
Pertunjukan | Lebih baik dari Llama 2 di semua benchmark | Lebih baik dari Llama 1 pada sebagian besar benchmark |
Persyaratan Komputasi | Sangat tinggi (model 70B) | Sangat tinggi (model 70B) |
Tersedianya | Sumber terbuka | Sumber terbuka |
Pembelajaran penguatan dari umpan balik manusia | Ya | Ya |
Jumlah bahasa yang didukung | 30 bahasa | 20 bahasa |
Cocok untuk | Terbaik untuk tugas yang lebih menuntut, seperti penalaran, pengkodean, dan tes kecakapan | Cocok untuk tugas yang lebih menuntut, seperti penalaran, pengkodean, dan tes kecakapan |
Memahami arsitektur LLaMA 3 adalah penting sebelum mendalami pengkodeannya. Untuk pemahaman visual yang lebih baik, berikut diagram perbandingan antara vanilla Transformer, LLaMA 2/3, dan Mistral.
Mari kita lihat komponen terpenting LLaMA 3 dengan lebih detail:
Pada pendekatan LLaMA 3 yang sama dengan LLaMA 2, digunakan teknik yang disebut RMSNorm untuk menormalkan input setiap sub-lapisan transformator.
Bayangkan Anda sedang belajar untuk ujian besar, dan Anda memiliki buku teks yang sangat banyak dan penuh dengan bab. Setiap bab mewakili topik yang berbeda, namun beberapa bab lebih penting untuk memahami subjek dibandingkan bab lainnya. Sekarang, sebelum menyelami keseluruhan buku teks, Anda memutuskan untuk mengevaluasi pentingnya setiap bab. Anda tentu tidak ingin menghabiskan jumlah waktu yang sama pada setiap bab; Anda ingin lebih fokus pada hal-hal penting. Di sinilah Pra-normalisasi menggunakan RMSNorm berperan untuk model bahasa besar (LLM) seperti ChatGPT. Ini seperti memberi bobot pada setiap bab berdasarkan signifikansinya. Bab-bab yang pokok bahasannya mendapat bobot lebih tinggi, sedangkan bab-bab yang kurang penting mendapat bobot lebih rendah.
Jadi, sebelum mempelajari secara mendalam, Anda menyesuaikan rencana belajar Anda berdasarkan bobot kepentingan setiap bab. Anda mengalokasikan lebih banyak waktu dan tenaga pada bab-bab dengan bobot lebih tinggi, memastikan Anda memahami konsep inti secara menyeluruh.
Demikian pula, Pra-normalisasi menggunakan RMSNorm membantu LLM memprioritaskan bagian teks mana yang lebih penting untuk memahami konteks dan maknanya. Model ini memberikan bobot yang lebih tinggi pada elemen-elemen penting dan bobot yang lebih rendah pada elemen-elemen yang kurang penting, sehingga memastikan model memfokuskan perhatiannya pada tempat yang paling dibutuhkan untuk pemahaman yang akurat. Pembaca yang tertarik dapat menjelajahi implementasi RMSNorm secara mendetail di sini.
LLaMA memperkenalkan fungsi aktivasi SwiGLU, mengambil inspirasi dari PaLM.
Bayangkan Anda seorang guru yang mencoba menjelaskan topik yang kompleks kepada siswa Anda. Anda memiliki papan tulis besar tempat Anda menuliskan poin-poin penting dan menggambar diagram untuk memperjelasnya. Namun terkadang, tulisan tangan Anda mungkin tidak terlalu rapi, atau diagram Anda mungkin tidak tergambar dengan sempurna. Hal ini dapat mempersulit siswa Anda untuk memahami materi.
Sekarang, bayangkan jika Anda memiliki pena ajaib yang secara otomatis menyesuaikan ukuran dan gaya tulisan tangan Anda berdasarkan seberapa penting setiap poinnya. Jika ada sesuatu yang sangat penting, pena akan menulisnya lebih besar dan jelas, sehingga membuatnya menonjol. Kalau kurang penting, pena menulisnya lebih kecil, tapi tetap terbaca. SwiGLU seperti pena ajaib untuk model bahasa besar (LLM) seperti ChatGPT. Sebelum menghasilkan teks, SwiGLU menyesuaikan pentingnya setiap kata atau frasa berdasarkan relevansinya dengan konteks. Sama seperti pena ajaib yang menyesuaikan ukuran dan gaya tulisan Anda, SwiGLU menyesuaikan penekanan setiap kata atau frasa.
Jadi, ketika LLM menghasilkan teks, bagian-bagian penting dapat lebih ditonjolkan, membuatnya lebih terlihat dan memastikan bagian-bagian tersebut berkontribusi lebih besar terhadap pemahaman teks secara keseluruhan. Dengan cara ini, SwiGLU membantu LLM menghasilkan teks yang lebih jelas dan mudah dipahami, seperti bagaimana pena ajaib membantu Anda membuat penjelasan yang lebih jelas untuk siswa Anda di papan tulis. Rincian lebih lanjut tentang SwiGLU dapat ditemukan di makalah terkait.
Rotary Embeddings, atau RoPE, adalah jenis penyematan posisi yang digunakan di LLaMA 3.
Bayangkan Anda berada di ruang kelas, dan Anda ingin memberikan tempat duduk kepada siswa untuk diskusi kelompok. Biasanya, Anda dapat mengatur kursi dalam baris dan kolom, dengan setiap siswa memiliki posisi tetap. Namun, dalam beberapa kasus, Anda ingin membuat pengaturan tempat duduk yang lebih dinamis sehingga siswa dapat bergerak dan berinteraksi dengan lebih bebas.
TALI seperti pengaturan tempat duduk khusus yang memungkinkan siswa untuk memutar dan mengubah posisi dengan tetap mempertahankan posisi relatif satu sama lain. Daripada terpaku di satu tempat, siswa kini dapat bergerak dalam gerakan melingkar, memungkinkan interaksi yang lebih lancar.
Dalam skenario ini, setiap siswa mewakili sebuah kata atau token dalam urutan teks, dan posisinya sesuai dengan posisinya dalam urutan tersebut. Sama seperti ROPE yang memungkinkan siswa memutar dan mengubah posisi, ROPE memungkinkan penyematan posisi kata-kata dalam urutan teks berubah secara dinamis berdasarkan posisi relatifnya satu sama lain. Jadi, saat memproses teks, alih-alih memperlakukan penyematan posisi sebagai sesuatu yang tetap dan statis, ROPE memperkenalkan aspek rotasi, memungkinkan representasi yang lebih fleksibel yang menangkap hubungan dinamis antar kata dalam urutan. Fleksibilitas ini membantu model seperti ChatGPT lebih memahami dan menghasilkan teks yang mengalir secara alami dan menjaga koherensi, mirip dengan bagaimana pengaturan tempat duduk yang dinamis mendorong diskusi yang lebih interaktif di dalam kelas. Mereka yang tertarik dengan detail matematika dapat merujuk pada makalah RoPE.
LLaMA 3 menggunakan Byte Pair Encoding (BPE) dari perpustakaan tiktoken yang diperkenalkan oleh OpenAI, sedangkan BPE tokenizer LLaMA 2 didasarkan pada perpustakaan kalimat. Ada sedikit perbedaan di antara keduanya, tapi
pertama mari kita pelajari apa sebenarnya BPE itu.
Mari kita mulai dengan contoh sederhana. Misalkan kita memiliki korpus teks dengan kata-kata: "ab", "bc", "bcd", dan "cde". Kita mulai dengan menginisialisasi kosakata kita dengan semua karakter individual dalam korpus teks, sehingga kosakata awal kita adalah {"a", "b", "c", "d", "e"}.
Selanjutnya kita menghitung frekuensi setiap karakter dalam korpus teks. Sebagai contoh, frekuensinya adalah: {"a": 1, "b": 3, "c": 3, "d": 2, "e": 1}.
Sekarang, kita memulai proses penggabungan. Kami mengulangi langkah-langkah berikut hingga kosakata kami mencapai ukuran yang diinginkan:
Pertama, kita menemukan pasangan karakter berurutan yang paling sering. Dalam hal ini, pasangan yang paling sering adalah "bc" dengan frekuensi 2. Kami kemudian menggabungkan pasangan ini untuk membuat unit subkata baru "bc". Setelah penggabungan, kami memperbarui jumlah frekuensi untuk mencerminkan unit subkata baru. Frekuensi yang diperbarui adalah {"a": 1, "b": 2, "c": 2, "d": 2, "e": 1, "bc": 2}. Kita menambahkan unit subkata baru "bc" ke kosakata kita, yang sekarang menjadi {"a", "b", "c", "d", "e", "bc"}.
Kami mengulangi prosesnya. Pasangan paling sering berikutnya adalah "cd". Kami menggabungkan "cd" untuk membentuk unit subkata baru "cd" dan memperbarui jumlah frekuensi. Frekuensi yang diperbarui adalah {"a": 1, "b": 2, "c": 1, "d": 1, "e": 1, "bc": 2, "cd": 2}. Kita menambahkan "cd" pada kosakata, sehingga menghasilkan {"a", "b", "c", "d", "e", "bc", "cd"}.
Melanjutkan proses, pasangan berikutnya yang sering muncul adalah "de". Kami menggabungkan "de" untuk membentuk unit subkata "de" dan memperbarui jumlah frekuensi menjadi {"a": 1, "b": 2, "c": 1, "d": 1, "e": 0, "bc": 2, "cd": 1, "de": 1}. Kita menambahkan "de" pada kosakata, menjadikannya {"a", "b", "c", "d", "e", "bc", "cd", "de"}.
Selanjutnya, kita menemukan "ab" sebagai pasangan yang paling sering. Kami menggabungkan "ab" untuk membentuk unit subkata "ab" dan memperbarui jumlah frekuensi menjadi {"a": 0, "b": 1, "c": 1, "d": 1, "e": 0, "bc": 2, "cd": 1, "de": 1, "ab": 1}.
Kita menambahkan "ab" pada kosakata, yang menjadi {"a", "b", "c", "d", "e", "bc", "cd", "de", "ab"}.
Kemudian, pasangan berikutnya yang sering digunakan adalah "bcd". Kami menggabungkan "bcd" untuk membentuk unit subkata "bcd" dan memperbarui jumlah frekuensi menjadi {"a": 0, "b": 0, "c": 0, "d": 0, "e": 0, "bc": 1, "cd": 0, "de": 1, "ab": 1, "bcd": 1}. Kita menambahkan "bcd" pada kosakata, sehingga menghasilkan {"a", "b", "c", "d", "e", "bc", "cd", "de", "ab", "bcd "}.
Terakhir, pasangan yang paling sering digunakan adalah "cde". Kami menggabungkan "cde" untuk membentuk unit subkata "cde" dan memperbarui jumlah frekuensi menjadi {"a": 0, "b": 0, "c": 0, "d": 0, "e": 0, "bc": 1, "cd": 0, "de": 0, "ab": 1, "bcd": 1, "cde": 1}. Kita menambahkan "cde" pada kosakata, menjadikannya {"a", "b", "c", "d", "e", "bc", "cd", "de", "ab", "bcd ", "cd"}.
Teknik ini dapat meningkatkan kinerja LLM dan menangani kata-kata yang jarang dan di luar kosa kata. Perbedaan besar antara TikToken BPE dan BPE kalimat adalah TikToken BPE tidak selalu membagi kata menjadi bagian-bagian kecil jika keseluruhan kata sudah diketahui. Misalnya, jika "memeluk" ada dalam kosa kata, kata itu akan tetap menjadi satu tanda dan bukannya dipecah menjadi ["pelukan", "ging"].
Kami akan bekerja dengan sejumlah kecil perpustakaan Python, tetapi lebih baik menginstalnya untuk menghindari kesalahan "tidak ditemukan modul".
!p ip install sentencepiece tiktoken torch blobfile matplotlib huggingface_hub
Requirement already satisfied: sentencepiece in /opt/conda/lib/python3.10/site-packages (0.2.0)
Requirement already satisfied: tiktoken in /opt/conda/lib/python3.10/site-packages (0.7.0)
Requirement already satisfied: torch in /opt/conda/lib/python3.10/site-packages (2.1.2+cpu)
Requirement already satisfied: blobfile in /opt/conda/lib/python3.10/site-packages (2.1.1)
Requirement already satisfied: matplotlib in /opt/conda/lib/python3.10/site-packages (3.7.5)
Requirement already satisfied: huggingface_hub in /opt/conda/lib/python3.10/site-packages (0.22.2)
Requirement already satisfied: regex>=2022.1.18 in /opt/conda/lib/python3.10/site-packages (from tiktoken) (2023.12.25)
Requirement already satisfied: requests>=2.26.0 in /opt/conda/lib/python3.10/site-packages (from tiktoken) (2.31.0)
Requirement already satisfied: filelock in /opt/conda/lib/python3.10/site-packages (from torch) (3.13.1)
Requirement already satisfied: typing-extensions in /opt/conda/lib/python3.10/site-packages (from torch) (4.9.0)
Requirement already satisfied: sympy in /opt/conda/lib/python3.10/site-packages (from torch) (1.12)
Requirement already satisfied: networkx in /opt/conda/lib/python3.10/site-packages (from torch) (3.2.1)
Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from torch) (3.1.2)
Requirement already satisfied: fsspec in /opt/conda/lib/python3.10/site-packages (from torch) (2024.2.0)
Requirement already satisfied: pycryptodomex~=3.8 in /opt/conda/lib/python3.10/site-packages (from blobfile) (3.20.0)
Requirement already satisfied: urllib3<3,>=1.25.3 in /opt/conda/lib/python3.10/site-packages (from blobfile) (1.26.18)
Requirement already satisfied: lxml~=4.9 in /opt/conda/lib/python3.10/site-packages (from blobfile) (4.9.4)
Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (1.2.0)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (4.47.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (1.4.5)
Requirement already satisfied: numpy<2,>=1.20 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (1.26.4)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (21.3)
Requirement already satisfied: pillow>=6.2.0 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (9.5.0)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (3.1.1)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.10/site-packages (from matplotlib) (2.9.0.post0)
Requirement already satisfied: pyyaml>=5.1 in /opt/conda/lib/python3.10/site-packages (from huggingface_hub) (6.0.1)
Requirement already satisfied: tqdm>=4.42.1 in /opt/conda/lib/python3.10/site-packages (from huggingface_hub) (4.66.1)
Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/conda/lib/python3.10/site-packages (from requests>=2.26.0->tiktoken) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.10/site-packages (from requests>=2.26.0->tiktoken) (3.6)
Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.10/site-packages (from requests>=2.26.0->tiktoken) (2024.2.2)
Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->torch) (2.1.3)
Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.10/site-packages (from sympy->torch) (1.3.0)
Setelah menginstal perpustakaan yang diperlukan, kita perlu mengunduh beberapa file. Karena kami akan mereplikasi arsitektur llama-3–8B, Anda harus memiliki akun di HuggingFace. Selain itu, karena llama-3 adalah model yang terjaga keamanannya, Anda harus menerima syarat dan ketentuannya untuk mengakses konten model.
Berikut langkah-langkahnya:
Setelah Anda menyelesaikan kedua langkah ini, Sekarang kita harus mengunduh beberapa file. Ada dua opsi untuk melakukan itu:
(Opsi 1: Manual) Buka direktori llama-3–8B HF dari tautan ini dan unduh secara manual masing-masing dari ketiga file ini.
(opsi 2: Pengkodean) Kita dapat menggunakan pustaka hugging_face yang telah kita instal sebelumnya, untuk mengunduh semua file ini. Namun, pertama-tama, kita perlu masuk ke HuggingFace Hub di dalam buku kerja kita menggunakan Token HF kita. Anda dapat membuat token baru atau mengaksesnya dari tautan ini.
# Import the `notebook_login` function from the `huggingface_hub` module.
from huggingface_hub import notebook_login
# Execute the `notebook_login` function to log in to the Hugging Face Hub.
notebook_login ()
VBox(children=(HTML(value='<center> <imgnsrc=https://huggingface.co/front/assets/huggingface_logo-noborder.sv…
Setelah Anda menjalankan sel ini, ia akan meminta Anda memasukkan token. Jika ada kesalahan saat login, coba lagi tetapi pastikan untuk menghapus centang tambahkan token sebagai kredensial git. Setelah itu, kita hanya perlu menjalankan kode Python sederhana untuk mendownload tiga file yang menjadi tulang punggung arsitektur llama-3–8B.
# Import the necessary function from the huggingface_hub library
from huggingface_hub import hf_hub_download
# Define the repository information
repo_id = "meta-llama/Meta-Llama-3-8B"
subfolder = "original" # Specify the subfolder within the repository
# List of filenames to download
filenames = [ "params.json" , "tokenizer.model" , "consolidated.00.pth" ]
# Specify the directory where you want to save the downloaded files
save_directory = "llama-3-8B/" # Replace with your desired path
# Download each file
for filename in filenames :
hf_hub_download (
repo_id = repo_id , # Repository ID
filename = filename , # Name of the file to download
subfolder = subfolder , # Subfolder within the repository
local_dir = save_directory # Directory to save the downloaded file
)
original/params.json: 0%| | 0.00/211 [00:00<?, ?B/s]
original/tokenizer.model: 0%| | 0.00/2.18M [00:00<?, ?B/s]
original/consolidated.00.pth: 0%| | 0.00/16.1G [00:00<?, ?B/s]
Setelah semua file diunduh, kita perlu mengimpor perpustakaan yang akan kita gunakan di seluruh blog ini.
# File system paths
from pathlib import Path
# Tokenization library
import tiktoken
# BPE loading function
from tiktoken . load import load_tiktoken_bpe
# PyTorch library
import torch
# JSON handling
import json
# Plotting library
import matplotlib . pyplot as plt
Selanjutnya, kita perlu memahami untuk apa setiap file akan digunakan.
Karena kita bertujuan untuk mereplikasi llama-3 dengan tepat, itu berarti teks masukan kita harus menghasilkan keluaran yang bermakna. Misalnya, jika input kita adalah “warna matahari?”, maka outputnya harus “putih”. Untuk mencapai hal ini memerlukan pelatihan LLM kami pada kumpulan data besar, yang memerlukan daya komputasi tinggi, sehingga tidak dapat kami lakukan.
Namun, Meta telah merilis secara publik file arsitektur llama-3 mereka, atau dalam istilah yang lebih kompleks, bobot yang telah dilatih sebelumnya, untuk digunakan. Kami baru saja mengunduh file-file ini, memungkinkan kami mereplikasi arsitekturnya tanpa memerlukan pelatihan atau kumpulan data yang besar. Semuanya sudah disiapkan, tinggal menggunakan komponen yang tepat di tempat yang tepat.
Lihatlah masing-masing file ini dan kepentingannya:
tokenizer.model - Seperti yang telah kita bahas sebelumnya, LLaMA-3 menggunakan tokenizer Byte Pair Encoding (BPE) dari tiktoken, dilatih pada kumpulan data dengan 15 triliun token - 7 kali lebih besar dari kumpulan data yang digunakan untuk LLaMA-2. Mari muat file ini dan lihat apa yang ada di dalamnya.
# Loading the tokenizer from llama-3-8B
tokenizer_model = load_tiktoken_bpe ( "/kaggle/working/llama-3-8B/original/tokenizer.model" )
# Get the length of the tokenizer model
len ( tokenizer_model )
# OUTPUT: 128000
# Get the type of the `tokenizer_model` object.
type ( tokenizer_model )
# OUTPUT: dictionary
dict
Atribut length menunjukkan ukuran total kosakata, yang merupakan jumlah karakter unik dalam data pelatihan. Jenis tokenizer_model adalah kamus.
# Printing the first 10 items of tokenizer model
dict ( list ( tokenizer_model . items ())[ 5600 : 5610 ])
{b'mitted': 5600,
b" $('#": 5601,
b' saw': 5602,
b' approach': 5603,
b'ICE': 5604,
b' saying': 5605,
b' anyone': 5606,
b'meta': 5607,
b'SD': 5608,
b' song': 5609}
Saat kita mencetak 10 item acak darinya, Anda akan melihat string yang dibentuk menggunakan algoritma BPE, mirip dengan contoh yang kita bahas sebelumnya. Kunci mewakili urutan Byte dari pelatihan BPE, sedangkan nilai mewakili peringkat gabungan berdasarkan frekuensi.
konsolidasi.00.pth - berisi parameter yang dipelajari (bobot) Llama-3–8B. Parameter ini mencakup informasi tentang cara model memahami dan memproses bahasa, seperti cara model merepresentasikan token, menghitung perhatian, melakukan transformasi feed-forward, dan menormalkan outputnya.
# Loading a PyTorch model of LLaMA-3-8B
model = torch . load ( "/kaggle/working/llama-3-8B/original/consolidated.00.pth" )
# printing first 11 layers of the architecture
list ( model . keys ())[: 11 ]
['tok_embeddings.weight',
'layers.0.attention.wq.weight',
'layers.0.attention.wk.weight',
'layers.0.attention.wv.weight',
'layers.0.attention.wo.weight',
'layers.0.feed_forward.w1.weight',
'layers.0.feed_forward.w3.weight',
'layers.0.feed_forward.w2.weight',
'layers.0.attention_norm.weight',
'layers.0.ffn_norm.weight',
'layers.1.attention.wq.weight']
Jika Anda familiar dengan arsitektur transformator, Anda pasti sudah mengetahui tentang kueri, matriks kunci, dan banyak lagi. Nantinya, kita akan menggunakan lapisan/bobot ini untuk membuat matriks seperti itu dalam arsitektur Llama-3.
params.json- berisi berbagai nilai parameter, seperti:
# Opening the parameters JSON file
with open ( "/kaggle/working/llama-3-8B/original/params.json" , "r" ) as f :
config = json . load ( f )
# Printing the content
print ( config )
{'dim': 4096, 'n_layers': 32, 'n_heads': 32, 'n_kv_heads': 8, 'vocab_size': 128256, 'multiple_of': 1024, 'ffn_dim_multiplier': 1.3, 'norm_eps': 1e-05, 'rope_theta': 500000.0}
Nilai-nilai ini akan membantu kami mereplikasi arsitektur Llama-3 dengan menentukan detail seperti jumlah kepala, dimensi vektor penyematan, dan banyak lagi.
Mari kita simpan nilai-nilai ini agar kita dapat menggunakannya nanti.
# Dimension
dim = config [ "dim" ]
# Layers
n_layers = config [ "n_layers" ]
# Heads
n_heads = config [ "n_heads" ]
# KV_heads
n_kv_heads = config [ "n_kv_heads" ]
# Vocabulary
vocab_size = config [ "vocab_size" ]
# Multiple
multiple_of = config [ "multiple_of" ]
# Multiplier
ffn_dim_multiplier = config [ "ffn_dim_multiplier" ]
# Epsilon
norm_eps = config [ "norm_eps" ]
# RoPE
rope_theta = torch . tensor ( config [ "rope_theta" ])
Sekarang kita memiliki model tokenizer, model arsitektur yang berisi bobot, dan parameter konfigurasi, mari mulai membuat kode Llama-3 kita sendiri dari awal.
Hal pertama yang perlu kita lakukan adalah mengonversi teks masukan menjadi token, dan untuk mencapai hal ini pertama-tama kita harus membuat beberapa token khusus yang diperlukan untuk menyediakan penanda terstruktur dalam teks yang diberi token, memungkinkan tokenizer mengenali dan menangani kondisi tertentu. atau instruksi.
special_tokens = [
"<|begin_of_text|>" , # Marks the beginning of a text sequence.
"<|end_of_text|>" , # Marks the end of a text sequence.
"<|reserved_special_token_0|>" , # Reserved for future use.
"<|reserved_special_token_1|>" , # Reserved for future use.
"<|reserved_special_token_2|>" , # Reserved for future use.
"<|reserved_special_token_3|>" , # Reserved for future use.
"<|start_header_id|>" , # Indicates the start of a header ID.
"<|end_header_id|>" , # Indicates the end of a header ID.
"<|reserved_special_token_4|>" , # Reserved for future use.
"<|eot_id|>" , # Marks the end of a turn (in a conversational context).
] + [ f"<|reserved_special_token_ { i } |>" for i in range ( 5 , 256 - 5 )] # A large set of tokens reserved for future use.
Selanjutnya kita mendefinisikan aturan untuk memisahkan teks menjadi token dengan menentukan pola berbeda untuk mencocokkan berbagai jenis substring dalam teks masukan. Inilah cara kita melakukannya.
# patterns based on which text will be break into tokens
tokenize_breaker = r"(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^rnp{L}p{N}]?p{L}+|p{N}{1,3}| ?[^sp{L}p{N}]+[rn]*|s*[rn]+|s+(?!S)|s+"
Itu dapat mengekstrak kata, kontraksi, angka (hingga tiga digit), dan urutan karakter non-spasi dari teks input, Anda dapat menyesuaikannya berdasarkan kebutuhan Anda. Kita perlu mengkodekan fungsi tokenizer sederhana menggunakan TikToken BPE, yang mengambil tiga input: tokenizer_model, tokenize_breaker, dan special_tokens. Fungsi ini akan menyandikan/mendekode teks masukan kita sesuai dengan itu.
# Initialize tokenizer with specified parameters
tokenizer = tiktoken . Encoding (
# make sure to set path to tokenizer.model file
name = "/kaggle/working/llama-3-8B/original/tokenizer.model" ,
# Define tokenization pattern string
pat_str = tokenize_breaker ,
# Assign BPE mergeable ranks from tokenizer_model of LLaMA-3
mergeable_ranks = tokenizer_model ,
# Set special tokens with indices
special_tokens = { token : len ( tokenizer_model ) + i for i , token in enumerate ( special_tokens )},
)
# Encode "hello world!" and decode tokens to string
tokenizer . decode ( tokenizer . encode ( "hello world!" ))
'hello world!'
Untuk memverifikasi bahwa metode fungsi encoder kami berfungsi dengan benar, kami meneruskan "Hello World" ke dalamnya. Pertama, ia mengkodekan teks, mengubahnya menjadi nilai numerik. Kemudian, ia menerjemahkannya kembali ke teks, menghasilkan "halo dunia!". Ini mengonfirmasi bahwa fungsi tersebut berfungsi dengan benar. Mari kita beri token pada masukan kita.
# input prompt
prompt = "the answer to the ultimate question of life, the universe, and everything is "
# Encode the prompt using the tokenizer and prepend a special token (128000)
tokens = [ 128000 ] + tokenizer . encode ( prompt )
print ( tokens ) # Print the encoded tokens
# Convert the list of tokens into a PyTorch tensor
tokens = torch . tensor ( tokens )
# Decode each token back into its corresponding string
prompt_split_as_tokens = [ tokenizer . decode ([ token . item ()]) for token in tokens ]
print ( prompt_split_as_tokens ) # Print the decoded tokens
[128000, 1820, 4320, 311, 279, 17139, 3488, 315, 2324, 11, 279, 15861, 11, 323, 4395, 374, 220]
['<|begin_of_text|>', 'the', ' answer', ' to', ' the', ' ultimate', ' question', ' of', ' life', ',', ' the', ' universe', ',', ' and', ' everything', ' is', ' ']
Kami mengkodekan teks masukan kami "jawaban atas pertanyaan utama tentang kehidupan, alam semesta, dan segalanya adalah " yang dimulai dengan token khusus.
Jika kita memeriksa panjang vektor masukan kita, hasilnya adalah:
# checking dimension of input vector and embedding vector from llama-3 architecture
print ( dim , len ( tokens ))
4096 17
Vektor masukan kita, yang saat ini berdimensi (17x1), perlu diubah menjadi penyematan untuk setiap kata yang diberi token. Ini berarti token (17x1) kita akan menjadi (17x4096), di mana setiap token memiliki penyematan yang sesuai dengan panjang 4096.
# Define embedding layer with vocab size and embedding dimension
embedding_layer = torch . nn . Embedding ( vocab_size , dim )
# Copy pre-trained token embeddings to the embedding layer
embedding_layer . weight . data . copy_ ( model [ "tok_embeddings.weight" ])
# Get token embeddings for given tokens, converting to torch.bfloat16 format
token_embeddings_unnormalized = embedding_layer ( tokens ). to ( torch . bfloat16 )
# Print shape of resulting token embeddings
token_embeddings_unnormalized . shape
torch.Size([17, 4096])
Penyematan ini tidak dinormalisasi, dan akan berdampak serius jika kita tidak menormalkannya. Pada bagian selanjutnya, kita akan melakukan normalisasi pada vektor masukan kita.
Kita akan menormalkan vektor masukan menggunakan rumus yang sama yang telah kita lihat sebelumnya untuk RMSNorm guna memastikan masukan kita dinormalisasi.
# Calculating RMSNorm
def rms_norm ( tensor , norm_weights ):
# Calculate the mean of the square of tensor values along the last dimension
squared_mean = tensor . pow ( 2 ). mean ( - 1 , keepdim = True )
# Add a small value to avoid division by zero
normalized = torch . rsqrt ( squared_mean + norm_eps )
# Multiply normalized tensor by the provided normalization weights
return ( tensor * normalized ) * norm_weights
Kami akan menggunakan bobot perhatian dari layer_0 untuk menormalkan embeddings kami yang tidak dinormalisasi. Alasan menggunakan layer_0 adalah karena sekarang kita sedang membuat lapisan pertama arsitektur transformator LLaMA-3.
# using RMS normalization and provided normalization weights
token_embeddings = rms_norm ( token_embeddings_unnormalized ,
model [ "layers.0.attention_norm.weight" ])
# Print the shape of the resulting token embeddings
token_embeddings . shape
torch.Size([17, 4096])
Anda mungkin sudah mengetahui bahwa dimensi tidak akan berubah karena kita hanya melakukan normalisasi vektor dan tidak ada yang lain.
pertama, mari kita muat vektor kueri, kunci, nilai, dan keluaran dari model.
# Print the shapes of different weights
print (
# Query weight shape
model [ "layers.0.attention.wq.weight" ]. shape ,
# Key weight shape
model [ "layers.0.attention.wk.weight" ]. shape ,
# Value weight shape
model [ "layers.0.attention.wv.weight" ]. shape ,
# Output weight shape
model [ "layers.0.attention.wo.weight" ]. shape
)
torch.Size([4096, 4096]) torch.Size([1024, 4096]) torch.Size([1024, 4096]) torch.Size([4096, 4096])
Dimensi tersebut menunjukkan bahwa bobot model yang kami unduh bukan untuk masing-masing kepala secara individual tetapi untuk beberapa kepala perhatian karena penerapan pendekatan/pelatihan paralel. Namun, kita dapat membuka bungkus matriks ini agar tersedia untuk satu kepala saja.
# Retrieve query weight for the first layer of attention
q_layer0 = model [ "layers.0.attention.wq.weight" ]
# Calculate dimension per head
head_dim = q_layer0 . shape [ 0 ] // n_heads
# Reshape query weight to separate heads
q_layer0 = q_layer0 . view ( n_heads , head_dim , dim )
# Print the shape of the reshaped query weight tensor
q_layer0 . shape
torch.Size([32, 128, 4096])
Di sini, 32 adalah jumlah kepala perhatian di Llama-3, 128 adalah ukuran vektor kueri, dan 4096 adalah ukuran penyematan token. Kita dapat mengakses matriks bobot kueri dari kepala pertama lapisan pertama menggunakan:
# Extract the query weight for the first head of the first layer of attention
q_layer0_head0 = q_layer0 [ 0 ]
# Print the shape of the extracted query weight tensor for the first head
q_layer0_head0 . shape
torch.Size([128, 4096])
Untuk menemukan vektor kueri untuk setiap token, kami mengalikan bobot kueri dengan penyematan token.
# Matrix multiplication: token embeddings with transpose of query weight for first head
q_per_token = torch . matmul ( token_embeddings , q_layer0_head0 . T )
# Shape of resulting tensor: queries per token
q_per_token . shape
torch.Size([17, 128])
Vektor kueri pada dasarnya tidak mengetahui posisinya di prompt, jadi kami akan menggunakan RoPE untuk membuat mereka menyadarinya.
Kami sp