VGG Image Classification Practical

13 篇文章 0 订阅
3 篇文章 0 订阅

系列文章目录



前言

此次VGG作业的任务是图像分类,即通过图像视觉对图像进行分类。首先在图像数据集中检索特定视觉内容的图像,然后在图像中标记。
一共包括以下四个内容:

  1. 飞机、人、摩托车三种视觉分类器
  2. 通过曲线图评估分类器性能
  3. 增强训练集和测试集
  4. 使用Bing图像搜索和使用分类器为分类器获取数据集

在学习VGG时,最好将它的py文件好好看一下,因为我们的操作是在笔记本文件上的,而他自己定义的函数是在py文件里的,需要大致了解函数的内部结构。


一、加载绘图模块

%load_ext autoreload
%autoreload 2
%matplotlib inline
import matplotlib
matplotlib.rcParams['figure.dpi'] = 75

绘图模块

二、三种分类器

1.数据准备

目录数据中提供的数据由图像和每个映像的预计算描述符组成。JPEG图像包含在数据/图像中。数据包括三个图像类(包括飞机、摩托车或人员)和“背景”图像(即不包含这三个类的图像)。
内容如下
– aeroplane motorbike person background
train 112 120 1025 1019
test 126 125 983 1077
total 238 245 2008 2096

注意这里一个图像可能会有多个标签标记

  • 代码
import lab

imdb = lab.get_image_database()

total = 0
for class_name in ['aeroplane', 'motorbike', 'person', 'background']:
    for set_name in ['train', 'val']:
        indices = lab.get_indices(imdb, class_name, set_name)
        n = len(indices)
        total += n
        print(f"Number of {class_name} images for {set_name}: {n}")
print(f"Number of total examples: {total}")

图像由单个向量描述符表示。将图像的视觉内容映射到单个描述符向量通常被视为编码步骤,而由此产生的描述符有时被称为代码。使用固定长度向量的主要优点是它们可以通过简单的向量度量(如欧氏距离)进行比较。出于同样的原因,它们是用于学习图像分类器的自然表示。我们将使用卷积神经网络(CNN)编码。下面概述从图像开始构造CNN描述符的过程:
卷积3:第三卷积层输出,包含2×2×384特征通道。
卷积4:第四卷积层输出,包含2×2×256个特征通道。
卷积5:第五个卷积层输出,也包含2×2×256个特征通道。
Fc 7:最后一个完全连接的层输出,包含4096个特征

# Get the code vectors (for AlexNet layer conv3)
x = lab.get_codes(layer=3)

print(f"The tensor codes has size {list(x.shape)}")

2.训练摩托车分类器

摩托车训练图像将被用作正面图像,背景图像将用作负片。分类器是一种线性支持向量机(SVM)。

  • 展示图片
    下面的图片来自背景类的训练集,共16张。图片数值和类别可以自己在下面随意修改
# Show some training images
c = imdb['class_names'].index('background')
s = imdb['set_names'].index('train')
indices = lab.get_indices(imdb, c, s)
indices = indices[:16]

from matplotlib import pyplot as plt

plt.figure(figsize=(12,12))
for t, index in enumerate(indices):
    image = lab.get_image(imdb, index)
    plt.gcf().add_subplot(4,4,t+1)
    lab.imsc(image[0])

结果
在这里插入图片描述

  • 规范化
    训练前 将x的张量归一化
    这样有助于训练模型分类
import torch

# Normalize the code vectors
x /= torch.sqrt((x * x).sum(1))[:,None]
  • 获取所需图片的索引
    在对模型进行训练之前,必须选择一组合适的正面训练和验证图像。为此,我们使用提供的lab.get_index()函数。正面图像是摩托车在训练集中的图像。负片图像是训练集中的飞机、人和背景图像,减去任何正面图像(这是必需的,因为同一图像可以包含多个类)。
    任务:运行下面的代码,以获得正面和负面训练图像的索引。如所示,目前只使用20幅正面训练图像。
# Get the training images
pos_train = lab.get_indices(imdb, 'motorbike',  'train')
neg_train = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'train', minus=pos_train)

#分别获取正样本和负样本,正样本限制在20个,由于一个样本有多个标签,所以我们需要把负样本中的正样本筛出掉。


# Limit the training set size
pos_train = pos_train[:20]
neg_train = neg_train[:]
  • SCDA算法训练
    我们现在准备运行支持向量机优化器。为此,我们采用随机对偶坐标上升(SDCA)算法,在分类器目标函数上产生上、下界,从而自动检测收敛性。任务:运行下面的代码来训练支持向量机。
def get_scores(w, b, x):
    return x @ w + b

