TensorFlow 2.0 快速入门——手把手学会训练模型

目录

文章灵感

快速入门

代码解释

快速入门代码

加载数据集

构建机器学习模型

损失函数和编译模型

训练并评估模型

修改模型以返回概率

运行效果解释

我的修改

保存我们训练好的模型

代码解释

[1. 加载 MNIST 数据集](#1. 加载 MNIST 数据集)

[2. 数据预处理(归一化)](#2. 数据预处理(归一化))

[3. 构建模型](#3. 构建模型)

[4. 编译模型](#4. 编译模型)

[5. 训练模型](#5. 训练模型)

[6. 保存模型](#6. 保存模型)

识别我们本地的图片

代码解释

效果展示

总结


文章灵感

之前写了那么多使用第三方库,比如OpenCV、YOLO等这些已经训练好的模型来实现一些人工智能的代码,我一直在心中有一个蠢蠢欲动的想法,就是我想学会自己训练模型,即使这个想法听起来是很疯狂的吧,也很有难度,在网络上也很少有人系统的教学如何训练这么个模型,所以我的计划是慢慢的看一些模型训练框架的官方文档,慢慢理解。

我计划是使用TensorFlow,他相比与P开头的另一个好东西来说,更加的容易入门,那么话不多说进入正题吧。

快速入门

https://www.tensorflow.org/tutorials/quickstart/beginner?hl=zh-cn

上面的链接就是这篇文章下面代码的灵感来源了,当然由于是国外的网站,访问起来有点难度,我会把他的源码放在下面,并且源码也会放上详细的注释。

代码解释

其实这段代码运行完毕之后是没有任何比较直观的效果的,只有训练过程中的一些例如损失函数之类的数值 ,我们也可以讲讲他们都是干什么的。

快速入门代码

使用Keras API来加载MNIST数据集、构建一个简单的神经网络模型、训练这个模型、评估其性能,并最终将其修改为返回概率而非原始的logits。下面是对每个关键部分的详细讲解:

加载数据集

  • 使用tf.keras.datasets.mnist加载MNIST数据集,这是一个包含手写数字(0-9)的图像集合,每张图像大小为28x28像素。
  • 数据集被分为训练集和测试集,并且图像数据(x_train, x_test)被归一化到0-1之间,这是通过除以255(图像的最大像素值)来实现的。归一化有助于模型的训练。

构建机器学习模型

  • 使用tf.keras.models.Sequential构建了一个顺序模型,该模型通过堆叠层来定义。
  • 第一层是Flatten层,它将图像的二维输入(28x28像素)转换为一维(784像素)的,以便可以被全连接层处理。
  • 接着是两个Dense层,第一个是包含128个节点的隐藏层,使用ReLU激活函数;第二个是输出层,包含10个节点(对应于10个类别),但没有激活函数,因为我们将在计算损失时直接对这些logits应用softmax。
  • 为了防止过拟合,在第一个Dense层后添加了一个Dropout层,它在训练过程中随机丢弃(设置为0)一部分神经元的输出。

损失函数和编译模型

  • 使用tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)定义了损失函数,它适用于多类别分类问题,并假设标签是整数(而不是one-hot编码)。from_logits=True表示模型输出的是logits,而不是概率。
  • 使用model.compile编译模型,指定优化器(这里使用Adam)、损失函数和评估指标(准确率)。

训练并评估模型

  • 使用model.fit训练模型,通过多次迭代(epochs)在训练数据上调整模型的参数。
  • 使用model.evaluate在测试集上评估模型的性能,打印出损失值和准确率。

修改模型以返回概率

  • 创建一个新的tf.keras.Sequential模型probability_model,它将训练好的模型和Softmax层封装在一起。Softmax层将logits转换为概率分布。
  • 现在,当probability_model对输入数据进行预测时,它将返回每个类别的概率,而不是原始的logits。
python 复制代码
'''
    设置 TensorFlow
'''
import tensorflow as tf

'''
    加载数据集
'''
# 加载并准备 MNIST 数据集。将样本数据从整数转换为浮点数:
mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

'''
    构建机器学习模型
'''
# 通过堆叠层来构建 tf.keras.Sequential 模型
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10)
])

# 对于每个样本,模型都会返回一个包含 logits 或 log-odds 分数的向量,每个类一个
predictions = model(x_train[:1]).numpy()
predictions

# tf.nn.softmax 函数将这些 logits 转换为每个类的概率
# 注意:注:可以将 tf.nn.softmax 烘焙到网络最后一层的激活函数中。
# 虽然这可以使模型输出更易解释,但不建议使用这种方式,因为在使用 softmax 输出时不可能为所有模型提供精确且数值稳定的损失计算。
tf.nn.softmax(predictions).numpy()

# 使用 losses.SparseCategoricalCrossentropy 为训练定义损失函数,
# 它会接受 logits 向量和 True 索引,并为每个样本返回一个标量损失
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# 此损失等于 true 类的负对数概率:如果模型确定类正确,则损失为零。

# 这个未经训练的模型给出的概率接近随机(每个类为 1/10),因此初始损失应该接近 -tf.math.log(1/10) ~= 2.3。
loss_fn(y_train[:1], predictions).numpy()

# 在开始训练之前,使用 Keras Model.compile 配置和编译模型。
# 将 optimizer 类设置为 adam,将 loss 设置为我们之前定义的 loss_fn 函数,
# 并通过将 metrics 参数设置为 accuracy 来指定要为模型评估的指标。
model.compile(optimizer='adam',
              loss=loss_fn,
              metrics=['accuracy'])

'''
    训练并评估模型
'''
# 使用 Model.fit 方法调整我们的模型参数并最小化损失
model.fit(x_train, y_train, epochs=5)


# Model.evaluate 方法通常在 "Validation-set" 或 "Test-set" 上检查模型性能
model.evaluate(x_test,  y_test, verbose=2)

# 现在,这个照片分类器的准确度已经达到 98%。想要了解更多,请阅读 TensorFlow 教程。

# 如果您想让模型返回概率,可以封装经过训练的模型,并将 softmax 附加到该模型:
probability_model = tf.keras.Sequential([
  model,
  tf.keras.layers.Softmax()
])
probability_model(x_test[:5])

'''
    总结
    恭喜!您已经利用 Keras API 借助预构建数据集训练了一个机器学习模型。
'''

运行效果解释

正如我刚才所说的结果,这段代码运行完成之后的效果并不明显,输出了一堆数字对吧,我来解释一下。

Epoch 1/5

1875/1875 [==============================] - 3s 1ms/step - loss: 0.2943 - accuracy: 0.9153

Epoch 2/5

1875/1875 [==============================] - 2s 1ms/step - loss: 0.1408 - accuracy: 0.9583

Epoch 3/5

1875/1875 [==============================] - 2s 1ms/step - loss: 0.1051 - accuracy: 0.9678

Epoch 4/5

1875/1875 [==============================] - 2s 1ms/step - loss: 0.0860 - accuracy: 0.9734

Epoch 5/5

1875/1875 [==============================] - 2s 1ms/step - loss: 0.0745 - accuracy: 0.9764

313/313 - 0s - loss: 0.0728 - accuracy: 0.9764 - 405ms/epoch - 1ms/step

Process finished with exit code 0

这是它输出的东西。

  • 我们的模型进行了5个epoch的训练。每个epoch代表整个训练数据集通过模型一次。
  • 每个epoch完成后,都会输出当前epoch的loss(损失值)和accuracy(准确率)。loss值越小,表示模型预测值与实际值之间的差异越小accuracy值越高,表示模型分类的正确率越高。
  • 随着epoch的增加,loss值逐渐降低,accuracy值逐渐提高,这表明模型正在学习并改进其预测能力。
  • 最后一个epoch结束时,模型的loss为0.0745,accuracy为0.9764。这表示模型在训练集上的性能相当不错,能够以很高的准确率进行分类。
  • 随后,模型在测试集(或验证集,这里未明确区分)上的评估结果显示,loss为0.0728,accuracy为0.9764,与训练集上的表现相近,这表明模型可能没有过拟合或仅轻微过拟合。
  • 每个epoch大约需要2秒完成,每个step(即每个数据点的处理)大约需要1毫秒。
  • 在测试集上的评估非常快,只用了405毫秒/epoch,即几乎瞬间完成。

这些就是这些输出的参数的含义,在以后会经常的遇到。




我的修改

为了改善两个事情:

(1)模型我都训练好了,我为什么不保存下来

(2)我想识别一下我本地的文件不可以吗

所以我修改了原先的代码,我们可以首先将模型保存下来如下。

保存我们训练好的模型

代码解释

1. 加载 MNIST 数据集

代码首先通过TensorFlow的Keras API加载MNIST手写数字数据集。MNIST是一个广泛使用的数据集,包含60,000个训练样本和10,000个测试样本,每个样本都是28x28像素的灰度图像,表示一个手写数字(0到9之间)。加载的数据集被分为两部分:训练集(用于训练模型)和测试集(用于评估模型性能)。

2. 数据预处理(归一化)

接下来,代码对训练集和测试集中的图像数据进行归一化处理。归一化是指将图像的像素值从原始的[0, 255]范围缩放到[0, 1]范围。这是因为神经网络在训练过程中,对于较小的输入值通常能够更快地收敛,并且可以提高模型的性能。归一化有助于减少不同特征之间的尺度差异,使得模型能够更有效地学习。

3. 构建模型

然后,代码使用Keras的Sequential API构建了一个简单的神经网络模型。这个模型是一个多层感知机(MLP),由多个全连接层(Dense层)组成。第一层是Flatten层,它将图像的二维形状(28x28)转换为一维(784),因为全连接层需要一维输入。接下来的层是Dense层,包含一定数量的神经元和激活函数(如ReLU),用于学习数据的非线性特征。为了防止过拟合,模型中还包含了一个Dropout层,它在训练过程中随机丢弃一部分神经元的输出。最后,模型以一个没有激活函数的Dense层结束,其输出维度等于类别数(10),因为这是一个多分类问题(0到9的数字)。

4. 编译模型

在训练模型之前,需要编译模型,指定损失函数、优化器和评估指标。对于多分类问题,常用的损失函数是交叉熵损失(这里使用的是SparseCategoricalCrossentropy,因为它适用于多分类且标签是整数的情况)。优化器负责更新模型的权重以最小化损失函数。这里使用了Adam优化器,因为它能够自动调整学习率,适用于大多数情况。评估指标是准确率(accuracy),用于衡量模型在测试集上的性能。

5. 训练模型

使用训练数据对模型进行训练,通过多次迭代(epochs)来更新模型的权重。在每个epoch结束时,可以使用验证集(这里使用的是测试集,尽管在实际应用中通常会保留一个单独的验证集)来评估模型的性能。验证集的结果可以帮助我们了解模型是否过拟合或欠拟合,并相应地调整模型的结构或训练参数。

6. 保存模型

最后,将训练好的模型保存为文件,以便将来使用。这里使用了HDF5格式(文件扩展名为.h5),它是一种用于存储和组织大量数据的文件格式,能够很好地保存Keras模型的结构和权重。保存模型后,可以在需要时重新加载它,进行预测或进一步训练。

python 复制代码
import tensorflow as tf


# 加载 MNIST 数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 归一化,将图像像素值从 0-255 缩放到 0-1
x_train, x_test = x_train / 255.0, x_test / 255.0

# 构建模型
model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),  # 将28x28图像展平为一维
    tf.keras.layers.Dense(128, activation='relu'),  # 全连接层,128个神经元,使用ReLU激活函数
    tf.keras.layers.Dropout(0.2),                   # Dropout层,防止过拟合
    tf.keras.layers.Dense(10)                       # 输出层,10个类别,对应数字0-9
])

