Chuyển tới nội dung chính

RoPE và chuyện mã hoá vị trí bằng phép quay

Phép quay thậtBất biến kiểm đượcSáu tham số sửa được

RoPE và chuyện mã hoá vị trí bằng phép quay

Bạn đã học cách cộng một vector sin cos vào embedding để mô hình biết token nằm ở đâu. RoPE làm một việc khác hẳn: nó quay. Và cái được của việc quay tính ra được bằng số, không cần tin ai.

positional encoding bạn đã gặp cách làm nguyên bản của transformer: sinh một vector sin cos theo vị trí rồi cộng nó vào embedding của token. Cách đó chạy được, nhưng nó có một chỗ hơi vô lý mà lúc học lần đầu ít ai để ý. Self-attention tính điểm số bằng tích vô hướng giữa truy vấn và khoá, tức là nó quan tâm hai token cách nhau bao xa. Vậy mà cái ta nhồi vào lại là vị trí tuyệt đối của từng token, rồi để mạng tự học cách trừ hai vị trí đó ra. Ta đưa cho nó dữ kiện sai loại và hy vọng nó tự xoay xở.

RoPE, tức rotary position embedding, đổi đúng chỗ đó. Nó không cộng gì vào. Nó chia vector ra thành từng cặp chiều, coi mỗi cặp như một điểm trên mặt phẳng, rồi quay cặp đó một góc tỉ lệ với vị trí của token. Cặp thứ i quay với tốc độ riêng theta_i, nên ở vị trí m nó quay góc m × theta_i. Nghe như một mẹo hình học, nhưng nó dẫn tới một tính chất chính xác tuyệt đối, và đó là thứ cả bài này xoay quanh: tích vô hướng giữa truy vấn ở vị trí m và khoá ở vị trí n chỉ phụ thuộc hiệu m - n, không phụ thuộc m hay n riêng lẻ.

RoPE · mã hoá vị trí bằng phép quay, không phải phép cộng
Điểm ở hiệu 5: -0.709554
1 · Một cặp chiều, một phép quay
chiều 0chiều 1
truy vấn q trước khi quay truy vấn ở vị trí 7 khoá k ở vị trí 2
Tần số của cặp 0: 10000^(-2 × 0 / 8)1.00000000
Góc quay của truy vấn: 7 × theta, gấp lại còn 41.07°7.000000 rad
Góc quay của khoá: 2 × theta2.000000 rad
Bán kính cặp truy vấn trước rồi sau khi quay, lệch 1.1e-161.0000001.000000
Góc giữa hai mũi tên đã quay. Dời cả hai vị trí là góc này không đổi163.52°
CặpChiềutheta_iGóc quay (rad)VàoRaBán kính lệch
00, 11.0000007.0000(1.000, 0.000)(0.754, 0.657)1.1e-16
12, 30.1000000.7000(0.500, 0.500)(0.060, 0.705)0.0e+0
24, 50.0100000.0700(1.000, -1.000)(1.067, -0.928)0.0e+0
36, 70.0010000.0070(0.250, 0.000)(0.250, 0.002)0.0e+0
2 · Điểm số chỉ phụ thuộc hiệu m - n
Chưa quay gì: q · k0.500000000
Ở vị trí m = 7 n = 2, hiệu 5-0.709554095
Dời cả hai đi 100: m = 107n = 102, hiệu 5-0.709554095
Chênh lệch giữa hai dòng trên1.110e-16
Đây là điều đáng nhìn nhất trang này. Hai vị trí tuyệt đối đã đổi từ 72 thành 107102, mọi vector đã quay sang một chỗ khác hẳn, vậy mà điểm số lệch đúng 1.110e-16, tức nhiễu dấu phẩy động chứ không phải khác biệt thật. Cái duy nhất không đổi là hiệu 5, và điểm số chỉ nhìn thấy đúng cái đó. Đổi ô Vị trí khoá n để đổi hiệu thì điểm số nhảy ngay.
Cặptheta_iGóc hiệu (m-n) × theta_iPhần thẳng q·kPhần chéoGóp vào điểm
01.0000005.0000000.00001.0000-0.958924
10.1000000.5000000.5000-0.50000.199079
20.0100000.0500000.00001.00000.049979
30.0010000.0050000.00000.06250.000312
Tổngcộng lại đúng bằng điểm số ở bảng trên-0.709554
góc hiệu = (7 - 2) × theta_i = 5 × theta_iđộ dài q giữ nguyên: 1.8874591.887459, lệch 0.0e+0độ dài k giữ nguyên: 1.6007811.600781, lệch 0.0e+0
3 · Cặp đầu quay rất nhanh, cặp cuối gần như đứng yên
CặpChiềutheta_iBước sóng (token)Số vòng trong cửa sổGóc ở vị trí 7
00, 11.000000006.28325.949341.070°
12, 30.1000000062.8332.594940.107°
24, 50.01000000628.323.25954.011°
3chậm hơn cửa sổ6, 70.001000006.28 nghìn0.32590.401°
Cặp nhanh nhất i = 0, bước sóng6.28 token
Cặp chậm nhất i = 3, bước sóng6.28 nghìn token
Cửa sổ ngữ cảnh đang đặt là 2.048 token, cặp chậm nhất đi được0.3259 vòng
Tỉ số bước sóng cặp cuối trên cặp đầu, tức base^((d-2)/d)×1000.00
1 trong 4 cặp mà bước sóng dài hơn cả cửa sổ 2.048 token. Những cặp đó chưa quay hết một vòng trong toàn bộ chuỗi, nên trên thực tế chúng gần như không đổi từ đầu tới cuối ngữ cảnh: chúng mang thông tin nội dung chứ gần như không mang thông tin vị trí. Kéo ô Cửa sổ ngữ cảnh lên là con số này tụt xuống.
Sim này không đo được cái gì. Nó tính đúng phép quay, đúng tần số theo từng chiều, và đúng tính chất điểm số chỉ phụ thuộc hiệu vị trí. Cả ba thứ đó là đại số, kiểm được đến chữ số cuối, và cổng kiểm số của bài đang canh chúng. Nhưng nó không nói được RoPE tốt hơn cách cộng sin cos ở chỗ nào về chất lượng mô hình. Câu đó là một khẳng định thực nghiệm: muốn biết thì phải huấn luyện hai mô hình giống hệt nhau trừ chỗ mã hoá vị trí rồi đo, mà đó là việc không tab trình duyệt nào làm được.
Ba chỗ nữa cần nói thẳng. Thứ nhất, cách ghép cặp ở đây là (0,1), (2,3), ..., còn nhiều bản cài ghép (i, i + d/2). Hai cách quay cùng những góc như nhau nên tính chất phụ thuộc khoảng cách y hệt, chỉ khác cách đánh số chiều. Thứ hai, RoPE tác động lên truy vấn và khoá, không tác động lên giá trị: sim này vì thế chỉ có hai ô vector chứ không có ô thứ ba, và đó là đúng cơ chế chứ không phải rút gọn. Thứ ba, con số base bằng 10000 là lựa chọn của bài báo gốc, không phải hằng số vật lý; nhiều mô hình ngữ cảnh dài đổi nó lên hàng trăm nghìn và đó là chuyện của bài nới ngữ cảnh.

