2 điểm bởi GN⁺ 2024-12-13 | 1 bình luận | Chia sẻ qua WhatsApp
  • Chỉ với mã C khoảng 3000 byte, trình suy luận GPT-2 xử lý trọn vẹn một luồng từ nạp trọng số, token hóa, chạy Transformer đến chuyển đổi đầu ra
  • Dù giữ kích thước mã nhỏ, chương trình vẫn tạo câu trả lời từ GPT-2 Small trong vài giây trên máy hiện đại nhờ KV caching, phép nhân ma trận nhanh và tùy chọn song song hóa OMP
  • Chất lượng đầu ra ở mức “khách quan mà nói là khá tệ”, và vẫn còn các hạn chế thực tế như xử lý UTF-8 cùng yêu cầu bộ nhớ khi chạy mô hình lớn
  • Phần triển khai được chia thành phép toán ma trận, các lớp mạng nơ-ron, Transformer, Byte Pair Encoding, I/O, nạp trọng số và BPE, qua đó cho thấy toàn bộ cấu trúc của một trình suy luận nhỏ
  • GPT-2 là mô hình mã nguồn mở năm 2019 yếu hơn GPT-4 rất nhiều, nhưng các thành phần cốt lõi để chạy mô hình ngôn ngữ hiện đại vẫn có thể được biểu diễn bằng mã C nhỏ gọn

Trình chạy GPT-2 viết bằng 3000 byte C

  • Chương trình này là một bản triển khai GPT-2 không phụ thuộc thư viện, đọc các ma trận trọng số và tệp BPE từ tệp TensorFlow gốc
  • Đầu vào được token hóa bằng bộ mã hóa Byte Pair Encoding(BPE) đơn giản, còn đầu ra được chuyển lại thành chuỗi bằng bộ giải mã BPE
  • Cấu trúc bên trong đi từ gói đại số tuyến tính cơ bản, phép toán ma trận, kiến trúc Transformer đến mã suy luận
  • Mã được công bố trên GitHub
  • GPT-2 Small tạo một câu trả lời trong khoảng vài giây trên máy hiện đại
    • Có triển khai KV caching
    • Sử dụng phép nhân ma trận hiệu quả
    • Có thể bật tùy chọn song song hóa OMP

Điều kiện chạy và giới hạn

  • Với bản triển khai này có thể tạo một chương trình hội thoại giống ChatGPT, nhưng chất lượng đầu ra không tốt
  • Việc xử lý ký tự UTF-8 có một số điểm đặc biệt
  • Nếu chạy mô hình cỡ XL với độ dài ngữ cảnh dài, có thể cần khoảng 100GB RAM
  • Với đầu vào ASCII kết hợp GPT-2 Small thì có thể chạy gần như ở mọi nơi

Cách GPT-2 và Transformer hoạt động

  • ChatGPT là một ứng dụng cho phép trò chuyện với mô hình ngôn ngữ như với con người, còn GPT-4 được giới thiệu là mô hình mới nhất vận hành ChatGPT
  • Chương trình C này triển khai hành vi tương tự ChatGPT bằng GPT-2, một mô hình từ năm 2019
  • GPT-2 là mô hình học máy thuộc họ Transformer
  • Transformer nhận một chuỗi từ có kích thước cố định làm đầu vào và dự đoán từ tiếp theo
  • Lặp lại cùng quy trình có thể tạo ra chuỗi có độ dài tùy ý

Phép toán ma trận và nén dựa trên macro

  • Vì mạng nơ-ron được cấu thành từ các phép toán ma trận, phần triển khai bắt đầu với struct Matrix tối thiểu
    • float* dat
    • int rows, cols
  • Các phép toán cần thiết chủ yếu thuộc hai loại
    • Phép toán ma trận-hằng số
    • Phép toán ma trận-ma trận
  • Macro C được dùng để giảm các cấu trúc vòng lặp lặp lại, chỉ thay toán tử cụ thể để sinh ra nhiều hàm
  • #define trong C gần giống phép thay thế đơn giản, nên có thể đưa cả biểu thức gồm dấu chấm phẩy, không chỉ toán tử thông thường, vào tham số macro để giảm kích thước mã