def train(x, pos, neg, lam=1e-4):
    print(f"Train an SVM using {len(pos)} positive, {len(neg)} negative examples, and regularization {lam}.")

    # Extract training codes and labels
    #torch.cat是将两个张量(tensor)拼接在一起,cat是concatnate的意思,即拼接,联系在一起。

    #使用torch.cat((A,B),dim)时,除拼接维数dim数值可不同外其余维数数值需相同,方能对齐。
    x_train = x[torch.cat((pos, neg))]
    c_train = torch.tensor([1] * len(pos) + [-1] * len(neg), dtype=torch.float32)

    # Train the SVM using the SDCA solver
    w, b = lab.svm_sdca(x_train, c_train, lam=lam)
    #后面结果由于使用SDCA会产生上界线和下界线
    
    
    # Get the training data scores
    scores_train = get_scores(w, b, x_train)    
    return w, b, c_train, scores_train
    
w, b, c_train, scores_train = train(x, pos_train, neg_train)

在这里插入图片描述
We will first assess qualitatively how well the classifier works by using it to rank all the training images.

Question: What do you expect to happen?

正样本的损失非常小,所以排序时,正样本图像在前面

  • 按得分降序排列
# Sort images by decreasing scores
_, perm = torch.sort(scores_train, descending=True)

# Show ranked 
plt.figure(1, figsize=(15,15))
lab.plot_ranked_list(imdb, pos_train, neg_train, perm)

在这里插入图片描述

3.对测试图像进行分类并对其性能进行评估

Question: Why is the bias term not needed 偏置项只是起到防止过拟合作用,并不像W是分类器从样本里学到的参数

Task: Use the code below to evaluate the SVM on the validation data and plot the corresponding ranked list of images.

# Get the validation images
pos_val = lab.get_indices(imdb, 'motorbike',  'val')
neg_val = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'val', minus=pos_val)

# Extract the validation codes
x_val = x[torch.cat((pos_val, neg_val))]

# Compute the validation scores
scores_val = get_scores(w, b, x_val)

# Sort images by decreasing scores
_, perm_val = torch.sort(scores_val, descending=True)

# Show ranked images
plt.figure(1, figsize=(15,15))
lab.plot_ranked_list(imdb, pos_val, neg_val, perm_val)
  • 曲线评估性能

精确率= 将正类预测为正类 / 所有预测为正类 TP/(TP+FP)

召回率 = 将正类预测为正类 / 所有正真的正类 TP/(TP+FN)

TP:正确的匹配数目

FP:误报,没有的匹配不正确

FN:漏报,没有找到正确匹配的数目

TN:正确的非匹配数目

通过改变分类器上的阈值(从高到低),绘制每个阈值的查全率与查全率值,计算出查全率(PR)曲线。为了用单个数字(而不是曲线)来评估检索性能,通常会计算平均精度(AP,曲线下面积)。确保您理解精确召回曲线中的精度值如何与检索结果中的正负排序相对应。

# Compute the validation labels
c_val = torch.cat((torch.ones(len(pos_val)), -torch.ones(len(neg_val))))

# Compute the precision-recall curves
plt.figure(figsize=(6,6))
_, _, ap_train = lab.pr(c_train, scores_train)
_, _, ap_val = lab.pr(c_val, scores_val)
plt.legend((f'train ({100*ap_train:.1f}%)', f'val ({100*ap_val:.1f}%)')) ;

在这里插入图片描述

4.增加训练图像数量

到目前为止,我们只使用了20(120)正面图像来训练分类器。任务:在改变训练图像的数量后,使用下面的代码对支持向量机进行再训练,特别是对正面图像进行再训练,并测量效果。

  • Questions:

How much does the performance vary as numPos is changed from 1 to +inf (indicating that all available positive images are used)?
Compare doubling the number of training images by either including fresh samples or generating new samples using augmentation. Which performs better? Why? 和通过数据增强的样本相比,肯定还是通过原始样本的训练模型效果更好,但是对原始样本进行数据增强可能会使结果变得更好。

# Get the training images
pos_train = lab.get_indices(imdb, 'motorbike',  'train')
neg_train = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'train', minus=pos_train)

# Limit the training set size
pos_train = pos_train[:]
neg_train = neg_train[:]

# Train the SVM
w, b, _, _ = train(x, pos_train, neg_train)

# Evalaute the SVM
scores_val = get_scores(w, b, x_val)

plt.figure(figsize=(6,6))
_, _, ap_val = lab.pr(c_val, scores_val)
message = f'pos {len(pos_train)} neg {len(neg_train)} ap {100*ap_val:.1f}%'
plt.legend((message,)) ;
  • 120个正样本
    在这里插入图片描述
  • 20个正样本
    在这里插入图片描述

