基于Keras(TF后端)的说话人识别:sampled_softmax_loss参数及用法问询
在Keras中结合tf.nn.sampled_softmax_loss加速说话人识别训练
我来帮你梳理清楚怎么在你的Keras模型里用上tf.nn.sampled_softmax_loss,完美适配你现有的CNN架构,解决类别数量庞大带来的训练速度问题。
核心前提先明确
首先要纠正两个关键认知,不然很容易踩坑:
- 标签不用做One-Hot编码:
tf.nn.sampled_softmax_loss需要的是整数形式的类别索引(比如0、1、2...对应不同说话人),而不是One-Hot向量,所以你原来的to_categorical步骤要去掉,直接用原始的y_train、y_valid、y_test即可。 - 模型最后输出是特征向量,不是概率/Logits:Sampled Softmax的核心优势就是避免计算所有类别的Logits,所以我们用CNN最后一个隐藏层的256维特征来计算损失,不用提前算所有类别的映射。
- 必须用
tf.reduce_mean:tf.nn.sampled_softmax_loss会返回每个样本的损失(形状是[batch_size]),而Keras需要一个标量作为整个Batch的损失,所以一定要用tf.reduce_mean把样本损失平均成标量,和你原来用的categorical_crossentropy行为保持一致。
具体代码修改步骤
1. 调整标签处理
删掉原来的One-Hot编码代码:
# 移除这三行 # y_train_hot = to_categorical(y_train) # y_valid_hot = to_categorical(y_valid) # y_test_hot = to_categorical(y_test) # 直接用原始的整数标签y_train、y_valid、y_test
2. 改用Keras函数式API构建模型
因为Sequential模型不好单独定义最后一层的权重变量,我们换成函数式API,保留你原来的CNN结构,只是最后输出特征向量:
from keras.models import Model from keras.layers import Input # 输入层和原来一致 inputs = Input(shape=(dim_1, dim_2, dim_3)) x = Conv2D(128, kernel_size=(6, 6), strides=2, activation='relu')(inputs) x = Conv2D(64, kernel_size=(2, 2), strides=1, activation='relu')(x) x = Conv2D(32, kernel_size=(2, 2), activation='relu')(x) x = MaxPooling2D(pool_size=(2, 2))(x) x = Dropout(0.3)(x) x = Flatten()(x) x = Dense(256, activation='relu', use_bias=True, kernel_regularizer=regularizers.l2(0.01))(x) x = Dropout(0.2)(x) x = Dense(256, activation='relu', use_bias=True, kernel_regularizer=regularizers.l2(0.02))(x) x = Dropout(0.3)(x) # 最后一个隐藏层,输出256维特征 features = Dense(256, activation='relu', use_bias=True, kernel_regularizer=regularizers.l2(0.02))(x) x = Dropout(0.3)(features) # 单独定义最后一层的权重和偏置(从256维特征到所有说话人类别) final_weights = tf.Variable( tf.random.truncated_normal([256, num_classes], stddev=0.01), name='final_dense_weights' ) final_biases = tf.Variable( tf.zeros([num_classes]), name='final_dense_biases' ) # 模型输出是256维特征向量 model = Model(inputs=inputs, outputs=features)
3. 自定义损失函数和准确率指标
现在写一个适配Keras的损失函数,内部调用tf.nn.sampled_softmax_loss,同时自定义准确率指标(因为模型输出是特征,没法用默认的accuracy):
def sampled_softmax_loss(y_true, y_pred): # y_true: 输入的整数标签,形状[batch_size] # y_pred: 模型输出的256维特征,形状[batch_size, 256] # 把标签转换成[batch_size, 1]的形状,符合sampled_softmax_loss的要求 labels = tf.expand_dims(tf.cast(y_true, tf.int64), axis=1) # 调用tf.nn.sampled_softmax_loss,参数对应你的场景 per_sample_loss = tf.nn.sampled_softmax_loss( weights=tf.transpose(final_weights), # 转置成[num_classes, 256],符合函数要求 biases=final_biases, labels=labels, inputs=y_pred, num_sampled=100, # 每次采样的类别数,可根据你的类别总数调整(比如类别数的1/10) num_classes=num_classes, num_true=1, # 每个样本对应1个真实类别 partition_strategy="div" # 默认采样策略即可 ) # 对Batch内的损失取平均,返回标量损失 return tf.reduce_mean(per_sample_loss) # 自定义准确率:计算所有类别的Logits,再和真实标签比较 def sampled_accuracy(y_true, y_pred): logits = tf.matmul(y_pred, final_weights) + final_biases predictions = tf.argmax(logits, axis=1, output_type=tf.int64) y_true_int = tf.cast(y_true, tf.int64) return tf.reduce_mean(tf.cast(tf.equal(predictions, y_true_int), tf.float32))
4. 编译并训练模型
现在用自定义的损失和指标编译模型,训练时直接用原始整数标签:
model.compile( loss=sampled_softmax_loss, optimizer='adamax', metrics=[sampled_accuracy] ) earlystopping = EarlyStopping(monitor='val_loss', min_delta=0.001, patience=15, verbose=verbose, mode='auto') model.fit( X_train, y_train, batch_size=batch_size, callbacks=[earlystopping], epochs=epochs, verbose=verbose, validation_data=(X_valid, y_valid) )
参数设置说明
给你拆解一下tf.nn.sampled_softmax_loss里每个参数的作用,方便你根据自己的数据集调整:
weights:从256维特征到所有类别的权重矩阵,转置后形状为[num_classes, 256],对应我们定义的final_weights的转置。biases:每个类别的偏置项,形状[num_classes],对应final_biases。labels:真实类别的整数索引,必须是[batch_size, num_true]的形状,这里我们把y_true扩展了一维。inputs:模型输出的256维特征向量,形状[batch_size, 256]。num_sampled:每次训练时随机采样的类别数量,建议设置为类别总数的1/10左右(比如你有1000个说话人,就设100),太小会导致损失估计偏差大,太大则失去加速效果。num_classes:你的说话人总类别数,也就是len(labels)。num_true:每个样本对应的真实类别数,这里每个样本只属于一个说话人,所以设为1。
内容的提问来源于stack exchange,提问作者Selim Alawwa
相关产品推荐
相关产品推荐

