Transformer Encoder Yapısı | Self-Attention

Kaan Bıçakcı
Machine Learning Turkiye
9 min readJan 12, 2022

NOT: Altına ref vermediğim çizimler bana aittir ve draw.io ile çizilmiştir.

Bu yazım aslında GitHub’a yüklemiş olduğum bir notebook’un içeriğidir. Notebook içerisinde anlattığım mimari ile CNN’i birleştirip bir classification yaptığım çalışma da mevcuttur.

Transformer mimarisi aslında iki yapıdan oluşur.

  • Encoder
  • Decoder

Bu yazı çok uzun olacağı için Transformer mimarisinde sadece encoder yapısına değineceğim.

Bu yazımda:

  • Yüzeysel Olarak RNN Yapılarından,
  • RNN’lerdeki Problemlerden,
  • RNN vs Transformer
  • Detaylı Olarak Word & Positional Embedding
  • Self-Attention Tanımından
  • Adım Adım MultiHead Attention Yapısından

bahsedeceğim.

Yüzeysel Olarak RNN Yapıları

1) Vektör — Dizi Modelleri (Vector-to-Sequence):

  • Girdi: Resim vektörü
  • Çıktı: Başlık, altyazı (caption)

2) Dizi — Vektör Modelleri (Sequence-to-Vector)

  • Girdi: Kelime dizisi — Film değerlendirmesi olabilir.
  • Çıktı: Vektör — Olumlu/Olumsuz dağılım çıktısı olabilir.
  • Örnek: Dizi: Film sürükleyici olmadığı için beğenmedim → Çıktı: [0.01, 0.99] (model tarafından)

3) Dizi — Dizi Modelleri (Sequence-to-Sequence)

Translate modelleri örnek verilebilir.

  • Girdi: Kelime dizisi — İngilizce dizi
  • Çıktı: Kelime dizisi — Türkçe dizi
  • Örnek: I love TensorFlow, you love PyTorch, both are great! → Çıktı: Ben TensorFlow’u seviyorum, sen PyTorch’u seviyorsun, ikisi de harika! (model tarafından)
https://www.analyticsvidhya.com/blog/2020/08/a-simple-introduction-to-sequence-to-sequence-models/

RNN Modellerindeki Sorun Nedir?

  • RNN modellerini eğitmek maliyet açısından sorunludur çünkü çok fazla hesaplama gerektirirler.
  • Diziler (sequences) uzadıkça gradyanlar sıfıra yaklaşabilir veya patlayabilir.

Daha fazlası için → http://proceedings.mlr.press/v28/pascanu13.pdf

Uzun Dizileri Daha İyi İşlemek — LSTM

  • LSTM mimarileri diziler uzadıkça normal RNN hücrelerine oranla daha iyi sonuç verirler. Çünkü LSTM’lerin aktarılan bilgilerin mevcut hücrenin üzerindeki işlemlerinin çoğunu atlamasına ve bir sonrakine geçmesine izin veren bir yapısı (dalı) vardır.
https://colah.github.io/posts/2015-08-Understanding-LSTMs/
  • Fakat normal RNN’leri bile eğitmek yavaş ve masraflı iken, LSTM’in eğitimi daha yavaştır.

RNN vs Transformer?

  • İyi çok güzel, cümleyi komple tek seferde veriyoruz fakat içerde bu nasıl oluyor?
  • Düşündüğümüz zaman Transformer aslında büyük bir güç… Ama biliyoruz ki büyük güç büyük sorumluluk gerektirir, onun için detaylarına bakalım.

Transformer Mimarisi

Dediğim gibi sadece encoder tarafına bakacağız.

https://d2l.ai/chapter_attention-mechanisms/transformer.html#model

Input Embedding

  • Bilgisayarlarla anlaşabilmemiz için onlara insan dilini bir şekilde vektör olarak vermeliyiz, kelimeleri vektörlere çevirirken de yakın anlamlı olan kelimelerin vektörlerinin arasındaki mesafenin (başka metrikler de olabilir) az olmasını isteriz.
  • Örneğin, sözlüğümde 500 kelime varsa bunları 128 boyutlu vektörler olacak şekilde ifade edersek (500, 128) şeklinde bir array elde etmiş oluruz.
  • Bu vektörlerin bir uzay oluşturduğunu düşünürsek şöyle bir görüntü ortaya çıkacaktır (T-SNE ile 2 boyuta indirgedim 128 → 2):