由此可以看出120个训练效果更好

5.通过数据增强增加训练图像的数量

到目前为止,描述符向量已经从图像中按原样计算出来了。我们现在考虑多次表示每个图像,方法是为原始图像和“翻转”后的图像生成一个描述符(一个垂直轴的反射镜)。这种数据增强将在培训和测试时间以不同的方式使用。在训练中,翻转图像中的描述符将用作额外的训练样本(即每幅原始图像生成两个数据样本进行训练)。在测试中,原始图像和翻转图像的描述符将进行平均处理,从而再次生成一个表示测试图像的向量

  • 这里使用的数据增强是最简单的,数据增强还有改变角度,倒置等等
# Get and normalize the code vectors
x = lab.get_codes(layer=3)
x /= torch.sqrt((x * x).sum(1))[:,None]

# Train with normal images together, as before
pos_train = lab.get_indices(imdb, 'motorbike',  'train', jitters=['normal'])
neg_train = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'train',
                            jitters=['normal'], minus=pos_train)
w, b, _, _ = train(x, pos_train, neg_train)

# Retrain with normal and jitter images together
pos_train_aug = lab.get_indices(imdb, 'motorbike',  'train', jitters=['normal', 'flipped'])
neg_train_aug = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'train',
                                jitters=['normal', 'flipped'], minus=pos_train_aug)
w_aug, b_aug, _, _ = train(x, pos_train_aug, neg_train_aug)

# Get the normal version of the validation images
pos_val = lab.get_indices(imdb, 'motorbike',  'val')
neg_val = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'val', minus=pos_val)
x_val = x[torch.cat((pos_val, neg_val))]

# Get the jittered version of the validation images
pos_val_jit = lab.get_indices(imdb, 'motorbike', 'val', jitters=['flipped'])
neg_val_jit = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'val',
                              jitters=['flipped'], minus=pos_val_jit)
x_val_jit = x[torch.cat((pos_val_jit, neg_val_jit))]

# Evaluate the model trained without and with jitter using validation data without and with jitter
scores_val               = get_scores(w, b, x_val)
scores_train_aug_val     = get_scores(w_aug, b_aug, x_val)
scores_val_aug           = get_scores(w, b, x_val_jit) + scores_val
scores_train_aug_val_aug = get_scores(w_aug, b_aug, x_val_jit) + scores_train_aug_val

# Compute the precision-recall curves
plt.figure(figsize=(6,6))
_, _, ap_val               = lab.pr(c_val, scores_val)
_, _, ap_train_aug_val     = lab.pr(c_val, scores_train_aug_val)
_, _, ap_val_aug           = lab.pr(c_val, scores_val_aug)
_, _, ap_train_aug_val_aug = lab.pr(c_val, scores_train_aug_val_aug)

plt.legend((f'no aug ({100*ap_val:.1f}%)', 
            f'train aug ({100*ap_train_aug_val:.1f}%)',
            f'val aug ({100*ap_val_aug:.1f}%)',
            f'train & val aug ({100*ap_train_aug_val_aug:.1f}%)',
           )) ;

在这里插入图片描述

在这里插入图片描述
可以看出只增强验证集会导致正确率相对于只增强训练集下降。
但是增强数据集后的正确率都比未增强数据集正确率高。

6.设置支持向量机的超参数λ

设置支持向量机的超参数λ
lam正则化的效果,降低你和,提高泛化。
如果训练和测试成绩之间有显著的差异,那就说明这是过度拟合。通过改变支持向量机的𝜆λ参数,可以减少差异,提高测试性能(泛化)。

  • Task: Use the code below to vary the 𝜆 parameter in the range 1 to 1e-5 (the default is 𝜆 =1e-4), and plot the AP on the training and test data as 𝜆 varies.

  • Question: How do validation performance and training time vary as 𝜆 is changed?

当lam变小时,训练时间将会明显增加 AP是对不同召回率点上的精度进行平均(即PR曲线下的面积) 但并不是LAM越小召回率越高。在我训练中,0.0001

# Get the training images
pos_train = lab.get_indices(imdb, 'motorbike',  'train')
neg_train = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'train', minus=pos_train)

# Train the SVM
w, b, _, _ = train(x, pos_train, neg_train, lam=0.0001)

# Evaluate the SVM
scores_val = get_scores(w, b, x_val)
plt.figure(figsize=(6,6))
_, _, ap_val = lab.pr(c_val, scores_val)
message = f'pos {len(pos_train)} neg {len(neg_train)} ap {100*ap_val:.1f}%'
plt.legend((message,)) ;

在这里插入图片描述

