【问题标题】:Setting up a MLP for binary classification with tensorflow使用 tensorflow 为二进制分类设置 MLP
【发布时间】:2017-02-10 14:12:28
【问题描述】:

我在尝试使用 tensorflow 设置用于二进制分类的多层感知器时遇到了一些麻烦。

我有一个非常大的数据集(大约 1,5*10^6 个示例),每个数据集都有一个二进制 (0/1) 标签和 100 个特征。我需要做的是设置一个简单的 MLP,然后尝试更改学习率和初始化模式以记录结果(这是一项作业)。 不过,我得到了奇怪的结果,因为我的 MLP 似乎很早就陷入了低但不是高成本的困境,并且永远不会摆脱它。在学习率相当低的情况下,成本几乎立即变为 NAN。我不知道问题是否在于我如何构建 MLP(我做了几次尝试,准备发布最后一个的代码),或者我的 tensorflow 实现是否遗漏了一些东西。

代码

import tensorflow as tf
import numpy as np
import scipy.io

# Import and transform dataset
print("Importing dataset.")
dataset = scipy.io.mmread('tfidf_tsvd.mtx')

with open('labels.txt') as f:
    all_labels = f.readlines()

all_labels = np.asarray(all_labels)
all_labels = all_labels.reshape((1498271,1))

# Split dataset into training (66%) and test (33%) set
training_set    = dataset[0:1000000]
training_labels = all_labels[0:1000000]
test_set        = dataset[1000000:1498272]
test_labels     = all_labels[1000000:1498272]

print("Dataset ready.") 

# Parameters
learning_rate   = 0.01 #argv
mini_batch_size = 100
training_epochs = 10000
display_step    = 500

# Network Parameters
n_hidden_1  = 64    # 1st hidden layer of neurons
n_hidden_2  = 32    # 2nd hidden layer of neurons
n_hidden_3  = 16    # 3rd hidden layer of neurons
n_input     = 100   # number of features after LSA

# Tensorflow Graph input
x = tf.placeholder(tf.float64, shape=[None, n_input], name="x-data")
y = tf.placeholder(tf.float64, shape=[None, 1], name="y-labels")

print("Creating model.")

# Create model
def multilayer_perceptron(x, weights):
    # First hidden layer with SIGMOID activation
    layer_1 = tf.matmul(x, weights['h1'])
    layer_1 = tf.nn.sigmoid(layer_1)
    # Second hidden layer with SIGMOID activation
    layer_2 = tf.matmul(layer_1, weights['h2'])
    layer_2 = tf.nn.sigmoid(layer_2)
    # Third hidden layer with SIGMOID activation
    layer_3 = tf.matmul(layer_2, weights['h3'])
    layer_3 = tf.nn.sigmoid(layer_3)
    # Output layer with SIGMOID activation
    out_layer = tf.matmul(layer_2, weights['out'])
    return out_layer

# Layer weights, should change them to see results
weights = {
    'h1': tf.Variable(tf.random_normal([n_input, n_hidden_1], dtype=np.float64)),       
    'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2], dtype=np.float64)),
    'h3': tf.Variable(tf.random_normal([n_hidden_2, n_hidden_3],dtype=np.float64)),
    'out': tf.Variable(tf.random_normal([n_hidden_2, 1], dtype=np.float64))
}

# Construct model
pred = multilayer_perceptron(x, weights)

# Define loss and optimizer
cost = tf.nn.l2_loss(pred-y,name="squared_error_cost")
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

# Initializing the variables
init = tf.initialize_all_variables()

print("Model ready.")

# Launch the graph
with tf.Session() as sess:
    sess.run(init)

    print("Starting Training.")

    # Training cycle
    for epoch in range(training_epochs):
        #avg_cost = 0.
        # minibatch loading
        minibatch_x = training_set[mini_batch_size*epoch:mini_batch_size*(epoch+1)]
        minibatch_y = training_labels[mini_batch_size*epoch:mini_batch_size*(epoch+1)]
        # Run optimization op (backprop) and cost op
        _, c = sess.run([optimizer, cost], feed_dict={x: minibatch_x, y: minibatch_y})

        # Compute average loss
        avg_cost = c / (minibatch_x.shape[0])

        # Display logs per epoch
        if (epoch) % display_step == 0:
        print("Epoch:", '%05d' % (epoch), "Training error=", "{:.9f}".format(avg_cost))

    print("Optimization Finished!")

    # Test model
    # Calculate accuracy
    test_error = tf.nn.l2_loss(pred-y,name="squared_error_test_cost")/test_set.shape[0]
    print("Test Error:", test_error.eval({x: test_set, y: test_labels}))

输出

