GPT được triển khai bằng 500 dòng SQL
(explainextended.com)- Chỉ với PostgreSQL và pgvector, tác giả đã chuyển luồng suy luận GPT-2 sang SQL, tái hiện từ token hóa đến các khối Transformer và sinh token tiếp theo
- LLM sinh nội dung gần giống một hàm xác định, tạo ra cùng xác suất cho các token ứng viên với cùng đầu vào; điểm khiến phản hồi khác nhau là bước lựa chọn ngẫu nhiên khi chọn token ứng viên
- Phần triển khai biểu diễn BPE tokenizer của GPT-2, 50.257 token, embedding 768 chiều, ngữ cảnh 1.024 token, 12 khối, 12 attention head và feedforward dựa trên GELU bằng các truy vấn và bảng SQL
- Cần có các cách đi vòng thực tế phù hợp với môi trường cơ sở dữ liệu, chẳng hạn như PostgreSQL regex không hỗ trợ thuộc tính Unicode và hạn chế của
EXPkhi xử lý các giá trị rất nhỏ - Ví dụ sinh 10 token từ
"Happy New Year! I wish you", xuất ra"Happy New Year! I wish you all the best in your new year!", mất 2 phút 44 giây trong môi trường của tác giả
Xây dựng pipeline suy luận GPT-2 bằng SQL
- ChatGPT trả lời rằng SQL không phù hợp để triển khai mô hình ngôn ngữ lớn, nhưng tác giả đã triển khai pipeline suy luận GPT-2 bằng PostgreSQL SQL
- Phần mô tả triển khai được tham khảo là GPT in 60 Lines of NumPy của Jay Mody, và các thành phần tương tự được chuyển sang bảng và truy vấn cơ sở dữ liệu
- LLM sinh nội dung có thể được xem như một hàm dạng
llm(prompt: str) -> list[tuple[str, float]]- Đầu vào là prompt văn bản
- Đầu ra là các chuỗi ứng viên tiếp theo cùng mảng xác suất
- Nếu toán học nội bộ và tham số giống nhau, cùng một đầu vào sẽ trả về cùng một kết quả
- Lý do các sản phẩm như ChatGPT có thể đưa ra câu trả lời khác nhau cho cùng một câu hỏi nằm ở lựa chọn ngẫu nhiên trong bước chọn token tiếp theo, hơn là ở bản thân mô hình
Vòng lặp sinh văn bản
- Quá trình sinh có cấu trúc: chuyển prompt thành mảng token, gọi mô hình lặp lại để chọn token tiếp theo rồi gắn vào sau prompt
- Luồng cơ bản gồm các bước sau
- Chuyển chuỗi thành mảng ID token bằng
tokenize(prompt) gpt2(tokens)tính xác suất cho 50.257 tokenselect_next_token(candidates)chọn token tiếp theo- Thêm token đã chọn vào mảng
- Dừng theo các điều kiện như số token định trước, timeout hoặc stopword
- Khôi phục mảng token thành chuỗi bằng
detokenize(tokens)
- Chuyển chuỗi thành mảng ID token bằng
- Chuỗi token được tích lũy theo cách này có thể trở thành văn bản ngôn ngữ tự nhiên mang các đặc điểm trông như ngữ pháp, cú pháp, ngữ nghĩa và suy luận
Triển khai BPE tokenizer bằng SQL
- Trước khi đưa vào mạng nơ-ron, văn bản phải được chuyển thành danh sách số, nhưng nếu dùng nguyên các codepoint Unicode thì không gian token và độ dài sẽ kém hiệu quả
- GPT-2 sử dụng một biến thể của Byte pair encoding
- Từ điển token sử dụng 50.257 codepoint
- Bao gồm chuỗi byte UTF-8 và token “end of text”
- Ban đầu bắt đầu từ 256 token byte, rồi thêm các cặp token liền kề xuất hiện thường xuyên làm token mới
- Lặp phép hợp nhất này 50.000 lần để tạo 50.256 token, cuối cùng thêm token end-of-text
- Tokenizer của GPT-2 có một lớp bổ sung ánh xạ byte thành ký tự chuỗi; ánh xạ này được định nghĩa trong
encoder.pycủa OpenAI GPT-2 - Trong bản triển khai SQL, từ điển token tải từ OpenAI được đưa vào bảng
tokenizer, còn ánh xạ byte-ký tự được lưu trong bảngencoder - Ví dụ
"Mississippilessly"bắt đầu từ các byte đơn bằng recursive CTE và lặp lại việc hợp nhất cặp liền kề tốt nhất có thể hợp nhất- Trong ví dụ, số token giảm từ 17 xuống 5
- Sử dụng không gian khoảng 50k token của GPT-2 thay vì không gian khoảng 150k codepoint của Unicode
- Khi xử lý nhiều từ, GPT-2 dùng regex để tách văn bản và hợp nhất bên trong từng từ
- PostgreSQL không hỗ trợ thuộc tính ký tự Unicode trong regex nên regex gốc của GPT-2 đã được sửa một phần
- Sửa đổi này có khả năng đã làm hỏng khả năng hỗ trợ Unicode đúng cách
"PostgreSQL is great"được tokenizer SQL chuyển thành[6307, 47701, 318, 1049]- Các cụm token là
Post,greSQL,Ġis,Ġgreat Ġbiểu thị khoảng trắng
- Các cụm token là
Embedding và cửa sổ ngữ cảnh
- ID token không được dùng trực tiếp trong phép tính của mô hình mà được chuyển thành vector embedding
- GPT-2 embedding riêng cho token và vị trí
WTElà word token embedding, ma trận 50257×768WPElà word position embedding, ma trận 1024×768
- Ở mỗi vị trí token, vector
WTEvà vectorWPEđược cộng lại để tạo vector đầu vào cho bước tiếp theo - Vì
WPEchỉ có 1.024 vị trí, số token tối đa có thể dùng trong prompt GPT-2 là 1.024- Con số này tương ứng với cửa sổ ngữ cảnh của LLM
- Đây là siêu tham số được quyết định khi thiết kế mô hình và không thay đổi nhờ huấn luyện
- Bản triển khai SQL sử dụng pgvector
- Cũng có thể làm bằng SQL thuần bằng cách tự định nghĩa phép toán vector trên mảng, nhưng hiệu năng sẽ thấp
- Phiên bản ban đầu chạy bằng các hàm SQL thuần nhưng chậm
Mở rộng self-attention thành truy vấn SQL
- Cốt lõi của Transformer là cơ chế self-attention, dựa trên bài báo năm 2017 Attention is all you need
- Attention khiến các vector token ảnh hưởng lẫn nhau, cho phép thông tin ở phần đầu prompt được truyền đến vector cuối cùng
- Bản triển khai GPT-2 sử dụng 12 bộ ma trận
Q,K,V- Mỗi bộ là một attention head
- Mỗi head có 64 chiều
c_attnlà phép biến đổi tuyến tính 768×2304, kết quả là vector 2304 chiều gồmQ,K,Vxếp ngang- Trọng số và bias được lưu trong các bảng
c_attn_w,c_attn_b
- Trước khi tính attention, layer normalization được áp dụng
- Tham số scale và shift được lưu trong các bảng
ln_1_g,ln_1_b
- Tham số scale và shift được lưu trong các bảng
- Trong causal self-attention, causal mask được áp dụng để token phía sau không thể ảnh hưởng đến token phía trước
- Ứng viên token tiếp theo của mô hình cuối cùng được quyết định từ embedding cuối cùng
- Luồng thông tin phải hướng về phía vector cuối cùng, và các giá trị trung gian của vector cuối không được ảnh hưởng đến các vector trước đó
- Bản triển khai SQL xử lý đầu vào nhỏ hơn -745.13 thành 0 để tránh vấn đề PostgreSQL
EXPthất bại với các số rất nhỏ trong phép tính softmax - Nhờ causal mask, ngay cả khi gắn token mới vào prompt, kết quả tính toán cho các token trước đó không thay đổi
- Bản triển khai GPT-2 gốc tận dụng tính chất này
- Bản triển khai SQL không dùng việc tái sử dụng này để giữ đơn giản
Multi-head attention và kết nối dư
- Kết quả attention của 12 head, mỗi kết quả 64 chiều, được xếp ngang để tạo lại vector 768 chiều
- Sau đó, đầu ra attention được chiếu bằng phép biến đổi tuyến tính đã học lưu trong
c_proj_w,c_proj_b - Kết quả multi-headed attention được cộng lại với đầu vào ban đầu
- Kết nối dư này là kỹ thuật có trong bài báo Transformer gốc
- Nó được giới thiệu như một thiết kế nhằm giảm nhẹ vấn đề vanishing gradient và exploding gradient trong quá trình huấn luyện
Bước feedforward và khối Transformer
- Sau attention là mạng nơ-ron feedforward
- Bước feedforward của GPT-2 gồm một multi-layer perceptron 3 lớp
- Số chiều là 768 → 3072 → 768
- Hàm kích hoạt sử dụng GELU
- Tham số biến đổi tuyến tính được lưu trong các bảng sau
mlp_c_fc_w,mlp_c_fc_bmlp_c_proj_w,mlp_c_proj_b
- Đầu vào feedforward cũng được chuẩn hóa trước bằng tham số
ln_2ln_2_g,ln_2_blưu scale và shift
- Đầu ra feedforward cũng được áp dụng kết nối dư, cộng lại đầu vào
- Tổ hợp attention + feedforward này là một block, và GPT-2 nối 12 block như một pipeline
- Mỗi block có bộ tham số đã học riêng
- Trong SQL, các block được nối bằng recursive CTE
- Đầu ra của block cuối cùng được chuẩn hóa lại bằng tham số
ln_f
Chuyển ngược về token tiếp theo
- Trong đầu ra cuối cùng, vector 768 chiều ở vị trí cuối là embedding của token tiếp theo
- Để chuyển vector này trở lại thành token, ma trận
WTEđã dùng cho embedding token ban đầu được dùng lại - Phép biến đổi ngược chính xác thường là không thể
- Embedding dự đoán có thể không trùng chính xác với một hàng cụ thể của
WTE - Vì vậy, tính dot product với từng embedding token để tìm token gần nhất
- Embedding dự đoán có thể không trùng chính xác với một hàng cụ thể của
- Kết quả dot product giữa
WTEvà embedding dự đoán là 50.257 điểm số, tức logit - Các điểm số này đi qua softmax để chuyển thành xác suất
- Số ứng viên hàng đầu là
top_n - Giá trị điều chỉnh phân phối xác suất là temperature
- Temperature càng cao thì khả năng token không ở hạng 1 được chọn càng lớn, khiến suy luận kém dự đoán hơn
- Số ứng viên hàng đầu là
- Trong ví dụ
"PostgreSQL is great", 5 ứng viên token tiếp theo hàng đầu như sauĠfor,.ĠatĠto
- Khi temperature thay đổi thành 0.5, 1, 2, phân phối xác suất softmax của cùng các ứng viên cũng thay đổi
Kết quả suy luận thực tế và mã nguồn
- SQL cuối cùng lặp lại quá trình chọn token theo xác suất và gắn vào prompt
- Bản thân mô hình là xác định; yếu tố không xác định chỉ là số ngẫu nhiên trong lựa chọn token
- Cấu hình ví dụ như sau
- Prompt:
"Happy New Year! I wish you" - Số token sinh: 10
- temperature: 2
- top_n: 1
- Dùng
SETSEED(0.20231231)
- Prompt:
- Trong môi trường của tác giả, truy vấn chạy trong 2 phút 44 giây
- Kết quả đầu ra là
"Happy New Year! I wish you all the best in your new year!" - Truy vấn và mã cài đặt có trong kho GitHub quassnoi/explain-extended-2024
1 bình luận
Ý kiến trên Hacker News
Đẹp thật. Tôi cũng từng đào một hang thỏ tương tự với SQLite, nhưng vẫn chưa kéo được cả mạng nơ-ron vào đến mức đó
Tôi lấy cảm hứng từ loạt bài giảng makemore[0], và sau khoảng 1 giờ thì nó chuyển từ cách đếm sang mạng nơ-ron; tôi cũng chỉ đi được đến cỡ đó
Việc tách nó ra để nhìn dưới dạng mô hình quan hệ đúng là một bài tập rất hay
[0] https://www.youtube.com/watch?v=PaCmpygFfXo
Demo hay đấy, nhưng phần giải thích về causal masking trong bài có vẻ đang trộn lẫn giữa huấn luyện và suy luận
Causal masking có mục đích ngăn việc “nhìn trộm” các token tương lai trong lúc huấn luyện, và trong các kiến trúc kiểu GPT thì nó cũng có mục đích ép tính tự hồi quy trong lúc suy luận
Khi suy luận, dù sao cũng chỉ dùng token cuối cùng, nên token đó sẽ chú ý tới toàn bộ chuỗi đầu vào; vì vậy token tiếp theo không phải chỉ được quyết định bằng embedding của token cuối cùng
Tôi thắc mắc liệu đây có phải là mô tả chính xác của driver loop của GPT không: token hóa prompt, lấy xác suất cho 50257 token bằng
gpt2(tokens), chọn token tiếp theo, gắn vào danh sách token, kiểm tra điều kiện dừng, rồi cuối cùng mới giải token hóaNhưng như vậy thì cỗ máy trạng thái này trông giống như đang hiện thực thuật toán Shlemiel the painter, nên tôi tự hỏi về chi phí tính toán bản chất của tác vụ sinh
Cửa sổ đó là một sliding window
gpt2Nó gần như chỉ cho thấy cách gom các token đã sinh thành câu
Tài liệu liên quan: A GPT in 60 Lines of NumPy - https://news.ycombinator.com/item?id=34726115 - tháng 2 năm 2023, 146 bình luận
Trong cùng tinh thần đó, tôi đã hiện thực toàn bộ GPT bằng hàm bảng tính, và cũng làm luôn video hướng dẫn để xem kèm
https://spreadsheets-are-all-you-need.ai/
Tôi thấy LLM khá ngầu, nhưng với tư cách người chưa từng cần học cách nó hoạt động ở mức nghề nghiệp, thì video 10 phút đó dạy tôi nhiều hơn cả mấy năm đọc những bình luận HN khó nhằn và các bài báo phổ thông hời hợt
Nhìn thấy một lượng khổng lồ số dấu phẩy động chất đống chờ được tính toán khiến tôi hiểu trực quan hơn rất nhiều vì sao công nghệ này ngốn GPU đến thế
Có lẽ cũng có thể giải thích tốt cả quá trình huấn luyện nếu tính đạo hàm của từng tham số trên từng ví dụ huấn luyện và chỉ ra rõ nó được ánh xạ vào tham số tương ứng như thế nào
Hay đấy. Mới 1 năm trước thôi, đây còn giống một dạng phép màu, vậy mà giờ đã được giải thích tốt đến mức gần như trẻ con cũng có thể theo được
Mô hình được giải thích trong bài là GPT-2, công bố từ đầu năm 2019
Muốn hiểu tử tế bài này thì phải có nền tảng khoa học máy tính khá chắc, và ngay cả tiêu đề thôi cũng đã khó tiếp cận với 99% nhân loại rồi
Tôi đã hoàn toàn tránh né GPT và LLM, nhưng cách này có vẻ chỉ tạo ra được mức độ trôi chảy nào đó trong đầu ra văn bản, chứ chưa thấy khả năng diễn giải câu hỏi và trả lời
Tôi tự hỏi có bài blog hay khóa học đơn giản nào giải thích cách nó thực sự hoạt động, hoặc cho thấy một engine đồ chơi kiểu Python hay không
Các tài liệu học tôi từng thấy đến giờ chủ yếu nghiêng về cách dùng nền tảng, chứ không bàn nhiều về cơ chế bên trong
Đặc biệt là [0], [1], [2] rất hay
[0] http://jalammar.github.io/illustrated-transformer/
[1] http://jalammar.github.io/illustrated-gpt2/
[2] https://jalammar.github.io/visualizing-neural-machine-transl...
Điều thú vị là machine learning hiện đại không đòi hỏi tính đầy đủ Turing
Thế mà người ta vẫn đang cân nhắc khả năng AGI, nên nếu rốt cuộc kết luận là không cần tính đầy đủ Turing thì sẽ khá thú vị
Vì một lý do đơn giản: tôi có thể lần theo việc thực thi của mã đầy đủ Turing ngay trong đầu mình
Bài viết rất hay, phần giải thích từng thành phần rõ ràng và khá kỹ nên đọc rất thích
Chỉ là tôi lỡ bấm vào “+ expand source”, rồi nhìn thấy con quái vật đáng kinh ngạc đó xong thì lại thấy đồng cảm với câu ChatGPT nói rằng “SQL không phù hợp để hiện thực mô hình ngôn ngữ lớn”
Câu “Unicode thông thường không hợp với mạng nơ-ron” thực ra không đúng. Cứ nhìn ByT5 là thấy
Thứ mà bài viết gọi là “alphabet” thường được gọi là vocabulary, và nếu dùng byte UTF-8 làm vocabulary thì số token sẽ là 256 chứ không phải 149186
ByT5 làm đúng như vậy
Việc các mô hình có hiệu năng tốt nhất trên thị trường đều dùng tokenization chính là bằng chứng
Tokenization về bản chất khá gần với một kiểu vá víu, và không có gì bí mật khi nói rằng lý tưởng nhất là một ngày nào đó muốn loại bỏ nó bằng cách nào đó (https://twitter.com/karpathy/status/1657949234535211009)
Về nguyên tắc, có thể bù đắp các khiếm khuyết của tokenization cấp byte bằng mô hình lớn hơn và ngữ cảnh lớn hơn, nhưng trên thực tế sẽ tốn tài nguyên hơn rất nhiều để huấn luyện một mô hình có cùng mức độ thông minh
Tất nhiên cũng có những tác vụ cụ thể mà tokenization lại làm hại trí thông minh, như đếm số chữ cái trong một từ chẳng hạn