计算机视觉3.3 :迁移学习之图像特征向量提取与运用

本文介绍了如何利用迁移学习中的预训练模型VGG16,作为特征提取器来处理图像数据。通过在大型数据集如ImageNet上预训练的模型,对新的数据集进行特征抽取,然后使用简单的线性分类器(如逻辑回归)进行分类,从而提高分类效率和准确性。文章详细讲解了如何使用HDF5存储大量特征数据,并展示了如何用Python实现特征提取和模型训练的过程。
摘要由CSDN通过智能技术生成
迁移学习之图像特征向量提取与运用

​ 本篇文章将要讨论的是关于计算机视觉中迁移学习的概念,一种能够利用预先训练好的模型,从它之前训练的数据集之外的数据集进行学习的能力。

​ 举个例子来说:

​ 现有A,B两个不同的数据集,我们的任务是能够识别中A,B中不同类别的图像(分类任务)

​ 常规做法:分别在A数据集上训练模型X,在B数据集上训练模型Y

​ 迁移学习的做法:在A数据集上训练模型X,改造训练好的模型X,再用X去B数据集上训练。

适用情况

​ 深度神经网络X已经在大型数据集(例如ImageNet)上训练完成。这些训练完成的模型在迁移学习方面表现优异,复用它们的卷积核相对于重新开始训练新的卷积核而言,更加有意义。

分类:
一般来说,应用于深度学习计算机视觉方向的迁移学习有两种类型:

  • 将模型X作为特征提取器,再将提取出来的特征作为其他机器学习算法的输入。
  • 将模型X的FC(全连接层)去掉,然后用新的FC层将其代替,然后微调其权重。

本文将着重讨论第一种类型。

用训练好的CNN来提取特征

到目前为止,我们一直将卷积神经网络当作端到端的分类器:

  1. 把图像输入网络
  2. 将图像通过前向传播穿过整个网络
  3. 从网络末端获取分类概率

​ 但是,没有人规定我们一定要让图像穿越整个网络,我们可以选择在任意一层停下来,例如Activation或者Pool层,这时从网络中取出该值,然后将其作为特征向量。

