0


【一起撸个DL框架】5 实现:自适应线性单元

文章目录

5 实现:自适应线性单元🍇

1 简介

上一篇:【一起撸个DL框架】4 反向传播求梯度

上一节我们实现了计算图的反向传播,可以求结果节点关于任意节点的梯度。下面我们将使用梯度来更新参数,实现一个简单的自适应线性单元

我们本次拟合的目标函数是一个简单的线性函数:

  1. y
  2. =
  3. 2
  4. x
  5. +
  6. 1
  7. y=2x+1
  8. y=2x+1,通过随机数生成一些训练数据,将许多组x和对应的结果y值输入模型,但是并不告诉模型具体函数中的系数参数“2”和偏置参数“1”,看看模型能否通过数据“学习”到参数的值。

图1:自适应线性单元的计算图

2 损失函数

2.1 梯度下降法

损失是对模型好坏的评价指标,表示模型输出结果与正确答案(也称为标签)之间的差距。所以损失值越小就说明模型越准确,训练过程的目的便是最小化损失函数的值。

自适应线性单元是一个回归任务,我们这里将使用绝对值损失,将模型输出与正确答案之间的差的绝对值作为损失函数的值,即

  1. l
  2. o
  3. s
  4. s
  5. =
  6. l
  7. a
  8. d
  9. d
  10. loss=|l-add|
  11. loss=∣ladd∣。

评价指标有了,可是如何才能达标呢?或者说如何才能降低损失函数的值?计算图中有四个变量:

  1. x
  2. ,
  3. w
  4. ,
  5. b
  6. ,
  7. l
  8. x,w,b,l
  9. x,w,b,l,而我们训练过程的任务是调整参数
  10. w
  11. ,
  12. b
  13. w,b
  14. w,b的值,以降低损失。因此训练过程中的自变量是wb,而把xl看作常量。此时损失函数是关于wb的二元函数
  15. l
  16. o
  17. s
  18. s
  19. =
  20. f
  21. (
  22. w
  23. ,
  24. b
  25. )
  26. loss=f(w,b)
  27. loss=f(w,b),我们只需要求函数的梯度
  28. f
  29. (
  30. w
  31. ,
  32. b
  33. )
  34. =
  35. (
  36. f
  37. w
  38. ,
  39. f
  40. b
  41. )
  42. \triangledown f(w,b)=(\frac{\partial f}{\partial w},\frac{\partial f}{\partial b})
  43. f(w,b)=(∂wf​,∂bf​),则梯度的反方向就是函数下降最快的方向。沿着梯度的方向更新参数wb的值,就可以降低损失。这就是经典的优化算法:**梯度下降法**。

2.2 补充

关于损失和优化的概念,大家可能还是有些模糊。上面损失只讲到了一个输入x值对应的模型输出与实际结果之间的差距,但使用整个数据集的平均差距可能更容易理解,就像中学的线性回归

图2所示,改变直线的斜率w,将改变直线与数据点的贴近程度,即改变了损失函数loss的值。
在这里插入图片描述图2:损失与参数更新示意图

参考: 【深度学习】3-从模型到学习的思路整理_清风莫追的博客-CSDN博客

3 整理项目结构

我们的小项目的代码也渐渐多起来了,好的目录结构将使它更加易于扩展。关于python包结构的知识大家可以自行去了解,大致目录结构如下:

  1. - example
  2. - ourdl
  3. - core
  4. - __init__.py
  5. - node.py
  6. - ops
  7. - __init__.py
  8. - loss.py
  9. - ops.py
  10. __init__.py

给这个简单框架的名字叫做OurDL,使用框架搭建的计算图等程序放在

  1. example

目录下。在

  1. ourdl/core/node.py

中存放了节点基类和变量类的定义,在

  1. ourdl/ops/

下存放了运算节点的定义,包括损失函数和加法、乘法节点等。

4 损失函数的实现

  1. /ourdl/ops/loss.py

中,

  1. from..core import Node
  2. classValueLoss(Node):'''损失函数:作差取绝对值'''defcompute(self):
  3. self.value = self.parent1.value - self.parent2.value
  4. self.flag = self.value >0ifnot self.flag:
  5. self.value =-self.value
  6. defget_parent_grad(self, parent):
  7. a =1if self.flag else-1
  8. b =1if parent == self.parent1 else-1return a * b