# 定义损失函数和优化器
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer='adam', loss=loss_fn, metrics=['accuracy'])

# 训练模型
model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))

# 保存模型为 HDF5 格式文件
model.save('models/my_mnist_model.h5')

运行完毕之后会得到这么个模型文件。

识别我们本地的图片

既然我们的模型已经训练完毕并且成功的保存下来了,那我们为什么不用我们本地的图片来测验一下呢。

代码解释

  1. 加载模型

    使用tf.keras.models.load_model函数从指定路径('models/my_mnist_model.h5')加载预训练的模型。这个模型应该是一个能够识别MNIST手写数字的神经网络。

  2. 创建概率模型

    通过tf.keras.Sequential创建一个新的模型,该模型在加载的模型之后添加了一个Softmax层。这个Softmax层将模型的原始输出(即对数概率)转换为概率分布,使得每个类别的预测概率都在0到1之间,并且所有类别的概率之和为1。然而,需要注意的是,在大多数情况下,如果模型的最后一层没有使用softmax激活函数,但设置了from_logits=True在损失函数中,那么在预测时通常不需要额外添加Softmax层,因为predict方法已经默认返回了概率分布(如果模型是以这种方式训练的)。不过,这里添加Softmax层可能是为了明确展示概率转换的过程或者确保兼容性。

  3. 设置中文字体

    为了在图表中显示中文字符,需要指定一个支持中文的字体文件路径。这里提供了Windows系统的示例路径('C:/Windows/Fonts/simhei.ttf'),并使用了matplotlib.font_manager.FontProperties来加载这个字体。这样,在图表中使用fontproperties=my_font参数就可以显示中文了。

  4. 预测函数

    定义了一个predict_single_image函数,该函数接受一个图像路径作为输入,加载该图像,将其转换为灰度图并调整大小为28x28像素(与MNIST数据集中的图像大小一致),然后将图像数据归一化到0-1之间。之后,将归一化后的图像数据扩展为四维张量(因为模型期望的输入是四维的,通常是[batch_size, height, width, channels]),并使用加载的模型进行预测。最后,使用np.argmax获取概率最高的类别的索引,作为预测结果,并使用Matplotlib显示原始图像和预测结果。

  5. 使用模型进行预测

    调用predict_single_image函数,传入一个包含手写数字图像的路径(在这个例子中是'./imgs/001.jpg'),然后函数将显示该图像以及模型预测的数字。

