{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 00｜环境自检\n",
    "\n",
    "目标：在运行其他实验前，确认 Python、PyTorch、NumPy、计算设备和随机种子都可用。这个 Notebook 不下载模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import platform\n",
    "import sys\n",
    "import numpy as np\n",
    "import torch\n",
    "\n",
    "device = 'mps' if torch.backends.mps.is_available() else 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "print('python:', sys.version.split()[0])\n",
    "print('platform:', platform.platform())\n",
    "print('numpy:', np.__version__)\n",
    "print('torch:', torch.__version__)\n",
    "print('device:', device)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 张量与梯度冒烟测试\n",
    "\n",
    "预期：断言通过，损失为有限数，参数在一步优化后发生变化。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(42)\n",
    "layer = torch.nn.Linear(3, 1).to(device)\n",
    "x = torch.tensor([[1.0, 2.0, 3.0]], device=device)\n",
    "target = torch.tensor([[1.0]], device=device)\n",
    "before = layer.weight.detach().clone()\n",
    "loss = torch.nn.functional.mse_loss(layer(x), target)\n",
    "loss.backward()\n",
    "with torch.no_grad():\n",
    "    layer.weight -= 0.01 * layer.weight.grad\n",
    "assert torch.isfinite(loss)\n",
    "assert not torch.equal(before, layer.weight)\n",
    "print(f'smoke test passed: loss={loss.item():.6f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 验收\n",
    "\n",
    "- 能解释为什么 `loss.backward()` 之后 `weight.grad` 才存在。\n",
    "- 能记录实际选择的 device。CPU 不是失败，只表示后续训练更慢。\n",
    "- 如果这里失败，先修复环境，不要继续排查模型代码。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
  "language_info": {"name": "python", "version": "3.12"}
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
