RoFormer: หม้อแปลงที่ได้รับการปรับปรุงพร้อมการฝังตำแหน่งแบบหมุน

RoFormer: หม้อแปลงที่ได้รับการปรับปรุงพร้อมการฝังตำแหน่งแบบหมุน

โมเดลที่ใช้หม้อแปลงไฟฟ้า มีชื่อเสียงในด้านความสามารถในการแยกวิเคราะห์และตีความข้อความที่ซับซ้อน พวกเขาพึ่งพาการทำความเข้าใจลำดับและบริบทของคำ ซึ่งเป็นงานที่วิธีการเข้ารหัสแบบระบุตำแหน่งแบบดั้งเดิมได้แสดงขีดจำกัดของตนแล้ว เพื่อแก้ไขช่องว่างนี้ โมเดล ROFORMER ซึ่งขับเคลื่อนโดย Rotary Position Embedding (RoPE) ได้กำหนดแนวทางใหม่ในการเข้ารหัสตำแหน่งของเรา

การเข้ารหัสตำแหน่งแบบดั้งเดิม

หม้อแปลงไฟฟ้าถือว่าข้อความเป็นชุดของโทเค็น และอนุญาตให้ ประมวลผลลำดับแบบขนาน เพื่อประสิทธิภาพที่ดียิ่งขึ้น อย่างไรก็ตาม จุดแข็งนี้นำมาซึ่งความท้าทาย: การไม่เชื่อเรื่องพระเจ้าโดยธรรมชาติของโมเดลในการสั่งซื้อโทเค็น การเข้ารหัสตำแหน่ง คือคำตอบ โดยให้แต่ละโทเค็นมีลายเซ็นที่ไม่ซ้ำกันซึ่งแสดงถึงตำแหน่งลำดับ

การฝังตำแหน่งที่แน่นอน

ในตอนแรก โมเดลอย่าง BERT ใช้ การฝังตำแหน่งสัมบูรณ์ โดยกำหนดเวกเตอร์คงที่ให้กับแต่ละตำแหน่งตามลำดับ วิธีนี้แม้จะตรงไปตรงมา แต่โดยเนื้อแท้แล้ว ขาดความสามารถในการปรับให้เข้ากับการเปลี่ยนแปลงความยาวของลำดับ หรือการเน้นระยะห่างสัมพัทธ์ระหว่างโทเค็น ซึ่งมีความสำคัญอย่างยิ่งต่อการทำความเข้าใจโครงสร้างทางภาษาจำนวนมาก

การฝังตำแหน่งสัมพัทธ์

เพื่อจับภาพธรรมชาติที่มีพลังของภาษา จึงมีการใช้การฝังตำแหน่งสัมพัทธ์ โดยเน้นที่ระยะห่างระหว่างโทเค็นมากกว่าตำแหน่งสัมบูรณ์ แม้จะมีข้อได้เปรียบด้านแนวคิด แต่การฝังเหล่านี้ทำให้เกิด ความซับซ้อนในการคำนวณ และล้มเหลวในการผสานรวมเข้ากับกลไกการเอาใจใส่ตนเองของ Transformers อย่างราบรื่น ซึ่งจำกัดประสิทธิภาพ

ROFORMER และการฝังตำแหน่งโรตารี

ด้วยตระหนักถึงข้อจำกัดของกลยุทธ์การเข้ารหัสตำแหน่งที่มีอยู่ ROFORMER จึงแนะนำ Rotary Position Embedding (RoPE) ซึ่งเป็นแนวทางที่ผสมผสานประโยชน์ของข้อมูลตำแหน่งสัมบูรณ์และข้อมูลตำแหน่งสัมพัทธ์โดยไม่มีข้อบกพร่องที่เกี่ยวข้อง

การฝังตำแหน่งโรตารี

RoPE เข้ารหัสข้อมูลตำแหน่งโดยใช้ เมทริกซ์การหมุน ช่วยให้โมเดลเข้าใจไม่เพียงแค่ว่าโทเค็นอยู่ที่ไหน แต่ยังเกี่ยวข้องกับโทเค็นอื่นๆ ในลำดับอย่างไร

Reformer.pngCredit: 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]:

  1. โคลอนแรก : หมายถึง "เลือกองค์ประกอบทั้งหมดในมิตินี้" ซึ่งในกรณีนี้หมายถึงตำแหน่งทั้งหมดในลำดับ

  2. ไม่มี ใช้เพื่อเพิ่มมิติเพิ่มเติม ทำให้เทนเซอร์เป็นแบบ 3 มิติ ซึ่งมักจะทำเพื่อให้แน่ใจว่าเข้ากันได้กับการดำเนินงานบางอย่างที่คาดหวังอินพุตของมิติจำนวนหนึ่ง ตัวอย่างเช่น เมื่อทำการคูณตามองค์ประกอบด้วยเทนเซอร์ตัวอื่นที่เป็น 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()

Code Labs Academy © 2025 สงวนลิขสิทธิ์.