注意:

  • 'models/my_mnist_model.h5'路径下的模型文件存在且已经完成训练。
  • 'C:/Windows/Fonts/simhei.ttf'是存在的(不然显示不了中文啊┭┮﹏┭┮)
  • './imgs/001.jpg'路径下的图像文件存在,而且图像中包含一个清晰的手写数字。
python 复制代码
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import matplotlib.font_manager as fm  # 导入字体管理器

# 从文件加载模型
loaded_model = tf.keras.models.load_model('models/my_mnist_model.h5')

# 将 softmax 包装进模型以便输出概率
probability_model = tf.keras.Sequential([
    loaded_model,
    tf.keras.layers.Softmax()
])

# 设置中文字体,假设你有 SimHei 字体(黑体),路径可能需要根据系统调整
# 可以将 font_path 设置为你系统中的字体文件路径
# font_path = '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc'  # Linux 示例
font_path = 'C:/Windows/Fonts/simhei.ttf'  # Windows 示例

# 加载中文字体
my_font = fm.FontProperties(fname=font_path)

# 处理图片并进行预测
def predict_single_image(image_path):
    # 加载图片
    img = Image.open(image_path).convert('L')  # 将图片转换为灰度模式 ('L')
    img = img.resize((28, 28))  # 调整图片大小为28x28像素
    img_array = np.array(img) / 255.0  # 将图片像素值归一化到0-1之间

    # 预测图片
    img_array_expanded = np.expand_dims(img_array, 0)  # 扩展为4D张量 (1, 28, 28)
    prediction = probability_model(img_array_expanded)

    predicted_label = np.argmax(prediction)  # 获取预测结果

    # 显示图片和预测结果
    plt.figure(figsize=(3, 3))
    plt.imshow(img_array, cmap='gray')
    plt.title(f"预测值: {predicted_label}", fontproperties=my_font)  # 使用中文字体显示预测结果
    plt.axis('off')  # 不显示坐标轴
    plt.show()