Phép quay, viết ra cho rõ

Cho vector xd chiều với d chẵn. Ghép nó thành d / 2 cặp: (x_0, x_1), (x_2, x_3), và cứ thế. Cặp thứ i có tần số riêng

theta_i = base^(-2i/d)

với base mặc định là 10000, đúng con số của bài báo gốc. Ở vị trí m, cặp thứ i quay góc m × theta_i, tức là

x_2i mới = x_2i × cos(m θ_i) - x_2i+1 × sin(m θ_i)

x_2i+1 mới = x_2i × sin(m θ_i) + x_2i+1 × cos(m θ_i)

Hết. Không có phép cộng nào ở đây, và không có tham số nào phải học: theta_i là một công thức đóng.

Cấu hình mở trang có d bằng 8 và base bằng 10000, và đó là một cặp số dễ chịu để tính tay: -2i/8 bằng -i/4, còn 10000^(-1/4) bằng đúng 1/10. Nên bốn tần số là 1, 0,1, 0,01, 0,001, tròn trịa. Vector truy vấn mặc định là 1, 0, 0.5, 0.5, 1, -1, 0.25, 0, ở vị trí m bằng 7.

Nhìn hình tròn. Cặp 0 có theta_0 bằng 1, nên góc quay là 7 × 1 bằng đúng 7 radian. Cặp đó vào là (1, 0), tức mũi tên nằm ngang, dài 1. Quay 7 radian thì nó ra (cos 7, sin 7) bằng (0,753902, 0,656987), và vì 7 lớn hơn bằng 6,2832 nên thực tế nó đã đi hết một vòng rồi đi thêm 41,07 độ nữa. Kéo ô Vị trí truy vấn m là mũi tên đỏ chạy vòng quanh, còn đường tròn nét đứt thì đứng yên: mũi tên không bao giờ rời đường tròn đó, vì quay không đổi độ dài. Cột Bán kính lệch trong bảng ngay dưới ghi con số lệch thật, và nó ở mức 1e-16, tức nhiễu của số thực máy tính.

