美文网首页
keras学习-CNN

keras学习-CNN

作者: 锦绣拾年 | 来源:发表于2020-07-21 21:57 被阅读0次

keras学习-卷积神经网络部分

参考《Python 深度学习》一书

卷积神经网络

from keras import layers
from keras import models

model = models.Sequential()
# 32 3×3
'''
filter:整数,卷积输出滤波器的数量。
kernel_size:2个整数或2个整数构成的元组/列表,指定2-dim卷积窗口的高度和宽度。可以是单个整数,以指定具有相同值的所有空间维度。
'''
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
#28-2 = 26
model.add(layers.MaxPooling2D((2, 2)))
#26/2=13 32个13×13
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
# 64个 13-2=11 64个11×11
model.add(layers.MaxPooling2D((2, 2)))
# 11/2=5
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
#5-2=3
model.add(layers.Flatten())#展平 64×9=576
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten (Flatten)            (None, 576)               0         
_________________________________________________________________
dense (Dense)                (None, 64)                36928     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________

在使用mnist数据训练时

from keras.datasets import mnist
from keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.shape)
#原本shape(60000,28,28)
#这个地方注意一下
#不过input shape变成28*28呢?
#不行,conv2D接受输入是个四维向量,所以根据这个变更一下向量的shape为(60000, 28, 28, 1)
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
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)))
#32个148×148
#输出空间的维度,滤波器的数量 ,每一个Filter的深度要和输入层深度保持一致。
'''
filters参数同input_channel参数连接到kernel_size后面。所以filters参数是kernel_shape四元组的最后一个元素。
而kernel_shape元祖格式为(height,width,input_channel, output_channel)所以filter就是卷积操作后结果输出的通道数。
相当于图像卷积后输出的图像通道数。
作者:wkgreat
链接:https://www.jianshu.com/p/fbc616b59cfd
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
'''
#想起来了 面对×3这种,可能是把结果加起来(输入通道的结果加起来得到一个输出通道的结果),(其实不是像想象的一样扩张了一下什么的。)
model.add(layers.MaxPooling2D((2, 2)))
#148/2=74 32个74×74
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
#64个72×72
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'))

使用Imagegenerator对图片数据进行预处理:

from keras.preprocessing.image import ImageDataGenerator

# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=20,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
for data_batch, labels_batch in train_generator:
    print('data batch shape:', data_batch.shape)
    print('labels batch shape:', labels_batch.shape)
    break
data batch shape: (20, 150, 150, 3)
labels batch shape: (20,)
    
history = model.fit_generator(# 使用生成器进行训练,这里用的是fit_generator
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)

使用数据增强后:

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,)

# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=32,
        # Since we use binary_crossentropy loss, we need binary labels
        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)

fit & fit_generator

https://blog.csdn.net/xovee/article/details/91357143

当你使用.fit()函数时,意味着如下两个假设:
训练数据可以 完整地 放入到内存(RAM)里
数据已经不需要再进行任何处理了
fit_generator()与fit()的主要区别就在一个generator上。之前,我们把整个训练数据都输入到fit()里,我们也不需要考虑batch的细节;现在,我们使用一个generator,每次生成一个batch送给fit_generator()训练

目前看到的写法

关于generator部分需要再研究

#在TensorFlow2.0里用flow方法,有以下这种写法
history = model.fit(image_gen_train.flow(x_train, y_train, batch_size=64), epochs=12,
                    validation_data=(x_test, y_test),
                    validation_freq=1, callbacks=[cp_callback], verbose=1,shuffle=True)

history = model.fit(x_train,y_train,, batch_size=64, epochs=100,validation_data=(x_test,y_test),validation_freq=2)#直接写的数据

history = model.fit_generator(
      train_generator,#写的是generator
      steps_per_epoch=100,
      epochs=100,
      validation_data=validation_generator,
      validation_steps=50)


这里涉及到的是keras里的数据增强,对于数据增强有了新的理解,它用了generator,所以它得到的那些改造新数据,不是一下子生成的,而是在训练中不断迭代,不断生成改造得到新的图片,所以说数据增强对提供模型泛化能力应该还是很大的。

数据增强时 注意不可以增强验证数据

数据增强方法:

