使用对象检测进行复杂图像分类方案
使用MobileNet和转移学习进行政策识别
本文内容介绍深度转移学习,作为一种利用多个数据源来克服数据稀缺性问题的方法。
处理图像
我们的目标是从训练图像中提取差分信号
在我们尝试为复杂策略构建分类器之前,让我们首先看一下MNIST数据集,以便更好地理解关键图像分类概念,然后我们将这些概念放在一起并将他们应用到我们的数据集。
MNIST数据集中的图像样本
要在MNIST上训练分类模型,我们首先需要一种方法来表示我们的图像和标签 。有许多方法可以将图像表示为张量,矩阵或向量。
对于我们的第一个模型,我们将使用矢量表示。为此,我们首先将图像展平为长矢量。
当我们将此过程应用于尺寸为28 x 28图像像素的下面“3”的图像时,将产生长度为784像素的平坦阵列。
MNIST图像28×28图像像素,它将产生长度为784的扁平阵列。
现在即使我们很容易看到这个图像并且知道它是一个“3”,计算机本身并不知道这一点,我们需要训练一个模型来学习如何识别图像中有一个“3” 。为此,我们首先需要一种表示上图中包含“3”图像这一事实的方法。
编码图像标签
为了实现,我们将每个图像与一个独热编码标签相关联,其中第一个索引对应于数字0,最后一个对应于数字9。
MNIST 3的独热编码矢量表示
当我们训练模型时,我们使用此值作为目标。该Keras下面的代码加载MNIST数据
from keras.datasets import mnist from keras.utils import np_utils output_dim = nb_classes = 10 batch_size = 128 nb_epoch = 5 # the data, shuffled and split between train and test sets (x_train, y_train), (x_test, y_test) = mnist.load_data() input_dim = 784 #28*28 X_train = x_train.reshape(60000, input_dim) X_test = x_test.reshape(10000, input_dim) 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)
现在我们已经处理了MNIST图像及其标签,让我们使用Keras训练我们的第一个图像分类模型。
线性模型
Logistic回归(LR)是一种基本的机器学习技术,它使用线性加权特征组合并生成不同类别的基于概率的预测。
要在MNIST上训练LR模型,我们应用以下步骤:
- 初始化784个值的随机权重向量
- 取第一个784位 MNIST训练图像矢量,如上面的“3”,并乘以我们的权重向量。
- 取我们的乘法结果并将每个784值相加,直到我们得到一个数字
- 将数字传递给一个函数,该函数获取我们的总和并将其拟合到0-9之间的分布和独热编码输出。对于第一个例子,这个数字很可能是不正确的,因为我们乘以了随机值
- 将输出向量与图像标签向量进行比较,并使用损失函数计算我们的预测有多接近。
- 对损失值应用SGD优化以更新权重向量中的每个值。
然后,我们对MNIST训练集中的每个图像重复此过程。对于每个图像,更新权重值,以便它们可以更好地将输入的MNIST向量转换为与其标签匹配的值。
机器学习过程
当我们在我们的训练集上完成上述步骤时,称为Epoch。在第一个Epoch之后,值仍然可能很差但是在对数据集进行混洗并重复几个Epochs的过程之后,线性模型学习线性权重,它们收敛于数据的适当表示。
下面的Keras代码显示了此过程的结果。
from keras.models import Sequential from keras.layers import Dense, Activation model = Sequential() model.add(Dense(output_dim, input_dim=input_dim, activation='softmax')) model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy']) history = model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,verbose=1, validation_data=(X_test, Y_test)) score = model.evaluate(X_test, Y_test, verbose=0) print('Test Loss:', score[0]) print('Test accuracy:', score[1])
Train on 60000 samples, validate on 10000 samples Epoch 1/5 60000/60000 [==============================] - 1s 16us/step - loss: 1.2899 - acc: 0.6898 - val_loss: 0.8185 - val_acc: 0.8255 Epoch 2/5 60000/60000 [==============================] - 1s 17us/step - loss: 0.7228 - acc: 0.8374 - val_loss: 0.6113 - val_acc: 0.8588 Epoch 3/5 60000/60000 [==============================] - 1s 11us/step - loss: 0.5912 - acc: 0.8575 - val_loss: 0.5281 - val_acc: 0.8724 Epoch 4/5 60000/60000 [==============================] - 1s 11us/step - loss: 0.5280 - acc: 0.8681 - val_loss: 0.4821 - val_acc: 0.8800 Epoch 5/5 60000/60000 [==============================] - 1s 13us/step - loss: 0.4897 - acc: 0.8749 - val_loss: 0.4514 - val_acc: 0.8858 Test Loss: 0.4514175675392151 Test accuracy: 0.8858
非线性模型(MLP)
可以想象,仅基于一个输出递增和求和权重向量值是不一定是很好的,并且在某些情况下可能是无效的。
毕竟,并非所有数据都是线性的。
以下示例为两个名为spam和not spam的图像类。无论我们如何更新我们的权重,我们都无法学习线性权重来区分这些类。
但是,如果我们有一种方法可以组合多个线性模型以获得更好的表示能力呢?然后我们可以训练一个可以区分两个图像类的模型。
我们可以通过前馈神经网络来实现这一点。
多层感知
为了使MLP工作,我们需要一个非线性激活函数,例如RELU。
下面的代码显示了如何在MNIST上训练多层感知器以获得比我们的线性模型提供的更好的结果。
from keras.models import Sequential from keras.layers import Dense, Activation output_dim = nb_classes = 10 batch_size = 128 nb_epoch = 5 model = Sequential() model.add(Dense(input_dim, input_dim=input_dim, activation='relu')) model.add(Dense(input_dim, input_dim=input_dim, activation='relu')) model.add(Dense(output_dim, input_dim=input_dim, activation='softmax')) model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy']) history = model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,verbose=1, validation_data=(X_test, Y_test)) score = model.evaluate(X_test, Y_test, verbose=0) print('Test Loss:', score[0]) print('Test accuracy:', score[1])
Train on 60000 samples, validate on 10000 samples Epoch 1/5 60000/60000 [==============================] - 9s 150us/step - loss: 1.0790 - acc: 0.7676 - val_loss: 0.5100 - val_acc: 0.8773 Epoch 2/5 60000/60000 [==============================] - 9s 143us/step - loss: 0.4401 - acc: 0.8866 - val_loss: 0.3650 - val_acc: 0.9011 Epoch 3/5 60000/60000 [==============================] - 12s 194us/step - loss: 0.3530 - acc: 0.9032 - val_loss: 0.3136 - val_acc: 0.9127 Epoch 4/5 60000/60000 [==============================] - 16s 272us/step - loss: 0.3129 - acc: 0.9124 - val_loss: 0.2868 - val_acc: 0.9188 Epoch 5/5 60000/60000 [==============================] - 12s 203us/step - loss: 0.2875 - acc: 0.9194 - val_loss: 0.2659 - val_acc: 0.9246 Test Loss: 0.2659078140795231 Test accuracy: 0.9246
当然我们也会有很多非常大的图像
注意MLP模型比我们简单的线性模型更准确,但更慢。当我们的图像大于500Kb到1Mb时,处理我们的图像作为一个序列的计算成本越来越高,另外在我们的序列数据中检测复杂分层模式变得更困难。
这种维度的诅咒,是计算机视觉领域在2012年AlexNet问世之前停滞不前的关键原因之一。
我们将图像表示为一个矩阵(28x28),而不是将完整图像作为向量表示传递,并提取具有代表性的特征来进行分类决策。我们通过尝试将边缘作为模型的特征来深入研究传统的图像特征提取。
我们先拍摄如下图像。
然后我们采用预定义的图像蒙板,在这种情况下是一个用于提取边缘的索贝尔矩阵
我们将sobel矩阵蒙版应用于我们的图像,如过滤器一样
当我们可视化生成的图像时,我们得到以下边缘,我们可以将其用作特征
卷积神经网络简介
CNN是一个深层神经网络,由一堆层组成,这样一层的输出被馈送到下一层 。通常,CNN开始在卷积层和汇集层(下采样)之间交替,然后以分类部分的完全连接层结束。
卷积
卷积层是一组滤波器。每个滤波器由权重(W)矩阵和偏差(b)定义。
池
我们使用池来降低前一层的维度,从而加快网络的速度。
许多不同的池方法,最大池化和均值池化最常见的。
这是一个最大池化和均值池化的示例,步长为2:
各种池操作
综合起来:
在大多数CNN中,我们堆叠了一组卷积层和池化层,直到我们得到一组代表性的特征,我们可以将其展平并用于类预测。
下面的代码显示了如何从上面训练MNIST图像上的CNN。
from keras.layers import Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D model = Sequential() model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape)) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(nb_classes, activation='softmax')) model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=['accuracy']) model.fit(x_train, y_train, batch_size=batch_size, epochs=nb_epoch, verbose=1, validation_data=(x_test, y_test)) score = model.evaluate(x_test, y_test, verbose=0) print('Test loss:', score[0]) print('Test accuracy:', score[1])
Train on 60000 samples, validate on 10000 samples Epoch 1/5 60000/60000 [==============================] - 177s 3ms/step - loss: 0.2638 - acc: 0.9204 - val_loss: 0.0662 - val_acc: 0.9790 Epoch 2/5 60000/60000 [==============================] - 173s 3ms/step - loss: 0.0882 - acc: 0.9732 - val_loss: 0.0404 - val_acc: 0.9865 Epoch 3/5 60000/60000 [==============================] - 166s 3ms/step - loss: 0.0651 - acc: 0.9806 - val_loss: 0.0350 - val_acc: 0.9883 Epoch 4/5 60000/60000 [==============================] - 163s 3ms/step - loss: 0.0549 - acc: 0.9836 - val_loss: 0.0334 - val_acc: 0.9887 Epoch 5/5 60000/60000 [==============================] - 159s 3ms/step - loss: 0.0472 - acc: 0.9859 - val_loss: 0.0322 - val_acc: 0.9899 Test loss: 0.03221080291894468 Test accuracy: 0.9899
在MNIST数据集中我们有成千上万的训练例子,但我们使用转移学习就可以不需要那么大量的数据。从头开始训练一个深度神经网络需要成千上万的图像,但是训练一个已经在某些领域中学习了特征的神经网络却需要很少量的图像数据就够用了。
转移学习简介
转移学习是使用预先适应我们的问题后训练出来的模型。在转移学习中,我们利用在基础模型训练期间学到的特征和概念。旧的和新的预测层的输入与基本模型相同,我们只是重新使用训练的特征。然后我们训练这个修改过的网络,或者只训练新预测层的新权重,或者整个网络的所有权重。
例如,当我们有一小组图像与现有训练模型处于类似域时,可以使用这种方法。在我们的例子中,这意味着使在ImageNet图像上训练的网络适应策略分类的任务。
预训练模型(MobileNet)
我们选择使用预训练的MobileNet模型作为基础模型。虽然有许多分类架构,但我们将使用MobileNet,因为它可以在CPU上快速运行并提供强大的结果。
from keras.layers import Dense,GlobalAveragePooling2D from keras.applications import MobileNet from keras.preprocessing import image from keras.applications.mobilenet import preprocess_input from keras.preprocessing.image import ImageDataGenerator from keras.models import Model from keras.optimizers import Adam base_model=MobileNet(weights='imagenet',include_top=False) #imports the mobilenet model and discards the last 1000 neuron layer. x=base_model.output x=GlobalAveragePooling2D()(x) x=Dense(1024,activation='relu')(x) #we add dense layers so that the model can learn more complex functions and classify for better results. x=Dense(1024,activation='relu')(x) #dense layer 2 x=Dense(512,activation='relu')(x) #dense layer 3 preds=Dense(2,activation='softmax')(x) #final layer with softmax activation model=Model(inputs=base_model.input,outputs=preds)
for layer in model.layers[:20]: layer.trainable=False for layer in model.layers[20:]: layer.trainable=True Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.6/mobilenet_1_0_224_tf_no_top.h5 17227776/17225924 [==============================] - 13s 1us/step
让我们来看看这个MobileNet模型
处理我们的数据
下面的代码显示了如何使用Keras库在我们的自定义策略上训练自定义MobileNet模型。
训练数据/测试数据
train_datagen=ImageDataGenerator(preprocessing_function=preprocess_input) #included in our dependencies train_generator=train_datagen.flow_from_directory( '/data/dataset/Beverages/Train/', target_size=(224,224), color_mode='rgb', batch_size=32, class_mode='categorical', shuffle=True) test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input) test_generator = test_datagen.flow_from_directory( directory=r"/data/dataset/Beverages/Test/", target_size=(224, 224), color_mode="rgb", batch_size=1, class_mode='categorical', shuffle=False, seed=42 )
Found 180 images belonging to 2 classes. Found 60 images belonging to 2 classes.
查看图像数据样本
i = 0 for data in test_generator: if i > 3: break else: i+=1 img, cls = data print(np.argmax(cls)) plt.imshow(img[0]) plt.show()
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). 0
lipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). 0
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). 0
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). 0
训练模型
model.compile(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy']) # Adam optimizer # loss function will be categorical cross entropy # evaluation metric will be accuracy step_size_train=train_generator.n//train_generator.batch_size model.fit_generator(generator=train_generator, steps_per_epoch=step_size_train, epochs=5)
Epoch 1/5 5/5 [==============================] - 96s 19s/step - loss: 0.8017 - acc: 0.7313 Epoch 2/5 5/5 [==============================] - 77s 15s/step - loss: 0.0101 - acc: 1.0000 Epoch 3/5 5/5 [==============================] - 79s 16s/step - loss: 0.0289 - acc: 0.9937 Epoch 4/5 5/5 [==============================] - 111s 22s/step - loss: 0.0023 - acc: 1.0000 Epoch 5/5 5/5 [==============================] - 87s 17s/step - loss: 0.0025 - acc: 1.0000
基准模型
正如我们在下面看到的,MobileNet是一个非常强大的模型,用于学习和表示我们的玩具政策。
from utils import classification_report y_true = np.concatenate([np.argmax(test_generator[i][1], axis=1) for i in range(test_generator.n)]) y_pred = np.argmax(model.predict_generator(test_generator, steps=test_generator.n), axis=1) classification_report(y_true, y_pred)
precision recall f1-score support 0 1.00 1.00 1.00 30 1 1.00 1.00 1.00 30 micro avg 1.00 1.00 1.00 60 macro avg 1.00 1.00 1.00 60 weighted avg 1.00 1.00 1.00 60 Confusion matrix, without normalization [[30 0] [ 0 30]] Normalized confusion matrix [[1. 0.] [0. 1.]]
但是,如果我们的策略更加复杂,那么我们以这种方式进行建模可能很困难。