forked from lyhue1991/eat_tensorflow2_in_30_days
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
460 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,93 @@ | ||
# 5-3,激活函数activation | ||
|
||
|
||
|
||
激活函数在深度学习中扮演着非常重要的角色,它给网络赋予了非线性,从而使得神经网络能够拟合任意复杂的函数。 | ||
|
||
如果没有激活函数,无论多复杂的网络,都等价于单一的线性变换,无法对非线性函数进行拟合。 | ||
|
||
目前,深度学习中最流行的激活函数为 relu, 但也有些新推出的激活函数,例如 swish、GELU 据称效果优于relu激活函数。 | ||
|
||
激活函数的综述介绍可以参考下面两篇文章。 | ||
|
||
[《一文概览深度学习中的激活函数》](https://mp.weixin.qq.com/s?__biz=MzA3MzI4MjgzMw==&mid=2650732724&idx=4&sn=5230b8bb1811cda38ab97afb417d1613&chksm=871b3ccab06cb5dcdf0bdfadcc7ae85d8ae95588bed0b884a55ba50b76d541771104675fbb3e&scene=21#wechat_redirect) | ||
|
||
[《从ReLU到GELU,一文概览神经网络中的激活函数》](https://zhuanlan.zhihu.com/p/98863801) | ||
|
||
|
||
|
||
### 一,常用激活函数 | ||
|
||
|
||
* tf.nn.sigmoid:将实数压缩到0到1之间,一般只在二分类的最后输出层使用。主要缺陷为存在梯度消失问题,计算复杂度高,输出不以0为中心。 | ||
|
||
![](./data/sigmoid.png) | ||
|
||
* tf.nn.softmax:sigmoid的多分类扩展,一般只在多分类问题的最后输出层使用。 | ||
|
||
![](./data/softmax.png) | ||
|
||
* tf.nn.tanh:将实数压缩到-1到1之间,输出期望为0。主要缺陷为存在梯度消失问题,计算复杂度高。 | ||
|
||
![](./data/tanh.png) | ||
|
||
* tf.nn.relu:修正线性单元,最流行的激活函数。一般隐藏层使用。主要缺陷是:输出不以0为中心,输入小于0时存在梯度消失问题(死亡relu)。 | ||
|
||
![](./data/relu.png) | ||
|
||
* tf.nn.leaky_relu:对修正线性单元的改进,解决了死亡relu问题。 | ||
|
||
![](./data/leaky_relu.png) | ||
|
||
* tf.nn.elu:指数线性单元。对relu的改进,能够缓解死亡relu问题。 | ||
|
||
![](./data/elu.png) | ||
|
||
* tf.nn.selu:扩展型指数线性单元。在权重用tf.keras.initializers.lecun_normal初始化前提下能够对神经网络进行自归一化。不可能出现梯度爆炸或者梯度消失问题。需要和Dropout的变种AlphaDropout一起使用。 | ||
|
||
![](./data/selu.png) | ||
|
||
* tf.nn.swish:自门控激活函数。谷歌出品,相关研究指出用swish替代relu将获得轻微效果提升。 | ||
|
||
![](./data/swish.png) | ||
|
||
* gelu:高斯误差线性单元激活函数。在Transformer中表现最好。tf.nn模块尚没有实现该函数。 | ||
|
||
![](./data/gelu.png) | ||
|
||
```python | ||
|
||
``` | ||
|
||
### 二,在模型中使用激活函数 | ||
|
||
|
||
在keras模型中使用激活函数一般有两种方式,一种是作为某些层的activation参数指定,另一种是显式添加layers.Activation激活层。 | ||
|
||
```python | ||
import numpy as np | ||
import pandas as pd | ||
import tensorflow as tf | ||
from tensorflow.keras import layers,models | ||
|
||
tf.keras.backend.clear_session() | ||
|
||
model = models.Sequential() | ||
model.add(layers.Dense(32,input_shape = (None,16),activation = tf.nn.relu)) #通过activation参数指定 | ||
model.add(layers.Dense(10)) | ||
model.add(layers.Activation(tf.nn.softmax)) # 显式添加layers.Activation激活层 | ||
model.summary() | ||
|
||
``` | ||
|
||
```python | ||
|
||
``` | ||
|
||
如果对本书内容理解上有需要进一步和作者交流的地方,欢迎在公众号"Python与算法之美"下留言。作者时间和精力有限,会酌情予以回复。 | ||
|
||
![image.png](./data/Python与算法之美logo.jpg) | ||
|
||
```python | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,218 @@ | ||
# 5-4,模型层layers | ||
|
||
|
||
|
||
深度学习模型一般由各种模型层组合而成。 | ||
|
||
tf.keras.layers内置了非常丰富的各种功能的模型层。例如, | ||
|
||
layers.Dense,layers.Flatten,layers.Input,layers.DenseFeature,layers.Dropout | ||
|
||
layers.Conv2D,layers.MaxPooling2D,layers.Conv1D | ||
|
||
layers.Embedding,layers.GRU,layers.LSTM,layers.Bidirectional等等。 | ||
|
||
如果这些内置模型层不能够满足需求,我们也可以通过编写tf.keras.Lambda匿名模型层或继承tf.keras.layers.Layer基类构建自定义的模型层。 | ||
|
||
其中tf.keras.Lambda匿名模型层只适用于构造没有学习参数的模型层。 | ||
|
||
```python | ||
|
||
``` | ||
|
||
### 一,内置模型层 | ||
|
||
```python | ||
import numpy as np | ||
import pandas as pd | ||
import tensorflow as tf | ||
from tensorflow.keras import layers,models,regularizers | ||
``` | ||
|
||
一些常用的内置模型层简单介绍如下。 | ||
|
||
**基础层** | ||
|
||
* Dense:密集连接层。参数个数 = 输入层特征数× 输出层特征数(weight)+ 输出层特征数(bias) | ||
|
||
* Activation:激活函数层。一般放在Dense层后面,等价于在Dense层中指定activation。 | ||
|
||
* Dropout:随机置零层。训练期间以一定几率将输入置0,一种正则化手段。 | ||
|
||
* BatchNormalization:批标准化层。通过线性变换将输入批次缩放平移到稳定的均值和标准差。可以增强模型对输入不同分布的适应性,加快模型训练速度,有轻微正则化效果。一般在激活函数之前使用。 | ||
|
||
* SpatialDropout2D:空间随机置零层。训练期间以一定几率将整个特征图置0,一种正则化手段,有利于避免特征图之间过高的相关性。 | ||
|
||
* Input:输入层。通常使用Functional API方式构建模型时作为第一层。 | ||
|
||
* DenseFeature:特征列接入层,用于接收一个特征列列表并产生一个密集连接层。 | ||
|
||
* Flatten:压平层,用于将多维张量压成一维。 | ||
|
||
* Reshape:形状重塑层,改变输入张量的形状。 | ||
|
||
* Concatenate:拼接层,将多个张量在某个维度上拼接。 | ||
|
||
* Add:加法层。 | ||
|
||
* Subtract: 减法层。 | ||
|
||
* Maximum:取最大值层。 | ||
|
||
* Minimum:取最小值层。 | ||
|
||
|
||
**卷积网络相关层** | ||
|
||
* Conv1D:普通一维卷积,常用于文本。参数个数 = 输入通道数×卷积核尺寸(如3)×卷积核个数 | ||
|
||
* Conv2D:普通二维卷积,常用于图像。参数个数 = 输入通道数×卷积核尺寸(如3乘3)×卷积核个数 | ||
|
||
* Conv3D:普通三维卷积,常用于视频。参数个数 = 输入通道数×卷积核尺寸(如3乘3乘3)×卷积核个数 | ||
|
||
* SeparableConv2D:二维深度可分离卷积层。不同于普通卷积同时对区域和通道操作,深度可分离卷积先操作区域,再操作通道。即先对每个通道做独立卷即先操作区域,再用1乘1卷积跨通道组合即再操作通道。参数个数 = 输入通道数×卷积核尺寸 + 输入通道数×1×1×输出通道数。深度可分离卷积的参数数量一般远小于普通卷积,效果一般也更好。 | ||
|
||
* DepthwiseConv2D:二维深度卷积层。仅有SeparableConv2D前半部分操作,即只操作区域,不操作通道,一般输出通道数和输入通道数相同,但也可以通过设置depth_multiplier让输出通道为输入通道的若干倍数。输出通道数 = 输入通道数 × depth_multiplier。参数个数 = 输入通道数×卷积核尺寸× depth_multiplier。 | ||
|
||
* Conv2DTranspose:二维卷积转置层,俗称反卷积层。并非卷积的逆操作,但在卷积核相同的情况下,当其输入尺寸是卷积操作输出尺寸的情况下,卷积转置的输出尺寸恰好是卷积操作的输入尺寸。 | ||
|
||
* LocallyConnected2D: 二维局部连接层。类似Conv2D,唯一的差别是没有空间上的权值共享,所以其参数个数远高于二维卷积。 | ||
|
||
* MaxPooling2D: 二维最大池化层。也称作下采样层。池化层无参数,主要作用是降维。 | ||
|
||
* AveragePooling2D: 二维平均池化层。 | ||
|
||
* GlobalMaxPool2D: 全局最大池化层。每个通道仅保留一个值。一般从卷积层过渡到全连接层时使用,是Flatten的替代方案。 | ||
|
||
* GlobalAvgPool2D: 全局平均池化层。每个通道仅保留一个值。 | ||
|
||
|
||
**循环网络相关层** | ||
|
||
* Embedding:嵌入层。一种比Onehot更加有效的对离散特征进行编码的方法。一般用于将输入中的单词映射为稠密向量。嵌入层的参数需要学习。 | ||
|
||
* LSTM:长短记忆循环网络层。最普遍使用的循环网络层。具有携带轨道,遗忘门,更新门,输出门。可以较为有效地缓解梯度消失问题,从而能够适用长期依赖问题。设置return_sequences = True时可以返回各个中间步骤输出,否则只返回最终输出。 | ||
|
||
* GRU:门控循环网络层。LSTM的低配版,不具有携带轨道,参数数量少于LSTM,训练速度更快。 | ||
|
||
* SimpleRNN:简单循环网络层。容易存在梯度消失,不能够适用长期依赖问题。一般较少使用。 | ||
|
||
* ConvLSTM2D:卷积长短记忆循环网络层。结构上类似LSTM,但对输入的转换操作和对状态的转换操作都是卷积运算。 | ||
|
||
* Bidirectional:双向循环网络包装器。可以将LSTM,GRU等层包装成双向循环网络。从而增强特征提取能力。 | ||
|
||
* RNN:RNN基本层。接受一个循环网络单元或一个循环单元列表,通过调用tf.keras.backend.rnn函数在序列上进行迭代从而转换成循环网络层。 | ||
|
||
* LSTMCell:LSTM单元。和LSTM在整个序列上迭代相比,它仅在序列上迭代一步。可以简单理解LSTM即RNN基本层包裹LSTMCell。 | ||
|
||
* GRUCell:GRU单元。和GRU在整个序列上迭代相比,它仅在序列上迭代一步。 | ||
|
||
* SimpleRNNCell:SimpleRNN单元。和SimpleRNN在整个序列上迭代相比,它仅在序列上迭代一步。 | ||
|
||
* AbstractRNNCell:抽象RNN单元。通过对它的子类化用户可以自定义RNN单元,再通过RNN基本层的包裹实现用户自定义循环网络层。 | ||
|
||
* Attention:Dot-product类型注意力机制层。可以用于构建注意力模型。 | ||
|
||
* AdditiveAttention:Additive类型注意力机制层。可以用于构建注意力模型。 | ||
|
||
* TimeDistributed:时间分布包装器。包装后可以将Dense、Conv2D等作用到每一个时间片段上。 | ||
|
||
```python | ||
|
||
``` | ||
|
||
### 二,自定义模型层 | ||
|
||
|
||
如果自定义模型层没有需要被训练的参数,一般推荐使用Lamda层实现。 | ||
|
||
如果自定义模型层有需要被训练的参数,则可以通过对Layer基类子类化实现。 | ||
|
||
Lamda层由于没有需要被训练的参数,只需要定义正向传播逻辑即可,使用比Layer基类子类化更加简单。 | ||
|
||
Lamda层的正向逻辑可以使用Python的lambda函数来表达,也可以用def关键字定义函数来表达。 | ||
|
||
```python | ||
import tensorflow as tf | ||
from tensorflow.keras import layers,models,regularizers | ||
|
||
mypower = layers.Lambda(lambda x:tf.math.pow(x,2)) | ||
mypower(tf.range(5)) | ||
``` | ||
|
||
``` | ||
<tf.Tensor: shape=(5,), dtype=int32, numpy=array([ 0, 1, 4, 9, 16], dtype=int32)> | ||
``` | ||
|
||
|
||
Layer的子类化一般需要重新实现初始化方法,Build方法和Call方法。下面是一个简化的线性层的范例,类似Dense. | ||
|
||
```python | ||
class Linear(layers.Layer): | ||
def __init__(self, units=32, **kwargs): | ||
super(Linear, self).__init__(**kwargs) | ||
self.units = units | ||
|
||
def build(self, input_shape): #build方法一般定义Layer需要被训练的参数。 | ||
self.w = self.add_weight(shape=(input_shape[-1], self.units), | ||
initializer='random_normal', | ||
trainable=True) | ||
self.b = self.add_weight(shape=(self.units,), | ||
initializer='random_normal', | ||
trainable=True) | ||
super(Linear,self).build(input_shape) # 相当于设置self.built = True | ||
|
||
def call(self, inputs): #call方法一般定义正向传播运算逻辑,__call__方法调用了它。 | ||
return tf.matmul(inputs, self.w) + self.b | ||
|
||
def get_config(self): #如果要让自定义的Layer通过Functional API 组合成模型时可以序列化,需要自定义get_config方法。 | ||
config = super(Linear, self).get_config() | ||
config.update({'units': self.units}) | ||
return config | ||
|
||
``` | ||
|
||
```python | ||
linear = Linear(units = 8) | ||
print(linear.built) | ||
linear.build(input_shape = (None,16)) #指定input_shape,显式调用build方法,第0维代表样本数量,用None填充 | ||
print(linear.built) | ||
``` | ||
|
||
```python | ||
linear = Linear(units = 8) | ||
print(linear.built) | ||
linear.build(input_shape = (None,16)) | ||
linear.compute_output_shape(input_shape = (None,16)) | ||
``` | ||
|
||
```python | ||
linear = Linear(units = 16) | ||
print(linear.built) | ||
linear(tf.random.uniform((100,64))) #如果built = False,调用__call__时会先调用build方法, 再调用call方法。 | ||
print(linear.built) | ||
config = linear.get_config() | ||
print(config) | ||
``` | ||
|
||
```python | ||
tf.keras.backend.clear_session() | ||
|
||
model = models.Sequential() | ||
model.add(Linear(units = 16,input_shape = (64,))) #注意该处的input_shape会被模型加工,无需使用None代表样本数量维 | ||
print("model.input_shape: ",model.input_shape) | ||
print("model.output_shape: ",model.output_shape) | ||
model.summary() | ||
``` | ||
|
||
```python | ||
|
||
``` | ||
|
||
如果对本书内容理解上有需要进一步和作者交流的地方,欢迎在公众号"Python与算法之美"下留言。作者时间和精力有限,会酌情予以回复。 | ||
|
||
![image.png](./data/Python与算法之美logo.jpg) | ||
|
||
```python | ||
|
||
``` |
Oops, something went wrong.