使用TensorFlow查询类似图片
介绍
假设你要搜索与任何图片相似的图像。想象一下在网上搜索与我们用手机拍摄的图像相似的图像。在本文中,我们将开发和比较两种不同的方式,这些方式可以使用深度学习算法解决在数千张图像(最相似的图像)之间进行查询的问题。
我们将比较两种不同的方法:
自动编码器
图像特征提取
数据
我们将使用Flipkart图像数据集来解决这个问题。因此,我们将从这种庞大的印度电子商务产品中找到相似的图片。
从数据上的可用URL下载图像后,我们获得了18322张不同产品的图像。
可以在此Github存储库中找到用于下载图像和开发两种方法的代码。
Github存储库:https://github.com/luchonaveiro/image-search-engine
自动编码器
让我们尝试通过使用自动编码器模型获得相似的图像。这种类型的模型包括三个主要部分:
编码器
潜在空间
解码器
该模型背后的思想是重构我们为算法提供的输入,因此输入和输出大小相同。因此,如果我们可以输入,则可以将图像的尺寸减小到非常小的向量,而该向量就是潜在空间。如果模型是健壮的,我们可以将图像的所有复杂性降低到较小的尺寸。
为了训练自动编码器,我们将使用Tensorflow 2.0库中的Keras模块。
下载图像后,我们可以定义训练和验证集。在这里,我们将使用ImageDataGenerator API。
from tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.layers import Flatten, Conv2D, Conv2DTranspose, LeakyReLU, BatchNormalization, Input, Dense, Reshape, Activation
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint
import tensorflow.keras.backend as K
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm_notebook as tqdm
import pickle
import pandas as pd# Load images
img_height = 256
img_width = 256
channels = 3
batch_size = 16train_datagen = ImageDataGenerator(rescale=1./255,validation_split=0.2)training_set = train_datagen.flow_from_directory('./flipkart/images',target_size = (img_height, img_width),batch_size = batch_size,class_mode = 'input',subset = 'training',shuffle=True)validation_set = train_datagen.flow_from_directory('./flipkart/images',target_size = (img_height, img_width),batch_size = batch_size,class_mode = 'input',subset = 'validation',shuffle=False) 参数class_mode='input'是这里的关键。在ImageDataGenerator文档上,我们发现了以下内容:
class_mode:“binary”,“categorical”,“input”,“ multi_output”,“raw”,sparse或“None”之一。默认值:“categorical”。
产生目标的模式—
"binary":二进制标签的一维numpy数组。—
"categorical":独热编码标签的二维numpy数组。支持多标签输出;—
"input":与输入图像相同的图像(主要用于自动编码器);—
"multi_output":列出不同列的值的列表;—
"raw":由y_col列中的值组成的numpy数组;—
"sparse": 由整型标签组成的一维numpy数组;—
“None",不返回任何目标(生成器只会产生一批图像数据,这在model.predict()中很有用)。
另外,为使此方法有效,你应将所有图像放在另一个文件夹中,因此Keras API假定你只有一个类。在此示例中,我具有以下图像目录:flipkart/images/images/...
现在,我们可以定义我们的模型架构,并将其与图像匹配:
# Define the autoencoder
input_model = Input(shape=(img_height, img_width, channels))# Encoder layers
encoder = Conv2D(32, (3,3), padding='same', kernel_initializer='normal')(input_model)
encoder = LeakyReLU()(encoder)
encoder = BatchNormalization(axis=-1)(encoder)encoder = Conv2D(64, (3,3), padding='same', kernel_initializer='normal')(encoder)
encoder = LeakyReLU()(encoder)
encoder = BatchNormalization(axis=-1)(encoder)encoder = Conv2D(64, (3,3), padding='same', kernel_initializer='normal')(input_model)
encoder = LeakyReLU()(encoder)
encoder = BatchNormalization(axis=-1)(encoder)encoder_dim = K.int_shape(encoder)
encoder = Flatten()(encoder)# Latent Space
latent_space = Dense(16, name='latent_space')(encoder)# Decoder Layers
decoder = Dense(np.prod(encoder_dim[1:]))(latent_space)
decoder = Reshape((encoder_dim[1], encoder_dim[2], encoder_dim[3]))(decoder)decoder = Conv2DTranspose(64, (3,3), padding='same', kernel_initializer='normal')(decoder)
decoder = LeakyReLU()(decoder)
decoder = BatchNormalization(axis=-1)(decoder)decoder = Conv2DTranspose(64, (3,3), padding='same', kernel_initializer='normal')(decoder)
decoder = LeakyReLU()(decoder)
decoder = BatchNormalization(axis=-1)(decoder)decoder = Conv2DTranspose(32, (3,3), padding='same', kernel_initializer='normal')(decoder)
decoder = LeakyReLU()(decoder)
decoder = BatchNormalization(axis=-1)(decoder)decoder = Conv2DTranspose(3, (3, 3), padding="same")(decoder)
output = Activation('sigmoid', name='decoder')(decoder)# Create model object
autoencoder = Model(input_model, output, name='autoencoder')# Compile the model
autoencoder.compile(loss="mse", optimizer= Adam(learning_rate=1e-3))# Fit the model
history = autoencoder.fit_generator(training_set,steps_per_epoch=training_set.n // batch_size,epochs=100,validation_data=validation_set,validation_steps=validation_set.n // batch_size,callbacks = [ModelCheckpoint('models/image_autoencoder_2.h5', monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=False)]) 拟合模型后,我们可以尝试重建一些图像,因为这是自动编码器的目标:
左:图像输入 / 右:使用训练有素的自动编码器进行图像重建
现在是时候使用潜在空间来寻找相似的图像了。要实现这一点,我们不需要最终的预测,我们需要中间层的输出,特别是我们在模型定义上命名为latent_space的那个层。这就是为什么命名模型中的每个层很重要,这样我们就可以快速和透明地访问我们需要的任何层。
使用模型API和训练模型的.get_layer()方法很容易定义我们选择的输入输出层模型:
latent_space_model = Model(autoencoder.input, autoencoder.get_layer(‘latent_space’).output) 现在,每当我们使用.predict()带有图像的方法作为此新模型的输入时,我们都会获得潜在空间作为输出。
要使用这个方法来得到类似的图片,我们需要使用latent_space_model来预测每个图像,所以我们可以计算所有保存的图像之间的欧氏距离,以及希望找到相似图像的任何新图像之间的距离。
# Load all images and predict them with the latent space model
X = []
indices = []for i in tqdm(range(len(os.listdir('./flipkart/images/images')))):try:img_name = os.listdir('./flipkart/images')[i]img = load_img('./flipkart/images/images/{}'.format(img_name), target_size = (256, 256))img = img_to_array(img) / 255.0img = np.expand_dims(img, axis=0)pred = latent_space_model.predict(img)pred = np.resize(pred, (16))X.append(pred)indices.append(img_name)except Exception as e:print(img_name)print(e)# Export the embeddings
embeddings = {'indices': indices, 'features': np.array(X)}
pickle.dump(embeddings, open('./flipkart/image_embeddings.pickle', 'wb')) 正如我已经说过的,我们将通过计算欧几里德距离来找到相似的图像,因此该计算的值越低,图像的相似度就越高。我们将欧式距离定义为:
def eucledian_distance(x,y):eucl_dist = np.linalg.norm(x - y)return eucl_dist 定义完所有内容后,我们可以获取任何输入图像的三个最相似的乘积。例如,如果输入以下Polo衫,则会得到以下3个最相似的对象:
图像特征提取
解决此问题的另一种方法是计算到图像特征的距离。在这里,我们可以使用预先训练的深度学习模型,提取每个图像特征,然后将它们与任何新图片进行比较。从这个意义上讲,这种方法与Autoencoder模型的方法并没有太大的不同,但是非常不同的是我们将要使用的模型架构。
在这种情况下,我们将在imagenet数据集上使用VGG16预训练模型
在这里,我们不会训练模型,而是通过获取全连接层(名为fc1)的输出来提取图像特征。
我们定义以下类来提取图像的特征
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.vgg16 import VGG16, preprocess_input
from tensorflow.keras.models import Model
import numpy as npclass FeatureExtractor:def __init__(self):# Use VGG-16 as the architecture and ImageNet for the weightbase_model = VGG16(weights='imagenet')# Customize the model to return features from fully-connected layerself.model = Model(inputs=base_model.input, outputs=base_model.get_layer('fc1').output)def extract(self, img):# Resize the imageimg = img.resize((224, 224))# Convert the image color spaceimg = img.convert('RGB')# Reformat the imagex = image.img_to_array(img)x = np.expand_dims(x, axis=0)x = preprocess_input(x)# Extract Featuresfeature = self.model.predict(x)[0]return feature / np.linalg.norm(feature) 一旦获得每张图像的输出,我们就可以选择一张图片并获得前三张最相似的图像。如果我们比较与自动编码器模型使用的相同的Polo衫,则会得到以下结果:
我们可以看到,这些结果与以前的方法并没有太大不同。
结论
在本文中,我们比较了两种不同的方法来开发图像搜索引擎并通过使用图片作为输入来获取图像结果。
我们开发了自动编码器和图像特征提取方法,并获得了非常相似的结果。
参考文献
Keras自动编码器: https://blog.keras.io/building-autoencoders-in-keras.html
用于大规模图像识别的超深度卷积网络:https://arxiv.org/abs/1409.1556
☆ END ☆
如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 mthler」,每日朋友圈更新一篇高质量博文。
↓扫描二维码添加小编↓
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
