"Açıklamalı GPT-2" ye hoş geldiniz.
Okuduğum en heyecan verici ve açıkça anlatılan makalelerden biri "The Annotated Transformer" https://nlp.seas.harvard.edu/2018/04/03/attention.html. Benzeri görülmemiş bir ilgi çekmiştir ve basit bir fikir, ihtiyacınız olan koda açıklama eklemek için bir dosya kullanmaktır.
Makine öğrenimindeki deneyimim, bir şeyleri koda yazdığınızda, uygulamanın ve sırların sihir değil, daha net hale geleceğini fark etmemi sağladı.
Sihir hakkında büyülü hiçbir şey yoktur. Sihirbaz bazı basit şeyleri anlar ve bu şeyler eğitimsiz bir izleyici için basit görünmez. Sihirli kartları nasıl kullanacağınızı öğrendikten sonra sihir de yapabilirsiniz.
Jeffrey Friedl "Normal İfadelerde Uzmanlaşmak"
GPT-2 ilk bakışta sihir gibi görünüyor, çok güzel görünüyor ama umarım sihri size açıklayabilirim ve bu makaleyi okumayı bitirdiğinizde tüm püf noktalarını ortaya çıkarabilirim. Amacım budur. GPT-2 modelinin nasıl çalıştığını anlamak isteyenler için daha iyi hale getirin.
Not: Hemen hemen tüm kod, Hugging Face'in GPT-2 uygulamasından (https://github.com/huggingface/transformers/blob/master/src/transformers/modeling_gpt2.py) kopyalanır, esinlenir ve referans alınır, yalnızca Basit temel öğeler. GPT-2 modellerini paralel GPU'larda eğitmek, ince ayar sırasında kontrol noktalarını kaydetmek, birden çok CPU'da çıkarım görevleri çalıştırmak vb. İstiyorsanız Hugging Face API'yi kullanmanızı öneririm. Son zamanlarda, Hugging Face, bunu nasıl yapacağınızı öğreten basit bir eğitim yayınladı: https://huggingface.co/blog/how-to-train.
Bu makalede, tekerleği yeniden icat etmeye çalışmıyorum, ancak okuyucuların GPT-2'de ustalaşmasını kolaylaştırmak için zaten var olan bir dizi mükemmel kaynağı bir araya getiriyorum. Okurlardan, seçtikleri alanda bu temelleri daha da inşa etmelerini istiyorum.
Zayıf bir temel üzerine harika bir bina inşa edemezsiniz. Güçlü bir üst yapı istiyorsanız, sağlam bir temele sahip olmalısınız.
Gordon B. Hinckley
Bu öğretici için ön koşullar
Bu makale, okuyucunun dikkat mekanizmaları ve tansformerler hakkında sağlam bir anlayışa sahip olduğunu varsaymaktadır. GPT-2, yalnızca kod çözücüler içeren 12 katmanlı bir transformatör mimarisi kullanır. Dikkat mekanizmalarını ve dönüştürücüleri incelemek veya öğrenmek istiyorsanız, işte kaynakların iyi bir listesi:
Jay Alammar'ın çizdiği Transformer:
Harvard Üniversitesi Açıklamalı Transformatör: https://nlp.seas.harvard.edu/2018/04/03/attention.html
Transformer'a Giriş, Rachel Thomas ve Jeremy Howard: https://www.youtube.com/watch?v=AFkGPmU16QAlist=PLtmWHNX-gukKocXQOkQjuVxglSDYWsSh9index=18t=0s
NLP yolculuğunuza yeni başlıyorsanız veya bir uzmansanız, Rachel Thomas ve Jeremy Howard tarafından verilen fast.ai NLP kursunu kesinlikle tavsiye ederim (https://www.fast.ai/2019/07/08/fastai -nlp /). Bu ders, naif Bayes ve Logistic regresyon kullanarak semantik sınıflandırma, ardından RNN ve daha sonra transfer öğrenme, ULMFiT, Seq2Seq çeviri ve transformatörler dahil olmak üzere temellerden başlar. Fast.ai ekibi tarafından ücretsiz olarak sağlanan mükemmel bir kaynaktır.
GPT-2'nin kendisi ile ilgili bir başka mükemmel kaynak, Jay Alammar'ın Resimli GPT-2'sidir ( Bu makale, dil modeline temel bir girişle başlar ve GPT-2 modelini anlaşılması çok kolay bir şekilde adım adım açıklar. Okuyucuların bu makaleyi okumalarını şiddetle tavsiye ediyorum.
Harvard Üniversitesi Açıklamalı Transformatör, transformatörü anlamanın iyi bir yolu olan eksiksiz bir transformatör mimarisi uygulamak için PyTorch'u kullanıyor.
Ardından, GPT-2'yi bu mükemmel mevcut kaynaklar temelinde kodla uygulayalım ~
Özet
Soru cevaplama, makine çevirisi, okuduğunu anlama, vb. Gibi doğal dil işleme görevleri genellikle belirli görevler için veri kümeleri üzerinde öğrenim olarak denetlenir. Bir dil modelinin, WebText adı verilen milyonlarca web sayfasından oluşan yeni bir veri kümesi üzerinde eğitildiğinde, bu görevleri herhangi bir açık denetim olmaksızın öğrenmeye başladığını gösteriyoruz. En büyük modelimiz olan GPT-2, en gelişmiş dil modelleme sonuçlarını elde edebilen 1.5B parametreli bir transformatördür, ancak yine de WebText için uygun değildir. Modeldeki örnekler bu gelişmeleri yansıtır ve tutarlı metin paragrafları içerir. Bu bulgular, doğal olarak meydana gelen gösterilerden görevleri gerçekleştirmeyi öğrenebilen dil işleme sistemleri oluşturmak için umut verici bir yol sağlar.
Sıfır atış ayarı, dil modelinde ince ayar yapmayan ve doğrudan hedef veri kümesi üzerinde çıkarım yapan bir ayardır. Örneğin, WebText'te bir LM'yi önizleyin ve doğrudan Amazon film inceleme veri kümesindeki bir sonraki kelimeyi tahmin etmeye çalışın.
Model mimarisi (GPT-2)
LM'miz transformatör tabanlı bir mimari kullanır. Model, bazı değişikliklerle birlikte temel olarak OpenAI GPT modelinin ayrıntılarını takip eder. Katman normalizasyonu, önceden etkinleştirilen artık ağa benzer şekilde her bir alt bloğun girişine taşınır ve son kendinden odaklı bloktan sonra ek katman normalizasyonu eklenir. Kalan katmanların ağırlıklarını başlatma sırasında 1 / N faktörü ile ölçeklendiriyoruz, burada N kalan katman sayısıdır. Kelime dağarcığı 50257 kelimeye genişletildi. Ayrıca bağlam boyutunu 512'den 1024'e çıkardık ve daha büyük bir parti boyutu-512 kullandık.
Model özellikleri (GPT)
Modelimiz temelde orijinal transformatörün çalışma prensibini takip etmektedir. Gizli öz-dikkat başlıkları (768 boyutlu durumlar ve 12 dikkat kafası) olan 12 katmanlı bir yalnızca kod çözme transformatörü eğittik. Konum ileri besleme ağı için 3072 boyutlu dahili durum kullandık. Adam optimizasyon şemasını maksimum öğrenme oranı 2.5e-4 olarak kullanıyoruz. Öğrenme hızı, ilk 2000 güncellemede sıfırdan doğrusal olarak artar ve kosinüs çizelgeleme kullanılarak sıfıra tavlanır. 64 rastgele örneklenmiş mini grupta sürekli 512 jeton dizisi üzerinde 100 aşamayı eğittik. Layernorm, model boyunca yaygın olarak kullanıldığından, basit bir N (0, 0.02) ağırlık başlatma yeterlidir. Bir bytepair kodlama (BPE) sözlüğü kullanıyoruz. Ayrıca, içinde önerilen, tüm tarafsız veya kazanç ağırlıkları için w = 0.01 olan, geliştirilmiş bir L2 regülasyonu versiyonunu benimsedik. Aktivasyon fonksiyonu için Gauss Hatalı Doğrusal Birim (GELU) kullanıyoruz.
İthalat
meşale ithal
kopyayı içe aktar
torch.nn'yi nn olarak içe aktar
Torch.nn.F olarak işlevsel içe aktar
Torch.nn.modules'den ModuleList içe aktarma
torch.nn.modules.normalization'dan import LayerNorm
numpy'yi np olarak içe aktar
işletim sistemini içe aktar
tqdm'den içe aktar tqdm_notebook, trange
içe aktarma günlük kaydı
logging.basicConfig (düzey = logging.INFO)
logger = logging.getLogger
GPT-2 içinde transformatör kod çözücü
Transformatörü tanımlamak için kullanılan terimi yeniden kullanmak için, bunun bir sorgu (Q) ve bir dizi anahtar (K) ve değer (V) çiftlerinin bir işlevi olduğuna dikkat edin. Daha uzun dizileri işlemek için, Q ve K arasındaki nokta çarpımını sınırlayarak bellek kullanımını azaltmak için transformatörün çok başlı öz-dikkat mekanizmasını değiştirdik:
Dikkat, sorgu, anahtar ve değerin birleşimidir
sınıf Conv1D (nn.Module):
def __init __ (self, nx, nf): super .__ init__
self.nf = nf
w = meşale. boş (nx, nf)
nn.init.normal_ (w, std = 0,02)
self.weight = nn.Parameter (w)
self.bias = nn.Parameter (torch.zeros (nf))
def ileri (öz, x):
size_out = x.size + (self.nf,)
x = torch.addmm (self.bias, x.view (-1, x.size (-1)), self.weight)
x = x.view (* size_out)
dönüş x
CONV1D katman açıklaması
CONV1D katmanının kendisi doğrusal bir katman olarak kabul edilebilir. Esasen, bir başlangıç tensörü x (son boyut x.size (-1)) yansıtmak ve ona iletmektir ve son boyut self.nf'dir.
İşte aynı çıktının bir örneği:
d_model = 768
conv1d = Dönş1D (d_model, d_model * 3)
x = torch.rand (1,4, d_model) # batch_size = 1, seq_len = 4 ve embedding_sz = 768 dizisini temsil eder, "Merhaba nasılsın" gibi bir şey
x = dönş1d (x)
x.shape
> > torch.Size ()
Yukarıdaki örnekte gösterildiği gibi, CONV1D tarafından döndürülen tensörün son boyutu, başlangıç boyutunun 3 katıdır. Bunu girdiyi bir sorgu, anahtar ve değer matrisine dönüştürebilmek için yapıyoruz.
Daha sonra sorguyu, anahtarı ve değer matrisini aşağıdaki gibi alabilirsiniz:
sorgu, anahtar, değer = x.split (d_model, dim = -1)
query.shape, key.shape, value.shape
> > (torch.Size (), torch.Size (), torch.Size ())
Girişi Q, K ve V matrislerine dönüştürmenin başka bir yolu, ayrı Wq, Wk ve Wv matrislerine sahip olmaktır. Bunu bu makalenin altındaki ek bölümde açıkladım. Bu yöntemi daha sezgisel ve alakalı buluyorum, ancak bu makalede CONV1D katmanını kullandık çünkü Hugging Face'in CONV1D antrenman öncesi ağırlıklarını yeniden kullandık.
İleri katman yorumu
sınıf FeedForward (nn.Module):
def __init __ (self, dropout, d_model = 768, nx = 768 * 4):
super .__ init__
self.c_fc = Dönş1D (d_model, nx)
self.c_proj = Dönş1D (nx, d_model)
self.act = F.gelu
self.dropout = nn.Dropout (bırakma)
def ileri (öz, x):
dönüş self.dropout (self.c_proj (self.act (self.c_fc (x))))
Jay Alammar'ın makalesinde iyi bir açıklama var, yani yukarıda bahsedildiği gibi girdinin ilk önce dikkat katmanından nasıl geçtiği ve sonra ön katmana girdiği. İleri besleme ağı normal bir ağdır, dikkat katmanından (768) gelen çıktıyı kabul eder, nx (768 × 4) boyutlarına yansıtır, self.act (GELU) etkinleştirme işlevi ekler ve d_model'e geri yansıtır ( 768) ve bırakma (0.1) ekleyin.
Dikkat düzeyi yorumu
Aşağıdaki alıntı gazeteden alınmıştır: https://arxiv.org/abs/1706.03762.
Ölçek noktası ürün dikkatini
Dikkatimize "ölçek noktası ürün dikkati" diyoruz. Girdi, dk boyutunun sorgusunu ve anahtarını ve dv boyutunun değerini içerir. Sorgunun iç çarpımını hesaplamak için tüm tuşları kullanırız, dk'yi her bir tuşa böleriz ve ardından değerin ağırlığını elde etmek için softmax işlevini uygularız.
Pratik uygulamalarda, bir grup sorgunun dikkat fonksiyonunu aynı anda hesaplıyoruz, bunları bir Q matrisinde birleştiriyoruz ve anahtarları ve değerleri K ve V matrislerinde birleştiriyoruz. Çıktı matrisini şu şekilde hesaplıyoruz:
Çıktı matrisi Q, K ve V'nin bir kombinasyonudur
En sık kullanılan iki dikkat işlevi, toplamsal dikkat işlevi ve iç çarpım (çarpımsal) kuvvet işlevi dikkattir. Ölçek faktörü 1 / dk dışında, nokta ürün dikkati bizim algoritmamızla aynıdır. Ek dikkat, uyumluluk işlevini hesaplamak için tek bir gizli katmana sahip ileri beslemeli bir ağ kullanır. İkisi teorik karmaşıklıkta benzer olsa da, pratik uygulamalarda, nokta ürün dikkat hızı daha hızlıdır ve alan verimliliği daha yüksektir, çünkü yüksek düzeyde optimize edilmiş matris çarpım kodları kullanılarak uygulanabilir. Dk değeri küçük olduğunda, iki mekanizmanın performansı benzerdir, ancak dk değeri büyük olduğunda, ek dikkat, nokta ürün dikkatinden daha iyidir. Daha büyük dk değerleri için, iç çarpım sayısının artarak softmax işlevini minimum gradyanlı bir alana ittiğinden şüpheleniyoruz. Bu etkiye karşı koymak için iç çarpımı 1 / dk olarak ölçeklendirdik.
Dikkat katmanını kodda uygulamak için, daha önce açıklanan Q, K ve V matrislerini elde etmek için önce CONV1D katmanını kullanıyoruz.
Q, K ve V matrislerine sahip olduğumuzda, dikkat yapmak için _attn fonksiyonunu kullanabiliriz. Bu işlev, yukarıdaki dikkat nokta çarpımı formülünü tekrarlar.
sınıf Dikkat (nn.Module):
def __init __ (self, d_model = 768, n_head = 12, n_ctx = 1024, d_head = 64, bias = True, scale = False):
super .__ init__
self.n_head = n_head
self.d_model = d_model
self.c_attn = Dönş1D (d_model, d_model * 3)
self.scale = ölçek
self.softmax = nn.Softmax (dim = -1)
self.register_buffer ("bias", torch.tril (torch.ones (n_ctx, n_ctx)). view (1, 1, n_ctx, n_ctx))
self.dropout = nn.Dropout (0.1)
self.c_proj = Dönş1D (d_model, d_model)
def split_heads (self, x):
"dönüş şekli"
new_shape = x.size + (self.n_head, x.size (-1) // self.n_head)
x = x.view (* new_shape)
return x.permute (0, 2, 1, 3)
def _attn (self, q, k, v, attn_mask = Yok):
puanlar = torch.matmul (q, k.transpose (-2, -1))
self.scale ise: puanlar = puanlar / math.sqrt (v.size (-1))
nd, ns = puanlar.size (-2), puanlar.size (-1)
attn_mask None değilse: puanlar = puanlar + attn_mask
puanlar = self.softmax (puanlar)
puanlar = self.dropout (puanlar)
çıktılar = torch.matmul (puanlar, v)
dönüş çıktıları
def merge_heads (self, x):
x = x.permute (0, 2, 1, 3). bitişik
new_shape = x.size + (x.size (-2) * x.size (-1),)
return x.view (* new_shape)
def ileri (öz, x):
x = self.c_attn (x) #yeni `x` şekil-``
q, k, v = x.split (self.d_model, dim = 2)
q, k, v = self.split_heads (q), self.split_heads (k), self.split_heads (v)
out = self._attn (q, k, v)
out = self.merge_heads (out)
out = self.c_proj (çıkış)
geri dönmek
Dikkat çekmenin başka bir yolu bu blogun altındaki ek bölümde açıklanmıştır. Araştırma makaleleri ile karşılaştırmayı daha sezgisel ve daha kolay buluyorum. Girişi Q, K ve V matrislerine dönüştürmek için CONV1D yerine doğrusal katmanlar kullanır. Kullanmamamızın nedeni, Hugging Face'ten tek boyutlu bir katmana dönüştürmek için önceden eğitilmiş ağırlıklar kullanmamızdır.
Çok başlı dikkat
Aşağıdaki paragraf, "Tek ihtiyacın olan dikkat" makalesinden alınmıştır.
Sırasıyla dk, dk ve dv boyutlarına sorguları, anahtarları ve değerleri doğrusal olarak eşlemek için farklı, öğrenilmiş doğrusal eşlemeler kullanmanın daha iyi olduğunu bulduk. Daha sonra, bu sorguların, anahtarların ve değerlerin örtük versiyonunda, dv boyutlu çıktı değerleri oluşturmak için dikkat fonksiyonunu paralel olarak yürütürüz. Bu değerler bağlanır ve ardından aşağıdaki şekilde gösterildiği gibi nihai değeri elde etmek için tekrar eşlenir:
Çok başlı dikkat mekanizması, modelin farklı konumlardaki farklı temsil alt uzaylarından gelen bilgilere odaklanmasına izin verir.
Çok başlı dikkat denklemi
Bu çalışmada h = 8 paralel dikkat katmanları veya başları kullandık. Bunların arasında hepimiz dk = dv = dmodel / h = 64 kullanıyoruz. Her bir kafanın boyutsallığı azaltıldığından, toplam hesaplama maliyeti, tam boyutlu tek bir kafa dikkatine benzer.
Kafanızı karıştırmayın, esasen yaptığımız şey Q, K ve V matrislerine bir boyut eklemektir. Diğer bir deyişle, eğer bu matrislerin önceki boyutu ise, yani bu matrisler projelendirilir ve boyutu olur. GPT-2, 12 paralel kafa kullanır. Q, K ve V matrislerini split_heads fonksiyonuna ayırıyoruz. Son olarak, paralel dikkat uygulayarak bir çıktı aldığımızda, onu birleştirme başlığına bağlayıp boyut matrisine geri dönüyoruz.
Kodda GPT-2 model mimarisi
Şimdiye kadar, çok başlı dikkat ve ileri besleme katmanını uyguladık. Yukarıdaki şekilde gösterildiği gibi, bu iki katman, transformatör şifre çözücü bloğunun yapı bloklarını oluşturur. GPT-2, 12 trafo grubundan oluşur.
Bu, Jay Alammar'ın makalesinde şu şekilde gösterilmektedir:
12 kod çözme bloğundan oluşan GPT mimarisi
trafo kod çözücü blok açıklaması
sınıf TransformerBlock (nn.Module):
def __init __ (self, d_model = 768, n_head = 12, dropout = 0.1):
super (TransformerBlock, self) .__ init__
self.attn = Dikkat (d_model = 768, n_head = 12, d_head = 64, n_ctx = 1024, bias = True, scale = False)
self.feedforward = FeedForward (bırakma = 0.1, d_model = 768, nx = 768 * 4)
self.ln_1 = LayerNorm (d_model)
self.ln_2 = LayerNorm (d_model)
def ileri (öz, x):
x = x + self.attn (self.ln_1 (x))
x = x + self.feedforward (self.ln_2 (x))
dönüş x
Transformatör grubu, GPT-2 mimari modeli spesifikasyonunda belirtildiği gibi, bir dikkat katmanından ve bir ileri besleme katmanından oluşur: katman normalizasyonu, alt blokların dikkat ve ileri beslendiği her bir alt bloğun girişine taşınır.
Bu nedenle, transformatör kod çözücü bloğunda, önce girişi bir LayerNorm'a ve ardından ilk alt dikkat bloğuna geçiririz. Daha sonra, bu alt bloğun çıktısını tekrar LayerNorm'a ve son olarak feedforward katmanına iletiyoruz.
GPT-2 mimarisi açıklaması
GPT belgesinde belirtildiği gibi: Gizli öz-ilgi kafaları olan (768 boyut ve 12 dikkat kafası) 12 katmanlı bir yalnızca kod çözme transformatörü eğittik.
Bu nedenle, eksiksiz GPT-2 mimarisi 12 kez kopyalanan bir TransformerBlock'tur.
def _get_clones (modül, n): ModuleList () döndür
sınıf GPT2 (nn.Module):
def __init __ (self, nlayers = 12, n_ctx = 1024, d_model = 768, vcb_sz = 50257):
super (GPT2, self) .__ init__
self.nlayers = nlayers
blok = TransformerBlock (d_model = 768, n_head = 12, dropout = 0.1)
self.h = _get_clones (blok, 12)
self.wte = nn.Embedding (vcb_sz, d_model)
self.wpe = nn.Embedding (n_ctx, d_model)
self.drop = nn.Dropout (0.1)
self.ln_f = LayerNorm (d_model)
self.out = nn.Linear (d_model, vcb_sz, bias = False)
self.loss_fn = nn.CrossEntropyLoss
self.init_weights
def init_weights (öz):
self.out.weight = self.wte.weight
self.apply (self._init_weights)
def _init_weights (öz, modül):
örnek ise (modül, (nn.Linear, nn.Embedding, Conv1D)):
module.weight.data.normal_ (ortalama = 0.0, std = 0.02)
isinstance (module, (nn.Linear, Conv1D)) ve module.bias None değilse:
module.bias.data.zero_
elif isinstance (modül, nn.LayerNorm):
module.bias.data.zero_
module.weight.data.fill_ (1.0)
def forward (self, src, etiketler = Yok, pos_ids = Yok):
pos_ids None ise: pos_ids = torch.arange (0, src.size (-1)). unsqueeze (0)
inp = self.drop ((self.wte (src) + self.wpe (pos_ids)))
aralıktaki i için (self.nlayers): inp = self.h (inp)
inp = self.ln_f (inp)
logits = self.out (inp)
çıktılar = (günlükler,) + (inp,)
etiketler Yok değilse:
shift_logits = logits.contiguous
shift_labels = labels.contiguous
kayıp = self.loss_fn (shift_logits.view (-1, shift_logits.size (-1)), shift_labels.view (-1))
çıktılar = (kayıp,) + çıktılar
dönüş çıktıları
dönüş günlükleri
Bahsetmediğim şey konum kodlama ve işaret yerleştirmedir. "Hey" veya "merhaba" gibi kelimeleri doğrudan modele geçiremediğimiz için, önce girdiyi belirtiyoruz. Ardından, işareti bir sayı olarak göstermek için yerleştirmeyi kullanırız. Jay Alammar tarafından yazılan bu makale ( yerleştirmeyi çok iyi açıklıyor.
Ek olarak, giriş kelimelerini sırayla geçiren RNN'den farklı olarak, transformatör giriş matrisini paralel olarak kabul eder ve böylece giriş kelimelerinin konum anlamını kaybeder. Bu kaybı telafi etmek için, jetonları modele yerleştirmeden önce, dizideki kelimelerin sırasını belirten bir sinyal olan Konumsal Kodlama ekledik. Daha önce de belirtildiği gibi, GPT-2'nin bağlam boyutu 1024 olduğundan, konum kodunun boyutu.
Alıntılanan konumdan kodlayın (
Bu nedenle, GPT-2 mimarisinin girdisi, bir bırakma yoluyla etiket gömme ve konum kodlamasının toplamıdır. Giriş matrisini elde ettikten sonra, GPT-2 mimarisinin her biri iki alt katmandan oluşan dikkat ve ileri beslemeden oluşan bir transformatör kod çözücü bloğu olan 12 katmanından geçmesine izin veriyoruz. İnternet.
Dil modelleme veya sınıflandırma
GPT-2'yi dil modeli olarak kullanırken, girdiyi son katman formuna geçiririz ve boyut çıktısını almak için son boyut (50257) olan doğrusal katmanı geçiririz. Bu çıktı, bir sonraki kelime haznesi girdisini temsil eder Artık bir softmax katmanını kolayca geçebilir ve kelimenin kelime haznesindeki konumunu en büyük olasılıkla elde etmek için argmax kullanabiliriz.
Sınıflandırma görevleri için, GPT-2 mimarisinden alınan çıktıyı, her kategorinin olasılığını (burada n, kategori sayısını temsil eder) elde etmek için doğrusal bir boyut katmanından geçirebilir ve ardından en yüksek tahmin edilen kategoriyi elde etmek için softmax'ten geçebiliriz , Ve sınıflandırma için mimariyi eğitmek için CrossEntropyLoss'u kullanın.
GPT-2'nin arkasındaki tüm sihir budur. RNN'den farklı, kod çözücü tabanlı bir transformatör yapısıdır.Konum kodlamasına paralel giriş kullanır ve 12 transformatör kod çözücü katmanının her biri aracılığıyla geri döner (çok başlı dikkat ve ileri beslemeli ağlardan oluşur) Nihai çıktı.
Bu modelin dil modeli görevindeki gerçek etkisine bakalım.
Örnek metin oluşturmak için Hugging Face önceden eğitilmiş ağırlıkları kullanın
Öncelikle modeli Hugging Face tarafından sağlanan önceden eğitilmiş ağırlıklarla başlatalım.
model = GPT2
# yüzü sarılmaktan önceden eğitilmiş ağırlıkları yükle
# https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-pytorch_model.bin dosyasını "."
model_dict = model.state_dict #currently with random initializationstate_dict = torch.load ("./ gpt2-pytorch_model.bin") # önceden eğitilmiş ağırlıklar
old_keys =
new_keys =
state_dict.keys içindeki anahtar için:
anahtarda "mlp" ise: # Kucaklayan yüz durumu diktesi, ileri besleme ağına mlp olarak başvurursa, bu ağırlıkları yeniden kullanabilmek için "ileri besleme" için değiştirilmesi gerekir
new_key = key.replace ("mlp", "ileri besleme")
new_keys.append (new_key)
old_keys.append (anahtar)
old_key, zip içinde new_key için (eski_anahtarlar, yeni_anahtarlar): state_dict = state_dict.pop (eski_key)
pretrained_dict = {k için k: v, state_dict.items içinde v, model_dict içinde k ise}
model_dict.update (pretrained_dict)
model.load_state_dict (model_dict)
model.eval #model, şimdi önceden eğitilmiş ağırlıklarla başlatıldığı için çıkarım modunda
Şimdi metni oluşturalım. Kelimeleri giriş düğünlerine dönüştürmek için Hugging Face'in önceden eğitilmiş jetonlaştırıcısını kullanacağız.
transformatörlerden GPT2Tokenizer ithal
tokenizer = GPT2Tokenizer.from_pretrained ("gpt2")
bağlam = torch.tensor ()
def üretmek (bağlam, ntok = 20):
aralıktaki _ için (ntok):
out = model (bağlam)
logits = out
indices_to_remove = günlük kayıtları = np.NINF
next_tok = torch.multinomial (F.softmax (logits, dim = -1), num_samples = 1) .squeeze (1)
bağlam = torch.cat (, dim = -1)
dönüş bağlamı
out = generate (bağlam, ntok = 20) tokenizer.decode (out)
> > Hükümetin yapacağı çalışma, "Dünya gezegeni tüm ışığın kaynağıdır" diyor.
Ek içerik
Dikkat çekmenin bir başka yolu da fast.ai'nin NLP kursudur (https://github.com/fastai/course-nlp/blob/master/8-translation-transformer.ipynb), bunu daha sezgisel buldum Yöntem aşağıdaki gibidir:
class Attention_FASTAI (nn.Module):
def __init __ (self, d_model = 768, n_head = 12, d_head = 64, n_ctx = 1024, bias = True, scale = False):
super .__ init__
self.n_head = n_head
self.d_head = d_head
self.softmax = nn.Softmax (dim = -1)
self.scale = ölçek
self.atn_drop = nn.Dropout (0.1)
self.wq, self.wk, self.wv =
def split_heads (self, x, layer, bs):
x = katman (x)
return x.view (bs, x.size (1), self.n_head, self.d_head) .permute (0,2,1,3)
def _attn (self, q, k, v, attn_mask = Yok):
puanlar = torch.matmul (q, k.transpose (-2, -1))
self.scale ise: puanlar = puanlar / math.sqrt (v.size (-1))
attn_mask None değilse:
puanlar = score.float.masked_fill (attn_mask, -float ('inf')). type_as (puanlar)
attn_prob = self.atn_drop (self.softmax (puanlar))
attn_vec = attn_prob @ v
attn_vec döndür
def merge_heads (self, x, bs, seq_len):
x = x.permute (0, 2, 1, 3). bitişik
return x.view (bs, seq_len, -1)
def ileri (öz, q, k, v, maske = Yok):
bs, seq_len = q.size (0), q.size (1)
wq, wk, wv = harita (lambda o: self.split_heads (* o, bs),
zip ((q, k, v), (self.wq, self.wk, self.wv)))
attn_vec = self._attn (wq, wk, wv)
attn_vec = self.merge_heads (attn_vec, bs, seq_len)
attn_vec döndür
Yukarıdaki uygulama ile benimsediğimiz uygulama yöntemi arasındaki temel fark, bu uygulamanın CONV1D kullanmaması, ancak önce x girdisini self.wq, self.wk ve self.wv doğrusal katmanlarına geçirerek wq, wk ve wv matrislerini elde etmesidir, Sonra bir sonraki adım öncekiyle aynı.
Sonuna yaz
Açık kaynaklı bir NLP kitaplığı oluşturduğu ve kullanılabilecek birçok önceden eğitilmiş model sağladığı için Hugging Face'e özel teşekkürler. Daha önce de belirtildiği gibi, bu makaledeki kod doğrudan Hugging Face kitaplığından geliyor. Resimli GPT-2 ( GPT-2 ile ilgili en kapsamlı bloglardan biridir. Son olarak, Harvard NLP's The Annotated Transformer (https://nlp.seas.harvard.edu/2018/04/03/attention.html), PyTorch'ta Transformers'ın harika ve öğrenmesi kolay bir uygulamasını tamamladı.
aracılığıyla: https://amaarora.github.io/2020/02/18/annotatedGPT2.html
Lei Feng Ağı Lei Feng Ağı Lei Feng Ağı