​ 如果我们将整个图像数据集中的图像都经过上述操作提取出对应的特征向量,然后利用这些提取出来的特征向量来训练现有的机器学习的模型(例如线性SVM,逻辑回归分类器和随机森林。

注意在整个过程中,我们的卷积神经网络部分是不能完成分类操作的,我们只是单纯的将其用作 特征提取器,而下游的机器学习分类器负责从卷积神经网络提取的特征中学习潜在模式。

认识HDF5

​ HDF5是一种由HDF5群创建的二进制数据格式,用来在硬盘上存储巨型数据类型的数据集,同时方便对数据集中的数据进行访问和操作。

​ HDF5中的数据按照层次存储,和文件系统存储数据的方式非常相似。

  • group:数据首先被定义在group中,group就像是一个容器一样的结构,它可以容纳数据集和其他的group。

  • dataset:一旦group被定义,那么数据集就可以被创建在group中。数据集可以被看成是同一种数据类型的多维数据。

​ HDF5是通过C编写的,但是凭借h5py模块,我们可以使用python语言来操纵底层的C API。

​ HDF5令人惊奇的是其与数据交互十分轻松,我们可以在HDF5数据集中存储海量的数据,并且用一种类似于操纵Numpy数组的方式操纵它。

​ 当通过h5py来使用HDF5 的时候,你可以把你的数据当作一个巨大的NumPy数组,这个数组太大了而无妨装入内存中,但是通过HDF5我们仍然可对其进行操作。

​ 最好的点在于,HDF5这种格式是标准化的,意味着存储在HDF5的数据集可以被其他开发者用不同的语言进行读取的操作,比如C,MATLAB和JAVA。

向HDF5中写入数据

​ 工欲善其事,必先利其器。

​ 在开始我们正式工作之前,我们先需要先编写一个小工具,用来读写HDF5文件。

目录结构:

----pyimgsearch
|		|----__init__.py
|		|----callbacks
|		|----inputoutput
|		|		|----__init__.py
|		|		|----hdf5datasetwriter.py
|		|----nn
|		|----preprocessing
|		|----utils
import h5py
import os


class HDF5DatasetWriter:
    def __init__(self, dims, outputPath, dataKey="images",
                 bufSize=1000):
        if os.path.exists(outputPath):
            raise ValueError("The supplied 'outputPath' already exists and "
                             "cannot be overwritten. Manually delete the file before continuing", outputPath)
        self.db = h5py.File(outputPath, "w")
        self.data = self.db.create_dataset(dataKey, dims, dtype="float")
        self.labels = self.db.create_dataset("labels", (dims[0],), dtype="int")
        self.bufsize = bufSize
        self.buffer = {"data" : [], "labels" : []}
        self.idx = 0

    def add(self, rows, labels):
        self.buffer["data"].extend(rows)
        self.buffer["labels"].extend(labels)

        if len(self.buffer["data"]) >= self.bufsize:
            self.flush()

    def flush(self):
        i = self.idx + len(self.buffer["data"])
        self.data[self.idx:i] = self.buffer["data"]
        self.labels[self.idx:i] = self.buffer["labels"]
        self.idx = i
        self.buffer = {"data": [], "labels": []}

    def storeClassLabels(self, classLabels):
        dt = h5py.special_dtype(vlen=str)
        labelSet = self.db.create_dataset("label_names", (len(classLabels),), dtype=dt)
        labelSet[:] = classLabels

    def close(self):
        if len(self.buffer["data"]) > 0:
            self.flush()
        self.db.close()

​ 在上面的程序中,我们通过几个函数来对HDF5文件中的数据集进行操作。

其功能分别是:

flush: 将缓存中的数据写入文件中,然后将缓存区清空

add:向缓存中写入数据和对应的标签,如果缓存中的数据大小超过了缓存区的大小,则调用flush方法

storeClassLabels:向文件中写入每个类别的名称,格式为字符串

close:关闭文件流,如果此时缓存区仍有数据,则先调用flush方法写入文件。

特征提取

​ 创建一个python文件,命名为:extract_feature.py, 写入如下代码:

from tensorflow.keras.applications import VGG16
from tensorflow.keras.applications import imagenet_utils
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing.image import load_img
from sklearn.preprocessing import LabelEncoder
from inOutput.hdf5datasetwriter import HDF5DatasetWriter
from imutils import paths
import numpy as np
import progressbar
import random
import os

dataset = "/Users/lingg/Desktop/dataset/Flower17-master/dataset/train"
output = "/Users/lingg/PycharmProjects/DLstudy/feature/flower-17/hdf5/feature.hdf5"
batchsize = 32
bufferSize = 1000

bs = batchsize
print("[INFO] loading images...")
imagePaths = list(paths.list_images(dataset))
random.shuffle(imagePaths)
labels = [p.split(os.path.sep)[-2] for p in imagePaths]
le = LabelEncoder()
labels = le.fit_transform(labels)

print("[INFO] loading network...")
model = VGG16(weights="imagenet", include_top=False)

dataset = HDF5DatasetWriter((len(imagePaths), 512 * 7 * 7), output, dataKey="features", bufSize=bufferSize)

dataset.storeClassLabels(le.classes_)

widgets = ["Extracting Features: ", progressbar.Percentage(), " ", progressbar.Bar(), " ", progressbar.ETA()]
pbar = progressbar.ProgressBar(maxval=len(imagePaths), widgets=widgets).start()

for i in np.arange(0, len(imagePaths), bs):
    batchPaths = imagePaths[i:i + bs]
    batchLabels = labels[i:i + bs]
    batchImages = []

    for (j, imagePath) in enumerate(batchPaths):
        image = load_img(imagePath, target_size=(224, 224))
        image = img_to_array(image)
        image = np.expand_dims(image, axis=0)
        image = imagenet_utils.preprocess_input(image)
        batchImages.append(image)
    batchImages = np.vstack(batchImages)
    features = model.predict(batchImages, batch_size=bs)
    features = features.reshape((features.shape[0], 512 * 7 * 7))
    dataset.add(features, batchLabels)
    pbar.update(i)
dataset.close()
pbar.finish()

其中

变量dataset是数据集所在目录,我们使用flower-17数据集。

变量output是提取到的特征存储的目标路径

注意,我们的文件feature.hdf5是程序自动生成的,无需我们手动创建,但其所在目录需要我们提前创建好,例如本文中的/Users/lingg/PycharmProjects/DLstudy/feature/flower-17/hdf5/是要提交创建好的,否则会提示目标路径不存在的错误。

另外,由于等待的时间比较长,我们添加了一个程序与外界交互的控件progressbar,来向外界展示当前特征提取的进度。可以通过以下命令进行安装:

pip progressbar

当然,不想用也可以不用,这不是必须的。

执行程序 我们可以在对应的目录下看到feature.hdf5文件。

这个文件中保存的是我们对flower-17数据集提取出来的特征,我们将在下面的步骤中用于训练分类器。

用提取的特征来训练分类器

​ 众所周知,用简单的线性分类器直接在图像进行训练效果不是很理想,那么如果在我们上文中提取出来的特征上进行训练呢?效果会怎么样?

​ 让我们一探究竟。

​ 创建一个文件,将其命名为:train_model.py,然后写入如下代码:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
import pickle
import h5py
import numpy as np
import sys

db = "/Users/lingg/PycharmProjects/DLstudy/feature/flower-17/hdf5/feature.hdf5"
model_path = "/Users/liushanlin/PycharmProjects/DLstudy/model/animals.cpickle"
jobs = -1

db = h5py.File(db, "r")
i = int(db["labels"].shape[0] * 0.75)
print("[INFO] tuning hyperparameters...")
params = {"C": [0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0]}
model = GridSearchCV(LogisticRegression(), params, cv=3, n_jobs=-1)
trainX = db["features"][:i]
trainY = db["labels"][:i]
testX = db["features"][i:]
testY = db["labels"][i:]
targets = db["label_names"][:]
for i in np.arange(0, len(targets)):
    targets[i] = str(targets[i], encoding='utf-8')
print(targets)
model.fit(trainX, trainY)
print("[INFO] best hypermeters:{}".format(model.best_params_))

print("[INFO] evaluating...")
preds = model.predict(testX)
print(classification_report(testY, preds, target_names=targets))
print("[INFO] saving model...")
f = open(model_path, "wb")
f.write(pickle.dumps(model.best_estimator_))
f.close()
db.close()

其中,

变量db存储的是我们上一步提取出来的特征文件

变量model_path是我们想要将我们训练的线性分类器序列化存储的路径。

我们在代码中通过逻辑回归来对特征进行分类,并使用了GridSearchCV(用来对比验证最佳参数),一共比较了6个参数:{“C”: [0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0]},它会帮我们找到效果最好的那个,具体的使用方法可以参考官方文档。

在本代码中使用GridSearchCV可能遇到的问题:网格搜索报错:(GridSearchCV): ‘ascii‘ codec can‘t encode characters in position 18-20

运行结果:

[INFO] tuning hyperparameters...
['bluebell' 'buttercup' 'colts_foot' 'cowslip' 'crocus' 'daffodil' 'daisy'
 'dandelion' 'fritillary' 'iris' 'lily_valley' 'pansy' 'snowdrop'
 'sunflower' 'tigerlily' 'tulip' 'windflower']

[INFO] best hypermeters:{'C': 1.0}
[INFO] evaluating...
              precision    recall  f1-score   support

    bluebell       0.96      0.96      0.96        26
   buttercup       0.93      1.00      0.97        14
  colts_foot       1.00      0.94      0.97        17
     cowslip       0.74      0.88      0.80        16
      crocus       0.74      0.93      0.82        15
    daffodil       0.88      0.94      0.91        16
       daisy       0.94      0.89      0.92        19
   dandelion       0.94      0.88      0.91        17
  fritillary       0.94      0.89      0.91        18
        iris       1.00      0.89      0.94        19
 lily_valley       0.83      0.94      0.88        16
       pansy       1.00      0.88      0.93        16
    snowdrop       0.62      0.81      0.70        16
   sunflower       1.00      1.00      1.00        16
   tigerlily       1.00      1.00      1.00        22
       tulip       1.00      0.58      0.73        19
  windflower       0.94      0.94      0.94        16

    accuracy                           0.90       298
   macro avg       0.91      0.90      0.90       298
weighted avg       0.92      0.90      0.90       298

[INFO] saving model...

Process finished with exit code 0

可以看到,网格搜索帮我们找到了效果最好的参数:c=1000.0.。

并且更加令人惊讶的是,简单的逻辑回归也达到了非常高的准确率,这多亏了我们VGG所提取的特征。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值