从零开始 Keras :搭建你的第一个神经网络

优秀外文博文搬运 + 翻译

原作者:Victor Zhou
链接:Keras for Beginners: Building Your First Neural Network @Victor Zhou

本文为转载翻译内容,文章内容著作权归原作者所有

本文翻译已获得原作者授权


以下为翻译内容(斜体加粗部分为博主补充内容,非原文内容):


keras_log
Keras 是 Python 中一种简单易用但却非常强大的深度学习库。在这篇博文中,我们将认识到用 Keras 去建立一个前馈型神经网络,训练并用它解决实际问题是多么的简单。

这篇博文主要面向 Keras 的完全零基础初学者,但是默认读者有一定的神经网络知识。我的这篇博文:机器学习初学:神经网络原理 + Python 简单实例
基本涵盖了你阅读本篇文章所需的全部知识,如果需要的话可以先看看。

开工吧!

只想要代码?完整代码放在文末了。


实际问题:MNIST 数字分类

我们将解决一个在机器学习中非常非常经典的问题:MNIST 手写数字集的分类(classification)。它的要求很简单:给定一张图片(手写的单个数字),将它分类为 0 - 9 中的某个数字。

mnist_datasets
MNIST 数据集中的每一张图都是一个 28x28(像素),居中的灰度数字图。我们需要将这个 28x28 转换成一个有 784 个维度的向量然后作为神经网络的输入。输出将会是 10 种分类里的一种:即 0 - 9 中的某个数字。


1. 准备工作

我这里默认你已经安装了基本的 Python(大概),我们先来装一些我们需要的 Python 包:

$ pip install tensorflow numpy mnist

:我们不需要单独去安装 keras 包因为它现在已经正式作为 TensorFlow 的高级(high-level)API 和 TensorFlow 绑定了。比起单独的 keras 包现在更推荐用 TensorFlow 下的 keras

你现在应该能导入这些软件包并查看 MNIST 数据集了:

import numpy as np
import mnist
from tensorflow import keras

# 第一次运行可能会有点慢,因为需要下载并缓存 mnist 数据包
train_images = mnist.train_images()
train_labels = mnist.train_labels()

print(train_images.shape) # (60000, 28, 28)
print(train_labels.shape) # (60000,)

这里博主在自己测试的时候发现导入数据集时可能会出现报错,如果上述方法无法导入 MNIST 数据集,可以采用如下方法,同样是从 keras 的数据集中加载 MNIST,对后面程序不会有影响:

import numpy as np
from tensorflow import keras
from keras.datasets import mnist  # 从 keras 中导入 mnist

# 获取完整 MNIST 数据集
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

print(train_images.shape) # (60000, 28, 28)
print(train_labels.shape) # (60000,)

导入之后我们还可以通过 matplotlib 查看每张图片

import numpy as np
import matplotlib.pyplot as plt  # 导入 matplot 绘图包
from tensorflow import keras
from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# plot第1张图片 (从0开头)
img = plt.imshow(train_images[1],cmap='gray')
plt.show()

mnist_sample


2. 准备数据:

之前提到过,我们需要先将图片数据转换一下以方便输入神经网络。另外我们还需要把图片中的像素值从 [0, 255] 归一化(normalize)至 [-0.5, 0.5] 这个区间让神经网络更容易训练(一般来说用更小,更集中的数值比较好训练)。

import numpy as np
from tensorflow import keras
from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# 对图片数据归一化处理.
train_images = (train_images / 255) - 0.5
test_images = (test_images / 255) - 0.5

# 转换图片数据.
train_images = train_images.reshape((-1, 784))
test_images = test_images.reshape((-1, 784))

print(train_images.shape) # (60000, 784)
print(test_images.shape)  # (10000, 784)

上述代码中的 reshape 将原本 60000 页,每页 28 行 28 列的三维数据转换成了 60000 行,每行 28x28=784 列的二维数据,每一行代表一张图片,方便作为神经网络的输入。感兴趣的朋友可以自行查一下 reshape -1的用法。

