{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  },
  "accelerator": "GPU",
  "colab": {
   "provenance": []
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PyTorch ile Derin Öğrenme · Autograd: hesaplama grafiği ve geri yayılım\n",
    "\n",
    "Macademia · Büyükler için"
   ],
   "id": "c00"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **Colab'da çalıştırma:** Bu dosyayı [colab.research.google.com](https://colab.research.google.com) adresinde **Dosya → Not defteri yükle** ile aç. GPU için **Çalışma zamanı → Çalışma zamanı türünü değiştir → T4 GPU** seç. Hücreleri yukarıdan aşağı sırayla çalıştır (Shift + Enter). PyTorch Colab'da kuruludur; bilgisayarında çalıştırmak istersen `pip install torch torchvision`."
   ],
   "id": "c01"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch, random, numpy as np\n",
    "torch.manual_seed(0); random.seed(0); np.random.seed(0)          # tekrarlanabilirlik\n",
    "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "print('PyTorch', torch.__version__, '| device:', device)"
   ],
   "id": "c02"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Tek nöron\n",
    "$z = w_1x_1 + w_2x_2 + b$, $y = \\tanh(z)$, $L = (y - t)^2$"
   ],
   "id": "c03"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x1, x2, t = torch.tensor(2.0), torch.tensor(0.5), torch.tensor(0.8)\n",
    "w1 = torch.tensor(-0.5, requires_grad=True)\n",
    "w2 = torch.tensor(1.2, requires_grad=True)\n",
    "b = torch.tensor(0.3, requires_grad=True)\n",
    "y = torch.tanh(w1 * x1 + w2 * x2 + b)\n",
    "L = (y - t) ** 2\n",
    "print('y =', y.item(), 'L =', L.item())\n",
    "print(L.grad_fn, '->', L.grad_fn.next_functions)                     # grafik düğümleri"
   ],
   "id": "c04"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Geri yayılım ve elle kontrol"
   ],
   "id": "c05"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "L.backward()\n",
    "manual = 2 * (y - t) * (1 - y ** 2) * x1                              # zincir kuralı\n",
    "print('autograd:', w1.grad.item(), '| elle:', manual.item())"
   ],
   "id": "c06"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Sayısal türev\n",
    "Merkezî fark: $(L(\\theta+h) - L(\\theta-h)) / 2h$"
   ],
   "id": "c07"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loss(w):\n",
    "    return ((torch.tanh(w * x1 + w2 * x2 + b) - t) ** 2).item()\n",
    "h = 1e-4\n",
    "print('sayısal:', (loss(w1.item() + h) - loss(w1.item() - h)) / (2 * h))\n",
    "print(torch.autograd.gradcheck(lambda w: torch.tanh(w * 2.0) ** 2, torch.tensor([0.3], dtype=torch.float64, requires_grad=True)))"
   ],
   "id": "c08"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Gradyanlar birikir"
   ],
   "id": "c09"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "w = torch.tensor(1.0, requires_grad=True)\n",
    "for i in range(3):\n",
    "    (w * 3).backward()\n",
    "    print(i, w.grad.item())                                          # 3, 6, 9: birikir\n",
    "w.grad = None"
   ],
   "id": "c10"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Elle gradyan inişi"
   ],
   "id": "c11"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for step in range(20):\n",
    "    y = torch.tanh(w1 * x1 + w2 * x2 + b)\n",
    "    L = (y - t) ** 2\n",
    "    L.backward()\n",
    "    with torch.no_grad():                                            # güncelleme grafiğe girmesin\n",
    "        for p in (w1, w2, b):\n",
    "            p -= 0.1 * p.grad\n",
    "            p.grad = None\n",
    "    if step % 5 == 0:\n",
    "        print(step, round(L.item(), 5))"
   ],
   "id": "c12"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Alıştırmalar\n",
    "1. $x_1 = 0$ yap: $w_1$'in gradyanı neden sıfır?\n",
    "2. `detach()` ile $y$'yi grafikten kopar ve `backward` dene: ne oldu?\n",
    "3. Kendi fonksiyonun için `gradcheck` yaz."
   ],
   "id": "c13"
  }
 ]
}