2.1 图像分类介绍

2.1 图像分类介绍

学习目标

  • 目标

    • 了解图像分类任务以及挑战

    • 知道最近邻分类器的特点、L1与L2距离的特点

  • 应用

2.1.1 图像分类

  • 任务目的:对输入的图像赋予一个标签,这个标签在指定类别集合中。

下面这个例子中,图像分类模型拍摄一张图像并将概率分配给4个标签{cat,dog,hat,mug}。如图所示,请记住,对于计算机而言,图像表示为一个大型3维数字数组。在此示例中,猫图像的宽度为248像素,高度为400像素,并具有红色,绿色,蓝色(简称RGB)三个颜色通道。因此,图像由248 x 400 x 3个数字或总共297,600个数字组成。每个数字都是一个整数,范围是0(黑色)到255(白色)。我们的任务是将这一百万分之一的数字变成一个单独的标签,例如“ cat”。

2.1.1.1 挑战:

由于识别视觉概念的任务对人类来说相对来说是微不足道的,因此从计算机视觉算法的角度考虑所涉及的挑战存在并且具有价值的。图像的原始表示为亮度值的三维数组:

  • Viewpoint variation:单个物体从不同的角度照出来的图像

  • Scale variation:展现出的图像大小会变化

  • Deformation:许多物体的边缘可以形成不同的形状形式

  • Occlusion:关注的某个物体会被遮挡,只有一小部分会被显示出来

  • Illumination conditions: 光照对像素级的影响很大

  • Background clutter: 感兴趣的物体与环境融合,很难进行区分

  • Intra-class variation: 物体的类别非常广泛相近,例如椅子,这些对象有许多不同的类型,每个都有自己的外观

2.1.2 近邻分类器

首先我们将介绍一个最近邻分类的方法,虽然这个方法没有用到深度学习的方法,并且也很少在实践中使用。但是能够给我们提供一个去解决图像分类问题的基础思路。

  • 数据驱动方式

如何编写一个算法进行将一个张图片分类到具体一个类别,会给计算机提供每个类别的很多样本,然后使用算法去学习这些样本学习每个类别的视觉特点。这样的方式依赖于大量的指定类别的训练数据。如下所示

2.1.2.1 CIFAR-10例子介绍

图像分类数据集示例:CIFAR-10,一个流行的图像分类数据集。这个数据集由60000个32像素高和宽组成的小图像组成。每个图像都被标记为10个类之一(例如“飞机、汽车、鸟等”)。这60000个图像被分割成50000个图像的训练集和10000个图像的测试集。在下图中,您可以看到10个类中每个类的10个随机示例图像:

上面图中就是数据集的类别和图像的示例,右边展示了一部分测试图像以及最相近的在训练集中前10张图片集合。

2.1.2.2 算法思路

假设现在我们得到了cifar-10训练集,它包含50000个图像(每个标签有5000个图像),我们希望标记预测剩下的10000个图像。

  • 最近邻分类器将得到一个测试图像,将其与每个训练图像进行比较,并预测其标签,为最近的训练图像的标签。

在上面和右边的图像中,您可以看到10个示例测试图像的这种过程的示例结果。注意,在大约10个示例中,只有3个检索到同一类的图像,而在其他7个示例中则不是这样。例如,在第8排,离马头最近的训练图像是一辆红色的汽车,大概是由于强烈的黑色背景。因此,在这种情况下,马的图像会被错误地标记为汽车。

  • 训练数据集与测试数据L1距离代码实现

    • Xtr为所有训练数据,

distances = np.sum(np.abs(Xtrain - Xtest[i,:]), axis = 1)

设定输入的图片和标签形状为

Xtrain_rows = Xtrain.reshape(Xtrain.shape[0], 32 * 32 * 3) # Xtr_rows becomes 50000 x 3072

Xtest_rows = Xtest.reshape(Xtest.shape[0], 32 * 32 * 3) # Xte_rows becomes 10000 x 3072

完整代码实现(Numpy)

import numpy as np

 

class NearestNeighbor(object):

def __init__(self):

pass

 

def train(self, X, y):

"""

X:N x D形状,N为样本数,D为像素数量

Y:1维,大小为N

"""

# 所有最近邻需要的训练数据集

self.Xtrain = X

self.ytrain = y

 

def predict(self, Xtest):

"""对输入的X若干个测试图片,每个进行预测"""

num_test = Xtest.shape[0]

# 确保输出类型一样

Ypred = np.zeros(num_test, dtype = self.ytrain.dtype)

 

# 循环所有测试数据

for i in xrange(num_test):

# 使用L1距离找到i最近的训练图片

