gpt4 book ai didi

python - siamese-net 中的自定义组合铰链/kb-divergence 损失函数无法生成有意义的说话人嵌入

转载 作者:太空狗 更新时间:2023-10-29 17:50:06 27 4
gpt4 key购买 nike

我目前正在尝试在 Keras 中实现 siamese-net,我必须在其中实现以下损失函数:

loss(p ∥ q) = Is · KL(p ∥ q) + Ids · HL(p ∥ q)

detailed description of loss function from paper

其中 KL 是 Kullback-Leibler 散度,HL 是 Hinge-loss。

在训练过程中,我将相同说话人对标记为 1,将不同说话人标记为 0。

目标是使用经过训练的网络从频谱图中提取嵌入。频谱图是一个二维 numpy 数组 40x128(时间 x 频率)

问题是我从来没有超过 0.5 的准确度,当对说话人嵌入进行聚类时,结果显示嵌入和说话人之间似乎没有相关性

我将 kb-divergence 实现为距离度量,并相应地调整 hinge-loss:

def kullback_leibler_divergence(vects):
x, y = vects
x = ks.backend.clip(x, ks.backend.epsilon(), 1)
y = ks.backend.clip(y, ks.backend.epsilon(), 1)
return ks.backend.sum(x * ks.backend.log(x / y), axis=-1)


def kullback_leibler_shape(shapes):
shape1, shape2 = shapes
return shape1[0], 1


def kb_hinge_loss(y_true, y_pred):
"""
y_true: binary label, 1 = same speaker
y_pred: output of siamese net i.e. kullback-leibler distribution
"""
MARGIN = 1.
hinge = ks.backend.mean(ks.backend.maximum(MARGIN - y_pred, 0.), axis=-1)
return y_true * y_pred + (1 - y_true) * hinge

单个频谱图将被馈送到基础网络的一个分支中,连体网络由两个这样的分支组成,因此两个频谱图被同时馈送,并在距离层中加入。基础网络的输出为 1 x 128。距离层计算 kullback-leibler 散度并将其输出馈入 kb_hinge_loss。 base-network的架构如下:

    def create_lstm(units: int, gpu: bool, name: str, is_sequence: bool = True):
if gpu:
return ks.layers.CuDNNLSTM(units, return_sequences=is_sequence, input_shape=INPUT_DIMS, name=name)
else:
return ks.layers.LSTM(units, return_sequences=is_sequence, input_shape=INPUT_DIMS, name=name)


def build_model(mode: str = 'train') -> ks.Model:
topology = TRAIN_CONF['topology']

is_gpu = tf.test.is_gpu_available(cuda_only=True)

model = ks.Sequential(name='base_network')

model.add(
ks.layers.Bidirectional(create_lstm(topology['blstm1_units'], is_gpu, name='blstm_1'), input_shape=INPUT_DIMS))

model.add(ks.layers.Dropout(topology['dropout1']))

model.add(ks.layers.Bidirectional(create_lstm(topology['blstm2_units'], is_gpu, is_sequence=False, name='blstm_2')))

if mode == 'extraction':
return model

num_units = topology['dense1_units']
model.add(ks.layers.Dense(num_units, name='dense_1'))
model.add(ks.layers.advanced_activations.PReLU(init='zero', weights=None))

model.add(ks.layers.Dropout(topology['dropout2']))

num_units = topology['dense2_units']
model.add(ks.layers.Dense(num_units, name='dense_2'))
model.add(ks.layers.advanced_activations.PReLU(init='zero', weights=None))

num_units = topology['dense3_units']
model.add(ks.layers.Dense(num_units, name='dense_3'))
model.add(ks.layers.advanced_activations.PReLU(init='zero', weights=None))

num_units = topology['dense4_units']
model.add(ks.layers.Dense(num_units, name='dense_4'))
model.add(ks.layers.advanced_activations.PReLU(init='zero', weights=None))
return model

然后我构建了一个连体网如下:

    base_network = build_model()

input_a = ks.Input(shape=INPUT_DIMS, name='input_a')
input_b = ks.Input(shape=INPUT_DIMS, name='input_b')

processed_a = base_network(input_a)
processed_b = base_network(input_b)

distance = ks.layers.Lambda(kullback_leibler_divergence,
output_shape=kullback_leibler_shape,
name='distance')([processed_a, processed_b])

