keras自定义simm作为损失函数,并且实现Tensor和数组之间的转换

  1. ssim介绍

在比较两幅图像误差或者相似度时,常用的衡量方法有MAE和MSE,
https://blog.youkuaiyun.com/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.youkuaiyun.com/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]。
在这里插入图片描述

以上!

<think>我们将在C#中使用Keras.NET实现自定义损失函数评估指标。根据引用[2][5]中的信息,当使用自定义损失函数或层时,我们需要在加载模型时通过`custom_objects`参数指定。同样,在编译模型时,我们也需要能够使用自定义损失函数评估指标。在Keras.NET中,我们可以通过以下步骤实现:1.**定义自定义损失函数**:自定义损失函数是一个接受两个张量(y_truey_pred)并返回一个标量(损失值)的函数。2.**定义自定义评估指标**:自定义评估指标与自定义损失函数的定义方式类似,也是一个接受y_truey_pred并返回标量的函数。3.**在编译模型时指定**:在调用`model.compile`时,将自定义损失函数评估指标函数分别传递给`loss``metrics`参数。4.**保存加载模型**:当保存模型后重新加载时,需要提供`custom_objects`参数来映射自定义函数名称到函数实现。以下是一个具体的示例:###步骤1:定义自定义损失函数评估指标假设我们想实现一个Dice系数损失函数(常用于图像分割)作为自定义损失函数,并实现IoU(IntersectionoverUnion)作为自定义评估指标。```csharpusingTensorflow.Keras.Losses;usingTensorflow.Keras.Metrics;usingstaticTensorflow.Binding;//自定义损失函数:DiceLosspublicclassDiceLoss:LossFunctionWrapper{publicDiceLoss():base("dice_loss"){}protectedoverrideTensorCall(Tensory_true,Tensory_pred,Tensorsample_weight=null){//平滑因子,避免除以零floatsmooth=1.0f;//将y_truey_pred展平y_true=tf.reshape(y_true,new[]{-1});y_pred=tf.reshape(y_pred,new[]{-1});//计算交集varintersection=tf.reduce_sum(y_true*y_pred);//计算并集varunion=tf.reduce_sum(y_true)+tf.reduce_sum(y_pred);//计算Dice系数vardice=(2.0f*intersection+smooth)/(union+smooth);//返回Dice损失(1-Dice系数)return1.0f-dice;}}//自定义评估指标:IoUpublicclassIoU:Metric{floattotal_iou;intcount;publicIoU():base("iou"){total_iou=0;count=0;}publicoverridevoidreset_states(){total_iou=0;count=0;}publicoverrideTensorresult(){returntotal_iou/count;}publicoverridevoidupdate_state(Tensory_true,Tensory_pred,Tensorsample_weight=null){//计算IoUvarintersection=tf.reduce_sum(y_true*y_pred);varunion=tf.reduce_sum(y_true)+tf.reduce_sum(y_pred)-intersection;variou=intersection/union;total_iou+=(float)iou.numpy();count+=1;}}```###步骤2:在编译模型时使用自定义函数```csharpusingTensorflow.Keras.Models;usingTensorflow.Keras.Optimizers;usingstaticTensorflow.KerasApi;//创建模型varmodel=keras.Sequential();model.add(keras.layers.Dense(64,activation:"relu",input_shape:newShape(10)));model.add(keras.layers.Dense(1,activation:"sigmoid"));//实例化自定义损失函数评估指标vardiceLoss=newDiceLoss();variou=newIoU();//编译模型model.compile(optimizer:newAdam(learning_rate:0.001f),loss:diceLoss,metrics:new[]{iou});```###步骤3:训练模型```csharp//假设我们有一些训练数据varx_train=np.array(...);vary_train=np.array(...);//训练模型model.fit(x_train,y_train,epochs:10,batch_size:32);```###步骤4:保存加载模型保存模型:```csharpmodel.save("model_with_custom");```加载模型时,需要提供`custom_objects`:```csharpvarloaded_model=keras.models.load_model("model_with_custom",custom_objects:newDictionary<string,object>{{"dice_loss",newDiceLoss()},{"iou",newIoU()}});```###关键点说明1.**自定义损失函数**:-继承自`LossFunctionWrapper`,并重写`Call`方法。-`Call`方法中计算损失,注意处理张量运算。2.**自定义评估指标**:-继承自`Metric`类,需要重写以下方法:-`reset_states()`:重置状态(每个epoch开始时调用)。-`update_state()`:根据每个batch的数据更新状态。-`result()`:返回最终的指标值(例如,所有batch的平均IoU)。-在`update_state`中计算每个batch的指标,并累积到成员变量中。3.**编译模型**:-将自定义损失函数评估指标的实例传递给`compile`方法。4.**加载模型**:-通过`custom_objects`字典将自定义组件的名称映射到对应的类实例。###注意事项-在自定义损失函数评估指标中,使用TensorFlow操作(如`tf.reduce_sum`)以确保计算图正确构建。-评估指标类中的状态变量(如`total_iou``count`)用于在整个epoch中累积值。###相关问题1.如何在自定义评估指标中处理多分类问题?2.自定义损失函数中如何实现样本权重(sample_weight)?3.在分布式训练(如使用`MirroredStrategy`)时,自定义评估指标需要注意什么?4.如何将Python中定义的Keras自定义损失函数转换为C#实现?5.在自定义损失函数中,如何访问除y_truey_pred之外的其他层参数(如正则化损失)?>更多示例可参考Keras.NET官方文档TensorFlow.NET示例库[^2][^5]。</think>### C# Keras 自定义损失函数评估指标实现指南 在C#中使用Keras.NET实现自定义损失函数评估指标需要以下关键步骤: #### 一、自定义损失函数实现 ```csharp using Tensorflow.Keras.Losses; using static Tensorflow.Binding; // 1. 继承LossFunctionWrapper实现自定义损失函数 public class DiceLoss : LossFunctionWrapper { public DiceLoss() : base("dice_loss") { } protected override Tensor Call(Tensor y_true, Tensor y_pred, Tensor sample_weight = null) { // Dice系数计算 (2 * |X∩Y|) / (|X| + |Y|) float smooth = 1.0f; // 平滑因子防止除零 var intersection = tf.reduce_sum(y_true * y_pred); var union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred); var dice = (2.0f * intersection + smooth) / (union + smooth); // 返回1 - Dice系数作为损失值 return 1.0f - dice; } } // 2. 在编译模型时使用自定义损失函数 var model = keras.Sequential(); model.add(keras.layers.Dense(64, activation: "relu", input_shape: new Shape(10))); model.add(keras.layers.Dense(1, activation: "sigmoid")); model.compile( optimizer: new Adam(0.001f), loss: new DiceLoss(), // 使用自定义损失函数 metrics: new[] { "accuracy" } ); ``` #### 二、自定义评估指标实现 ```csharp using Tensorflow.Keras.Metrics; // 1. 继承Metric实现自定义评估指标 public class IoUMetric : Metric { float total_iou; int count; public IoUMetric() : base("iou") { total_iou = 0; count = 0; } // 重置状态(每个epoch开始时调用) public override void reset_states() { total_iou = 0; count = 0; } // 更新状态(每个batch结束时调用) public override void update_state(Tensor y_true, Tensor y_pred, Tensor sample_weight = null) { var intersection = tf.reduce_sum(y_true * y_pred); var union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred) - intersection; var iou = intersection / (union + 1e-7f); // 添加小值防止除零 total_iou += (float) iou.numpy(); count += 1; } // 计算最终指标值 public override Tensor result() { return total_iou / count; } } // 2. 在模型编译时添加自定义指标 model.compile( optimizer: new Adam(0.001f), loss: "binary_crossentropy", metrics: new[] { new IoUMetric() } // 使用自定义评估指标 ); ``` #### 三、模型保存与加载(含自定义函数) ```csharp // 保存模型 model.save("custom_model.h5"); // 加载模型时需注册自定义函数 var loadedModel = keras.models.load_model( "custom_model.h5", custom_objects: new Dictionary<string, object> { { "dice_loss", new DiceLoss() }, { "iou", new IoUMetric() } } ); ``` #### 数学原理说明 1. **Dice损失函数**: $$ \text{Dice} = \frac{2 \times |X \cap Y|}{|X| + |Y|}, \quad \text{Loss} = 1 - \text{Dice} $$ 常用于图像分割任务,优化目标区域的重叠率[^2] 2. **IoU评估指标**: $$ \text{IoU} = \frac{|X \cap Y|}{|X \cup Y|} = \frac{|X \cap Y|}{|X| + |Y| - |X \cap Y|} $$ 衡量预测与真实区域的重合度,值域$[0, 1]$,值越大越好[^3] #### 高级应用技巧 1. **多指标组合**: ```csharp model.compile( loss: new DiceLoss(), metrics: new IMetric[] { new IoUMetric(), new Precision(), new Recall() } ); ``` 2. **参数化损失函数**: ```csharp public class WeightedDiceLoss : LossFunctionWrapper { private float _alpha; public WeightedDiceLoss(float alpha) : base("weighted_dice_loss") { _alpha = alpha; } protected override Tensor Call(Tensor y_true, Tensor y_pred, Tensor sample_weight = null) { // 使用_alpha参数实现加权计算 } } ``` 3. **TensorFlow原生操作**: ```csharp // 在自定义函数中使用TF原生操作 var squared_diff = tf.square(y_true - y_pred); var loss = tf.reduce_mean(squared_diff); ``` #### 常见问题解决 1. **加载模型报错**: ```csharp // 错误:Unknown loss function: dice_loss // 解决方案:添加custom_objects参数[^2][^5] var model = keras.models.load_model( "model.h5", custom_objects: new Dictionary<string, object> { { "dice_loss", new DiceLoss() } } ); ``` 2. **梯度计算问题**: - 确保所有操作使用`tf.*`方法而非C#原生运算 - 检查损失函数输出为标量值 > 完整实现参考Keras.NET官方示例TensorFlow.NET文档[^1][^4] ### 相关问题 1. 如何在自定义损失函数中处理多分类问题的类别不平衡? 2. 自定义评估指标如何实现按批次(batch)累积计算? 3. 在分布式训练中如何确保自定义函数的跨设备一致性? 4. 如何将Python实现Keras自定义函数迁移到C#? 5. 自定义函数如何访问模型中间层的输出进行计算?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值