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.

mlp_bp.py 24 kB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  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. # 神经元和感知器本质上是一样的,只不过我们说感知器的时候,它的激活函数是阶跃函数;而当我们说神经元时,激活函数往往选择为sigmoid函数或tanh函数。如下图所示:
  25. # ![neuron](images/neuron.gif)
  26. #
  27. # 计算一个神经元的输出的方法和计算一个感知器的输出是一样的。假设神经元的输入是向量$\vec{x}$,权重向量是$\vec{w}$(偏置项是$w_0$),激活函数是sigmoid函数,则其输出y:
  28. # $$
  29. # y = sigmod(\vec{w}^T \cdot \vec{x})
  30. # $$
  31. #
  32. # sigmoid函数的定义如下:
  33. # $$
  34. # sigmod(x) = \frac{1}{1+e^{-x}}
  35. # $$
  36. # 将其带入前面的式子,得到
  37. # $$
  38. # y = \frac{1}{1+e^{-\vec{w}^T \cdot \vec{x}}}
  39. # $$
  40. #
  41. # sigmoid函数是一个非线性函数,值域是(0,1)。函数图像如下图所示
  42. # ![sigmod_function](images/sigmod.jpg)
  43. #
  44. # sigmoid函数的导数是:
  45. # $$
  46. # y = sigmod(x) \ \ \ \ \ \ (1) \\
  47. # y' = y(1-y)
  48. # $$
  49. # 可以看到,sigmoid函数的导数非常有趣,它可以用sigmoid函数自身来表示。这样,一旦计算出sigmoid函数的值,计算它的导数的值就非常方便。
  50. #
  51. #
  52. # ## 神经网络是啥?
  53. #
  54. # ![nn1](images/nn1.jpeg)
  55. #
  56. # 神经网络其实就是按照一定规则连接起来的多个神经元。上图展示了一个全连接(full connected, FC)神经网络,通过观察上面的图,我们可以发现它的规则包括:
  57. #
  58. # * 神经元按照层来布局。最左边的层叫做输入层,负责接收输入数据;最右边的层叫输出层,我们可以从这层获取神经网络输出数据。输入层和输出层之间的层叫做隐藏层,因为它们对于外部来说是不可见的。
  59. # * 同一层的神经元之间没有连接。
  60. # * 第N层的每个神经元和第N-1层的所有神经元相连(这就是full connected的含义),第N-1层神经元的输出就是第N层神经元的输入。
  61. # * 每个连接都有一个权值。
  62. #
  63. # 上面这些规则定义了全连接神经网络的结构。事实上还存在很多其它结构的神经网络,比如卷积神经网络(CNN)、循环神经网络(RNN),他们都具有不同的连接规则。
  64. #
  65. # ## 计算神经网络的输出
  66. #
  67. # 神经网络实际上就是一个输入向量$\vec{x}$到输出向量$\vec{y}$的函数,即:
  68. #
  69. # $$
  70. # \vec{y} = f_{network}(\vec{x})
  71. # $$
  72. # 根据输入计算神经网络的输出,需要首先将输入向量$\vec{x}$的每个元素的值$x_i$赋给神经网络的输入层的对应神经元,然后根据式1依次向前计算每一层的每个神经元的值,直到最后一层输出层的所有神经元的值计算完毕。最后,将输出层每个神经元的值串在一起就得到了输出向量$\vec{y}$。
  73. #
  74. # 接下来举一个例子来说明这个过程,我们先给神经网络的每个单元写上编号。
  75. #
  76. # ![nn2](images/nn2.png)
  77. #
  78. # 如上图,输入层有三个节点,我们将其依次编号为1、2、3;隐藏层的4个节点,编号依次为4、5、6、7;最后输出层的两个节点编号为8、9。因为我们这个神经网络是全连接网络,所以可以看到每个节点都和上一层的所有节点有连接。比如,我们可以看到隐藏层的节点4,它和输入层的三个节点1、2、3之间都有连接,其连接上的权重分别为$w_{41}$,$w_{42}$,$w_{43}$。那么,我们怎样计算节点4的输出值$a_4$呢?
  79. #
  80. #
  81. # 为了计算节点4的输出值,我们必须先得到其所有上游节点(也就是节点1、2、3)的输出值。节点1、2、3是输入层的节点,所以,他们的输出值就是输入向量$\vec{x}$本身。按照上图画出的对应关系,可以看到节点1、2、3的输出值分别是$x_1$,$x_2$,$x_3$。我们要求输入向量的维度和输入层神经元个数相同,而输入向量的某个元素对应到哪个输入节点是可以自由决定的,你偏非要把$x_1$赋值给节点2也是完全没有问题的,但这样除了把自己弄晕之外,并没有什么价值。
  82. #
  83. # 一旦我们有了节点1、2、3的输出值,我们就可以根据式1计算节点4的输出值$a_4$:
  84. # ![eqn_3_4](images/eqn_3_4.png)
  85. #
  86. # 上式的$w_{4b}$是节点4的偏置项,图中没有画出来。而$w_{41}$,$w_{42}$,$w_{43}$分别为节点1、2、3到节点4连接的权重,在给权重$w_{ji}$编号时,我们把目标节点的编号$j$放在前面,把源节点的编号$i$放在后面。
  87. #
  88. # 同样,我们可以继续计算出节点5、6、7的输出值$a_5$,$a_6$,$a_7$。这样,隐藏层的4个节点的输出值就计算完成了,我们就可以接着计算输出层的节点8的输出值$y_1$:
  89. # ![eqn_5_6](images/eqn_5_6.png)
  90. #
  91. # 同理,我们还可以计算出$y_2$的值。这样输出层所有节点的输出值计算完毕,我们就得到了在输入向量$\vec{x} = (x_1, x_2, x_3)^T$时,神经网络的输出向量$\vec{y} = (y_1, y_2)^T$。这里我们也看到,输出向量的维度和输出层神经元个数相同。
  92. #
  93. #
  94. # ## 神经网络的矩阵表示
  95. #
  96. # 神经网络的计算如果用矩阵来表示会很方便(当然逼格也更高),我们先来看看隐藏层的矩阵表示。
  97. #
  98. # 首先我们把隐藏层4个节点的计算依次排列出来:
  99. # ![eqn_hidden_units](images/eqn_hidden_units.png)
  100. #
  101. # 接着,定义网络的输入向量$\vec{x}$和隐藏层每个节点的权重向量$\vec{w}$。令
  102. #
  103. # ![eqn_7_12](images/eqn_7_12.png)
  104. #
  105. # 代入到前面的一组式子,得到:
  106. #
  107. # ![eqn_13_16](images/eqn_13_16.png)
  108. #
  109. # 现在,我们把上述计算$a_4$, $a_5$,$a_6$,$a_7$的四个式子写到一个矩阵里面,每个式子作为矩阵的一行,就可以利用矩阵来表示它们的计算了。令
  110. # ![eqn_matrix1](images/eqn_matrix1.png)
  111. #
  112. # 带入前面的一组式子,得到
  113. # ![formular_2](images/formular_2.png)
  114. #
  115. # 在式2中,$f$是激活函数,在本例中是$sigmod$函数;$W$是某一层的权重矩阵;$\vec{x}$是某层的输入向量;$\vec{a}$是某层的输出向量。式2说明神经网络的每一层的作用实际上就是先将输入向量左乘一个数组进行线性变换,得到一个新的向量,然后再对这个向量逐元素应用一个激活函数。
  116. #
  117. # 每一层的算法都是一样的。比如,对于包含一个输入层,一个输出层和三个隐藏层的神经网络,我们假设其权重矩阵分别为$W_1$,$W_2$,$W_3$,$W_4$,每个隐藏层的输出分别是$\vec{a}_1$,$\vec{a}_2$,$\vec{a}_3$,神经网络的输入为$\vec{x}$,神经网络的输出为$\vec{y}$,如下图所示:
  118. # ![nn_parameters_demo](images/nn_parameters_demo.png)
  119. #
  120. # 则每一层的输出向量的计算可以表示为:
  121. # ![eqn_17_20](images/eqn_17_20.png)
  122. #
  123. #
  124. # 这就是神经网络输出值的矩阵计算方法。
  125. #
  126. # ## 神经网络的训练 - 反向传播算法
  127. #
  128. # 现在,我们需要知道一个神经网络的每个连接上的权值是如何得到的。我们可以说神经网络是一个模型,那么这些权值就是模型的参数,也就是模型要学习的东西。然而,一个神经网络的连接方式、网络的层数、每层的节点数这些参数,则不是学习出来的,而是人为事先设置的。对于这些人为设置的参数,我们称之为超参数(Hyper-Parameters)。
  129. #
  130. # 反向传播算法其实就是链式求导法则的应用。然而,这个如此简单且显而易见的方法,却是在Roseblatt提出感知器算法将近30年之后才被发明和普及的。对此,Bengio这样回应道:
  131. #
  132. # > 很多看似显而易见的想法只有在事后才变得显而易见。
  133. #
  134. # 按照机器学习的通用套路,我们先确定神经网络的目标函数,然后用随机梯度下降优化算法去求目标函数最小值时的参数值。
  135. #
  136. # 我们取网络所有输出层节点的误差平方和作为目标函数:
  137. # ![bp_loss](images/bp_loss.png)
  138. #
  139. # 其中,$E_d$表示是样本$d$的误差。
  140. #
  141. # 然后,使用随机梯度下降算法对目标函数进行优化:
  142. # ![bp_weight_update](images/bp_weight_update.png)
  143. #
  144. # 随机梯度下降算法也就是需要求出误差$E_d$对于每个权重$w_{ji}$的偏导数(也就是梯度),怎么求呢?
  145. # ![nn3](images/nn3.png)
  146. #
  147. # 观察上图,我们发现权重$w_{ji}$仅能通过影响节点$j$的输入值影响网络的其它部分,设$net_j$是节点$j$的加权输入,即
  148. # ![eqn_21_22](images/eqn_21_22.png)
  149. #
  150. # $E_d$是$net_j$的函数,而$net_j$是$w_{ji}$的函数。根据链式求导法则,可以得到:
  151. #
  152. # ![eqn_23_25](images/eqn_23_25.png)
  153. #
  154. #
  155. # 上式中,$x_{ji}$是节点传递给节点$j$的输入值,也就是节点$i$的输出值。
  156. #
  157. # 对于的$\frac{\partial E_d}{\partial net_j}$推导,需要区分输出层和隐藏层两种情况。
  158. #
  159. #
  160. # ### 输出层权值训练
  161. #
  162. # ![nn3](images/nn3.png)
  163. #
  164. # 对于输出层来说,$net_j$仅能通过节点$j$的输出值$y_j$来影响网络其它部分,也就是说$E_d$是$y_j$的函数,而$y_j$是$net_j$的函数,其中$y_j = sigmod(net_j)$。所以我们可以再次使用链式求导法则:
  165. # ![eqn_26](images/eqn_26.png)
  166. #
  167. # 考虑上式第一项:
  168. # ![eqn_27_29](images/eqn_27_29.png)
  169. #
  170. #
  171. # 考虑上式第二项:
  172. # ![eqn_30_31](images/eqn_30_31.png)
  173. #
  174. # 将第一项和第二项带入,得到:
  175. # ![eqn_ed_net_j.png](images/eqn_ed_net_j.png)
  176. #
  177. # 如果令$\delta_j = - \frac{\partial E_d}{\partial net_j}$,也就是一个节点的误差项$\delta$是网络误差对这个节点输入的偏导数的相反数。带入上式,得到:
  178. # ![eqn_delta_j.png](images/eqn_delta_j.png)
  179. #
  180. # 将上述推导带入随机梯度下降公式,得到:
  181. # ![eqn_32_34.png](images/eqn_32_34.png)
  182. #
  183. # ### 隐藏层权值训练
  184. #
  185. # 现在我们要推导出隐藏层的$\frac{\partial E_d}{\partial net_j}$。
  186. #
  187. # ![nn3](images/nn3.png)
  188. #
  189. # 首先,我们需要定义节点$j$的所有直接下游节点的集合$Downstream(j)$。例如,对于节点4来说,它的直接下游节点是节点8、节点9。可以看到$net_j$只能通过影响$Downstream(j)$再影响$E_d$。设$net_k$是节点$j$的下游节点的输入,则$E_d$是$net_k$的函数,而$net_k$是$net_j$的函数。因为$net_k$有多个,我们应用全导数公式,可以做出如下推导:
  190. # ![eqn_35_40](images/eqn_35_40.png)
  191. #
  192. # 因为$\delta_j = - \frac{\partial E_d}{\partial net_j}$,带入上式得到:
  193. # ![eqn_delta_hidden.png](images/eqn_delta_hidden.png)
  194. #
  195. #
  196. # 至此,我们已经推导出了反向传播算法。需要注意的是,我们刚刚推导出的训练规则是根据激活函数是sigmoid函数、平方和误差、全连接网络、随机梯度下降优化算法。如果激活函数不同、误差计算方式不同、网络连接结构不同、优化算法不同,则具体的训练规则也会不一样。但是无论怎样,训练规则的推导方式都是一样的,应用链式求导法则进行推导即可。
  197. #
  198. # ### 具体解释
  199. #
  200. # 我们假设每个训练样本为$(\vec{x}, \vec{t})$,其中向量$\vec{x}$是训练样本的特征,而$\vec{t}$是样本的目标值。
  201. #
  202. # ![nn3](images/nn3.png)
  203. #
  204. # 首先,我们根据上一节介绍的算法,用样本的特征$\vec{x}$,计算出神经网络中每个隐藏层节点的输出$a_i$,以及输出层每个节点的输出$y_i$。
  205. #
  206. # 然后,我们按照下面的方法计算出每个节点的误差项$\delta_i$:
  207. #
  208. # * **对于输出层节点$i$**
  209. # ![formular_3.png](images/formular_3.png)
  210. # 其中,$\delta_i$是节点$i$的误差项,$y_i$是节点$i$的输出值,$t_i$是样本对应于节点$i$的目标值。举个例子,根据上图,对于输出层节点8来说,它的输出值是$y_1$,而样本的目标值是$t_1$,带入上面的公式得到节点8的误差项应该是:
  211. # ![forumlar_delta8.png](images/forumlar_delta8.png)
  212. #
  213. # * **对于隐藏层节点**
  214. # ![formular_4.png](images/formular_4.png)
  215. #
  216. # 其中,$a_i$是节点$i$的输出值,$w_{ki}$是节点$i$到它的下一层节点$k$的连接的权重,$\delta_k$是节点$i$的下一层节点$k$的误差项。例如,对于隐藏层节点4来说,计算方法如下:
  217. # ![forumlar_delta4.png](images/forumlar_delta4.png)
  218. #
  219. #
  220. # 最后,更新每个连接上的权值:
  221. # ![formular_5.png](images/formular_5.png)
  222. #
  223. # 其中,$w_{ji}$是节点$i$到节点$j$的权重,$\eta$是一个成为学习速率的常数,$\delta_j$是节点$j$的误差项,$x_{ji}$是节点$i$传递给节点$j$的输入。例如,权重$w_{84}$的更新方法如下:
  224. # ![eqn_w84_update.png](images/eqn_w84_update.png)
  225. #
  226. # 类似的,权重$w_{41}$的更新方法如下:
  227. # ![eqn_w41_update.png](images/eqn_w41_update.png)
  228. #
  229. #
  230. # 偏置项的输入值永远为1。例如,节点4的偏置项$w_{4b}$应该按照下面的方法计算:
  231. # ![eqn_w4b_update.png](images/eqn_w4b_update.png)
  232. #
  233. # 我们已经介绍了神经网络每个节点误差项的计算和权重更新方法。显然,计算一个节点的误差项,需要先计算每个与其相连的下一层节点的误差项。这就要求误差项的计算顺序必须是从输出层开始,然后反向依次计算每个隐藏层的误差项,直到与输入层相连的那个隐藏层。这就是反向传播算法的名字的含义。当所有节点的误差项计算完毕后,我们就可以根据式5来更新所有的权重。
  234. #
  235. #
  236. # ## Program
  237. # +
  238. % matplotlib inline
  239. import numpy as np
  240. from sklearn import datasets, linear_model
  241. import matplotlib.pyplot as plt
  242. # generate sample data
  243. np.random.seed(0)
  244. X, y = datasets.make_moons(200, noise=0.20)
  245. # generate nn output target
  246. t = np.zeros((X.shape[0], 2))
  247. t[np.where(y==0), 0] = 1
  248. t[np.where(y==1), 1] = 1
  249. # plot data
  250. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
  251. plt.show()
  252. # +
  253. # generate the NN model
  254. class NN_Model:
  255. epsilon = 0.01 # learning rate
  256. n_epoch = 1000 # iterative number
  257. nn = NN_Model()
  258. nn.n_input_dim = X.shape[1] # input size
  259. nn.n_output_dim = 2 # output node size
  260. nn.n_hide_dim = 4 # hidden node size
  261. nn.X = X
  262. nn.y = y
  263. # initial weight array
  264. nn.W1 = np.random.randn(nn.n_input_dim, nn.n_hide_dim) / np.sqrt(nn.n_input_dim)
  265. nn.b1 = np.zeros((1, nn.n_hide_dim))
  266. nn.W2 = np.random.randn(nn.n_hide_dim, nn.n_output_dim) / np.sqrt(nn.n_hide_dim)
  267. nn.b2 = np.zeros((1, nn.n_output_dim))
  268. # defin sigmod & its derivate function
  269. def sigmod(X):
  270. return 1.0/(1+np.exp(-X))
  271. def sigmod_derivative(X):
  272. f = sigmod(X)
  273. return f*(1-f)
  274. # network forward calculation
  275. def forward(n, X):
  276. n.z1 = sigmod(X.dot(n.W1) + n.b1)
  277. n.z2 = sigmod(n.z1.dot(n.W2) + n.b2)
  278. return n
  279. # use random weight to perdict
  280. forward(nn, X)
  281. y_pred = np.argmax(nn.z2, axis=1)
  282. # plot data
  283. plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=plt.cm.Spectral)
  284. plt.show()
  285. # +
  286. from sklearn.metrics import accuracy_score
  287. y_true = np.array(nn.y).astype(float)
  288. # back-propagation
  289. def backpropagation(n, X, y):
  290. for i in range(n.n_epoch):
  291. # forward to calculate each node's output
  292. forward(n, X)
  293. # print loss, accuracy
  294. L = np.sum((n.z2 - y)**2)
  295. y_pred = np.argmax(nn.z2, axis=1)
  296. acc = accuracy_score(y_true, y_pred)
  297. print("epoch [%4d] L = %f, acc = %f" % (i, L, acc))
  298. # calc weights update
  299. d2 = n.z2*(1-n.z2)*(y - n.z2)
  300. d1 = n.z1*(1-n.z1)*(np.dot(d2, n.W2.T))
  301. # update weights
  302. n.W2 += n.epsilon * np.dot(n.z1.T, d2)
  303. n.b2 += n.epsilon * np.sum(d2, axis=0)
  304. n.W1 += n.epsilon * np.dot(X.T, d1)
  305. n.b1 += n.epsilon * np.sum(d1, axis=0)
  306. nn.n_epoch = 2000
  307. backpropagation(nn, X, t)
  308. # +
  309. # plot data
  310. y_pred = np.argmax(nn.z2, axis=1)
  311. plt.scatter(X[:, 0], X[:, 1], c=nn.y, cmap=plt.cm.Spectral)
  312. plt.title("ground truth")
  313. plt.show()
  314. plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=plt.cm.Spectral)
  315. plt.title("predicted")
  316. plt.show()
  317. # -
  318. # ## 如何使用类的方法封装多层神经网络?
  319. # +
  320. import numpy as np
  321. from sklearn import datasets, linear_model
  322. from sklearn.metrics import accuracy_score
  323. import matplotlib.pyplot as plt
  324. # define sigmod
  325. def sigmod(X):
  326. return 1.0/(1+np.exp(-X))
  327. # generate the NN model
  328. class NN_Model:
  329. def __init__(self, nodes=None):
  330. self.epsilon = 0.01 # learning rate
  331. self.n_epoch = 1000 # iterative number
  332. if not nodes:
  333. self.nodes = [2, 4, 2] # default nodes size (from input -> output)
  334. else:
  335. self.nodes = nodes
  336. def init_weight(self):
  337. W = []
  338. B = []
  339. n_layer = len(self.nodes)
  340. for i in range(n_layer-1):
  341. w = np.random.randn(self.nodes[i], self.nodes[i+1]) / np.sqrt(self.nodes[i])
  342. b = np.random.randn(1, self.nodes[i+1])
  343. W.append(w)
  344. B.append(b)
  345. self.W = W
  346. self.B = B
  347. def forward(self, X):
  348. Z = []
  349. x0 = X
  350. for i in range(len(self.nodes)-1):
  351. z = sigmod(np.dot(x0, self.W[i]) + self.B[i])
  352. x0 = z
  353. Z.append(z)
  354. self.Z = Z
  355. return Z[-1]
  356. # back-propagation
  357. def backpropagation(self, X, y, n_epoch=None, epsilon=None):
  358. if not n_epoch: n_epoch = self.n_epoch
  359. if not epsilon: epsilon = self.epsilon
  360. self.X = X
  361. self.Y = y
  362. for i in range(n_epoch):
  363. # forward to calculate each node's output
  364. self.forward(X)
  365. self.evaluate()
  366. # calc weights update
  367. W = self.W
  368. B = self.B
  369. Z = self.Z
  370. D = []
  371. d0 = y
  372. n_layer = len(self.nodes)
  373. for j in range(n_layer-1, 0, -1):
  374. jj = j - 1
  375. z = self.Z[jj]
  376. if j == n_layer - 1:
  377. d = z*(1-z)*(d0 - z)
  378. else:
  379. d = z*(1-z)*np.dot(d0, W[j].T)
  380. d0 = d
  381. D.insert(0, d)
  382. # update weights
  383. for j in range(n_layer-1, 0, -1):
  384. jj = j - 1
  385. if jj != 0:
  386. W[jj] += epsilon * np.dot(Z[jj-1].T, D[jj])
  387. else:
  388. W[jj] += epsilon * np.dot(X.T, D[jj])
  389. B[jj] += epsilon * np.sum(D[jj], axis=0)
  390. def evaluate(self):
  391. z = self.Z[-1]
  392. # print loss, accuracy
  393. L = np.sum((z - self.Y)**2)
  394. y_pred = np.argmax(z, axis=1)
  395. y_true = np.argmax(self.Y, axis=1)
  396. acc = accuracy_score(y_true, y_pred)
  397. print("L = %f, acc = %f" % (L, acc))
  398. # +
  399. # generate sample data
  400. np.random.seed(0)
  401. X, y = datasets.make_moons(200, noise=0.20)
  402. # generate nn output target
  403. t = np.zeros((X.shape[0], 2))
  404. t[np.where(y==0), 0] = 1
  405. t[np.where(y==1), 1] = 1
  406. # plot data
  407. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
  408. plt.show()
  409. # +
  410. # use the NN model and training
  411. nn = NN_Model([2, 6, 2])
  412. nn.init_weight()
  413. nn.backpropagation(X, t, 2000)
  414. # +
  415. # predict results & plot results
  416. y_res = nn.forward(X)
  417. y_pred = np.argmax(y_res, axis=1)
  418. # plot data
  419. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
  420. plt.title("ground truth")
  421. plt.show()
  422. plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=plt.cm.Spectral)
  423. plt.title("predicted")
  424. plt.show()
  425. # -
  426. # ## 深入分析
  427. # +
  428. # print some results
  429. print(y_res[1:10, :])
  430. # -
  431. # **问题**
  432. # 1. 我们希望得到的每个类别的概率
  433. # 2. 如何做多分类问题?
  434. # 3. 如何能让神经网络更快的训练好?
  435. # 4. 如何抽象,让神经网络的类支持更多的类型的层
  436. # ## Softmax & 交叉熵代价函数
  437. #
  438. # softmax经常被添加在分类任务的神经网络中的输出层,神经网络的反向传播中关键的步骤就是求导,从这个过程也可以更深刻地理解反向传播的过程,还可以对梯度传播的问题有更多的思考。
  439. #
  440. # ### softmax 函数
  441. #
  442. # softmax(柔性最大值)函数,一般在神经网络中, softmax可以作为分类任务的输出层。其实可以认为softmax输出的是几个类别选择的概率,比如我有一个分类任务,要分为三个类,softmax函数可以根据它们相对的大小,输出三个类别选取的概率,并且概率和为1。
  443. #
  444. # softmax函数的公式是这种形式:
  445. # ![softmax](images/softmax.png)
  446. #
  447. # * $S_i$是经过softmax的类别概率输出
  448. # * $z_k$是神经元的输出
  449. #
  450. # 更形象的如下图表示:
  451. # ![softmax_demo](images/softmax_demo.png)
  452. # softmax直白来说就是将原来输出是3,1,-3通过softmax函数一作用,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们就可以将它理解成概率,在最后选取输出结点的时候,我们就可以选取概率最大(也就是值对应最大的)结点,作为我们的预测目标!
  453. #
  454. #
  455. #
  456. # 首先是神经元的输出,一个神经元如下图:
  457. # ![softmax_neuron](images/softmax_neuron.png)
  458. #
  459. # 神经元的输出设为:
  460. # ![softmax_neuron_output_eqn.png](images/softmax_neuron_output_eqn.png)
  461. # 其中$W_{ij}$是第$i$个神经元的第$j$个权重,$b$是偏置。$z_i$表示该网络的第$i$个输出。
  462. #
  463. # 给这个输出加上一个softmax函数,那就变成了这样:
  464. # ![softmax_neuron_output2_eqn.png](images/softmax_neuron_output2_eqn.png)
  465. # $a_i$代表softmax的第$i$个输出值,右侧套用了softmax函数。
  466. #
  467. #
  468. # ### 损失函数 loss function
  469. #
  470. # 在神经网络反向传播中,要求一个损失函数,这个损失函数其实表示的是真实值与网络的估计值的误差,知道误差了,才能知道怎样去修改网络中的权重。
  471. #
  472. # 损失函数可以有很多形式,这里用的是交叉熵函数,主要是由于这个求导结果比较简单,易于计算,并且交叉熵解决某些损失函数学习缓慢的问题。交叉熵的函数是这样的:
  473. #
  474. # ![cross_entropy_loss](images/cross_entropy_loss.png)
  475. #
  476. # 其中$y_i$表示真实的分类结果。
  477. #
  478. #
  479. # ## References
  480. # * 反向传播算法
  481. # * [零基础入门深度学习(3) - 神经网络和反向传播算法](https://www.zybuluo.com/hanbingtao/note/476663)
  482. # * [Neural Network Using Python and Numpy](https://www.python-course.eu/neural_networks_with_python_numpy.php)
  483. # * http://www.cedar.buffalo.edu/%7Esrihari/CSE574/Chap5/Chap5.3-BackProp.pdf
  484. # * https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/
  485. # * Softmax & 交叉熵
  486. # * [交叉熵代价函数(作用及公式推导)](https://blog.csdn.net/u014313009/article/details/51043064)
  487. # * [手打例子一步一步带你看懂softmax函数以及相关求导过程](https://www.jianshu.com/p/ffa51250ba2e)
  488. # * [简单易懂的softmax交叉熵损失函数求导](https://www.jianshu.com/p/c02a1fbffad6)

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