Advertisement

读书笔记--Deep Learning for Computer Vision with Python(第12章)

阅读量:

文章目录

  • 第十二章 训练你的第一个CNN
    • 一、图像预处理器及加载

      • 1.1 imagetoarraypreprocessor
      • 1.2 simplepreprocessor
      • 1.3 simpledatasetloader
      • 1.4 这些预处理程序的作用
    • 二、很浅的CNN - ShallowNet

      • 2.1 ShallowNet
      • 2.2 ShallowNet用在Animals数据集上

第十二章 训练你的第一个CNN

【概述】本章的目标有两个
一、搭建一个图像预处理器。其功能是:将输入图像转换为Keras可以直接使用的Numpy数组。
二、搭建一个CNN。其特点是只有一层(慢慢来)。

一、图像预处理器及加载

1.1 imagetoarraypreprocessor

话不多说直接分析代码啦。

复制代码
    from keras.preprocessing.image import img_to_array
    
    class ImageToArrayPreprocessor:
    def __init__(self, dataFormat=None):
        self.dataFormat = dataFormat
    
    def preprocess(self, image):
        return img_to_array(image, data_format=self.dataFormat)

逐段分析:

复制代码
    def __init__(self, dataFormat=None):
        self.dataFormat = dataFormat

构造函数接受名为dataFormat的可选参数。此值默认为None,可以使用channels_first或channels_last字符串,但最好让Keras根据配置文件选择要使用的图像维度排序。

复制代码
    def preprocess(self, image):
        return img_to_array(image, data_format=self.dataFormat)

此段的功能:
1.接受一个图像作为输入
2.调用 img_to_array ,并且指定是channels_first或channels_last
3.按channels_first或channels_last返回一个 numpy 数组

1.2 simplepreprocessor

这个预处理器在前几章,但是我比较懒。。。然后以后可能也要用到这个预处理器,所以就放到这章了。
代码如下:

复制代码
    import cv2
    
    class SimplePreprocessor:
    	def __init__(self, width, height, inter=cv2.INTER_AREA):
    		self.width = width
    		self.height = height
    		self.inter = inter
    
    	def preprocess(self, image):
    		return cv2.resize(image, (self.width, self.height),
    			interpolation=self.inter)

这个预处理器的功能是:将缩放输入图像的大小。逐段分析:

复制代码
    	def __init__(self, width, height, inter=cv2.INTER_AREA):
    		self.width = width
    		self.height = height
    		self.inter = inter

构造函数接受宽度和高度两个参数,即输出图像的宽度和高度。最后一个inter参数是插值方法,默认为cv2.INTER_AREA,插值方法总共有5种:

1、INTER_NEAREST - 最近邻插值法
2、INTER_LINEAR - 双线性插值法
3、INTER_AREA - 基于局部像素的重采样(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。
4、INTER_CUBIC - 基于4x4像素邻域的3次插值法
5、INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值

复制代码
    	def preprocess(self, image):
    		return cv2.resize(image, (self.width, self.height),
    			interpolation=self.inter)

调用 cv2.resize 将输入图像缩放,最后输出缩放后的图像。

1.3 simpledatasetloader

直接上代码:

复制代码
    import numpy as np
    import cv2
    import os
    
    class SimpleDatasetLoader:
    	def __init__(self, preprocessors=None):
    		self.preprocessors = preprocessors
    		if self.preprocessors is None:
    			self.preprocessors = []
    
    	def load(self, imagePaths, verbose=-1):
    		data = []
    		labels = []
    		for (i, imagePath) in enumerate(imagePaths):
    			image = cv2.imread(imagePath)
    			label = imagePath.split(os.path.sep)[-2]
    			if self.preprocessors is not None:
    				for p in self.preprocessors:
    					image = p.preprocess(image)
    			data.append(image)
    			labels.append(label)
    			if verbose > 0 and i > 0 and (i + 1) % verbose == 0:
    				print("[INFO] processed {}/{}".format(i + 1, len(imagePaths)))
    
    		return (np.array(data), np.array(labels))

这段代码是从磁盘加载图像,并将加载的图像做处理。

复制代码
    	def __init__(self, preprocessors=None):
    		self.preprocessors = preprocessors
    		if self.preprocessors is None:
    			self.preprocessors = []

构造函数接受preprocessors参数,该参数是预处理函数,默认值是None,如果没有预处理的话preprocessors就为空。

复制代码
    	def load(self, imagePaths, verbose=-1):
    		data = []
    		labels = []
    		for (i, imagePath) in enumerate(imagePaths):
    			image = cv2.imread(imagePath)
    			label = imagePath.split(os.path.sep)[-2]
    			if self.preprocessors is not None:
    				for p in self.preprocessors:
    					image = p.preprocess(image)
    			data.append(image)
    			labels.append(label)
    			if verbose > 0 and i > 0 and (i + 1) % verbose == 0:
    				print("[INFO] processed {}/{}".format(i + 1, len(imagePaths)))
    
    		return (np.array(data), np.array(labels))

load函数接受数据集的路径作为传入参数,数据集路径格式…/cat/cat.jpg或者…/dog/dog.jpg,lable可以直接从路径中得到,如果preprocessors非空的话,依次对图像进行预处理。

1.4 这些预处理程序的作用

乍看上去,这两个预处理操作是在干嘛呀,代码这么长,功能这么简单,每个预处理程序就调用了一个函数,我直接在程序里调用 img_to_arraycv2.resize不就行了吗?
直接调用当然可以,但是这么做的好处是:我们可以从硬盘加载数据集时将预处理器链接在一起。
例如,假设我们希望将所有输​​入图像的大小调整为32:
sp = SimplePreprocessor(32, 32)
调整大小之后,又想让图像转换成channels_first或channels_last的 numpy 数组:
iap = ImageToArrayPreprocessor()
现在,假设我们希望从磁盘加载图像数据集并预处理数据集中的所有图像。

复制代码
    sdl = SimpleDatasetLoader(preprocessor=[sp, iap])
    (data, labels) = sdl.load(imagePaths, verbose=500)

上一段代码完成的任务是:按SimplePreprocessor、ImageToArrayPreprocessor的顺序处理从磁盘中读取的图片数据,输出对应的numpy数组。这样看起来代码简洁多了,并且下次再用直接调用也很方便。
预处理过程

二、很浅的CNN - ShallowNet

【概述】本章的网络结构为:INPUT -> CONV -> RELU -> FC。该网络实现后将用于Animals和CIFAR-10数据集。

2.1 ShallowNet

直接上代码:

复制代码
    from keras.models import Sequential
    from keras.layers.convolutional import Conv2D
    from keras.layers.core import Activation
    from keras.layers.core import Flatten
    from keras.layers.core import Dense
    from keras import backend as K
    
    # Input => CONV => RELU => FC
    class ShallowNet:
    @staticmethod
    def build(width, height, depth, classes):
        model = Sequential()
        inputShape = (height, width, depth)
        if K.image_data_format() == "channels_first":
            inputShape = (depth, height, width)
    
        # define the first (and only) CONV => RELU layer
        model.add(Conv2D(32, (3, 3), padding="same",input_shape=inputShape))
    
        model.add(Activation("relu"))
        model.add(Flatten())
        model.add(Dense(classes))
        model.add(Activation("softmax"))
    
        return model

该段代码实现的是INPUT -> CONV -> RELU -> FC的网络结构。
分段分析:
@staticmethod参见 staticmethod

复制代码
    class ShallowNet:
    @staticmethod
    def build(width, height, depth, classes):
        model = Sequential()
        inputShape = (height, width, depth)
        if K.image_data_format() == "channels_first":
            inputShape = (depth, height, width)

该段定义构建方法,该方法接受四个参数:输入图像的宽、高、深度、类别的总数(对CIFAR-10来说,classes=10)
倒数后三行是为了保证输入数据的shape与Keras backend的一致(channels last或者channels first)。

复制代码
        model.add(Conv2D(32, (3, 3), padding="same",input_shape=inputShape))
        model.add(Activation("relu"))
        model.add(Flatten())
        model.add(Dense(classes))
        model.add(Activation("softmax"))

首先,定义第一层卷积层,这一层用32个滤波器,每个滤波器的大小为3x3,还应用了same padding(对于此例子来说不是必须的,但是一开始就养成这个好习惯很必要);
其次,添加relu层;
然后,将网络展平;
然后,应用全连接层;
最后,用softmax分类。

2.2 ShallowNet用在Animals数据集上

复制代码
    from sklearn.preprocessing import LabelBinarizer
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report
    from pyimagesearch.preprocessing import ImageToArrayPreprocessor
    from pyimagesearch.preprocessing import SimplePreprocessor
    from pyimagesearch.datasets import SimpleDatasetLoader
    from CNN_shallownet import ShallowNet
    from keras.optimizers import SGD
    from imutils import paths
    import matplotlib.pyplot as plt
    import numpy as np
    import argparse
    
    ap = argparse.ArgumentParser()
    ap.add_argument("-d", "--dataset", required=True,
                help="path to input dataset")
    args = vars(ap.parse_args())    # 用vars将ap.parse_args()变成字典
    
    print('[INFO] loading images...')
    imagePaths = list(paths.list_images(args["dataset"]))   # 列出dataset路径下的所有图像路径
    sp = SimplePreprocessor(32, 32)
    iap = ImageToArrayPreprocessor()
    
    sdl = SimpleDatasetLoader(preprocessors=[sp, iap])
    (data, labels) = sdl.load(imagePaths, verbose=500)
    data = data.astype("float") / 255.0
    
    (trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)		#划分训练和测试集
    
    trainY = LabelBinarizer().fit_transform(trainY)
    testY = LabelBinarizer().fit_transform(testY)
    
    print("[INFO] compiling model...")
    opt = SGD(lr=0.005)
    model = ShallowNet.build(width=32, height=32, depth=3, classes=3)		#创建ShallowNet
    model.compile(loss='categorical_crossentropy', optimizer=opt,  metrics=['accuracy'])		#编译模型,采用随机梯度下降法优化
    
    print('[INFO] training network...')
    H = model.fit(trainX, trainY, validation_data=(testX, testY), batch_size=32, epochs=100, verbose=1)		#训练模型
    
    print('[INFO] evaluating network...' )			#评价模型
    prediations = model.predict(testX, batch_size=32)
    print(classification_report(testY.argmax(axis=1),
        prediations.argmax(axis=1),
        target_names=['cat', 'dog', 'panda']))

argparse的简单介绍:argparse

全部评论 (0)

还没有任何评论哟~