微信扫一扫打赏支持

《python深度学习》笔记---5.3-2、猫狗分类(使用预训练网络-实战)

《python深度学习》笔记---5.3-2、猫狗分类(使用预训练网络-实战)

一、总结

一句话总结:

【卷积就是特征提取】:从预训练网络训练猫狗分类,可以更加方便的理解卷积层就是特征提取
【使用预训练网络效果非常好】:我们的验证精度达到了约90%,比上一节从头开始训练的小型模型效果要好得多。但从图 中也可以看出,虽然 dropout 比率相当大,但模型几乎从一开始就过拟合。这是因为本方法没有 使用数据增强,而数据增强对防止小型图像数据集的过拟合非常重要。

 

 

1、引入vgg16已经训练好的模型?

from tensorflow.keras.applications import VGG16
conv_base = VGG16(weights='imagenet',include_top=False,input_shape=(150, 150, 3))
# 把vgg模型弄过来
conv_base = VGG16(weights='imagenet',    
                  # include_top=False表示不包含dense层
                  include_top=False,                   
                  input_shape=(150, 150, 3))

这里向构造函数中传入了三个参数。

‰ weights 指定模型初始化的权重检查点。
‰ include_top 指定模型最后是否包含密集连接分类器。默认情况下,这个密集连接分类器对应于ImageNet 的 1000 个类别。因为我们打算使用自己的密集连接分类器(只有 两个类别:cat 和 dog),所以不需要包含它。 
‰ input_shape 是输入到网络中的图像张量的形状。这个参数完全是可选的,如果不传 入这个参数,那么网络能够处理任意形状的输入。

 

 

2、利用预训练好的VGG16抽取训练集、验证集和测试集的特征?

【主要就是VGG16的predict方法】:features_batch = conv_base.predict(inputs_batch)
'''
directory:目录
sample_count:样本数
'''
def extract_features(directory, sample_count):
    # 初始化 features
    features = np.zeros(shape=(sample_count, 4, 4, 512))      
    # 初始化 labels
    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: 
        # 调用 conv_base 模型的 predict 方法来从这些图像中提取特征。
        features_batch = conv_base.predict(inputs_batch)         
        # 切片依次找到features和labels
        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)

 

 

3、预训练好的VGG16后面接dense层实例?

其实就是把vgg16提取好的特征放到作为输入数据放到dense网络里面就好
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)) 

 

 

二、5.3-2、猫狗分类(使用预训练网络-实战)

博客对应课程的视频位置:

 

import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
In [3]:
from tensorflow.keras.applications import VGG16 

# 把vgg模型弄过来
conv_base = VGG16(weights='imagenet',    
                  # include_top=False表示不包含dense层
                  include_top=False,                   
                  input_shape=(150, 150, 3))
# C:\Users\Fan Renyi\.keras\models\vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5

这里向构造函数中传入了三个参数。

‰ weights 指定模型初始化的权重检查点。

‰ include_top 指定模型最后是否包含密集连接分类器。默认情况下,这个密集连接分类器对应于ImageNet 的 1000 个类别。因为我们打算使用自己的密集连接分类器(只有 两个类别:cat 和 dog),所以不需要包含它。 

‰ input_shape 是输入到网络中的图像张量的形状。这个参数完全是可选的,如果不传 入这个参数,那么网络能够处理任意形状的输入。

1、 不使用数据增强的快速特征提取

首先,运行 ImageDataGenerator 实例,将图像及其标签提取为Numpy 数组。我们需要 调用 conv_base 模型的 predict 方法来从这些图像中提取特征。

In [1]:
import os 
from tensorflow.keras.preprocessing.image import ImageDataGenerator 
In [4]:
base_dir = 'E:\\78_recorded_lesson\\001_course_github\\AI_dataSet\\dogs-vs-cats\\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') 
In [5]:
datagen = ImageDataGenerator(rescale=1./255)  
batch_size = 20 
In [12]:
'''
directory:目录
sample_count:样本数
'''
def extract_features(directory, sample_count):
    # 初始化 features
    features = np.zeros(shape=(sample_count, 4, 4, 512))      
    # 初始化 labels
    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: 
        # 调用 conv_base 模型的 predict 方法来从这些图像中提取特征。
        features_batch = conv_base.predict(inputs_batch)         
        # 切片依次找到features和labels
        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 
In [15]:
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)
Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.
In [17]:
print(train_features.shape)
print(train_labels.shape)
(2000, 4, 4, 512)
(2000,)

目前,提取的特征形状为 (samples, 4, 4, 512)。我们要将其输入到密集连接分类器中, 所以首先必须将其形状展平为 (samples, 8192)。

In [18]:
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)) 
In [19]:
print(train_features.shape)
(2000, 8192)

现在你可以定义你的密集连接分类器(注意要使用dropout 正则化),并在刚刚保存的数据 和标签上训练这个分类器。

In [20]:
from tensorflow.keras import models 
from tensorflow.keras import layers 
from tensorflow.keras import optimizers 
In [21]:
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')) 
In [22]:
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),               
              loss='binary_crossentropy',               
              metrics=['acc']) 
In [23]:
history = model.fit(train_features, train_labels,                     
                    epochs=30,                     
                    batch_size=20,                     
                    validation_data=(validation_features, validation_labels)) 
