AI-For-Beginners/translations/tr/lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb

414 lines
21 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Üretici Ağlar\n",
"\n",
"Tekrarlayan Sinir Ağları (Recurrent Neural Networks - RNN'ler) ve Uzun Kısa Süreli Bellek Hücreleri (Long Short Term Memory Cells - LSTM'ler) ile Gated Recurrent Units (GRU'lar) gibi kapılı hücre varyantları, dil modelleme için bir mekanizma sağladı, yani kelime sıralamasını öğrenebilir ve bir dizideki bir sonraki kelime için tahminlerde bulunabilirler. Bu, RNN'leri **üretici görevler** için kullanmamıza olanak tanır; örneğin, sıradan metin üretimi, makine çevirisi ve hatta görüntü altyazılama gibi.\n",
"\n",
"Önceki birimde tartıştığımız RNN mimarisinde, her RNN birimi bir sonraki gizli durumu çıktı olarak üretiyordu. Ancak, her tekrarlayan birime başka bir çıktı daha ekleyebiliriz; bu, bir **dizi** (orijinal dizinin uzunluğuna eşit) üretmemizi sağlar. Dahası, her adımda bir girdi kabul etmeyen ve sadece bir başlangıç durum vektörü alarak bir çıktı dizisi üreten RNN birimleri de kullanabiliriz.\n",
"\n",
"Bu not defterinde, metin üretmemize yardımcı olan basit üretici modeller üzerine odaklanacağız. Basitlik adına, harf harf metin üreten bir **karakter düzeyinde ağ** oluşturalım. Eğitim sırasında, bir metin korpusu almamız ve bunu harf dizilerine bölmemiz gerekecek.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"import numpy as np\n",
"from torchnlp import *\n",
"train_dataset,test_dataset,classes,vocab = load_dataset()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Karakter kelime dağarcığı oluşturma\n",
"\n",
"Karakter seviyesinde bir üretici ağ oluşturmak için metni kelimeler yerine bireysel karakterlere ayırmamız gerekir. Bu, farklı bir ayrıştırıcı tanımlayarak yapılabilir:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary size = 82\n",
"Encoding of 'a' is 1\n",
"Character with code 13 is c\n"
]
}
],
"source": [
"def char_tokenizer(words):\n",
" return list(words) #[word for word in words]\n",
"\n",
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" counter.update(char_tokenizer(line))\n",
"vocab = torchtext.vocab.vocab(counter)\n",
"\n",
"vocab_size = len(vocab)\n",
"print(f\"Vocabulary size = {vocab_size}\")\n",
"print(f\"Encoding of 'a' is {vocab.get_stoi()['a']}\")\n",
"print(f\"Character with code 13 is {vocab.get_itos()[13]}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Haydi, veri setimizden metni nasıl kodlayabileceğimize dair bir örneğe bakalım:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([ 0, 1, 2, 2, 3, 4, 5, 6, 3, 7, 8, 1, 9, 10, 3, 11, 2, 1,\n",
" 12, 3, 7, 1, 13, 14, 3, 15, 16, 5, 17, 3, 5, 18, 8, 3, 7, 2,\n",
" 1, 13, 14, 3, 19, 20, 8, 21, 5, 8, 9, 10, 22, 3, 20, 8, 21, 5,\n",
" 8, 9, 10, 3, 23, 3, 4, 18, 17, 9, 5, 23, 10, 8, 2, 2, 8, 9,\n",
" 10, 24, 3, 0, 1, 2, 2, 3, 4, 5, 9, 8, 8, 5, 25, 10, 3, 26,\n",
" 12, 27, 16, 26, 2, 27, 16, 28, 29, 30, 1, 16, 26, 3, 17, 31, 3, 21,\n",
" 2, 5, 9, 1, 23, 13, 32, 16, 27, 13, 10, 24, 3, 1, 9, 8, 3, 10,\n",
" 8, 8, 27, 16, 28, 3, 28, 9, 8, 8, 16, 3, 1, 28, 1, 27, 16, 6])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def enc(x):\n",
" return torch.LongTensor(encode(x,voc=vocab,tokenizer=char_tokenizer))\n",
"\n",
"enc(train_dataset[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Bir üretici RNN eğitmek\n",
"\n",
"RNN'yi metin üretmek üzere eğitme yöntemimiz şu şekilde olacak. Her adımda, `nchars` uzunluğunda bir karakter dizisi alacağız ve ağdan her bir giriş karakteri için bir sonraki çıkış karakterini üretmesini isteyeceğiz:\n",
"\n",
"![RNN'nin 'HELLO' kelimesini üretme örneğini gösteren bir görsel.](../../../../../translated_images/tr/rnn-generate.56c54afb52f9781d.webp)\n",
"\n",
"Gerçek senaryoya bağlı olarak, *dizinin sonu* `<eos>` gibi bazı özel karakterleri de dahil etmek isteyebiliriz. Ancak bizim durumumuzda, ağı sonsuz metin üretimi için eğitmek istiyoruz, bu nedenle her bir dizinin boyutunu `nchars` tokenine eşit olarak sabitleyeceğiz. Sonuç olarak, her bir eğitim örneği `nchars` giriş ve `nchars` çıkıştan (giriş dizisinin bir sembol sola kaydırılmış hali) oluşacak. Minibatch, bu tür birkaç diziden oluşacak.\n",
"\n",
"Minibatch'leri oluşturma yöntemimiz, uzunluğu `l` olan her bir haber metnini alıp, ondan tüm olası giriş-çıkış kombinasyonlarını üretmek olacak (bu kombinasyonlardan `l-nchars` kadar olacaktır). Bu kombinasyonlar bir minibatch oluşturacak ve minibatch boyutları her bir eğitim adımında farklılık gösterecektir.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[ 0, 1, 2, ..., 28, 29, 30],\n",
" [ 1, 2, 2, ..., 29, 30, 1],\n",
" [ 2, 2, 3, ..., 30, 1, 16],\n",
" ...,\n",
" [20, 8, 21, ..., 1, 28, 1],\n",
" [ 8, 21, 5, ..., 28, 1, 27],\n",
" [21, 5, 8, ..., 1, 27, 16]]),\n",
" tensor([[ 1, 2, 2, ..., 29, 30, 1],\n",
" [ 2, 2, 3, ..., 30, 1, 16],\n",
" [ 2, 3, 4, ..., 1, 16, 26],\n",
" ...,\n",
" [ 8, 21, 5, ..., 28, 1, 27],\n",
" [21, 5, 8, ..., 1, 27, 16],\n",
" [ 5, 8, 9, ..., 27, 16, 6]]))"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nchars = 100\n",
"\n",
"def get_batch(s,nchars=nchars):\n",
" ins = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
" outs = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
" for i in range(len(s)-nchars):\n",
" ins[i] = enc(s[i:i+nchars])\n",
" outs[i] = enc(s[i+1:i+nchars+1])\n",
" return ins,outs\n",
"\n",
"get_batch(train_dataset[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Şimdi jeneratör ağını tanımlayalım. Bu ağ, önceki bölümde tartıştığımız herhangi bir yinelemeli hücreye (basit, LSTM veya GRU) dayanabilir. Bizim örneğimizde LSTM kullanacağız.\n",
"\n",
"Ağ karakterleri girdi olarak aldığı ve kelime dağarcığı boyutu oldukça küçük olduğu için, bir gömme katmanına ihtiyacımız yok; tekil kodlanmış (one-hot-encoded) girdi doğrudan LSTM hücresine gidebilir. Ancak, karakter numaralarını girdi olarak verdiğimiz için, bunları LSTM'ye geçirmeden önce tekil kodlamamız gerekiyor. Bu, `forward` geçişi sırasında `one_hot` fonksiyonunu çağırarak yapılır. Çıktı kodlayıcı, gizli durumu tekil kodlanmış bir çıktıya dönüştürecek bir doğrusal katman olacaktır.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class LSTMGenerator(torch.nn.Module):\n",
" def __init__(self, vocab_size, hidden_dim):\n",
" super().__init__()\n",
" self.rnn = torch.nn.LSTM(vocab_size,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, vocab_size)\n",
"\n",
" def forward(self, x, s=None):\n",
" x = torch.nn.functional.one_hot(x,vocab_size).to(torch.float32)\n",
" x,s = self.rnn(x,s)\n",
" return self.fc(x),s"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Eğitim sırasında, üretilen metni örneklemek isteyebiliriz. Bunu yapmak için, başlangıç dizesi `start` ile başlayarak, uzunluğu `size` olan bir çıktı dizesi üretecek `generate` fonksiyonunu tanımlayacağız.\n",
"\n",
"Bu işlem şu şekilde çalışır: İlk olarak, başlangıç dizesinin tamamını ağdan geçiririz ve çıktı durumu `s` ile bir sonraki tahmin edilen karakter `out` elde edilir. `out` bir one-hot kodlaması olduğundan, sözlükteki karakterin indeksini `argmax` ile alırız (`nc`) ve ardından `itos` kullanarak gerçek karakteri bulur ve bunu sonuç karakterleri listesi `chars`a ekleriz. Bu süreç, gerekli sayıda karakter üretmek için `size` kez tekrarlanır.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def generate(net,size=100,start='today '):\n",
" chars = list(start)\n",
" out, s = net(enc(chars).view(1,-1).to(device))\n",
" for i in range(size):\n",
" nc = torch.argmax(out[0][-1])\n",
" chars.append(vocab.get_itos()[nc])\n",
" out, s = net(nc.view(1,-1),s)\n",
" return ''.join(chars)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Haydi eğitime başlayalım! Eğitim döngüsü, önceki örneklerimizin neredeyse aynısıdır, ancak doğruluk yerine her 1000 epoch'ta bir örneklenmiş üretilen metni yazdırıyoruz.\n",
"\n",
"Kaybı hesaplama şekline özel dikkat gösterilmelidir. Tek-sıcak-kodlanmış çıktı `out` ve beklenen metin `text_out` (karakter indekslerinin listesi) verildiğinde kaybı hesaplamamız gerekiyor. Neyse ki, `cross_entropy` fonksiyonu, ilk argüman olarak normalleştirilmemiş ağ çıktısını ve ikinci argüman olarak sınıf numarasını bekler, ki bu tam olarak elimizde olan şeydir. Ayrıca minibatch boyutu üzerinde otomatik ortalama alma işlemi gerçekleştirir.\n",
"\n",
"Eğitimi çok uzun sürmemesi için `samples_to_train` örnekleriyle sınırlandırıyoruz. Daha uzun bir eğitim denemenizi ve birkaç epoch boyunca çalışmayı denemenizi öneririz (bu durumda bu kodun etrafında başka bir döngü oluşturmanız gerekecektir).\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current loss = 4.398899078369141\n",
"today sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr s\n",
"Current loss = 2.161320447921753\n",
"today and to the tor to to the tor to to the tor to to the tor to to the tor to to the tor to to the tor t\n",
"Current loss = 1.6722588539123535\n",
"today and the court to the could to the could to the could to the could to the could to the could to the c\n",
"Current loss = 2.423795223236084\n",
"today and a second to the conternation of the conternation of the conternation of the conternation of the \n",
"Current loss = 1.702607274055481\n",
"today and the company to the company to the company to the company to the company to the company to the co\n",
"Current loss = 1.692358136177063\n",
"today and the company to the company to the company to the company to the company to the company to the co\n",
"Current loss = 1.9722288846969604\n",
"today and the control the control the control the control the control the control the control the control \n",
"Current loss = 1.8705692291259766\n",
"today and the second to the second to the second to the second to the second to the second to the second t\n",
"Current loss = 1.7626899480819702\n",
"today and a security and a security and a security and a security and a security and a security and a secu\n",
"Current loss = 1.5574463605880737\n",
"today and the company and the company and the company and the company and the company and the company and \n",
"Current loss = 1.5620026588439941\n",
"today and the be that the be the be that the be the be that the be the be that the be the be that the be t\n"
]
}
],
"source": [
"net = LSTMGenerator(vocab_size,64).to(device)\n",
"\n",
"samples_to_train = 10000\n",
"optimizer = torch.optim.Adam(net.parameters(),0.01)\n",
"loss_fn = torch.nn.CrossEntropyLoss()\n",
"net.train()\n",
"for i,x in enumerate(train_dataset):\n",
" # x[0] is class label, x[1] is text\n",
" if len(x[1])-nchars<10:\n",
" continue\n",
" samples_to_train-=1\n",
" if not samples_to_train: break\n",
" text_in, text_out = get_batch(x[1])\n",
" optimizer.zero_grad()\n",
" out,s = net(text_in)\n",
" loss = torch.nn.functional.cross_entropy(out.view(-1,vocab_size),text_out.flatten()) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" if i%1000==0:\n",
" print(f\"Current loss = {loss.item()}\")\n",
" print(generate(net))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Bu örnek zaten oldukça iyi bir metin üretiyor, ancak birkaç şekilde daha da geliştirilebilir:\n",
"\n",
"* **Daha iyi minibatch oluşturma**. Veriyi eğitime hazırlama şeklimiz, her bir örnekten bir minibatch oluşturmak şeklindeydi. Bu ideal bir yöntem değil, çünkü minibatch'ler farklı boyutlarda oluyor ve bazıları metin `nchars` değerinden küçük olduğu için oluşturulamıyor. Ayrıca, küçük minibatch'ler GPU'yu yeterince verimli kullanmıyor. Daha akıllıca bir yaklaşım, tüm örneklerden büyük bir metin parçası almak, ardından tüm giriş-çıkış çiftlerini oluşturmak, bunları karıştırmak ve eşit boyutta minibatch'ler oluşturmak olacaktır.\n",
"\n",
"* **Çok katmanlı LSTM**. 2 veya 3 katmanlı LSTM hücrelerini denemek mantıklı olabilir. Daha önceki bölümde bahsettiğimiz gibi, LSTM'nin her bir katmanı metinden belirli desenleri çıkarır ve karakter düzeyinde bir jeneratör durumunda, daha alt seviyedeki LSTM'nin heceleri çıkarmaktan, daha üst seviyelerin ise kelimeleri ve kelime kombinasyonlarını çıkarmaktan sorumlu olmasını bekleyebiliriz. Bu, LSTM yapıcısına katman sayısı parametresi geçirerek basitçe uygulanabilir.\n",
"\n",
"* **GRU birimleri** ile denemeler yapmak isteyebilirsiniz ve hangilerinin daha iyi performans gösterdiğini görebilirsiniz. Ayrıca **farklı gizli katman boyutları** ile de deneyebilirsiniz. Çok büyük bir gizli katman aşırı öğrenmeye (örneğin, ağın metni birebir öğrenmesine) yol açabilir, küçük bir boyut ise iyi sonuçlar üretmeyebilir.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Yumuşak metin üretimi ve sıcaklık\n",
"\n",
"`generate` fonksiyonunun önceki tanımında, oluşturulan metindeki bir sonraki karakter olarak her zaman en yüksek olasılığa sahip karakteri seçiyorduk. Bu durum, metnin sık sık aynı karakter dizileri arasında \"dönmesine\" neden oluyordu, örneğin şu örnekte olduğu gibi:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Ancak, bir sonraki karakter için olasılık dağılımına baktığımızda, en yüksek olasılıklar arasındaki farkın çok büyük olmayabileceğini görebiliriz. Örneğin, bir karakterin olasılığı 0.2, diğerinin ise 0.19 olabilir, vb. Örneğin, '*play*' dizisindeki bir sonraki karakteri ararken, bir sonraki karakterin boşluk veya **e** (kelime *player*'daki gibi) olması eşit derecede olasıdır.\n",
"\n",
"Bu durum, her zaman daha yüksek olasılığa sahip karakteri seçmenin \"adil\" olmadığını gösteriyor, çünkü ikinci en yüksek olasılığa sahip karakteri seçmek de anlamlı bir metne yol açabilir. Daha mantıklı olan, ağ çıktısının verdiği olasılık dağılımından karakterleri **örneklemektir**.\n",
"\n",
"Bu örnekleme, **multinomial dağılımı** olarak adlandırılan bir yöntemi uygulayan `multinomial` fonksiyonu kullanılarak yapılabilir. Bu **yumuşak** metin üretimini uygulayan bir fonksiyon aşağıda tanımlanmıştır:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--- Temperature = 0.3\n",
"Today and a company and complete an all the land the restrational the as a security and has provers the pay to and a report and the computer in the stand has filities and working the law the stations for a company and with the company and the final the first company and refight of the state and and workin\n",
"\n",
"--- Temperature = 0.8\n",
"Today he oniis its first to Aus bomblaties the marmation a to manan boogot that pirate assaid a relaid their that goverfin the the Cappets Ecrotional Assonia Cition targets it annight the w scyments Blamity #39;s TVeer Diercheg Reserals fran envyuil that of ster said access what succers of Dour-provelith\n",
"\n",
"--- Temperature = 1.0\n",
"Today holy they a 11 will meda a toket subsuaties, engins for Chanos, they's has stainger past to opening orital his thempting new Nattona was al innerforder advan-than #36;s night year his religuled talitatian what the but with Wednesday to Justment will wemen of Mark CCC Camp as Timed Nae wome a leaders\n",
"\n",
"--- Temperature = 1.3\n",
"Today gpone 2.5 fech atcusion poor cocles toparsdorM.cht Line Pamage put 43 his calt lowed to the book, that has authh-the silia rruch ailing to'ory andhes beutirsimi- Aefffive heading offil an auf eacklets is charged evis, Gunymy oy) Mony has it after-sloythyor loveId out filme, the Natabl -Najuntaxiggs \n",
"\n",
"--- Temperature = 1.8\n",
"Today plary, P.slan chly\\401 mardregationly #39;t 8.1Mide) closes ,filtcon alfly playin roven!\\grea.-QFBEP: Iss onfarchQ/itilia CCf Zivesigntwasta orce.-Peul-aw.uicrin of fuglinfsut aftaningwo, MIEX awayew Aice Woiduar Corvagiugge oppo esig ThusBratourid canthly-RyI.co lagitems\\eexciaishes.conBabntusmor I\n",
"\n"
]
}
],
"source": [
"def generate_soft(net,size=100,start='today ',temperature=1.0):\n",
" chars = list(start)\n",
" out, s = net(enc(chars).view(1,-1).to(device))\n",
" for i in range(size):\n",
" #nc = torch.argmax(out[0][-1])\n",
" out_dist = out[0][-1].div(temperature).exp()\n",
" nc = torch.multinomial(out_dist,1)[0]\n",
" chars.append(vocab.get_itos()[nc])\n",
" out, s = net(nc.view(1,-1),s)\n",
" return ''.join(chars)\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"--- Temperature = {i}\\n{generate_soft(net,size=300,start='Today ',temperature=i)}\\n\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sıcaklık olarak adlandırılan bir parametre daha tanıttık, bu parametre en yüksek olasılığa ne kadar sıkı bağlı kalmamız gerektiğini belirtmek için kullanılır. Eğer sıcaklık 1.0 ise, adil bir multinomial örnekleme yaparız ve sıcaklık sonsuza yaklaştığında - tüm olasılıklar eşit hale gelir ve bir sonraki karakteri rastgele seçeriz. Aşağıdaki örnekte, sıcaklığı çok fazla artırdığımızda metnin anlamsız hale geldiğini ve sıcaklık 0'a yaklaştığında \"döngüsel\" şekilde zor üretilmiş bir metni andırdığını gözlemleyebiliriz.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Feragatname**: \nBu belge, AI çeviri hizmeti [Co-op Translator](https://github.com/Azure/co-op-translator) kullanılarak çevrilmiştir. Doğruluk için çaba göstersek de, otomatik çevirilerin hata veya yanlışlıklar içerebileceğini lütfen unutmayın. Belgenin orijinal dili, yetkili kaynak olarak kabul edilmelidir. Kritik bilgiler için profesyonel insan çevirisi önerilir. Bu çevirinin kullanımından kaynaklanan yanlış anlamalar veya yanlış yorumlamalar için sorumluluk kabul etmiyoruz.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "7673cd150d96c74c6d6011460094efb4",
"translation_date": "2025-08-28T14:07:29+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
"language_code": "tr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}