# TensorFlow 2.0 深度学习实战 —— 详细介绍损失函数、优化器、激活函数、多层感知机的实现原理

AI 人工智能包含了机器学习与深度学习，在前几篇文章曾经介绍过机器学习的基础知识，包括了监督学习和无监督学习，有兴趣的朋友可以阅读《 Python 机器学习实战 》。

[En]

In the beginning, deep learning is only a branch of machine learning, and it emphasizes learning from continuous layers. Each layer in this hierarchical structure represents the abstraction of different programs. The higher the level, the greater the degree of abstraction. These layers are mainly learned through the model of neural network, and the largest model will have as many as hundreds of layers. The simplest neural network is divided into input layer, middle layer (middle layer often contains multiple hidden layers) and output layer.

1.1 深度学习的起源

AI 人工智能包含了机器学习，而深度学习本属于机器学习的一个分支，它结合了生物神经学的原理，以多层学习模型，将数据层层细化提炼，最后完成输出。如今深度学习已经广泛应用于图像识别、人脸识别、语音识别、搜索引擎、自动驾驶等多个领域。

[En]

Deep learning emphasizes learning from continuous layers, each layer is learned through the model of the neural network, the structure of the neural network is superimposed layer by layer, and finally through the output layer to complete the output. The principle of deep learning originates from biological neurology. Due to external stimulation, neuronal cells send signals and receive them from other neurons. Multi-layer superposition forms a tree-like structure, and when it reaches a certain threshold, it changes into an active state. The structure of deep learning is similar, the original data input will go through multiple hidden layers, each hidden layer will have a number of neurons, and finally through the output layer to complete the output, the deeper the layer, the higher the purity.

1.2 深度学习的工作原理

[En]

When the value of the node exceeds a certain value, it triggers the activation function Φ () to output the data to the next layer, over and over again to the output layer. The significance of deep learning is to repeatedly learn through thousands of supervisory data, so as to adjust the weight and bias to make it match with the supervisory data. After the completion of the study, even if there is a new data input that has not been tested, the system can be judged according to the original basis and constantly improved.

[En]

Different from the traditional data learning, the difference of deep learning lies in: 1. Deep learning emphasizes the depth of the model structure, which has at least three layers, namely, the input layer, the hidden layer and the output layer, and usually has 5, 6, or even more than 10 hidden layers. Depth defines the importance of feature learning. Through layer-by-layer feature transformation, the feature representation of the sample in the original space is transformed into a new feature space, which makes the feature purity higher and higher and makes it easier to classify or predict.

1.3 Tensorflow 2.0 简介

2.1 mean_squared_error 均方误差

1 def mean_squared_error(y_true, y_pred):
2     return K.mean(K.square(y_pred - y_true), axis=-1)


[En]

The mean square error is the most commonly used loss function, which is generally used in regression calculation.

2.2 mean_absolute_error 平均绝对误差

1 def mean_absolute_error(y_true, y_pred):
2     return K.mean(K.abs(y_pred - y_true), axis=-1)


[En]

The average absolute error is similar to the mean square error, which is generally used in regression calculation, except that the mean square error formula uses the average value of the sum of squares, while the average absolute error is the average value of the absolute value.

2.3 mean_absolute_percentage_error 平均绝对值百分比误差

1 def mean_absolute_percentage_error(y_true, y_pred):
2     diff = K.abs((y_true - y_pred) / K.clip(K.abs(y_true),
3                                             K.epsilon(),
4                                             None))
5     return 100. * K.mean(diff, axis=-1)


[En]

The percentage error of the average absolute value is calculated by the ratio of the difference between the real value and the predicted value, which is usually multiplied by the percentage and is generally used in regression calculation. This is the most commonly used index of sales forecast, and it has a very important evaluation significance in the actual online and offline sales forecast.

2.4 mean_squared_logarithmic_error 对数方差

1 def mean_squared_logarithmic_error(y_true, y_pred):
2     first_log = K.log(K.clip(y_pred, K.epsilon(), None) + 1.)
3     second_log = K.log(K.clip(y_true, K.epsilon(), None) + 1.)
4     return K.mean(K.square(first_log - second_log), axis=-1)


[En]

Logarithmic variance calculates a predictive risk measure corresponding to square logarithmic (quadratic) error or loss, which is generally used in regression calculation. This indicator is most suitable for use when the target has an exponential growth trend, such as population, average sales of goods across the year, and so on.

2.5 categorical_crossentropy 多类交叉熵

1 def categorical_crossentropy(y_true, y_pred):
2     return K.categorical_crossentropy(y_true, y_pred)


2.6 sparse_categorical_crossentropy 稀疏交叉熵

1 def sparse_categorical_crossentropy(y_true, y_pred, from_logits=False, axis=-1):
2     y_pred = ops.convert_to_tensor_v2_with_dispatch(y_pred)
3     y_true = math_ops.cast(y_true, y_pred.dtype)
4     return backend.sparse_categorical_crossentropy(
5         y_true, y_pred, from_logits=from_logits, axis=axis)


SCCE 稀疏多分类交叉熵与 CCE 多分类交叉熵的实现方式相类似，主要用于分类算法，只是目标值输出值格式略有不同，CEE以向量作为输出，而SCEE 则直接转化为索引值进行输出。例如同一组测试数据如果有10个分类CEE 的输出方式是 [-1，10]，类似于 [ [0,0,0,0,1,0,0,0,0,0],[0,1,0,0,0,0,0,0,0,0] , […] … ] 数组)。而SCCE的输出方式将会是[-1,1]，即类似 [ [1]，[3]，[4]，[9] ….. ] 类型的数组。

2.7 binary_crossentropy 二进制交叉熵

def binary_crossentropy(y_true, y_pred, from_logits=False, label_smoothing=0):
y_pred = ops.convert_to_tensor_v2_with_dispatch(y_pred)
y_true = math_ops.cast(y_true, y_pred.dtype)
label_smoothing = ops.convert_to_tensor_v2_with_dispatch(
label_smoothing, dtype=backend.floatx())

def _smooth_labels():
return y_true * (1.0 - label_smoothing) + 0.5 * label_smoothing