T-SNE ile Embeddinglerin görselleştirilmesi

Görüldüğü üzere yakın kelimelerin vektörleri uzayda birbirine yakındır (her nokta bir kelime vektörünü temsil etmektedir).

Neticede her kelimeyi bir vektörle ifade etmiş oluruz.

  • Örnek: -Çalışmak- kelimesi 128 boyutlu bir vektör ile ifade edilebilir.

Transformerda Tek Başına Input Embedding Yeterli Mi?

Yukarıda da bahsettiğim gibi Transformer mimarisi timestepler kullanmıyor. RNN yapılarında timestepler vardı ve cümleyi sıra halinde işliyorduk. Transformer ile işleri hızlandırmak adına cümlenin tamamıyla birden işlem yapmak isteyince sıra kavramı kaybolmuş oldu.

Word embedding ile oluşturduğumuz kelime vektörleri bize cümledeki her anlamı çıkarmamıza olanak sağlamıyor. Şu senaryoya bakalım.

  • Çay kenarında çay içtim.

Cümlede geçen çay kelimelerine baktığımız anda farklı anlamlara geldiğini anlayabiliyoruz fakat diğer bir yandan bu iki kelimenin vektör gösterimleri modeller için farklı anlamlara geliyor mu?

  • HAYIR. Çünkü bu iki kelimenin vektör gösterimi aynı, yani bu cümleden iki tane tamamen aynı vektör elde etmiş oluyor.
  • Yerlerini modele anlatacak bir yapı geliştirebiliriz. (Positional Encoding)
  • Kelimelerin birbirleriyle olan ilişkilerini Transformera anlatacak bir yapı da geliştirebiliriz. (Self-Attention)

Görselleştirecek olursak:

Positional Embedding

https://arxiv.org/pdf/1706.03762.pdf

Attention Is All You Need makalesinde yazarlar bunu sin ve cos fonksiyonları ile vermişlerdir. Göz korkutmaması açısından basitten başlayalım.

Positional Embedding’i Nasıl Oluşturabiliriz

Açıklama: Alttaki şemalarda position vector kısmı kolaylık olsun diye tek sayı olarak gösterilmiş olsa da kelime embeddingleri kadar boyutu vardır. Kelime embeddingleri 128 boyutlu ise position vector de 128 boyutlu olacaktır.

1. Yöntem: Kelimelerin Bulunduğu Sırayla Direkt Vektör Oluşturmak

T1, T2, …, T5 ile kelimeler ifade edilmektedir
  • Toplanabilir, çarpılabilir ifadeleri sadece birer örnektir. Önemli olan iki vektörü bir şekilde birleştirmektir ve TOPLAMAK genel bir çözümdür.

2. İlk Yöntemdeki Konum Vektörünü (Position Vector) Normalize Etmek

Cümleleri Padleyelim veya Uzunları Kırpalım ve 2.Yöntemi Uygulayalım?

Cümle uzunluğunu sabitlediğimizi varsayalım. Veri setimizdeki max. cümle uzunluğu 10 olsun.

Konum embedding’i oluşturmak için kelime indekslerini kullandık ve gradyanlarla ilgili sorun çıkmaması için normalize ettik. Sonuç olarak bu vektör değerleri 0 ve 1 arasında değerler oldu

Kullanışlı Görünüyor Değil Mi?

İlgilendiğimiz problemi görselleştirelim:

Normalize Positional Vector ile Olası Problemler

  • Bu yaklaşımın ortasını bulacak bir formüle ihtiyacımız var.

Yaptığımız Şeyleri Toplamak Gerekirse…

Konum belirten vektörler her kelime için aynı olmalı.

  • Örnek: 1.indisteki değer 0.1 ise, kelime değiştiği zaman da 0.1 olmalı.

Elde etmemiz gereken positional embedding’leri henüz nasıl elde edeceğimizi kararlaştıramadık.

3. Yöntem: Frekans Tabanlı Embedding

İlk 2 yaklaşımda gördüğümüz üzere problemler mevcuttu. Bu problemleri yenmek için Attention is All You Need makalesinde yazarlar yukarda gördüğümüz formülü önermişlerdir.

Buradaki ana fikir, modelin genelleyebileceği özellikler ortaya çıkarıp öğretmektir.