ImageDataGenerator

比较详细的介绍

https://www.cnblogs.com/Dean0731/p/12341141.html

flow flor_from_dataframe flow_from_directory(后两个方法有固定的标签、数据格式)

使用预训练的卷积神经网络

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

为什么仅重复使用卷积基?我们能否也重复使用密集连接分类器?一般来说,应该避免这
么做。原因在于卷积基学到的表示可能更加通用,因此更适合重复使用。

卷积神经网络的特征图表示通用概念在图像中是否存在,无论面对什么样的计算机视觉问题,这种特征图都可能很有用。

但是,分类器学到的表示必然是针对于模型训练的类别,其中仅包含某个类别出现在整张图像中的概率信息。

此外,密集连接层的表示不再包含物体在输入图像中的位置信息。密集连接层舍弃了空间的概念,而物体位置信息仍然由卷积特征图所描述。如果物体位置对于问题很重要,那么密集连接层的特征在很大程度上是无用的。

注意,某个卷积层提取的表示的通用性(以及可复用性)取决于该层在模型中的深度。模型中更靠近【底部】的层提取的是局部的、高度通用的特征图(比如视觉边缘、颜色和纹理),而更靠近【顶部】的层提取的是更加抽象的概念(比如“猫耳朵”或“狗眼睛”) 因此,如果你的新数据集与原始模型训练的数据集有很大差异,那么最好只使用模型的【前几层】(浅层)来做特征提取,而不是使用整个卷积基。(这个应该是之前实验的结果)
本例中,由于 ImageNet 的类别中包含多种狗和猫的类别,所以重复使用原始模型密集连接
层中所包含的信息可能很有用。但我们选择不这么做,以便涵盖新问题的类别与原始模型的类
别不一致的更一般情况。我们来实践一下,使用在 ImageNet 上训练的 VGG16 网络的卷积基从猫狗图像中提取有趣的特征,然后在这些特征上训练一个猫狗分类器。
VGG16 等模型内置于 Keras 中。你可以从 keras.applications 模块中导入。下面是
keras.applications 中的一部分图像分类模型(都是在 ImageNet 数据集上预训练得到的):

  • Xception
  • Inception V3
  • ResNet50
  • VGG16
  • VGG19
  • MobileNet
from keras.applications import VGG16

conv_base = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(150, 150, 3))
from keras import models
from keras import layers

model = models.Sequential()
model.add(conv_base)#直接把vgg输入添加到层里
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
'''
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
vgg16 (Model)                (None, 4, 4, 512)         14714688  
_________________________________________________________________
flatten_1 (Flatten)          (None, 8192)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 256)               2097408   
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 257       
=================================================================
Total params: 16,812,353
Trainable params: 16,812,353
Non-trainable params: 0
_________________________________________________________________
'''

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

【这一点我好像之前做的不对,我都是重新训练了】

如此设置之后,只有添加的两个 Dense 层的权重才会被训练。总共有 4 个权重张量,每层
2 个(主权重矩阵和偏置向量)。注意,为了让这些修改生效,你必须先编译模型。如果在编译之后修改了权重的 trainable 属性,那么应该重新编译模型,否则这些修改将被忽略

conv_base.trainable = True

set_trainable = False
for layer in conv_base.layers:
    if layer.name == 'block5_conv1':
        set_trainable = True# 'block5_conv1'分类层的开始 这个时候开始训练
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False

卷积神经网络可视化

中间激活

from keras.models import load_model

model = load_model('cats_and_dogs_small_2.h5')
model.summary()  # As a reminder.

img_path = '/Users/fchollet/Downloads/cats_and_dogs_small/test/cats/cat.1700.jpg'

# We preprocess the image into a 4D tensor
from keras.preprocessing import image
import numpy as np

img = image.load_img(img_path, target_size=(150, 150))# keras.preprocessing 中处理图片的函数
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)# 扩充图片维度
# Remember that the model was trained on inputs
# that were preprocessed in the following way:
img_tensor /= 255.

# Its shape is (1, 150, 150, 3)
print(img_tensor.shape)
#(1, 150, 150, 3)
#这里是三通道,所以可以直接用plt展示
import matplotlib.pyplot as plt

