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.

linear-regression-gradient-descend.py 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. # -*- coding: utf-8 -*-
  2. # ---
  3. # jupyter:
  4. # jupytext_format_version: '1.2'
  5. # kernelspec:
  6. # display_name: Python 3
  7. # language: python
  8. # name: python3
  9. # language_info:
  10. # codemirror_mode:
  11. # name: ipython
  12. # version: 3
  13. # file_extension: .py
  14. # mimetype: text/x-python
  15. # name: python
  16. # nbconvert_exporter: python
  17. # pygments_lexer: ipython3
  18. # version: 3.5.2
  19. # ---
  20. # # 线性模型和梯度下降
  21. # 这是神经网络的第一课,我们会学习一个非常简单的模型,线性回归,同时也会学习一个优化算法-梯度下降法,对这个模型进行优化。线性回归是监督学习里面一个非常简单的模型,同时梯度下降也是深度学习中应用最广的优化算法,我们将从这里开始我们的深度学习之旅
  22. #
  23. #
  24. # ## 一元线性回归
  25. # 一元线性模型非常简单,假设我们有变量 $x_i$ 和目标 $y_i$,每个 i 对应于一个数据点,希望建立一个模型
  26. #
  27. # $$
  28. # \hat{y}_i = w x_i + b
  29. # $$
  30. #
  31. # $\hat{y}_i$ 是我们预测的结果,希望通过 $\hat{y}_i$ 来拟合目标 $y_i$,通俗来讲就是找到这个函数拟合 $y_i$ 使得误差最小,即最小化
  32. #
  33. # $$
  34. # \frac{1}{n} \sum_{i=1}^n(\hat{y}_i - y_i)^2
  35. # $$
  36. # 那么如何最小化这个误差呢?
  37. #
  38. # 这里需要用到**梯度下降**,这是我们接触到的第一个优化算法,非常简单,但是却非常强大,在深度学习中被大量使用,所以让我们从简单的例子出发了解梯度下降法的原理
  39. # ## 梯度下降法
  40. # 在梯度下降法中,我们首先要明确梯度的概念,随后我们再了解如何使用梯度进行下降。
  41. # ### 梯度
  42. # 梯度在数学上就是导数,如果是一个多元函数,那么梯度就是偏导数。比如一个函数f(x, y),那么 f 的梯度就是
  43. #
  44. # $$
  45. # (\frac{\partial f}{\partial x},\ \frac{\partial f}{\partial y})
  46. # $$
  47. #
  48. # 可以称为 grad f(x, y) 或者 $\nabla f(x, y)$。具体某一点 $(x_0,\ y_0)$ 的梯度就是 $\nabla f(x_0,\ y_0)$。
  49. #
  50. # 下面这个图片是 $f(x) = x^2$ 这个函数在 x=1 处的梯度
  51. #
  52. # ![](https://ws3.sinaimg.cn/large/006tNc79ly1fmarbuh2j3j30ba0b80sy.jpg)
  53. # 梯度有什么意义呢?从几何意义来讲,一个点的梯度值是这个函数变化最快的地方,具体来说,对于函数 f(x, y),在点 $(x_0, y_0)$ 处,沿着梯度 $\nabla f(x_0,\ y_0)$ 的方向,函数增加最快,也就是说沿着梯度的方向,我们能够更快地找到函数的极大值点,或者反过来沿着梯度的反方向,我们能够更快地找到函数的最小值点。
  54. # ### 梯度下降法
  55. # 有了对梯度的理解,我们就能了解梯度下降发的原理了。上面我们需要最小化这个误差,也就是需要找到这个误差的最小值点,那么沿着梯度的反方向我们就能够找到这个最小值点。
  56. #
  57. # 我们可以来看一个直观的解释。比如我们在一座大山上的某处位置,由于我们不知道怎么下山,于是决定走一步算一步,也就是在每走到一个位置的时候,求解当前位置的梯度,沿着梯度的负方向,也就是当前最陡峭的位置向下走一步,然后继续求解当前位置梯度,向这一步所在位置沿着最陡峭最易下山的位置走一步。这样一步步的走下去,一直走到觉得我们已经到了山脚。当然这样走下去,有可能我们不能走到山脚,而是到了某一个局部的山峰低处。
  58. #
  59. # 类比我们的问题,就是沿着梯度的反方向,我们不断改变 w 和 b 的值,最终找到一组最好的 w 和 b 使得误差最小。
  60. #
  61. # 在更新的时候,我们需要决定每次更新的幅度,比如在下山的例子中,我们需要每次往下走的那一步的长度,这个长度称为学习率,用 $\eta$ 表示,这个学习率非常重要,不同的学习率都会导致不同的结果,学习率太小会导致下降非常缓慢,学习率太大又会导致跳动非常明显,可以看看下面的例子
  62. #
  63. # ![](https://ws2.sinaimg.cn/large/006tNc79ly1fmgn23lnzjg30980gogso.gif)
  64. #
  65. # 可以看到上面的学习率较为合适,而下面的学习率太大,就会导致不断跳动
  66. #
  67. # 最后我们的更新公式就是
  68. #
  69. # $$
  70. # w := w - \eta \frac{\partial f(w,\ b)}{\partial w} \\
  71. # b := b - \eta \frac{\partial f(w,\ b)}{\partial b}
  72. # $$
  73. #
  74. # 通过不断地迭代更新,最终我们能够找到一组最优的 w 和 b,这就是梯度下降法的原理。
  75. #
  76. # 最后可以通过这张图形象地说明一下这个方法
  77. #
  78. # ![](https://ws3.sinaimg.cn/large/006tNc79ly1fmarxsltfqj30gx091gn4.jpg)
  79. #
  80. #
  81. # 上面是原理部分,下面通过一个例子来进一步学习线性模型
  82. # +
  83. import torch
  84. import numpy as np
  85. from torch.autograd import Variable
  86. torch.manual_seed(2017)
  87. # +
  88. # 读入数据 x 和 y
  89. x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
  90. [9.779], [6.182], [7.59], [2.167], [7.042],
  91. [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)
  92. y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
  93. [3.366], [2.596], [2.53], [1.221], [2.827],
  94. [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)
  95. # +
  96. # 画出图像
  97. import matplotlib.pyplot as plt
  98. # %matplotlib inline
  99. plt.plot(x_train, y_train, 'bo')
  100. # +
  101. # 转换成 Tensor
  102. x_train = torch.from_numpy(x_train)
  103. y_train = torch.from_numpy(y_train)
  104. # 定义参数 w 和 b
  105. w = Variable(torch.randn(1), requires_grad=True) # 随机初始化
  106. b = Variable(torch.zeros(1), requires_grad=True) # 使用 0 进行初始化
  107. # +
  108. # 构建线性回归模型
  109. x_train = Variable(x_train)
  110. y_train = Variable(y_train)
  111. def linear_model(x):
  112. return x * w + b
  113. # -
  114. y_ = linear_model(x_train)
  115. # 经过上面的步骤我们就定义好了模型,在进行参数更新之前,我们可以先看看模型的输出结果长什么样
  116. plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
  117. plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
  118. plt.legend()
  119. # **思考:红色的点表示预测值,似乎排列成一条直线,请思考一下这些点是否在一条直线上?**
  120. # 这个时候需要计算我们的误差函数,也就是
  121. #
  122. # $$
  123. # \frac{1}{n} \sum_{i=1}^n(\hat{y}_i - y_i)^2
  124. # $$
  125. # +
  126. # 计算误差
  127. def get_loss(y_, y):
  128. return torch.mean((y_ - y) ** 2)
  129. loss = get_loss(y_, y_train)
  130. # -
  131. # 打印一下看看 loss 的大小
  132. print(loss)
  133. # 定义好了误差函数,接下来我们需要计算 w 和 b 的梯度了,这时得益于 PyTorch 的自动求导,我们不需要手动去算梯度,有兴趣的同学可以手动计算一下,w 和 b 的梯度分别是
  134. #
  135. # $$
  136. # \frac{\partial}{\partial w} = \frac{2}{n} \sum_{i=1}^n x_i(w x_i + b - y_i) \\
  137. # \frac{\partial}{\partial b} = \frac{2}{n} \sum_{i=1}^n (w x_i + b - y_i)
  138. # $$
  139. # 自动求导
  140. loss.backward()
  141. # 查看 w 和 b 的梯度
  142. print(w.grad)
  143. print(b.grad)
  144. # 更新一次参数
  145. w.data = w.data - 1e-2 * w.grad.data
  146. b.data = b.data - 1e-2 * b.grad.data
  147. # 更新完成参数之后,我们再一次看看模型输出的结果
  148. y_ = linear_model(x_train)
  149. plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
  150. plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
  151. plt.legend()
  152. # 从上面的例子可以看到,更新之后红色的线跑到了蓝色的线下面,没有特别好的拟合蓝色的真实值,所以我们需要在进行几次更新
  153. for e in range(10): # 进行 10 次更新
  154. y_ = linear_model(x_train)
  155. loss = get_loss(y_, y_train)
  156. w.grad.zero_() # 记得归零梯度
  157. b.grad.zero_() # 记得归零梯度
  158. loss.backward()
  159. w.data = w.data - 1e-2 * w.grad.data # 更新 w
  160. b.data = b.data - 1e-2 * b.grad.data # 更新 b
  161. print('epoch: {}, loss: {}'.format(e, loss.data[0]))
  162. y_ = linear_model(x_train)
  163. plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
  164. plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
  165. plt.legend()
  166. # 经过 10 次更新,我们发现红色的预测结果已经比较好的拟合了蓝色的真实值。
  167. #
  168. # 现在你已经学会了你的第一个机器学习模型了,再接再厉,完成下面的小练习。
  169. # **小练习:**
  170. #
  171. # 重启 notebook 运行上面的线性回归模型,但是改变训练次数以及不同的学习率进行尝试得到不同的结果
  172. # ## 多项式回归模型
  173. # 下面我们更进一步,讲一讲多项式回归。什么是多项式回归呢?非常简单,根据上面的线性回归模型
  174. #
  175. # $$
  176. # \hat{y} = w x + b
  177. # $$
  178. #
  179. # 这里是关于 x 的一个一次多项式,这个模型比较简单,没有办法拟合比较复杂的模型,所以我们可以使用更高次的模型,比如
  180. #
  181. # $$
  182. # \hat{y} = w_0 + w_1 x + w_2 x^2 + w_3 x^3 + \cdots
  183. # $$
  184. #
  185. # 这样就能够拟合更加复杂的模型,这就是多项式模型,这里使用了 x 的更高次,同理还有多元回归模型,形式也是一样的,只是出了使用 x,还是更多的变量,比如 y、z 等等,同时他们的 loss 函数和简单的线性回归模型是一致的。
  186. #
  187. #
  188. # 首先我们可以先定义一个需要拟合的目标函数,这个函数是个三次的多项式
  189. # +
  190. # 定义一个多变量函数
  191. w_target = np.array([0.5, 3, 2.4]) # 定义参数
  192. b_target = np.array([0.9]) # 定义参数
  193. f_des = 'y = {:.2f} + {:.2f} * x + {:.2f} * x^2 + {:.2f} * x^3'.format(
  194. b_target[0], w_target[0], w_target[1], w_target[2]) # 打印出函数的式子
  195. print(f_des)
  196. # -
  197. # 我们可以先画出这个多项式的图像
  198. # +
  199. # 画出这个函数的曲线
  200. x_sample = np.arange(-3, 3.1, 0.1)
  201. y_sample = b_target[0] + w_target[0] * x_sample + w_target[1] * x_sample ** 2 + w_target[2] * x_sample ** 3
  202. plt.plot(x_sample, y_sample, label='real curve')
  203. plt.legend()
  204. # -
  205. # 接着我们可以构建数据集,需要 x 和 y,同时是一个三次多项式,所以我们取了 $x,\ x^2, x^3$
  206. # +
  207. # 构建数据 x 和 y
  208. # x 是一个如下矩阵 [x, x^2, x^3]
  209. # y 是函数的结果 [y]
  210. x_train = np.stack([x_sample ** i for i in range(1, 4)], axis=1)
  211. x_train = torch.from_numpy(x_train).float() # 转换成 float tensor
  212. y_train = torch.from_numpy(y_sample).float().unsqueeze(1) # 转化成 float tensor
  213. # -
  214. # 接着我们可以定义需要优化的参数,就是前面这个函数里面的 $w_i$
  215. # +
  216. # 定义参数和模型
  217. w = Variable(torch.randn(3, 1), requires_grad=True)
  218. b = Variable(torch.zeros(1), requires_grad=True)
  219. # 将 x 和 y 转换成 Variable
  220. x_train = Variable(x_train)
  221. y_train = Variable(y_train)
  222. def multi_linear(x):
  223. return torch.mm(x, w) + b
  224. # -
  225. # 我们可以画出没有更新之前的模型和真实的模型之间的对比
  226. # +
  227. # 画出更新之前的模型
  228. y_pred = multi_linear(x_train)
  229. plt.plot(x_train.data.numpy()[:, 0], y_pred.data.numpy(), label='fitting curve', color='r')
  230. plt.plot(x_train.data.numpy()[:, 0], y_sample, label='real curve', color='b')
  231. plt.legend()
  232. # -
  233. # 可以发现,这两条曲线之间存在差异,我们计算一下他们之间的误差
  234. # 计算误差,这里的误差和一元的线性模型的误差是相同的,前面已经定义过了 get_loss
  235. loss = get_loss(y_pred, y_train)
  236. print(loss)
  237. # 自动求导
  238. loss.backward()
  239. # 查看一下 w 和 b 的梯度
  240. print(w.grad)
  241. print(b.grad)
  242. # 更新一下参数
  243. w.data = w.data - 0.001 * w.grad.data
  244. b.data = b.data - 0.001 * b.grad.data
  245. # +
  246. # 画出更新一次之后的模型
  247. y_pred = multi_linear(x_train)
  248. plt.plot(x_train.data.numpy()[:, 0], y_pred.data.numpy(), label='fitting curve', color='r')
  249. plt.plot(x_train.data.numpy()[:, 0], y_sample, label='real curve', color='b')
  250. plt.legend()
  251. # -
  252. # 因为只更新了一次,所以两条曲线之间的差异仍然存在,我们进行 100 次迭代
  253. # 进行 100 次参数更新
  254. for e in range(100):
  255. y_pred = multi_linear(x_train)
  256. loss = get_loss(y_pred, y_train)
  257. w.grad.data.zero_()
  258. b.grad.data.zero_()
  259. loss.backward()
  260. # 更新参数
  261. w.data = w.data - 0.001 * w.grad.data
  262. b.data = b.data - 0.001 * b.grad.data
  263. if (e + 1) % 20 == 0:
  264. print('epoch {}, Loss: {:.5f}'.format(e+1, loss.data[0]))
  265. # 可以看到更新完成之后 loss 已经非常小了,我们画出更新之后的曲线对比
  266. # +
  267. # 画出更新之后的结果
  268. y_pred = multi_linear(x_train)
  269. plt.plot(x_train.data.numpy()[:, 0], y_pred.data.numpy(), label='fitting curve', color='r')
  270. plt.plot(x_train.data.numpy()[:, 0], y_sample, label='real curve', color='b')
  271. plt.legend()
  272. # -
  273. # 可以看到,经过 100 次更新之后,可以看到拟合的线和真实的线已经完全重合了
  274. # **小练习:上面的例子是一个三次的多项式,尝试使用二次的多项式去拟合它,看看最后能做到多好**
  275. #
  276. # **提示:参数 `w = torch.randn(2, 1)`,同时重新构建 x 数据集**

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