Phép nhân ma trận nhanh

  • Phép nhân ma trận cơ bản bắt đầu từ bản triển khai O(n³) đơn giản dùng ba vòng lặp lồng nhau
  • Xét đến đặc tính cache và truy cập bộ nhớ, các vòng lặp được thay đổi để đọc và ghi lặp lại cùng vùng nhớ
  • Bản triển khai nhanh tăng jk mỗi lần 4 đơn vị, rồi dùng các vòng lặp k2, j2 bên trong
  • Ở bước suy luận, để tái sử dụng một phần kết quả đã tính, có thêm cách chỉ nhân một phần của ma trận A với B

Triển khai các lớp mạng nơ-ron

  • Để tạo Transformer, một số lớp mạng nơ-ron được triển khai trực tiếp
  • Hàm kích hoạt GELU được triển khai bằng macro
  • Có hàm xử lý phần tam giác dưới của ma trận cho causal attention
    • Giới hạn ma trận attention để không nhìn thấy token tương lai mà chỉ nhìn quá khứ
  • LayerNorm chuẩn hóa trung bình và phương sai của mỗi lớp
  • Hàm Linear cộng bias theo kiểu lát gạch sau phép nhân ma trận

Phần lõi Transformer

  • Bản triển khai Transformer lặp lại luồng sau ở mỗi lớp
    • Qua LayerNorm và Linear để tính query, key, value cùng lúc
    • Chia qkv theo từng head
    • Tính tích của query và key, rồi áp dụng xử lý causal attention
    • Nhân kết quả softmax với ma trận value
    • Gom kết quả và áp dụng residual connection
    • Qua GELU và Linear, rồi lại áp dụng residual connection
  • Cuối cùng, sau LayerNorm cuối, nhân đầu ra tại vị trí token cuối với trọng số embedding để tính các ứng viên token tiếp theo

Cách KV caching hoạt động

  • Trong suy luận Transformer, sau khi tạo một token, không cần tính lại toàn bộ hàm để tạo token tiếp theo
  • Nếu tái sử dụng phần lớn kết quả đã tính đến token thứ N, thì việc tạo token thứ N+1 chỉ cần thêm một phần công việc
  • Bản triển khai thực hiện mọi phép cấp phát tuần tự trong cùng một khối bộ nhớ
  • Mỗi phép nhân ma trận luôn dùng cùng vùng nhớ, nhờ đó ở vòng lặp tiếp theo không cần khởi tạo bộ nhớ về 0 mà vẫn giữ kết quả trước đó
  • Ở vòng lặp mới, chỉ hàng thứ N+1 được tính

Triển khai Byte Pair Encoding

  • Mô hình ngôn ngữ cần đầu vào có kích thước cố định, nên khó xử lý trực tiếp vô số từ theo đơn vị từ
  • Mô hình cấp ký tự phải học ý nghĩa của mọi từ từ đầu, đồng thời làm giảm kích thước ngữ cảnh hiệu dụng theo độ dài từ trung bình
  • Các mô hình như GPT-2 dùng BPE để tạo token từ các mảnh từ
    • Từ phổ biến có thể trở thành một token
    • Từ hiếm được tách thành các mảnh nhỏ hơn
    • Ví dụ nicholas có thể được chia thành nich, o, las
  • Thuật toán BPE thông thường lặp lại việc gộp các cặp token liền kề
  • Bản triển khai C này dùng phương pháp đệ quy có thể tốn thời gian theo cấp số nhân thay cho thuật toán thời gian tuyến tính, nhằm giảm kích thước mã
    • Tìm mục trong vocabulary khớp với prefix của từ hiện tại
    • Token hóa đệ quy phần chuỗi còn lại
    • Chọn cách token hóa tốt nhất dựa trên độ dài và chỉ số trong vocabulary

