VGG网络与中间层特征提取

本文介绍了VGG网络的结构和特点,它是用于大型图片识别的深度卷积网络。文章详细展示了如何在Keras中构建和训练VGG模型,以及如何在ImageNet数据集上进行预测。同时,讨论了从深度学习模型中提取特征的重要性,并以VGG-16和InceptionV3为例,说明了特征提取在迁移学习中的应用。
部署运行你感兴趣的模型镜像

1. 背景

VGG是常见的用于大型图片识别的极深度卷积网络,

这里主要介绍VGG网络预测在ImageNet数据集上的训练及预测。

小知识:

1. VGG块的组成规律是: 连续使用数个相同的填充为1、窗口形状为3*3的卷积层后接上一个步幅为2、窗口大小为2*2的最大池化层。卷积层保持输入的高和宽不变,而池化层则对其减半。

2. VGG特点: 高和宽减半、通道翻倍的设计使其多数卷积层都有相同的模型参数尺寸和计算复杂度。

2. ImageNet图像数据集简介

        ImageNet包含了145W张224*224像素的三通道彩色图像数据集,图像划分为1000个种类。其中训练集130W张,验证集5W张,测试集10W。

数据加载及预处理:
 

3. VGG 网络

3.1 网络定义

from keras.models import Sequential
from keras.layers.convolutional import Conv2D, MaxPooling2D, ZeroPadding2D
from keras.layers.core import Activation, Flatten, Dense, Dropout
from keras.datasets import cifar10
from keras.utils import np_utils
from keras.optimizers import SGD, RMSprop
import cv2
import numpy as np

NB_EPOCH = 5
BATCH_SIZE = 128
VALIDATION_SPLIT = 0.2
# IMG_ROWS, IMG_COLS = 224, 224
IMG_ROWS, IMG_COLS = 32, 32

IMG_CHANNELS = 3
INPUT_SHAPE = (IMG_ROWS, IMG_COLS, IMG_CHANNELS)  # 注意顺序
NB_CLASSES = 1000

