继续上一课的神经网络的话题,这次我们来实践一个简单的BP神经网络分类任务。
数据集介绍
首先,来认识一下任务所需的数据集 - voice gender dataset。
这个数据集是用于通过声音的一些属性来鉴别声音的性别。数据集包含了3168条数据记录, 分别来自男性或女性的声音属性。数据集格式是csv格式。声音的属性包括平均频率,频率的表方差,频率的中位数等20项声音属性,皆为float类型数据。每条记录还标识了性别,male(男),female(女)。我们将其中2640作为训练集, 剩下528条作为测试数据。
该数据下载链接
https://download.csdn.net/download/deecheanW/12302204
网络结构设计
这个数据分类任务,如果使用逻辑回归的话,准确率大约可以达到91%左右。1
逻辑回归的代码可以参照下文中的例子稍作修改即可。
https://blog.csdn.net/deecheanW/article/details/82927922
此处,我们来搭建一个2层的神经网络分类器,来实现分类任务。网络结构示意图如下图。
- 输入层: 20个输入参数分布
- **隐藏层:**第一层隐藏层为全连接层,有17个节点,第二层隐藏层依旧是全连接层,2个节点。
- 输出层: 使用softmax归一化,2个节点的参数转换成概率值。
代码实现
1. 构建网络
#定义输入参数的placehoder
with tf.name_scope('input'):
x = tf.placeholder(tf.float32, [None, 20])
y_ = tf.placeholder(tf.int64, [None])
#构建第一层隐藏层,输入20个参数,输出17个参数,使用tanh作为激活函数
with tf.name_scope('hidden_layer_1'):
W_1 = tf.Variable(tf.random_uniform([20,17]))
b_1 = tf.Variable(tf.zeros([17]))
node_1 = tf.nn.tanh(tf.matmul(x,W_1)+b_1)
print(node_1.shape)
#构建第二层隐藏层,输入17个参数,输出2个参数,最后使用softmax归一化获得输出结果
with tf.name_scope('output'):
W_2 = tf.Variable(tf.random_uniform([17,2]))
b_2 = tf.Variable(tf.zeros([2]))
y = tf.nn.softmax(tf.matmul(node_1,W_2)+b_2)
构建损失函数,使用tensorflow函数sparse_softmax_cross_entropy_with_logits来获得交叉熵。
此处,参数logits是softmax输出结果,labels则是标签值,这里的标签值不需要做成onehot形式,但需要确保表格中可能出现的值得个数和softmax的输出维度一直。比如,此处我们标签就只有0和1两个可能的值,所以softmax的输出维度为2。
with tf.name_scope('loss'):
loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=y_,name="cross_entropy_per_example"))
定义训练,此处推荐使用AdagradOptimizer,这是一个自适应学习率的学习优化器,我们给出的学习率(learn rate)仅是一个初始值,在参数更新过程中,实际的学习率是会根据loss的速度进行调整。
with tf.name_scope('train_step'):
train_step = tf.train.AdagradOptimizer(LEARN_RATE).minimize(loss)
最后,我们定义准确率,以便衡量训练效果。tf.argmax函数可以获得张量某一维度上最大值得位子索引,这就是我们的预测结果了。然后,我们将预测结果和标签比较,计算正确率。tf.equal用于比较两个tensor是否相等,如相等则返回一,即预测成功,如不相等则预测失败,由此获得一个只有0,1的一维向量。tf.reduce_mean对该向量求平均值,获得预测准确率。
with tf.name_scope('accuracy'):
prediction =tf.argmax(y, 1)
accuracy = tf.reduce_mean(tf.cast(tf.equal(prediction,y_), tf.float32))
2. 训练模型
以下是模型训练的完整代码。
首先,定义参数初始化运算 init 和模型保存对象 saver。
然后,启动一个Session来进行训练。tf.get_default_graph().finalize()是用来锁定算图,后面任何代码试图改变算图都会报错,这样可以帮助我们养成良好代码习惯,提高运算速度。
我们进行100000次的迭代训练,每次迭代首先获得训练数据集,然后调用session的run方法来启动之前定义的训练对象 train_step。同时,我们还加入了accuracy, loss的运算,方便我们查看训练效果。每1000次迭代,输出一次训练准确度。大约在接近100000次迭代左右,就可以比较稳定的获得98%的准确率。
训练完成后,保存模型到之前预定义的目录下。
import time
#载入训练数据集, mini-batch的size是1000
train_data = voicegender_data('./voicegender_data/voicegender_train.csv', batch_size=1000)
#参数初始化
init = tf.global_variables_initializer()
#定义一个saver对象用于保存训练后的模型
saver = tf.train.Saver(max_to_keep=1)
with tf.Session() as sess:
sess.run(init)
tf.get_default_graph().finalize()
for j in range(100000):
input_data, train_label = train_data.get_batch_data()
accuracy_val, loss_val, _ = sess.run([accuracy, loss, train_step],
feed_dict={x: input_data, y_: train_label})
#每1000次迭代,输出一次训练准确度
if (j+1) % 1000 == 0:
print('Step %d: loss = %.2f, accuracy = %.2f'%(j,loss_val,accuracy_val))
#训练完成,保存模型参数
saver.save(sess,ckpt_dir+'simpl_nn_ckpt',global_step=j+1)
print("Training is done.")
3. 测试模型
先载入测试数据集, 然后启动一个Session, saver对象将从之前保存的训练参数载入,启动run来获得accuracy和loss。这里因为不需要训练,所以run时不要放入train_step对象,就不会更新训练的参数。
test_data = voicegender_data('./voicegender_data/voicegender_test.csv', batch_size=1000)
with tf.Session() as sess:
sess.run(init)
model_file=tf.train.latest_checkpoint(ckpt_dir)
saver.restore(sess,model_file)
accuracy_val, loss_val = sess.run([accuracy, loss],
feed_dict={x: test_data.data_list, y_: test_data.label_list})
print('Test result: loss = %.2f, accuracy = %.2f'%(loss_val,accuracy_val))
源码下载链接
https://download.csdn.net/download/deecheanW/12302445
参照该数据集的说明,逻辑回归的分类准确度可达98%。 ↩︎