plt.imshow(img_tensor[0])
plt.show()
#图片展示
from keras import models
# 这里选取部分层 进行训练得到结果。
# Extracts the outputs of the top 8 layers:
layer_outputs = [layer.output for layer in model.layers[:8]]#提取前8层的输出
# Creates a model that will return these outputs, given the model input:
activation_model = models.Model(inputs=model.input, outputs=layer_outputs)

# This will return a list of 5 Numpy arrays:
# one array per layer activation
activations = activation_model.predict(img_tensor)
#返回8个numpy数组组成的列表,每个层激活对应一个numpy数组
first_layer_activation = activations[0]# 第一个卷积层的激活
print(first_layer_activation.shape)
#(1, 148, 148, 32) 

#它是大小为 148×148 的特征图,有 32 个通道。我们来绘制原始模型第一层激活的第 3 个通道
import matplotlib.pyplot as plt

plt.matshow(first_layer_activation[0, :, :, 3], cmap='viridis')
plt.show()
plt.matshow(first_layer_activation[0, :, :, 7], cmap='viridis')#第七个通道可视化

import keras

# These are the names of the layers, so can have them as part of our plot
layer_names = []
for layer in model.layers[:8]:
    layer_names.append(layer.name)

images_per_row = 16 #16行

# Now let's display our feature maps
for layer_name, layer_activation in zip(layer_names, activations):
    # This is the number of features in the feature map
    ##(1, 148, 148, 32) 
    n_features = layer_activation.shape[-1] #特征图中的特征个数

    # The feature map has shape (1, size, size, n_features)
    size = layer_activation.shape[1]

    # We will tile the activation channels in this matrix
    #在这个矩阵中将激活通道平铺
    n_cols = n_features // images_per_row
    display_grid = np.zeros((size * n_cols, images_per_row * size))

    # We'll tile each filter into this big horizontal grid
    for col in range(n_cols):
        for row in range(images_per_row):
            #将每个过滤器平铺到一个大的水平网格中
            #然后提取出每一层每个特征
            channel_image = layer_activation[0,
                                             :, :,
                                             col * images_per_row + row]
            # Post-process the feature to make it visually palatable
            '''
            将每个过滤器平铺到一个大的水平网格中
            '''
            channel_image -= channel_image.mean()
            channel_image /= channel_image.std()
            channel_image *= 64
            channel_image += 128
            channel_image = np.clip(channel_image, 0, 255).astype('uint8')
            display_grid[col * size : (col + 1) * size,
                         row * size : (row + 1) * size] = channel_image
            #显示网格

    # Display the grid
    scale = 1. / size
    plt.figure(figsize=(scale * display_grid.shape[1],
                        scale * display_grid.shape[0]))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')
    
plt.show()
1595339369(1).png

这里需要注意以下几点。

  • 【第一层是各种边缘探测器的集合。在这一阶段,激活几乎保留了原始图像中的所有信息。】
    -【 随着层数的加深,激活变得越来越抽象,并且越来越难以直观地理解。它们开始表示更
    高层次的概念,比如“猫耳朵”和“猫眼睛”。层数越深,其表示中关于图像视觉内容的信息就越少,而关于类别的信息就越多。】
  • 【激活的稀疏度(sparsity)随着层数的加深而增大。在第一层里,所有过滤器都被输入图
    像激活,但在后面的层里,越来越多的过滤器是空白的。也就是说,输入图像中找不到这些过滤器所编码的模式。】
    我们刚刚揭示了深度神经网络学到的表示的一个重要普遍特征:随着层数的加深,层所提取的特征变得越来越抽象。更高的层激活包含关于特定输入的信息越来越少,而关于目标的信息越来越多(本例中即图像的类别:猫或狗)。深度神经网络可以有效地作为信息蒸馏管道
    (information distillation pipeline),输入原始数据(本例中是 RGB 图像),反复对其进行变换,将无关信息过滤掉(比如图像的具体外观),并放大和细化有用的信息(比如图像的类别)。

可视化卷积神经网络的过滤器

跳过【待补充,可以再看一下】

可视化类激活的热力图

跳过【待补充,可以再看一下】

相关文章

网友评论

      本文标题:keras学习-CNN

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