class VGGNet:
    @staticmethod
    def build(input_shape, classes, weights_path=None):
        model = Sequential()
        model.add(ZeroPadding2D((1, 1), input_shape=input_shape))
        model.add(Conv2D(64, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(64, kernel_size=3, activation='relu'))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(128, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(128, kernel_size=3, activation='relu'))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(256, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(256, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(256, kernel_size=3, activation='relu'))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(512, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(512, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(512, kernel_size=3, activation='relu'))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(512, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))

        model.add(Conv2D(512, kernel_size=3, activation='relu'))
        model.add(ZeroPadding2D((1, 1)))
        model.add(Conv2D(512, kernel_size=3, activation='relu'))
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        model.add(Flatten())
        model.add(Dense(4096, activation='relu'))
        model.add(Dropout(0.5))
        model.add(Dense(4096, activation='relu'))
        model.add(Dropout(0.5))
        model.add(Dense(classes, activation='softmax'))
        model.summary()  # 概要汇总网络
        if weights_path:
            model.load_weights(weights_path)
        return model

3.2 数据加载

def load_and_proc_data():
    (X_train, y_train), (X_test, y_test) = cifar10.load_data()
    print('X_train shape', X_train.shape)

    X_train = X_train.astype('float32')
    X_test = X_test.astype('float32')
    X_train /= 255
    X_test /= 255

    # 将类向量转换成二值类别矩阵
    y_train = np_utils.to_categorical(y_train, NB_CLASSES)
    y_test = np_utils.to_categorical(y_test, NB_CLASSES)
    return X_train, X_test, y_train, y_test

3.3 模型训练与保存

def model_train(X_train, y_train):
    OPTIMIZER = RMSprop()
    model = VGGNet.build(input_shape=INPUT_SHAPE, classes=NB_CLASSES)
    model.compile(loss='categorical_crossentropy', optimizer=OPTIMIZER, metrics=['accuracy'])
    model.fit(X_train, y_train, batch_size=BATCH_SIZE, epochs=NB_EPOCH, verbose=0, validation_split=VALIDATION_SPLIT)
    return model

def model_save(model):
    # 保存网络结构
    model_json = model.to_json()
    with open('cifar10_architecture.json', 'w') as f:
        f.write(model_json)
    # 保存网络权重
    model.save_weights('cifar10_weights.h5', overwrite=True)


3.4 模型评估与预测

def model_evaluate(model, X_test, y_test):
    score = model.evaluate(X_test, y_test, batch_size=BATCH_SIZE, verbose=1)
    print('Test score: ', score[0])
    print('Test acc: ', score[1])

def model_predict():
    model = VGGNet().build(INPUT_SHAPE, NB_CLASSES, 'vgg16_weights.h5')
    sgd = SGD()
    model.compile(loss='categorical_crossentropy', optimizer=sgd)
    img = cv2.imread('tain.png')
    im = cv2.resize(img, (224, 224)).astype(np.float32)
    im = im.transpose((2, 0, 1))
    im = np.expand_dims(im, axis=0)
    out = model.predict(im)
    print(np.argmax(out))

3.5 测试

if __name__ == '__main__':
    X_train, X_test, y_train, y_test = load_and_proc_data()
    model = model_train(X_train, y_train)
    # model_save(model)
    model_evaluate(model, X_test, y_test)

4. 使用keras自带的VGG16网络

        keras中模型都是预训练好的,其权重存储在~/.keras/models/中,模型初始化时权重会自动下载。

from keras.optimizers import SGD
from keras.applications.vgg16 import VGG16
import matplotlib.pyplot as plt
import numpy as np
import cv2

model = VGG16(weights='imagenet', include_top=True)
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd)

im = cv2.resize(cv2.imread('tain.png'), (224, 224))
im = np.expand_dims(im, axis=0)
out = model.predict(im)
plt.plot(out.ravel())
plt.show()
print(np.argmax(out))
# 820 蒸汽火车

5. 从深度学习模型特定网络层中提取特征

        深度学习模型天然就具有可重用的特性:可以把一个在大规模数据上训练好的图像分类或语音识别的模型重用在另一个很不一样的问题上,而只需要做有限的一点改动。尤其是在计算机视觉领域,许多预训练的模型现在都被公开下载,并重用在其他问题上以提升在小数据集上的性能。

5.1 为什么从DCNN的中间网络层提取特征?

1. 网络的每一层都在学习识别对最终分类必要的那些特征

2. 低级的网络层识别的是类似颜色和边界这样的顺序特征;

3. 高级的网络层则是把低层的这些顺序特征组合成更高的顺序特征,如形状或者物体等;

因此,中间的网络层才有能力从图像中提取出重要的特征,这些特征有可能对不同的分类有益。

5.2 特征提取的益处?

1. 可以依赖大型的公开可用的训练并把学习转换到新的领域;

2. 可以为大型的耗时训练节省时间;

3. 即使该领域没有足够多的训练样例,也可以提供出合理的解决方案(有了一个良好的初始网络模型)。

5.3 从VGG-16网络特定网络层中进行特征提取

from keras.models import Model
from keras.preprocessing import image
from keras.applications.vgg16 import VGG16, preprocess_input
from keras.optimizers import SGD
import numpy as np
import cv2

# 加载预训练好的VGG16模型
base_model = VGG16(weights='imagenet', include_top=True)
for i, layer in enumerate(base_model.layers):
    print(i, layer.name, layer.output_shape)

5.4 极深度 inception-v3 网络中间层特征提取

         迁移学习是非常强大的深度学习技术,在那些数据集没有大到足以从头训练整个CNN的地方, 通常会使用预训练好的CNN网络来生成新任务的代理,然后对此网络进行微调。

inception-v3是google开发的一个深度网络。

1. 加载与训练网络模型

可选择是否包括顶部处理层

from keras.applications.inception_v3 import InceptionV3
from keras.models import Model
from keras.preprocessing import image
from keras.layers import Dense, GlobalAveragePooling2D

# 加载预训练好的InceptionV3模型,不包括顶部的网络层,以便在新数据集上进行微调
base_model = InceptionV3(weights='imagenet', include_top=False)

for i, layer in enumerate(base_model.layers):
    print(i, layer.name, layer.output_shape)

 2. 冻结特定网络层,修改网络结构

# 冻结所有卷积InceptionV3层
for layer in base_model.layers:
    layer.trainable = False

x = base_model.output  # (n_samples, rows, cols, channels)
# shape=(None, None, None, 2048), name='mixed10/concat:0')
# 把输入转换成dense层可以处理的形状 (n_samples, channels)
x = GlobalAveragePooling2D()(x)  
# shape=(None, 2048), name='global_average_pooling2d/Mean:0')
x = Dense(1024, activation='relu')(x)
# shape=(None, 1024), name='dense/Relu:0'
preds = Dense(200, activation='softmax')(x)
# shape=(None, 200), name='dense_1/Softmax:0'

3. 调整优化后,重新编译模型

model = Model(input=base_model.input, output=preds)  # ????该句有问题

# 只冻结前172层
for layer in model.layers[:172]:
    layer.trainable = False
for layer in model.layers[172:]:
    layer.trainable = True

model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
# 将模型在新数据集上进行训练
# model.fit_generator(...)

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.15

TensorFlow-v2.15

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

<think>好的,我现在需要帮助用户解决如何使用VGG16模型提取全连接层的特征值的问题。首先,我需要回顾一下用户提供的引用内容,确保回答准确。 根据引用[3],VGG16的结构分为13个卷积层和3个全连接层(fc6、fc7、fc8)。用户的目标是提取全连接层的特征值,所以需要明确全连接层的位置和如何访问它们。 接下来,我应该考虑不同深度学习框架下的实现方法,比如PyTorch和TensorFlow/Keras,因为用户可能使用不同的框架。在PyTorch中,可以通过修改模型的前向传播函数或使用钩子(hooks)来获取中间层的输出。而Keras则提供了更简便的方式,比如使用Model类来创建中间输出模型。 需要提醒用户注意预处理步骤,比如图像归一化,因为VGG16对输入有特定的要求。此外,全连接层之前的特征需要经过展平处理,这部分可能在模型中已经包含,比如在PyTorch的Sequential模块中,view或flatten操作可能会被应用。 还要提到迁移学习中修改全连接层的情况。如果用户在自己的任务中修改了fc8层,那么在提取特征时应该使用修改前的模型,或者确保加载的是原始权重。这一点需要根据用户的具体情况来处理,可能需要加载预训练权重并冻结部分层。 最后,生成相关问题,帮助用户进一步理解相关概念,比如不同框架的实现差异、迁移学习的应用,以及特征提取后的具体应用场景。确保问题覆盖了用户可能感兴趣的扩展内容。</think>要使用VGG16模型提取全连接层的特征值,需根据深度学习框架选择具体方法。以下是分步说明及代码示例: ### 方法一:PyTorch实现 1. **加载预训练模型** ```python import torch from torchvision import models model = models.vgg16(weights='VGG16_Weights.IMAGENET1K_V1') ``` 2. **截断模型至目标全连接层** 通过修改前向传播函数获取fc6/fc7层输出: ```python class FeatureExtractor(torch.nn.Module): def __init__(self, original_model): super().__init__() self.features = original_model.features self.avgpool = original_model.avgpool self.classifier = original_model.classifier[:4] # 截取到fc6层 def forward(self, x): x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.classifier(x) return x ``` ### 方法二:TensorFlow/Keras实现 ```python from tensorflow.keras.applications import VGG16 from tensorflow.keras.models import Model base_model = VGG16(weights='imagenet') feature_model = Model(inputs=base_model.input, outputs=base_model.get_layer('fc2').output) # 提取fc7层特征 features = feature_model.predict(preprocessed_image) ``` ### 关键注意事项 1. **输入预处理** 必须对输入图像进行归一化处理: - PyTorch: 使用`transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])` - TensorFlow: 内置在`preprocess_input`函数中 2. **特征维度说明** - `fc6`层输出维度:$4096$ - `fc7`层输出维度:$4096$ - `fc8`层输出维度:$1000$(原始ImageNet分类维度) 3. **迁移学习场景** 若需自定义分类层,应保留原始全连接层参数: ```python for param in model.classifier.parameters(): param.requires_grad = False # 冻结全连接层参数 ``` [^1]: VGG16的改进主要在于采用连续小卷积核替代大卷积核,增强非线性表达能力 [^2]: 卷积层计算遵循$y_{ij} = \sum_{k} W_{ik} * x_{kj} + b_i$的线性组合公式 [^3]: 全连接层特征提取需注意输入尺寸匹配问题,原始模型要求输入为$224\times224$ RGB图像
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值