Cặp cuối, cặp 3, có theta_3 bằng 0,001 nên ở cùng vị trí 7 nó chỉ quay 0,007 radian, tức 0,4 độ. Cùng một token, cùng một vị trí, mà cặp đầu đã đi hơn một vòng còn cặp cuối gần như chưa nhúc nhích. Ý này quay lại ở mục cuối.

Tính chất then chốt, và cách tự thấy nó bằng số

Đây là mục quan trọng nhất của bài, nên hãy làm theo từng bước thay vì đọc suông.

Bảng 2 trong sim in ba con số. Dòng đầu là tích vô hướng chưa quay gì, q · k, và với hai vector mặc định nó bằng đúng 0,500000. Dòng hai là điểm số sau khi quay truy vấn theo vị trí m bằng 7 và quay khoá theo vị trí n bằng 2: -0,709554. Vậy là phép quay đổi điểm số rất mạnh, từ dương 0,5 sang âm 0,71, không phải một hiệu chỉnh nhỏ.

Dòng ba mới là chỗ để dừng lại. Nó lấy đúng hai vector đó, nhưng dời cả hai vị trí đi 100: truy vấn sang vị trí 107, khoá sang vị trí 102. Mọi cặp chiều bây giờ nằm ở một chỗ hoàn toàn khác trên đường tròn của nó, hình vẽ đổi hẳn. Điểm số thì vẫn là -0,709554. Dòng cuối in chênh lệch giữa hai dòng, và nó cỡ 1e-16, tức bằng 0 trong khả năng biểu diễn của máy.

Bạn thử tiếp: đổi ô Dời cả hai một lượng s sang -1000, sang 4096, sang bất cứ số nào. Điểm số không nhích. Rồi đổi ô Vị trí khoá n từ 2 sang 3, tức chỉ đổi hiệu từ 5 xuống 4: điểm số nhảy ngay. Vị trí tuyệt đối vô hình, khoảng cách thì không.

Vì sao lại thế, và tại sao nó chính xác chứ không xấp xỉ? Vì hai phép quay liên tiếp thì góc cộng lại, nên nghịch đảo của một phép quay góc a là phép quay góc -a. Với R_a là phép quay góc a trên mặt phẳng, ta có R_a chuyển vị nhân R_b bằng R_(b-a). Áp vào một cặp chiều, truy vấn quay m θ còn khoá quay n θ:

(R_(mθ) a) · (R_(nθ) b) = a chuyển vị × R_((n-m)θ) × b

Hai vị trí chỉ còn xuất hiện qua hiệu của chúng. Cộng lại trên mọi cặp, tổng cũng chỉ phụ thuộc hiệu. Đây là một đẳng thức đại số, không phải một xu hướng thống kê, nên nó đúng với mọi q, mọi k, mọi m, mọi n.

Bảng dưới bảng số đó bóc điểm số ra theo từng cặp, và cột Góc hiệu là bằng chứng trực quan: với hiệu bằng 5, bốn cặp nhìn thấy các góc 5, 0,5, 0,05, 0,005, đúng là hiệu nhân với tần số riêng của từng cặp. Vị trí 7 và 2 không xuất hiện ở đâu trong cột đó cả. Bốn cột Góp vào điểm-0,958924, 0,199079, 0,049979, 0,000312, cộng lại đúng bằng -0,709554. Cặp 0 một mình đủ dìm điểm số xuống dưới 0, vì nó là cặp quay nhanh nhất nên nó cảm nhận khoảng cách 5 mạnh nhất.

Cổng kiểm số của bài khẳng định tính chất này trên lưới 245 bộ (m, n, s), sai số 1e-12, và giá trị mong đợi được tính bằng một cài đặt độc lập viết riêng trong file kiểm, dùng công thức góc hiệu chứ không quay vector nào. Nói cách khác, hai đường đi khác nhau tới cùng một con số, chứ không phải một bản cài tự soi gương.

Cặp đầu quay nhanh, cặp cuối gần như đứng yên

Bảng 3 là mặt còn lại của cùng một công thức. Vì theta_i giảm theo i, mỗi cặp có một bước sóng riêng, tức số token cần đi qua để cặp đó quay trọn một vòng:

bước sóng của cặp i = 2π / theta_i = 2π × base^(2i/d)

