TensorFlow2.0实战:Cats vs Dogs_cats_vs_dogs tensorflow_datasets-程序员宅基地

技术标签: tensorflow  python  深度学习  TensorFlow  

数据集准备

在本文中,我们使用“Cats vs Dogs”的数据集。这个数据集包含了23,262张猫和狗的图像

在这里插入图片描述
你可能注意到了,这些照片没有归一化,它们的大小是不一样的

但是非常棒的一点是,你可以在Tensorflow Datasets中获取这个数据集

所以,确保你的环境里安装了Tensorflow Dataset

pip install tensorflow-dataset

和这个库中的其他数据集不同,这个数据集没有划分成训练集和测试集

所以我们需要自己对这两类数据集做个区分

关于数据集的更多信息:https://www.tensorflow.org/datasets/catalog/cats_vs_dogs


实现

这个实现分成了几个部分

首先,我们实现了一个类,其负责载入数据和准备数据。

然后,我们导入预训练模型,构建一个类用于修改最顶端的几层网络。

最后,我们把训练过程运行起来,并进行评估。

当然,在这之前,我们必须导入一些代码库,定义一些全局常量:

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
import tensorflow_datasets as tfds

IMG_SIZE = 160
BATCH_SIZE = 32
SHUFFLE_SIZE = 1000
IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)

数据载入器

这个类负责载入数据和准备数据,用于后续的数据处理。以下是这个类的实现:

class DataLoader(object):
    def __init__(self, image_size, batch_size):
        
        self.image_size = image_size
        self.batch_size = batch_size
        
        # 80% train data, 10% validation data, 10% test data
        split_weights = (8, 1, 1)
        splits = tfds.Split.TRAIN.subsplit(weighted=split_weights)
        
        (self.train_data_raw, self.validation_data_raw, self.test_data_raw), self.metadata = tfds.load(
            'cats_vs_dogs', split=list(splits),
            with_info=True, as_supervised=True)
        
        # Get the number of train examples
        self.num_train_examples = self.metadata.splits['train'].num_examples*80/100
        self.get_label_name = self.metadata.features['label'].int2str
        
        # Pre-process data
        self._prepare_data()
        self._prepare_batches()
        
    # Resize all images to image_size x image_size
    def _prepare_data(self):
        self.train_data = self.train_data_raw.map(self._resize_sample)
        self.validation_data = self.validation_data_raw.map(self._resize_sample)
        self.test_data = self.test_data_raw.map(self._resize_sample)
    
    # Resize one image to image_size x image_size
    def _resize_sample(self, image, label):
        image = tf.cast(image, tf.float32)
        image = (image/127.5) - 1
        image = tf.image.resize(image, (self.image_size, self.image_size))
        return image, label
    
    def _prepare_batches(self):
        self.train_batches = self.train_data.shuffle(1000).batch(self.batch_size)
        self.validation_batches = self.validation_data.batch(self.batch_size)
        self.test_batches = self.test_data.batch(self.batch_size)
   
    # Get defined number of  not processed images
    def get_random_raw_images(self, num_of_images):
        random_train_raw_data = self.train_data_raw.shuffle(1000)
        return random_train_raw_data.take(num_of_images)

这个类实现了很多功能,它实现了很多public方法:

  • _prepare_data:内部方法,用于缩放和归一化数据集里的图像。构造函数需要用到该函数。
  • _resize_sample:内部方法,用于缩放单张图像。
  • _prepare_batches:内部方法,用于将图像打包创建为batches。创建train_batchesvalidation_batchestest_batches,分别用于训练、评估过程。
  • get_random_raw_images:这个方法用于从原始的、没有经过处理的数据中随机获取固定数量的图像。

但是,这个类的主要功能还是在构造函数中完成的。让我们仔细看看这个类的构造函数。

def __init__(self, image_size, batch_size):

    self.image_size = image_size
    self.batch_size = batch_size

    # 80% train data, 10% validation data, 10% test data
    split_weights = (8, 1, 1)
    splits = tfds.Split.TRAIN.subsplit(weighted=split_weights)

    (self.train_data_raw, self.validation_data_raw, self.test_data_raw), self.metadata = tfds.load(
        'cats_vs_dogs', split=list(splits),
        with_info=True, as_supervised=True)

    # Get the number of train examples
    self.num_train_examples = self.metadata.splits['train'].num_examples*80/100
    self.get_label_name = self.metadata.features['label'].int2str

    # Pre-process data
    self._prepare_data()
    self._prepare_batches()

