在本节中,我们将了解如何构建能够学习复杂关系的神经网络。 这里的关键思想是模块化(modularity),从简单的功能单元构建一个复杂的网络。我们已经看到了线性单元如何计算线性函数——现在我们将看到如何组合和修改这些单个的单元来模拟更复杂的关系。
Layers
神经网络通常将它们的神经元组织成层。当我们将具有一组公共输入的线性单元收集在一起时,我们会得到一个密集层(dense layer)。
你可以将神经网络中的每一层视为执行某种相对简单的变换。通过一层很深的堆栈,神经网络可以以越来越复杂的方式转换其输入。在训练有素的神经网络中,每一层都是一个转换,使我们更接近目标结果。
The Activation Function
然而,事实证明,中间没有任何东西的两个密集层(dense layers)并不比一个单独的密集层好。dense层本身永远无法将我们带出线和平面的世界。我们需要的是非线性的东西。我们需要的是激活函数(activation functions)。
Keras 中的“层”是一种非常通用的东西。一个层本质上可以是任何类型的数据转换。许多层,如卷积层和循环层,通过使用神经元转换数据,主要区别在于它们形成的连接模式。其他虽然用于特征工程或只是简单的算术。有一个层次的整个世界要发现 - 检查它们!
简单来说,一般我们是将激活函数应用到每层的输出结果上,目前最常用的激活函数是整流函数(rectifier function): max(0,x) 。
整流函数(rectifier function)的图形如上图所示,该图形中小于0的部分是“整流”为零的线。将该函数应用于神经元的输出将使数据弯曲,使我们远离简单的线条。
当我们将整流器连接到线性单元时,我们会得到一个整流线性单元或 ReLU。(出于这个原因,通常将整流器函数称为“ReLU 函数”。)将 ReLU 激活应用于线性单元意味着输出变为 max(0, w * x + b),我们可以将其绘制成如下图:
目前激活函数有很多种,这里我们列举几个常见的激活函数的优缺点:
1.sigmoid函数
2.tanh函数
3.ReLU函数
4.Leaky ReLU函数
5.ELU函数
Stacking Dense Layers
现在我们具有了一些非线性,让我们看看如何堆叠层以获得复杂的数据转换。
输出层之前的层有时被称为隐藏层,因为我们从未直接看到它们的输出。 现在,请注意输出层是一个线性单元(意思是,没有激活函数)。这使得这个网络适用于回归任务,即我们试图预测一些任意的数值。其他任务(如分类)可能需要输出的激活函数。
Building Sequential Models
我们一直使用的 Sequential 模型将按照从头到尾的顺序将一系列层连接在一起:第一层获得输入,最后一层产生输出。这将创建上图中的模型:
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
# the hidden ReLU layers
layers.Dense(units=4, activation='relu', input_shape=[2]),
layers.Dense(units=3, activation='relu'),
# the linear output layer
layers.Dense(units=1),
])
确保将所有层一起传递到列表中,例如 [layer, layer, layer, ...],而不是作为单独的参数。要将激活函数添加到层,只需在激活参数中指定其名称。
练习
在下边的例子中,我们会看到如何通过在 Sequential 模型中堆叠层来构建深度神经网络。通过在隐藏层之后添加一个激活函数,我们赋予网络学习数据中更复杂(非线性)关系的能力。 在这些练习中,您将构建一个具有多个隐藏层的神经网络,然后探索 ReLU 之外的一些激活函数。 在混凝土数据集(concrete.csv)中,我们的任务是预测根据各种配方制造的混凝土的抗压强度。
import tensorflow as tf
import matplotlib.pyplot as plt
import pandas as pd
concrete=pd.read_csv('concrete_data.csv')
concrete.head()
import tensorflow as tf
import matplotlib.pyplot as plt
import pandas as pd
#加载数据集
concrete=pd.read_csv('concrete_data.csv')
concrete.head()
concrete.shape #输出(1030, 9)
此任务的目标是“CompressiveStrength”列,其余列是我们将用作输入的特征。 这个数据集的输入形状是什么? input_shape = [8]
现在创建一个具有三个隐藏层的模型,每个隐藏层有 512 个单元,使用ReLU 激活函数。输出层只包含一个单元并且没有激活函数,将 input_shape 作为第一层的参数。
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
# the hidden ReLU layers
layers.Dense(units=512, activation='relu', input_shape=[8]),
layers.Dense(units=512, activation='relu', input_shape=[512]),
layers.Dense(units=512, activation='relu'),
# the linear output layer
layers.Dense(units=1),
])
注意:将激活函数附加到 Dense 层的常用方法是将其作为Dense()函数的一部分包含在参数中。但是,有时我们想在 Dense 层和它的激活函数之间放置一些其他层。 在这种情况下,我们可以将其分开写,如下所示: - layers.Dense(units=8), - layers.Activation('relu') 这完全等同于普通方式:layers.Dense(units=8, activation='relu')。 重写一下上边定义的模型model,使每个激活都在其自己的激活层中。
model = keras.Sequential([
layers.Dense(512, input_shape=[8]),
layers.Activation('relu'),
layers.Dense(512),
layers.Activation('relu'),
layers.Dense(512),
layers.Activation('relu'),
layers.Dense(1),
])
有一系列“relu”激活函数的变体——“elu”、“selu”和“swish”等等,所有这些都可以在 Keras 中使用。有时,在给定任务上,一个激活会比另一个表现更好,因此你可以考虑在开发模型时尝试改变所用的激活函数。 ReLU 激活往往在大多数问题上都表现良好,所以一般我们优先考虑relu激活函数。 让我们看看其中一些的图表。将激活从“relu”更改为上面指定的其他激活之一。然后运行单元格以查看图形。
# YOUR CODE HERE: Change 'relu' to 'elu', 'selu', 'swish'... or something else
activation_layer = layers.Activation('swish')
x = tf.linspace(-3.0, 3.0, 100)
y = activation_layer(x) # once created, a layer is callable just like a function
plt.figure(dpi=100)
plt.plot(x, y)
plt.xlim(-3, 3)
plt.xlabel("Input")
plt.ylabel("Output")
plt.show()