其中

  1. compute()

方法很显然就是对两个输入作差取绝对值;

  1. get_parent_grad()

方法求本节点关于父节点的梯度。有绝对值如何求梯度?大家可以画一画绝对值函数的图像。

5 修改节点类(Node)

  1. ourdl/core/node.py

  1. classNode:pass# 省略了一些方法的定义,大家可以查看上一篇文章defclear(self):'''递归清除父节点的值和梯度信息'''
  2. self.grad =Noneif self.parent1 isnotNone:# 清空非变量节点的值
  3. self.value =Nonefor parent in[self.parent1, self.parent2]:if parent isnotNone:
  4. parent.clear()defupdate(self, lr=0.001):'''根据本节点的梯度,更新本节点的值'''
  5. self.value -= lr * self.grad # 减号表示梯度的反方向

我在节点类中新增了两个方法,其中

  1. clear()

用于清除多余的节点值和梯度信息,因为当节点值或梯度已经存在时会直接返回结果而不会递归去求了(*见

  1. get_grad()

  1. forward()

的代码*)。

  1. update()

有一个学习率参数

  1. lr

,更新幅度太大可能导致参数值一直在目标值左右晃悠,无法收敛

6 自适应线性单元

  1. /example/01_esay/自适应线性单元.py

  1. import sys
  2. sys.path.append('../..')from ourdl.core import Varrible
  3. from ourdl.ops import Mul, Add
  4. from ourdl.ops.loss import ValueLoss
  5. if __name__ =='__main__':# 搭建计算图
  6. x = Varrible()
  7. w = Varrible()
  8. mul = Mul(parent1=x, parent2=w)
  9. b = Varrible()
  10. add = Add(parent1=mul, parent2=b)
  11. label = Varrible()
  12. loss = ValueLoss(parent1=label, parent2=add)# 参数初始化
  13. w.set_value(0)
  14. b.set_value(0)# 生成训练数据import random
  15. data_x =[random.uniform(-10,10)for i inrange(10)]# 按均匀分布生成[-10, 10]范围内的随机实数
  16. data_label =[2* data_x_one +1for data_x_one in data_x]# 开始训练for i inrange(len(data_x)):
  17. x.set_value(data_x[i])
  18. label.set_value(data_label[i])
  19. loss.forward()# 前向传播 --> 求梯度会用到损失函数的值
  20. w.get_grad()
  21. b.get_grad()
  22. w.update(lr=0.05)
  23. b.update(lr=0.1)
  24. loss.clear()print("w:{:.2f}, b:{:.2f}".format(w.value, b.value))print("最终结果:{:.2f}x+{:.2f}".format(w.value, b.value))

运行结果:

  1. w:0.13, b:0.10
  2. w:0.36, b:0.20
  3. w:0.58, b:0.10
  4. w:0.74, b:0.00
  5. w:1.13, b:0.10
  6. w:1.43, b:0.20
  7. w:1.62, b:0.30
  8. w:1.94, b:0.20
  9. w:1.50, b:0.30
  10. w:1.87, b:0.40
  11. 最终结果:1.87x+0.40

上面自适应线性单元的训练,已经能够大致展现深度学习模型的训练流程:

  • 搭建模型 --> 初始化参数 --> 准备数据 --> 使用数据更新参数的值

我们这里参数只更新了10次,结果就已经大致接近了我们的目标函数

  1. y
  2. =
  3. 2
  4. x
  5. +
  6. 1
  7. y=2x+1
  8. y=2x+1。大家可以试试更改学习率
  1. lr

,训练数据集的大小,观察运行结果会发生怎样的变化。(必备技能:调参)


下一篇:【一起撸个深度学习框架】6 折与曲的相会——激活函数


本文转载自: https://blog.csdn.net/m0_63238256/article/details/130449344
版权归原作者 清风莫追 所有, 如有侵权,请联系我们删除。

“【一起撸个DL框架】5 实现:自适应线性单元”的评论:

还没有评论