首先我们通过传入参数定义了图像大小和batch大小

然后,由于该数据集本身没有区分训练集和测试集,我们通过划分权值对数据进行划分

一旦我们执行了数据划分,我们就开始计算训练样本数量,然后调用辅助函数来为训练准备数据

在这之后,我们需要做的仅仅是实例化这个类的对象,然后载入数据即可。

data_loader = DataLoader(IMG_SIZE, BATCH_SIZE)

plt.figure(figsize=(10, 8))
i = 0
for img, label in data_loader.get_random_raw_images(20):
    plt.subplot(4, 5, i+1)
    plt.imshow(img)
    plt.title("{} - {}".format(data_loader.get_label_name(label), img.shape))
    plt.xticks([])
    plt.yticks([])
    i += 1
plt.tight_layout()
plt.show()

输出结果
在这里插入图片描述
基础模型 & Wrapper

下一个步骤就是载入预训练模型了

这些模型位于tensorflow.kearas.applications

我们可以用下面的语句直接载入它们

vgg16_base = tf.keras.applications.VGG16(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')
googlenet_base = tf.keras.applications.InceptionV3(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')
resnet_base = tf.keras.applications.ResNet101V2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')

这段代码就是我们创建上述三种网络结构基础模型的方式

注意,每个模型构造函数的include_top参数传入的是false

这意味着这些模型是用于提取特征的

我们一旦创建了这些模型,我们就需要修改这些模型顶部的网络层,使之适用于我们的具体问题

我们使用Wrapper类来完成这个步骤

这个类接收预训练模型,然后添加一个Global Average Polling Layer和一个Dense Layer

本质上,这最后的Dense Layer会用于我们的二分类问题(猫或狗)

Wrapper类把所有这些元素都放到了一起,放在了同一个模型中

class Wrapper(tf.keras.Model):
    def __init__(self, base_model):
        super(Wrapper, self).__init__()
        
        self.base_model = base_model
        self.average_pooling_layer = tf.keras.layers.GlobalAveragePooling2D()
        self.output_layer = tf.keras.layers.Dense(1)
        
    def call(self, inputs):
        x = self.base_model(inputs)
        x = self.average_pooling_layer(x)
        output = self.output_layer(x)
        return output

然后我们就可以创建Cats vs Dogs分类问题的模型了,并且编译这个模型。

base_learning_rate = 0.0001

vgg16_base.trainable = False
vgg16 = Wrapper(vgg16_base)
vgg16.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),
              loss='binary_crossentropy',
              metrics=['accuracy'])

googlenet_base.trainable = False
googlenet = Wrapper(googlenet_base)
googlenet.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),
              loss='binary_crossentropy',
              metrics=['accuracy'])

resnet_base.trainable = False
resnet = Wrapper(resnet_base)
resnet.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),
              loss='binary_crossentropy',
              metrics=['accuracy'])

注意,我们标记了基础模型是不参与训练的

这意味着在训练过程中,我们只会训练新添加到顶部的网络层,而在网络底部的权重值不会发生变化。

训练

在我们开始整个训练过程之前,让我们思考一下,这些模型的大部头其实已经被训练过了

所以,我们可以执行评估过程来看看评估结果如何:

steps_per_epoch = round(data_loader.num_train_examples)//BATCH_SIZE
validation_steps = 20

loss1, accuracy1 = vgg16.evaluate(data_loader.validation_batches, steps = 20)
loss2, accuracy2 = googlenet.evaluate(data_loader.validation_batches, steps = 20)
loss3, accuracy3 = resnet.evaluate(data_loader.validation_batches, steps = 20)

print("--------VGG16---------")
print("Initial loss: {:.2f}".format(loss1))
print("Initial accuracy: {:.2f}".format(accuracy1))
print("---------------------------")

print("--------GoogLeNet---------")
print("Initial loss: {:.2f}".format(loss2))
print("Initial accuracy: {:.2f}".format(accuracy2))
print("---------------------------")

print("--------ResNet---------")
print("Initial loss: {:.2f}".format(loss3))
print("Initial accuracy: {:.2f}".format(accuracy3))
print("---------------------------")

有意思的是,这些模型在没有预先训练的情况下,我们得到的结果也还过得去(50%的精确度):

———VGG16———
Initial loss: 5.30
Initial accuracy: 0.51
—————————-

——GoogLeNet—–
Initial loss: 7.21
Initial accuracy: 0.51
—————————-

