
microgpt
Đây là hướng dẫn ngắn gọn về dự án nghệ thuật mới microgpt của tôi, một tệp gồm 200 dòng Python thuần túy không có phần phụ thuộc giúp đào tạo và suy luận GPT. Tệp này chứa nội dung thuật toán đầy đủ về những thứ cần thiết: tập dữ liệu tài liệu, mã thông báo, công cụ tự động chuyển đổi, kiến trúc mạng thần kinh giống GPT-2, trình tối ưu hóa Adam, vòng lặp đào tạo và vòng suy luận. Mọi thứ khác chỉ là hiệu quả. Tôi không thể đơn giản hóa điều này hơn nữa. Kịch bản này là đỉnh cao của nhiều dự án (micrograd, makemore, nanogpt, v.v.) và nỗi ám ảnh kéo dài hàng thập kỷ nhằm đơn giản hóa LLM thành những điều cơ bản nhất của chúng và
Đây là hướng dẫn ngắn gọn về dự án nghệ thuật mới microgpt của tôi, một tệp gồm 200 dòng Python thuần túy không có phần phụ thuộc giúp đào tạo và suy luận GPT. Tệp này chứa nội dung thuật toán đầy đủ về những thứ cần thiết: tập dữ liệu tài liệu, mã thông báo, công cụ tự động chuyển đổi, kiến trúc mạng thần kinh giống GPT-2, trình tối ưu hóa Adam, vòng lặp đào tạo và vòng suy luận. Mọi thứ khác chỉ là hiệu quả. Tôi không thể đơn giản hóa điều này hơn nữa. Kịch bản này là đỉnh cao của nhiều dự án (micrograd, makemore, nanogpt, v.v.) và nỗi ám ảnh kéo dài hàng thập kỷ nhằm đơn giản hóa LLM thành những điều cơ bản nhất của chúng và tôi nghĩ nó thật đẹp 🥹. Nó thậm chí còn phá vỡ hoàn hảo trên 3 cột:
Tìm nó ở đâu:
Ý chính GitHub này có mã nguồn đầy đủ: microgpt.py
Nó cũng có sẵn trên trang web này: https://karpathy.ai/microgpt.html
Cũng có sẵn dưới dạng sổ ghi chép Google Colab
MỚI: mua microgpt dưới dạng bộ ba trên cửa hàng nghệ thuật của tôi tại karpathy.art :)
Sau đây là hướng dẫn của tôi về cách hướng dẫn người đọc quan tâm thực hiện mã.
Tập dữ liệu
Nguồn nhiên liệu của các mô hình ngôn ngữ lớn là luồng dữ liệu văn bản, được phân tách tùy ý thành một bộ tài liệu. Trong các ứng dụng cấp sản xuất, mỗi tài liệu sẽ là một trang web internet nhưng đối với microgpt, chúng tôi sử dụng một ví dụ đơn giản hơn gồm 32.000 tên, mỗi tên một dòng:
# Giả sử có một tập dữ liệu đầu vào `docs`: list[str] các tài liệu (ví dụ: tập dữ liệu về tên)
nếu không phải os.path.exists('input.txt'):
nhập urllib.request
name_url = 'https://raw.githubusercontent.com/karpathy/makemore/refs/heads/master/names.txt'
urllib.request.urlretrieve(names_url, 'input.txt')
docs = [l.strip() for l in open('input.txt').read().strip().split('\n') if l.strip()] # list[str] của tài liệu
ngẫu nhiên.shuffle(docs)
print(f"num docs: {len(docs)}")
Tập dữ liệu trông như thế này. Mỗi tên là một tài liệu:
emma
Olivia
ava
Isabella
sophia
charlotte
Mia
amelia
người chơi đàn hạc
... (~32.000 tên theo dõi)
Mục tiêu của mô hình là tìm hiểu các mẫu trong dữ liệu và sau đó tạo ra các tài liệu mới tương tự có chung các mẫu thống kê bên trong. Ở dạng xem trước, ở cuối tập lệnh, mô hình của chúng tôi sẽ tạo ra những cái tên mới (“ảo giác”!) mới, nghe có vẻ hợp lý. Bỏ qua phía trước, chúng ta sẽ nhận được:
mẫu 1: kamon
mẫu 2: ann
mẫu 3: karai
mẫu 4: jaire
mẫu 5: vialan
mẫu 6: karia
mẫu 7: yeran
mẫu 8: anna
mẫu 9: areli
mẫu 10: kaina
mẫu 11: kona
mẫu 12: keylen
mẫu 13: liole
mẫu 14: alerin
mẫu 15: tai
mẫu 16: lenne
mẫu 17: kana
mẫu 18: lara
mẫu 19: alela
mẫu 20: anton
Trông thì không nhiều nhưng dưới góc nhìn của một mô hình như ChatGPT, cuộc trò chuyện của bạn với nó chỉ là một “tài liệu” trông buồn cười. Khi bạn khởi tạo tài liệu bằng lời nhắc của mình, phản hồi của mô hình từ góc độ của nó chỉ là việc hoàn thành tài liệu thống kê.
Mã thông báo
Về cơ bản, mạng lưới thần kinh hoạt động với các con số chứ không phải ký tự, vì vậy chúng ta cần một cách để chuyển đổi văn bản thành một chuỗi các id mã thông báo số nguyên và ngược lại. Các mã thông báo sản xuất như tiktoken (được GPT-4 sử dụng) hoạt động trên các khối ký tự để đạt hiệu quả, nhưng mã thông báo đơn giản nhất có thể chỉ gán một số nguyên cho mỗi ký tự duy nhất trong tập dữ liệu:
# Hãy có một Tokenizer để dịch các chuỗi thành các ký hiệu rời rạc và ngược lại
uchars = được sắp xếp(set(''.join(docs))) # ký tự duy nhất trong tập dữ liệu trở thành id mã thông báo 0..n-1
BOS = len(uchars) # mã thông báo id cho mã thông báo Bắt đầu chuỗi (BOS) đặc biệt
vocab_size = len(uchars) + 1 # tổng số mã thông báo duy nhất, +1 dành cho BOS
print(f"kích thước từ vựng: {vocab_size}")
Trong đoạn mã trên, chúng tôi thu thập tất cả các ký tự duy nhất trên tập dữ liệu (chỉ là tất cả các chữ cái viết thường từ a đến z), sắp xếp chúng và mỗi chữ cái sẽ nhận được một id theo chỉ mục của nó. Lưu ý rằng bản thân các giá trị số nguyên không có ý nghĩa gì cả; mỗi mã thông báo chỉ là một biểu tượng riêng biệt. Thay vì 0, 1, 2, chúng có thể là các biểu tượng cảm xúc khác nhau. Ngoài ra, chúng tôi tạo thêm một mã thông báo đặc biệt gọi là BOS (Bắt đầu chuỗi), hoạt động như một dấu phân cách: nó cho mô hình biết “một tài liệu mới bắt đầu/kết thúc tại đây”. Sau này trong quá trình đào tạo, mỗi tài liệu sẽ được bọc BOS trên cả hai mặt: [BOS, e, m, m, a, BOS]. Mô hình biết rằng BOS đặt tên mới và BOS khác kết thúc tên đó. Do đó, chúng tôi có từ vựng cuối cùng là 27 (26 ký tự chữ thường có thể có từ a-z và +1 cho mã thông báo BOS).
Tự động tốt nghiệp
Việc đào tạo mạng nơ-ron yêu cầu độ dốc: đối với mỗi tham số trong mô hình, chúng ta cần biết “nếu tôi nâng con số này lên một chút thì tổn thất sẽ tăng hay giảm và tăng bao nhiêu?”. Biểu đồ tính toán có nhiều đầu vào (tham số mô hình và mã thông báo đầu vào) nhưng chuyển xuống một đầu ra vô hướng duy nhất: tổn thất (chúng tôi sẽ xác định chính xác tổn thất bên dưới). Lan truyền ngược bắt đầu ở đầu ra duy nhất đó và hoạt động ngược lại thông qua biểu đồ, tính toán độ dốc của tổn thất đối với mọi đầu vào. Nó dựa vào quy tắc dây chuyền từ phép tính. Trong quá trình sản xuất, các thư viện như PyTorch tự động xử lý việc này. Ở đây, chúng tôi triển khai nó từ đầu trong một lớp duy nhất có tên là Value:
Giá trị lớp:
__slots__ = ('dữ liệu', 'grad', '_children', '_local_grads')
def __init__(self, data, Children=(), local_grads=()):
self.data = data # giá trị vô hướng của nút này được tính toán trong quá trình chuyển tiếp
self.grad = 0 # phái sinh của tổn thất w.r.t. nút này, được tính theo đường truyền ngược
self._children = con # con của nút này trong biểu đồ tính toán
self._local_grads = local_grads # đạo hàm cục bộ của nút này w.r.t. con cái của nó
def __add__(bản thân, người khác):
other = other if isinstance(other, Value) else Value(other)
trả về Giá trị(self.data + other.data, (self, other), (1, 1))
def __mul__(bản thân, người khác):
other = other if isinstance(other, Value) else Value(other)
trả về Giá trị(self.data * other.data, (self, other), (other.data, self.data))
def __pow__(self, other): trả về Giá trị(self.data**other, (self,), (other * self.data**(other-1),))
def log(self): trả về Giá trị(math.log(self.data), (self,), (1/self.data,))
def exp(self): trả về Giá trị(math.exp(self.data), (self,), (math.exp(se
Nguồn tin: Andrej Karpathy Blog. Bản dịch tiếng Việt do AI thực hiện, có thể có sai sót.