Önerilen formülü açalım:

  • d_model: Modelin kullandığı word embedding boyutu (dimension).
  • i: Embedding indisi. → 𝑖=(0,1,…,𝑑𝑚𝑜𝑑𝑒𝑙/2−1)
  • pos: Kelimenin cümledeki yeri.
# Ref: https://www.tensorflow.org/text/tutorials/transformer#positional_encoding
# Hazır bir implementasyon kullanalım.
def get_angles(pos, i, d_model):
angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
return pos * angle_rates

def positional_encoding(position, d_model):
angle_rads = get_angles(np.arange(position)[:, np.newaxis],
np.arange(d_model)[np.newaxis, :],
d_model)

#Çift indislere sin uygula; 2i
angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])

# Tek indislere cos uygula; 2i+1
angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])

pos_encoding = angle_rads[np.newaxis, ...]

return pos_encoding.astype(np.float32)

pos = 64
d_model = 128

n, d = pos, d_model
pos_encoding = positional_encoding(n, d)
pos_encoding = pos_encoding[0]

Bu Yöntem Neden Daha İyi?

Orijinal kağıda bakacak olursak, yazarlar şunu söylemiştir:

Attention Is All You Need

512 boyutlu bir Positional Embedding vektörünü şu şekilde yazabiliriz:

En sondaki transpoz aldığımızı biraz daha açarsak:

Artık bunları ikili vektörler olarak görebiliriz. Yani pos değerlerini değiştirip, i değerlerini sabit tutarsak bu vektörden 2 boyutlu vektörler elde edebiliriz.

# Örnek: pos: 0# pos encoding yukarıda tanımlanmıştı.
pos_encoding[0, 0], pos_encoding[0, 1]
--> (0.0, 1.0)
# Örnek: pos: 1
pos_encoding[1, 0], pos_encoding[1, 1]
--> (0.84147096, 0.5403023)

Bu vektörleri bir scatter plot üzerinde çizersek, aldığımız vektörlerin aslında birim çember üzerinde döndüğünü görebiliriz.

plt.figure(figsize = (16, 16))
i = 0
for pos in range(0, pos):
plt.xlim([-1, 1])
plt.ylim([-1, 1])
plt.scatter(pos_encoding[pos, 2*i],
pos_encoding[pos, 2*i + 1],
alpha = 0.4, color = np.random.rand(3,), linewidth = 10)

Plotly ile interaktif bir plot çizebiliriz:

Positionları 7'şer artıracak şekilde plot edecek olursak yukardaki gibi görünecektir.

Bu yaptığımızı 3 boyuta çıkaracak olursak:

# pos encoding yukarıda tanımlanmıştı.
pos_encoding[0, 0], pos_encoding[0, 1], pos_encoding[0, 2]
--> (0.0, 1.0, 0.0)
# Örnek: pos: 1
pos_encoding[1, 0], pos_encoding[1, 1], pos_encoding[1, 2]
--> (0.84147096, 0.5403023, 0.761)

Aynı şekilde positionları yedişer artırırsak plot şekildeki gibi olacaktır.

Şu kısma tekrardan bakalım:

We chose this function because we hypothesized it would allow the model to easily learn to attend by relative positions, since for any fixed offset k, PE_(pos+k) can be represented as a linear function of PE_pos.

Herhangi bir sabit ofset k için, PE_(pos+k), PE_pos’un doğrusal bir fonksiyonu olarak temsil edilebilir. Bu da modelin gerekli yerleri öğrenmesine olanak sağlar.

Bunu biraz açacak olursak:

Çıkan sonuç Rotation Matrix’e denk oluyor.

Yani i sabit kalırsa, rotation matrix sadece k'ye bağlı olmuş oluyor. POS değiştikçe her nokta, dairelerin derinliğine bağlı olarak farklı frekanslarda POS'un ofsetiyle doğru orantılı olarak dönüyor. Daire ne kadar derinse, frekans o kadar küçük oluyor.

Sonuç olarak bu tür konumsal kodlamalar Transformer modellerinin bu dairelerin dönüşleri ile kelimelerin konumlarını öğrenmesini sağlamış oluyor. Ayrıca rotation matrix'in her bir öğesinin değerleri -1' ile 1'arasında olduğu için gelen dizi ne kadar uzun olursa olsun büyümeden aynı aralıkta bulunurlar :)