——–ResNet———
Initial loss: 6.01
Initial accuracy: 0.51
—————————-

把50%作为训练的起点已经挺好的了

所以,就让我们把训练过程跑起来吧,看看我们是否能得到更好的结果

首先,我们训练VGG16:

history = vgg16.fit(data_loader.train_batches, epochs=10, validation_data=data_loader.validation_batches)

训练过程历史数据显示大致如下:

在这里插入图片描述
然后我们可以训练GoogLeNet

history = googlenet.fit(data_loader.train_batches,
                    epochs=10,
                    validation_data=data_loader.validation_batches)

在这里插入图片描述
最后是ResNet的训练

history = resnet.fit(data_loader.train_batches,
                    epochs=10,
                    validation_data=data_loader.validation_batches)

在这里插入图片描述
由于我们只训练了顶部的几层网络,而不是整个网络,所以训练这三个模型只用了几个小时


评估

我们看到在训练开始前,我们已经有了50%左右的精确度。让我们来看下训练后是什么情况:

loss1, accuracy1 = vgg16.evaluate(data_loader.test_batches, steps = 20)
loss2, accuracy2 = googlenet.evaluate(data_loader.test_batches, steps = 20)
loss3, accuracy3 = resnet.evaluate(data_loader.test_batches, steps = 20)

print("--------VGG16---------")
print("Loss: {:.2f}".format(loss1))
print("Accuracy: {:.2f}".format(accuracy1))
print("---------------------------")

print("--------GoogLeNet---------")
print("Loss: {:.2f}".format(loss2))
print("Accuracy: {:.2f}".format(accuracy2))
print("---------------------------")

print("--------ResNet---------")
print("Loss: {:.2f}".format(loss3))
print("Accuracy: {:.2f}".format(accuracy3))
print("---------------------------")

结果如下:

——–VGG16———
Loss: 0.25
Accuracy: 0.93
—————————

——–GoogLeNet———
Loss: 0.54
Accuracy: 0.95
—————————
——–ResNet———
Loss: 0.40
Accuracy: 0.97
—————————

我们可以看到这三个模型的结果都相当好,其中ResNet效果最好,精确度高达97%。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38973721/article/details/128545103

智能推荐

Linux下scrapy安装问题汇总_linux安装scrapy的问题-程序员宅基地

文章浏览阅读3.8k次。首先,在安装scrapy之前,需要用apt-get以及pip安装以下依赖包:apt-get install build-essential python3-dev libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev zlib1g-dev在Linux系统下python3.4版本下成功安装scrapy:pip install_linux安装scrapy的问题

pfm格式转png格式_将深度图像(.pfm格式)转换为深度图像数据(.png格式)-程序员宅基地

文章浏览阅读6.1k次,点赞8次,收藏29次。PFM是一种用浮点数存储图片的文件格式,包含文件信息header和二进制数据raster。在一些数据集中经常可以看到以pfm格式存储的视差图。pfm格式的文件不利于浏览,可以转成png格式文件已方便浏览。PFM的头文件共有3行:二进制数据区:图像的读取顺序按照从下到上,从左到右进行的顺序进行读取。当pfm文件只有一张图片的矩阵时,代码如下:# -*- coding: UT..._将深度图像(.pfm格式)转换为深度图像数据(.png格式)

win10使用roLabelimg(可标注旋转矩形)保存带有汉字的label及xml转txt(含文件)_rolabellmg下载-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏22次。win10使用roLabelimg保存带有汉字的label(含文件)简介roLabelimg可以标注旋转矩形,虽不太好用,但比不能标注强太多太多,转载请注明出处。文件地址源文件修改&编译将roLabelImg.py中的#!/usr/bin/env python# -*- coding: utf8 -*-更改为#!/usr/bin/env python# -*- coding: utf-8 -*-编译pyrcc5 -o resources.py resources.qrc_rolabellmg下载

对Neo4j导出数据做知识图谱可视化 D3库实现_neo4jd3-程序员宅基地

文章浏览阅读2.5w次,点赞52次,收藏360次。知识图谱可视化 D3库的使用引言Neo4j导出数据引言好久没用D3库作可视化了,现在主要是用百度的echarts库,在项目中做简单的图表太方便了。但像是做关系图其实用echarts也很方便,这次用D3实现主要是复习一下以前做的东西,顺便记录一下。以下是我参考到的实例代码:D3官方图实例参考echarts做关系图实例参考Neo4j导出数据我们先通过Cypher查询将数据从Neo4j中查询出来,Neo4j构建和查询可以参考我上篇博客基于Neo4j的外贸企业关系图谱做企业相似度查询查询后的结果如下_neo4jd3

