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]。
在这里插入图片描述

以上!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值