distances = np.sum(np.abs(self.Xtrain - Xtest[i,:]), axis = 1)

min_index = np.argmin(distances)# 获取最近的距离的图像下标

Ypred[i] = self.ytrain[min_index]# 预测标签(获取对应训练那张图片的目标标签)

return Ypred

结果

使用上述方法,我们在CIFAR-10的测试机上面只能达到38.6% 的准确率,距离目前人类的测试结果(大概)94%的准确率,还有后面着重介绍的state of the art(SOTA,前沿的)的卷积神经网络取得的效果95%

2.1.3 近邻分类器的优缺点

  • 优点是实现和理解起来非常简单。此外,分类器不需要时间进行训练,因为所需的只是存储并可能索引训练数据。

  • 缺点是我们在测试时候的计算成本,因为对测试示例进行分类需要与每个单独的训练示例进行比较。这是向后的,因为在实践中,我们通常更关心测试时间的效率,而不是训练时间的效率。

    • 利用L1和L2进行距离测量不适合描述图片的相似度,不适合描述图片在视觉感知上的差异

    • 测试太慢

    • 维数灾难

注:最近邻分类器的计算复杂度是非常值得研究的领域,现有的几种近似最近邻(ann)算法和库可以加速数据集中的最近邻查找。这些算法允许在检索过程中权衡最近邻检索的正确性和其空间/时间复杂性,并且通常依赖于涉及构建kdtree或运行k-means算法的预处理/索引阶段。在某些情况下(尤其是在数据低维的情况下),最近邻分类器有时可能是一个不错的选择,但它很少适用于实际的图像分类设置。一个问题是,图像是高维对象,高维空间上的距离可能非常反直观。

