为啥 Spark ML 感知器分类器的 F1 分数很高,而 TensorFlow 上的相同模型表现很差?

Posted

技术标签:

【中文标题】为啥 Spark ML 感知器分类器的 F1 分数很高,而 TensorFlow 上的相同模型表现很差?【英文标题】:Why Spark ML perceptron classifier has high F1-score while the same model on TensorFlow performs very badly?为什么 Spark ML 感知器分类器的 F1 分数很高,而 TensorFlow 上的相同模型表现很差? 【发布时间】:2018-11-30 14:03:44 【问题描述】:

我们的团队正在研究 NLP 问题。我们有一个带有一些标记句子的数据集,我们必须将它们分为 两类,0 或 1。

我们预处理数据并使用词嵌入,使每个句子都有 300 个特征,然后我们使用简单的神经网络来训练模型。

由于数据非常偏斜,我们使用 F1 分数来衡量模型分数,同时在训练集 (80%) 和测试集 (20%) 上进行计算。

火花

我们使用了 PySpark 的 MLlib 中的 multilayer perceptron classifier:

layers = [300, 600, 2]

trainer = MultilayerPerceptronClassifier(featuresCol='features', labelCol='target',
                                         predictionCol='prediction', maxIter=10, layers=layers,
                                         blockSize=128)
model = trainer.fit(train_df)
result = model.transform(test_df)

predictionAndLabels = result.select("prediction", "target").withColumnRenamed("target", "label")
evaluator = MulticlassClassificationEvaluator(metricName="f1")
f1_score = evaluator.evaluate(predictionAndLabels)

这样我们得到的 F1 分数介于 0.91 和 0.93 之间。

TensorFlow

然后我们选择切换(主要用于学习目的)到 TensorFlow,因此我们使用与 MLlib 相同的架构和公式实现了一个神经网络:

# Network Parameters
n_input = 300
n_hidden_1 = 600
n_classes = 2

# TensorFlow graph input
features = tf.placeholder(tf.float32, shape=(None, n_input), name='inputs')
labels = tf.placeholder(tf.float32, shape=(None, n_classes), name='labels')

# Initializes weights and biases
init_biases_and_weights()

# Layers definition
layer_1 = tf.add(tf.matmul(features, weights['h1']), biases['b1'])
layer_1 = tf.nn.sigmoid(layer_1)

out_layer = tf.matmul(layer_1, weights['out']) + biases['out']
out_layer = tf.nn.softmax(out_layer)

# Optimizer definition
learning_rate_ph = tf.placeholder(tf.float32, shape=(), name='learning_rate')
loss_function = tf.losses.log_loss(labels=labels, predictions=out_layer)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_ph).minimize(loss_function)

# Start TensorFlow session
init = tf.global_variables_initializer()
tf_session = tf.InteractiveSession()
tf_session.run(init)

# Train Neural Network
learning_rate = 0.01
iterations = 100
batch_size = 256

total_batch = int(len(y_train) / batch_size)
for epoch in range(iterations):
    avg_cost = 0.0
    for block in range(total_batch):
        batch_x = x_train[block * batch_size:min(block * batch_size + batch_size, len(x_train)), :]
        batch_y = y_train[block * batch_size:min(block * batch_size + batch_size, len(y_train)), :]
        _, c = tf_session.run([optimizer, loss_function], feed_dict=learning_rate_ph: learning_rate,
                                                                     features: batch_x,
                                                                     labels: batch_y)
        avg_cost += c
    avg_cost /= total_batch
    print("Iteration " + str(epoch + 1) + " Logistic-loss=" + str(avg_cost))

# Make predictions
predictions_train = tf_session.run(out_layer, feed_dict=features: x_train, labels: y_train)
predictions_test = tf_session.run(out_layer, feed_dict=features: x_test, labels: y_test)

# Compute F1-score
f1_score = f1_score_tf(y_test, predictions_test)

支持功能:

