基于内容的自动编码器图像检索
基于内容的图像检索也称为图像内容查询,它是基于计算机视觉技术应用于图像检索的问题。它涉及从图像数据库中检索视觉上相似的图像到给定的查询图像。在本文中,基于内容的图像检索问题是使用无监督的机器学习技术即自动编码器来实现的。
我们将使用包含10个类的32x32彩色图像的CIFAR10机器学习图像数据集。该机器学习模型使用python,numpy,pandas,Keras API开发。
导入Python库
import tensorflow as tf import keras, keras.layers as L, keras.backend as K import numpy as np from sklearn.model_selection import train_test_split %matplotlib inline import matplotlib.pyplot as plt import numpy as np from collections import defaultdict from keras.models import save_model import keras

所需的辅助函数
class ModelSaveCallback(keras.callbacks.Callback):
def __init__(self, file_name):
super(ModelSaveCallback, self).__init__()
self.file_name = file_name
def on_epoch_end(self, epoch, logs=None):
model_filename = self.file_name.format(epoch)
save_model(self.model, model_filename)
print("Model saved in {}".format(model_filename))
# !!! remember to clear session/graph if you rebuild your graph to avoid out-of-memory errors !!!
def reset_tf_session():
curr_session = tf.get_default_session()
# close current session
if curr_session is not None:
curr_session.close()
# reset graph
K.clear_session()
# create new session
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
s = tf.InteractiveSession(config=config)
K.set_session(s)
return s
加载机器学习数据集
from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
NUM_CLASSES = 10
cifar10_classes = ["airplane", "automobile", "bird", "cat", "deer",
"dog", "frog", "horse", "ship", "truck"]
# show random images from train
cols = 8
rows = 2
fig = plt.figure(figsize=(2 * cols - 1, 2.5 * rows - 1))
for i in range(cols):
for j in range(rows):
random_index = np.random.randint(0, len(y_train))
ax = fig.add_subplot(rows, cols, i * rows + j + 1)
ax.grid('off')
ax.axis('off')
ax.imshow(x_train[random_index, :])
ax.set_title(cifar10_classes[y_train[random_index, 0]])
plt.show()
上述Python代码输出显示如下:

CIFAR10
IMG_SHAPE = x_train.shape[1:]
# center images
x_train = x_train.astype('float32') / 255.0 - 0.5
x_test = x_test.astype('float32') / 255.0 - 0.5
自动编码器

自动编码器
自动编码器是用于无监督任务的神经网络的变体,其目标是将其输入复制到其输出。它有一个隐藏层z,用于描述用于表示输入的代码。自动编码器由两部分组成: 编码器,Z = F(X),这部分网络负责将输入编码到一个潜在空间表示。解码器,目的是重构从latent-space表示的输入r = g(z)。
我们将自动编码器设计为两个sequential keras模型:编码器和解码器。
编码器
我们将卷积层和池化层堆叠起来,最后使用一个dense层来表示所需的大小(code_size)。我们对所有卷积层和dense层使用“elu”激活。
解码器
对于解码器,我们将使用“转置卷积”。在传统的卷积层中,获取图像的一个patch并生成一个数字。在转置卷积中,我们要取一个数字并输出图像的patch。我们的解码器以dense层开始,以“undo”最后一层编码器。
Python实现
def build_deep_autoencoder(img_shape, code_size): H,W,C = img_shape # encoder encoder = keras.models.Sequential() encoder.add(L.InputLayer(img_shape)) encoder.add(L.Conv2D(filters=32, kernel_size=(3, 3), activation='elu', padding='same')) encoder.add(L.MaxPooling2D(pool_size=(2, 2))) encoder.add(L.Conv2D(filters=64, kernel_size=(3, 3), activation='elu', padding='same')) encoder.add(L.MaxPooling2D(pool_size=(2, 2))) encoder.add(L.Conv2D(filters=128, kernel_size=(3, 3), activation='elu', padding='same')) encoder.add(L.MaxPooling2D(pool_size=(2, 2))) encoder.add(L.Conv2D(filters=256, kernel_size=(3, 3), activation='elu', padding='same')) encoder.add(L.MaxPooling2D(pool_size=(2, 2))) encoder.add(L.Flatten()) #flatten image to vector encoder.add(L.Dense(code_size)) # encoder.summary() # decoder decoder = keras.models.Sequential() decoder.add(L.InputLayer((code_size,))) decoder.add(L.Dense(2*2*256)) #actual decoder, height*width*3 units decoder.add(L.Reshape((2,2,256))) decoder.add(L.Conv2DTranspose(filters=128, kernel_size=(3, 3), strides=2, activation='elu', padding='same')) decoder.add(L.Conv2DTranspose(filters=64, kernel_size=(3, 3), strides=2, activation='elu', padding='same')) decoder.add(L.Conv2DTranspose(filters=32, kernel_size=(3, 3), strides=2, activation='elu', padding='same')) decoder.add(L.Conv2DTranspose(filters=3, kernel_size=(3, 3), strides=2, activation=None, padding='same')) return encoder, decoder

