損失函數除了作為模型訓練時候的優化目標,也能夠作為模型好壞的一種評價指標。但通常人們還會從其它角度評估模型的好壞。
這就是評估指標。通常損失函數都可以作為評估指標,如MAE,MSE,CategoricalCrossentropy等也是常用的評估指標。
但評估指標不一定可以作為損失函數,例如AUC,Accuracy,Precision。因為評估指標不要求連續可導,而損失函數通常要求連續可導。
編譯模型時,可以通過列表形式指定多個評估指標。
如果有需要,也可以自定義評估指標。
自定義評估指標需要接收兩個張量y_true,y_pred作為輸入參數,並輸出一個標量作為評估值。
也可以對tf.keras.metrics.Metric進行子類化,重寫初始化方法, update_state方法, result方法實現評估指標的計算邏輯,從而得到評估指標的類的實現形式。
由於訓練的過程通常是分批次訓練的,而評估指標要跑完一個epoch才能夠得到整體的指標結果。因此,類形式的評估指標更為常見。即需要編寫初始化方法以創建與計算指標結果相關的一些中間變量,編寫update_state方法在每個batch后更新相關中間變量的狀態,編寫result方法輸出最終指標結果。
如果編寫函數形式的評估指標,則只能取epoch中各個batch計算的評估指標結果的平均值作為整個epoch上的評估指標結果,這個結果通常會偏離拿整個epoch數據一次計算的結果。
一,常用的內置評估指標
-
MeanSquaredError(平方差誤差,用於回歸,可以簡寫為MSE,函數形式為mse)
-
MeanAbsoluteError (絕對值誤差,用於回歸,可以簡寫為MAE,函數形式為mae)
-
MeanAbsolutePercentageError (平均百分比誤差,用於回歸,可以簡寫為MAPE,函數形式為mape)
-
RootMeanSquaredError (均方根誤差,用於回歸)
-
Accuracy (准確率,用於分類,可以用字符串"Accuracy"表示,Accuracy=(TP+TN)/(TP+TN+FP+FN),要求y_true和y_pred都為類別序號編碼)
-
Precision (精確率,用於二分類,Precision = TP/(TP+FP))
-
Recall (召回率,用於二分類,Recall = TP/(TP+FN))
-
TruePositives (真正例,用於二分類)
-
TrueNegatives (真負例,用於二分類)
-
FalsePositives (假正例,用於二分類)
-
FalseNegatives (假負例,用於二分類)
-
AUC(ROC曲線(TPR vs FPR)下的面積,用於二分類,直觀解釋為隨機抽取一個正樣本和一個負樣本,正樣本的預測值大於負樣本的概率)
-
CategoricalAccuracy(分類准確率,與Accuracy含義相同,要求y_true(label)為onehot編碼形式)
-
SparseCategoricalAccuracy (稀疏分類准確率,與Accuracy含義相同,要求y_true(label)為序號編碼形式)
-
MeanIoU (Intersection-Over-Union,常用於圖像分割)
-
TopKCategoricalAccuracy (多分類TopK准確率,要求y_true(label)為onehot編碼形式)
-
SparseTopKCategoricalAccuracy (稀疏多分類TopK准確率,要求y_true(label)為序號編碼形式)
-
Mean (平均值)
-
Sum (求和)
二, 自定義評估指標
我們以金融風控領域常用的KS指標為例,示范自定義評估指標。
KS指標適合二分類問題,其計算方式為 KS=max(TPR-FPR).
其中TPR=TP/(TP+FN) , FPR = FP/(FP+TN)
TPR曲線實際上就是正樣本的累積分布曲線(CDF),FPR曲線實際上就是負樣本的累積分布曲線(CDF)。
KS指標就是正樣本和負樣本累積分布曲線差值的最大值。
import numpy as np import pandas as pd import tensorflow as tf from tensorflow.keras import layers,models,losses,metrics # 函數形式的自定義評估指標 @tf.function def ks(y_true,y_pred): y_true = tf.reshape(y_true,(-1,)) y_pred = tf.reshape(y_pred,(-1,)) length = tf.shape(y_true)[0] t = tf.math.top_k(y_pred,k = length,sorted = False) y_pred_sorted = tf.gather(y_pred,t.indices) y_true_sorted = tf.gather(y_true,t.indices) cum_positive_ratio = tf.truediv( tf.cumsum(y_true_sorted),tf.reduce_sum(y_true_sorted)) cum_negative_ratio = tf.truediv( tf.cumsum(1 - y_true_sorted),tf.reduce_sum(1 - y_true_sorted)) ks_value = tf.reduce_max(tf.abs(cum_positive_ratio - cum_negative_ratio)) return ks_value y_true = tf.constant([[1],[1],[1],[0],[1],[1],[1],[0],[0],[0],[1],[0],[1],[0]]) y_pred = tf.constant([[0.6],[0.1],[0.4],[0.5],[0.7],[0.7],[0.7], [0.4],[0.4],[0.5],[0.8],[0.3],[0.5],[0.3]]) tf.print(ks(y_true,y_pred))
0.625
# 類形式的自定義評估指標 class KS(metrics.Metric): def __init__(self, name = "ks", **kwargs): super(KS,self).__init__(name=name,**kwargs) self.true_positives = self.add_weight( name = "tp",shape = (101,), initializer = "zeros") self.false_positives = self.add_weight( name = "fp",shape = (101,), initializer = "zeros") @tf.function def update_state(self,y_true,y_pred): y_true = tf.cast(tf.reshape(y_true,(-1,)),tf.bool) y_pred = tf.cast(100*tf.reshape(y_pred,(-1,)),tf.int32) for i in tf.range(0,tf.shape(y_true)[0]): if y_true[i]: self.true_positives[y_pred[i]].assign( self.true_positives[y_pred[i]]+1.0) else: self.false_positives[y_pred[i]].assign( self.false_positives[y_pred[i]]+1.0) return (self.true_positives,self.false_positives) @tf.function def result(self): cum_positive_ratio = tf.truediv( tf.cumsum(self.true_positives),tf.reduce_sum(self.true_positives)) cum_negative_ratio = tf.truediv( tf.cumsum(self.false_positives),tf.reduce_sum(self.false_positives)) ks_value = tf.reduce_max(tf.abs(cum_positive_ratio - cum_negative_ratio)) return ks_value y_true = tf.constant([[1],[1],[1],[0],[1],[1],[1],[0],[0],[0],[1],[0],[1],[0]]) y_pred = tf.constant([[0.6],[0.1],[0.4],[0.5],[0.7],[0.7], [0.7],[0.4],[0.4],[0.5],[0.8],[0.3],[0.5],[0.3]]) myks = KS() myks.update_state(y_true,y_pred) tf.print(myks.result())
0.625
參考:
開源電子書地址:https://lyhue1991.github.io/eat_tensorflow2_in_30_days/
GitHub 項目地址:https://github.com/lyhue1991/eat_tensorflow2_in_30_days