keras自定義simm作爲損失函數,並且實現Tensor和數組之間的轉換

  1. ssim介紹

在比較兩幅圖像誤差或者相似度時,常用的衡量方法有MAE和MSE,
https://blog.csdn.net/u011875342/article/details/78036380
在這裏插入圖片描述
但是上述這兩種損失不足以表達人的視覺系統對圖像的直觀感受,有時候兩張圖片只是亮度不同,但是他們之間的MSE loss相差很大,而一副很模糊與另一幅很清晰的圖,他們的MSE loss可能反而很小。

而SSIM(Structural Similarity)結構相似度更能反映人類視覺系統對兩幅圖相似性的判斷。
具體理論和公式計算可見以下鏈接:
https://mp.weixin.qq.com/s/hGum0fXrKUHCoaInVVS3rQ
在這裏插入圖片描述
在這裏插入圖片描述

  1. ssim調用

目前可以從anaconda自帶的庫skimage庫中找到該函數,具體調用方法如下:
另外,也可以調用其他圖像相似度評價函數,如MSE,PSNR。

from skimage.measure import compare_ssim  
from skimage.measure import compare_mse
from skimage measure import  compare_psnr

在這裏插入圖片描述
這三個函數只默認支持 兩張圖片進行對比,無法進行批量比較。X,Y必須都爲數組。
另外,如果是X Y 都是彩色圖三色,即爲多通道的,則需要設置mutichannel=True;

import keras
from skimage.measure import compare_ssim
from skimage.measure import compare_mse

img1=keras.preprocessing.image.load_img(img_path1_1)  #先通過keras讀取圖片,
img1_array=keras.preprocessing.image.img_to_array(img1)/255   #將圖片轉換爲數組

img2=keras.preprocessing.image.load_img(img_path2_2)
img2_array=keras.preprocessing.image.img_to_array(img2)/255

ssim=compare_ssim(img1_array,img2_array,multichannel=True)
mse=compare_mse(img1_array,img2_array)
print('ssim value:' ,ssim)
print('mse value:',mse)

ssim value: 0.5348699316504579
mse value: 0.030186621034944017
一般情況下,ssim在-1~1之間,數值越大代表相似度越高。
肉眼上兩張圖片相似度較高,但是ssim值卻不到0.6.說明 ssim可以很容易捕捉到兩幅圖像之間的差異。因此可以考慮使用ssim作爲圖像迴歸中的損失函數;
兩張圖片如下
在這裏插入圖片描述

詳細介紹可以參考次鏈接:https://www.jianshu.com/p/ca343a441e6d

  1. Tensor和數組之間的轉換
    先插入一個題外話,目前tensorflow和keras等深度學習框架中,默認使用張量Tensor,雖然Tensor在概念上和我們常見的矩陣沒什麼區別,但是在深度模型運算中,稍不注意,就會出錯。
    這裏對如何對 Tensor和數組進行轉化給與說明:
import tensorflow as tf
import numpy as np

#a爲數組
a=np.random.randint(0,10,(2,3))
print('type of a:',type(a))
print()

#將a轉化成Tensor
tensor_a=tf.convert_to_tensor(a)
print('type of tensor_a:',type(tensor_a))
print()

#將tensor轉化成數組
with tf.Session():
    array_a=tensor_a.eval()    
print('type of array_a:',type(array_a))
print()


輸出結果如下:
type of a: <class ‘numpy.ndarray’>

type of tensor_a: <class ‘tensorflow.python.framework.ops.Tensor’>

type of array_a: <class ‘numpy.ndarray’>

  1. ssim作爲損失函數

需要說明的是:ssim爲衡量圖片相似度的,數值在-1~1之間,數值越大,越相似。而訓練神經網絡的爲了使得損失函數越來越小,因此這裏選取 1-ssim作爲損失。

