我目前正在尝试在Keras中实现一个孪生网络,需要实现以下损失函数:
loss(p ∥ q) = Is · KL(p ∥ q) + Ids · HL(p ∥ q)
其中KL是Kullback-Leibler散度,HL是铰链损失(Hinge-loss)。
在训练过程中,我将同一个说话人的配对标记为1,不同说话人的配对标记为0。
目标是使用训练好的网络从频谱图中提取嵌入。频谱图是一个40×128(时间 x 频率)的二维numpy数组。
问题是我从未超过0.5的准确率,并且在聚类说话人嵌入时,结果显示嵌入和说话人之间似乎没有相关性。
我实现了KB-散度作为距离度量,并相应地调整了铰链损失:
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], 1def kb_hinge_loss(y_true, y_pred): """ y_true: 二进制标签,1 = 相同说话人 y_pred: 孪生网络的输出,即Kullback-Leibler分布 """ 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中。基础网络的架构如下:
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仓库
我正在尝试找出是否有任何错误的假设以及如何提高我的准确性。
回答: