用于存放学校的作业便于复习。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

440 lines
15 KiB

преди 7 месеца
  1. % https://zhuanlan.zhihu.com/p/165140693
  2. % https://zhuanlan.zhihu.com/p/36868831
  3. %声明文档类型和比例
  4. \documentclass[aspectratio=169, 10pt, utf8, mathserif]{ctexbeamer}
  5. %调用相关的宏包
  6. % \usepackage{beamerfoils}
  7. \usepackage[outputdir=./latex-output]{minted}
  8. \usepackage{multicol}
  9. \setminted{breaklines=true, fontsize=\zihao{-6}}
  10. % \PassOptionsToPackage{fontsize=\zihao{-6}}{minted}
  11. \definecolor{shadecolor}{RGB}{204,232,207}
  12. \usetheme{Berlin} %主题包之一,直接换名字即可
  13. \setbeamertemplate{page number in head/foot}[totalframenumber]
  14. \usecolortheme{beaver} %主题色之一,直接换名字即可。
  15. \usefonttheme{professionalfonts}
  16. % 设置用acrobat打开就会全屏显示
  17. \hypersetup{pdfpagemode=FullScreen}
  18. % 设置logo
  19. % \pgfdeclareimage[height=2cm, width=2cm]{university-logo}{120701101}
  20. % \logo{\pgfuseimage{university-logo}}
  21. \parskip=1.2em
  22. %--------------正文开始---------------
  23. \begin{document}
  24. %每个章节都有小目录
  25. \AtBeginSubsection[]
  26. {
  27. \begin{frame}<beamer>
  28. \tableofcontents[currentsection,currentsubsection]
  29. \end{frame}
  30. }
  31. \title{《深度学习》实验4讲解}
  32. \subtitle{多层感知机/全连接层}
  33. \author[岳锦鹏]{岳锦鹏 \\ \small 10213903403}
  34. \date{\today}
  35. \begin{frame}
  36. %\maketitle
  37. \titlepage
  38. \end{frame}
  39. \begin{frame}
  40. \frametitle{目录}
  41. \tableofcontents[hideallsubsections]
  42. \end{frame}
  43. \section{整体浏览}
  44. \begin{frame}[fragile]
  45. 首先逐个观察每个填空的部分需要完成哪些内容。
  46. 可以看到需要完成ReLU的反向传播过程。
  47. \begin{minted}{python}
  48. class Relu:
  49. def __init__(self):
  50. self.mem = {}
  51. def forward(self, x):
  52. self.mem['x'] = x
  53. return np.where(x > 0, x, np.zeros_like(x))
  54. def backward(self, grad_y):
  55. '''
  56. grad_y: same shape as x
  57. '''
  58. # ==========
  59. # todo '''请完成激活函数的梯度后传'''
  60. # ==========
  61. \end{minted}
  62. \end{frame}
  63. \begin{frame}[fragile]
  64. 对于主要的模型部分,需要完成计算损失。
  65. \begin{minted}{python}
  66. def compute_loss(self, log_prob, labels):
  67. '''
  68. log_prob is the predicted probabilities
  69. labels is the ground truth
  70. Please return the loss
  71. '''
  72. # ==========
  73. # todo '''请完成多分类问题的损失计算 损失为: 交叉熵损失 + L2正则项'''
  74. # ==========
  75. \end{minted}
  76. \end{frame}
  77. \begin{frame}[fragile]
  78. 按照给定的网络结构完成前向传播过程。
  79. \begin{minted}{python}
  80. def forward(self, x):
  81. '''
  82. x is the input features
  83. Please return the predicted probabilities of x
  84. '''
  85. # ==========
  86. # todo '''请搭建一个MLP前馈神经网络 补全它的前向传播 MLP结构为FFN --> RELU --> FFN --> Softmax'''
  87. # ==========
  88. \end{minted}
  89. \end{frame}
  90. \begin{frame}[fragile]
  91. 完成主模型的后向传播,注意这里可以使用其中各层的反向传播函数。
  92. \begin{minted}{python}
  93. def backward(self, label):
  94. '''
  95. label is the ground truth
  96. Please compute the gradients of self.W1 and self.W2
  97. '''
  98. # ==========
  99. # todo '''补全该前馈神经网络的后向传播算法'''
  100. # ==========
  101. \end{minted}
  102. \end{frame}
  103. \begin{frame}[fragile]
  104. 更新参数,这里要注意不要忘记正则项的损失。
  105. \begin{minted}{python}
  106. def update(self):
  107. '''
  108. Please update self.W1 and self.W2
  109. '''
  110. # ==========
  111. # todo '''更新该前馈神经网络的参数'''
  112. # ==========
  113. \end{minted}
  114. \end{frame}
  115. \section{逐个实现}
  116. \subsection{ReLU的反向传播}
  117. \begin{frame}[fragile]
  118. \begin{multicols}{2}
  119. 首先看ReLU的反向传播,由于ReLU的公式为(符号和课件中保持一致所以用了$a$$x$
  120. $$
  121. a = \begin{cases}
  122. x,\quad & x>0 \\
  123. 0,\quad & x\leqslant 0 \\
  124. \end{cases}
  125. $$
  126. 所以显然
  127. $$
  128. \frac{\mathrm{d}a}{\mathrm{d}x} = \begin{cases}
  129. 1,\quad & x>0 \\
  130. 0,\quad & x\leqslant 0 \\
  131. \end{cases}
  132. $$
  133. \columnbreak
  134. \begin{minted}{python}
  135. class Relu:
  136. def __init__(self):
  137. self.mem = {}
  138. def forward(self, x):
  139. self.mem['x'] = x
  140. return np.where(x > 0, x, np.zeros_like(x))
  141. def backward(self, grad_y):
  142. '''
  143. grad_y: same shape as x
  144. '''
  145. # ==========
  146. # todo '''请完成激活函数的梯度后传'''
  147. # ==========
  148. \end{minted}
  149. \end{multicols}
  150. \end{frame}
  151. \begin{frame}[fragile]
  152. \begin{multicols}{2}
  153. 由于要计算梯度时要根据输入$x$是否大于0判断,所以这里使用了\mintinline{python}{self.mem}来记忆上次输入的$x$,在反向传播的时候就可以使用记忆的$x$来进行分支,这里可以利用 numpy的批量操作能力实现,\mintinline{python}{grad_y}是传入的梯度,返回的结果应为本层梯度与传入梯度的乘积:
  154. $$
  155. return = \frac{\mathrm{d}a}{\mathrm{d}x} \times grad\_y=\begin{cases}
  156. grad\_y,\quad & x>0 \\
  157. 0,\quad & x\leqslant 0 \\
  158. \end{cases}
  159. $$
  160. 因此写出代码如下:
  161. \columnbreak
  162. \begin{minted}{python}
  163. class Relu:
  164. def __init__(self):
  165. self.mem = {}
  166. def forward(self, x):
  167. self.mem['x'] = x
  168. return np.where(x > 0, x, np.zeros_like(x))
  169. def backward(self, grad_y):
  170. '''
  171. grad_y: same shape as x
  172. '''
  173. # ==========
  174. # todo '''请完成激活函数的梯度后传'''
  175. return np.where(self.mem['x'] > 0, grad_y, np.zeros_like(grad_y))
  176. # ==========
  177. \end{minted}
  178. \end{multicols}
  179. \mint{python}|return np.where(self.mem['x'] > 0, grad_y, np.zeros_like(grad_y))|
  180. \end{frame}
  181. \subsection{交叉熵损失+L2正则项}
  182. \begin{frame}[fragile]
  183. \begin{multicols}{2}
  184. 交叉熵损失的函数为
  185. $$
  186. loss=\sum_{\text{每个类别}i} -y_i \log(\hat{y}_i)
  187. $$
  188. L2正则项的损失为
  189. $
  190. \lambda \left\Vert W \right\Vert
  191. $$\lambda$为系数,$W$为权重,距离用的是欧几里得距离,即
  192. $$\displaystyle \sqrt{\sum_{W\text{中的每个参数}x} x^{2} }$$
  193. 这里有两层网络,也就是两层权重,所以
  194. $$
  195. L2 = \lambda_1 \left\Vert W_1 \right\Vert +\lambda_2 \left\Vert W_2 \right\Vert
  196. $$
  197. \columnbreak
  198. \begin{minted}{python}
  199. def compute_loss(self, log_prob, labels):
  200. '''
  201. log_prob is the predicted probabilities
  202. labels is the ground truth
  203. Please return the loss
  204. '''
  205. # ==========
  206. # todo '''请完成多分类问题的损失计算 损失为: 交叉熵损失 + L2正则项'''
  207. # ==========
  208. \end{minted}
  209. \end{multicols}
  210. \end{frame}
  211. \begin{frame}[fragile]
  212. \begin{multicols}{2}
  213. \mintinline{python}{log_prob}应该是希望传入已经经过$\log$计算的$\hat{y}$,但是在lab4.ipynb里发现其实是没有经过$\log$计算的\mintinline{python}{pred_y},这里还得自己计算$\log(\hat{y})$,但是$\log (\hat{y}_i)$由于在前向传播的时候计算过就提前缓存在\mintinline{python}{self.log_value}了。
  214. \mintinline{python}{labels}|$y$\mintinline{python}{self.log_value}|$\log(\hat{y})$是one-hot编码的,形状为[批大小,类别数],根据公式在类别数维度求和,所以是\mintinline{python}{axis=1}。注意还要在批大小维度求平均,即\mintinline{python}{.mean(0)}
  215. 计算距离这里直接使用了\mintinline{python}{np.linalg.norm}
  216. \columnbreak
  217. \begin{minted}{python}
  218. def compute_loss(self, log_prob, labels):
  219. '''
  220. log_prob is the predicted probabilities
  221. labels is the ground truth
  222. Please return the loss
  223. '''
  224. # ==========
  225. # todo '''请完成多分类问题的损失计算 损失为: 交叉熵损失 + L2正则项'''
  226. return - np.sum(labels * self.log_value, axis=1).mean(0) + self.lambda1 * np.linalg.norm(self.W1) + self.lambda1 * np.linalg.norm(self.W2)
  227. # ==========
  228. \end{minted}
  229. \end{multicols}
  230. \end{frame}
  231. \subsection{主模型的前向传播}
  232. \begin{frame}[fragile]
  233. \begin{multicols}{2}
  234. 这里$x$的形状是[批大小,28,28],这里的两个28分别是图像高度和宽度,而且可以观察到\mintinline{python}{self.W1}的形状是[100, 785],但是$28\times 28=784$,说明需要把高度和宽度拉平后还需要拼接一个\mintinline{python}{np.ones}来替代偏置项的作用。即
  235. \mint{python}|np.concatenate((x.reshape(x.shape[0], -1), np.ones((x.shape[0], 1))), axis=1)|
  236. \mintinline{python}{Matmul.backward}的注释中可以看到\\
  237. \mintinline{python}{x: shape(d, N)},所以拼接好之后还需要进行转置。
  238. \columnbreak
  239. \begin{minted}{python}
  240. def forward(self, x):
  241. '''
  242. x is the input features
  243. Please return the predicted probabilities of x
  244. '''
  245. # ==========
  246. # todo '''请搭建一个MLP前馈神经网络 补全它的前向传播 MLP结构为FFN --> RELU --> FFN --> Softmax'''
  247. # ==========
  248. \end{minted}
  249. \end{multicols}
  250. \end{frame}
  251. \begin{frame}[fragile]
  252. \begin{multicols}{2}
  253. \mintinline{python}{Softmax.forward}的注释中可以看到\mintinline{python}{x: shape(N, c)},因此在进行Softmax操作前还需要再转置回来。
  254. 理论上这时候就可以直接返回了,不需要用到\mintinline{python}{self.log}$\log$是在计算交叉熵时才会用到的操作,但是在lab4.ipynb中非要先反向传播再计算损失,反向传播需要\mintinline{python}{self.log.backward},但这又需要先调用过\mintinline{python}{self.log.forward}才能把输入记忆到\mintinline{python}{self.mem}中,才能正确返回梯度。
  255. 那没办法,只能先调用一下\mintinline{python}{self.log.forward}把结果缓存起来。
  256. \columnbreak
  257. \begin{minted}{python}
  258. def forward(self, x):
  259. '''
  260. x is the input features
  261. Please return the predicted probabilities of x
  262. '''
  263. # ==========
  264. # todo '''请搭建一个MLP前馈神经网络 补全它的前向传播 MLP结构为FFN --> RELU --> FFN --> Softmax'''
  265. y = np.concatenate((x.reshape(x.shape[0], -1), np.ones((x.shape[0], 1))), axis=1).T # 这形状真难弄
  266. y = self.mul_h1.forward(self.W1, y)
  267. y = self.relu.forward(y)
  268. y = self.mul_h2.forward(self.W2, y).T
  269. y = self.softmax.forward(y)
  270. # print(y)
  271. # 唉没办法,非要先反向传播再计算损失,那只能把log的结果缓存起来了
  272. self.log_value = self.log.forward(y)
  273. return y
  274. # ==========
  275. \end{minted}
  276. \end{multicols}
  277. \end{frame}
  278. \subsection{主模型的反向传播}
  279. \begin{frame}[fragile]
  280. \begin{multicols}{2}
  281. 前面的准备工作都实现了后,这里就很简单了,只需要逐层反向传播就行了。
  282. 注意交叉熵损失为
  283. $$
  284. loss=\sum_{\text{每个类别}i} -y_i \log(\hat{y}_i)
  285. $$
  286. 所以
  287. $$
  288. \frac{\mathrm{d}loss}{\mathrm{d}\log(\hat{y}_i)}= -y_i
  289. $$
  290. 因此首个梯度为 \mintinline{python}{-label},后续的反向传播就交给各层的\mintinline{python}{backward}函数了。
  291. \columnbreak
  292. \begin{minted}{python}
  293. def backward(self, label):
  294. '''
  295. label is the ground truth
  296. Please compute the gradients of self.W1 and self.W2
  297. '''
  298. # ==========
  299. # todo '''补全该前馈神经网络的后向传播算法'''
  300. # ==========
  301. \end{minted}
  302. \end{multicols}
  303. \end{frame}
  304. \begin{frame}[fragile]
  305. \begin{multicols}{2}
  306. 仍然要注意在Softmax反向传播后需要转置一下。
  307. \mintinline{python}{Matmul.backward}返回的结果为\mintinline{python}{return grad_x, grad_W},这也提示了全连接层要保留对输入和对参数的求导,对输入的求导用来继续反向传播,对参数的求导用来更新参数。
  308. \columnbreak
  309. \begin{minted}{python}
  310. def backward(self, label):
  311. '''
  312. label is the ground truth
  313. Please compute the gradients of self.W1 and self.W2
  314. '''
  315. # ==========
  316. # todo '''补全该前馈神经网络的后向传播算法'''
  317. temp = self.log.backward(-label)
  318. temp = self.softmax.backward(temp).T
  319. temp, self.gradient2 = self.mul_h2.backward(temp)
  320. temp = self.relu.backward(temp)
  321. temp, self.gradient1 = self.mul_h1.backward(temp)
  322. # ==========
  323. \end{minted}
  324. \end{multicols}
  325. \end{frame}
  326. \subsection{更新参数}
  327. \begin{frame}[fragile]
  328. \begin{multicols}{2}
  329. 更新参数只需要按照公式即可,不要忘记L2正则项的梯度,以下以$W_1$为例,$W_2$同理。
  330. $W_1^{(i,j)}$表示$W_1$的第$i$$j$列的元素,lr表示learning rate,即学习率。
  331. $$
  332. \frac{\mathrm{d}L2}{\mathrm{d}W_1^{(i,j)}}= \frac{2 \lambda_1 W_1^{(i,j)}}{\left\Vert W_1 \right\Vert }
  333. $$
  334. $$
  335. W_1 = W_1 - \left( \frac{\mathrm{d}loss}{\mathrm{d}W_1}+\frac{\mathrm{d}L2}{\mathrm{d}W_1} \right) \times lr
  336. $$
  337. \columnbreak
  338. \begin{minted}{python}
  339. def update(self):
  340. '''
  341. Please update self.W1 and self.W2
  342. '''
  343. # ==========
  344. # todo '''更新该前馈神经网络的参数'''
  345. self.W1 -= (self.gradient1 + 2 * self.lambda1 * self.W1 / np.linalg.norm(self.W1)) * self.lr
  346. self.W2 -= (self.gradient2 + 2 * self.lambda1 * self.W2 / np.linalg.norm(self.W2)) * self.lr
  347. # ==========
  348. \end{minted}
  349. \end{multicols}
  350. \end{frame}
  351. \begin{frame}
  352. \zihao{-4}\centering{感谢观看!}
  353. \end{frame}
  354. \end{document}