Go together

If you want to go fast, go alone. If you want to go far, go together.

핸즈온 머신러닝 2판

이진 오토인코더를 이용한 해싱

NowChan 2022. 2. 6. 20:29

인코더의 출력층을 sigmoid 함수로 해놓고 출력층 바로 뒤에 가우시안 잡음 층을 놓으면, 가우시안 잡음 층 바로 뒤에 있는 층의 출력이 커집니다. 잡음에 입력이 묻히지 않도록 하기 위해서 모델이 그렇게 학습합니다. 아마 이렇게 하면 이미지의 특성을 명확하게 표현하려고 하려나?

 

인코더의 출력층을 16개의 뉴런으로 하고, 출력값을 0과 1로 반올림하면 16비트의 '시맨틱 해시'를 얻습니다. 모든 것이 잘 수행되면 비슷한 이미지는 같은 해시를 가지게 됩니다. 이는 검색 엔진에 매우 유용합니다. 이미지의 시맨틱 해시에 따라 서버에 이미지를 저장하면 같은 서버에는 모두 비슷한 이미지가 저장될 것이고, 사용자가 탐색할 이미지를 전달하면 검색 엔진이 인코더를 사용해서, 전달된 이미지의 해시를 계산하고 이 해시에 해당하는 서버의 모든 이미지를 빠르게 반환할 수 있습니다.

 

인코더가 암호화하는 거니깐 결국 이미지는 16비트의 해시로 암호화한다고 볼 수도 있을 것 같다.

 

hashing_encoder = keras.models.Sequential([
        keras.layers.Flatten(input_shape=[28, 28]),
        keras.layers.Dense(100, activation='selu'),
        keras.layers.GaussianNoise(15.),
        keras.layers.Dense(16, activation='sigmoid')
])
hashing_decoder = keras.models.Sequential([
        keras.layers.Dense(100, activation='selu', input_shape=[16]),
        keras.layers.Dense(28*28, activation='sigmoid'),
        keras.layers.Reshape([28, 28])
])
hashing_ae = keras.models.Sequential([hashing_encoder, hashing_decoder])
hashing_ae.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Nadam())
history = hashing_ae.fit(X_train, X_train, epochs=10,\
                         validation_data=(X_valid, X_valid))
show_reconstructions(hashing_ae)
plt.show()

오토인코더가 이미지를 16비트로 많이 압축했습니다. 이미지가 많이 흐려졌지만 이미지를 재구성하는 게 목적이 아니라 시맨틱 해시를 생성하는게 목적이므로 상관없습니다.

 

plot_activations_histogram(hashing_encoder)
plt.show()

왼쪽 그래프에서 볼 수 있듯이 출력은 0과 1에 매우 가깝습니다.

 

hashes = np.round(hashing_encoder.predict(X_valid)).astype(np.int32) # 0, 1로 반올림
hashes *= np.array([[2**bit for bit in range(16)]]) 
# array([[    1,     2,     4,     8,    16,    32,    64,   128,   256, 
#           512,  1024,  2048,  4096,  8192, 16384, 32768]])
hashes = hashes.sum(axis=1) # 16비트 값으로 만듬
for h in hashes[:5]: # 5개 이미지에 대한 해시값 출력, 행 당 1개의 이미지
  print("{:016b}".format(h))
print('...')
'''
1010100100101000
1000110100101000
1101100100101000
0010101100001001
0000110101010000
...
'''
from collections import Counter

n_hashes = 10
n_images = 8

top_hashes = Counter(hashes).most_common(n_hashes)

plt.figure(figsize=(n_images, n_hashes))
for hash_index, (image_hash, hash_count) in enumerate(top_hashes):
  indices = (hashes == image_hash)
  # X_valid[False]면 빈 배열 반환, True면 X_valid 반환
  for index, image in enumerate(X_valid[indices][:n_images]): 
    plt.subplot(n_hashes, n_images, hash_index*n_images + index + 1)
    plt.imshow(image, cmap='binary')
    plt.axis('off')

같은 행에 있는 이미지는 모두 같은 해시를 가집니다.

 

출처: https://github.com/rickiepark/handson-ml2/blob/master/17_autoencoders_and_gans.ipynb