Với d bằng 8 và base bằng 10000, bốn bước sóng là 6,28 · 62,83 · 628,32 token rồi tới 6,28 nghìn token, và cột bước sóng in đúng bốn chuỗi đó. Cặp 0 quay trọn một vòng cứ mỗi 6,28 token, tức nó phân biệt được "cách nhau 1 token" với "cách nhau 2 token" rất rõ, nhưng nó không phân biệt được "cách nhau 3" với "cách nhau 3 cộng 6,28", vì nó đã quay về đúng chỗ cũ. Cặp 3 thì ngược lại: bước sóng 6,28 nghìn token, và với cửa sổ mặc định 2048 token thì nó đi được 0,3259 vòng, chưa tới một phần ba vòng, trong toàn bộ chuỗi.

Đây là chỗ đáng nhấn. Với cửa sổ 2048 token, sim đánh dấu 1 trong 4 cặp là chậm hơn cửa sổ. Những chiều đó gần như không đổi từ token đầu tới token cuối của cả ngữ cảnh, nên trên thực tế chúng gần như không mang thông tin vị trí: chúng chở nội dung. Nghĩa là RoPE không phải một cái núm duy nhất, nó là một dải thang đo: vài cặp lo khoảng cách gần, vài cặp lo khoảng cách xa, và vài cặp cuối thì thôi không lo nữa.

Muốn thấy con số của một mô hình thật cỡ nào thì đặt ô Chiều d lên 128 và giữ base bằng 10000, đây là hình dạng một đầu chú ý rất thường gặp. Cặp cuối lúc đó là cặp 63, tần số 10000^(-126/128), bước sóng 2π × 10000^(126/128) bằng 54.410,14 token, tức khoảng 54,41 nghìn token. Đặt cửa sổ là 8192 thì cặp đó cần hơn 6,6 lần cả cửa sổ mới quay hết một vòng, và 14 trong 64 cặp rơi vào diện chậm hơn cửa sổ. Bảng chỉ vẽ 12 dòng đầu và nói thẳng là đã cắt 52 dòng, nhưng cặp cuối vẫn được báo riêng ở khối số ngay dưới, chính để phần cắt không giấu mất điều đáng xem nhất.

Ô Cơ số base là cái điều khiển toàn bộ dải này. Dòng cuối khối số in tỉ số bước sóng cặp cuối trên cặp đầu, và nó bằng base^((d-2)/d): với d bằng 8 và base bằng 10000 thì đúng 1000,00 lần. Đặt base về 1 thì mọi tần số bằng nhau, dải sụp xuống còn 1,00, mọi cặp quay cùng tốc độ, và RoPE mất hẳn tính nhiều thang đo. Sim không chặn cấu hình đó, nó chỉ nói ra: số vẫn đúng, chỉ là cơ chế không còn thú vị.

RoPE tác động lên truy vấn và khoá, không lên giá trị

Chi tiết này hay bị bỏ qua và nó không phải chuyện nhỏ. Điểm số chú ý sinh ra từ tích vô hướng của truy vấn với khoá, nên đó là đúng hai chỗ mà thông tin vị trí cần có mặt. Vector giá trị thì không tham gia phép tính điểm số nào, nó chỉ là thứ được lấy ra sau khi trọng số đã tính xong. Quay nó không giúp gì cho việc mã hoá khoảng cách mà chỉ làm méo thứ mạng sẽ đọc.

Đó là lý do sim này chỉ có hai ô vector, qk, chứ không có ô thứ ba. Đấy không phải rút gọn cho gọn bài: đấy đúng là cơ chế.

Sim này không nói được gì

Cần rạch ròi chỗ này, vì nó là ranh giới giữa cái bài đo được và cái bài không đo được.

Cái sim tính đúng là ba thứ, và cả ba đều là đại số kiểm được đến chữ số cuối: phép quay từng cặp theo công thức trên, tần số và bước sóng của từng chiều, và tính chất điểm số chỉ phụ thuộc hiệu vị trí. Cổng kiểm số đang canh cả ba, gồm bốn ca vàng: quay góc 0 trả về đúng vector ban đầu, quay m rồi quay n bằng quay m + n, quay m rồi quay -m về đúng gốc, và độ dài vector không đổi với mọi m.

Cái sim không nói được là RoPE tốt hơn cách cộng sin cos ở chỗ nào về chất lượng mô hình. Không có một con số nào trên trang này ủng hộ hay phản đối câu đó. "Chỉ phụ thuộc khoảng cách" là một tính chất toán học đẹp và nó là lý do người ta thử RoPE, nhưng đẹp về toán không tự động thành tốt về perplexity. Muốn biết thì phải huấn luyện hai mô hình giống hệt nhau trừ đúng chỗ mã hoá vị trí, trên cùng dữ liệu, cùng ngân sách, rồi đo. Đó là thực nghiệm, và không tab trình duyệt nào làm được.

