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

本文是Keras初学者指南,通过构建一个前馈神经网络解决MNIST手写数字分类问题。首先介绍Keras库和数据预处理,接着搭建包含ReLU激活函数和Softmax输出层的神经网络模型,然后进行模型编译和训练,最后评估模型并在测试集上展示效果。在训练过程中,探讨了超参数调整、激活函数选择、防止过拟合等概念。
摘要由CSDN通过智能技术生成

优秀外文博文搬运 + 翻译

原作者: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: 在 TensorFlow 中导入 MNIST 数据集的方法如下: 1. 首先需要安装 TensorFlow 和 Numpy,可以使用 `pip install tensorflow numpy` 安装。 2. 导入 TensorFlow 和 Numpy,并使用 TensorFlowkeras 模块加载 MNIST 数据集。 ```python import tensorflow as tf (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() ``` 3. 可以使用 `x_train`,`y_train`,`x_test` 和 `y_test` 变量来访问 MNIST 数据集中的训练数据和测试数据。 注意:如果你使用的版本是 Tensorflow 2.x以上,使用tf.keras.datasets.mnist.load_data()会报错,需要使用tf.keras.datasets.mnist.load_data(path='mnist.npz') ### 回答2: TensorFlow 是谷歌推出的一个开源机器学习框架,其具有良好的库函数、高效的计算能力、灵活可扩展、易于调试及部署等优点,是当前深度学习领域中应用广泛的框架之一。在 TensorFlow 中,MNIST 数据集是非常重要的一个标准数据集,因此学习如何导入 MNIST 数据集是很有必要的。 MNIST 数据集是一个手写数字数据集,包含 10 个类别,每个类别包含大约 7,000 个不同大小的灰度图像。在 TensorFlow 中,MNIST 数据集是有专门的模块进行处理的,可以方便地读取和使用。 在 TensorFlow 中导入 MNIST 数据集有多种方法,其中比较常见的有以下两种: 1. 使用 TensorFlow 内置的模块导入 MNIST 数据集 通过以下代码可以使用 TensorFlow 内置的模块直接导入 MNIST 数据集: ``` import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) ``` 其中,read_data_sets 函数的第一个参数指定 MNIST 数据集的存储路径,one_hot=True 表示采用 One-Hot 编码将标签转换为独热向量形式。使用后,我们可以像访问字典一样访问 MNIST 数据集。 2. 使用 Python 的第三方库 scikit-learn 导入 MNIST 数据集 使用 scikit-learn 导入 MNIST 数据集的代码如下: ``` from sklearn.datasets import fetch_mldata mnist = fetch_mldata('MNIST original', data_home='./') ``` 其中,fetch_mldata 函数的第一个参数指定要加载的数据集名称,第二个参数指定存储路径。使用 after_import() 函数可以访问 MNIST 数据集。 总的来说,在 TensorFlow 中导入 MNIST 数据集非常方便,直接使用内置的模块就可以轻松导入,使用 Python 的第三方库也可以实现数据的导入。通过导入 MNIST 数据集,我们可以更好地了解 TensorFlow 在深度学习领域的应用。 ### 回答3: TensorFlow是一个广泛使用的开源机器学习框架,可用于各种任务,如分类、回归、聚类等。其中,MNIST数据集是一个常用的手写数字识别数据集,它包含了一系列的灰度图像和相应的标签。 TensorFlow支持导入MNIST数据集并对其进行预处理。下面介绍如何导入MNIST数据集: 首先,需要从TensorFlow中导入MNIST数据集。可以通过以下代码来完成: ``` from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) ``` 这将从MNIST_data文件夹中下载MNIST数据集,并将其分成三个部分:训练集、验证集和测试集。其中,训练集用于训练模型,验证集用于调整模型的超参数(如学习率、批大小等),测试集用于评估模型的性能。 接着,可以使用以下代码来检查数据的维度和大小: ``` print("Training data shape: {}".format(mnist.train.images.shape)) print("Training label shape: {}".format(mnist.train.labels.shape)) print("Validation data shape: {}".format(mnist.validation.images.shape)) print("Validation label shape: {}".format(mnist.validation.labels.shape)) print("Test data shape: {}".format(mnist.test.images.shape)) print("Test label shape: {}".format(mnist.test.labels.shape)) ``` 输出结果应该是: ``` Training data shape: (55000, 784) Training label shape: (55000, 10) Validation data shape: (5000, 784) Validation label shape: (5000, 10) Test data shape: (10000, 784) Test label shape: (10000, 10) ``` 其中,784是因为每张图像是28x28的,所以需要将其展平成一个784维的向量。而标签是一个10维的one-hot向量,表示该图像属于哪个数字类别。 最后,可以使用以下代码来可视化MNIST数据集中的一些样本: ``` import matplotlib.pyplot as plt # Plot the first 10 images for i in range(10): plt.subplot(2, 5, i+1) plt.imshow(mnist.train.images[i].reshape(28, 28), cmap='gray') plt.title('Label: {}'.format(mnist.train.labels[i])) plt.show() ``` 这将绘制前10个训练图像。 通过这些步骤,我们可以轻松地导入、预处理和可视化MNIST数据集,为机器学习任务打下坚实的基础
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值