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

简介: 6 折与曲的相会——激活函数🍈1 前言在上一节,我们实现了一个“自适应线性单元”,不断地将一个一次函数的输入和输出“喂”给它,它就可以自动地找到一次函数y = w x + b y=wx+by=wx+b中合适的参数值w和b。计算图通过前向传播和反向传播,初步展现了它的神奇之处。

6 折与曲的相会——激活函数🍈

1 前言

在上一节,我们实现了一个“自适应线性单元”,不断地将一个一次函数的输入和输出“喂”给它,它就可以自动地找到一次函数y = w x + b  中合适的参数值w和b。计算图通过前向传播和反向传播,初步展现了它的神奇之处。

但在实际遇到的问题中,输入与输出之间往往并不是简单的线性关系,它们之间的函数关系可能是二次的、指数、甚至分段的。此时”自适应线性单元“就不足以满足我们的需求了。而”激活函数“,将为计算图带来一种拟合这些非线性函数关系的能力。


同时为了得到对于激活函数更加清晰和形象化的认知,本节我们还将使用matplotlib对拟合过程进行一些可视化的展现。

本节任务:在计算图中加入激活函数relu,拟合二次函数  y=x^2y

在区间[0,2]的一小段曲线。

7f1e1a9f53c342b8a3c7681d52b54469.gif

2 激活函数

关于”激活函数“这个名称(非专业解释),首先我们可以看阶跃函数。当输入超过0这个阈值时,输出就从0跳到了1,0是一个非激活的状态,而1是一个激活的状态。这个和生物领域中神经元间的突触有一定相似性,当突触间的兴奋性神经元递质超过某个阈值后,下一个神经元才会进入兴奋状态继续传递信号。

屏幕截图 2023-12-28 183956.png

33ec7068204b47b4a14aff5f8c1bb47a.png

2.1 Relu

人们发明了许多各式各样的激活函数,它们有着不同的特点,而Relu是其中比较常用的一种。Relu是一个简单的分段函数,它的核心思想是通过多段折线来贴近曲线,折线段越多、越短,拟合效果就越好,理论上使用relu几乎可以较好地任何曲线。

屏幕截图 2023-12-28 184036.png

abd64edce0f94fd98f19d804e06e34ba.png


Relu节点的实现

# ourdl/ops/ops.py
class Relu(Op):
    def compute(self):
        assert len(self.parents) == 1
        self.value = self.parents[0].value if self.parents[0].value >= 0 else 0
    def get_parent_grad(self, parent):
        return 1. if self.parents[0].value > 0 else 0  # 发现relu的导函数就是step
    @staticmethod
    def relu(x: float):
        '''静态方法 --> 在计算图之外使用relu'''
        return x if x >= 0. else 0.

在前向传播的过程中,它接受一个父节点的输入,并产生一个输出。我们还使用装饰器@staticmethod,实现了一个静态方法relu(x),这样我们也可以在计算图之外直接调用relu函数了,例如可以在使用matplotlib绘制函数图像时用到。

2.3 LeakyRelu

和加法节点、乘法节点等节点一样,激活函数也是计算图中的一个运算节点,需要在该节点类中实现对应的get_parent_grad()方法对父节点进行求导。Relu函数在输入小于0时函数值都是0,对应的导数也是0,这种情况下参数就不会进行更新了。

人们提出了一种对Relu函数的修正方案,那就是LeakyRelu。在输入大于等于0的部分函数值不变,仍然是x;但是在输入小于0的部分取0.1 x 0.1x0.1x,这样在反向传播的过程中,节点的输入小于0时,虽然导数只有0.1,但并没有直接消失,参数仍然可以进行更新。(这里0.1是一个”超参数“,也可以取其它值)


在我的一些尝试中,使用Relu函数时训练过程会卡住一直无法拟合,但LeakyRelu可以一定程度上缓解问题,仍然可以拟合只是比较慢。

3d5c64a925964edabd7378a23716d674.png

LeakyRelu节点的实现