Ba đơn giản hoá nữa phải nói thẳng.

  • Cách ghép cặp là một quy ước. Ở đây tôi ghép (0,1), (2,3), .... Nhiều bản cài thật ghép (i, i + d/2), tức nửa đầu với nửa sau. Hai cách quay cùng những góc như nhau nên tính chất phụ thuộc khoảng cách y hệt, chỉ khác cách đánh số chiều, nhưng nếu bạn đối chiếu số với một bản cài cụ thể thì phải biết nó ghép kiểu nào.
  • base bằng 10000 là một lựa chọn, không phải hằng số vật lý. Bài báo gốc chọn con số đó. Rất nhiều mô hình ngữ cảnh dài nâng nó lên hàng trăm nghìn, và vì sao làm thế thì là nội dung của bài nới ngữ cảnh.
  • d lẻ không phải cấu hình của RoPE. Nếu bạn gõ một số lẻ vào ô Chiều d, sim không lặng lẽ bỏ chiều cuối: nó chép chiều đó sang nguyên vẹn, quay các cặp còn lại, và hiện một khung đỏ nói rõ cấu hình chưa hợp lệ. Thú vị là tính chất khoảng cách vẫn sống, vì một chiều không quay thì đóng góp đúng một tích như nhau ở mọi vị trí, nhưng đó là một cấu hình không ai dùng.

Vài chỗ dễ vấp

  • Đừng nhớ "RoPE quay vector". Nó quay từng cặp chiều một góc khác nhau. Một phép quay duy nhất cho cả vector d chiều sẽ không cho ra dải thang đo, và bảng 3 sẽ phẳng lì như khi bạn đặt base về 1.
  • Bước sóng dài không có nghĩa là "quan trọng hơn". Nó có nghĩa là cặp đó gần như không mang thông tin vị trí trong cửa sổ này. Xem cột số vòng: dưới 1 vòng là chưa quay hết một chu kỳ nào.
  • Hiệu m - n âm cho điểm số khác hiệu dương.sin là hàm lẻ, phần chéo đổi dấu. Đặt m bằng 2 và n bằng 7 rồi so với cấu hình mặc định là thấy.
  • Điểm số không bị chặn trong khoảng nào. Quay giữ nguyên độ dài từng vector, nên tích vô hướng vẫn nằm trong [-|q||k|, +|q||k|] như thường, chứ không phải trong [-1, 1]. Khối công thức dưới bảng 2 in cả hai độ dài trước và sau khi quay để bạn tự kiểm.
  • Cửa sổ ngữ cảnh không hề tham gia phép quay. Nó chỉ là con số để so với bước sóng. Kéo nó không đổi một số nào ở bảng 12.
Điều rút ra

RoPE mã hoá vị trí bằng cách quay cặp chiều thứ i một góc m × base^(-2i/d), chứ không cộng gì vào. Cái được là một đẳng thức chính xác: tích vô hướng giữa truy vấn ở m và khoá ở n chỉ phụ thuộc m - n, nên dời cả hai token đi bao nhiêu cũng không đổi điểm số. Vì mỗi cặp có tần số riêng, mô hình có sẵn một dải thang đo từ vài token tới hàng chục nghìn token, và các cặp cuối có bước sóng dài hơn cả cửa sổ thì gần như không mang thông tin vị trí. Còn chuyện quay có làm mô hình giỏi hơn cách cộng hay không thì trang này không trả lời được, đó là việc của thực nghiệm.

Câu hỏi tự kiểm0/3 đúngchưa trả lời
  1. 1Với cấu hình mặc định, đặt truy vấn ở vị trí 7 và khoá ở vị trí 2 cho điểm số -0,709554. Bây giờ dời cả hai lên 107 và 102. Điểm số thành bao nhiêu?
  2. 2Đặt d bằng 128 và base bằng 10000. Cặp cuối cùng, cặp 63, có bước sóng khoảng 54.410 token. Với cửa sổ ngữ cảnh 8192 token thì điều đó nghĩa là gì?
  3. 3Sim khẳng định được điều nào sau đây, và điều nào thì không?

Đi tiếp

Bài sau trong chương này lấy đúng cái núm base mà bạn vừa vặn và biến nó thành một câu hỏi thực dụng: nếu một mô hình được huấn luyện với cửa sổ 4096 token, làm sao cho nó đọc 32768 token mà không phải huấn luyện lại từ đầu. Muốn xem lại tại sao tích vô hướng là thứ quyết định trọng số chú ý thì quay lại self-attention, còn muốn so trực tiếp với cách làm nguyên bản thì đọc lại positional encoding.