Derinlik görebileceğimiz plotlara bakıp, artık Positional Encoding konusunu kapatalım.

pos_encoding vektörlerimiz toplamda 128 boyutluydu. En son olarak bunlara PCA uygulayıp 3 boyuta düşürüp bakacak olursak:

Bu positionlara yeniden yedişerli bakacak olursak:

Attention ve MultiheadAttention Layer

Attention’a High Level Bakış

Query, Key ve Value Kavramları

Query/Key/Value kavramı, sisteme göre değişiklik gösterebilir. Örneğin, Youtube’da video aradığınızda, arama çubuğundaki metin (query), veritabanındaki bizim metnimizle eşleşen videoların başlığını bir dizi anahtarla (KEY: video başlığı, açıklama vb.) eşleştirir ve ardından sonuçları döndürür. En iyi eşleşen videolar ise VALUES yani değerlerdir. (Ref: https://stats.stackexchange.com/a/424127)

Bu Benzerlik Nasıl Hesaplanır??

  • 1) Cos. Similarity yardımıyla iki vektör arasındaki benzerliği hesaplayabiliriz.
  • 2) Bunu görülen formül ile genelleştirebiliriz.
  • 3) Matrislerle de aynı işlemi yapabiliriz fakat çarpım kurallarından ötürü b’nin transpozunu alıyoruz.
  • 4) Query ve Key arasındaki benzerlik de bu mantıkla hesaplanabilir

Multihead Attention Yapısı

Adım adım başlayalım.

Attention Head

Bir dakika, üç tane input var ve biz bunların hepsine aynı embedding matrisini verdik. Kulağa saçma geliyor biliyorum, onun için şimdilik devam edelim.

Self-Attention kavramı aslında tam olarak burada devreye giriyor. Self-Attention olmadan modellerin cümle içindeki birbirine benzeyen veya ilişkili olan kelimelerin bilgisini öğrenmesi çok zor olacaktır.

  • Örnek: Dere yatağına yapılan evler … cümlesinde dere ve yatak kelimelerinin vektör gösterimi çok farklı olacaktır fakat self-attention mekanizması ile bunları modele anlatabiliriz.

Adım adım hesaplamaları yapalım:

Multihead Attention ilk kısım, Linear Dense.

Linear Layerda 3x2 kısmındaki:

  • 3Embedding matrisinin 3 satır oluşundan
  • 2Linear Dense Layerın 2 unit içermesinden gelmektedir.

Fakat görüldüğü üzere bu matris çarpımını yapabilmemiz için Word + Position Embedding matrislerinin transpozunu almamız gerekiyor.

Yapılan matris çarpımlarından sonra Query, Key ve Value değerlerini elde etmiş oluyoruz.

Query * Key^T = Output Filter

Output filter kelimelerin birbiri ile ilişkilerini içeren ham matristir.

Output Filter, Scaling ve Softmax

Attention is All You Need kağıdında önerilen scale işlemini yapalım. Daha sonra ise softmax uygulayacağız.

Attention Scores, bize her kelimenin ne kadar benzer olduğunu söyler.

Matrix Multiplication Layer

Filtrelenmiş özellikler tahmin için gerekli yerlere odaklanmamızı sağlar.

Multihead Kısmına Geçelim :)

Bu filtrelerden birden çok kullanırsak farklı özellikler öğrenebilir miyiz?

  • Evet. Transformer mimarileri de bu filtreleri birleştirerek farklı özellikler öğreniyor. Çünkü gerçekte birden çok filtre öğrenilir. Gösterecek olursak:

Farklı farklı Attention Headleri birleştirerek aslında Transformer modelinin Self-Attention yardımıyla kelimelerin birbiriyle olan ilişkisel anlamlarını da öğrenmesini sağlıyoruz. Yani sağlanıyor :)

Başka Çizimler İle Bakacak Olursak

Yaptığımız işlemleri 2 resim ile özetleyecek olursak:

Attention Is All You Need (https://arxiv.org/pdf/1706.03762.pdf)

Sonuç

Elimden geldiğince açık anlatmaya çalıştım, umarım faydalı olmuştur. Notebook’a buradan ulaşabilirsiniz.

Referanslar ve Ek Kaynaklar

https://arxiv.org/pdf/1706.03762.pdf (Attention Is All You Need)

--

--