目前keras中不支持ssim作爲損失函數,但是tensorflow中有ssim函數
可以參看tensorflow文檔:
https://www.w3cschool.cn/tensorflow_python/tensorflow_python-c5xz2rsh.html 說明
https://www.w3cschool.cn/tensorflow_python/tensorflow_python-ats62pzl.html 源碼

如果想在keras中使用ssim爲損失函數,可以自己根據自己要求單獨寫,損失函數寫法可以參考我的另一篇博客,鏈接如下:
https://blog.csdn.net/weixin_43718675/article/details/89041352

我嘗試了兩種寫法,一種是使用skimage庫,一種是使用tensorflow

先查看keras中損失函數的輸入數據type和維度:
https://keras.io/zh/losses/
https://github.com/keras-team/keras/blob/master/keras/losses.py
如:

def mean_squared_error(y_true, y_pred):
    return K.mean(K.square(y_pred - y_true), axis=-1)

這裏面默認的輸入和輸出都是 Tensor

因此自定義損失函數時候,需要保證輸入輸出都是張量

4.1 使用skimag庫

def my_ssim_loss(y_true,y_pred):
    
    from keras import backend as K
    import tensorflow as tf
    
    print(type(y_pred))
    print('y_shape:',y_true.shape)
    with tf.Session():
        y_pred=y_pred.eval()
        y_true=y_true.eval()

#    y_true=np.array(y_true)
#    y_pred=np.array(y_pred)
    print(type(y_pred))
    
    print('y_shape:',y_true.shape)
    shape=y_true.shape
    if len(shape)==5:
        batch=shape[0]
        set_length=shape[1]
        
        total_loss=np.zeros((batch,set_length,400,400))
        
        for i in range(batch):
            for j in range(set_length):
                ssim_loss=1-compare_ssim(np.array(y_true[i,j]),np.array(y_pred[i,j]),multichannel=True)
                total_loss[i,j,:,:]  +=ssim_loss
#                loss.append(ssim_loss)
        
#        loss=np.array(loss).reshape(batch,set_lenth)
               
        total_loss=tf.convert_to_tensor(total_loss)
        print(type(total_loss))
        
        print('total_loss_shape:',total_loss.shape) 
        return total_loss       

由於使用了ConvLSTM2D, 因此輸入y爲5D的,使用MSE爲loss時候,損失爲4D的,爲了與mse_loss保持一致,這裏使用擴充,具體過程見以上代碼。雖然在類型和維度上保持了一致,可是在調用時,還是出錯了。
原因不太清楚。

4.2 使用tf.image.ssim

def tf_ssim_loss(y_true,y_pred):
    
    import keras.backend as k
    
#    total_loss=1-tf.image.ssim_multiscale(y_true,y_pred,max_val=1)
    total_loss=1-tf.image.ssim(y_true,y_pred,max_val=1)

    with tf.Session():  
        total_loss=1-total_loss.eval()    
    
    
    batch=total_loss.shape[0]
    set_length=total_loss.shape[1]
    loss=np.zeros((batch,set_length,400,400))
    
    for i in range(batch):
        for j in range(set_length):
            loss[i,j]=total_loss[i,j]
            
    loss=tf.convert_to_tensor(loss)
    
    return loss

同樣爲了保持維度,還是出錯。

後來,我不人爲進行維度設置,而是使用tf默認的輸出loss維度,結果倒還正確了
可能上述出錯的原因在於:傳入的y_true等張量雖然是5D的,但是其每個維度的大小是佔位符placeholder,無法實體化,即無法進行類似batch=total_loss.shape[0]這樣的獲取維度具體信息的操作。

def tf_ssim_loss(y_true,y_pred):
    
    total_loss=1-tf.image.ssim(y_true,y_pred,max_val=1)
    
    return total_loss    
model.compile(optimizer=opt,loss=tf_ssim_loss,metrics=['accuracy'])

這裏的total_loss的維度爲2D,即[batch, set_length ]而上述使用MSE時候,loss維度爲4D,即[batch, set_length, width,high]。
在這裏插入圖片描述

以上!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章