大多数Keras教程都尝试使用图像分类数据集(如MNIST(手写识别)或基本对象CIFAR-10(基本对象识别))来开启Keras库的基础知识学习。
这篇文章将对Keras入门教程进行不同的尝试。使用自定义数据集训练第一个神经网络和卷积神经网络——将深度学习应用您自己的数据集,而不是Keras内置的数据集。
使用Keras训练您的第一个简单神经网络不需要很多代码,但是我们将逐步开始,逐步进行,以确保您了解如何在自己的自定义数据集上训练网络的过程。
今天介绍的步骤包括:
1. 安装Keras和其依赖库
2. 从磁盘加载数据
3. 训练和测试数据分组
4. 定义模型
5. 编译模型
6. 训练模型
7. 评估模型
8. 使用训练的模型进行预测
这看起来似乎需要很多步骤,但是我向您保证,一旦我们开始使用示例,您将看到示例是线性的、直观的,并且将帮助您了解使用Keras训练神经网络的基础知识。
实质上,使用MINST或者CIFARr10训练集相当于只是调用了内置的函数,训练、测试数据分组创建模型等都已预定义好,当我们尝试使用自己的数据集时,我们可能会抓耳挠腮考虑下边几个问题:
这些辅助函数从何处加载数据?磁盘上的数据集应采用什么格式?如何将数据集加载到内存中?我需要执行哪些预处理步骤?
在此Keras教程中,我们将使用名为“动物数据集”的自定义数据集。
1. 此数据集的目的是将图像正确分类: 猫 小狗 大熊猫
使用此自定义数据集可以使您了解:
1. 如何在磁盘上组织数据集
2. 如何从磁盘加载图像和分类标签
3. 如何将数据划分为训练和测试数据
4. 如何在训练数据上训练您的第一个Keras神经网络
5. 如何根据测试数据评估模型
6. 如何在训练和测试划分之外的全新数据上复用训练过的模型
按照本Keras教程中的步骤进行操作,只要您使用下面详述的项目/目录结构,就可以将“动物”数据集换成您选择的任何数据集。
抓取数据集可参考:
Bing ImagesGoogle Images
2. 项目结构
cats、dogs、panda文件夹下文件数至少1000个,并且相同。某个文件夹下文件多可能导致训练出的模型有偏差;
3. 开始写我们的代码
(1)配置环境
ubuntu上 TensorFlow2.0安装:
windows上 TensorFlow2.0安装:
pip install tensorflow==2.0.0 # or tensorflow-gpu==2.0.0
(2)简单神经网络模型及卷积神经网络模型
train_simple_nn.py 第一个简单神经网络的模型train_vgg.py 更完整的SimpleVGGNet的卷积网络模型# train_simple_nn.py# 训练第一个简单的 Keras 模型# Python的必备绘图包 我们指定matplotlib 使用“ Agg” 后使我们能够将绘图保存到磁盘import matplotlibmatplotlib.use("Agg")# 导入必要的模块包# scikit-learn库将帮助我们对标签进行二值化处理,拆分数据以进行训练/测试以及在终端生成训练报告from sklearn.preprocessing import LabelBinarizerfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_report# 您正在阅读本教程以了解Keras,它是TensorFlow和其他深度学习后端的高级前端, Keras并入了TensorFlowfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Densefrom tensorflow.keras.optimizers import SGD# imutils 便利功能包 我们将使用paths 生成用于训练的图像文件路径列表from imutils import pathsimport matplotlib.pyplot as plt# numpy Python中进行数值处理的包 必备包之一import numpy as np# argparse 模块是内置于Python中的模块,将动态处理命令行提供的信息import argparseimport randomimport pickleimport cv2import os# 1. 构建命令行参数# --dataset 磁盘上图像数据集的路径# --model训练得到的模型的输出路径# --label-bin 序列化的二进制标签文件的输出路径# --plot输出训练图(精确度、损失)图像文件的路径,我们将通过该图判断模型拟合是否 过度/不足ap = argparse.ArgumentParser()ap.add_argument("-d", "--dataset", required=True,help="path to input dataset of images")ap.add_argument("-m", "--model", required=True,help="path to output trained model")ap.add_argument("-l", "--label-bin", required=True,help="path to output label binarizer")ap.add_argument("-p", "--plot", required=True,help="path to output accuracy/loss plot")args = vars(ap.parse_args())# 2. 从磁盘加载数据print("[INFO] loading images...")# 初始化 数据和分类标签列表data = []labels = []# 抓取imagePaths 并随机洗牌(randomly shuffle)# paths.list_images() 很方便的返回路径中的所有图片列表 播下种子因此随机重排序是可重现的。imagePaths = sorted(list(paths.list_images(args["dataset"])))random.seed(42)random.shuffle(imagePaths)# 循环遍历所有的图片for imagePath in imagePaths:# 加载图像,忽略宽高比缩放到 32*32 像素,展平数据为 32*32*3 = 3072 像素图像, 并存储到list中# resize数据是至关重要的,因为neural network需要这样的维度,不同神经网络需要不同的维度# 展平数据使我们可以轻松地将原始像素强度传递到输入层神经元。稍后您会看到,对于VGGNet,我们将卷传递到网络,因为它是卷积的。该示例只是一个简单的非卷积网络-我们将在后文中介绍一个更高级的示例image = cv2.imread(imagePath)image = cv2.resize(image, (32, 32)).flatten()data.append(image)# 从图像路径中提取分类标签并存储到分类标签数组中label = imagePath.split(os.path.sep)[-2]labels.append(label)# 将像素强度的缩放范围从[0,255]缩放到[0,1](常见的预处理步骤)。转换array 为 NumPy数组data = np.array(data, dtype="float") / 255.0labels = np.array(labels)# 3. 构建训练和测试数据分组# 将数据划分为75%的训练数据和25%的测试数据# 通常分配一定比例的数据用于训练,而分配较小比例的数据用于测试。 scikit-learn提供了方便的train_test_split 函数为我们拆分数据(trainX, testX, trainY, testY) = train_test_split(data,labels, test_size=0.25, random_state=42)# 我们的分类标签当前以字符串表示; 但是,Keras会假定两者都:标签被编码为整数 而且,对这些标签执行单热编码,使每个标签表示为矢量而不是整数# 要完成此编码,我们可以使用scikit-learn 中的LabelBinarizer# 将标签从 integer 转换为 vector# 初始化LabelBinaries 调用fit_transform zai trainY中找到所有唯一的类标签并将他们转换为单热编码的标签# testY 上执行单热编码# 由于数组中只有一个是 1,因此成为单热编码# [1, 0, 0] # corresponds to cats# [0, 1, 0] # corresponds to dogs# [0, 0, 1] # corresponds to pandalb = LabelBinarizer()trainY = lb.fit_transform(trainY)testY = lb.transform(testY)# 4. 定义您的Keras模型架构# 使用Keras定义我们的神经网络架构。在这里,我们将使用一个具有一个输入层,两个隐藏层和一个输出层的网络:# define the 3072-1024-512-3 architecture using Keras# 32*32*3 = 3072 拼合的输入图像中的像素 第一个隐藏层由1024节点 第二个隐藏层有512节点 最终输出层中的节点数将为可能的类标签的数目-# 在这种情况下,输出层将具有三个节点,每个节点对应一个类标签(“ cats”,“ dogs” ”和“ panda”)。model = Sequential()model.add(Dense(1024, input_shape=(3072,), activation="sigmoid"))model.add(Dense(512, activation="sigmoid"))model.add(Dense(len(lb.classes_), activation="softmax"))# 5. 编译Keras模型# 初始化学习速度和要训练的时期总数INIT_LR = 0.01EPOCHS = 80# 使用随机梯度下降(SGD Stochastic Gradient Descent)优化器编译模型,“categorical_crossentropy”作为损失函数# 分类交叉熵(Categorical cross-entropy)被用作几乎所有训练执行分类的网络的损失函数。# 唯一的例外是2-class classification (2类分类 其中只有两个可能的类标签),在这种情况下,选择“ binary_crossentropy”print("[INFO] training network...")opt = SGD(lr=INIT_LR)pile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])# 6. “拟合”Keras模型使其适合数据# 训练神经网络# batch_size 控制通过网络传递的每组数据的大小。较大的GPU将能够容纳较大的批处理大小。建议从32或者64开始,然后逐步增加H = model.fit(x=trainX, y=trainY, validation_data=(testX, testY),epochs=EPOCHS, batch_size=32)# 7. 评估Keras模型# 我们已经训练了实际模型,现在我们需要根据测试数据对其进行评估。# 重要的是,我们必须对测试数据进行评估,以便我们能够对模型进行从未被训练过的数据进行无偏(或尽可能接近无偏)表示。# 要评估我们的Keras模型,我们可以结合使用.predict 模型的方法以及scikit-learn 中的category_report :print("[INFO] evaluating network...")predictions = model.predict(x=testX, batch_size=32)print(classification_report(testY.argmax(axis=1),predictions.argmax(axis=1), target_names=lb.classes_))# plot the training loss and accuracyN = np.arange(0, EPOCHS)plt.style.use("ggplot")plt.figure()plt.plot(N, H.history["loss"], label="train_loss")plt.plot(N, H.history["val_loss"], label="val_loss")plt.plot(N, H.history["accuracy"], label="train_acc")plt.plot(N, H.history["val_accuracy"], label="val_acc")plt.title("Training Loss and Accuracy (Simple NN)")plt.xlabel("Epoch #")plt.ylabel("Loss/Accuracy")plt.legend()plt.savefig(args["plot"])# 8.保存模型和二进制标签文件print("[INFO] serializing network and label binarizer...")model.save(args["model"], save_format="h5")f = open(args["label_bin"], "wb")f.write(pickle.dumps(lb))f.close()
(3)第一个卷积神经网络
train_vgg.py# 训练第一个卷积神经网络# python train_vgg.py --dataset animals --model output/smallvggnet.model \# --label-bin output/smallvggnet_lb.pickle \# --plot output/smallvggnet_plot.png# 在Google相册中,如果您在搜索框中输入“ dog”,则将返回照片库中的狗的图片-我很确定该图片搜索引擎功能已使用CNN。# 图像搜索引擎并不是CNN的唯一用例-我敢打赌,您的想法已经开始提出各种可应用于深度学习的想法。## SmallVggnet 精确度76% 高于 之前的普通模型 60%# 设置matplotlib为Agg,因此图像可以保存在磁盘上import matplotlibmatplotlib.use("Agg")# 导入必要的包from pyimagesearch.smallvggnet import SmallVGGNetfrom sklearn.preprocessing import LabelBinarizerfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_report# 我们将使用ImageDataGenerator扩充数据。我几乎总是建议使用数据扩充,这样会导致模型更好地推广。# 数据扩充涉及对现有训练数据添加随机旋转,平移,剪切和缩放比例。from tensorflow.keras.preprocessing.image import ImageDataGeneratorfrom tensorflow.keras.optimizers import SGDfrom imutils import pathsimport matplotlib.pyplot as pltimport numpy as npimport argparseimport randomimport pickleimport cv2import os# 构建命令行参数ap = argparse.ArgumentParser()ap.add_argument("-d", "--dataset", required=True,help="path to input dataset of images")ap.add_argument("-m", "--model", required=True,help="path to output trained model")ap.add_argument("-l", "--label-bin", required=True,help="path to output label binarizer")ap.add_argument("-p", "--plot", required=True,help="path to output accuracy/loss plot")args = vars(ap.parse_args())# --dataset: 磁盘上图像数据集的路径# --model: 我们的模型将序列化并输出到磁盘。此参数包含输出模型文件的路径。确保对模型进行相应命名,以免覆盖以前训练过的模型(例如简单的神经网络模型)。# --label-bin: 数据集标签被序列化到磁盘的路径# --plot:输出训练图图像文件的路径。我们将检查该图,以检查数据是否过度/不足。每次通过更改参数来训练模型时,都应在命令行中指定不同的绘图文件名,以便在笔记本或注释文件中拥有与训练笔记相对应的绘图历史记录。# 初始化数据和标签类print("[INFO] loading images...")data = []labels = []# 获取图像路径并随机洗牌(给定42的随机种子,以重新结果)imagePaths = sorted(list(paths.list_images(args["dataset"])))random.seed(42)random.shuffle(imagePaths)# 循环遍历输入图像for imagePath in imagePaths:# 加载图像,resize到64*64(SmallVGGNet所需的输入空间尺寸) 并存储道list中image = cv2.imread(imagePath)image = cv2.resize(image, (64, 64))# 一个关键的区别是我们不会为神经网络拼合数据,因为它是卷积的data.append(image)# 从图像路径中提取分类标签并添加到list中label = imagePath.split(os.path.sep)[-2]labels.append(label)# scale 像素强度到 [0,1]data = np.array(data, dtype="float") / 255.0labels = np.array(labels)# 分组训练数据(75%)和测试数据(25%)一个建议是80/20比例去看一下是否有明显提高(trainX, testX, trainY, testY) = train_test_split(data,labels, test_size=0.25, random_state=42)# 初始化类标签器 将类标签器从整数转换为向量# 标签二值化这样后续可以进行一键热编码,并在脚本的后面将标签二值化器序列化为pickle文件lb = LabelBinarizer()trainY = lb.fit_transform(trainY)testY = lb.transform(testY)# 构建图像加强生成器# 图像增强使我们可以通过随机旋转,移动,剪切,缩放和翻转从现有的训练数据中构建“其他”训练数据。# 数据扩充通常是以下关键步骤:# -避免过度拟合# -确保模型能很好地泛化 我建议您始终执行数据增强,除非您有明确的理由不这样做。aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,horizontal_flip=True, fill_mode="nearest")# 初始化 构建 VGG-like Convolutional Neural Networkmodel = SmallVGGNet.build(width=64, height=64, depth=3,classes=len(lb.classes_))# 初始化初始学习率,训练次数,训练批次INIT_LR = 0.01EPOCHS = 75BS = 32# initialize the model and optimizer (you'll want to use# binary_crossentropy for 2-class classification)# 编译模型# 初始化模型及在二值分类中的二值交叉熵优化器print("[INFO] training network...")opt = SGD(lr=INIT_LR, decay=INIT_LR / EPOCHS)pile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])# 训练网络# model.fit 可同时处理训练和即时扩充的增强数据。# 我们必须将训练数据作为第一个参数传递给生成器。生成器将根据我们先前进行的设置生成批量的增强训练数据。H = model.fit(x=aug.flow(trainX, trainY, batch_size=BS),validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,epochs=EPOCHS)# 评估网络模型print("[INFO] evaluating network...")predictions = model.predict(x=testX, batch_size=32)print(classification_report(testY.argmax(axis=1),predictions.argmax(axis=1), target_names=lb.classes_))# 画出训练精确度和损失图N = np.arange(0, EPOCHS)plt.style.use("ggplot")plt.figure()plt.plot(N, H.history["loss"], label="train_loss")plt.plot(N, H.history["val_loss"], label="val_loss")plt.plot(N, H.history["accuracy"], label="train_acc")plt.plot(N, H.history["val_accuracy"], label="val_acc")plt.title("Training Loss and Accuracy (SmallVGGNet)")plt.xlabel("Epoch #")plt.ylabel("Loss/Accuracy")plt.legend()plt.savefig(args["plot"])# 保存模型和类标签器到磁盘print("[INFO] serializing network and label binarizer...")model.save(args["model"], save_format="h5")f = open(args["label_bin"], "wb")f.write(pickle.dumps(lb))f.close()
(4)ImageDataGenerator
使用ImageDataGenerator扩充数据,是为了使模型更好地推广,泛化
数据扩充涉及对现有训练数据添加随机旋转,平移,剪切和缩放比例。
(5)SGD 随机梯度下降损失函数
使用随机梯度下降(SGD Stochastic Gradient Descent)优化器编译模型,“categorical_crossentropy”作为损失函数
分类交叉熵(Categorical cross-entropy)被用作几乎所有训练执行分类的网络的损失函数。
唯一的例外是2-class classification (2类分类 其中只有两个可能的类标签),在这种情况下,选择“ binary_crossentropy”.
(6) 简单的神经网络 与 卷积神经网络训练结果对比
简单的神经网络训练速度快,精确度稍低;
卷积神经网络训练速度慢,精确度高,由于数据集经过了数据增强具有比较好的泛化能力;
同样的数据下的预测精确度:
简单神经网络预测结果:
卷积神经网络预测结果:
简单神经网络训练损失/精确度图:
卷积神经网络损失/精确度图:
ps: 啃英文对我来说有点难,又有点简单。这是我大学无比喜欢上的课,后来也会记单词,英汉互译的看诗集。这一篇文章确实有点难啃,有些学术名词我不是很懂,不过我在努力,学习的感觉真的太好了。
晚安~~~
参考: //09/10/keras-tutorial-how-to-get-started-with-keras-deep-learning-and-python/
如果觉得《Keras TensorFlow教程:使用自己的数据集进行训练》对你有帮助,请点赞、收藏,并留下你的观点哦!