- เป็นการทดลองออกแบบเฉพาะน้ำหนักของ Transformer แบบ decoder-only คล้าย GPT-2 ด้วยตัวเองโดยไม่ต้องเทรน เพื่อให้ทำนายแพตเทิร์น
(aab)* - การทำนาย
"aabaabaabaab..."ต้องดู สองโทเค็น ก่อนหน้า จึงเหมาะกว่ารูปแบบสลับแบบง่ายในการแสดงให้เห็นการทำงานของ attention - โมเดลถูกจัดให้มีขนาดเล็กด้วย
N_CTX=5,N_VOCAB=2,N_EMBED=8ใช้การ tokenize แบบa=0,b=1และ one-hot embedding - attention head เดี่ยวกระจายความสนใจไปยังสองโทเค็นล่าสุดอย่างละ 0.5 และคำนวณโทเค็นถัดไปโดยใช้การหักล้างกันของ encoding แบบ
a=1,b=-1 - ในบริบทที่ไม่กำกวม ได้ความแม่นยำ 100.0% (27/27) แต่การทำนายหนึ่งครั้งบนบริบท 5 โทเค็นต้องใช้ราว 4,000 FLOPs จึงไม่มีประสิทธิภาพกว่ากฎที่เขียนเองโดยตรงมาก
Mini GPT-2 ที่กำหนดน้ำหนักเองโดยไม่ต้องเทรน
- เป้าหมายคือการทำความเข้าใจเชิงสัญชาตญาณว่าองค์ประกอบแต่ละส่วนของ Transformer และ attention ทำอะไรจริง ๆ
- โมเดลนี้ไม่ผ่านการเทรนและไม่ใช้ค่าน้ำหนักที่ pretrain มา แต่สร้างขึ้นด้วยการกำหนด น้ำหนัก แต่ละตัวเองโดยตรงภายในเย็นเดียว
- โครงสร้างเป็น Transformer แบบ decoder-only คล้าย GPT-2 และ implementation ถูกทำให้ง่ายลงโดยอิงจาก picoGPT implementation ของ jaymody
- เอา layer norm ออก
- ใช้ head เดี่ยวแทน multi-head attention
- เอา feed-forward layer
mlpของ transformer block ออก
เหตุผลที่เลือกลำดับ (aab)*
- ตอนแรกตั้งใจจะทำนายลำดับอย่าง
"ababababab"แต่เนื่องจาก transformer ทำนาย shifted sequence งานนี้จึงง่ายเกินไป- ถ้าเป็น
aก็ทำนายbไม่เช่นนั้นก็ทำนายaโดยไม่ต้องใช้ position embedding
- ถ้าเป็น
- งานสุดท้ายจึงถูกกำหนดเป็นการทำนายลำดับ
"aabaabaabaab..."หรือ(aab)*- ถ้าสองโทเค็นก่อนหน้าเป็น
abหรือbaโทเค็นถัดไปคือa - ถ้าสองโทเค็นก่อนหน้าเป็น
aaโทเค็นถัดไปคือb bbถือเป็นกรณีนอกขอบเขตของงาน
- ถ้าสองโทเค็นก่อนหน้าเป็น
- การ tokenize เป็นแบบง่ายที่จัดการเพียงสองสัญลักษณ์
aคือ0bคือ1
มิติของโมเดลและลำดับการคำนวณ
- พารามิเตอร์ของโมเดลที่เลือกมีสามค่า
N_CTX = 5: ความยาวบริบทสูงสุดที่โมเดลมองเห็นในครั้งเดียวN_VOCAB = 2: สองโทเค็นคือa,bN_EMBED = 8: ขนาด embedding ที่บรรจุพื้นที่สำหรับโทเค็น ตำแหน่ง และการคำนวณ
- งานจริงต้องการเพียงสองโทเค็นก่อนหน้า แต่ตั้ง
N_CTX=5เพื่อรวมสถานการณ์ที่ต้องละเว้นโทเค็นที่ไม่เกี่ยวข้องด้วย - ฟังก์ชัน
gptทำงานตามลำดับต่อไปนี้- บวก token embedding และ position embedding ด้วย
wte[inputs] + wpe[range(len(inputs))] - ส่งผ่าน transformer block หนึ่งชั้น
- สุดท้ายสร้าง logits ใน vocabulary space ด้วย
x @ wte.T
- บวก token embedding และ position embedding ด้วย
Embedding ที่เก็บตำแหน่งและโทเค็นแบบ one-hot
wpeคือ position embedding โดยใช้ 5 มิติแรกของ embedding เป็น position one-hot- position 0 คือ
[1, 0, 0, 0, 0] - position 4 คือ
[0, 0, 0, 0, 1]
- position 0 คือ
wteคือ token embedding โดยใช้ 2 มิติถัดไปเป็น token one-hot- token
aคือ[1, 0]ในมิติของโทเค็นนั้น - token
bคือ[0, 1]
- token
- ตำแหน่ง embedding ที่ 8 ตอนแรกไม่ถูกใช้ และใช้เป็น scratch space ภายใน transformer block
- ตัวอย่างเช่น
"aabaa"ถูกแทนด้วยเมทริกซ์ embedding ขนาด5 x 8โดยแต่ละแถวมีทั้ง position one-hot และ token one-hot
วิธีที่ attention head เลือกสองโทเค็นล่าสุด
- transformer block ประกอบด้วย attention head หนึ่งตัวและชั้นเชิงเส้น
c_projที่ส่งผลลัพธ์ attention กลับไปยัง embedding space c_attnเป็นชั้นเชิงเส้นขนาดembed_size x (embed_size * 3)ที่แปลง input embedding เป็นเมทริกซ์qkvแล้วแยกเป็นq,k,vkแยก position embedding ออกมาเพื่อแทน ข้อมูลตำแหน่ง ที่แต่ละโทเค็นมีqแทนช่วงตำแหน่งที่แต่ละตำแหน่งต้องการค้นหา และสร้างเมทริกซ์ attention score ผ่านq @ k.T- หลัง softmax และ causal mask เมทริกซ์ attention มีคุณสมบัติดังนี้
- แถวแรกมี attention 100% ไปยังโทเค็นแรกเท่านั้น
- แถวถัด ๆ ไปมี attention อย่างละ
0.5ไปยังสองโทเค็นล่าสุดที่เข้าถึงได้
- causal mask จะบวกค่าที่เล็กมากให้ตำแหน่งของโทเค็นในอนาคต ในโค้ดจริงใช้ค่าอย่าง
-1e10เพื่อไม่ให้มองเห็นโทเค็นอนาคต- โมเดลที่ทำด้วยมือนี้ไม่ได้ถูกออกแบบให้แอบดูอนาคต แต่ยังคง mask ไว้เพื่อให้ใกล้เคียงโครงสร้าง GPT-2
- การ scale ด้วยการหาร
np.sqrt(q.shape[-1])ช่วยปรับปรุง gradient ในการเทรนจริง แต่ไม่มีผลกับโมเดลทำมือชุดนี้
การทำนายที่สร้างจาก v encoding และการหักล้างด้วยการบวก
vแปลง token one-hot เป็น encoding แบบa=1,b=-1- เนื่องจากผลลัพธ์ attention เฉลี่ยสองโทเค็นล่าสุดอย่างละ 0.5 encoding นี้จึงคำนวณกฎต่อไปนี้
a, b→0.5 * 1 + 0.5 * (-1) = 0b, a→0.5 * (-1) + 0.5 * 1 = 0a, a→0.5 * 1 + 0.5 * 1 = 1
- ผลคือเกิดค่าต่อไปนี้ในตำแหน่งที่ 7 ของแถว
- ถ้าเป็น
0คือกรณีที่ควรทำนายa - ถ้าเป็น
1คือกรณีที่ควรทำนายb
- ถ้าเป็น
- สำหรับอินพุต
"aabaa"แถวแรกมีข้อมูลไม่พอ จึงอาจเกิดการทำนายbแต่การทำนายถัด ๆ ไปตรงกับกฎ(aab)*
ส่งค่าทำนายกลับไปยัง vocabulary space
c_projแปลงค่าที่ตำแหน่งที่ 7 ของผลลัพธ์ attention กลับเป็นรูปแบบ token one-hot- ไม่ได้สร้างเพียง
[..., 1, 0, ...]หรือ[..., 0, 1, ...]แต่สร้าง one-hot ที่ scale ด้วย1024embedding[row, 5] = 1024 + (-1024) * predictionembedding[row, 6] = 0 + 1024 * prediction
- transformer block มี residual connection จึงบวก embedding เดิมด้วย
x = x + causal_self_attention(...) - เนื่องจาก residual signal ที่ไม่จำเป็นยังคงเหลืออยู่ จึงใช้ scale
1024เพื่อกลบสัญญาณนี้ - สุดท้ายคำนวณ
x @ wte.Tเพื่อสร้าง logits และใช้ softmax- ในบริบท
"aabaa"แถวทำนายสุดท้ายชี้ไปที่b - ตอนเทรน การทำนายทุกแถวมีประโยชน์ แต่ตอน inference ต้องการเฉพาะแถวสุดท้าย
- ในบริบท
ผลการสร้างและความแม่นยำ
- ฟังก์ชัน
completeใส่โทเค็นล่าสุดไม่เกิน 5 ตัวเข้าโมเดล แล้วเลือกโทเค็นถัดไปด้วยargmaxจากแถวสุดท้ายของผลลัพธ์ softmax - ตัวอย่างผลการสร้างมีดังนี้
complete("a")→a :: baabaabaabcomplete("ba")→ba :: abaabaabaacomplete("abaab")→abaab :: aabaabaaba
- แม้กับอินพุตนอกขอบเขต ก็มีบางกรณีที่กู้กลับมาเป็นแพตเทิร์นซ้ำได้
complete("ababa")→ababa :: abaabaabaacomplete("bbbbb")→bbbbb :: aabaabaaba
- เมื่อประเมินเฉพาะบริบทที่ไม่กำกวมในการทดสอบ
"aab" * 10ความแม่นยำคือ 100.0% (27/27)
ความต่างระหว่าง 4,000 FLOPs กับ 8 คำสั่ง
- เมื่อใช้บริบทเต็ม 5 โทเค็น โมเดลนี้ต้องใช้ประมาณ 4,000 floating point operations สำหรับการทำนายโทเค็นเดียว
- ส่วนใหญ่ถูกใช้ในการคำนวณ attention
- สามารถลดลงได้ด้วยการลด context window, fused multiply-add, kv caching ฯลฯ
- แต่การทำนายโทเค็นเดียวยังต้องใช้คำสั่งของเครื่องหลายร้อยคำสั่งอยู่ดี
- x64 assembly ที่เขียนกฎ
(aab)*เดียวกันโดยตรงคำนวณโทเค็นถัดไปได้ด้วย 8 คำสั่ง - จึงยังเหลือคำถามว่าเราสามารถเทรน language model ที่มีประสิทธิภาพกว่ารุ่นปัจจุบัน 1000 เท่าสำหรับการสร้างภาษาธรรมชาติได้หรือไม่
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
มีงานที่เกี่ยวข้องชื่อ "Thinking Like Transformers"
งานนี้แนะนำภาษาโปรแกรมมิงพื้นฐานชื่อ RASP ซึ่งประกอบด้วยการดำเนินการที่สามารถจำลองด้วยองค์ประกอบของ Transformer ได้ และแสดงให้เห็นว่าสามารถเขียนโปรแกรมอย่างฮิสโตแกรมหรือการจัดเรียงได้
นอกจากนี้ยังมีบล็อกโพสต์ยอดเยี่ยมของ Sasha Rush และ Gail Weiss และงานวิจัยต่อเนื่องก็แสดงให้เห็นว่าสามารถคอมไพล์โปรแกรมตระกูล RASP ให้เป็น น้ำหนักโมเดล จริงได้โดยไม่ต้องฝึก
[1] https://arxiv.org/abs/2106.06981
[2] https://srush.github.io/raspy/
[3] https://arxiv.org/abs/2301.05062
ถ้าสนใจด้านนี้ งาน HandCrafted Transformers ของผม/ฉันก็น่าลองดูเช่นกัน เป็นงานที่เลือกน้ำหนักของโมเดล Transformer เองโดยตรง เพื่อให้ทำ การบวกเลขยาว ในลักษณะคล้ายกับวิธีที่คนเรียนในโรงเรียนประถม
[1] https://colab.research.google.com/github/newhouseb/handcraft...
ทิศทางจากเครือข่ายประสาทไปสู่โค้ดก็น่าจะน่าสนใจมากในแง่ของความสามารถในการอธิบาย
คิดว่าตัวเองเข้าใจ Transformer ดีแล้ว แต่ไม่เคยลอง implement เองจริง ๆ
วันหนึ่งพอลอง implement เอง ก็พบว่ามันทำงานหรือฝึกได้ไม่ดีเท่า PyTorch Transformer มาตรฐาน และสุดท้ายถึงได้รู้ว่าสาเหตุคือมองข้าม dropout ไป
ตอนนั้นฝึกให้มันบวกเลข และไม่เคยให้เห็นคู่ตัวเลขเดียวกันซ้ำสองครั้ง เลยคิดว่า overfitting เป็นไปไม่ได้ แต่บทบาทของ dropout ใหญ่กว่าที่คิดมาก
สรุปคือควรลอง implement Transformer เอง และยิ่งทำจากระดับพื้นฐานเท่าไรก็ยิ่งดี
คนที่เคยทำทุกคนได้เรียนรู้อะไรที่คาดไม่ถึงกันหมด และแต่ละคนก็เข้าใจคนละจุด ตั้งแต่การ parallelize การเรียนรู้ในระดับโทเค็น ไปจนถึงว่า backpropagation ทำงานจริงอย่างไร
เอกสารของ Karpathy ก็ดีเหมือนกัน แต่สิ่งที่ทำให้ผม/ฉันเข้าใจ Transformer ได้ในที่สุดคือวิดีโอนี้: https://youtu.be/kWLed8o5M2Y?si=SJT5_lCJ0hSR7Z_k
เคยคิดคล้าย ๆ กันมาพักหนึ่งแล้ว
จะสร้าง อินเทอร์เฟซเชิงสัญชาตญาณ สำหรับน้ำหนักของโมเดล ที่ผู้เชี่ยวชาญโดเมนสามารถปรับด้วยมือเพื่อเร่งการฝึกได้ไหม?
ตัวอย่างเช่น ในโมเดลด้านภาพ เวลาตรวจจับกรวยจราจร ก็เพิ่มชุดน้ำหนักที่สอดคล้องกับ “ความเป็นสีส้ม”
แบบนั้นมนุษย์ก็จะช่วยเร่งได้ แทนที่จะต้องขอตัวอย่างเพิ่มอีกเป็นพัน ๆ หรือเป็นล้าน ๆ เพื่อปรับเทียบ “ความเป็นสีส้ม” ให้ถูกต้อง
แน่นอนว่าสิ่งที่ยากคืออินเทอร์เฟซนี้ต้องแมปไปยังชุดน้ำหนักที่มีความหมายแตกต่างกัน เลยสงสัยว่ามีเหตุผลทางเทคนิคอะไรที่ทำให้เป็นไปไม่ได้หรือไม่
[1] https://www.youtube.com/watch?v=8SF_h3xF3cE&t=1358s
พูดสั้น ๆ คือ วิธีที่ให้มนุษย์เข้ามาช่วย AI แทบจะมีความคุ้มค่าต่อต้นทุนน้อยกว่าการใช้พลังประมวลผลมากขึ้นเสมอ
ระหว่างที่มนุษย์ปรับชั้นน้ำหนักให้ตรวจจับกรวยจราจรสีส้ม คลัสเตอร์ GPU ก็ฝึก AI ให้ตรวจจับกรวยจราจร ไฟจราจร ต้นไม้ รถคันอื่น และกรวยจราจรสีส้มที่เฉดต่างออกไปเล็กน้อยไปเรียบร้อยแล้ว
แม้จะปรับภาพให้มองสีส้มได้ดีขึ้น แต่ถ้าไม่สามารถเฝ้าดูความแม่นยำของสีอื่นทั้งหมดไปพร้อมกันได้ ก็มีโอกาสสร้างปัญหากับสีอื่นโดยไม่รู้ตัว
ไม่ใช่ว่าคลัสเตอร์ของนิวรอนบางกลุ่มจะสอดคล้องกับแนวคิดเฉพาะบางอย่าง แต่โดยรวมแล้วทุกอย่างทำทุกงานทีละนิด
มีพารามิเตอร์ที่เกี่ยวข้องมากเกินไป
เปเปอร์ Transformer เป็นเทคนิคมากเกินไป เลยอยากเข้าใจแม้เพียงผิวเผินมาตลอด แต่ก็ยาก
บทความนี้ช่วยให้เข้าใจวิธีการทำงานได้จริง ๆ และอย่างน้อยตัวอย่างก็ชัดเจนมาก
ยังทำให้นึกถึง เมทริกซ์ ที่เคยเรียนตอนมหาวิทยาลัยขึ้นมาได้ด้วย
มันไม่ใช่ เครื่องจักรเชิงนามธรรม แบบหนึ่ง คล้ายกับเครื่องจักร Turing หรือเครื่องจักรที่พาร์ส regex หรอกหรือ?
ก่อนอื่นให้กำหนดรายการโทเคน เช่น เพื่อให้ง่าย สมมติว่าเป็นตัวอักษร 24 ตัว
เครื่องจักรนี้รับลำดับอินพุตของโทเคน ทำการคำนวณเมทริกซ์แบบกำหนดแน่นอน แล้วส่งออกรายการความน่าจะเป็นของโทเคนทั้งหมด
“การเรียนรู้” เป็นเพียงกระบวนการตั้งค่าตัวเลขบางส่วนในเมทริกซ์ที่ใช้ในการคำนวณนั้นเท่านั้น
น่าสังเกตว่าในโค้ดสุดท้ายมีคำสั่ง
ifเพียงคำสั่งเดียว และนั่นก็ใช้เพื่อประเมินความถูกต้องของผลลัพธ์“ตรรกะ” ทั้งหมดมาจาก ผลลัพธ์ของการคำนวณเมทริกซ์
ในโครงข่ายประสาท ทุกอย่างโดยมากจะค่อนข้างคลุมเครือ และแทบไม่มีสิ่งอย่าง
if/elseแต่ก็มีกรณีที่ “มาสก์” ค่าเป็น 0 หรือ -∞ เหมือนในตัวอย่าง Transformerเอาต์พุตก็มักจะเป็นชุดคะแนนหรือความน่าจะเป็นเกือบเสมอ ดังนั้นถ้าโมเดลที่แยกรูปแมวกับสุนัขให้ผลลัพธ์อย่าง
dog:0.95 cat:0.05เราจะบอกว่ามันทำนายว่าเป็นสุนัข เพราะคะแนนสุนัขสูงกว่ากลไก attention ซึ่งเป็นหัวใจของ Transformer อิงอยู่กับการคำนวณแบบ การค้นหาแบบนุ่มนวล ชนิดหนึ่ง
ถ้าเป็นระบบที่ไม่คลุมเครือ ก็คงวนผ่านโทเคนแต่ละตัวในลำดับ ตรวจสอบว่าเกี่ยวข้องกับโทเคนปัจจุบันหรือไม่ และถ้าเกี่ยวข้องก็ทำอะไรบางอย่าง แต่ใน Transformer ความเกี่ยวข้องไม่ใช่การตัดสินใจแบบไบนารี
แต่จะคำนวณคะแนนความเกี่ยวข้องแบบต่อเนื่องระหว่างคู่โทเคนทั้งหมดในลำดับ และใช้คะแนนนั้นเพื่อทำขั้นตอนถัดไป
อย่างไรก็ตาม บางอย่างก็ไม่ง่ายที่จะทำให้เป็นการขยายโดยตรงจากระบบที่อิงการตัดสินใจแบบไบนารี
เช่น คะแนนความเกี่ยวข้องเหล่านั้นถูกใช้เป็นน้ำหนักสำหรับคำนวณค่าเฉลี่ยถ่วงน้ำหนักของโทเคนในคลังคำศัพท์ และได้ “โทเคนเฉลี่ย” สำหรับตำแหน่งปัจจุบัน
ดูเหมือนไม่มีวิธีง่าย ๆ ที่จะตีความสิ่งนี้ว่าเป็นส่วนขยายของกระบวนการที่อิงตรรกะการแตกแขนง
ลองดูเปเปอร์นี้ที่อธิบายว่า Linear Transformers แท้จริงแล้วคือ Fast Weight Programmers: https://arxiv.org/abs/2102.11174
ถ้าตั้งค่าน้ำหนักอย่างระมัดระวัง ก็ทำให้มันทำการคำนวณอะไรก็ได้
เพียงแต่อยากให้มีคอมไพเลอร์ที่ไม่ได้อิงการประมาณค่า
สงสัยว่าคำว่า “อาจทำให้คุณอยากลองสร้างโมเดลเอง” นั้นจะเอาไปใช้ทำอะไร นอกจากเป็นแบบฝึกหัดการเรียนรู้เพื่อสนองความอยากรู้อยากเห็น
เริ่มรู้สึกว่า โมเดลแมชชีนเลิร์นนิง ที่ซับซ้อนนั้นไม่สมจริงสำหรับคนที่อ่านบล็อกจากที่บ้านจะจัดการได้
มันสร้างคำอังกฤษโบราณที่ดูน่าเชื่อได้มากมาย และเรียนรู้พื้นฐานไวยากรณ์อังกฤษ รวมถึงรูปแบบของบทละคร เป็นต้น
ค่อนข้างน่าทึ่งที่ไปถึงระดับนั้นได้ในเวลาอันสั้นขนาดนั้น
ถ้าลองเทรนโมเดลหลายตัวในเครื่องจนได้ระดับความเที่ยงตรงแบบ Shakespeare-from-Wish.com ก็น่าจะช่วยตัดสินได้ว่าเราเจอสถาปัตยกรรมที่ดีหรือยัง และถึงเวลาลองขยายสเกลหรือไม่
เขาบอกว่าอยากเข้าใจ Transformer และ attention ให้ดียิ่งขึ้น และถึงจะอ่าน The Illustrated Transformer แล้ว ก็ยังไม่รู้สึกเข้าใจโดยสัญชาตญาณว่าชิ้นส่วนต่าง ๆ ของ attention ทำอะไรกันแน่
ประมาณว่าความแตกต่างระหว่าง
qกับkยังไม่ต้องพูดถึงvเลยมันช่วยสร้างและทำให้ความเข้าใจลึกขึ้น มากกว่าการสนองความอยากรู้อยากเห็นเฉย ๆ
ถ้าใส่คำอย่าง neural network ไว้ในชื่อเรื่องได้ก็คงดี
เรื่องนี้เกี่ยวกับสถาปัตยกรรม “Transformer” ในแมชชีนเลิร์นนิง ไม่ใช่ชุดขดลวดที่ใช้เชื่อมวงจรสองวงเข้าด้วยกันทางแม่เหล็กไฟฟ้า