y_true = smart_cond.smart_cond(label_smoothing, _smooth_labels,
lambda: y_true)
return backend.mean(
backend.binary_crossentropy(
y_true, y_pred, from_logits=from_logits), axis=-1)


CCE 和 SCCE 主要用于多分类, 而 BCE 更适用于二分类，由于CCE 需要输出 n_class 个通道而 BCE 只需要输出一条通道，所以同一组测试数据往往 BCE 的运行效率会更高。需要注意的是，如果使用BCE损失函数，则节点的输出应介于(0-1)之间，这意味着你必须在最终输出中使用sigmoid激活函数。

2.8 hinge 合页

1 def hinge(y_true, y_pred):
2     return K.mean(K.maximum(1. - y_true * y_pred, 0.), axis=-1)


2.9 squared_hinge 平方合页

1 def squared_hinge(y_true, y_pred):
2       y_pred = ops.convert_to_tensor_v2_with_dispatch(y_pred)
3       y_true = math_ops.cast(y_true, y_pred.dtype)
4       y_true = _maybe_convert_labels(y_true)
5       return backend.mean(
6           math_ops.square(math_ops.maximum(1. - y_true * y_pred, 0.)), axis=-1)


squared_hinge 平方合页损失函数与 hinge 类似，只有取最大值时加上平方值，与常规 hinge 合页损失函数相比，平方合页损失函数对离群值的惩罚更严厉，一般多于二分类计算。

2.10 categorical_hinge 多类合页

1 def categorical_hinge(y_true, y_pred):
2       y_pred = ops.convert_to_tensor(y_pred)
3       y_true = math_ops.cast(y_true, y_pred.dtype)
4       pos = math_ops.reduce_sum(y_true * y_pred, axis=-1)
5       neg = math_ops.reduce_max((1. - y_true) * y_pred, axis=-1)
6       return math_ops.maximum(0., neg - pos + 1.)


categorical_hinge 更多用于多分类形式

2.11 log_cosh

1 def log_cosh(y_true, y_pred):
2       y_pred = ops.convert_to_tensor_v2_with_dispatch(y_pred)
3       y_true = math_ops.cast(y_true, y_pred.dtype)
4
5       def _logcosh(x):
6           return x + math_ops.softplus(-2. * x) - math_ops.cast(
7           math_ops.log(2.), x.dtype)
8
9       return backend.mean(_logcosh(y_pred - y_true), axis=-1)


log_cosh 适用于回归，且比L2更平滑。它的计算方式是预测误差的双曲余弦的对数，对于较小的 x , log ( cosh(x))近似等于 x2 / 2 。对于大的 x，近似于 |x| – log2 。这表示 ‘logcosh’ 与均方误差算法大致相同，但是不会受到偶发性错误预测的强烈影响。

2.12 huber

