我在尝试使用TensorFlow进行二元分类的多层感知机设置时遇到了一些问题。
我有一个非常大的数据集(约1.5*10^6个样本),每个样本都有一个二元标签(0/1)和100个特征。我需要做的是设置一个简单的多层感知机,然后尝试改变学习率和初始化模式来记录结果(这是一个作业)。然而,我得到了一些奇怪的结果,我的多层感知机似乎在早期就卡在一个低但不理想的成本上,并且一直没有摆脱它。当学习率设置得相当低时,成本几乎立即变为NAN。我不知道问题出在我如何构建多层感知机(我尝试了几次,将最后一次的代码发布出来)还是我的TensorFlow实现中有什么遗漏的地方。
代码
import tensorflow as tfimport numpy as npimport scipy.io# 导入并转换数据集print("正在导入数据集。")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))# 将数据集分为训练集(66%)和测试集(33%)training_set = dataset[0:1000000]training_labels = all_labels[0:1000000]test_set = dataset[1000000:1498272]test_labels = all_labels[1000000:1498272]print("数据集准备就绪。") # 参数learning_rate = 0.01 #argvmini_batch_size = 100training_epochs = 10000display_step = 500# 网络参数n_hidden_1 = 64 # 第一隐藏层神经元数n_hidden_2 = 32 # 第二隐藏层神经元数n_hidden_3 = 16 # 第三隐藏层神经元数n_input = 100 # LSA后的特征数量# TensorFlow图形输入x = tf.placeholder(tf.float64, shape=[None, n_input], name="x-data")y = tf.placeholder(tf.float64, shape=[None, 1], name="y-labels")print("正在创建模型。")# 创建模型def multilayer_perceptron(x, weights): # 第一隐藏层使用SIGMOID激活 layer_1 = tf.matmul(x, weights['h1']) layer_1 = tf.nn.sigmoid(layer_1) # 第二隐藏层使用SIGMOID激活 layer_2 = tf.matmul(layer_1, weights['h2']) layer_2 = tf.nn.sigmoid(layer_2) # 第三隐藏层使用SIGMOID激活 layer_3 = tf.matmul(layer_2, weights['h3']) layer_3 = tf.nn.sigmoid(layer_3) # 输出层使用SIGMOID激活 out_layer = tf.matmul(layer_2, weights['out']) return out_layer# 层权重,应该更改它们以查看结果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))}# 构建模型pred = multilayer_perceptron(x, weights)# 定义损失和优化器cost = tf.nn.l2_loss(pred-y,name="squared_error_cost")optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)# 初始化变量init = tf.initialize_all_variables()print("模型准备就绪。")# 启动图形with tf.Session() as sess: sess.run(init) print("开始训练。") # 训练周期 for epoch in range(training_epochs): #avg_cost = 0. # 加载小批量数据 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)] # 运行优化操作(反向传播)和成本操作 _, c = sess.run([optimizer, cost], feed_dict={x: minibatch_x, y: minibatch_y}) # 计算平均损失 avg_cost = c / (minibatch_x.shape[0]) # 每轮显示日志 if (epoch) % display_step == 0: print("轮次:", '%05d' % (epoch), "训练误差=", "{:.9f}".format(avg_cost)) print("优化完成!") # 测试模型 # 计算准确率 test_error = tf.nn.l2_loss(pred-y,name="squared_error_test_cost")/test_set.shape[0] print("测试误差:", test_error.eval({x: test_set, y: test_labels}))
输出
python nn.py正在导入数据集。数据集准备就绪。正在创建模型。模型准备就绪。开始训练。轮次: 00000 训练误差= 0.331874878轮次: 00500 训练误差= 0.121587482轮次: 01000 训练误差= 0.112870921轮次: 01500 训练误差= 0.110293652轮次: 02000 训练误差= 0.122655269轮次: 02500 训练误差= 0.124971940轮次: 03000 训练误差= 0.125407845轮次: 03500 训练误差= 0.131942481轮次: 04000 训练误差= 0.121696954轮次: 04500 训练误差= 0.116669835轮次: 05000 训练误差= 0.129558477轮次: 05500 训练误差= 0.122952110轮次: 06000 训练误差= 0.124655344轮次: 06500 训练误差= 0.119827300轮次: 07000 训练误差= 0.125183779轮次: 07500 训练误差= 0.156429254轮次: 08000 训练误差= 0.085632880轮次: 08500 训练误差= 0.133913128轮次: 09000 训练误差= 0.114762624轮次: 09500 训练误差= 0.115107805优化完成!测试误差: 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))}
这是输出
轮次: 00000 训练误差= 0.107566668轮次: 00500 训练误差= 0.289380907轮次: 01000 训练误差= 0.339091784轮次: 01500 训练误差= 0.358559815轮次: 02000 训练误差= 0.122639698轮次: 02500 训练误差= 0.125160135轮次: 03000 训练误差= 0.126219718轮次: 03500 训练误差= 0.132500418轮次: 04000 训练误差= 0.121795254轮次: 04500 训练误差= 0.116499476轮次: 05000 训练误差= 0.124532673轮次: 05500 训练误差= 0.124484790轮次: 06000 训练误差= 0.118491177轮次: 06500 训练误差= 0.119977633轮次: 07000 训练误差= 0.127532511轮次: 07500 训练误差= 0.159053519轮次: 08000 训练误差= 0.083876224轮次: 08500 训练误差= 0.131488483轮次: 09000 训练误差= 0.123161189轮次: 09500 训练误差= 0.125011362优化完成!测试误差: 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的旧值,因为它似乎导致成本函数波动较少。
输出仍然令人担忧
轮次: 00000 训练误差= 0.477673073轮次: 00500 训练误差= 0.121848744轮次: 01000 训练误差= 0.112854530轮次: 01500 训练误差= 0.110597624轮次: 02000 训练误差= 0.122603499轮次: 02500 训练误差= 0.125051472轮次: 03000 训练误差= 0.125400717轮次: 03500 训练误差= 0.131999354轮次: 04000 训练误差= 0.121850889轮次: 04500 训练误差= 0.116551533轮次: 05000 训练误差= 0.129749704轮次: 05500 训练误差= 0.124600464轮次: 06000 训练误差= 0.121600218轮次: 06500 训练误差= 0.121249676轮次: 07000 训练误差= 0.132656938轮次: 07500 训练误差= 0.161801757轮次: 08000 训练误差= 0.084197352轮次: 08500 训练误差= 0.132197409轮次: 09000 训练误差= 0.123249055轮次: 09500 训练误差= 0.126602369优化完成!测试误差: 0.129230736355
Steven提出的两个更多更改Steven建议将Sigmoid激活函数改为ReLu,所以我尝试了。同时,我注意到我没有为输出节点设置激活函数,所以我也做了这个更改(应该很容易看出我做了什么更改)。
开始训练。轮次: 00000 训练误差= 293.245977809轮次: 00500 训练误差= 0.290000000轮次: 01000 训练误差= 0.340000000轮次: 01500 训练误差= 0.360000000轮次: 02000 训练误差= 0.285000000轮次: 02500 训练误差= 0.250000000轮次: 03000 训练误差= 0.245000000轮次: 03500 训练误差= 0.260000000轮次: 04000 训练误差= 0.290000000轮次: 04500 训练误差= 0.315000000轮次: 05000 训练误差= 0.285000000轮次: 05500 训练误差= 0.265000000轮次: 06000 训练误差= 0.340000000轮次: 06500 训练误差= 0.180000000轮次: 07000 训练误差= 0.370000000轮次: 07500 训练误差= 0.175000000轮次: 08000 训练误差= 0.105000000轮次: 08500 训练误差= 0.295000000轮次: 09000 训练误差= 0.280000000轮次: 09500 训练误差= 0.285000000优化完成!测试误差: 0.220196439287
这是对每个节点(包括输出)使用Sigmoid激活函数的结果
轮次: 00000 训练误差= 0.110878121轮次: 00500 训练误差= 0.119393080轮次: 01000 训练误差= 0.109229532轮次: 01500 训练误差= 0.100436962轮次: 02000 训练误差= 0.113160662轮次: 02500 训练误差= 0.114200962轮次: 03000 训练误差= 0.109777990轮次: 03500 训练误差= 0.108218725轮次: 04000 训练误差= 0.103001394轮次: 04500 训练误差= 0.084145737轮次: 05000 训练误差= 0.119173495轮次: 05500 训练误差= 0.095796251轮次: 06000 训练误差= 0.093336573轮次: 06500 训练误差= 0.085062860轮次: 07000 训练误差= 0.104251661轮次: 07500 训练误差= 0.105910949轮次: 08000 训练误差= 0.090347288轮次: 08500 训练误差= 0.124480612轮次: 09000 训练误差= 0.109250224轮次: 09500 训练误差= 0.100245836优化完成!测试误差: 0.110234139674
我发现这些数字非常奇怪,在第一种情况下,它卡在一个比Sigmoid更高的成本上,尽管Sigmoid应该很早就饱和了。在第二种情况下,它以几乎是最后一个的训练误差开始…所以它基本上用一个小批量就收敛了。我开始认为我没有正确计算成本,在这一行:avg_cost = c / (minibatch_x.shape[0])
回答:
所以可能是几个原因:
- 你可能饱和了Sigmoid单元(如MMN提到的),我建议尝试使用ReLU单元来代替。
替换:
tf.nn.sigmoid(layer_n)
为:
tf.nn.relu(layer_n)
- 你的模型可能没有足够的表达能力来实际学习你的数据。即它可能需要更深的层。
- 你也可以尝试不同的优化器,如Adam(),如下所示
替换:
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
为:
optimizer = tf.train.AdamOptimizer().minimize(cost)
其他几点:
- 你应该为你的权重添加一个偏置项
如下所示:
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): # 第一隐藏层使用SIGMOID激活 layer_1 = tf.matmul(x, weights['h1']) + biases['b1'] layer_1 = tf.nn.sigmoid(layer_1) # 第二隐藏层使用SIGMOID激活 layer_2 = tf.matmul(layer_1, weights['h2']) + biases['b2'] layer_2 = tf.nn.sigmoid(layer_2) # 第三隐藏层使用SIGMOID激活 layer_3 = tf.matmul(layer_2, weights['h3']) + biases['b3'] layer_3 = tf.nn.sigmoid(layer_3) # 输出层使用SIGMOID激活 out_layer = tf.matmul(layer_2, weights['out']) + biases['bout'] return out_layer
- 你可以随时间更新学习率
如下所示:
learning_rate = tf.train.exponential_decay(INITIAL_LEARNING_RATE, global_step, decay_steps, LEARNING_RATE_DECAY_FACTOR, staircase=True)
你只需要定义衰减步骤,即何时衰减,以及LEARNING_RATE_DECAY_FACTOR,即衰减多少。