我们已经准备好构建自己的神经网络了!


3. 建立神经网络模型

每个 Keras 模型都可以由 Sequential 类建立,它代表了多个层的线性堆叠。另外也可以由功能性的 Model 类建立,它的自定义性更强。我们这里将使用较为简单的 Sequential 类,因为我们的神经网络确实就是多个层的线性堆叠而已。

我们从实例化一个 Sequential 模型开始:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

# WIP
model = Sequential([
  # layers...
])

Sequential 的构造器可以包含一个代表 Keras 层(layers)的数组。由于我们只是搭建一个标准前馈网络,我们只需要 Dense 层,也就是标准的全连接(dense)网络层。

废话不多说我们先上3个层:

# WIP
model = Sequential([
  Dense(64, activation='relu'),
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

前面两个层各自有 64 个节点(神经元),且每个节点采用 ReLU 激活函数。最后一个输出层是有10个节点的 Softmax 层,每个节点对应一个数字分类。

你或许想复习一下 Softmax 函数,可以看看我的这篇介绍:Softmax 函数简单解释

最后一件事就是告诉 Keras 我们的数据集是长啥样的。我们可以把输入维度填到 Sequential 模型第一层的 input_shape 中:

model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

一旦确定了输入的维度,Keras 会自动推断后面层的输入维度。我们已经成功定义了网络模型,贴一下到目前为止的完整代码:

import numpy as np
import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

train_images = mnist.train_images()
train_labels = mnist.train_labels()
test_images = mnist.test_images()
test_labels = mnist.test_labels()

# 对图片数据归一化处理.
train_images = (train_images / 255) - 0.5
test_images = (test_images / 255) - 0.5

# 转换图片数据维度.
train_images = train_images.reshape((-1, 784))
test_images = test_images.reshape((-1, 784))

# 建立模型.
model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

编译模型

在训练模型之前,我们要对训练流程做配置。我们把这个步骤划分为 3 个关键点:

  • 优化器(optimizer):这里我们就用默认的 Adam 优化器。当然 Keras 还提供很多其他种类的优化器,你可以自行查看。(关于优化器的介绍可以参考这篇博文Keras 学习笔记-优化器 @ice_actor)。
  • 损失函数:由于我们采用了 Softmax 输出层,我们需要用交叉熵损失(Cross-Entropy loss)。Keras 需要选择设置 binary_corssentropy(只有两种分类)或者 categorical_crossentropy(大于两种分类),我们这里选择后者。(关于交叉熵损失函数的详细解析,可以参考知乎大佬文章交叉熵损失函数 @飞鱼Talk)。
  • 一个用于衡量模型表现的数组 metrics:由于这是个分类问题,我们这里只关注模型的准确率(accuracy)。

下面是模型编译器的最终设定:

model.compile(
  optimizer='adam',
  loss='categorical_crossentropy',
  metrics=['accuracy'],
)

接着干!


5. 训练模型

在 Keras 中训练模型其实只需要调用 fit() 函数并且确定一些参数就完事了。fit() 函数有很多可填写的参数,但是目前我们只需填写一部分:

  • 训练数据(图片数据和标签),一般分别用 X 和 Y 表示。
  • 训练迭代次数(epochs),表示训练时整个数据集会被迭代使用多少次。
  • 训练批次大小(batch size),只每次梯度更新时所用的样本数。

下面是这些参数在代码中的样子:

model.fit(
  train_images, # 训练集
  train_labels, # 训练集的标签
  epochs=5,
  batch_size=32,
)

光有这些神经网络还不能工作,我们还漏了一件事:Keras 希望训练后的输出是一个 10 维的向量,因为 Softmax 输出层有 10 个节点。但我们提供的是一个单一的整数来代表图像的类别。

方便的是,Keras 自带一个实用的方法来解决这个问题:
to_categorical。它会将我们的整数类数组转换成一个单热(one-hot)向量数组。比如说输出为 2 就会变成 [0,0,1,0,0,0,0,0,0,0](从 0 开头)

博主额外补充一下:一般来说,10个节点的 Softmax 函数输出的是一个包含10个概率值的数组,每个值分别对应某一分类的概率。在这个例子中,这 10 个概率值分别表示被处理的这张图片属于 0 - 9 中哪一个数字的概率。最高的概率值所对应的分类也就是此次输出的结果。以原作者的输出是 2 为例,这表明输出的数组中 10 个概率值里,第 3 个值最大(从 0 开头),表示这次输出结果相信这张图中的手写数字属于数字 2。to_categorical 应该是将输出数组中最大值标为1,其他全都标 0,得到了一个新的单热向量数组)


现在我们可以把所有环节都放在一起然后训练神经网络了:

import numpy as np
from tensorflow import keras
from keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# 对图片数据归一化处理.
train_images = (train_images / 255) - 0.5
test_images = (test_images / 255) - 0.5

# 转换图片数据.
train_images = train_images.reshape((-1, 784))
test_images = test_images.reshape((-1, 784))

# 建立模型.
model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

# 编译模型.
model.compile(
  optimizer='adam',
  loss='categorical_crossentropy',
  metrics=['accuracy'],
)

# 训练模型.
model.fit(
  train_images,
  to_categorical(train_labels),
  epochs=5,
  batch_size=32,
)

运行结果大概会像这样:

Epoch 1/5
60000/60000 [==============================] - 2s 35us/step - loss: 0.3772 - acc: 0.8859
Epoch 2/5
60000/60000 [==============================] - 2s 31us/step - loss: 0.1928 - acc: 0.9421
Epoch 3/5
60000/60000 [==============================] - 2s 31us/step - loss: 0.1469 - acc: 0.9536
Epoch 4/5
60000/60000 [==============================] - 2s 31us/step - loss: 0.1251 - acc: 0.9605
Epoch 5/5
60000/60000 [==============================] - 2s 31us/step - loss: 0.1079 - acc: 0.9663

我们在 5 个训练迭代后达到了 96.6% 的准确率!虽然这并不能说明什么,因为有可能出现过拟合(overfitting)问题。真正的挑战还需看我们的模型在测试集中的表现。(过拟合问题指模型在训练集上的表现非常好,但在交叉验证集和测试集上表现平平。换言之,模型可能对训练集的数据过度学习,过度贴合,导致对训练集范围之外数据的预测能力一般,即泛化能力很弱


博主补充:这里不知道是因为版本原因还是其他原因,博主自己测试的训练结果是这样的:

Epoch 1/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.3533 - accuracy: 0.8938
Epoch 2/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.1827 - accuracy: 0.9447
Epoch 3/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.1435 - accuracy: 0.9564
Epoch 4/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.1197 - accuracy: 0.9633
Epoch 5/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.1052 - accuracy: 0.9671

最终准确率没有什么问题,重点在于每个 Epoch 下显示的是 1875/1875 而不是作者的 60000/60000 。由于我们训练前填写了 batch_size = 32(即使不填写,系统也默认是32),模型是分批次处理训练数据并更新的。由于批次大小设定是 32,所以每次迭代将处理 60000/32 = 1875 个批次的数据,即如结果显示的那样。作者也设定了 32 的batch_size 但是显示仍是 60000/60000,也许是不同版本的原因,虽然不影响结果但是不同的显示方式可能会造成一定的困惑。Stackoverflow 上也有人提出了相同的问题:Keras not training on entire dataset,可以参考一下。


6. 测试模型

评估测试模型很简单:

model.evaluate(
  test_images,
  to_categorical(test_labels)
)

跑一遍会给出这样的结果:

10000/10000 [==============================] - 0s 15us/step
[0.10821614159140736, 0.965]

:这里可能同样存在上面提到的显示问题)

evaluate() 返回一个包含测试集 loss 的数组,当然也可以设置让它包含更多其他衡量指标(metrics)。我们的模型在这次测试中达到了 0.108 的 loss 与 96.5% 的准确率!对于我们的第一个神经网络来说还算不错 ~


7. 使用模型

现在我们有了一个训练完毕且能用的模型了,我们来把它投入应用。首先我们得把训练好的模型参数保存起来以便随时可以加载它:

model.save_weights('model.h5')

(注:保存为 .h5 文件)

现在我们可以通过加载已保存的模型信息(包含权重和偏移量等参数)来随时建立之前已训练的模型:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

# 建立模型.
model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

# 加载已保存的模型参数.
model.load_weights('model.h5')

使用已训练的模型会让预测更加简单:我们向 predict() 中传入一组输入,然后它返回一组输出。别忘了我们神经网络输出的是10个概率值(由于 softmax 函数),还需要用 np.argmax() 将它们转成实际数字。

# 预测前5个图片.
predictions = model.predict(test_images[:5])

# 打印模型的预测结果.
print(np.argmax(predictions, axis=1)) # [7, 2, 1, 0, 4]

# 打印正确答案(标签)以确认预测结果的正确性.
print(test_labels[:5]) # [7, 2, 1, 0, 4]

8. 扩展内容

到目前为止我们只是对 Keras 有了一个简单的了解,还有更多的东西可以用来测试和提升模型的表现。接下来我介绍一部分例子:

调节超参数(Hyperparameters)

调整 Adam 优化器的学习率(learning rate)是一个学习超参的好开头。当你增加或减少学习率的时候,模型会发生什么呢?

from tensorflow.keras.optimizers import Adam  # 导入优化器相关包

model.compile(
  optimizer=Adam(lr=0.005),  # 设置学习率
  loss='categorical_crossentropy',
  metrics=['accuracy'],
)

那修改批次大小 batch_size 和迭代次数 epochs 又会怎么样呢?

model.fit(
  train_images,
  to_categorical(train_labels),
  epochs=10,  # 设置迭代次数
  batch_size=64,  # 设置批次大小
)

网络深度

如果我们增加或减少全连接层(fully-connected layers)的数量会发生什么?它会如何影响神经网络的训练和最终表现?

model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(64, activation='relu'),  # 增加更多的层
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

激活函数

如果我们采用别的激活函数而不是 ReLU 呢?(比如说改成 Sigmoid)

model = Sequential([
  Dense(64, activation='sigmoid', input_shape=(784,)),  # 激活函数换成 sigmoid 函数
  Dense(64, activation='sigmoid'),
  Dense(10, activation='softmax'),
])

随机失活层(Dropout)

我们还可以加入 dropout 层试试,它用于预防过拟合问题。(dropout 层会以一定的概率禁用一部分神经元来减少模型对局部特征的依赖,强化泛化能力。Keras 中设置的概率值是神经元被丢弃的概率 )

from tensorflow.keras.layers import Dense, Dropout  # 导入相关包

model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dropout(0.5),  # 加入 dropout 层
  Dense(64, activation='relu'),
  Dropout(0.5),  # 加入 dropout 层
  Dense(10, activation='softmax'),
])