# 使用模型识别指定路径的图片
predict_single_image('./imgs/001.jpg')  # 使用本地图片进行识别

效果展示



总结

入门就是这么个如法,之后我在继续的研究,入门容易坚持难,看我是否可以成功的坚持下来吧,其实最近的事情还是挺多的,唉,加油吧

相关推荐
一点媛艺1 小时前
Kotlin函数由易到难
开发语言·python·kotlin
qzhqbb1 小时前
基于统计方法的语言模型
人工智能·语言模型·easyui
冷眼看人间恩怨2 小时前
【话题讨论】AI大模型重塑软件开发:定义、应用、优势与挑战
人工智能·ai编程·软件开发
2401_883041082 小时前
新锐品牌电商代运营公司都有哪些?
大数据·人工智能
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
_.Switch2 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
AI极客菌3 小时前
Controlnet作者新作IC-light V2:基于FLUX训练,支持处理风格化图像,细节远高于SD1.5。
人工智能·计算机视觉·ai作画·stable diffusion·aigc·flux·人工智能作画
阿_旭3 小时前
一文读懂| 自注意力与交叉注意力机制在计算机视觉中作用与基本原理
人工智能·深度学习·计算机视觉·cross-attention·self-attention
王哈哈^_^3 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
测开小菜鸟4 小时前
使用python向钉钉群聊发送消息
java·python·钉钉