Nạp trọng số

  • Trọng số mạng nơ-ron phải được đọc từ đĩa, và tệp là định dạng tuần tự hóa nhị phân phẳng của float 32-bit
  • Các kích thước mô hình GPT-2 dùng cùng kiến trúc và trọng số cũng được lưu theo cùng thứ tự, nên chỉ cần đọc lần lượt các ma trận có hình dạng đúng
  • Thứ tự lưu lớp khác với kỳ vọng
    • Sau lớp 0, 1 là lớp 10
    • Vì tên được sắp xếp theo lexicographic order
    • Trong sắp xếp chuỗi, 10 đứng trước 2
  • Bản triển khai dùng mã hoán vị để chuyển thứ tự này về thứ tự lớp thực tế

Nạp vocabulary BPE

  • Để chạy BPE, trước tiên cần đọc tệp vocabulary từ đĩa
  • Tệp gốc có định dạng để đọc bằng Python, không phải định dạng dễ phân tích bằng mã C nhỏ
  • Tệp không phải danh sách từ mà là danh sách gộp BPE
    • Ví dụ, thay vì lưu trực tiếp token Hello, nó lưu theo kiểu cần gộp Hello
  • Tệp dùng một encoding giống UTF-8 nhưng không hoàn toàn giống
    • Các ký tự ASCII in được được lưu nguyên dạng
    • Các ký tự không in được trong phạm vi 0~31 được mã hóa thành 188 + giá trị ký tự
    • Ví dụ, dấu cách được mã hóa thành token Ġ
  • Ġ trên đĩa là 0xc4 0xa0 trong UTF-8, nên cần xử lý riêng để đổi nó lại thành dấu cách

Điều mà đoạn mã nhỏ cho thấy

  • Có thể nén nhiều thập kỷ phát triển học máy vào vài nghìn byte mã
  • Ngoài trọng số mô hình thực tế, hầu như không thiếu các yếu tố cần thiết để chạy một mạng nơ-ron hiện đại
  • Bản triển khai này chủ yếu được tạo ra cho vui, nhưng là ví dụ cho thấy mạng nơ-ron thực ra có thể được chạy bằng các thành phần đơn giản

