โมเดลที่ใช้หม้อแปลงไฟฟ้า มีชื่อเสียงในด้านความสามารถในการแยกวิเคราะห์และตีความข้อความที่ซับซ้อน พวกเขาพึ่งพาการทำความเข้าใจลำดับและบริบทของคำ ซึ่งเป็นงานที่วิธีการเข้ารหัสแบบระบุตำแหน่งแบบดั้งเดิมได้แสดงขีดจำกัดของตนแล้ว เพื่อแก้ไขช่องว่างนี้ โมเดล ROFORMER ซึ่งขับเคลื่อนโดย Rotary Position Embedding (RoPE) ได้กำหนดแนวทางใหม่ในการเข้ารหัสตำแหน่งของเรา
การเข้ารหัสตำแหน่งแบบดั้งเดิม
หม้อแปลงไฟฟ้าถือว่าข้อความเป็นชุดของโทเค็น และอนุญาตให้ ประมวลผลลำดับแบบขนาน เพื่อประสิทธิภาพที่ดียิ่งขึ้น อย่างไรก็ตาม จุดแข็งนี้นำมาซึ่งความท้าทาย: การไม่เชื่อเรื่องพระเจ้าโดยธรรมชาติของโมเดลในการสั่งซื้อโทเค็น การเข้ารหัสตำแหน่ง คือคำตอบ โดยให้แต่ละโทเค็นมีลายเซ็นที่ไม่ซ้ำกันซึ่งแสดงถึงตำแหน่งลำดับ
การฝังตำแหน่งที่แน่นอน
ในตอนแรก โมเดลอย่าง BERT ใช้ การฝังตำแหน่งสัมบูรณ์ โดยกำหนดเวกเตอร์คงที่ให้กับแต่ละตำแหน่งตามลำดับ วิธีนี้แม้จะตรงไปตรงมา แต่โดยเนื้อแท้แล้ว ขาดความสามารถในการปรับให้เข้ากับการเปลี่ยนแปลงความยาวของลำดับ หรือการเน้นระยะห่างสัมพัทธ์ระหว่างโทเค็น ซึ่งมีความสำคัญอย่างยิ่งต่อการทำความเข้าใจโครงสร้างทางภาษาจำนวนมาก
การฝังตำแหน่งสัมพัทธ์
เพื่อจับภาพธรรมชาติที่มีพลังของภาษา จึงมีการใช้การฝังตำแหน่งสัมพัทธ์ โดยเน้นที่ระยะห่างระหว่างโทเค็นมากกว่าตำแหน่งสัมบูรณ์ แม้จะมีข้อได้เปรียบด้านแนวคิด แต่การฝังเหล่านี้ทำให้เกิด ความซับซ้อนในการคำนวณ และล้มเหลวในการผสานรวมเข้ากับกลไกการเอาใจใส่ตนเองของ Transformers อย่างราบรื่น ซึ่งจำกัดประสิทธิภาพ
ROFORMER และการฝังตำแหน่งโรตารี
ด้วยตระหนักถึงข้อจำกัดของกลยุทธ์การเข้ารหัสตำแหน่งที่มีอยู่ ROFORMER จึงแนะนำ Rotary Position Embedding (RoPE) ซึ่งเป็นแนวทางที่ผสมผสานประโยชน์ของข้อมูลตำแหน่งสัมบูรณ์และข้อมูลตำแหน่งสัมพัทธ์โดยไม่มีข้อบกพร่องที่เกี่ยวข้อง
การฝังตำแหน่งโรตารี
RoPE เข้ารหัสข้อมูลตำแหน่งโดยใช้ เมทริกซ์การหมุน ช่วยให้โมเดลเข้าใจไม่เพียงแค่ว่าโทเค็นอยู่ที่ไหน แต่ยังเกี่ยวข้องกับโทเค็นอื่นๆ ในลำดับอย่างไร
Credit: ArXiv
มันทำงานผ่านเลนส์เรขาคณิต โดยถือว่าตำแหน่งโทเค็นเป็นจุดในพื้นที่หลายมิติที่ถูกหมุนเพื่อทำเครื่องหมายความสัมพันธ์ตามลำดับ การหมุนนี้ทำให้โมเดลสามารถรักษาและใช้ประโยชน์จากทั้งตำแหน่งสัมบูรณ์และตำแหน่งสัมพัทธ์ภายในกลไกการเอาใจใส่ตนเอง
การนำ RoPE ไปใช้
การใช้ RoPE เกี่ยวข้องกับการเข้ารหัสตำแหน่งของโทเค็นแต่ละตำแหน่งลงในเมทริกซ์การหมุน และการใช้เมทริกซ์นี้ภายในกลไกการเอาใจใส่ตนเองของ Transformer กระบวนการนี้ช่วยให้สามารถตีความข้อมูลตำแหน่งได้อย่างยืดหยุ่นและไดนามิก รองรับความยาวลำดับที่แตกต่างกัน และบันทึกสาระสำคัญของความสัมพันธ์ระหว่างโทเค็นโดยไม่มีค่าใช้จ่ายในการคำนวณที่มีนัยสำคัญ
ขั้นแรก คุณจะต้องมีฟังก์ชันเพื่อสร้างการฝังแบบหมุน จากนั้นจึงรวมการฝังเหล่านี้เข้ากับโมเดลของคุณ ตัวอย่างด้านล่างนี้ถือว่าคุณคุ้นเคยกับการสร้างเลเยอร์ที่กำหนดเองใน Keras
ขั้นตอนที่ 1: กำหนดฟังก์ชันการฝังแบบโรตารี
ฟังก์ชันนี้สร้างการฝังแบบหมุนตามความยาวลำดับสูงสุด และมิติของการฝัง
from tensorflow.keras.layers import Layer
import numpy as np
def get_rotary_embedding(dim, max_seq_len):
inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))
t = tf.range(max_seq_len, dtype=tf.float32)
freqs = tf.einsum('i,j->ij', t, inv_freq)
emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
return emb
inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))
เส้นนี้จะคำนวณค่าผกผันของความถี่ที่ปรับขนาดแบบเอ็กซ์โปเนนเชียลโดยอิงตามดัชนีตำแหน่ง ความถี่เหล่านี้ใช้ในการสร้างรูปแบบไซน์ซอยด์สำหรับการฝังแบบหมุน ซึ่งช่วยในการเข้ารหัสข้อมูลตำแหน่งสัมพัทธ์ตามลำดับ กลไกนี้มีประโยชน์อย่างยิ่งในงานที่การทำความเข้าใจลำดับและการวางตำแหน่งสัมพัทธ์ขององค์ประกอบเป็นสิ่งสำคัญ เช่น ในการประมวลผลภาษาธรรมชาติหรือการวิเคราะห์อนุกรมเวลา
ในรายละเอียด:
-
tf.range(0, dim, 2, dtype=tf.float32)
สร้างช่วงของค่าเริ่มต้นจาก 0 จนถึงdim
(เฉพาะ) ทีละ 2 อาร์กิวเมนต์dtype=tf.float32
ระบุ องค์ประกอบของเทนเซอร์นี้เป็นตัวเลขทศนิยม 32 บิต ตัวอย่างเช่น หากdim
เป็น 8 ก็จะได้[0, 2, 4, 6]
-
เทนเซอร์ที่สร้างโดย
tf.range
จากนั้นจะถูกหารด้วยมิติข้อมูล (สลัว
) ของการฝัง การดำเนินการนี้จะปรับขนาดดัชนีเหล่านี้ลงให้อยู่ในช่วงระหว่าง 0 ถึง 1 (ยกเว้นหากdim
เป็นเลขคู่ และจะรวมเล็กน้อยหาก 'dimเป็นเลขคี่ เนื่องจากขั้นตอนของช่วงจะข้ามค่าอื่นๆ ทั้งหมด) ดำเนินการต่อตัวอย่างด้วย
dim= 8 หารด้วย 8 จะได้
[0.0, 0.25, 0.5, 0.75]` -
การดำเนินการ
10,000 ** (...)
จะเพิ่มกำลัง 10,000 ให้กับแต่ละองค์ประกอบในเทนเซอร์ที่ปรับขนาดไว้ก่อนหน้านี้ ฐานของ 10,000 นั้นค่อนข้างจะเป็นไปตามอำเภอใจ แต่ถูกเลือกเพื่อให้แน่ใจว่าความถี่จะแตกต่างกันไปในช่วงกว้าง ซึ่งช่วยให้โมเดลสามารถแยกความแตกต่างระหว่างตำแหน่งต่างๆ ได้อย่างมีประสิทธิภาพมากขึ้น สำหรับ[0.0, 0.25, 0.5, 0.75]
จะใช้การดำเนินการด้านพลังงานกับแต่ละรายการ ส่งผลให้ค่าที่สูงกว่ามากสำหรับองค์ประกอบที่สูงกว่า -
สุดท้ายนี้ จะได้ความถี่ผกผันโดยการนำส่วนกลับ (1/x) ของค่าจากขั้นตอนที่แล้ว ความถี่ผกผันจะเล็กลงสำหรับดัชนีที่สูงกว่า หมายความว่าองค์ประกอบที่อยู่ลำดับต่อไปจะมีความถี่ที่เล็กลง ซึ่งส่งผลต่อวิธีการเข้ารหัสตำแหน่งขององค์ประกอบเหล่านั้นลงในแบบจำลอง ซึ่งช่วยให้การฝังสามารถปรับขนาดในลักษณะที่สามารถอนุมานตำแหน่งสัมพัทธ์ได้ผ่านกลไกความสนใจของโมเดล
บรรทัด:
freqs = tf.einsum('i,j->ij', t, inv_freq)
ใช้ฟังก์ชัน tf.einsum
ของ TensorFlow ซึ่งเป็นเครื่องมือที่ช่วยให้สามารถแสดงออกการดำเนินการของเทนเซอร์ได้อย่างกระชับและมีประสิทธิภาพโดยใช้สัญลักษณ์การรวมของ Einstein
การดำเนินการนี้จะคำนวณผลคูณด้านนอกของเวกเตอร์ t
และ inv_freq
อย่างมีประสิทธิภาพ ส่งผลให้เมทริกซ์โดยที่แต่ละองค์ประกอบ (i, j)
เป็นผลคูณขององค์ประกอบ i
-th ของ t
และ องค์ประกอบที่ j
ของ inv_freq
เมทริกซ์นี้ (ความถี่
) แสดงถึงความถี่ที่ใช้ในการสร้างรูปแบบไซน์ซอยด์สำหรับการฝังแบบหมุน
ขั้นตอนที่ 2: เลเยอร์ Keras แบบกำหนดเองสำหรับการฝังแบบโรตารี
ตอนนี้ เรามาสร้างเลเยอร์ Keras แบบกำหนดเองที่ใช้การฝังแบบหมุนกับเทนเซอร์อินพุต เลเยอร์นี้จะถือว่าเทนเซอร์อินพุตมีรูปร่าง (batch_size, sequence_length, embedding_dim)
class RotaryEmbeddingLayer(Layer):
def __init__(self, dim, max_seq_len, **kwargs):
super().__init__(**kwargs)
self.dim = dim
self.max_seq_len = max_seq_len
self.rotary_embeddings = get_rotary_embedding(dim, max_seq_len)
def call(self, inputs):
seq_len = tf.shape(inputs)[1]
embeddings = self.rotary_embeddings[:seq_len]
cos_emb = embeddings[:, None, :self.dim // 2]
sin_emb = embeddings[:, None, self.dim // 2:]
# Decompose inputs into sine and cosine components
inputs_cos = inputs[..., :self.dim // 2]
inputs_sin = inputs[..., self.dim // 2:]
# Apply rotary embeddings
rotated_cos = inputs_cos * cos_emb - inputs_sin * sin_emb
rotated_sin = inputs_sin * cos_emb + inputs_cos * sin_emb
return tf.concat([rotated_cos, rotated_sin], axis=-1)
def get_config(self):
config = super().get_config()
config.update({
"dim": self.dim,
"max_seq_len": self.max_seq_len
})
return config
บรรทัด embeddings = self.rotary_embeddings[:seq_len]
จะเลือกเซ็ตย่อยที่เหมาะสมของการฝังแบบหมุนที่คำนวณไว้ล่วงหน้าตามความยาวลำดับอินพุตปัจจุบัน เนื่องจากความยาวของลำดับอาจแตกต่างกันไปในแต่ละชุด การดำเนินการแบ่งส่วนนี้ช่วยให้มั่นใจได้ว่าจะใช้เฉพาะการฝังที่สอดคล้องกับความยาวลำดับจริงเท่านั้น
ขณะนี้ตัวแปร embeddings
มีเทนเซอร์ของรูปร่าง (seq_len, embedding_dim)
โดยที่ seq_len
คือความยาวของลำดับในชุดปัจจุบัน และ embedding_dim
คือมิติของการฝัง เทนเซอร์นี้มีการฝังตำแหน่งแบบหมุนสำหรับแต่ละตำแหน่งในลำดับจนถึง seq_len
emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
รวมการแปลงไซน์และโคไซน์ของความถี่ตำแหน่งให้เป็นเทนเซอร์ตัวเดียว:
-tf.cos(freqs)
และ tf.sin(freqs)
ใช้การแปลงโคไซน์และไซน์ตามลำดับกับเมตริกซ์ freqs
เทนเซอร์ "ความถี่" มีค่าความถี่สำหรับแต่ละตำแหน่งในลำดับอินพุตและแต่ละมิติของพื้นที่การฝัง ซึ่งคำนวณตามตำแหน่งลำดับและความถี่ผกผันของมิติการฝัง ฟังก์ชันไซน์และโคไซน์ถูกนำมาใช้แบบองค์ประกอบ ส่งผลให้เทนเซอร์สองตัวมีรูปร่างเดียวกันกับ "ความถี่" การเปลี่ยนแปลงเหล่านี้ช่วยในการเข้ารหัสตำแหน่งในลักษณะที่จับลักษณะวงจรของความสัมพันธ์เชิงตำแหน่ง ช่วยให้แบบจำลองเข้าใจตำแหน่งสัมพัทธ์ได้ง่ายขึ้น
-tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
เชื่อมต่อเทนเซอร์ที่เปลี่ยนรูปโคไซน์และไซน์ไปตามแกนสุดท้าย (แสดงด้วย axis=-1
) การต่อเทนเซอร์เหล่านี้ไว้ข้างกันช่วยเพิ่มมิติของเทนเซอร์ "ความถี่" ได้อย่างมีประสิทธิภาพ โดยครึ่งแรกแทนค่าที่แปลงโคไซน์ และครึ่งหลังแทนค่าที่แปลงไซน์สำหรับแต่ละตำแหน่ง การต่อข้อมูลช่วยให้มั่นใจได้ว่าการเข้ารหัสตำแหน่งแต่ละรายการมีทั้งข้อมูลไซน์และโคไซน์ ซึ่งช่วยให้สามารถรักษาข้อมูลเกี่ยวกับทั้งแอมพลิจูดและเฟสของสัญญาณตำแหน่งได้
- ตอนนี้เทนเซอร์ที่ต่อกัน
emb
จะยึดการฝังแบบหมุนทั้งหมดสำหรับตำแหน่งอินพุต รูปร่างของ "Emb" จะเหมือนกับ "ความถี่" ในสองมิติแรก (สอดคล้องกับตำแหน่งลำดับและมิติที่ฝัง) แต่มิติสุดท้ายจะมีขนาดใหญ่เป็นสองเท่า โดยคำนึงถึงทั้งค่าไซน์และโคไซน์ การฝังเหล่านี้ใช้เพื่อปรับการฝังอินพุตโดยการเพิ่มข้อมูลตำแหน่งในลักษณะที่เทียบเท่าการหมุน
-cos_emb = การฝัง[:, ไม่มี, :self.dim // 2]
:
-
โคลอนแรก
:
หมายถึง "เลือกองค์ประกอบทั้งหมดในมิตินี้" ซึ่งในกรณีนี้หมายถึงตำแหน่งทั้งหมดในลำดับ -
ไม่มี
ใช้เพื่อเพิ่มมิติเพิ่มเติม ทำให้เทนเซอร์เป็นแบบ 3 มิติ ซึ่งมักจะทำเพื่อให้แน่ใจว่าเข้ากันได้กับการดำเนินงานบางอย่างที่คาดหวังอินพุตของมิติจำนวนหนึ่ง ตัวอย่างเช่น เมื่อทำการคูณตามองค์ประกอบด้วยเทนเซอร์ตัวอื่นที่เป็น 3 มิติ รูปร่างจะต้องจัดเรียงตามกฎการออกอากาศ -
:self.dim // 2
เลือกครึ่งแรกของมิติข้อมูลในแกนสุดท้าย เนื่องจาก `มิติการฝัง' ถูกเพิ่มเป็นสองเท่าเพื่อรวมทั้งค่าไซน์และโคไซน์ การหารด้วย 2 จะเลือกเฉพาะองค์ประกอบโคไซน์ของการฝังได้อย่างมีประสิทธิภาพ
ขั้นตอนที่ 3: บูรณาการกับโมเดล Keras
หลังจากกำหนด RotaryEmbeddingLayer
แล้ว คุณสามารถรวมเข้ากับโมเดล Keras ของคุณได้ ควรใช้เลเยอร์นี้กับการฝังของคุณก่อนที่จะป้อนลงในเลเยอร์ที่สนใจหรือเลเยอร์โมเดลอื่นๆ ที่ตามมา
ต่อไปนี้คือตัวอย่างง่ายๆ ของวิธีการรวมการฝังแบบหมุนเข้ากับโมเดล:
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, Dense
max_seq_len = 512
embedding_dim = 64
inp = Input(shape=(max_seq_len,))
x = Embedding(input_dim=10000, output_dim=embedding_dim)(inp)
x = RotaryEmbeddingLayer(dim=embedding_dim, max_seq_len=max_seq_len)(x)
# Add your model's layers here, e.g., Transformer blocks
x = Dense(1, activation='sigmoid')(x)
model = Model(inputs=inp, outputs=x)
model.summary()