7.不同类别分类下的机器性能

  • 飞机

  • 这两种可以在pos_train = lab.get_indices(imdb, 'person', 'train') neg_train = lab.get_indices(imdb, ['motorbike', 'aeroplane', 'background'], 'train', minus=pos_train)设置即可,这里设置的是人
# Get the training images and train the SVM
pos_train = lab.get_indices(imdb, 'person',  'train')
neg_train = lab.get_indices(imdb, ['motorbike', 'aeroplane', 'background'], 'train', minus=pos_train)
w, b, c_train, scores_train = train(x, pos_train, neg_train)

# Get the validation images and labels
pos_val = lab.get_indices(imdb, 'person',  'val')
neg_val = lab.get_indices(imdb, ['motorbike', 'aeroplane', 'background'], 'val', minus=pos_val)
x_val = x[torch.cat((pos_val, neg_val))]
c_val = torch.cat((torch.ones(len(pos_val)), -torch.ones(len(neg_val))))

# Evaluate the SVM
scores_val = get_scores(w, b, x_val)
plt.figure(figsize=(6,6))
_, _, ap_train = lab.pr(c_train, scores_train)
_, _, ap_val = lab.pr(c_val, scores_val)
plt.legend((f'train {100*ap_train:.1f}%', f'val {100*ap_val:.1f}%')) ;

在这里插入图片描述

8.改用L1规范,或者不规范化处理

ayer = 7#这里设置卷积层或者全连接层
num_pos = 120#这里设置你要训练的正样本个数

# Get the code vectors and normalize them
x = lab.get_codes(layer)
#x /= torch.sqrt((x * x).sum(1))[:,None]
x /= torch.abs(x).sum(1)[:,None]

# Get the training images and train the SVM
pos_train = lab.get_indices(imdb, 'motorbike', 'train')
neg_train = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'train', minus=pos_train)
pos_train = pos_train[:num_pos]
w, b, c_train, scores_train = train(x, pos_train, neg_train)

# Get the validation images and labels
pos_val = lab.get_indices(imdb, 'motorbike', 'val')
neg_val = lab.get_indices(imdb, ['aeroplane', 'person', 'background'], 'val', minus=pos_val)
x_val = x[torch.cat((pos_val, neg_val))]
c_val = torch.cat((torch.ones(len(pos_val)), -torch.ones(len(neg_val))))

# Evaluate the SVM
scores_val = get_scores(w, b, x_val)
plt.figure(figsize=(6,6))
_, _, ap_train = lab.pr(c_train, scores_train)
_, _, ap_val = lab.pr(c_val, scores_val)
plt.legend((f'train {100*ap_train:.1f}%', f'val {100*ap_val:.1f}%')) ;
  • L1规范化结果
    在这里插入图片描述
  • 不规范化结果
    在这里插入图片描述
  • L2规范化结果
    在这里插入图片描述
  • 由此可见,在这种情况下,L1规范化效果要比L2规范化,不规范化效果好,去除过拟合,提高泛化能力。

9.CNN里不同层的提取特征

-查看不同层提取的特征

Tasks:

Restore L2 normalization for the image representation (see the previous stage) and choose a category.
Rerun classification using only 10 training images by and pick code vectors from conv1, conv2, conv3, conv4, conv5, fc6 and fc7.#data文件夹里有conv1到conv7的path文件,由于卷积层只有5层,所以后面两个文件是FC6,FC7。
Note the resulting performance.
Question: How much does the choice of feature depth affect classification performance? 深度特征:神经网络模型来挖掘图像更深、更为抽象的且是机器自己认可的特征。所以与分类性能的相关性很强,它是机器做出分类的重要依据。

Now make the setup even more extreme by considering the so-called one-shot learning problem, i.e. learning an image classifier from a single training image. Thus, set pos_train to one element and rerun the experiments.

Questions:

Can you get good performance using a single training images and the deeper features?并不能
If so, how is it possible that the system can learn to recognize an object from a single example?

10.可视化显著性

  • 使用lab.plot_saliency()函数对包含对象类别的图像使用fc7特性可视化类显着性
# Get a positive validation image as a PIL image
im_pil = lab.get_pil_image(imdb, pos_val[5])

# Get the encoder neural network and normalize the image accordingly
model = lab.get_encoder_cnn()
im = model.normalize(im_pil)

# Plot the saliency
plt.figure(1, figsize=(12,12))
lab.plot_saliency(model, layer, w, im)
  • 结果
    第七层显著性
    在这里插入图片描述
    第一层显著性

在这里插入图片描述
第四层显著性
在这里插入图片描述


总结

VGG图像分类作业练习内容较多,问题也较多,有些东西,我一开始漏看了,导致后面题目没看懂,所以就算是学过的知识,也要逐行仔细看,不然后面的问题不好处理。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值