Epoch 1/30
100/100 [==============================] - 1s 5ms/step - loss: 0.5869 - acc: 0.6830 - val_loss: 0.4461 - val_acc: 0.8280
Epoch 2/30
100/100 [==============================] - 0s 4ms/step - loss: 0.4259 - acc: 0.8145 - val_loss: 0.3650 - val_acc: 0.8550
Epoch 3/30
100/100 [==============================] - 0s 4ms/step - loss: 0.3570 - acc: 0.8530 - val_loss: 0.3341 - val_acc: 0.8670
Epoch 4/30
100/100 [==============================] - 0s 4ms/step - loss: 0.3124 - acc: 0.8780 - val_loss: 0.3015 - val_acc: 0.8750
Epoch 5/30
100/100 [==============================] - 0s 4ms/step - loss: 0.2856 - acc: 0.8885 - val_loss: 0.2838 - val_acc: 0.8840
Epoch 6/30
100/100 [==============================] - 0s 4ms/step - loss: 0.2666 - acc: 0.8960 - val_loss: 0.2731 - val_acc: 0.8880
Epoch 7/30
100/100 [==============================] - 0s 4ms/step - loss: 0.2477 - acc: 0.9030 - val_loss: 0.2646 - val_acc: 0.8910
Epoch 8/30
100/100 [==============================] - 0s 4ms/step - loss: 0.2388 - acc: 0.9080 - val_loss: 0.2625 - val_acc: 0.8920
Epoch 9/30
100/100 [==============================] - 0s 4ms/step - loss: 0.2251 - acc: 0.9130 - val_loss: 0.2534 - val_acc: 0.8960
Epoch 10/30
100/100 [==============================] - 0s 4ms/step - loss: 0.2059 - acc: 0.9255 - val_loss: 0.2513 - val_acc: 0.8970
Epoch 11/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1988 - acc: 0.9265 - val_loss: 0.2598 - val_acc: 0.8920
Epoch 12/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1903 - acc: 0.9320 - val_loss: 0.2458 - val_acc: 0.8990
Epoch 13/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1818 - acc: 0.9335 - val_loss: 0.2417 - val_acc: 0.9050
Epoch 14/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1786 - acc: 0.9340 - val_loss: 0.2402 - val_acc: 0.9030
Epoch 15/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1706 - acc: 0.9390 - val_loss: 0.2375 - val_acc: 0.9020
Epoch 16/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1574 - acc: 0.9480 - val_loss: 0.2349 - val_acc: 0.9020
Epoch 17/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1587 - acc: 0.9455 - val_loss: 0.2346 - val_acc: 0.9060
Epoch 18/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1448 - acc: 0.9500 - val_loss: 0.2474 - val_acc: 0.9000
Epoch 19/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1411 - acc: 0.9485 - val_loss: 0.2345 - val_acc: 0.9010
Epoch 20/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1383 - acc: 0.9510 - val_loss: 0.2326 - val_acc: 0.9030
Epoch 21/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1313 - acc: 0.9570 - val_loss: 0.2412 - val_acc: 0.9010
Epoch 22/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1262 - acc: 0.9600 - val_loss: 0.2337 - val_acc: 0.9040
Epoch 23/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1213 - acc: 0.9600 - val_loss: 0.2347 - val_acc: 0.9040
Epoch 24/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1164 - acc: 0.9615 - val_loss: 0.2337 - val_acc: 0.9020
Epoch 25/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1124 - acc: 0.9630 - val_loss: 0.2342 - val_acc: 0.9040
Epoch 26/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1033 - acc: 0.9650 - val_loss: 0.2321 - val_acc: 0.9070
Epoch 27/30
100/100 [==============================] - 0s 4ms/step - loss: 0.1029 - acc: 0.9685 - val_loss: 0.2391 - val_acc: 0.9040
Epoch 28/30
100/100 [==============================] - 0s 4ms/step - loss: 0.0988 - acc: 0.9670 - val_loss: 0.2335 - val_acc: 0.9090
Epoch 29/30
100/100 [==============================] - 0s 4ms/step - loss: 0.0935 - acc: 0.9720 - val_loss: 0.2416 - val_acc: 0.9030
Epoch 30/30
100/100 [==============================] - 0s 4ms/step - loss: 0.0933 - acc: 0.9730 - val_loss: 0.2348 - val_acc: 0.9080

训练速度非常快,因为你只需处理两个 Dense 层。即使在CPU 上运行,每轮的时间也不 到一秒钟。

训练期间的损失曲线和精度曲线

In [26]:
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, 'b--', label='Training acc') 
plt.plot(epochs, val_acc, 'r-', label='Validation acc') 
plt.title('Training and validation accuracy') 
plt.legend() 

plt.figure() 

plt.plot(epochs, loss, 'b--', label='Training loss') 
plt.plot(epochs, val_loss, 'r-', label='Validation loss') 
plt.title('Training and validation loss') 
plt.legend() 

plt.show()

我们的验证精度达到了约90%,比上一节从头开始训练的小型模型效果要好得多。但从图 中也可以看出,虽然 dropout 比率相当大,但模型几乎从一开始就过拟合。这是因为本方法没有 使用数据增强,而数据增强对防止小型图像数据集的过拟合非常重要。

根本原因的话其实就是 因为预训练的vgg16中训练了很多数据,而这些数据使准确率提供很正常

In [ ]:
 
 
posted @ 2020-10-11 17:20  范仁义  阅读(766)  评论(0编辑  收藏  举报