本文演示的 TensorFlow 和 Keras 版本如下:
import platform
import tensorflow as tf
2025-01-06 00:36:45.843367: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations. To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
print(f"Python 的版本:{platform.python_version()}")
print(f"TensorFlow 的版本:{tf.__version__}")
print(f"Keras 的版本:{tf.keras.__version__}")
Python 的版本:3.12.8 TensorFlow 的版本:2.18.0 Keras 的版本:3.7.0
深度学习是机器学习的一个重要分支,也是人工智能的一部分。与其他技术一样,深度学习旨在解决问题。本文以鸢尾花分类问题为背景,演示如何使用 Keras 快速构建一个神经网络,并利用 Keras 提供的接口方法,基于数据训练一个鸢尾花分类模型,尝试解决鸢尾花分类问题。
鸢尾花有非常多的种类,相关专家能识别 300 多个花种。在这里,我们简化一下问题,仅识别以下三种:
从左至右依次为,Iris setosa (by Radomil, CC BY-SA 3.0),Iris versicolor (by Dlanglois, CC BY-SA 3.0) 和 Iris virginica (by Frank Mayfield,CC BY-SA 2.0)。
看到这三张不同鸢尾花的示例图,在考虑鸢尾花分类问题时,你首先可能注意到它们的颜色存在明显区别,应该尝试用计算机视觉相关技术来解决。仔细观察,除了颜色以外,它们的形状也存在差异。例如,我们测量了如下 5 个样本的花萼和花瓣的长宽,并标记了它们对应的花种。
花萼长度 | 花萼宽度 | 花瓣长度 | 花瓣宽度 | 种属 |
---|---|---|---|---|
6.4 | 2.8 | 5.6 | 2.2 | virginica |
5.0 | 2.3 | 3.3 | 1.0 | versicolor |
4.9 | 2.5 | 4.5 | 1.7 | virginica |
4.9 | 3.1 | 1.5 | 0.1 | setosa |
5.7 | 3.8 | 1.7 | 0.3 | setosa |
能否仅依靠这四个特征进行鸢尾花分类?这是机器学习领域的经典案例。你可以尝试运用各种机器学习模型和技术来解决这个问题,以观察哪种方案能更准确地进行分类。这个过程的本质是找到一个函数 y=f(X),它接受四个特征作为输入,并输出三个类别中的一个。显然,你可以通过观察数据并从中总结规律来手动编写这样一个函数,但可以想象,仅凭人工编写是相当困难的,最终的分类精度可能也不会很高。然而,进行数据间的计算是机器擅长的领域,希望能设计一个算法让机器通过数据间的各种计算来发现并总结出一个最优的分类函数,这个过程就是机器学习,而相应的算法即为机器学习算法。
进行机器学习首先需要数据。作为经典案例,sklearn 已经整理并收录了这个数据集,我们可以非常方便地使用。
from sklearn import datasets
iris = datasets.load_iris()
type(iris).mro()
[sklearn.utils._bunch.Bunch, dict, object]
iris.keys()
dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module'])
X = iris.data
y = iris.target
print(f"X 的类型:{type(X)}, 形状:{X.shape}")
print(f"y 的类型:{type(y)}, 形状:{y.shape}")
X 的类型:<class'numpy.ndarray'>, 形状:(150, 4) y 的类型:<class'numpy.ndarray'>, 形状:(150,)
X[:4]
array([[5.1, 3.5, 1.4, 0.2], [4.9, 3. , 1.4, 0.2], [4.7, 3.2, 1.3, 0.2], [4.6, 3.1, 1.5, 0.2]])
y[:4]
array([0, 0, 0, 0])
将数据集划分为训练集和测试集。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
from tensorflow.keras import (
activations,
layers,
losses,
metrics,
models,
optimizers,
utils,
)
Keras 的核心数据结构是模型 (model),它是一种组织网络层的方式。最简单的模型是顺序模型 (Sequential),它由多个网络层线性堆叠而成。对于更复杂的结构,你可以使用 Keras 的函数式 API (Functional API),它允许构建任意的神经网络图。
你可以使用 Sequential 的成员函数 .add()
来堆叠模型,也可以在构造函数中传入一个模型层的列表。
model = models.Sequential(
[
layers.Input(shape=(4,)),
layers.Dense(units=32, activation=activations.relu),
layers.Dense(units=16, activation=activations.relu),
layers.Dense(units=3, activation=activations.softmax),
]
)
model.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param# ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ dense (Dense) │ (None, 32) │ 160 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 16) │ 528 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_2 (Dense) │ (None, 3) │ 51 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 739 (2.89 KB)
Trainable params: 739 (2.89 KB)
Non-trainable params: 0 (0.00 B)
tf.keras.utils.plot_model(
model,
show_shapes=True,
show_dtype=True,
show_layer_names=True,
show_layer_activations=True,
show_trainable=True,
)
在完成模型构建后,可以使用 .compile()
方法来配置学习过程。Keras 的核心原则是简化操作,你可以轻松地使用其默认配置,同时在需要时也能进行完全控制。
model.compile(
loss=losses.sparse_categorical_crossentropy,
optimizer=optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True),
metrics=[metrics.SparseCategoricalAccuracy()],
)
接下来,就可以在训练数据上进行批量迭代了。X_train
和 y_train
是 Numpy 数组,就像在 sklearn API 中一样。
model.fit(X_train, y_train, epochs=10, batch_size=100)
Epoch 1/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 28ms/step - loss: 1.3816 - sparse_categorical_accuracy: 0.3489 Epoch 2/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step - loss: 1.1300 - sparse_categorical_accuracy: 0.0489 Epoch 3/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step - loss: 1.0662 - sparse_categorical_accuracy: 0.3000 Epoch 4/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 22ms/step - loss: 1.0135 - sparse_categorical_accuracy: 0.3300 Epoch 5/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.9400 - sparse_categorical_accuracy: 0.3900 Epoch 6/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step - loss: 0.8764 - sparse_categorical_accuracy: 0.5967 Epoch 7/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step - loss: 0.8237 - sparse_categorical_accuracy: 0.6689 Epoch 8/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step - loss: 0.7782 - sparse_categorical_accuracy: 0.7011 Epoch 9/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step - loss: 0.7269 - sparse_categorical_accuracy: 0.7722 Epoch 10/10 2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 0.6720 - sparse_categorical_accuracy: 0.7411
<keras.src.callbacks.history.History at 0x7fc78ea89100>
在测试集上评估模型性能,输出 loss 和 accuracy。
model.evaluate(X_test, y_test)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 211ms/step - loss: 0.6290 - sparse_categorical_accuracy: 0.8333
[0.628977358341217, 0.8333333134651184]
对新数据进行预测时,每个样本的输出是三个浮点数,它们的和正好等于 1。每个数值可以近似理解为该类别的概率。因此,最大值的索引即为模型预测的花的种类。
y_hat = model.predict(X_test)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 85ms/step
y_hat[:5]
array([[0.10919946, 0.46215808, 0.42864245], [0.7395903, 0.1207026, 0.139707 ], [0.05714067, 0.3672582, 0.57560116], [0.12168241, 0.47635025, 0.40196735], [0.11827871, 0.4687825, 0.41293886]], dtype=float32)
import numpy as np
np.argmax(y_hat[:5], axis=1)
array([1, 0, 2, 1, 1])
y_test[:5]
array([1, 0, 2, 1, 1])
可以看到,对于测试集的前 5 个样本,预测的分类与人工标注的分类有 5 个是一致的,也就是说,对于这 5 个样本,模型预测的准确率为 1.0,与整体的 0.83 比较接近。