验证

我们也可以用测试集作为神经网络训练期间的验证数据。Keras 会在每次训练迭代结束后用验证集评估神经网络的表现,并汇报 loss 或其他我们要求输出的指标。这允许我们在训练阶段就能保持对神经网络的监视,对于鉴定过拟合问题,甚至是提早结束训练都很有用。

model.fit(
  train_images,
  to_categorical(train_labels),
  epochs=5,
  batch_size=32,
  validation_data=(test_images, to_categorical(test_labels))  # 设置验证集
)

结论

你成功的用 Keras 实现了自己的第一个神经网络,并且在 MNIST 数据集上用 5 次训练迭代达到了 96.5% 的准确率,还不错啦。我会在下面再附上完整代码供你参考。


完整代码

import numpy as np
from tensorflow import keras
from keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# 对图片数据归一化处理..
train_images = (train_images / 255) - 0.5
test_images = (test_images / 255) - 0.5

# 转换图片数据.
train_images = train_images.reshape((-1, 784))
test_images = test_images.reshape((-1, 784))

# 建立模型.
model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(10, activation='softmax'),
])

# 编译模型.
model.compile(
  optimizer='adam',
  loss='categorical_crossentropy',
  metrics=['accuracy'],
)

# 训练模型.
model.fit(
  train_images,
  to_categorical(train_labels),
  epochs=5,
  batch_size=32,
)

