目录
2.4 SparseCategoricalCrossentropy()
1 基础数学知识
1.1 数学分布
分布可能指代不同的东西,比如数据分布或概率分布。这两种分布其实没有本质的区别,可以将x看做随机点,某一数据分布P为目标分布,概率分布f为x落在P上的概率值,形式化表示为f(x)。
假设我们是一组正在广袤无垠的太空中进行研究的科学家。我们发现了一些太空蠕虫,这些太空蠕虫的牙齿数量各不相同。现在我们需要将这些信息发回地球。但从太空向地球发送信息的成本很高,所以我们需要用尽量少的数据表达这些信息。我们有个好方法:我们不发送单个数值,而是绘制一张图表,其中 X 轴表示所观察到的不同牙齿数量(0,1,2…),Y 轴是看到的太空蠕虫具有 x 颗牙齿的概率(即具有 x 颗牙齿的蠕虫数量/蠕虫总数量)。这样,我们就将观察结果转换成了分布。
1.2 信息熵
熵是信息混乱程度的度量,信息越混乱越不确定熵越小。
我们对于信息内容的度量依赖于概率分布p(x),因此我们想要寻找一个函数h(x),它是概率p(x)的单调函数,表达了信息的内容。h()的形式可以这样寻找:如果我们有两个不相关的事件x和y,那么我们观察到两个事件同时发生时获得的信息应该等于观察到事件各自发生时获得的信息之和,即h(x, y)=h(x)+h(y)。两个不相关事件是统计独立的,因此p(x, y)=p(x)*p(y)。根据这两个关系,很容易看出h(x)一定与p(x) 的对数有关。因此,我们有:
其中负号使信息量取值为非负,一个事件发生的概率越大则信息量越小。该公式定义了概率分布和信息量函数成反比的关系。
现在假设一个发送者想传输一个随机变量的值给接收者。这个过程中,他们传输的平均信息量通可以通过计算概率分布p(x) 的期望得到。这个期望值为:
事实上H(x)就是随机变量x的熵,熵计算公式:
y1 = tf.constant([0.25, 0.25, 0.25, 0.25])
y2 = tf.constant([0.1, 0.1, 0.1, 0.7])
y3 = tf.constant([0.01, 0.01, 0.01, 0.97])
loss1 = -tf.reduce_sum(y1*tf.math.log(y1)) # 1.3862944
loss2 = -tf.reduce_sum(y2*tf.math.log(y2)) # 0.94044805
loss3 = -tf.reduce_sum(y3 * tf.math.log(y3)) # 0.1677005
1.3 条件熵
条件熵H(Y∣X)定义为X给定条件下Y的条件概率分布的熵对X的数学期望:
条件概率P(y|x)为已知x的情况下y的概率,p(y|x)=p(x, y)-p(x)。
1.4 交叉熵(entropy)
交叉熵是度量两个数学分布的相似度。
交叉熵(针对onehot编码)计算公式:
y = tf.constant([0., 1., 0., 0.])
p = tf.constant([1., 10.2, 3.2, 0.5])
loss5 = losses.categorical_crossentropy(y, p, from_logits=True) # 0.0010736184
1.5 散度(divergency)
散度度量了两个分布的匹配程度,经常作为优化的目标。
1.5.1 KL散度
KL 散度,又称相对熵,是一种衡量两个概率分布的匹配程度的指标,两个分布差异越大,KL散度越大。公式:
其中 p(x) 是目标分布,q(x)是去匹配的分布,如果两个分布完全匹配,则KL为0,若完全没有交集则无穷大。
2 典型损失函数调用
tensorfow2.0中的损失函数调用。
2.1 均方差损失函数MSE
-
回归问题中最常用的损失函数;
-
优点是便于梯度下降,误差大时下降快,误差小时下降慢,有利于函数收敛;
-
缺点是受明显偏离正常范围的离群样本的影响较大。
公式:
y_true = tf.random.uniform(shape=[5, 9])
y_pred = tf.random.uniform(shape=[5, 9])
Mse = losses.MeanSquaredError()
m = Mse(y_true, y_pred)
mse = losses.mean_squared_error(y_true, y_pred)
mse_mean = tf.reduce_mean(tf.square(y_true - y_pred))
print(mse) # tf.Tensor([0.21396121 0.16804615 0.18875246 0.13844354 0.13452384], shape=(5,), dtype=float32)
print(mse_mean) # tf.Tensor(0.16874544, shape=(), dtype=float32)
print(m) # tf.Tensor(0.16874544, shape=(), dtype=float32)
2.2 平均绝对误差MAE
-
计算标签和预测之间的绝对差的平均值,想格外增强对离群样本的健壮性时使用;
-
优点是其克服了 MSE 的缺点,受偏离正常范围的离群样本影响较小;
-
缺点是收敛速度比 MSE 慢,因为当误差大或小时其都保持同等速度下降,而且在某一点处还不可导,计算机求导比较困难。
公式:
y_true = tf.random.uniform(shape=[5, 9])
y_pred = tf.random.uniform(shape=[5, 9])
Mae = losses.MeanAbsoluteError()
m = Mae(y_true, y_pred)
mae = losses.mean_absolute_error(y_true, y_pred)
mae_mean = tf.reduce_mean(tf.square(y_true - y_pred))
print(mae) # tf.Tensor([0.35941643 0.3585953 0.34886912 0.24132997 0.3855058 ], shape=(5,), dtype=float32)
print(mae_mean) # tf.Tensor(0.17012018, shape=(), dtype=float32)
print(m) # tf.Tensor(0.17012018, shape=(), dtype=float32)
2.3 BinaryCrossentropy()
二分类问题
y_true = tf.constant([[0.], [0.], [1.], [0.]]) # (2, 1)
y_pred = tf.constant([[0.01], [0.02], [0.8], [0.1]]) # (2, 1)
losser = losses.BinaryCrossentropy(from_logits=False)
loss1 = losser(y_true, y_pred)
loss2 = losses.binary_crossentropy(y_true, y_pred)
print(loss1) # tf.Tensor(0.08968914, shape=(), dtype=float32)
print(loss2) # tf.Tensor([0.01005021 0.02020257 0.22314338 0.10536041], shape=(4,), dtype=float32)
2.4 SparseCategoricalCrossentropy()
from_logits:True就是需要经过softmax进行概率化,默认False,即y_pred是经过softmax处理的 reduction:False:返回一个list, True:默认返回一个平均值
比起CategoricalCrossentropy(),其实就多个自动one-hot的功能。
y_true = tf.constant([0., 0., 1., 0.])
y_pred = tf.constant([[0.1, 0.2, 0.7], [0.2, 0.3, 0.5], [0.5, 0.6, 0.1], [0.2, 0.2, 0.6]])
Scc = losses.SparseCategoricalCrossentropy()
m = Scc(y_true, y_pred)
scc = losses.sparse_categorical_crossentropy(y_true, y_pred)
scc_mean = tf.reduce_mean(scc)
print(scc) # tf.Tensor([2.3025851 1.609438 0.6931471 1.609438 ], shape=(4,), dtype=float32)
print(scc_mean) # tf.Tensor(1.553652, shape=(), dtype=float32)
print(m) # tf.Tensor(1.553652, shape=(), dtype=float32)
2.5 CategoricalCrossentropy()
from_logits:True就是需要经过softmax进行概率化,默认False,即y_pred是经过softmax处理的 reduction:False:返回一个list, True:默认返回一个平均值。
y_true = tf.constant([[0., 0., 1.], [0., 1., 0.], [0., 0., 1.], [1., 0., 0.]])
y_pred = tf.constant([[0.1, 0.2, 0.7], [0.2, 0.3, 0.5], [0.5, 0.6, 0.1], [0.2, 0.2, 0.6]])
Cc = losses.CategoricalCrossentropy()
m = Cc(y_true, y_pred)
cc = losses.categorical_crossentropy(y_true, y_pred)
cc_mean = tf.reduce_mean(cc)
print(cc) # tf.Tensor([2.3025851 1.609438 0.6931471 1.609438 ], shape=(4,), dtype=float32)
print(cc_mean) # tf.Tensor(1.553652, shape=(), dtype=float32)
print(m) # tf.Tensor(1.553652, shape=(), dtype=float32)
2.6 KLDivergence()
y_true = tf.constant([[0., 0., 1.], [0., 1., 0.], [0., 0., 1.], [1., 0., 0.]])
y_pred = tf.constant([[0.1, 0.2, 0.7], [0.2, 0.3, 0.5], [0.5, 0.6, 0.1], [0.2, 0.2, 0.6]])
KL = losses.KLDivergence()
kl = losses.kl_divergence(y_true, y_pred)
m = KL(y_true, y_pred)
print(m) # tf.Tensor(1.3681648, shape=(), dtype=float32)
print(kl) # tf.Tensor([0.35667214 1.2039698 2.302582 1.609435 ], shape=(4,), dtype=float32)
3 自定义损失函数
3.1 计算分布的相似度
class SequenceKL(losses.Loss):
def __init__(self, from_logits=False):
super(SequenceKL, self).__init__()
self.from_logits = from_logits
def call(self, data, fake):
"""
计算分布的相似度
:param data: 真实分布 is a tuple shape: labels * (b, features)
:param fake: 生成的虚假分布is a tensor shape: (labels, features, b)
:return:
"""
kl_lossor = losses.KLDivergence()
loss = [] # 存储labels个分类的损失值
labels = fake.shape[0]
features = fake.shape[1]
for i in range(labels):
dis = tf.transpose(data[i])
kl_d = [] # 存储features个分布的相似度
for j in range(features):
kl_d.append(kl_lossor(dis[j], fake[i][j]))
loss.append(tf.reduce_mean(kl_d))
return tf.reduce_sum(loss)