基于內(nèi)容的圖像檢索也稱為圖像內(nèi)容查詢,它是基于計算機視覺技術(shù)應(yīng)用于圖像檢索的問題。它涉及從圖像數(shù)據(jù)庫中檢索視覺上相似的圖像到給定的查詢圖像。在本文中周詳基于內(nèi)容的圖像檢索研究周詳基于內(nèi)容的圖像檢索研究,基于內(nèi)容的圖像檢索問題是使用無監(jiān)督的機器學(xué)習(xí)技術(shù)即自動編碼器來實現(xiàn)的。
我們將使用包含10個類的32x32彩色圖像的機器學(xué)習(xí)圖像數(shù)據(jù)集。該機器學(xué)習(xí)模型使用,numpy,,Keras API開發(fā)。
導(dǎo)入庫
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
所需的輔助函數(shù)
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
加載機器學(xué)習(xí)數(shù)據(jù)集
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()
上述代碼輸出顯示如下:
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
自動編碼器
自動編碼器
自動編碼器是用于無監(jiān)督任務(wù)的神經(jīng)網(wǎng)絡(luò)的變體,其目標(biāo)是將其輸入復(fù)制到其輸出。它有一個隱藏層z,用于描述用于表示輸入的代碼。自動編碼器由兩部分組成: 編碼器,Z = F(X),這部分網(wǎng)絡(luò)負(fù)責(zé)將輸入編碼到一個潛在空間表示。解碼器,目的是重構(gòu)從-space表示的輸入r = g(z)。
我們將自動編碼器設(shè)計為兩個 keras模型:編碼器和解碼器。
編碼器
我們將卷積層和池化層堆疊起來,最后使用一個dense層來表示所需的大小()。我們對所有卷積層和dense層使用“elu”激活。
解碼器
對于解碼器,我們將使用“轉(zhuǎn)置卷積”。在傳統(tǒng)的卷積層中,獲取圖像的一個patch并生成一個數(shù)字。在轉(zhuǎn)置卷積中,我們要取一個數(shù)字并輸出圖像的patch。我們的解碼器以dense層開始,以“undo”最后一層編碼器。
實現(xiàn)
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()
訓(xùn)練機器學(xué)習(xí)模型
使用“”優(yōu)化器和平均平方誤差損失對網(wǎng)絡(luò)進行訓(xùn)練。實現(xiàn)如下:
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)
MSE: 0.
所以我們訓(xùn)練了一個不完美的網(wǎng)絡(luò),將圖像轉(zhuǎn)換為自身。我們將使用此網(wǎng)絡(luò)生成表示并在潛在空間中查找類似的圖像。
圖像搜索
我們想在一個潛在空間( code)中找到圖像的最近鄰。為了簡單起見,我們用蠻力法計算最近的鄰居。實現(xiàn)如下:
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])
改進
為了產(chǎn)生更好的結(jié)果,您可以嘗試以下一些方法;
結(jié)論
在本文中,我們使用一種稱為自動編碼器的神經(jīng)網(wǎng)絡(luò)來研究基于內(nèi)容的圖像檢索。我們還研究了使用卷積,“轉(zhuǎn)置卷積”和池化層進行編碼,解碼圖像表示。我們將這個網(wǎng)絡(luò)應(yīng)用于圖像檢索任務(wù),使用最近鄰算法在由歐氏距離分隔的潛在空間中找到相似的圖像。