1 def huber(y_true, y_pred, delta=1.0):
2       y_pred = math_ops.cast(y_pred, dtype=backend.floatx())
3       y_true = math_ops.cast(y_true, dtype=backend.floatx())
4       delta = math_ops.cast(delta, dtype=backend.floatx())
5       error = math_ops.subtract(y_pred, y_true)
6       abs_error = math_ops.abs(error)
7       half = ops.convert_to_tensor_v2_with_dispatch(0.5, dtype=abs_error.dtype)
8       return backend.mean( array_ops.where_v2(abs_error Huber 适用于回归, 它是平滑的平均绝对误差，优点是能增强平方误差损失函数对离群点的鲁棒性。当预测偏差小于 δ（delta）时，它采用平方误差,当预测偏差大于 δ 时，采用的绝对值误差，误差降到多小才变为平方误差由超参数δ。相比于均方误差，Huber 降低了对离群点的惩罚程度，所以 Huber 是一种常用的鲁棒的回归损失函数。也就是说当Huber 损失在 [0-δ，0+δ] 之间时，等价为MSE，而在 [-∞，δ] 和 [δ，+∞] 时相当于 MAE。这里超参数 δ（delta）的选择非常重要，因为这决定了对异常点的定义。当残差大于 δ（delta），应当采用 L1（对较大的异常值不那么敏感）来最小化，而残差小于超参数，则用 L2 来最小化。2.13 poisson 泊松损失函数1 def poisson(y_true, y_pred):
2       y_pred = ops.convert_to_tensor_v2_with_dispatch(y_pred)
3       y_true = math_ops.cast(y_true, y_pred.dtype)
4       return backend.mean(
5           y_pred - y_true * math_ops.log(y_pred + backend.epsilon()), axis=-1)poisson 用于回归算法，一般用于计算事件发性的概率2.14 cosine_similarity 余旋相似度1 def cosine_similarity(y_true, y_pred, axis=-1):
2       y_true = nn.l2_normalize(y_true, axis=axis)
3       y_pred = nn.l2_normalize(y_pred, axis=axis)
4       return -math_ops.reduce_sum(y_true * y_pred, axis=axis)预测值与真实标签的余弦距离平均值的相反数，它是一个介于-1和1之间的数字。当它是负数时在-1和0之间，0表示正交，越接近-1 表示相似性越大，值越接近1表示不同性越大，这使得它在设置中可用作损失函数。如果' y_true '或' y_pred '是一个零向量，余弦无论预测的接近程度如何，则相似度都为 0，而与预测值和目标值之间的接近程度无关。2.15 kl_divergence 散度1 def kl_divergence(y_true, y_pred):
2       y_pred = ops.convert_to_tensor_v2_with_dispatch(y_pred)
3       y_true = math_ops.cast(y_true, y_pred.dtype)
4       y_true = backend.clip(y_true, backend.epsilon(), 1)
5       y_pred = backend.clip(y_pred, backend.epsilon(), 1)
6       return math_ops.reduce_sum(y_true * math_ops.log(y_true / y_pred), axis=-1)用于分类计算，通过衡量预测值概率分布到真值概率分布的相似度差异，在运动捕捉里面可以衡量未添加标签的运动与已添加标签的运动，进而进行运动的分类。由于输入数据的特征很多，所以让计算损失函数变成很复杂，因此人们想出通过巧妙地利用梯度来计算损失函数最小值，而最常用的计算梯度方法就有梯度下降法和反向传播法。通过 model.compile (optimizer , loss , metrics) 中的 optimizer 优化器可绑定 SGD、AdaGrad、Adam、RMSProp 等多种算法。1 @keras_export('keras.Model', 'keras.models.Model')
2 class Model(base_layer.Layer, version_utils.ModelVersionSelector):
3       def compile(self, optimizer='rmsprop', loss=None,
4               metrics=None,loss_weights=None,
5               weighted_metrics=None, run_eagerly=None,
6               steps_per_execution=None, **kwargs):optimizer：绑定优化器，用于绑定训练时所使用的优化器，一般有 SGD、AdaGrad、Adam、RMSProp 等。loss：绑定损失函数，有上述所介绍过的 15 种 损失函数可选择。metrics：绑定用于评估当前训练模型的性能参数，例如 ['acc' , 'losses'] 正确率等，损失函数。评价函数和损失函数相似，只不过评价函数的结果只用于监测数据而不会用于训练过程中。loss_weights：参数类型 列表或字典，例如（0.7，0.3）。模型优化过程中，将损失函数最小化，是指对两个损失值求和之后最小化，那么这个loss_weights就是给这两个输出的损失值加权用的。3.1 SGD 随机梯度下降法1 class SGD(optimizer_v2.OptimizerV2):
2   def __init__(self,
3                learning_rate=0.01,
4                momentum=0.0,
5                nesterov=False,
6                name="SGD",
7                **kwargs):参数说明learning_rate: float 类型，默认值为0.01，表示学习率momentum: float 类型，默认值为0，表示动量，大于等于0时可以抑制振荡nesterov: bool 类型，默认值为 False，是否启动 momentum 参数name: str类型，默认为 'SGD'，算法名称梯度下降法的实现原理在 《 Python 机器学习实战 —— 监督学习（上）》已经详细介绍过，就是利用在微积分原理，对多元函数的输入参数求偏导数，把求得的各个参数的导数以向量的形式写出来就是梯度。梯度下降是迭代法的一种，通过不断小幅修改输入参数，求得切线斜率接近无限于0时的数据点（即鞍点）。通过多次递归方式，切线斜率就是无限接近于0，此就是数据点的最小值 。用此方法求得损失函数最小值，当损失函数到达最小值时，权重 w 和偏置量 h 则最符合数据集的特征。在无约束问题时，梯度下降是最常采用的方法之一。在使用 SGD 时，最重要的是选择合适的学习率 learning rate 比较困难 ，学习率太低会收敛缓慢，学习率过高会使收敛时的波动过大。而梯度下降法也存在一定问题，因为所有参数都是用同样的 learning rate，在复杂的数据集情况下，损失函数就犹如一个小山丘，有多处小坑其梯度（偏导数）都为0，此时会有多个局部最小值（local minimum）和一个是全局最小值（global minimum。到达局部最小值时梯度为0，如果 learning rate 太小，无论移向哪一方，其梯度都会增加，运算就会停止在坑中。因此，局部最小值就会被误认为全局最小值。遇到此情况可尝试调节学习率 learning rate 或添加动量 momentum 加速收敛，但并不解决所有问题。若想要加速收敛速度，可以尝试添加动量 momentum ，使用 momentum 的算法思想是：参数更新时在一定程度上保留之前更新的方向，同时又利用当前batch的梯度微调最终的更新方向，简言之就是通过积累之前的动量来加速当前的梯度。在梯度方向改变时，momentum 能够降低参数更新速度，从而减少震荡；在梯度方向相同时，momentum可以加速参数更新， 从而增加冲过小坡的可能性。总而言之，momentum 能够加速SGD收敛，抑制震荡。3.2 AdaGrad 算法1 class Adagrad(optimizer_v2.OptimizerV2):
2   def __init__(self,
3                learning_rate=0.001,
4                initial_accumulator_value=0.1,
5                epsilon=1e-7,
7                **kwargs):参数说明learning_rate: float 类型，默认值为0.001，表示学习率initial_accumulator_value：float 类型，默认值为 0.1 ，累加器的初始值epsilon：float 类型，默认值为 1e-7，为的是避免计算时出现分母为 0 的特殊情况。name: str类型，默认为 'Adagrad'，算法名称AdaGrad 也称自适应梯度法，它是 SGD 的一个优化算法，由 John Duchi 等人在 2011 年于 Adaptive Subgradient Methods for Online Learning and Stochastic Optimization 提出。相比起 SGD 它可以不断地自动调整学习率，当初期梯度矩阵平方的累积较小时，学习率相对比较快，到后期梯度矩阵平方的累积较大时，学习率会相对降低。AdaGrad 的原理大概就是累计每一次梯度的矩阵平方，接着让学习率除以它的开方。这个的作用是为了不断地改变学习率。在前期，梯度累计平方和比较小，也就是 r 相对较小，这样就能够放大梯度对权重的影响力; 随着迭代次数增多，梯度累计矩阵平方和也越来越大，即 r 也相对较大，梯度对权重的影响力变得越来越小。假设训练集中包含有 n 个样本 xi ，其对应的输出值为 yi，权重为 ωi ，学习率为 ϵ，则针对损失函数 L 的梯度 g 的计算公式如下：r 为梯度矩阵平方的累积变量，初始值由 initial_accumulator_value 参数确定，默认为 0.1计算权重的更新值 Δ ω，其中 δ 为小常数即参数 epsilon，默认值为 1e-7，为的是避免出现分母为 0 的特殊情况。而 ϵ 为学习率，默认值为0.001。由于分母越大值越小，这反映在图上就是在初始阶段，累积梯度矩阵平方值 r 较小，因此刚开发训练时变化会较快。但随着 r 值累计越来越大，变化会越来慢。但有个坏处就是有可能导致累计梯度矩阵平方 r 增速过大，权重的变化过早减小，学习率过早降低的情况。最后更新权重值3.3 RMSProp 算法1 class RMSprop(optimizer_v2.OptimizerV2):
2       def __init__(self,
3                learning_rate=0.001,
4                rho=0.9,
5                momentum=0.0,
6                epsilon=1e-7,
7                centered=False,
8                name="RMSprop",
9                **kwargs):learning_rate: float 类型，默认值为0.001，表示学习率rho : float 类型 ，默认值0.9，衰减速率， 即是等式中的 ρ。epsilon : float 类型，默认值为 1e-7，为的是避免计算时出现分母为 0 的特殊情况。momentum ：float类型，默认值 0.0，即方程中的动量系数 α 。centered：bool类型，默认值为False。如果为True，则通过梯度的估计方差,对梯度进行归一化；如果False，则由未centered的第二个moment归一化。将此设置为True有助于模型训练，但会消耗额外计算和内存资源。默认为False。name：str类型，默认为 'RMSprop'，算法名称RMSProp 算法是 AdaGrad 算法的改量版，上面说过AdaGrad最大的问题在于当累计梯度矩阵平方 r 早期增速过大时，会导致权重的变化过早减小，学习率过早降低的情况。为解决这个问题，RMSProp 加入了衰减速率参数 rho ，使梯度矩阵平方的累积变量 r 值增加的速度更加平稳，从而避免权重的变化过早减小，学习率过早降低的情况。假设训练集中包含有 n 个样本 xi ，其对应的输出值为 yi，权重为 ωi ，学习率为 ϵ，则针对损失函数 L 的梯度 g 的计算公式如下：计算权重的更新值 Δ ω，其中 δ 为小常数即参数 epsilon，默认值为 1e-7，为的是避免出现分母为 0 的特殊情况。而 ϵ 为学习率，默认值为0.001。与 AdaGrad相似，在累积梯度矩阵平方值 r 较小，因此刚开发训练时变化会较快。但随着 r 值累计越来越大，变化会越来慢。然而不同在于，由于 r 的取值加入了衰减速率 ρ 的控制，所以其减速现象明显得以抑制，避免了权重的变化过早减小，学习率过早降低的情况。若要调整权重更新值 Δ ω 的占比，也可设置动量系数 α。由于动量系数 α （即参数 momentum ）默认值为 0，所以默认情况下权重更新值 Δ ω 与 AdaGrad 类似。最后更新权重值3.4 Adam 算法1 class Adam(optimizer_v2.OptimizerV2):
2       def __init__(self,
3                learning_rate=0.001,
4                beta_1=0.9,
5                beta_2=0.999,
6                epsilon=1e-7,
9                **kwargs):learning_rate: float 类型，默认值为0.001，表示学习率beta_1：float 类型，默认值为 0.9 ，指定一阶矩估计的指数衰减率 ρ1beta_2：float 类型，默认值为 0.999 , 指定二阶矩估计的指数衰减率 ρ2epsilon：float 类型，默认值为 1e-7，为的是避免计算时出现分母为 0 的特殊情况。amsgrad: bool 类型，默认值为 False，是否应用该算法的 AMSGrad 变体name: str类型，默认为 'Adam'，算法名称Adam 相当于把 AdaGrad 算法融合了动量 momentum 的概念，整合出来的新算法，由 Diederik Kingma 等人 2014 年在 A Method for Stochastic Optimization .arXiv:1412.6980 提出。实现偏置校正是 Adam 的特征，这使 Adam 算法的运算效率更高。它分别指定了一阶矩估计的指数衰减率 ρ1 和二阶矩估计的指数衰减率 ρ2，随着训练集的循环会不断更新一阶矩偏差 s^ 和 二阶矩偏差 r^，从而计算出计算权重的更新值 Δ ω 。假设训练集中包含有 n 个样本 xi ，其对应的输出值为 yi，权重为 ωi ，学习率为 ϵ，则针对损失函数 L 的梯度 g 的计算公式如下：Adam 把 RMSProp 中的梯度矩阵平方的累积变量的算法转换成计算一阶矩偏差 s^ 和 二阶矩偏差 r^，最后用它们的比值 s ^ / sqrt( r ^ ) 得出结果 。这样做梯度经过偏置校正，每一次迭代学习率都有一个固定范围，因此学习流程更加平稳。s 与 r 的初始值均为 0 ，指数衰减率 ρ1、ρ2 的初始值默认为 0.9 和 0.999 。每次循环，系统都会根据累积变量 s、r 修正偏差值 s^、 r^。更新一阶矩累积变量更新二阶矩估计累积变量跟随训练，循环修正一阶矩的偏差值 s^跟随训练，循环修正二阶矩的偏差值 r^计算权重的更新值 Δ ω，其中 δ 为小常数即参数 epsilon，默认值为 1e-7，为的是避免出现分母为 0 的特殊情况。而 ϵ 为学习率，默认值为0.001。最后更新权重值在第一节曾经介绍到，每个神经元要传播到下一层时，都需要通过激活函数，我们可以把这看成是输入信号的加权和转化为输出信号的的一个过程，它为神经元提供了模拟非线性数据集所必需的非线性特征。正因为大部分神经元之间的数据都存在非线性关系，所以激活函数都非线性的，要不然隐藏层就会失去其意义。最常见的激活函数有阶跃激活函数、Sigmoid 激活函数、ReLU激活函数、Tanh 激活函数、Softmax激活函数等，下面将一一介绍。4.1 阶跃激活函数这是最简单的一种激活方式，它以0为临界时，当输入值 input 小于0 时返回 0，大于 0 时返回 1。由于它的值呈阶梯式变化，因为被称为阶跃激活函数（也称阈值激活函数）。但是这简单方法的缺点也是非常明显的。 首先它是不连续且不光滑的，这就导致在反向传播时这一层很难学习。 其次阶跃函数有着 "非黑即白" 的特性，所以一般只适用于简单的二分类非线性激活。由于它在 x=0 时不具有连贯性，因此不适合用于梯度下降的数据训练中。 1 def func(x):
2     return np.array(x>0)
3
4 x=np.linspace(-5,5,50)
5 y=func(x)
6 plt.xlabel('input')
7 plt.ylabel('output')
8 plt.title('Step Function Activation Func')
9 plt.plot(x,y)
10 plt.show()4.2 Sigmoid 激活函数sigmoid 的输出函数由 f(x)=1/(1+exp(-x)) 确定，在tensorflow 中可以通过 tf.sigmoid 调用，由于形状很像 S，因此命名为 sigmoid 。相比起阶跃激活函数，sigmoid 在0~1之间值过渡显得更平滑，但在两个边缘值梯度都无穷接近于 0，这非常容易造成 "梯度消失"，因此为优化训练增加了难度。此外 sigmoid 函数输出值在 0 到 1 之间，其均值为0.5，不符合神经网络内数值期望为 0 的设想。一般情况 sigmoid 用于隐藏层的激活或输出层的回归。 1 def func(x):
2     return 1/(1+np.exp(-x))
3
4 x=np.linspace(-5,5,50)
5 y=func(x)
6 plt.xlabel('input')
7 plt.ylabel('output')
8 plt.title('Sigmoid Activation Func')
9 plt.plot(x,y)
10 plt.show()4.3 Tanh 激活函数tanh 双曲正切激活函数公式由 f(x)=(1-exp(-2x)) / (1+exp(-2x)) 确定，在tensorflow 中可以通过 tf.tanh 调用。它相当于 sigmoid 的改良版，其输入值从 -1 到 1 之间均值为0，这正解决了 sigmoid 均值为0.5 的缺陷，并且它的切线比 sigmoid 更陡峭。然而 tanh 的两个边缘值梯度也是无穷接近于 0， 并不能解决 sigmoid "梯度消失" 的问题。一般情况 tanh 会用于隐藏层的激活或输出层的回归。 1 def func(x):
2     return (1-np.exp(-2*x))/(1+np.exp(-2*x))
3
4 x=np.linspace(-5,5,50)
5 y=func(x)
6 plt.xlabel('input')
7 plt.ylabel('output')
8 plt.title('Tanh Activation Func')
9 plt.plot(x,y)
10 plt.show()4.4 ReLU激活函数ReLU 激活函数是分段线性函数，它能在多层激活时捕获非线性特征，在tensorflow 中可以通过 tf.nn.relu 调用。在输入为正数的时候，其输出值为无穷大，弥补了sigmoid函数以及tanh函数的梯度消失问题。而且 ReLU 函数只有线性关系，因此不管是前向传播还是反向传播，计算速度都比 sigmod 和 tanh 要快。ReLU 最大的问题在于当输入值小于0 时，梯度一直为0，因此产生梯度消失问题。尽管如此，ReLU 也是最常用的激活函数之一，常用于隐藏层的激活和输出层的回归。 1 def func(x):
2     return np.maximum(0,x)
3
4 x=np.linspace(-5,5,50)
5 y=func(x)
6 plt.xlabel('input')
7 plt.ylabel('output')
8 plt.title('ReLU Activation Func')
9 plt.plot(x,y)
10 plt.show()4.5 Leaky ReLU 激活函数Leaky ReLU 激活函数是 ReLU 的改良版，在 tensorflow 中可以通过 tf.nn.leaky_relu 调用。它是为了改善 ReLU 输入值小于0 时，梯度一直为 0 的问题而设计的。在 Leaky ReLU 中当输入值小于0时，输出值将为 ax，因此不会造成神经元失效。当神经元输出值有可能出现小于0的情况，就会使用 Leaky ReLU 输出，常用于隐藏层的激活和输出层的回归。 1 def func(x,a=0.01):
2     return np.maximum(a*x,x)
3
4 x=np.linspace(-5,5,50)
5 y=func(x)
6 plt.xlabel('input')
7 plt.ylabel('output')
8 plt.title('Leaky ReLU Activation Func')
9 plt.plot(x,y)
10 plt.show()4.6 Softmax 函数Softmax 的计算公式如下，在tensorflow中可直接通过 tf.nn.softmax 调用，一般用作输出层的分类激活函数，它表示每个分类的输出概率，所有输出概率合共为 1。 1 def func(x):
2     return np.exp(x)/np.sum(np.exp(x))
3
4 x=np.linspace(-5,5,50)
5 y=func(x)
6 plt.xlabel('input')
7 plt.ylabel('output')
8 plt.title('Softmax Activation Func')
9 plt.plot(x,y)
10 plt.show()到此归纳总结一下阶跃激活函数、Sigmoid 激活函数、Tanh 激活函数、ReLU激活函数、Leaky ReLU 激活函数、Softmax 函数和恒等输出的应用场景。一般阶跃激活函数只用于简单的二分类输出，Sigmoid 和 Tanh 激活函数用于隐藏层的输出或输出层的回归，但两者皆会存在输出梯度消失的风险。ReLU 和 Leaky ReLU 激活函数可用于隐藏层的输出或输出层的回归，它能消除梯度消失的风险，而且性能高。当输出值为正值时可使用 ReLU，当输出值存在负值的可能时使用 Leaky ReLU 。而在输出层的分类计算可使用 Softmax 函数，简单分类也可用 Sigmoid 逻辑回归，在输出层的回归计算可使用恒等函数，即对输入信息不作任何修改直接输出。多层感知机 MLP，它以层为组织搭建：至少包含一个一个输入层，一个或多个隐藏层，一个输出层。数据通过输入层输入，通过多个隐藏层传递，通过输出层输出，过程中层与层之间没有信息反馈，因此被称为前馈神经网络 FNN。当数据传到输出层时，系统会把输出数据与正确数据进行对比，把梯度反馈同时更新权重，如此反复循环，最后把误差减到最小。5.1 三层分类感知机下面从最简单的分类感知机开始介绍，用 mnist 数据进行测试，当中只包含一个输入层，一个隐藏层，一个输出层 。为了方便讲解，第一个例子先用 tensorflow 1.x 版本进行讲解，先用占位符设定输入数据X，y。由于 mnist 的图像是28*28 像素，0~9 的数字，这个感知机的目的就是把 784 特征的数据分成 0~9 的10类，所以隐藏层参数形状应为 [784,10]，h 为 [10] 。通过公式 y=w*x+h 进行计算，最后 softmax 分类函数输出。由于是分类计算，所以损失函数选用了常用的交叉熵损失函数，算法使用 Adam 算法，把学习率设置为 0.3 。输入测试数据，分多批每批 500 个进行训练，每隔200个输出一次正确率，如此循环训练10次。可见10次后，测试数据的准确率已经达到 91%，最后查看测试数据的准确率，也将近有 90%。这就是最简单的三层感知机 ，可见其结构原理比较简单，然后使用 tensorflow 1.x 的方法略显繁琐。下面介绍一下多层感知机，使用 tensorflow 2.x 进行编写，可读性会更高。def test():
tf.disable_eager_execution()
X=tf.placeholder(tf.float32,[None,784])
y=tf.placeholder(tf.float32,[None,10])
# 隐藏层参数w0,h0
w0=tf.Variable(tf.random_normal([784,10],stddev=0.1))
h0=tf.Variable(tf.random_normal([10],stddev=0.1))
# 计算 logits
logits=tf.matmul(X,w0)+h0
# 计算输出值 y_
y_=tf.nn.softmax(logits)
# 交叉熵损失函数
cross_entropy=tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=y)
cross_entropy=tf.reduce_mean(cross_entropy)
# Adam 算法,学习率为 0.3
# 计算准确率
correct=tf.equal(tf.argmax(y_,1),tf.argmax(y,1))
accuray=tf.reduce_mean(tf.cast(correct,tf.float32))
# 计入测试数据
with tf.Session() as session:
session.run(tf.global_variables_initializer())
#训练10次
for epoch in range(10):
#分批处理训练数据，每批500个数据
start=0
n=int(len(X_train)/500)
print('---------------epoch'+str(epoch)+'---------------')
for index in range(n):
end = start + 500
batch_X,batch_y=X_train[start:end],y_train[start:end]
batch_X=batch_X.reshape(500,784)
batch_y=keras.utils.to_categorical(batch_y)
#分批训练
train_,cross,acc=session.run([train_step,cross_entropy,accuray]
,feed_dict={X:batch_X,y:batch_y})
if index%200==0:
# 每隔200个输出准确率
print('  accuray:'+str(acc*100))
start+=500
#处理测试数据输出准确率
X_test=X_test.reshape(-1,784)
y_test=keras.utils.to_categorical(y_test)
accuray=session.run(accuray,feed_dict={X:X_test,y:y_test})
print('--------------test data-------------\n  accuray:'+str(accuray*100))            运行结果5.2 多层感知机前面介绍过 tensorflow 2.0 已经融入 keras 库，因此可以直接使用层 layer 的概念，先建立一个 model，然后通过 model.add(layer) 方法，加入每层的配置。完成层设置后，调用 model.compile(optimizer, loss, metrics) 可绑定损失函数和计算方法。最后用 model.fit() 进行训练，分批的数据量和重复训练次数都可以直接通过参数设置。1 @keras_export('keras.Model', 'keras.models.Model')
2 class Model(base_layer.Layer, version_utils.ModelVersionSelector):
3   　　def fit(self, x=None,y=None,batch_size=None, epochs=1,
4           verbose='auto',callbacks=None, validation_split=0.,
5           validation_data=None, shuffle=True, class_weight=None,
6           sample_weight=None,initial_epoch=0, steps_per_epoch=None,
7           validation_steps=None,validation_batch_size=None,
8           validation_freq=1,max_queue_size=10,
9           workers=1, use_multiprocessing=False):参数说明x：输入数据。如果模型只有一个输入，那么x的类型是 array，如果模型有多个输入，那么x的类型应当为list，list的元素是对应于各个输入的 arrayy：输入数据的验证标签，arraybatch_size：整数，指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降，使目标函数优化一步。epochs：整数，训练终止时的epoch值，训练将在达到该epoch值时停止，当没有设置initial_epoch时，它就是训练的总轮数，否则训练的总轮数为epochs - inital_epochverbose：日志显示，0为不在标准输出流输出日志信息，1为输出进度条记录，2为每个epoch输出一行记录callbacks：list，其中的元素是keras.callbacks.Callback的对象。这个list中的回调函数将会在训练过程中的适当时机被调用，参考回调函数validation_split：0~1之间的浮点数，用来指定训练集的一定比例数据作为验证集。验证集将不参与训练，并在每个epoch结束后测试的模型的指标，如损失函数、精确度等。注意，validation_split的划分在shuffle之前，因此如果你的数据本身是有序的，需要先手工打乱再指定validation_split，否则可能会出现验证集样本不均匀。validation_data：形式为（X，y）的 tuple，是指定的验证数量集。设定此参数后将覆盖 validation_spilt。shuffle：布尔值或字符串，一般为布尔值，表示是否在训练过程中随机打乱输入样本的顺序。若为字符串"batch"，则是用来处理HDF5数据的特殊情况，它将在batch内部将数据打乱。class_weight：字典，将不同的类别映射为不同的权值，该参数用来在训练过程中调整损失函数（只能用于训练）sample_weight：权值的numpy array，用于在训练时调整损失函数（仅用于训练）。可以传递一个1D的与样本等长的向量用于对样本进行1对1的加权，或者在面对时序数据时，传递一个的形式为（samples，sequence_length）的矩阵来为每个时间步上的样本赋不同的权。这种情况下请确定在编译模型时添加了sample_weight_mode='temporal'。initial_epoch: 从该参数指定的epoch开始训练，在继续之前的训练时有用。validation_steps：验证数据的批次数validation_batch_size：每批次验证数据的数量相比起 tensorflow 1.x 可读性更强，而且能支持多平台运行，它去除了占位符的概念，开发时无受到 session 的约束，而是直接通过函数来调用。还是以 mnist 为例子，通过 5 层的训练，神经元数目从 784 逐层下降 200、100、60、30、10，最后通过 softmax 函数输出。通过 5 层的神经元，正确率可提升到将近 93%。 1 def getModel():
2     # 神经元数目从 784 逐层下降 200、100、60、30、10，最后通过 softmax 函数输出
3     model=keras.models.Sequential()
10     return model
11
12 def test():
13     # 获取数据集
15     X_train,y_train=tf.convert_to_tensor(X_train,tf.float32) , tf.convert_to_tensor(y_train,tf.float32)
16     # 建立 model
17     model=getModel()
18     # 使用 SGD 梯度下降法，学习率为 0.003
19     # 使用交叉熵算法
20     model.compile(optimizer=optimizers.SGD(0.003),
21                   loss=losses.sparse_categorical_crossentropy,
22                   metrics=['accuracy'])
23     # 绑定 tensorboard 对日志数据进行监测
24     callback=keras.callbacks.TensorBoard(log_dir='logs', histogram_freq=1, embeddings_freq=1)
25     # 重复训练30次，每 500 个作为一批
26     model.fit(X_train,y_train,epochs=30,batch_size=500，callbacks=callback)
27     # 输出测试数据准确率
28     X_test, y_test = tf.convert_to_tensor(X_test, tf.float32), tf.convert_to_tensor(y_test, tf.float32)
29     print('\n-----test data------')
30     model.fit(X_test,y_test)运行结果通过 keras.callbacks.TensorBoard(log_dir='日志路径'）直接绑定日志目录，训练时绑定回调函数即可将检测数据写入日志 model.fit (x=测试数据, y=正确输出结果,batch_size=分批运行时每批数量, epochs=训练重复次数, callbacks=绑定回调），最后通过命令 " tensorboard --logdir=日志路径 " 即可在浏览器 http://localhost:6006/ 上查看日志。5.3 多层感知机回归测试以波士顿房价作为测试数据集，尝试使用多层感知机对未来房价进行预测，看一下测试结果如何。首先建好 Model，测试数据集有13个特征，把神经元扩展到 20、50 个。由于是回归计算，所以输出层使用 sigmoid 所以输出值只需要一列。由于 boston 数据集中有多列数据，大小不一，所以在输入可以前先利用 MinMaxScaler 进行归一化处理。注意测试数据集中 y_train 和 y_test只有一列，所以在归一化处理前先要先利用 y_train[:,np.newaxis] 或其他方式进行行列调换，不然系统将报错。然后使用 Adam 算法，huber 损失函数进行10次训练。完成训练后，对比测试数据与原数据的值。可见经过15次训练后，损失率已到达0.003 以下，测试值与真实值已经相当接近。 1 def getModel():
2     # 神经元从13到20、50，输出层使用sigmoid激活函数
3     model=keras.models.Sequential()
8     return model
9
10 def test():
11     # Boston房价测试数据集
12     (X_train,y_train),(X_test,y_test)=keras.datasets\
14     # 数据多列大于1，所以先把数据进行归一化处理
15     scale = MinMaxScaler()
16     X_train = scale.fit_transform(X_train)
17     X_test = scale.fit_transform(X_test)
18     y_train=y_train[:,np.newaxis]
19     y_train=scale.fit_transform(y_train)
20     y_test=y_test[:,np.newaxis]
21     y_test=scale.fit_transform(y_test)
22     # 生成 Model
23     model=getModel()
26                   ,loss=losses.huber)
27     # 回调生成日志记录
28     callback=keras.callbacks.TensorBoard(log_dir='logs'
29                   , histogram_freq=1, embeddings_freq=1)
30     # 训练数据训练
31     model.fit(X_train,y_train,10,epochs=15,callbacks=callback)
32     # 计算测试输出数据
33     y_hat=model.predict(X_test)
34     # 画图对象测试输出数据与真实输出数据差别
35     x=np.linspace(0,1,len(y_hat))
36     # 把单列数据变形返回单行数据
37     y1=y_hat.flatten()
38     y2=y_test.flatten()
39     # 画出对比图
40     plt.scatter(x,y1,marker='^',s=60)
41     plt.scatter(x,y2,marker='*',s=60)
42     plt.title('Boston_Housing Test vs Actual')
43     plt.legend(['test data','actual data'])
44     plt.show()运行结果对比图损失函数图6.1 回顾 L1/L2 正则化处理过拟合就是说模型在训练数据上的效果远远好于在测试集上的性能，参数越多，模型越复杂，而越复杂的模型越容易过拟合。记得在《Python 机器学习实战 —— 监督学习（上）》的第四节曾经介绍过通过正则化处理过拟合问题，常用的处理方式方式 L1/ L2 两种：L1 正则化则是以累加绝对值来计算惩罚项，因此使用 L1 会让 W(i) 元素产生不同量的偏移，使某些元素为0，从而产生稀疏性，提取最有效的特征进行计算。L2 正则化则是使用累加 W 平方值计算惩罚项，使用 L2 时 W(i) 的权重都不会为0，而是对每个元素进行不同比例的放缩。 此时可以考虑正则化，通过设置正则项前面的 hyper parameter，来权衡损失函数和正则项，减小参数规模，达到模型简化的目的，从而使模型具有更好的泛化能力。6.2 Dropout 优化处理而在 MLP 中也提供了 dropout 对过拟合的数据进行正则化处理，它的处理方式是在学习阶段，设置丢失神经元的概率，当一个神经元被丢弃时，它的输出值被设为0。由于神经元在每次新的训练中被随机丢弃，所以每个训练阶段其丢失的神经元都不相同。在面对复杂的数据集时，很多时候 dropout 会跟 L2 正则化同时使用以降低过拟合情况。下面的例子以 mnist 数据集为例子，经过五层的训练，每层训练都加入 5% 的丢失率进行正则化处理。反复训练 30 次后，测试数据的准确率依然达到 90%，可见 dropout 对避免过拟合是有一定的效果。 1 def getModel():
2     # 神经元数目从 784 逐层下降 200、100、60、30、10，最后通过 softmax 函数输出
3     model=keras.models.Sequential()
14     return model
15
16 def test():
17     # 获取数据集
19     X_train,y_train=tf.convert_to_tensor(X_train,tf.float32) , tf.convert_to_tensor(y_train,tf.float32)
20     # 建立 model
21     model=getModel()
22     # 使用 SGD 梯度下降法，学习率为 0.003
23     # 使用交叉熵算法
24     model.compile(optimizer=optimizers.SGD(0.003),
25                   loss=losses.sparse_categorical_crossentropy,
26                   metrics=['accuracy'])
27     # 绑定 tensorboard 对日志数据进行监测
28     callback=keras.callbacks.TensorBoard(log_dir='logs', histogram_freq=1, embeddings_freq=1)
29     # 重复训练50次，每 500 个作为一批
30     model.fit(X_train,y_train,epochs=30,batch_size=500,callbacks=callback)
31     # 输出测试数据准确率
32     X_test, y_test = tf.convert_to_tensor(X_test, tf.float32), tf.convert_to_tensor(y_test, tf.float32)
33     print('\n-----test data------')
34     model.fit(X_test,y_test)运行结果本篇总结本文主要介绍了MSE、MAE、CEE 、Hinge、Huber 等 15 个常用损失函数的计算方式和使用场景，分析 SGD、AdaGrad、Adam、RMSProp 4类优化器的公式原理，对阶跃激活函数、Sigmoid 激活函数、ReLU激活函数、Leaky ReLU 激活函数、Tanh 激活函数、Softmax激活函数等进行讲解。多层感知器 MLP 是深度学习的基础，本文通过分类、回归的使用例子对 MLP 的使用进行介绍。最后，讲解了如何使用 dropout 正则化对复杂类型的数据集进行优化处理。希望本篇文章对相关的开发人员有所帮助，由于时间仓促，错漏之处敬请点评。后面的文章将开始对 CNN 卷积神经网络和 RNN 循环神经网络进行介绍，敬请留意！对 .Python 开发有兴趣的朋友欢迎加入QQ群：790518786 共同探讨 ！对 JAVA 开发有兴趣的朋友欢迎加入QQ群：174850571 共同探讨！对 .NET 开发有兴趣的朋友欢迎加入QQ群：162338858共同探讨 ！AI人工智能相关文章


Original: https://blog.csdn.net/Leslies2/article/details/123801809
Author: 风尘浪子
Title: TensorFlow 2.0 深度学习实战 —— 详细介绍损失函数、优化器、激活函数、多层感知机的实现原理

(0)

### 大家都在看

人工智能 2023年7月18日
0145
• #### OpenCV、cv、cv2的区别？

Officially, OpenCV releases two types of Python interfaces, cv and cv2. I started working …

人工智能 2023年7月18日
0167
• #### 【DeeplabV3+】DeeplabV3+网络结构详解

文章目录 1 常规卷积与空洞卷积的对比 * 1.1 空洞卷积简介 1.2 空洞卷积的优点 2 DeeplabV3+模型简介 3 DeeplabV3+网络代码 4 mobilenet…

人工智能 2023年7月20日
0278
• #### 数学建模-数据分析Pandas(利用python进行数据分析)

Pandas读书笔记-数据分析 * – + ①Serises + * 1基本用法1 * 2基本用法2(对索引进行修改) * 3传入字典 * 4判空isnull() * …

人工智能 2023年7月7日
0132

Masked-attention Mask Transformer for Universal Image Segmentation 图像分割是关于将不同语义的像素分组，例如，类别…

人工智能 2023年6月18日
0143
• #### tensorflow版本及其对应环境

来自官网的发布 ，持续更新。 CPU 版本Python 版本编译器构建工具tensorflow-2.4.03.6-3.8MSVC 2019Bazel 3.1.0tensorflow…

人工智能 2023年5月24日
0167
• #### python——正则表达式(re模块)详解

在Python中需要通过正则表达式对字符串进⾏匹配的时候，可以使⽤⼀个python自带的模块，名字为re。 正则表达式的大致匹配过程是：1.依次拿出表达式和文本中的字符比较，2.如…

人工智能 2023年7月30日
0112
• #### 【论文解读】NER任务中的MRC（机器阅读理解）

论文：https://arxiv.org/pdf/1910.11476v6.pdf 前沿： 在之前的NER任务中常常分为两种：nested NER和 ﬂat NER。从直观的角度来…

人工智能 2023年6月10日
0183
• #### pandas dataframe多层索引取值

import pandas as pd import numpy as np 新建df数据 df = pd.DataFrame(np.random.randint(50, 100,…

人工智能 2023年6月2日
0232
• #### python实现LBP纹理提取

什么是LBP纹理特征？ LBP（Local Binary Patterns，局部二值模式）是提取局部特征作为判别依据的，一种有效的纹理描述算子，度量和提取图像局部的纹理信息。它具有…

人工智能 2023年5月28日
0120
• #### Python学生通讯录管理系统案例（文件版）

目录 2.接收用户从键盘输入的选择序号 3.根据输入的选择序号，判断并执行不同的功能 注意：本例中，要想保存所以学生的通讯信息，需要用到字典。 首先我们先定义一个showMenu(…

人工智能 2023年7月30日
0160
• #### pandas怎么去除数据中的重复值

废话不多说，直接进入正题。 比方说，现在有一份数据表，如下图：以ASIN为例，假设有3000行数据，且其中有些ASIN值有重复的，现在需要将重复的ASIN值去除并保留一个即可。 使…

人工智能 2023年7月8日
0135
• #### Google Earth Engine（GEE）——Sentinel-1 和 2 数据的融合，水稻范围识别和水稻种植季节区分地图绘制—马来西亚为例

最近发现了一个巨牛的人工智能学习网站，点击跳转到网站：前言 – 床长人工智能教程 这次给大家推荐一篇文章，关于水稻识别：使用 GEE 平台中的无监督分类整合 Sentinel-1 …

人工智能 2023年5月31日
0179
• #### Tableau制作漏斗图的两种方法

很多业务都是由多个流程、多个环节组成，每一个环节的数据很多时候呈现逐级递减的趋势，如电商中用户的下单数据。 漏斗图主要用于展现每个环节的留存、转化情况，本文讲一下如何使用Table…

人工智能 2023年7月16日
0130
• #### 【机器视觉案例】(12) 自制AI视觉小游戏–贪吃蛇，附python完整代码

各位同学好，今天和大家分享一下如何使用 mediapipe+opencv自制贪吃蛇小游戏。先放张图看效果。 规则： 食指指尖控制蛇头， 指尖每接触到黄色方块 ，计数加一，蛇身变长，…

人工智能 2023年7月28日
0122
• #### 亲测有效解决torch.cuda.is_available()返回False的问题（分析+多种方案），点进不亏

文章目录 解决torch.cuda.is_available()返回False * 出现返回False的原因 – 问题1：版本不匹配 问题2：错下成了cpu版本的（小编…

人工智能 2023年6月17日
0177