# ourdl/ops/ops.py
class LeakyRelu(Op):
    '''消除了relu中导数为0的情况'''
    def compute(self):
        assert len(self.parents) == 1
        t = self.parents[0].value
        self.value = t if t >= 0 else t * 0.1
    def get_parent_grad(self, parent):
        return 1. if self.parents[0].value > 0 else 0.1  # 发现relu的导函数就是step
    @staticmethod
    def relu(x: float):
        '''静态方法 --> 在计算图之外使用leakyrelu'''
        return x if x >= 0. else x * 0.1

超参数”0.1"直接写死在代码中了,因为它通常并不需要改变。

3 拟合曲线的尝试

3.1 设计计算图

a884f64dc9e34284bc9d4d1ede25cc13.png

这个计算图中明确地画出了所有的节点,看起来有一些复杂。从整体看,计算图包含了三次变换:


输 入 − − > 线 性 变 换 − − > 激 活 函 数 − − > 线 性 变 换 − − > 输 出

上述三次变换各自的意义是什么?计算图为什么设计成这个样子?这都是很重要的问题。


其实在图一中大致就能得到答案。


第一次变换,产生两条不同的直线,它们有着不同的斜率,更重要的是:它们与x轴有着不同的交点。

第二次变换,使用激活函数relu(图一中是LeakyRelu),两条直线都在与x轴的交点处折断,得到两条折线。

第三次变换,两条折线线性叠加,由于它们折断点不同,故得到是一个三段折线。

而我所希望的,就是利用三段的折线去尽可能地贴合二次函数的曲线。

【一起撸个深度学习框架】6 折与曲的相会——激活函数(2):https://developer.aliyun.com/article/1407204?spm=a2c6h.13148508.setting.25.79f64f0ecKMDuK


相关文章
|
机器学习/深度学习 PyTorch 算法框架/工具
深度学习Pytorch-Tensor函数
深度学习Pytorch-Tensor函数
94 0
|
机器学习/深度学习 算法
深度学习激活函数
深度学习激活函数
67 0
|
24天前
|
机器学习/深度学习 自然语言处理 语音技术
深入探索深度学习中的兼容性函数:从原理到实践
深入探索深度学习中的兼容性函数:从原理到实践
34 3
|
24天前
|
机器学习/深度学习 自然语言处理 语音技术
揭秘深度学习中的兼容性函数:原理、类型与应用
揭秘深度学习中的兼容性函数:原理、类型与应用
|
24天前
|
机器学习/深度学习 自然语言处理 语音技术
揭秘深度学习中的注意力机制:兼容性函数的深度解析
揭秘深度学习中的注意力机制:兼容性函数的深度解析
|
24天前
|
机器学习/深度学习 自然语言处理 语音技术
探索深度学习中的兼容性函数:构建高效注意力机制的基石
探索深度学习中的兼容性函数:构建高效注意力机制的基石
28 0
|
24天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的兼容性函数:原理、类型与未来趋势
深度学习中的兼容性函数:原理、类型与未来趋势
|
2月前
|
机器学习/深度学习 编解码
深度学习笔记(三):神经网络之九种激活函数Sigmoid、tanh、ReLU、ReLU6、Leaky Relu、ELU、Swish、Mish、Softmax详解
本文介绍了九种常用的神经网络激活函数:Sigmoid、tanh、ReLU、ReLU6、Leaky ReLU、ELU、Swish、Mish和Softmax,包括它们的定义、图像、优缺点以及在深度学习中的应用和代码实现。
172 0
深度学习笔记(三):神经网络之九种激活函数Sigmoid、tanh、ReLU、ReLU6、Leaky Relu、ELU、Swish、Mish、Softmax详解
|
4月前
|
机器学习/深度学习 算法
|
6月前
|
机器学习/深度学习 人工智能 算法
【机器学习】深度探索:从基础概念到深度学习关键技术的全面解析——梯度下降、激活函数、正则化与批量归一化
【机器学习】深度探索:从基础概念到深度学习关键技术的全面解析——梯度下降、激活函数、正则化与批量归一化
82 3