干貨滿滿!大神Karpathy兩小時AI大課文字版第一彈,全新工作流自動把視頻轉成文章
前段時間,AI大神Karpathy上線的AI大課,已經收獲了全網15萬次播放量。
當時還有網友表示,這2小時課程的含金量,相當于大學4年。
就在這幾天,Karpathy又萌生了一個新的想法:
那便是,將2小時13分鐘的「從頭開始構建GPT分詞器」的視頻,轉換為一本書的章節(或者博客文章)形式,專門討論「分詞」。
具體步驟如下:
- 為視頻添加字幕或解說文字。
- 將視頻切割成若干帶有配套圖片和文字的段落。
- 利用大語言模型的提示工程技術,逐段進行翻譯。
- 將結果輸出為網頁形式,其中包含指向原始視頻各部分的鏈接。
更廣泛地說,這樣的工作流程可以應用于任何視頻輸入,自動生成各種教程的「配套指南」,使其格式更加便于閱讀、瀏覽和搜索。
這聽起來是可行的,但也頗具挑戰。
他在GitHub項目minbpe下,寫了一個例子來闡述自己的想象。
地址:https://github.com/karpathy/minbpe/blob/master/lecture.md
Karpathy表示,這是自己手動完成的任務,即觀看視頻并將其翻譯成markdown格式的文章。
「我只看了大約4分鐘的視頻(即完成了3%),而這已經用了大約30分鐘來寫,所以如果能自動完成這樣的工作就太好了」。
接下來,就是上課時間了!
「LLM分詞」課程文字版
大家好,今天我們將探討LLM中的「分詞」問題。
遺憾的是,「分詞」是目前最領先的大模型中,一個相對復雜和棘手的組成部分,但我們有必要對其進行詳細了解。
因為LLM的許多缺陷可能歸咎于神經網絡,或其他看似神秘的因素,而這些缺陷實際上都可以追溯到「分詞」。
字符級分詞
那么,什么是分詞呢?
事實上,在之前的視頻《讓我們從零開始構建 GPT》中,我已經介紹過分詞,但那只是一個非常簡單的字符級版本。
如果你去Google colab查看那個視頻,你會發現我們從訓練數據(莎士比亞)開始,它只是Python中的一個大字符串:
First Citizen: Before we proceed any further, hear me speak.
All: Speak, speak.
First Citizen: You are all resolved rather to die than to famish?
All: Resolved. resolved.
First Citizen: First, you know Caius Marcius is chief enemy to the people.
All: We know't, we know't.
但是,我們如何將字符串輸入LLM呢?
我們可以看到,我們首先要為整個訓練集中的所有可能字符,構建一個詞匯表:
# here are all the unique characters that occur in this text
chars = sorted(list(set(text)))
vocab_size = len(chars)
print(''.join(chars))
print(vocab_size)
# !$&',-.3:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
# 65
然后根據上面的詞匯表,創建用于在單個字符和整數之間進行轉換的查找表。此查找表只是一個Python字典:
stoi = { ch:i for i,ch in enumerate(chars) }
itos = { i:ch for i,ch in enumerate(chars) }
# encoder: take a string, output a list of integers
encode = lambda s: [stoi[c] for c in s]
# decoder: take a list of integers, output a string
decode = lambda l: ''.join([itos[i] for i in l])
print(encode("hii there"))
print(decode(encode("hii there")))
# [46, 47, 47, 1, 58, 46, 43, 56, 43]
# hii there
一旦我們將一個字符串轉換成一個整數序列,我們就會看到每個整數,都被用作可訓練參數的二維嵌入的索引。
因為我們的詞匯表大小為 vocab_size=65 ,所以該嵌入表也將有65行:
class BigramLanguageModel(nn.Module):
def __init__(self, vocab_size):
super().__init__()
self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
def forward(self, idx, targets=None):
tok_emb = self.token_embedding_table(idx) # (B,T,C)
在這里,整數從嵌入表中「提取」出一行,這一行就是代表該分詞的向量。然后,該向量將作為相應時間步長的輸入輸入到Transformer。
使用BPE算法進行「字符塊」分詞
對于「字符級」語言模型的天真設置來說,這一切都很好。
但在實踐中,在最先進的語言模型中,人們使用更復雜的方案來構建這些表征詞匯。
具體地說,這些方案不是在字符級別上工作,而是在「字符塊」級別上工作。構建這些塊詞匯表的方式是使用字節對編碼(BPE)等算法,我們將在下面詳細介紹該算法。
暫時回顧一下這種方法的歷史發展,將字節級BPE算法用于語言模型分詞的論文,是2019年OpenAI發表的GPT-2論文Language Models are Unsupervised Multitask Learners。
論文地址:https://d4mucfpksywv.cloudfront.net/better-language-models/language_models_are_unsupervised_multitask_learners.pdf
向下翻到第2.2節「輸入表示」,在那里他們描述并激勵這個算法。在這一節的末尾,你會看到他們說:
詞匯量擴大到50257個。我們還將上下文大小從512增加到1024個token,并使用512更大batchsize。
回想一下,在Transformer的注意力層中,每個token都與序列中之前的有限token列表相關聯。
本文指出,GPT-2模型的上下文長度從GPT-1的512個token,增加到1024個token。
換句話說,token是 LLM 輸入端的基本「原子」。
「分詞」是將Python中的原始字符串,轉換為token列表的過程,反之亦然。
還有一個流行的例子可以證明這種抽象的普遍性,如果你也去Llama 2的論文中搜索「token」,你將得到63個匹配結果。
比如,該論文聲稱他們在2萬億個token上進行了訓練,等等。
論文地址:https://arxiv.org/pdf/2307.09288.pdf
淺談分詞的復雜性
在我們深入探討實現的細節之前,讓我們簡要地說明一下,需要詳細了解「分詞」過程的必要性。
分詞是LLM中許多許多怪異問題的核心,我建議你不要忽略它。
很多看似神經網絡架構的問題,實際上都與分詞有關。這里只是幾個例子:
- 為什么LLM不會拼寫單詞?——分詞
- 為什么LLM不能執行超簡單的字符串處理任務,比如反轉字符串?——分詞
- 為什么LLM在非英語語言(比如日語)任務中更差?——分詞
- 為什么LLM不擅長簡單的算術?——分詞
- 為什么GPT-2在用Python編碼時遇到了更多的問題?——分詞
- 為什么我的LLM在看到字符串<|endoftext|>時突然停止?——分詞
- 我收到的關于「trailing whitespace」的奇怪警告是什么?——分詞
- 如果我問LLM關于「SolidGoldMagikarp」的問題,為什么它會崩潰?——分詞
- 為什么我應該使用帶有LLM的YAML而不是JSON?——分詞
- 為什么LLM不是真正的端到端語言建模?——分詞
我們將在視頻的末尾,再回到這些問題上。
分詞的可視化預覽
接下來,讓我們加載這個分詞WebApp。
地址:https://tiktokenizer.vercel.app/
這個Web應用程序的優點是,分詞在網絡瀏覽器中實時運行,允許你輕松地在輸入端輸入一些文本字符串,并在右側看到分詞結果。
在頂部,你可以看到我們當前正在使用 gpt2 分詞器,并且可以看到,這個示例中粘貼的字符串目前正在分詞為 300個token。
在這里,它們用顏色明確顯示出來:
比如,字符串「Tokenization」編碼到token30642,其后是token是1634。
token「is」(注意,這是三個字符,包括前面的空格,這很重要!)是318。
注意使用空格,因為它在字符串中是絕對存在的,必須與所有其他字符一起分詞。但為了清晰可見,在可視化時通常會省略。
你可以在應用程序底部打開和關閉它的可視化功能。同樣,token「at」是379,「the」是262,依此類推。
接下來,我們有一個簡單的算術例子。
在這里,我們看到,分詞器對數字的分解可能不一致。比如,數字127是由3個字符組成的token,但數字677是因為有2個token:6(同樣,請注意前面的空格)和77。
我們依靠LLM來解釋這種任意性。
它必須在其參數內部和訓練過程中,了解這兩個token(6和77實際上組合成了數字677)。
同樣,我們可以看到,如果LLM想要預測這個總和的結果是數字804,它必須在兩個時間步長內輸出:
首先,它必須發出token「8」,然后是token「04」。
請注意,所有這些拆分看起來都是完全任意的。在下面的例子中,我們可以看到1275是「12」,然后「75」,6773實際上是三個token「6」、「77」、「3」,而8041是「8」、「041」。
(未完待續...)
(TODO:若想繼續文字版的內容,除非我們想出如何從視頻中自動生成)
網友在線,出謀劃策
網友表示,太好了,實際上我更喜歡閱讀這些帖子,而不是看視頻,更容易把握自己的節奏。
還有網友為Karpathy出謀劃策:
「感覺很棘手,但使用LangChain可能是可行的。我在想是否可以使用whisper轉錄,產生有清晰章節的高級大綱,然后對這些章節塊并行處理,在整體提綱的上下文中,專注于各自章節塊的具體內容(也為每個并行處理的章節生成配圖)。然后再通過LLM把所有生成的參考標記,匯編到文章末尾」。
有人為此還寫了一個pipeline,而且很快便會開源。