python nn.py
Importing dataset.
Dataset ready.
Creating model.
Model ready.
Starting Training.
Epoch: 00000 Training error= 0.331874878
Epoch: 00500 Training error= 0.121587482
Epoch: 01000 Training error= 0.112870921
Epoch: 01500 Training error= 0.110293652
Epoch: 02000 Training error= 0.122655269
Epoch: 02500 Training error= 0.124971940
Epoch: 03000 Training error= 0.125407845
Epoch: 03500 Training error= 0.131942481
Epoch: 04000 Training error= 0.121696954
Epoch: 04500 Training error= 0.116669835
Epoch: 05000 Training error= 0.129558477
Epoch: 05500 Training error= 0.122952110
Epoch: 06000 Training error= 0.124655344
Epoch: 06500 Training error= 0.119827300
Epoch: 07000 Training error= 0.125183779
Epoch: 07500 Training error= 0.156429254
Epoch: 08000 Training error= 0.085632880
Epoch: 08500 Training error= 0.133913128
Epoch: 09000 Training error= 0.114762624
Epoch: 09500 Training error= 0.115107805
Optimization Finished!
Test Error: 0.116647016708

这是 MMN 的建议

weights = {
    'h1': tf.Variable(tf.random_normal([n_input, n_hidden_1], stddev=0, dtype=np.float64)),     
    'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2], stddev=0.01, dtype=np.float64)),
    'h3': tf.Variable(tf.random_normal([n_hidden_2, n_hidden_3],  stddev=0.01, dtype=np.float64)),
    'out': tf.Variable(tf.random_normal([n_hidden_2, 1], dtype=np.float64))
}

这是输出

Epoch: 00000 Training error= 0.107566668
Epoch: 00500 Training error= 0.289380907
Epoch: 01000 Training error= 0.339091784
Epoch: 01500 Training error= 0.358559815
Epoch: 02000 Training error= 0.122639698
Epoch: 02500 Training error= 0.125160135
Epoch: 03000 Training error= 0.126219718
Epoch: 03500 Training error= 0.132500418
Epoch: 04000 Training error= 0.121795254
Epoch: 04500 Training error= 0.116499476
Epoch: 05000 Training error= 0.124532673
Epoch: 05500 Training error= 0.124484790
Epoch: 06000 Training error= 0.118491177
Epoch: 06500 Training error= 0.119977633
Epoch: 07000 Training error= 0.127532511
Epoch: 07500 Training error= 0.159053519
Epoch: 08000 Training error= 0.083876224
Epoch: 08500 Training error= 0.131488483
Epoch: 09000 Training error= 0.123161189
Epoch: 09500 Training error= 0.125011362
Optimization Finished!
Test Error: 0.129284643093

连接第三个隐藏层,感谢 MMN

我的代码中有一个错误,我有两个隐藏层而不是三个。我纠正了这样做:

'out': tf.Variable(tf.random_normal([n_hidden_3, 1], dtype=np.float64))

out_layer = tf.matmul(layer_3, weights['out'])

不过,我恢复了 stddev 的旧值,因为它似乎导致成本函数的波动较小。

输出还是很麻烦

Epoch: 00000 Training error= 0.477673073
Epoch: 00500 Training error= 0.121848744
Epoch: 01000 Training error= 0.112854530
Epoch: 01500 Training error= 0.110597624
Epoch: 02000 Training error= 0.122603499
Epoch: 02500 Training error= 0.125051472
Epoch: 03000 Training error= 0.125400717
Epoch: 03500 Training error= 0.131999354
Epoch: 04000 Training error= 0.121850889
Epoch: 04500 Training error= 0.116551533
Epoch: 05000 Training error= 0.129749704
Epoch: 05500 Training error= 0.124600464
Epoch: 06000 Training error= 0.121600218
Epoch: 06500 Training error= 0.121249676
Epoch: 07000 Training error= 0.132656938
Epoch: 07500 Training error= 0.161801757
Epoch: 08000 Training error= 0.084197352
Epoch: 08500 Training error= 0.132197409
Epoch: 09000 Training error= 0.123249055
Epoch: 09500 Training error= 0.126602369
Optimization Finished!
Test Error: 0.129230736355

感谢 Steven 带来的另外两个变化 于是 Steven 提出用 ReLu 改变 Sigmoid 激活函数,于是我就尝试了。同时,我注意到我没有为输出节点设置激活函数,所以我也这样做了(应该很容易看到我改变了什么)。

