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 15 kB

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

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