model = ks.Model(inputs=[input_a, input_b], outputs=distance)
adam = build_optimizer()
model.compile(loss=kb_hinge_loss, optimizer=adam, metrics=['accuracy'])

最后,我构建了一个只有一个输入的具有相同架构的网络,并尝试提取嵌入,然后在它们之上构建均值,其中嵌入应该作为说话者的表示,以便在聚类期间使用:

utterance_embedding = np.mean(embedding_extractor.predict_on_batch(spectrogram), axis=0)

我们在 voxceleb 扬声器组上训练网络。

完整的代码可以在这里看到:GitHub repo

我想弄清楚我是否做出了任何错误的假设以及如何提高我的准确性。

最佳答案

准确性问题

请注意,在您的模型中:

  • y_true = 标签
  • y_pred = kullback-leibler 散度

这两个不能比较,看这个例子:

For correct results, when y_true == 1 (same speaker), Kullback-Leibler is y_pred == 0 (no divergence).

因此,完全可以预料到指标无法正常工作。

然后,您要么创建自定义指标,要么仅根据损失进行评估。
此自定义指标需要进行一些调整才能可行,如下所述。

损失的可能问题

剪裁

This might be a problem

首先,请注意您在 Kullback-Leibler 的值中使用了 clip。这可能很糟糕,因为剪辑丢失了剪辑区域中的渐变。由于您的激活是 PRelu,因此您的值小于 0 且大于 1。然后肯定会有零梯度情况,存在卡住模型的风险。

因此,您可能不想裁剪这些值。为了避免 PRelu 出现负值,您可以尝试使用 'softplus' 激活,这是一种没有负值的软 relu。您也可以“求和”一个 epsilon 以避免麻烦,但保留大于 1 的值没有问题:

#considering you used 'softplus' instead of 'PRelu' in speakers
def kullback_leibler_divergence(speakers):
x, y = speakers
x = x + ks.backend.epsilon()
y = y + ks.backend.epsilon()
return ks.backend.sum(x * ks.backend.log(x / y), axis=-1)

Kullback-Leibler 中的体重测量

This IS a problem

另请注意,Kullback-Leibler 不是对称函数,并且其最小值也不为零!!完美匹配为零,但糟糕的匹配可能具有更低的值,这对损失函数不利,因为它会驱使你发散。

See this picture showing KB's graph

您的论文指出您应该对两个损失求和:(p||q) 和 (q||p)。
这消除了 assimetry 和负值。

所以:

distance1 = ks.layers.Lambda(kullback_leibler_divergence,
name='distance1')([processed_a, processed_b])
distance2 = ks.layers.Lambda(kullback_leibler_divergence,
name='distance2')([processed_b, processed_a])
distance = ks.layers.Add(name='dist_add')([distance1,distance2])

非常低的边距和剪裁的铰链

This might be a problem

最后,看到铰链损失也将值限制在零以下!
由于 Kullback-Leibler 不限于 1,因此具有高发散性的样本可能不受此损失的控制。不确定这是否真的是一个问题,但您可能想要:

  • 增加边际
  • 在 Kullback-Leibler 中,使用 mean 而不是 sum
  • 在铰链中使用 softplus 而不是 max,以避免丢失梯度。

参见:

MARGIN = someValue
hinge = ks.backend.mean(ks.backend.softplus(MARGIN - y_pred), axis=-1)

现在我们可以考虑自定义精度

这不是很容易,因为我们对告诉我们“正确/不正确”的知识库没有明确的限制

您可以随机尝试一个,但您需要调整此 threshold 参数,直到找到代表现实的好东西。例如,您可以使用您的验证数据来找到带来最佳准确性的阈值。

def customMetric(y_true_targets, y_pred_KBL):
isMatch = ks.backend.less(y_pred_KBL, threshold)
isMatch = ks.backend.cast(isMatch, ks.backend.floatx())

isMatch = ks.backend.equal(y_true_targets, isMatch)
isMatch = ks.backend.cast(isMatch, ks.backend.floatx())

return ks.backend.mean(isMatch)

关于python - siamese-net 中的自定义组合铰链/kb-divergence 损失函数无法生成有意义的说话人嵌入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53581298/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com