Starting Training.
Epoch: 00000 Training error= 293.245977809
Epoch: 00500 Training error= 0.290000000
Epoch: 01000 Training error= 0.340000000
Epoch: 01500 Training error= 0.360000000
Epoch: 02000 Training error= 0.285000000
Epoch: 02500 Training error= 0.250000000
Epoch: 03000 Training error= 0.245000000
Epoch: 03500 Training error= 0.260000000
Epoch: 04000 Training error= 0.290000000
Epoch: 04500 Training error= 0.315000000
Epoch: 05000 Training error= 0.285000000
Epoch: 05500 Training error= 0.265000000
Epoch: 06000 Training error= 0.340000000
Epoch: 06500 Training error= 0.180000000
Epoch: 07000 Training error= 0.370000000
Epoch: 07500 Training error= 0.175000000
Epoch: 08000 Training error= 0.105000000
Epoch: 08500 Training error= 0.295000000
Epoch: 09000 Training error= 0.280000000
Epoch: 09500 Training error= 0.285000000
Optimization Finished!
Test Error: 0.220196439287

这就是它对每个节点的 Sigmoid 激活函数所做的,包括输出

Epoch: 00000 Training error= 0.110878121
Epoch: 00500 Training error= 0.119393080
Epoch: 01000 Training error= 0.109229532
Epoch: 01500 Training error= 0.100436962
Epoch: 02000 Training error= 0.113160662
Epoch: 02500 Training error= 0.114200962
Epoch: 03000 Training error= 0.109777990
Epoch: 03500 Training error= 0.108218725
Epoch: 04000 Training error= 0.103001394
Epoch: 04500 Training error= 0.084145737
Epoch: 05000 Training error= 0.119173495
Epoch: 05500 Training error= 0.095796251
Epoch: 06000 Training error= 0.093336573
Epoch: 06500 Training error= 0.085062860
Epoch: 07000 Training error= 0.104251661
Epoch: 07500 Training error= 0.105910949
Epoch: 08000 Training error= 0.090347288
Epoch: 08500 Training error= 0.124480612
Epoch: 09000 Training error= 0.109250224
Epoch: 09500 Training error= 0.100245836
Optimization Finished!
Test Error: 0.110234139674

我发现这些数字很奇怪,在第一种情况下,它的成本比 sigmoid 高,尽管 sigmoid 应该很早就饱和了。在第二种情况下,它从一个几乎是最后一个的训练错误开始......所以它基本上与一个小批量收敛。我开始认为我没有正确计算成本,在这一行: avg_cost = c / (minibatch_x.shape[0])

【问题讨论】:

  • 您是否尝试将您的线路 cost = tf.nn.l2_loss(pred-y,name="squared_error_cost") 更改为 cost = tf.nn.square(tf.sub(pred, y))
  • 能否在训练过程中打印准确率(正确分类样本的百分比)?
  • @Kashyap:我在打印成本时收到“传递给 object.__format__ 的非空格式字符串”错误,我似乎无法解决它。
  • @DanevskyiDmytro:我会努力的。

标签: machine-learning tensorflow deep-learning text-classification


【解决方案1】:

除了上述答案,我建议您尝试使用成本函数 tf.nn.sigmoid_cross_entropy_with_logits(logits, targets, name=None)

作为二元分类,你必须尝试sigmoid_cross_entropy_with_logits代价函数

我还建议您还必须绘制训练和测试准确率与 epoch 数的折线图。即检查模型是否过拟合?

如果它没有过度拟合,试着让你的神经网络更复杂。这是通过越来越多的神经元,越来越多的层数。你会得到这样一个点,你的训练准确度会不断提高,但验证不会给出最好的模型。

【讨论】:

  • 嗨,普拉莫德,感谢您的回复。我正在阅读您提到的这个成本函数,但描述说它最适合标签不互斥的地方 - 但在我的模型中它们是。我现在正在 TensorBoard 的帮助下调整我的网络,我一定会尝试让我的网络更复杂。
  • 根据问题“我有一个非常大的数据集(大约 1,5*10^6 个示例),每个数据集都有一个二进制(0/1)标签”。它是二进制类分类,每个实例为真(1)或假(0)。你说的互斥是什么意思?我无法得到它。
  • 我想你在谈论这个“测量离散分类任务中的概率误差,其中每个类是独立的并且不相互排斥。”我认为根据你的描述,你的标签不是相互排斥的并且独立的。看看这个:stats.stackexchange.com/questions/107768/…
  • 很抱歉,我的描述误导您认为我的标签不是互斥的,但它们确实是互斥的。为了给出上下文,我有大量的推文数据集,每条推文都标有情绪,可以是“正面”(1)或“负面”(0)。
【解决方案2】:

所以这可能是几件事:

  1. 您可能会使 sigmoid 单元饱和(如 MMN 所述)我建议尝试使用 relu 单元。

替换:

tf.nn.sigmoid(layer_n)

与:

