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

目录

文章灵感

快速入门

代码解释        

快速入门代码

加载数据集

构建机器学习模型

损失函数和编译模型

训练并评估模型

修改模型以返回概率

运行效果解释

我的修改

保存我们训练好的模型

代码解释

1. 加载 MNIST 数据集

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

3. 构建模型

4. 编译模型

5. 训练模型

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_trainx_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。
'''
    设置 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模型的结构和权重。保存模型后,可以在需要时重新加载它,进行预测或进一步训练。

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'路径下的图像文件存在,而且图像中包含一个清晰的手写数字。
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')  # 使用本地图片进行识别

效果展示



总结

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

好的,可以使用PyTorch中的`nn.Module`来创建模型,并使用`nn.CrossEntropyLoss`作为损失函数。在训练过程中,我们可以使用PyTorch提供的`nn.utils`来计算每个epoch的平均loss和accuracy,并绘制图形。以下是一个示例代码: ```python import torch import torch.nn as nn import torch.optim as optim import torch.utils.data as data import torchvision.datasets as datasets import torchvision.transforms as transforms import matplotlib.pyplot as plt # 定义模型 class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.fc1 = nn.Linear(784, 512) self.fc2 = nn.Linear(512, 10) def forward(self, x): x = x.view(-1, 784) x = self.fc1(x) x = nn.functional.relu(x) x = self.fc2(x) return x # 加载数据集 train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transforms.ToTensor()) train_loader = data.DataLoader(train_dataset, batch_size=64, shuffle=True) # 定义模型、损失函数、优化器 model = MyModel() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.01) # 训练模型 epochs = 10 loss_list = [] acc_list = [] for epoch in range(epochs): running_loss = 0.0 running_corrects = 0 for inputs, labels in train_loader: optimizer.zero_grad() outputs = model(inputs) _, preds = torch.max(outputs, 1) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / len(train_dataset) epoch_acc = running_corrects.double() / len(train_dataset) loss_list.append(epoch_loss) acc_list.append(epoch_acc) print('Epoch [{}/{}], Loss: {:.4f}, Acc: {:.4f}'.format(epoch+1, epochs, epoch_loss, epoch_acc)) # 绘制loss和accuracy图像 plt.plot(range(epochs), loss_list) plt.xlabel('Epochs') plt.ylabel('Loss') plt.title('Training Loss') plt.show() plt.plot(range(epochs), acc_list) plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.title('Training Accuracy') plt.show() ``` 输出结果可能类似于: ``` Epoch [1/10], Loss: 1.3121, Acc: 0.6596 Epoch [2/10], Loss: 0.6635, Acc: 0.8314 Epoch [3/10], Loss: 0.5197, Acc: 0.8645 Epoch [4/10], Loss: 0.4566, Acc: 0.8807 Epoch [5/10], Loss: 0.4193, Acc: 0.8903 Epoch [6/10], Loss: 0.3947, Acc: 0.8964 Epoch [7/10], Loss: 0.3774, Acc: 0.9013 Epoch [8/10], Loss: 0.3637, Acc: 0.9049 Epoch [9/10], Loss: 0.3524, Acc: 0.9084 Epoch [10/10], Loss: 0.3429, Acc: 0.9112 ``` 并且会弹出两个图形窗口,分别显示训练loss和accuracy的变化趋势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值