2.1.4 总结

  • 图像分类任务引入以及挑战

  • 最近邻分类器

  • L1与L2距离在图像距离的计算和存在的问题

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。 首先,我们需要明确一下几个概念: - TensorFlow 2.1TensorFlow是一个开源的机器学习框架,2.1是它的一个版本号。 - ViT模型:ViT是一种基于注意力机制的图像分类模型,它将图像分解成一个个小块,然后通过注意力机制将这些小块组合起来,最终得到整张图像的表示。 - CIFAR-100数据集:CIFAR-100是一个图像分类数据集,其中包含100个不同的类别,每个类别有600张32x32的彩色图像。 下面是基于TensorFlow 2.1的ViT模型的CIFAR-100图像分类代码,损失函数为中心损失函数和焦点损失函数的联合函数,具体步骤和含义会在代码注释中解释。 ``` import tensorflow as tf import tensorflow_datasets as tfds from tensorflow.keras import layers, models, regularizers # 加载CIFAR-100数据集 cifar_train, cifar_test = tfds.load('cifar100', split=['train', 'test'], as_supervised=True) # 数据预处理函数,将像素值缩放到[0,1]之间 def preprocess(image, label): image = tf.cast(image, tf.float32) / 255. label = tf.cast(label, tf.int32) return image, label # 数据增强函数,包括随机裁剪、水平翻转、随机旋转 def augment(image, label): image = tf.image.random_crop(image, size=[32, 32, 3]) image = tf.image.random_flip_left_right(image) image = tf.image.random_rotation(image, 25) return image, label # 对训练集进行预处理和数据增强 cifar_train = cifar_train.map(preprocess).map(augment).shuffle(10000).batch(64) # 对测试集进行预处理 cifar_test = cifar_test.map(preprocess).batch(64) # 定义中心损失函数 class CenterLossLayer(layers.Layer): def __init__(self, alpha=0.5, num_classes=100): super(CenterLossLayer, self).__init__() self.alpha = alpha self.num_classes = num_classes def build(self, input_shape): self.centers = self.add_weight(name='centers', shape=(self.num_classes, input_shape[1]), initializer='uniform', trainable=False) def call(self, inputs, labels): # 计算当前样本所属的类别的中心 centers_batch = tf.gather(self.centers, labels) # 计算样本与中心的距离 diff = inputs - centers_batch loss = tf.reduce_mean(tf.square(diff), axis=1) / 2 # 更新中心的值 unique_label, unique_idx, unique_count = tf.unique_with_counts(labels) appear_times = tf.gather(unique_count, unique_idx) appear_times = tf.reshape(appear_times, [-1, 1]) diff = diff / tf.cast((1 + appear_times), tf.float32) self.centers.assign_add(self.alpha * tf.reduce_sum(diff, axis=0)) return loss # 定义焦点损失函数 class FocalLoss(tf.keras.losses.Loss): def __init__(self, gamma=2.0, alpha=0.25): super(FocalLoss, self).__init__() self.gamma = gamma self.alpha = alpha def call(self, y_true, y_pred): # 将y_true转换为one-hot形式 y_true = tf.one_hot(tf.cast(y_true, tf.int32), depth=100) # 计算交叉熵损失 ce_loss = tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_pred) # 计算权重 pt = tf.math.exp(-ce_loss) alpha_t = tf.where(tf.equal(y_true, 1), self.alpha, 1 - self.alpha) w = alpha_t * tf.math.pow(1 - pt, self.gamma) # 计算焦点损失 loss = w * ce_loss return tf.reduce_mean(loss) # 定义ViT模型 def build_vit_model(): inputs = layers.Input(shape=(32, 32, 3)) # 将图像分解成小块,并使用自注意力机制进行特征提取 x = layers.Resizing(224, 224)(inputs) x = layers.Conv2D(3, 3, padding='same', strides=1, kernel_regularizer=regularizers.l2(0.01))(x) x = layers.BatchNormalization()(x) x = layers.Conv2D(64, 7, strides=2, padding='same')(x) x = layers.BatchNormalization()(x) x = layers.Activation('relu')(x) x = layers.MaxPooling2D(3, strides=2, padding='same')(x) x = layers.BatchNormalization()(x) x = layers.Conv2D(128, 3, strides=1, padding='same')(x) x = layers.BatchNormalization()(x) x = layers.Activation('relu')(x) x = layers.Conv2D(256, 3, strides=2, padding='same')(x) x = layers.BatchNormalization()(x) x = layers.Activation('relu')(x) x = layers.Conv2D(512, 3, strides=2, padding='same')(x) x = layers.BatchNormalization()(x) x = layers.Activation('relu')(x) x = layers.Flatten()(x) # 添加中心损失函数层 x1 = layers.Dense(512)(x) x1 = layers.BatchNormalization()(x1) x1 = layers.Activation('relu')(x1) x1 = CenterLossLayer()(x1) # 添加全连接层和焦点损失函数层 x2 = layers.Dense(512)(x) x2 = layers.BatchNormalization()(x2) x2 = layers.Activation('relu')(x2) outputs = layers.Dense(100)(x2) outputs = FocalLoss()(tf.argmax(outputs, axis=-1), outputs) # 模型的输入和输出 model = models.Model(inputs=inputs, outputs=[outputs, x1]) return model # 构建ViT模型 model = build_vit_model() # 编译模型,使用Adam优化器和联合损失函数 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss=[lambda y_true, y_pred: y_pred, lambda y_true, y_pred: y_pred], loss_weights=[1, 0.1], metrics=['accuracy']) # 训练模型 history = model.fit(cifar_train, epochs=50, validation_data=cifar_test) ``` 这个代码非常长,但是我会尽量解释每一步的含义: 1. 加载CIFAR-100数据集,使用`tfds.load`函数从TensorFlow Datasets中加载数据集,`as_supervised=True`表示返回带有标签的数据。 2. 定义数据预处理函数`preprocess`,将像素值缩放到[0,1]之间。 3. 定义数据增强函数`augment`,包括随机裁剪、水平翻转、随机旋转。 4. 对训练集进行预处理和数据增强,使用`map`函数对每个样本应用预处理和数据增强函数,`shuffle(10000)`表示对数据集进行随机化,`batch(64)`表示将数据集划分为大小为64的小批次。 5. 对测试集进行预处理,使用`map`函数对每个样本应用预处理函数,`batch(64)`表示将数据集划分为大小为64的小批次。 6. 定义中心损失函数,继承`layers.Layer`类,实现`build`和`call`方法,`build`方法初始化中心的权重,`call`方法计算当前样本与所属类别的中心之间的距离,并更新中心的值。 7. 定义焦点损失函数,继承`tf.keras.losses.Loss`类,实现`call`方法,计算交叉熵损失和权重,然后计算焦点损失。 8. 定义ViT模型,使用`layers`和`models`模块中的函数构建模型,将图像分解成小块,并使用自注意力机制进行特征提取,添加中心损失函数层和全连接层和焦点损失函数层,最终模型的输入和输出。 9. 编译模型,使用`compile`方法对模型进行编译,使用Adam优化器和联合损失函数,其中第一个损失函数为焦点损失函数,第二个损失函数为中心损失函数,两个损失函数的权重分别为1和0.1。 10. 训练模型,使用`fit`方法对模型进行训练,`epochs=50`表示训练50个时期,`validation_data=cifar_test`表示在测试集上评估模型的性能。 希望这个回答能够帮助到你,如果还有不清楚的地方,可以再问我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值