You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

2-autograd.ipynb 14 kB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. {
  2. "cells": [
  3. {
  4. "cell_type": "markdown",
  5. "metadata": {},
  6. "source": [
  7. "# 自动求导\n",
  8. "这次课程我们会了解 PyTorch 中的自动求导机制,自动求导是 PyTorch 中非常重要的特性,能够让我们避免手动去计算非常复杂的导数,这能够极大地减少了我们构建模型的时间,这也是其前身 Torch 这个框架所不具备的特性,下面我们通过例子看看 PyTorch 自动求导的独特魅力以及探究自动求导的更多用法。"
  9. ]
  10. },
  11. {
  12. "cell_type": "code",
  13. "execution_count": 1,
  14. "metadata": {},
  15. "outputs": [],
  16. "source": [
  17. "import torch\n",
  18. "from torch.autograd import Variable"
  19. ]
  20. },
  21. {
  22. "cell_type": "markdown",
  23. "metadata": {},
  24. "source": [
  25. "## 1. 简单情况的自动求导\n",
  26. "下面我们显示一些简单情况的自动求导,\"简单\"体现在计算的结果都是标量,也就是一个数,我们对这个标量进行自动求导。"
  27. ]
  28. },
  29. {
  30. "cell_type": "code",
  31. "execution_count": 2,
  32. "metadata": {},
  33. "outputs": [
  34. {
  35. "name": "stdout",
  36. "output_type": "stream",
  37. "text": [
  38. "tensor([19.], grad_fn=<AddBackward0>)\n"
  39. ]
  40. }
  41. ],
  42. "source": [
  43. "x = Variable(torch.Tensor([2]), requires_grad=True)\n",
  44. "y = x + 2\n",
  45. "z = y ** 2 + 3\n",
  46. "print(z)"
  47. ]
  48. },
  49. {
  50. "cell_type": "markdown",
  51. "metadata": {},
  52. "source": [
  53. "通过上面的一些列操作,我们从 x 得到了最后的结果out,我们可以将其表示为数学公式\n",
  54. "\n",
  55. "$$\n",
  56. "z = (x + 2)^2 + 3\n",
  57. "$$\n",
  58. "\n",
  59. "那么我们从 z 对 x 求导的结果就是 \n",
  60. "\n",
  61. "$$\n",
  62. "\\frac{\\partial z}{\\partial x} = 2 (x + 2) = 2 (2 + 2) = 8\n",
  63. "$$\n",
  64. "\n",
  65. "如果你对求导不熟悉,可以查看以下[《导数介绍资料》](https://baike.baidu.com/item/%E5%AF%BC%E6%95%B0#1)网址进行复习"
  66. ]
  67. },
  68. {
  69. "cell_type": "code",
  70. "execution_count": 3,
  71. "metadata": {},
  72. "outputs": [
  73. {
  74. "name": "stdout",
  75. "output_type": "stream",
  76. "text": [
  77. "tensor([8.])\n"
  78. ]
  79. }
  80. ],
  81. "source": [
  82. "# 使用自动求导\n",
  83. "z.backward()\n",
  84. "print(x.grad)"
  85. ]
  86. },
  87. {
  88. "cell_type": "markdown",
  89. "metadata": {},
  90. "source": [
  91. "对于上面这样一个简单的例子,我们验证了自动求导,同时可以发现发现使用自动求导非常方便。如果是一个更加复杂的例子,那么手动求导就会显得非常的麻烦,所以自动求导的机制能够帮助我们省去麻烦的数学计算,下面我们可以看一个更加复杂的例子。"
  92. ]
  93. },
  94. {
  95. "cell_type": "code",
  96. "execution_count": 4,
  97. "metadata": {},
  98. "outputs": [
  99. {
  100. "name": "stdout",
  101. "output_type": "stream",
  102. "text": [
  103. "tensor([[1., 2.],\n",
  104. " [3., 4.]], requires_grad=True)\n"
  105. ]
  106. }
  107. ],
  108. "source": [
  109. "# 定义Variable\n",
  110. "x = Variable(torch.FloatTensor([1,2]), requires_grad=False)\n",
  111. "b = Variable(torch.FloatTensor([5,6]), requires_grad=False)\n",
  112. "w = Variable(torch.FloatTensor([[1,2],[3,4]]), requires_grad=True)\n",
  113. "print(w)"
  114. ]
  115. },
  116. {
  117. "cell_type": "code",
  118. "execution_count": 5,
  119. "metadata": {},
  120. "outputs": [],
  121. "source": [
  122. "z = torch.mean(torch.matmul(w, x) + b) # torch.matmul 是做矩阵乘法\n",
  123. "z.backward()"
  124. ]
  125. },
  126. {
  127. "cell_type": "markdown",
  128. "metadata": {},
  129. "source": [
  130. "如果你对矩阵乘法不熟悉,可以查看下面的[《矩阵乘法说明》](https://baike.baidu.com/item/%E7%9F%A9%E9%98%B5%E4%B9%98%E6%B3%95/5446029?fr=aladdin)进行复习"
  131. ]
  132. },
  133. {
  134. "cell_type": "code",
  135. "execution_count": 6,
  136. "metadata": {},
  137. "outputs": [
  138. {
  139. "name": "stdout",
  140. "output_type": "stream",
  141. "text": [
  142. "tensor([[0.5000, 1.0000],\n",
  143. " [0.5000, 1.0000]])\n"
  144. ]
  145. }
  146. ],
  147. "source": [
  148. "# 得到 w 的梯度\n",
  149. "print(w.grad)"
  150. ]
  151. },
  152. {
  153. "cell_type": "markdown",
  154. "metadata": {},
  155. "source": [
  156. "具体计算的公式为:\n",
  157. "$$\n",
  158. "z_1 = w_{11}*x_1 + w_{12}*x_2 + b_1 \\\\\n",
  159. "z_2 = w_{21}*x_1 + w_{22}*x_2 + b_2 \\\\\n",
  160. "z = \\frac{1}{2} (z_1 + z_2)\n",
  161. "$$"
  162. ]
  163. },
  164. {
  165. "cell_type": "markdown",
  166. "metadata": {},
  167. "source": [
  168. "则微分计算结果是:\n",
  169. "$$\n",
  170. "\\frac{\\partial z}{w_{11}} = \\frac{1}{2} x_1 \\\\\n",
  171. "\\frac{\\partial z}{w_{12}} = \\frac{1}{2} x_2 \\\\\n",
  172. "\\frac{\\partial z}{w_{21}} = \\frac{1}{2} x_1 \\\\\n",
  173. "\\frac{\\partial z}{w_{22}} = \\frac{1}{2} x_2\n",
  174. "$$"
  175. ]
  176. },
  177. {
  178. "cell_type": "markdown",
  179. "metadata": {},
  180. "source": [
  181. "上面数学公式就更加复杂,矩阵乘法之后对两个矩阵对应元素相乘,然后所有元素求平均,有兴趣的同学可以手动去计算一下梯度,使用 PyTorch 的自动求导,我们能够非常容易得到 x, y 和 w 的导数,因为深度学习中充满大量的矩阵运算,所以我们没有办法手动去求这些导数,有了自动求导能够非常方便地解决网络更新的问题。"
  182. ]
  183. },
  184. {
  185. "cell_type": "markdown",
  186. "metadata": {},
  187. "source": [
  188. "## 2. 复杂情况的自动求导\n",
  189. "\n",
  190. "上面我们展示了简单情况下的自动求导,都是对标量进行自动求导,那么如何对一个向量或者矩阵自动求导?"
  191. ]
  192. },
  193. {
  194. "cell_type": "code",
  195. "execution_count": 28,
  196. "metadata": {},
  197. "outputs": [
  198. {
  199. "name": "stdout",
  200. "output_type": "stream",
  201. "text": [
  202. "tensor([[2., 3.]], requires_grad=True)\n",
  203. "tensor([[0., 0.]])\n"
  204. ]
  205. }
  206. ],
  207. "source": [
  208. "m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 构建一个 1 x 2 的矩阵\n",
  209. "n = Variable(torch.zeros(1, 2)) # 构建一个相同大小的 0 矩阵\n",
  210. "print(m)\n",
  211. "print(n)"
  212. ]
  213. },
  214. {
  215. "cell_type": "code",
  216. "execution_count": 29,
  217. "metadata": {},
  218. "outputs": [
  219. {
  220. "name": "stdout",
  221. "output_type": "stream",
  222. "text": [
  223. "tensor(2., grad_fn=<SelectBackward>)\n",
  224. "tensor([[ 4., 27.]], grad_fn=<CopySlices>)\n"
  225. ]
  226. }
  227. ],
  228. "source": [
  229. "# 通过 m 中的值计算新的 n 中的值\n",
  230. "print(m[0,0])\n",
  231. "n[0, 0] = m[0, 0] ** 2\n",
  232. "n[0, 1] = m[0, 1] ** 3\n",
  233. "print(n)"
  234. ]
  235. },
  236. {
  237. "cell_type": "markdown",
  238. "metadata": {},
  239. "source": [
  240. "将上面的式子写成数学公式,可以得到 \n",
  241. "$$\n",
  242. "n = (n_0,\\ n_1) = (m_0^2,\\ m_1^3) = (2^2,\\ 3^3) \n",
  243. "$$"
  244. ]
  245. },
  246. {
  247. "cell_type": "markdown",
  248. "metadata": {},
  249. "source": [
  250. "下面我们直接对 n 进行反向传播,也就是求 n 对 m 的导数。\n",
  251. "\n",
  252. "这时我们需要明确这个导数的定义,即如何定义\n",
  253. "\n",
  254. "$$\n",
  255. "\\frac{\\partial n}{\\partial m} = \\frac{\\partial (n_0,\\ n_1)}{\\partial (m_0,\\ m_1)}\n",
  256. "$$\n"
  257. ]
  258. },
  259. {
  260. "cell_type": "markdown",
  261. "metadata": {},
  262. "source": [
  263. "在 PyTorch 中,如果要调用自动求导,需要往`backward()`中传入一个参数,这个参数的形状和 n 一样大,比如是 $(w_0,\\ w_1)$,那么自动求导的结果就是:\n",
  264. "$$\n",
  265. "\\frac{\\partial n}{\\partial m_0} = w_0 \\frac{\\partial n_0}{\\partial m_0} + w_1 \\frac{\\partial n_1}{\\partial m_0}\n",
  266. "$$\n",
  267. "$$\n",
  268. "\\frac{\\partial n}{\\partial m_1} = w_0 \\frac{\\partial n_0}{\\partial m_1} + w_1 \\frac{\\partial n_1}{\\partial m_1}\n",
  269. "$$"
  270. ]
  271. },
  272. {
  273. "cell_type": "code",
  274. "execution_count": 14,
  275. "metadata": {},
  276. "outputs": [],
  277. "source": [
  278. "n.backward(torch.ones_like(n)) # 将 (w0, w1) 取成 (1, 1)"
  279. ]
  280. },
  281. {
  282. "cell_type": "code",
  283. "execution_count": 15,
  284. "metadata": {},
  285. "outputs": [
  286. {
  287. "name": "stdout",
  288. "output_type": "stream",
  289. "text": [
  290. "tensor([[ 4., 27.]])\n"
  291. ]
  292. }
  293. ],
  294. "source": [
  295. "print(m.grad)"
  296. ]
  297. },
  298. {
  299. "cell_type": "markdown",
  300. "metadata": {},
  301. "source": [
  302. "通过自动求导我们得到了梯度是 4 和 27,我们可以验算一下\n",
  303. "$$\n",
  304. "\\frac{\\partial n}{\\partial m_0} = w_0 \\frac{\\partial n_0}{\\partial m_0} + w_1 \\frac{\\partial n_1}{\\partial m_0} = 2 m_0 + 0 = 2 \\times 2 = 4\n",
  305. "$$\n",
  306. "$$\n",
  307. "\\frac{\\partial n}{\\partial m_1} = w_0 \\frac{\\partial n_0}{\\partial m_1} + w_1 \\frac{\\partial n_1}{\\partial m_1} = 0 + 3 m_1^2 = 3 \\times 3^2 = 27\n",
  308. "$$\n",
  309. "通过验算我们可以得到相同的结果"
  310. ]
  311. },
  312. {
  313. "cell_type": "markdown",
  314. "metadata": {},
  315. "source": [
  316. "\n"
  317. ]
  318. },
  319. {
  320. "cell_type": "markdown",
  321. "metadata": {},
  322. "source": [
  323. "## 3. 多次自动求导\n",
  324. "通过调用 backward 我们可以进行一次自动求导,如果我们再调用一次 backward,会发现程序报错,没有办法再做一次。这是因为 PyTorch 默认做完一次自动求导之后,计算图就被丢弃了,所以两次自动求导需要手动设置一个东西,我们通过下面的小例子来说明。"
  325. ]
  326. },
  327. {
  328. "cell_type": "code",
  329. "execution_count": 16,
  330. "metadata": {},
  331. "outputs": [
  332. {
  333. "name": "stdout",
  334. "output_type": "stream",
  335. "text": [
  336. "tensor([18.], grad_fn=<AddBackward0>)\n"
  337. ]
  338. }
  339. ],
  340. "source": [
  341. "x = Variable(torch.FloatTensor([3]), requires_grad=True)\n",
  342. "y = x * 2 + x ** 2 + 3\n",
  343. "print(y)"
  344. ]
  345. },
  346. {
  347. "cell_type": "code",
  348. "execution_count": 17,
  349. "metadata": {},
  350. "outputs": [],
  351. "source": [
  352. "y.backward(retain_graph=True) # 设置 retain_graph 为 True 来保留计算图"
  353. ]
  354. },
  355. {
  356. "cell_type": "code",
  357. "execution_count": 18,
  358. "metadata": {},
  359. "outputs": [
  360. {
  361. "name": "stdout",
  362. "output_type": "stream",
  363. "text": [
  364. "tensor([8.])\n"
  365. ]
  366. }
  367. ],
  368. "source": [
  369. "print(x.grad)"
  370. ]
  371. },
  372. {
  373. "cell_type": "code",
  374. "execution_count": 19,
  375. "metadata": {},
  376. "outputs": [],
  377. "source": [
  378. "y.backward() # 再做一次自动求导,这次不保留计算图"
  379. ]
  380. },
  381. {
  382. "cell_type": "code",
  383. "execution_count": 24,
  384. "metadata": {},
  385. "outputs": [
  386. {
  387. "name": "stdout",
  388. "output_type": "stream",
  389. "text": [
  390. "tensor([16.])\n"
  391. ]
  392. }
  393. ],
  394. "source": [
  395. "print(x.grad)"
  396. ]
  397. },
  398. {
  399. "cell_type": "markdown",
  400. "metadata": {},
  401. "source": [
  402. "可以发现 x 的梯度变成了 16,因为这里做了两次自动求导,所以讲第一次的梯度 8 和第二次的梯度 8 加起来得到了 16 的结果。"
  403. ]
  404. },
  405. {
  406. "cell_type": "markdown",
  407. "metadata": {},
  408. "source": [
  409. "\n"
  410. ]
  411. },
  412. {
  413. "cell_type": "markdown",
  414. "metadata": {},
  415. "source": [
  416. "## 4. 练习题\n",
  417. "\n",
  418. "定义\n",
  419. "\n",
  420. "$$\n",
  421. "x = \n",
  422. "\\left[\n",
  423. "\\begin{matrix}\n",
  424. "x_0 \\\\\n",
  425. "x_1\n",
  426. "\\end{matrix}\n",
  427. "\\right] = \n",
  428. "\\left[\n",
  429. "\\begin{matrix}\n",
  430. "2 \\\\\n",
  431. "3\n",
  432. "\\end{matrix}\n",
  433. "\\right]\n",
  434. "$$\n",
  435. "\n",
  436. "$$\n",
  437. "k = (k_0,\\ k_1) = (x_0^2 + 3 x_1,\\ 2 x_0 + x_1^2)\n",
  438. "$$\n",
  439. "\n",
  440. "我们希望求得\n",
  441. "\n",
  442. "$$\n",
  443. "j = \\left[\n",
  444. "\\begin{matrix}\n",
  445. "\\frac{\\partial k_0}{\\partial x_0} & \\frac{\\partial k_0}{\\partial x_1} \\\\\n",
  446. "\\frac{\\partial k_1}{\\partial x_0} & \\frac{\\partial k_1}{\\partial x_1}\n",
  447. "\\end{matrix}\n",
  448. "\\right]\n",
  449. "$$\n",
  450. "\n",
  451. "参考答案:\n",
  452. "\n",
  453. "$$\n",
  454. "\\left[\n",
  455. "\\begin{matrix}\n",
  456. "4 & 3 \\\\\n",
  457. "2 & 6 \\\\\n",
  458. "\\end{matrix}\n",
  459. "\\right]\n",
  460. "$$"
  461. ]
  462. },
  463. {
  464. "cell_type": "code",
  465. "execution_count": 25,
  466. "metadata": {},
  467. "outputs": [],
  468. "source": [
  469. "x = Variable(torch.FloatTensor([2, 3]), requires_grad=True)\n",
  470. "k = Variable(torch.zeros(2))\n",
  471. "\n",
  472. "k[0] = x[0] ** 2 + 3 * x[1]\n",
  473. "k[1] = x[1] ** 2 + 2 * x[0]"
  474. ]
  475. },
  476. {
  477. "cell_type": "code",
  478. "execution_count": 29,
  479. "metadata": {},
  480. "outputs": [
  481. {
  482. "name": "stdout",
  483. "output_type": "stream",
  484. "text": [
  485. "tensor([2., 3., 4.], requires_grad=True)\n",
  486. "tensor([2., 0., 0.])\n"
  487. ]
  488. }
  489. ],
  490. "source": [
  491. "# demo to show how to use `.backward`\n",
  492. "x = torch.tensor([2,3,4], dtype=torch.float, requires_grad=True)\n",
  493. "print(x)\n",
  494. "y = x*2\n",
  495. "\n",
  496. "y.backward(torch.tensor([1, 0, 0], dtype=torch.float))\n",
  497. "print(x.grad)"
  498. ]
  499. },
  500. {
  501. "cell_type": "code",
  502. "execution_count": 26,
  503. "metadata": {},
  504. "outputs": [
  505. {
  506. "name": "stdout",
  507. "output_type": "stream",
  508. "text": [
  509. "tensor([13., 13.], grad_fn=<CopySlices>)\n",
  510. "tensor([4., 3.])\n",
  511. "tensor([2., 6.])\n"
  512. ]
  513. }
  514. ],
  515. "source": [
  516. "# calc k_0 -> (x_0, x_1)\n",
  517. "j = torch.zeros(2, 2)\n",
  518. "k.backward(torch.FloatTensor([1, 0]), retain_graph=True)\n",
  519. "print(k)\n",
  520. "j[0] = x.grad.data\n",
  521. "print(x.grad.data)\n",
  522. "\n",
  523. "x.grad.data.zero_() # 归零之前求得的梯度\n",
  524. "\n",
  525. "# calc k_1 -> (x_0, x_1)\n",
  526. "k.backward(torch.FloatTensor([0, 1]))\n",
  527. "j[1] = x.grad.data\n",
  528. "print(x.grad.data)\n"
  529. ]
  530. },
  531. {
  532. "cell_type": "code",
  533. "execution_count": 30,
  534. "metadata": {},
  535. "outputs": [
  536. {
  537. "name": "stdout",
  538. "output_type": "stream",
  539. "text": [
  540. "tensor([[4., 3.],\n",
  541. " [2., 6.]])\n"
  542. ]
  543. }
  544. ],
  545. "source": [
  546. "print(j)"
  547. ]
  548. }
  549. ],
  550. "metadata": {
  551. "kernelspec": {
  552. "display_name": "Python 3",
  553. "language": "python",
  554. "name": "python3"
  555. },
  556. "language_info": {
  557. "codemirror_mode": {
  558. "name": "ipython",
  559. "version": 3
  560. },
  561. "file_extension": ".py",
  562. "mimetype": "text/x-python",
  563. "name": "python",
  564. "nbconvert_exporter": "python",
  565. "pygments_lexer": "ipython3",
  566. "version": "3.7.9"
  567. }
  568. },
  569. "nbformat": 4,
  570. "nbformat_minor": 2
  571. }

机器学习越来越多应用到飞行器、机器人等领域,其目的是利用计算机实现类似人类的智能,从而实现装备的智能化与无人化。本课程旨在引导学生掌握机器学习的基本知识、典型方法与技术,通过具体的应用案例激发学生对该学科的兴趣,鼓励学生能够从人工智能的角度来分析、解决飞行器、机器人所面临的问题和挑战。本课程主要内容包括Python编程基础,机器学习模型,无监督学习、监督学习、深度学习基础知识与实现,并学习如何利用机器学习解决实际问题,从而全面提升自我的《综合能力》。