tf.nn.relu(layer_n)
  1. 您的模型可能没有实际学习数据的表达能力。 IE。它需要更深。
  2. 您也可以尝试不同的优化器,例如 Adam()

替换:

optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

与:

optimizer = tf.train.AdamOptimizer().minimize(cost)

其他几点:

  1. 您应该在权重中添加偏差项

像这样:

biases = {
 'b1': tf.Variable(tf.random_normal([n_hidden_1],   dtype=np.float64)),       
 'b2': tf.Variable(tf.random_normal([n_hidden_2], dtype=np.float64)),
 'b3': tf.Variable(tf.random_normal([n_hidden_3],dtype=np.float64)),
 'bout': tf.Variable(tf.random_normal([1], dtype=np.float64))
 }

def multilayer_perceptron(x, weights):
    # First hidden layer with SIGMOID activation
    layer_1 = tf.matmul(x, weights['h1']) + biases['b1']
    layer_1 = tf.nn.sigmoid(layer_1)
    # Second hidden layer with SIGMOID activation
    layer_2 = tf.matmul(layer_1, weights['h2']) + biases['b2']
    layer_2 = tf.nn.sigmoid(layer_2)
    # Third hidden layer with SIGMOID activation
    layer_3 = tf.matmul(layer_2, weights['h3']) + biases['b3']
    layer_3 = tf.nn.sigmoid(layer_3)
    # Output layer with SIGMOID activation
    out_layer = tf.matmul(layer_2, weights['out']) + biases['bout']
    return out_layer
  1. 您可以随时间更新学习率

像这样:

    learning_rate = tf.train.exponential_decay(INITIAL_LEARNING_RATE,
                                           global_step,
                                           decay_steps,
                                           LEARNING_RATE_DECAY_FACTOR,
                                           staircase=True)

您只需要定义衰减步骤,即何时衰减和 LEARNING_RATE_DECAY_FACTOR 即衰减多少。

【讨论】:

  • 我已根据您的建议编辑了答案。注意: 1. relu 给出了非常奇怪的值,你可以在问题的编辑中阅读它。 2.我把模型做得更深了,因为我的一个错误,它之前有2个隐藏层,现在它有3个隐藏层。 3. 我真的不能使用 Adam 优化器,因为它会违背我的任务目的,即使用学习率和一些初始化参数。你认为我在每个 mini_batch 之后计算的成本正确吗?
  • 有不同的成本函数,所以它真的取决于你的任务。我无法真正回答这个问题,因为我不知道 l2 损失是否正确或者交叉熵或其他什么的任务。不过,您正在正确使用 l2 损失。
  • 另一件“显而易见”但有时会被忽视的简单事情确保您的标签对应于正确的训练输入。
  • 是的,确实如此。我查了,谢谢。不过,您的回答对我帮助很大,所以我想我会将其标记为正确的。使用激活函数原来是解决方案。我现在正在玩随机梯度下降、批量梯度下降、偏差和其他东西。谢谢!我还要感谢 MMN,因为他也帮助了我很多。
【解决方案3】:

您的权重在初始化时的标准差为 1,因此第 1 层的输出标准差为 10 左右。这可能会使 sigmoid 函数饱和到大多数梯度为 0 的程度。

您可以尝试使用 .01 的 stddev 初始化隐藏权重吗?

【讨论】:

  • 看起来像这个00000 tr = 0.107566 00500 tr err = 0.339091 01500 tr err = 0.358559 02000 tr err = 0.122639 02500 tr错误= 0.125160 03000 tr err = 0.126219 03500 tr错误= 0.132500 04000 TR ERR = 0.121795 04500 TR ERR = 0.116499 05000 TR ERR = 0.124532 05500 TR ERR = 0.124484 06000 TR ERR = 0.118491 06500 TR ERR = 0.127000 TR ERR = 0.127532 07500 TR ERR = 0.159053 08000 TR ERR = 0.0830 TR = 0.080000 TR ERR = 0.127532 0750 TR ERR = 0.083876 08500 0.131488 09000 误差 = 0.123161 09500 误差 = 0.125011 误差:0.129284643
  • 嗯,无法为 cmets 赋予正确的形状,但我可以告诉你,这并没有解决我的问题。
  • hmm,也许这是您将获得的最好的两层网络?你的意思是不使用 h3 吗?
  • 哎哟。我还没有连接layer_3。我现在就试试。
  • 还是不行,我已经用新结果编辑了问题。
猜你喜欢
  • 1970-01-01
  • 2021-04-10
  • 2017-07-25
  • 1970-01-01
  • 2016-05-18
  • 2017-05-30
  • 2018-10-22
  • 2018-08-25
  • 2017-11-07
相关资源
最近更新 更多