编码器和解码器摘要如下:
encoder, decoder = build_deep_autoencoder(IMG_SHAPE, code_size=32) encoder.summary() decoder.summary()


训练机器学习模型
使用“adamax”优化器和平均平方误差损失对网络进行训练。Python实现如下:
inp = L.Input(IMG_SHAPE)
code = encoder(inp)
reconstruction = decoder(code)
autoencoder = keras.models.Model(inputs=inp, outputs=reconstruction)
autoencoder.compile(optimizer="adamax", loss='mse')
model_filename = 'autoencoder.{0:03d}.hdf5'
last_finished_epoch = 23
### uncomment below to continue training from model checkpoint
### fill `last_finished_epoch` with your latest finished epoch
from keras.models import load_model
s = reset_tf_session()
# last_finished_epoch = 4
autoencoder = load_model(model_filename.format(last_finished_epoch))
encoder = autoencoder.layers[1]
decoder = autoencoder.layers[2]
autoencoder.fit(x=x_train, y=x_train, epochs=25,
validation_data=[x_test, x_test],
callbacks=[ModelSaveCallback(model_filename)],
verbose=1,
initial_epoch=last_finished_epoch or 0)

def visualize(img,encoder,decoder):
"""Draws original, encoded and decoded images"""
code = encoder.predict(img[None])[0] # img[None] is the same as img[np.newaxis, :]
reco = decoder.predict(code[None])[0]
plt.subplot(1,3,1)
plt.title("Original")
show_image(img)
plt.subplot(1,3,2)
plt.title("Code")
plt.imshow(code.reshape([code.shape[-1]//2,-1]))
plt.subplot(1,3,3)
plt.title("Reconstructed")
show_image(reco)
plt.show()
def show_image(x):
plt.imshow(np.clip(x + 0.5, 0, 1))
reconstruction_mse = autoencoder.evaluate(x_test, x_test, verbose=0)
print("Convolutional autoencoder MSE:", reconstruction_mse)
for i in range(5):
img = x_test[i]
visualize(img,encoder,decoder)
Convolutional autoencoder MSE: 0.011431425794959068

所以我们训练了一个不完美的网络,将图像转换为自身。我们将使用此网络生成表示并在潜在空间中查找类似的图像。
图像搜索
我们想在一个潜在空间(encoder code)中找到图像的最近邻。为了简单起见,我们用蛮力法计算最近的邻居。Python实现如下:
images = x_train
codes = encoder.predict(images)
from sklearn.neighbors.unsupervised import NearestNeighbors
nei_clf = NearestNeighbors(metric="euclidean")
nei_clf.fit(codes)
def get_similar(image, n_neighbors=5):
assert image.ndim==3,"image must be [batch,height,width,3]"
code = encoder.predict(image[None])
(distances,),(idx,) = nei_clf.kneighbors(code,n_neighbors=n_neighbors)
return distances,images[idx]
def show_similar(image):
distances,neighbors = get_similar(image,n_neighbors=3)
plt.figure(figsize=[8,7])
plt.subplot(1,4,1)
show_image(image)
plt.title("Original image")
for i in range(3):
plt.subplot(1,4,i+2)
show_image(neighbors[i])
plt.title("Dist=%.3f"%distances[i])
plt.show()
进行测试一下:
show_similar(x_test[41])

show_similar(x_test[209])

show_similar(x_test[93])

改进
为了产生更好的结果,您可以尝试以下一些方法;
- 超参数调整
- 去噪自动编码器
- 为了加快检索过程,应该在编码向量之上使用局部敏感哈希。
结论
在本文中,我们使用一种称为自动编码器的神经网络来研究基于内容的图像检索。我们还研究了使用卷积,“转置卷积”和池化层进行编码,解码图像表示。我们将这个网络应用于图像检索任务,使用最近邻算法在由欧氏距离分隔的潜在空间中找到相似的图像。