# 评估模型.
model.evaluate(
  test_images,
  to_categorical(test_labels)
)

# 保存模型参数.
model.save_weights('model.h5')

# 加载模型参数(保存参数后无需再次训练,直接加载参数即可使用模型):
# model.load_weights('model.h5')

# 对前 5 张图片做预测.
predictions = model.predict(test_images[:5])

# 输出模型预测结果.
print(np.argmax(predictions, axis=1)) # [7, 2, 1, 0, 4]

# 输出真实答案对照.
print(test_labels[:5]) # [7, 2, 1, 0, 4]

期待翻译小哥的更多相关教程!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Keras是一个基于Python的深度学习库,提供了许多便捷的API用于神经网络搭建Keras框架的特点是高度模块化、易于扩展、支持GPU和CPU的混合计算、用户友好,可以方便的构建各种神经网络模型并进行训练和预测。 在Keras搭建神经网络,首先需要确定神经网络的模型。Keras支持多种模型构建方法,包括序列模型、函数式模型和子类化API等。 序列模型是最简单的一种,它是一个线性的神经网络模型,是多个网络层的线性堆叠,其中的每一层都是前一层的输出作为下一层的输入。可以用以下方式构建一个序列模型: ``` from keras.models import Sequential from keras.layers import Dense model = Sequential() model.add(Dense(units=32, activation='relu', input_dim=100)) model.add(Dense(units=10, activation='softmax')) ``` 函数式模型可以用于构建更复杂的模型,如多输入和多输出的神经网络。可以用以下方式构建一个函数式模型: ``` from keras.layers import Input, Dense from keras.models import Model # This returns a tensor inputs = Input(shape=(784,)) # a layer instance is callable on a tensor, and returns a tensor x = Dense(64, activation='relu')(inputs) x = Dense(64, activation='relu')(x) predictions = Dense(10, activation='softmax')(x) # This creates a model that includes # the Input layer and three Dense layers model = Model(inputs=inputs, outputs=predictions) model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) ``` 子类化API提供了更加灵活的构建方式,可以通过自定义网络层和模型的方式实现复杂的神经网络。可以用以下方式构建一个子类化API模型: ``` import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers class MyModel(keras.Model): def __init__(self): super(MyModel, self).__init__() self.dense1 = layers.Dense(64, activation='relu') self.dense2 = layers.Dense(64, activation='relu') self.dense3 = layers.Dense(10, activation='softmax') def call(self, inputs): x = self.dense1(inputs) x = self.dense2(x) x = self.dense3(x) return x model = MyModel() model.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[keras.metrics.SparseCategoricalAccuracy()]) ``` 无论采用何种方式搭建神经网络,都需要进行模型的编译和训练。模型的编译需要指定优化器、损失函数和评估指标。模型的训练则需要指定训练集、验证集、批处理大小和训练轮数等参数。可以用以下方式训练和评估模型: ``` history = model.fit(x_train, y_train, batch_size=64, epochs=5, validation_data=(x_val, y_val)) test_scores = model.evaluate(x_test, y_test, verbose=2) print('Test loss:', test_scores[0]) print('Test accuracy:', test_scores[1]) ``` 以上是Keras搭建神经网络的基本流程,需要根据具体问题和数据集的不同进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值