导入 Fashion MNIST 数据集
该数据集包含 10 个类别的 70,000 个灰度图像。这些图像以低分辨率(28x28 像素)展示了单件衣物
我们将使用 60,000 张图像来训练网络,使用 10,000 张图像来评估网络学习对图像进行分类的准确程度。可以直接从 TensorFlow 中访问 Fashion MNIST。直接从 TensorFlow 中导入和加载 Fashion MNIST 数据:
import tensorflow as tf
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
加载数据集会返回四个 NumPy 数组:
train_images
和train_labels
数组是训练集,即模型用于学习的数据。- 测试集、
test_images
和test_labels
数组会被用来对模型进行测试。
图像是 28x28 的 NumPy 数组,像素值介于 0 到 255 之间。标签是整数数组,介于 0 到 9 之间。这些标签对应于图像所代表的服装类:
标签 | 类 |
---|---|
0 | T恤/上衣 |
1 | 裤子 |
2 | 套头衫 |
3 | 连衣裙 |
4 | 外套 |
5 | 凉鞋 |
6 | 衬衫 |
7 | 运动鞋 |
8 | 包 |
9 | 短靴 |
每个图像都会被映射到一个标签。由于数据集不包括类名称,请将它们存储在下方,供稍后绘制图像时使用:
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
数据预处理
将这些值缩小至 0 到 1 之间,然后将其馈送到神经网络模型。为此,请将这些值除以 255。
train_images = train_images / 255.0
test_images = test_images / 255.0
搭建模型
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(units=128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(units=10)
]
)//注意这个结构是再列表中的。
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_images,train_labels,epochs=10,verbose=2)
print('\nTest accuracy:', test_acc)
Epoch 1/10
1875/1875 - 1s - loss: 0.5296 - accuracy: 0.8119
Epoch 2/10
1875/1875 - 1s - loss: 0.3999 - accuracy: 0.8551
Epoch 3/10
1875/1875 - 1s - loss: 0.3669 - accuracy: 0.8666
Epoch 4/10
1875/1875 - 1s - loss: 0.3422 - accuracy: 0.8753
Epoch 5/10
1875/1875 - 1s - loss: 0.3298 - accuracy: 0.8786
Epoch 6/10
1875/1875 - 1s - loss: 0.3159 - accuracy: 0.8824
Epoch 7/10
1875/1875 - 1s - loss: 0.3059 - accuracy: 0.8868
Epoch 8/10
1875/1875 - 1s - loss: 0.2982 - accuracy: 0.8893
Epoch 9/10
1875/1875 - 1s - loss: 0.2878 - accuracy: 0.8922
Epoch 10/10
RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd
RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd
RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd
1875/1875 - 1s - loss: 0.2815 - accuracy: 0.8943
313/313 - 0s - loss: 0.3346 - accuracy: 0.8813Test accuracy: 0.8812999725341797
进行预测
模型经过训练后,可以使用它对一些图像进行预测。附加一个 Softmax 层,将模型的线性输出 logits 转换成更容易理解的概率。
probability_model = tf.keras.Sequential([model, tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)
原理
假设有一个深度学习的图像识别任务,目标是将图像分类为不同的类别。在这个任务中,使用了一个预训练的卷积神经网络模型(CNN),该模型是全球图像识别挑战赛(ImageNet)的冠军模型,被称为“InceptionNet”。
在一个新的图像数据集上训练了此模型,并在训练完成后对其进行了评估。随后,想要使用这个模型对新来的测试图像进行识别。
问题点:
可能会发现原始模型的输出不是概率,而是一些未归一化的分数,通常被称为 logits。为了使得模型的输出易读(比如,可以直接用最大概率来做决策),需要将这些分数转换为概率分布。
解决方案:
可以使用 tf.keras.layers.Softmax()
层来将模型的输出转换为概率分布。当调用 model.predict()
时,模型会输出一个分数矩阵(一个形状为 (batch_size, num_classes)
的数组),这些分数是模型对于每一个图像类别输出的分数。但是这些分数并不是概率。
这里是如何应用 Softmax
层:
# 首先假设 model 是你的预训练模型
original_model_output = model.predict(test_images)
# 查看 original_model_output 的形状,应该是 (batch_size, num_classes)
print("Original model output shape:", original_model_output.shape)
# 将 Softmax 层添加到 Sequential 模型中,以创建一个新的概率模型
probability_model = tf.keras.Sequential([
model,
tf.keras.layers.Softmax()
])
# 使用新的模型进行预测
probability_model_output = probability_model.predict(test_images)
# 查看新的概率模型的输出形状,应该是 (batch_size, num_classes)
print("Probability model output shape:", probability_model_output.shape)
在这个场景中,probability_model_output
现在是一个概率分布,每个元素的值加起来等于1,你可以直接用来做决策。例如,你可以通过查找最大概率来预测一个图像的类别:
# 获取最大概率的类别
predicted_class = np.argmax(probability_model_output, axis=1)
总结:
在这个场景中,我们通过在最有可能已经训练好的模型后面增加一个 Softmax
层,创建了一个新的 probability_model
。这个新的模型可以直接用来进行新的图像识别任务,而不需要重新编译,因为在预测过程中并不需要了解模型的训练设置(如损失函数、优化器等)。这使得 probability_model
非常适合实际应用中快速预测新的数据。
注意:只是用Softmax计算概率不需要重新编译。
在深度学习和机器学习中,(batch_size, num_classes)
是表示模型输出的数组(张量)的形状。这里是对这两个维度的详细解释:
1. batch_size
(批次大小)
- 定义:
batch_size
表示每次训练或预测时输入数据的样本数量。一个批次包含的样本数即为批次大小。 - 作用:在训练过程中,深度学习模型通常不会一次处理所有训练样本,而是将数据分成多个批次进行处理。这样做的好处包括减少内存使用和加快训练速度。批次大小通常是一个可以调节的超参数,取决于硬件资源和模型的需要。
- 举例:如果有一个数据集包含 1000 张图像,并且设置
batch_size=32
,那么每次训练时,模型将处理 32 张图像作为一个批次。
2. num_classes
(类别数)
- 定义:
num_classes
表示模型的输出类别数,即模型可以预测的不同类别的数量。这通常对应于分类任务中的目标类别。 - 作用:在分类任务中,模型的最终输出层通常有
num_classes
个神经元,每个神经元输出一个类别的分数或概率。通过这些分数或概率,模型可以预测输入样本的类别。 - 举例:如果在进行手写数字识别任务(例如 MNIST 数据集),你会有 10 个类别(数字 0 到 9),因此
num_classes
将是 10。
输出形状示例
假设你在进行图像分类任务,模型的输出是一个形状为 (batch_size, num_classes)
的数组。下面是对这一形状的具体解释:
-
预测的输出:每一行代表一个样本的分类分数或概率。每一列代表一个类别的分数或概率。
- 如果
batch_size=32
和num_classes=10
,那么模型的输出形状将是(32, 10)
。 - 这表示模型一次处理 32 张图像,并为每张图像预测 10 个类别中的每一个的分数或概率。
- 如果
实际应用示例
假设你用一个模型进行预测:
# 假设模型的输出
output = model.predict(test_images)
# 输出形状为 (batch_size, num_classes)
print("Output shape:", output.shape)
如果 test_images
的 batch_size
为 64,且模型有 5 个类别,那么 output.shape
将是 (64, 5)
。这意味着模型为 64 张图像生成了 64 行输出,每行有 5 个类别的分数或概率。
总结
batch_size
:每次处理的样本数量。num_classes
:模型可以预测的不同类别的数量。
是否需要重新编译
假设使用一个预训练好的模型进行了微调训练(fine-tuning),并且现在希望重新编译概率模型以进行另一个评估步骤。下面是一个例子:
import tensorflow as tf
from tensorflow.keras.applications import InceptionV3
# 假设有一个数据集名为 "data",它包含了 "x_train", "y_train", "x_test", 和 "y_test"
# 定义原始模型
base_model = InceptionV3(include_top=False, input_shape=(299, 299, 3), weights='imagenet')
# 冻结住InceptionV3模型的权重以进行微调训练
for layer in base_model.layers:
layer.trainable = False
# 在模型后添加自定义层
# 这里我们使用 Dense 层和 Softmax 层
top_model = tf.keras.Sequential([
tf.keras.layers.GlobalAveragePooling2D(input_shape=base_model.output_shape[1:]),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(2, activation='softmax') # 假设数据集有两个类别
])
# 创建一个完全的新模型,它包括所有已有层
# 假设已经对 "base_model" 完成了微调训练
# 现在需要重新编译概率模型以进行评估
probability_model = tf.keras.Sequential([
base_model,
top_model
])
# 重新编译模型
probability_model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 进行评估
eval_results = probability_model.evaluate(x_test, y_test, verbose=2)
print(f'Test accuracy: {eval_results[1]*100:.2f}%')
在这个例子中,我们首先加载了一个预训练的 InceptionV3 模型,然后冻结了所有层以进行微调训练。之后,我们在模型上添加了一个自定义的顶层模型。因为我们之前对 base_model
进行了训练和编译,我们在添加 Softmax
层之后决定重新编译整个模型以进行评估。
在这种情况下,compile
方法是必须的,因为我们需要在新的层上指定优化器、损失函数和评估指标。如果不重新编译,权重将不会在评估中得到更新,而且如果我们改变了损失函数或者评估指标,则无法获得正确的评估结果。
请注意,上面的代码片段假设了你已经有了一个训练好的 base_model
。在这个示例中,我们通过 tf.keras.Sequential
创建了 probability_model
,它包含了完整的 base_model
和顶部的新自定义层。通过重新编译 probability_model
,我们可以使用新的评估指标和损失函数,并在新的层上应用优化器。
编译和训练的区别
在深度学习中,model.compile
和 model.fit
是两个重要的步骤,它们在模型的训练和评估中各自扮演着不同的角色。下面是对这两个方法的详细解释,以及它们在实际应用中的区别和作用。
model.compile
作用:model.compile
用于配置模型的学习过程,它设置了训练模型时所需的优化器、损失函数和评估指标。这一步骤必须在训练之前完成,因为它指定了如何优化模型的权重以及如何评估模型的表现。
参数:
- optimizer:指定用于优化模型的算法。例如,
'adam'
、'sgd'
(随机梯度下降)等。 - loss:定义模型的损失函数,用于衡量模型预测值与实际值之间的差距。例如,
'sparse_categorical_crossentropy'
(适用于分类任务)或mse
(均方误差,适用于回归任务)。 - metrics:用于评估模型性能的指标。例如,
'accuracy'
、'precision'
等。
情景示例:
假设在做图像分类任务,有一个卷积神经网络(CNN),已经定义好了模型结构。接下来,需要配置模型以准备好进行训练。需要选择优化器、损失函数和评估指标。这时候,会调用 model.compile
:
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
这段代码完成了模型配置:
- 优化器:
'adam'
是一种常用的自适应学习率优化器。 - 损失函数:
'sparse_categorical_crossentropy'
适用于多类别分类问题,其中标签是整数编码。 - 评估指标:
'accuracy'
用于计算分类准确率。
model.fit
作用:model.fit
用于实际训练模型,它将输入数据(特征和标签)喂给模型,并通过最小化损失函数来更新模型的权重。这个过程涉及多次遍历训练数据(称为 epochs),每次遍历更新模型的权重,以逐步优化模型的表现。
参数:
- x:训练数据的特征(输入)。
- y:训练数据的标签(目标)。
- epochs:训练的轮数,即模型遍历整个训练数据集的次数。
- batch_size:每次训练时使用的样本数,决定了模型每次权重更新的频率。
- validation_data:用于验证模型性能的数据集(可选)。
情景示例:
在配置好模型之后,可以使用 model.fit
进行训练。这一步将数据输入到模型中并优化模型的权重。假设有训练数据 x_train
和标签 y_train
,并且希望训练 10 个 epochs,会这样做:
history = model.fit(x_train, y_train,
epochs=10,
batch_size=32,
validation_data=(x_val, y_val))
这段代码将:
- 训练数据:
x_train
和y_train
是模型的输入数据和目标标签。 - epochs:指定训练轮数为 10 次。
- batch_size:每次训练使用 32 个样本。
- validation_data:提供一个验证数据集
x_val
和y_val
,用于在每个 epoch 结束时评估模型的性能,帮助监控模型的泛化能力。
总结
-
model.compile
:- 作用:配置模型的学习过程。
- 何时使用:在训练模型之前,设置优化器、损失函数和评估指标。
-
model.fit
:- 作用:实际训练模型,更新模型的权重。
- 何时使用:在模型配置好后,用于训练模型并优化权重。
Sequential
在 TensorFlow 中,可以通过在模型后面加一个 Softmax
层来创建一个概率模型,这样可以将模型的输出转换为概率分布。使用 tf.keras.Sequential
创建包含 Softmax
层的概率模型是一种常见的做法,但需要注意几个事项:
1. 模型结构和输出
- 模型输出:原始模型的输出是 logits(未归一化的得分),即没有经过 softmax 处理的原始分数。
- Softmax 层:将 logits 转换为概率分布。如果模型最后的输出层已经是 softmax,那么在其后再加一个 softmax 层就没有必要,因为这样可能会导致数值不稳定或者不必要的计算开销。
2. 代码示例
假设有一个已经训练好的模型 model
,并且这个模型的输出是 logits。可以这样创建一个概率模型:
import tensorflow as tf
# 假设 model 是训练好的模型
# 创建一个新模型,在原有模型后添加 Softmax 层
probability_model = tf.keras.Sequential([
model,
tf.keras.layers.Softmax()
])
# 编译新的模型
probability_model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
3. 注意事项
-
输出层是否已经包含 Softmax: 确保原始模型的最后一层不是 softmax。如果原始模型的最后一层是 softmax,那么加一个 softmax 层将导致重复计算,结果不正确。
-
使用
model
的.predict()
方法: 如果已经有一个训练好的模型model
,可以直接使用model.predict()
来得到概率分布(前提是原始模型的最后一层是 softmax 或类似的激活函数)。# 使用原始模型进行预测 predictions = model.predict(x_test) # 这里的 predictions 已经是概率分布
-
训练和评估: 如果想要对
probability_model
进行训练或评估,确保数据格式和标签与模型的要求一致。
4. 实际应用
示例:在使用 model
作为特征提取器时
# 假设原始模型用于提取特征
feature_extractor = tf.keras.Model(inputs=model.input, outputs=model.output)
# 在特征提取器后添加 Softmax 层
probability_model = tf.keras.Sequential([
feature_extractor,
tf.keras.layers.Softmax()
])
# 现在可以使用 probability_model 进行预测
predictions = probability_model.predict(x_test)
总结来说,在模型的后面加一个 Softmax
层可以实现将 logits 转换为概率分布,但要确保原始模型的输出是未归一化的 logits,并且原始模型的最后一层没有使用 softmax。如果原始模型已经包含 softmax,则直接使用原始模型的输出即可。
全部代码
import tensorflow as tf
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
train_images = train_images / 255.0
test_images = test_images / 255.0
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(units=128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(units=10)
]
)
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_images,train_labels,epochs=10,verbose=2)
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
probability_model = tf.keras.Sequential([model,tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)
def plot_image(i, predictions_array, true_label, img):
true_label, img = true_label[i], img[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.imshow(img, cmap=plt.cm.binary)
predicted_label = np.argmax(predictions_array)
if predicted_label == true_label:
color = 'blue'
else:
color = 'red'
plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
100*np.max(predictions_array),
class_names[true_label]),
color=color)
def plot_value_array(i, predictions_array, true_label):
true_label = true_label[i]
plt.grid(False)
plt.xticks(range(10))
plt.yticks([])
thisplot = plt.bar(range(10), predictions_array, color="#777777")
plt.ylim([0, 1])
predicted_label = np.argmax(predictions_array)
thisplot[predicted_label].set_color('red')
thisplot[true_label].set_color('blue')
i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i], test_labels)
plt.show()
i = 12
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i], test_labels)
plt.show()
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
plt.subplot(num_rows, 2*num_cols, 2*i+1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(num_rows, 2*num_cols, 2*i+2)
plot_value_array(i, predictions[i], test_labels)
plt.tight_layout()
plt.show()