Keras 最初是作为 Theano 的一个方便的附加组件而发展起来的,长久以来,Keras首早先开始支持Tensorflow,然后完全成为其中的一部分。然而,我们的文章不会致力于讲述这个框架的复杂命运,而是它的功能。
安装 Keras 非常简单,因为它是一个普通的 python 包:
pip install Keras
现在我们可以开始分析它了,但首先,让我们谈谈后端。
后端是提高 Keras 受欢迎程度的主要因素。Keras 支持使用许多其他框架作为后端。如果你想使用 Theano作为后端,那么有两种选择:
- 编辑位于 $HOME/.keras/keras.json 路径上的 keras.json 配置文件(或在 Windows 操作系统的情况下为 %USERPROFILE%.keraskeras.json)。我们需要一个backend字段:
{
"image_data_format": "channels_last",
"epsilon": 1e-07,
"floatx": "float32",
"backend": "theano"
}
- 第二种方法是设置环境变量 KERAS_BACKEND,如下所示:
KERAS_BACKEND=theano python -c "from keras import backend"
因此,Keras 后端正在扩展,并将随着时间的推移接管世界!
看来我们现在可以以一个[不那么]深度的神经网络为例。
训练任何机器学习模型都从数据开始。Keras 内部包含多个训练数据集,但它们已经以方便工作的形式放置,并且不允许展示 Keras 的全部功能。
因此,我们将采用更原始的数据集。它将是一个包含 20 个新闻组的数据集——来自 Usenet 组的 20,000 个新闻帖子(这是 1990 年代的邮件交换系统,类似于 FIDO,读者可能更熟悉一点),大致均匀地分为 20 个类别。
我们将训练我们的网络向这些新闻组正确分发消息。
from sklearn.datasets import fetch_20newsgroups
newsgroups_train = fetch_20newsgroups(subset='train')
newsgroups_test = fetch_20newsgroups(subset='test')
Here is an example of the content of a document from the training sample:
newsgroups_train ['data'] [0]
Keras 包含方便预处理文本、图片和时间序列的工具,即最常见的数据类型。今天我们处理文本,所以我们需要将它们分解成标记并将它们转化为矩阵形式。
tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(newsgroups_train["data"])
# now the tokenizer knows the dictionary for this corpus of texts
x_train = tokenizer.texts_to_matrix(newsgroups_train["data"], mode='binary')
x_test = tokenizer.texts_to_matrix(newsgroups_test["data"], mode='binary')
As a result, we got binary matrices of the following sizes:
x_train shape: (11314, 1000)
x_test shape: (7532, 1000)
We also need to convert class labels to matrix form for training using cross-entropy. To do this, we will translate the class number into the so-called one-hot vector:
y_train = keras.utils.to_categorical(newsgroups_train["target"], num_classes)
y_test = keras.utils.to_categorical(newsgroups_test["target"], num_classes)
At the output, we also get binary matrices of the following sizes:
y_train shape: (11314, 20)
y_test shape: (7532, 20)
正如我们所见,这些矩阵的大小部分与数据矩阵一致(在第一个坐标中——训练和测试样本中的文档数量),部分与数据矩阵不一致。在第二个坐标处,我们有类的数量(20,正如数据集的名称所暗示的那样)。
就是这样,现在我们准备教我们的网络对新闻进行分类!
Keras 中的模型可以通过两种主要方式来描述:
Sequential API
#The first one is a consistent description of the model, like this:
model = Sequential()
model.add(Dense(512, input_shape=(max_words,)))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))
or like this:
model = Sequential([
Dense(512, input_shape=(max_words,)),
Activation('relu'),
Dropout(0.5),
Dense(num_classes),
Activation('softmax')
])
Functional API
前段时间,使用函数式 API 来创建模型成为可能——第二种方式:
a = Input(shape=(max_words,))
b = Dense(512)(a)
b = Activation('relu')(b)
b = Dropout(0.5)(b)
b = Dense(num_classes)(b)
b = Activation('softmax')(b)
model = Model(inputs=a, outputs=b)
There is no specific difference between the methodologies, choose the preferred one.
This allows you to save models in a human-readable form, as well as instantiate models from such a description:
from keras.models import model_from_yaml
yaml_string = model.to_yaml()
model = model_from_yaml(yaml_string)
需要注意的是,以文本形式保存的模型(顺便说一句,也可以将其保存为 JSON)不包含权重。
要保存和加载权重,请相应地使用函数 save_weight 和 load_weights。
可视化不容忽视。Keras 具有内置的模型可视化功能:
from keras.utils import plot_model
plot_model(model, to_file='model.png', show_shapes=True)
这段代码将以model.png名称保存以下图片:
在这里,我们另外显示了层的输入和输出的维度。大小元组中的第一个是批大小。因为它不需要任何成本,所以批处理可以是任意的。
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))
需要注意的是,可视化需要 Graphviz 包以及 Python 包 pydot。
pip install pydot-ng
Ubuntu 中的 Graphviz 包是这样安装的(在其他 Linux 发行版中类似):
apt install graphviz
在 macOS 上(使用 HomeBrew 软件包系统):
brew install graphviz
所以,我们已经形成了我们的模型。现在你需要为工作做好准备:
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
编译的函数参数是什么意思?
loss-这是误差函数,在我们的例子中,它是交叉熵,正是为了这个函数,我们以矩阵的形式准备了我们的标签;
optimizer – 使用的优化器,可能有普通的随机梯度下降,但 Adam 在这个问题上表现出最好的收敛性;
metrics – 考虑模型质量的指标,在我们的例子中是准确度,即正确猜测答案的比例。
虽然 Keras 包含大多数流行的误差函数,但你的任务可能需要一些独特的东西。为了编写自己的损失函数,你需要做一点:只需定义一个函数,该函数采用正确和预测答案的向量,并为每个输出输出一个数字。
对于训练,让我们创建自己的函数来计算交叉熵。为了让它有所不同,我们将介绍所谓的裁剪——从顶部和底部切断向量值。
from keras import backend as K
epsilon = 1.0e-9
def custom_objective(y_true, y_pred):
'''Yet another cross-entropy'''
y_pred = K.clip(y_pred, eps, 1.0 - eps)
y_pred /= K.sum(y_pred, axis=-1, keepdims=True)
cce = categorical_crossentropy(y_pred, y_true)
return cce
这里 y_true 和 y_predare 张量来自 Tensorflow,所以使用 Tensorflow 函数来处理它们。
要使用另一个损失函数,通过编译传递我们损失函数的对象来改变损失函数参数的值就足够了(在 Python 中,函数也是对象,尽管这是一个完全不同的故事):
model.compile(loss=custom_objective,
optimizer='adam',
metrics=['accuracy'])
最后,是时候训练模型了:
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_split=0.1)
fit 方法正是这样做的。它接受一个训练样本作为输入以及标签 – x_train 和 y_train,batch_size 的大小,它限制了一次提供的示例数量,训练 epoch 的 epoch 数(一个 epoch 是模型一次完全通过的训练样本),以及提交用于验证的训练样本的比例——validation_split。
此方法返回每一步训练的错误历史记录。
最后,测试。该方法将测试样本评估为输入及其标签。该指标是为工作准备而设置的,因此不需要其他任何内容。(但我们也会指明批次的大小)。
score = model.evaluate(x_test, y_test, batch_size=batch_size)
关于 Keras 回调这样一个重要的特性,我还需要说几句。许多有用的功能都是通过它们实现的。
例如,如果你已经对网络进行了很长时间的训练,你需要了解如果数据集上的错误停止减少,何时停止。在英语中,此处所描述的功能称为“提前停止”。
from keras.callbacks import EarlyStopping
early_stopping=EarlyStopping(monitor='value_loss')
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_split=0.1,
callbacks=[early_stopping])
Run an experiment and check how quickly early stopping works in our example?
此外,作为回调,你可以使用 Tensorboard 方便0的格式保存日志(我们在一篇关于 Tensorflow 的文章中谈到了它,简而言之——这是一个用于处理和可视化来自 Tensorflow 日志的信息的特殊实用程序)。
from keras.callbacks import TensorBoard
tensorboard=TensorBoard(log_dir='./logs', write_graph=True)
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_split=0.1,
callbacks=[tensorboard])
训练结束后(甚至在训练过程中!),你可以通过指定包含日志的目录的绝对路径来启动 Tensorboard:
tensorboard –logdir=/path/to/logs
例如,你可以在那里看到目标指标在验证集上的变化情况:
现在让我们看看构建一个稍微复杂的计算图。一个神经网络可以有多个输入和输出,输入数据可以通过各种映射进行转换。
为了重用复杂计算图的部分(特别是对于迁移学习),以模块化风格描述模型是有意义的,这样你就可以方便地检索、保存模型的部分并将其应用于新的输入数据。
通过混合两种方法来描述模型是最方便的 – 前面描述的Functional API 和Sequential API。
让我们以 Siamese Network 模型为例来看看这种方法。在实践中积极使用类似的模型来获得具有有用属性的向量表示。例如,可以使用相似模型来学习将人脸照片映射到向量中,从而使相似人脸的向量彼此接近。特别是 FindFace 等图像搜索应用程序利用了这一点。
可以在图中看到模型的说明:
这里函数 G 将输入图像转换为向量,然后计算一对图像的向量之间的距离。如果图片来自同一类,则应将距离最小化,如果来自不同类,则应最大化距离。
在训练这样的神经网络之后,我们可以将任意图像表示为向量 G(x),并使用该表示来查找最近的图像或作为其他机器学习算法的特征向量。
首先,我们在 Keras 上定义一个映射输入向量的函数。
def create_base_network(input_dim):
seq = Sequential()
seq.add(Dense(128, input_shape=(input_dim,), activation='relu'))
seq.add(Dropout(0.1))
seq.add(Dense(128, activation='relu'))
seq.add(Dropout(0.1))
seq.add(Dense(128, activation='relu'))
return seq
请注意,我们已经使用 Sequential API 描述了模型,但是,我们将其创建包装在一个函数中。现在我们可以通过调用这个函数来创建这样一个模型,并使用它的 Functional API 将它应用于输入数据:
base_network = create_base_network(input_dim)
input_a = Input(shape=(input_dim,))
input_b = Input(shape=(input_dim,))
processed_a = base_network(input_a)
processed_b = base_network(input_b)
现在变量processed_a和processing_bare向量表示通过将先前定义的网络应用于输入数据而获得。
有必要计算它们之间的距离。为此,Keras 提供了一个包装函数 Lambda,它将任何表达式表示为一个层 (Layer)。不要忘记我们是分批处理数据的,所以所有张量总是有一个额外的维度负责批处理的大小。
from keras import backend as K
def euclidean_distance(vects):
x, y = vects
return K.sqrt(K.sum(K.square(x - y), axis=1, keepdims=True))
distance = Lambda(euclidean_distance)([processed_a, processed_b])
太好了,我们得到了内部视图之间的距离,现在将输入和距离收集到一个模型中。
model = Model([input_a, input_b], distance)
得益于模块化结构,我们可以单独使用base_network,这在训练模型后特别有用。
我怎样才能做到这一点?让我们来看看我们模型的层:
>>> model.layers
[<keras.engine.topology.InputLayer object at 0x7f238fdacb38>, <keras.engine.topology.InputLayer object at 0x7f238fdc34a8>, <keras.models.Sequential object at 0x7f239127c3c8>, <keras.layers.core.Lambda object at 0x7f238fddc4a8>]
我们在类型列表 models.Sequential 中看到第三个对象。这是将输入图像映射到向量的模型。要提取它并将其用作成熟的模型(你可以重新训练、验证、嵌入到另一个图中),你只需将其从层列表中拉出:
>>> embedding_model = model.layers[2]
>>> embedding_model.layers
[<keras.layers.core.dense object at 0x7f23c4e557f0>,<keras.layers.core.dropout object at 0x7f238fe97908>,<keras.layers.core.dense object at 0x7f238fe44898>,<keras.layers.core.dropout object at 0x7f238fe449e8>,<keras.layers.core.dense object at 0x7f238fe01f60>]
例如,对于已经在输出维度为 base_modeltwo 的 MNIST 数据上训练的 Siamese 网络,你可以将向量表示可视化如下:
让我们加载数据并将大小为 28×28 的图像转换为平面向量。
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_test = x_test.reshape(10000, 784)
让我们使用之前提取的模型显示图片:
embeddings = embedding_model.predict(x_test)
现在在嵌入中有二维向量,它们可以在一个平面上描述:
就是这样,我们制作了第一个 Keras 模型!
Keras 模型明显的优势包括创建模型的简单性,这可以转化为高速原型制作。总的来说,这个框架越来越流行:
通常,当你需要为特定任务快速构建和测试网络时,建议使用 Keras。但是,如果你需要一些复杂的东西,例如非标准层或在多个 GPU 上并行化代码,那么使用底层框架会更好(有时是不可避免的)。