1 bình luận

 
GN⁺ 2024-12-13
Ý kiến trên Hacker News
  • Tôi chưa tự chạy thử mã, nhưng ấn tượng ở chỗ kích thước nhỏ
    Nghĩ đến việc các chương trình ELIZA thuở đầu còn lớn hơn, có thể nói trong 4 năm qua ta đã có thể nhồi nhét thứ như thế này đến từng byte
    Nếu ai biết phép màu nằm ở đâu thì mong giải thích giúp. Tôi tò mò không biết là ở hàm GELU, hay ở mô hình được tải xuống bằng script Bash

    • Phần lớn phép màu nằm ở tệp mô hình 475MB được tải xuống bằng script Bash
    • Chạy thử thì không mấy ấn tượng
      Với Who are you? nó trả lời I am Alice., còn khi hỏi về máy tính hay chức năng thì lặp lại I am a computer model trained by OpenAI. How can I help you?
      Khi yêu cầu giải thích phép cộng thì nó đưa ra giải thích về phép nhân, còn 2+2 hay Sum 2+2 thì chỉ nhắc lại y nguyên
  • Tôi còn nhớ lúc nghịch GPT-2 khi nó mới ra mắt
    Tôi xuất log chat với một người bạn, fine-tune GPT-2 rồi để nó bắt chước cuộc trò chuyện giữa hai chúng tôi; vừa cực kỳ buồn cười, vừa đôi lúc chính xác đến rợn người
    Tôi tò mò không biết bước nhảy vọt từ GPT-2 lên GPT-3 là nhờ điều gì. Do mô hình lớn hơn, dữ liệu nhiều hơn, hay cả hai
    Tôi biết RLHF đã tạo ra khác biệt lớn, nhưng ngay cả mô hình GPT-3 nền tảng cũng khá hữu ích chỉ với khả năng hoàn thành văn bản nếu được đưa đủ ví dụ

  • Không rõ nữa, nhưng có vài truyện cổ tích tôi thích do GPT-2 viết
    https://deepdreams.stavros.io/episodes/the-princess-the-fair...

    • Thật sự hay, thực sự thú vị và cũng hợp để nghe rồi ngủ thiếp đi
      Tôi tự hỏi liệu nó có được tạo bằng GPT-2 trên trang này không
    • Ấn tượng, kỳ lạ nhưng khoảng 90% vẫn nhất quán, nên tạo ra một bầu không khí kỳ quái rất riêng
  • Đoạn “phần lớn chỉ làm cho vui, nhưng đây là ví dụ hay cho thấy mạng nơ-ron thực ra đơn giản đến mức nào” khá thú vị
    Suỵt, đừng nói với ai nhé. Trí tuệ nhân tạo là ma thuật hắc ám dùng để kiếm tiền

  • GPT-2 có được instruction tuning nên mới dùng được cho chat thực tế à?
    Nếu không thì gọi thứ này là bản nhái ChatGPT có vẻ khá gượng ép

    • Trong bài đã nói thế này rồi: nếu không quan tâm chất lượng đầu ra thì có thể tạo ra thứ giống ChatGPT, và về khách quan thì đầu ra khá kinh khủng nhưng vẫn chạy được
      Về cơ bản là không dùng được, và gần như chẳng liên quan gì ngoài việc mượn cái tên. Dù vậy nó vẫn là một chương trình biên dịch và chạy được
      Nhìn các phản ứng đánh giá cao hiệu năng của một dự án mà chính tác giả cũng thừa nhận là không hoạt động tử tế, rốt cuộc có vẻ cốt lõi là lôi kéo sự chú ý bằng từ khóa thịnh hành
  • Câu “Các ngôn ngữ có macro tử tế đang nhìn thấy chưa. Lisp không phải lúc nào cũng hơn C đâu!” lần này có thể chấp nhận được. Vì đó là trò đùa hướng lên trên
    Nếu bạn chưa thấy link mã nguồn thì nó nằm lẫn trong phần nội dung: https://github.com/carlini/c-chat-gpt-2

  • Tôi từng thấy các chatbot trí tuệ nhân tạo cổ điển còn tốt hơn
    https://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/areas...
    Splotch chỉ cần sửa chút là biên dịch tốt trên các hệ Unix-like hiện đại

  • Không biết có ai đã chạy cục bộ để xem GPT-2 này tạo ra đầu ra kiểu gì chưa

    • Cảm giác gần như lúc nào nó cũng lặp lại cùng một đầu ra
      Dù vậy vẫn khá thú vị, và tôi muốn tự xem bên trong rồi chỉnh thử. Tôi đã muốn nghịch GPT-2 cục bộ một thời gian rồi
    • Đọc qua thì có vẻ nếu dùng cùng temperature và seed, mô hình GPT-2 được tải theo cách thông thường và mô hình được chương trình này tải sẽ cho ra chính xác cùng đầu ra
      Tôi chưa trực tiếp kiểm tra temperature và seed trong mã, chủ yếu đang xem vì sao họ lại obfuscate
      Dù gỡ obfuscation thì mã chắc cũng không dài khủng khiếp; nếu khoảng 10.000 ký tự thì chỉ nhìn trên màn hình thôi cũng đủ ấn tượng rồi
  • Dạo này dùng gptscript là có thể nhanh chóng tự triển khai ChatGPT của riêng mình
    https://github.com/gptscript-ai/gptscript

  • GELU thật sự trông như phép màu:
    UNARY(GELU, b / 2 * (1 + tanh(.7978845 * (b + .044715 * b * b * b))))

    • Đây chỉ là một xấp xỉ thực dụng cho định nghĩa toán học thật sự của GELU
      Định nghĩa là GELU(x) := x * Φ(x), trong đó Φ(x) là hàm phân phối tích lũy của phân phối Gauss
    • Nó gợi nhớ đến fast inverse square root