def initialize_weights_and_biases():
    global weights, biases
    epsilon_1 = sqrt(6) / sqrt(n_input + n_hidden_1)
    epsilon_2 = sqrt(6) / sqrt(n_classes + n_hidden_1)
    weights = 
        'h1': tf.Variable(tf.random_uniform([n_input, n_hidden_1],
                                        minval=0 - epsilon_1, maxval=epsilon_1, dtype=tf.float32)),
        'out': tf.Variable(tf.random_uniform([n_hidden_1, n_classes],
                                         minval=0 - epsilon_2, maxval=epsilon_2, dtype=tf.float32))
    
    biases = 
        'b1': tf.Variable(tf.constant(1, shape=[n_hidden_1], dtype=tf.float32)),
        'out': tf.Variable(tf.constant(1, shape=[n_classes], dtype=tf.float32))
    

def f1_score_tf(actual, predicted):
    actual = np.argmax(actual, 1)
    predicted = np.argmax(predicted, 1)

    tp = tf.count_nonzero(predicted * actual)
    fp = tf.count_nonzero(predicted * (actual - 1))
    fn = tf.count_nonzero((predicted - 1) * actual)
    precision = tp / (tp + fp)
    recall = tp / (tp + fn)

    f1 = 2 * precision * recall / (precision + recall)
    return tf.Tensor.eval(f1)

这样我们得到的 F1 分数介于 0.24 和 0.25 之间。

问题

我可以看到两个神经网络之间的唯一区别是:

优化器: Spark 中的 L-BFGS,TensorFlow 中的梯度下降 权重和偏差初始化:Spark 自己初始化,而我们在 TensorFlow 中手动初始化它们

我不认为这两个参数会导致模型之间的性能差异如此之大,但 Spark 似乎仍然在很少的迭代中获得了非常高的分数。

我不明白是 TensorFlow 的表现很差,还是 Spark 的分数不真实。在这两种情况下,我认为我们都没有看到重要的东西。

【问题讨论】:

请出示您的init_biases_and_weights()功能;另外,为什么你用MulticlassClassificationEvaluator 代替binary one 来解决二元分类问题? 您好,我添加了支持功能的代码。关于MulticlassClassificationEvaluator,我们使用它是因为它实现了 f1 度量,而二进制则没有, 【参考方案1】:

将权重初始化为一致,将偏差初始化为 1 肯定不是一个好主意,而且很可能是造成这种差异的原因。

使用normaltruncated_normal 代替,默认零均值和small variance 作为权重:

weights = 
        'h1': tf.Variable(tf.truncated_normal([n_input, n_hidden_1],
                                        stddev=0.01, dtype=tf.float32)),
        'out': tf.Variable(tf.truncated_normal([n_hidden_1, n_classes],
                                         stddev=0.01, dtype=tf.float32))
    

用于偏差:

biases = 
        'b1': tf.Variable(tf.constant(0, shape=[n_hidden_1], dtype=tf.float32)),
        'out': tf.Variable(tf.constant(0, shape=[n_classes], dtype=tf.float32))
    

也就是说,我不确定将MulticlassClassificationEvaluator 用于二进制分类问题的正确性,我建议做一些进一步的手动检查以确认该函数确实返回了您认为它返回的内容...

【讨论】:

非常感谢关于偏差和权重的建议。此外,您对 Evaluator 的评价是正确的,我们手动检查了 F1 分数,它非常低,甚至低于 TensorFlow 分数。

以上是关于为啥 Spark ML 感知器分类器的 F1 分数很高,而 TensorFlow 上的相同模型表现很差?的主要内容,如果未能解决你的问题,请参考以下文章

在有监督的多类分类中,为啥使用宏 F1 分数而不是平衡精度?

从 Spark ML LinearSVC 解释 rawPrediction

如何在 scikit-learn 的分类问题中为 F1 分数做 GridSearchCV?

不同 ML 分类器的不同性能,我能推断出啥?

评分卡系列:分类学习器的评估

keras基于多层感知器的softmax多分类