美文网首页
【2019-07-16】深度学习用于计算机视觉

【2019-07-16】深度学习用于计算机视觉

作者: BigBigFlower | 来源:发表于2019-08-04 17:14 被阅读0次

    1、卷积神经网络
    一个简单的小例子🌰

    #实例化一个小型的卷积神经网络
    # Conv2D 层和 MaxPooling2D 层的堆叠
    
    from keras import layers
    from keras import models
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    
    卷积神经网络的架构,每个 Conv2D 层和 MaxPooling2D 层的输出都是一个形状为 (height, width, channels) 的 3D 张量。

    将最后的输出张量[大小为 (3, 3, 64)]输入到一个密集连接分类器网络中, 即 Dense 层的堆叠。

    #在卷积神经网络上添加分类器
    model.add(layers.Flatten()) 
    model.add(layers.Dense(64, activation='relu')) 
    model.add(layers.Dense(10, activation='softmax'))
    
    将进行 10 类别分类,最后一层使用带 10 个输出的 softmax 激活。网络的架构

    在MNIST图像上训练卷积神经网络

    from keras.datasets import mnist
    from keras.utils import to_categorical
    (train_images, train_labels), (test_images, test_labels) = mnist.load_data()
    train_images = train_images.reshape((60000, 28, 28, 1)) 
    train_images = train_images.astype('float32') / 255
    test_images = test_images.reshape((10000, 28, 28, 1)) 
    test_images = test_images.astype('float32') / 255
    train_labels = to_categorical(train_labels) 
    test_labels = to_categorical(test_labels)
    model.compile(optimizer='rmsprop', loss='categorical_crossentropy',
    metrics=['accuracy'])
    model.fit(train_images, train_labels, epochs=5, batch_size=64)
    
    在测试数据上对模型进行评估
    2、卷积运算
    密集连接层和卷积层的根本区别在于,Dense 层从输入特征空间中学到的是全局模式,而卷积层学到的是局部模式,对于图像来说,学到的就是在输入图像的二维小窗口中发现的模式。
    卷积神经网络的两个性质:
    a 卷积神经网络学到的模式具有平移不变性
    b 卷积神经网络可以学到模式的空间层次结构
    卷积工作原理:
    在 3D 输入特征图上滑动(slide)这些 3×3 或 5×5 的窗口,在每个可能 的位置停止并提取周围特征的 3D 图块[形状为 (window_height, window_width, input_ depth)]。然后每个 3D 图块与学到的同一个权重矩阵[叫作卷积核(convolution kernel)]做 2 张量积,转换成形状为 (output_depth,) 的 1D 向量。然后对所有这些向量进行空间重组, 使其转换为形状为 (height, width, output_depth) 的 3D 输出特征图。输出特征图中的 每个空间位置都对应于输入特征图中的相同位置(比如输出的右下角包含了输入右下角的信 息)。
    卷积工作原理

    3、最大池化运算
    最大池化的作用:对特征图进行下采样,与步进卷积类似。
    最大池化是从输入特征图中提取窗口,并输出每个通道的最大值。它的概念与卷积类似,但是最大池化使用硬编码的 max 张量运算对局部图块进行变换,而不是使用学到的线性变换(卷 积核)。最大池化与卷积的最大不同之处在于,最大池化通常使用 2×2 的窗口和步幅 2,其目 的是将特征图下采样 2 倍。与此相对的是,卷积通常使用 3×3 窗口和步幅 1。
    4、在小型数据集上训练一个神经网络
    猫狗分类数据集
    将图像复制到训练、验证和测试的目录

    import os, shutil
    #数据目录
    original_dataset_dir ="/Users/***/Desktop/learning_log/net_work/kaggle/train" 
     #小数据集目录
    base_dir = '/Users/fanhua/Desktop/learning_log/net_work/kaggle/cats_and_dogs_small' 
    os.mkdir(base_dir)
    #训练目录
    train_dir = os.path.join(base_dir, 'train') 
    os.mkdir(train_dir)
    #验证目录
    validation_dir = os.path.join(base_dir, 'validation') 
    os.mkdir(validation_dir)
    #测试目录
    test_dir = os.path.join(base_dir, 'test') 
    os.mkdir(test_dir)
    #猫的训练图像目录
    train_cats_dir = os.path.join(train_dir, 'cats') 
    os.mkdir(train_cats_dir)
    #狗的训练图像目录
    train_dogs_dir = os.path.join(train_dir, 'dogs') 
    os.mkdir(train_dogs_dir)
    #猫的验证图像目录
    validation_cats_dir = os.path.join(validation_dir, 'cats') 
    os.mkdir(validation_cats_dir)
    #狗的验证图像目录
    validation_dogs_dir = os.path.join(validation_dir, 'dogs') 
    os.mkdir(validation_dogs_dir)
    #猫的测试图像目录
    test_cats_dir = os.path.join(test_dir, 'cats') 
    os.mkdir(test_cats_dir)
    #狗的测试图像目录
    test_dogs_dir = os.path.join(test_dir, 'dogs') 
    os.mkdir(test_dogs_dir)
    #将前 1000 张猫的图像复制 到 train_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1000)] 
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname) 
        dst = os.path.join(train_cats_dir, fname) 
        shutil.copyfile(src, dst)
    #将接下来 500 张猫的图像复 制到 validation_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)] 
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname) 
        dst = os.path.join(validation_cats_dir, fname) 
        shutil.copyfile(src, dst)
    #将接下来的 500 张猫的图像 复制到 test_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)] 
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname) 
        dst = os.path.join(test_cats_dir, fname) 
        shutil.copyfile(src, dst)
    #将前 1000 张狗的图像复制 到 train_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1000)] 
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname) 
        dst = os.path.join(train_dogs_dir, fname) 
        shutil.copyfile(src, dst)
    #将接下来 500 张狗的图像复 制到 validation_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)] 
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname) 
        dst = os.path.join(validation_dogs_dir, fname) 
        shutil.copyfile(src, dst)
    #将接下来 500 张狗的图像复 制到 test_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)] 
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname) 
        dst = os.path.join(validation_dogs_dir, fname) 
        shutil.copyfile(src, dst)
    
    数据

    构建网络
    卷积神经网络由 Conv2D 层(使用 relu 激活)和 MaxPooling2D 层交替堆叠构成。

    from keras import layers
    from keras import models
    model = models.Sequential() 
    model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3))) 
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu')) 
    model.add(layers.MaxPooling2D((2, 2))) 
    model.add(layers.Conv2D(128, (3, 3), activation='relu')) 
    model.add(layers.MaxPooling2D((2, 2))) 
    model.add(layers.Conv2D(128, (3, 3), activation='relu')) 
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Flatten()) 
    model.add(layers.Dense(512, activation='relu')) 
    model.add(layers.Dense(1, activation='sigmoid'))
    
    特征图的维度随着每层变化

    配置模型用于训练

    from keras import optimizers
    model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4),
    metrics=['acc'])
    

    数据预处理

    (1) 读取图像文件
    (2) 将 JPEG 文件解码为 RGB 像素网格
    (3) 将这些像素网格转换为浮点数张量
    (4) 将像素值(0~255 范围内)缩放到 [0, 1] 区间
    使用 ImageDataGenerator 从目录中读取图像

    from keras.preprocessing.image import ImageDataGenerator
    #将所有图像乘以 1/255 缩放
    train_datagen = ImageDataGenerator(rescale=1./255) 
    test_datagen = ImageDataGenerator(rescale=1./255)
    train_generator = train_datagen.flow_from_directory(train_dir, 
                                                        target_size=(150, 150), #将所有图像的大小调整为 150×150
                                                        batch_size=20, 
                                                        class_mode='binary')
    validation_generator = test_datagen.flow_from_directory(validation_dir,
                                                            target_size=(150, 150), 
                                                            batch_size=20, 
                                                            class_mode='binary')
    

    利用批量生成器拟合模型,保存模型

    history = model.fit_generator(train_generator,
                                  steps_per_epoch=100,
                                  epochs=30, 
                                  validation_data=validation_generator,
                                  validation_steps=50)
    model.save('cats_and_dogs_small_1.h5')
    
    30轮

    绘制训练过程中的损失曲线和精度曲线

    import matplotlib.pyplot as plt
    acc = history.history['acc']
    val_acc = history.history['val_acc'] 
    loss = history.history['loss'] 
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label='Training acc') 
    plt.plot(epochs, val_acc, 'b', label='Validation acc') 
    plt.title('Training and validation accuracy') 
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss') 
    plt.plot(epochs, val_loss, 'b', label='Validation loss') 
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()
    
    精度和损失图,过拟合

    使用数据增强
    过拟合是因为样本少(2000个),数据增强是从现有的样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加(augment)样本。其目标是,模型在训练时不会两次查看完全相同的图像。这让模型能够观察 到数据的更多内容,从而具有更好的泛化能力。
    利用 ImageDataGenerator 来设置数据增强

    datagen = ImageDataGenerator(rotation_range=40,#图像随机旋转角度范围
                                 width_shift_range=0.2, #width_shift 和 height_shift 是图像在水平或垂直方向上平移的范围(相对于总宽
    度或总高度的比例)。
                                 height_shift_range=0.2, 
                                 shear_range=0.2, #shear_range 是随机错切变换的角度。
                                 zoom_range=0.2, #shear_range 是随机错切变换的角度。
                                 horizontal_flip=True, #horizontal_flip 是随机将一半图像水平翻转。如果没有水平不对称的假设(比如真
    实世界的图像),这种做法是有意义的。
                                 fill_mode='nearest')#fill_mode 是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度 / 高度平移。 我们来看一下增强后的图像
    

    显示几个随机增强后的训练图像

    from keras.preprocessing import image#图像预处理模块
    fnames = [os.path.join(train_cats_dir, fname) for  fname in os.listdir(train_cats_dir)]
    img_path = fnames[3]#选一张图像进行增强
    img = image.load_img(img_path, target_size=(150, 150))#读取图像并调整大小
    x = image.img_to_array(img)#将其转换为形状 (150, 150, 3) 的 Numpy 数组
    x = x.reshape((1,) + x.shape)#将其形状改变为 (1, 150, 150, 3)
    i=0
    #生成随机变换后的图像批量。 循环是无限的,因此需要在某个时刻终止循环
    for batch in datagen.flow(x, batch_size=1):
        plt.figure(i)
        imgplot = plt.imshow(image.array_to_img(batch[0])) 
        i += 1
        if i % 4 == 0:
            break 
    plt.show()
    
    新生成的图像
    网络看到的输入是高度相关的,因为这些输入都来自于少量的原始图像。无法生成新信息,而只能混合现有信息。为了进一步降低过拟合,还需要向模型中添加一个 Dropout 层,添加到密集连接分类器之前。
    定义一个包含 dropout 的新卷积神经网络
    model = models.Sequential() 
    model.add(layers.Conv2D(32, (3, 3), 
                            activation='relu',
                            input_shape=(150, 150, 3))) 
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), 
                            activation='relu')) 
    model.add(layers.MaxPooling2D((2, 2))) 
    model.add(layers.Conv2D(128, (3, 3), 
                            activation='relu')) 
    model.add(layers.MaxPooling2D((2, 2))) 
    model.add(layers.Conv2D(128, (3, 3), 
                            activation='relu')) 
    model.add(layers.MaxPooling2D((2, 2))) 
    model.add(layers.Flatten()) 
    model.add(layers.Dropout(0.5)) 
    model.add(layers.Dense(512, activation='relu')) 
    model.add(layers.Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', 
                  optimizer=optimizers.RMSprop(lr=1e-4),
    metrics=['acc'])
    

    利用数据增强生成器训练卷积神经网络

    train_datagen = ImageDataGenerator( rescale=1./255,
                                       rotation_range=40, 
                                       width_shift_range=0.2,
                                       height_shift_range=0.2,
                                       shear_range=0.2, 
                                       zoom_range=0.2, 
                                       horizontal_flip=True,)
    test_datagen = ImageDataGenerator(rescale=1./255)#注意,不能增强验证数据
    train_generator = train_datagen.flow_from_directory(train_dir,
                                                        target_size=(150, 150), 
                                                        batch_size=32, 
                                                        class_mode='binary')
    validation_generator = test_datagen.flow_from_directory( validation_dir,
                                                            target_size=(150, 150), 
                                                            batch_size=32, 
                                                            class_mode='binary')
    history = model.fit_generator( train_generator,
                                  steps_per_epoch=100,
                                  epochs=100, 
                                  validation_data=validation_generator, 
                                  validation_steps=50)
    model.save('cats_and_dogs_small_2.h5')
    

    绘制图像


    精度和损失

    5、使用预训练的卷积神经网络
    想要将深度学习应用于小型图像数据集,一种常用且非常高效的方法是使用预训练网络。 预训练网络(pretrained network)是一个保存好的网络,之前已在大型数据集(通常是大规模图像分类任务)上训练好。

    特征提取
    用于图像分类的卷积神经网络包含两部分:首先是一系列池化层和卷积层,最后是一个密集连接分类器。第一部分叫作模型的卷积基(convolutional base)。对于卷积神经网 络而言,特征提取就是取出之前训练好的网络的卷积基,在上面运行新数据,然后在输出上面训练一个新的分类器。
    使用在 ImageNet 上训练的 VGG16 网络的卷积基从 猫狗图像中提取有趣的特征,然后在这些特征上训练一个猫狗分类器。

    #将 VGG16 卷积基实例化
    from keras.applications import VGG16
    conv_base = VGG16(weights='imagenet', include_top=False,input_shape=(150, 150, 3))
    conv_base.summary()
    
    1. 不使用数据增强的快速特征提取
      速度快,计算代价低
      使用预训练的卷积基提取特征
    import os
    import numpy as np
    from keras.preprocessing.image import ImageDataGenerator
    base_dir = '/Users/fanhua/Desktop/learning_log/net_work/kaggle/cats_and_dogs_small' 
    train_dir = os.path.join(base_dir, 'train') 
    validation_dir = os.path.join(base_dir, 'validation') 
    test_dir = os.path.join(base_dir, 'test')
    datagen = ImageDataGenerator(rescale=1./255) 
    batch_size = 20
    def extract_features(directory, sample_count):
        features = np.zeros(shape=(sample_count, 4, 4, 512)) 
        labels = np.zeros(shape=(sample_count))
        generator = datagen.flow_from_directory(
            directory, target_size=(150, 150), 
            batch_size=batch_size, 
            class_mode='binary')
        i=0
        for inputs_batch, labels_batch in generator:
            features_batch = conv_base.predict(inputs_batch)
            features[i * batch_size : (i + 1) * batch_size] = features_batch
            labels[i * batch_size : (i + 1) * batch_size] = labels_batch
            i += 1
            if i * batch_size >= sample_count:
                break
        return features, labels
    train_features, train_labels = extract_features(train_dir, 2000) 
    validation_features, validation_labels = extract_features(validation_dir, 1000) 
    test_features, test_labels = extract_features(test_dir, 1000)
    #要将其输入到密集连接分类器中, 所以首先必须将其形状展平为 (samples, 8192)。
    train_features = np.reshape(train_features, (2000, 4 * 4 * 512)) 
    validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512)) 
    test_features = np.reshape(test_features, (1000, 4 * 4 * 512))
    

    定义并训练密集连接分类器

    from keras import models 
    from keras import layers 
    from keras import optimizers
    model = models.Sequential()
    model.add(layers.Dense(256, activation='relu', input_dim=4 * 4 * 512)) 
    model.add(layers.Dropout(0.5))
    model.add(layers.Dense(1, activation='sigmoid'))
    model.compile(optimizer=optimizers.RMSprop(lr=2e-5), 
                  loss='binary_crossentropy',
                  metrics=['acc'])
    history = model.fit(train_features, 
                        train_labels, 
                        epochs=30,
                        batch_size=20,
                        validation_data=(validation_features, validation_labels))
    
    

    绘制结果

    import matplotlib.pyplot as plt
    acc = history.history['acc']
    val_acc = history.history['val_acc'] 
    loss = history.history['loss'] 
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label='Training acc') 
    plt.plot(epochs, val_acc, 'b', label='Validation acc') 
    plt.title('Training and validation accuracy') 
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss') 
    plt.plot(epochs, val_loss, 'b', label='Validation loss') 
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()
    

    2、使用数据增强的特征提取
    速度慢、计算代价高
    在卷积基上添加一个密集连接分类器

    from keras import models
    from keras import layers
    model = models.Sequential() 
    model.add(conv_base) 
    model.add(layers.Flatten()) 
    model.add(layers.Dense(256, activation='relu')) 
    model.add(layers.Dense(1, activation='sigmoid'))
    model.summary()
    

    在编译和训练模型之前,一定要“冻结”卷积基。冻结(freeze)一个或多个层是指在训练过程中保持其权重不变。如果不这么做,那么卷积基之前学到的表示将会在训练过程中被修改。 因为其上添加的 Dense 层是随机初始化的,所以非常大的权重更新将会在网络中传播,对之前学到的表示造成很大破坏。

    print('This is the number of trainable weights before freezing the conv base:', len(model.trainable_weights))
    conv_base.trainable = False
    print('This is the number of trainable weights after freezing the conv base:', len(model.trainable_weights))
    

    利用冻结的卷积基端到端地训练模型

    from keras.preprocessing.image import ImageDataGenerator
    from keras import optimizers
    train_datagen = ImageDataGenerator(rescale=1./255,
                                       rotation_range=40, 
                                       width_shift_range=0.2, 
                                       height_shift_range=0.2, 
                                       shear_range=0.2, 
                                       zoom_range=0.2, 
                                       horizontal_flip=True, 
                                       fill_mode='nearest')
    test_datagen = ImageDataGenerator(rescale=1./255)
    train_generator = train_datagen.flow_from_directory(train_dir,
                                                        target_size=(150, 150),
                                                        batch_size=20,
                                                        class_mode='binary')
    validation_generator = test_datagen.flow_from_directory( validation_dir,
                                                            target_size=(150, 150), 
                                                            batch_size=20, 
                                                            class_mode='binary')
    model.compile(loss='binary_crossentropy', 
                  optimizer=optimizers.RMSprop(lr=2e-5),
                  metrics=['acc'])
    history = model.fit_generator( train_generator,
                                  steps_per_epoch=100,
                                  epochs=30, 
                                  validation_data=validation_generator, 
                                  validation_steps=50)
    
    

    模型微调(fine-tuning)
    另一种广泛使用的模型复用方法是模型微调,与特征提取互为补充。


    微调 VGG16 网络的最后一个卷积块

    微调网络步骤:
    (1) 在已经训练好的基网络(base network)上添加自定义网络。
    (2) 冻结基网络。
    (3) 训练所添加的部分。
    (4) 解冻基网络的一些层。
    (5) 联合训练解冻的这些层和添加的部分
    卷积基架构:

    conv_base.summary()
    

    冻结直到某一层的所有层

    conv_base.trainable = True
    set_trainable = False
    for layer in conv_base.layers:
    if layer.name == 'block5_conv1': 
        set_trainable = True
    if set_trainable: 
        layer.trainable = True
    else:
        layer.trainable = False
    

    微调模型

    model.compile(loss='binary_crossentropy', 
                  optimizer=optimizers.RMSprop(lr=1e-5),
                  metrics=['acc'])
    history = model.fit_generator( train_generator,
                                  steps_per_epoch=100,
                                  epochs=100, 
                                  validation_data=validation_generator, 
                                  validation_steps=50)
    
    

    绘制结果
    使曲线平滑

    def smooth_curve(points, factor=0.8): 
        smoothed_points = []
    for point in points:
        if smoothed_points:
            previous = smoothed_points[-1]
            smoothed_points.append(previous * factor + point * (1 - factor))
        else: 
            smoothed_points.append(point)
    return smoothed_points
    plt.plot(epochs,smooth_curve(acc), 'bo', label='Smoothed training acc')
    plt.plot(epochs,smooth_curve(val_acc), 'b', label='Smoothed validation acc')
    plt.title('Training and validation accuracy') 
    plt.legend()
    plt.figure()
    plt.plot(epochs,
    smooth_curve(loss), 'bo', label='Smoothed training loss')
    plt.plot(epochs,smooth_curve(val_loss), 'b', label='Smoothed validation loss')
    plt.title('Training and validation loss') 
    plt.legend()
    plt.show()
    

    卷积神经网络的可视化
    可视化卷积神经网络的中间输出(中间激活)
    可视化中间激活,是指对于给定输入,展示网络中各个卷积层和池化层输出的特征图(层 的输出通常被称为该层的激活,即激活函数的输出)。

    #加载模型
    from keras.models import load_model 4 
    model = load_model('cats_and_dogs_small_2.h5')
    model.summary() 
    #预处理单张图像
    
    

    可视化卷积神经网络的过滤器
    可视化图像中类激活的热力图

    相关文章

      网友评论

          本文标题:【2019-07-16】深度学习用于计算机视觉

          本文链接:https://www.haomeiwen.com/subject/jklxlctx.html