拓扑空间、距离空间、向量空间和内积空间_拓扑和距离的关系-程序员宅基地

文章浏览阅读7.5k次。拓扑空间是最基本的,是集合+开集构成,这个空间里没有距离。就像人群+关系=社会一样。距离空间=拓扑空间+距离。这个距离的来源主要是定义出来的。距离空间是拓扑空间的一个子集,也可以理解为是一个子概念。同理向量空间又是距离空间的一个子集,子概念。对拓扑向量空间来说,它是一个度量空间当且仅当其有可数局部拓扑基(见Rudin的泛函分析,对一般拓扑空间来说的充要条件还要多一个,这就是NS度量化定理,见Munk_拓扑和距离的关系

dubbo实战之一:准备和初体验,Java进阶-程序员宅基地

文章浏览阅读923次,点赞21次,收藏15次。Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

随便推点

yocto相关class总结-程序员宅基地

文章浏览阅读426次。2019独角兽企业重金招聘Python工程师标准>>> ..._bbclass

Qt 项目(CMake)设置国际化支持_qt5 cmake 多语言-程序员宅基地

文章浏览阅读3.2k次,点赞2次,收藏17次。文章目录国际化原理操作步骤注意:项目提交:参考:简 述: 准备将我写的悬浮网速窗口上面支持国际化(多语言);记录一下通过 CMake + Qt 来对项目设置国际化。本文初发于 “偕臧的小站” ifmet.cn,同步转载于此。编程环境:  ???? uos20 amd64 ???? Qt 5.11.3 ???? cmake 3.13.4 ???? gdb8.0国际化原理​ Qt 实现多语言的关键原理如图,先扫描所有的源码文件 .cpp、.ui 文件,找到被 tr() 包裹的字符_qt5 cmake 多语言

游戏编程模式------套路_数独套路女朋友-程序员宅基地

文章浏览阅读156次。https://blog.csdn.net/lhcmt1/article/details/80601414 //原文https://gpp.tkchu.me/behavioral-patterns.html //博客一:序列模式:1. 双缓冲模式工作原理: 例如计算机的渲染系统。一帧代表显卡读取的帧,一帧代表显卡写入的帧。何时使用: 可能有可见的错误(撕裂之类..._数独套路女朋友

如何轻松利用FUSB340TMX USB Type-C方案快速地助你实现完美的解决方案-程序员宅基地

文章浏览阅读154次。安森美深力科 10Gbps USB3.1超高速开关FUSB340TMX是 10Gbps USB3.1 超高速开关,典型带宽达 10 GHz,具有 1.5 V 至 5 V 的宽 VDD 范围,提供 2KV HBM ESD 保护,符合 Jedec 标准,有源功耗低于 12 uA,关断功耗低于1 uA,在2.5GHz时的插入损耗仅-1 dB,采用18引脚的 TMLP 小型封装(2.0mm x 2.8mm x 0.4mm),是适用于手机、平板电脑、笔记本电脑、超便携应用所需的可正反逆插的 Type-C USB 3_fusb340tmx

android nfc框架分析,Android NFC读写Tag快速框架-程序员宅基地

文章浏览阅读310次。这篇文章只讲NFC读写非接卡、读写标签的方式,且这里只讲符合TypeA和IsoDep技术标准的Tag,其他类型的Tag框架类似,只是有些许差别添加权限AndroidManifests.xml中添加:添加intent filterAndroidManifests.xml中添加:android:name=".MainActivity"android:label="@string/app_name"an..._android 卡片类型框架

1863. 找出所有子集的异或总和再求和 01.11(五)-程序员宅基地

文章浏览阅读352次,点赞10次,收藏7次。[5,1,6] 的异或总和为 5 XOR 1 XOR 6 = 2。- [1,3] 的异或总和为 1 XOR 3 = 2。- [5,1] 的异或总和为 5 XOR 1 = 4。- [5,6] 的异或总和为 5 XOR 6 = 3。- [1,6] 的异或总和为 1 XOR 6 = 7。每个子集的全部异或总和值之和为 480。- [1] 的异或总和为 1。- [3] 的异或总和为 3。- [5] 的异或总和为 5。- [1] 的异或总和为 1。- [6] 的异或总和为 6。