1. 使用tf的一般流程

1. tf的一般流程

    我把tf的一般流程普适性地分成以下三个部分:数据集的处理、神经网络的搭建、开始训练。其中前面两个部分,数据集以及神经网络则需要我们脱离tf工具去考虑问题。以下使用《tensorflow实战:Google深度学习框架》里面的最开始例子来说明如何通过一般流程来编程。

2. 例子一

    例子一要学习这样的特征:如果(x1 + x2) < 1,则为正样本y=1;否则为负样本y=0。首先我们定义数据集,使用随机数作为数据集,训练集10000,测试集100。其次我们考虑神经网络结构,因为该特征是一个简单的线性二分类模型,所以我们的神经网络仅需要较少层数,即可学习这样的特征。看下面的代码,即可了解本文说的三个流程。

import tensorflow as tf 
from numpy.random import RandomState

# 1. 数据集的制作
 # 训练集
train_set_size = 10000
train_set_x = RandomState(1).rand(train_set_size, 2) #以1为随机数种子
train_set_label = [[int(x1+x2<1)] for (x1, x2) in train_set_x]
 # 测试集
test_set_size = 200
test_set_x = RandomState(2).rand(test_set_size, 2)
test_set_label = [[int(x1+x2<1)] for (x1, x2) in test_set_x]

# 2. 定义神经网络结构
 # 三层神经元。第一层2个,第二层3个,第三层1个
  # ph是一种张量,张量具有类型,维度,名字三个基本属性。其优势是作为输入输出时可以只指定第二个维度
input_node = tf.placeholder(tf.float32, shape = (None, 2), name = "input_node")
  # 定义成变量,tf才会对权重进行优化
w1 = tf.Variable(tf.random_normal([2, 3], stddev = 1, seed = 1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev = 1, seed = 1))
label_node = tf.placeholder(tf.float32, shape = (None, 1), name = "label_node")
 # 定义前向传播
a = tf.matmul(input_node, w1)
b = tf.matmul(a, w2)
output_node = tf.sigmoid(b)
 # 定义优化方法
  # 交叉熵
  # 注意对于二分类,不可以仅仅用交叉熵作为损失函数,因为有可能全部预测成1,那么log(1)为0,这时候损失函数为0,但并没有意义。可以考虑加上正则项
cross_entropy = -tf.reduce_mean(label_node * tf.log(tf.clip_by_value(output_node, 1e-10, 1.0)))
#mse = tf.reduce_mean(tf.square(label_node - output_node))
train_step = tf.train.AdamOptimizer(0.001).minimize(C..E..)

# 3. 开始训练
batch_size = 8
with tf.Session() as sess :
    # 初始化变量
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    steps = train_set_size//batch_size * 100 # 对整个数据集训练100次
    for i in range(steps) :
        start = (i * batch_size) % train_set_size
        end = start + batch_size

        # 通过选取的样本训练神经网络并更新参数
        sess.run(train_step, feed_dict = {input_node : train_set_x[start : end], label_node : train_set_label[start : end]})
        if i % 10000 == 0:
            # 每隔一段时间计算再所有数据上的交叉熵并输出
            total_CE = sess.run(mse, feed_dict = {input_node : train_set_x, label_node : train_set_label})
            print("After %d training step(s), cross entropy on all data is %g" % (i, total_CE))

    # 测试集上准确率
    right_test = 0
    for i in range(test_set_size) :
        v = sess.run(output_node, feed_dict = {input_node : test_set_x[i : i + 1]})
        if (v < 0.5 and test_set_label[i] == [0]) or (v > 0.5 and test_set_label[i] == [1]) :
            right_test += 1
        else :
            print("{} : not right. v is {} and x is {}, label is {}".format(i, v, test_set_x[i], test_set_label[i]))
    print("Total accuracy on test set : {}".format(right_test/test_set_size))

  3. 对例子一的反思

cross_entropy = -tf.reduce_mean(label_node * tf.log(tf.clip_by_value(output_node, 1e-10, 1.0)))
cross_entropy = -tf.reduce_mean(label_node * tf.log(tf.clip_by_value(output_node, 1e-10, 1.0)) + (1 - label_node) * tf.log(tf.clip_by_value(1 - output_node, 1e-10, 1.0)))

       

在网上找交叉熵的写法,找到了第一条,但是细细思考之后,这样的写法放在本分类问题中是大错特错的。这样的写法只是考虑了那些正例,而对于负类,却没有任何约束作用。因此,极有可能最终预测方式为全部都为正类,按照损失函数的定义,此时损失函数为0。因此,需要考虑那些负类,也就有了后面那一项。两项一起表示的损失函数才具有意义。

        此外,在损失函数的选择上,分类问题使用交叉熵(CE),而回归问题使用MSE。

        为什么分类问题不能使用MSE呢?网上许多文章说是因为分类常用的sig函数和MSE连用出现K(1-K)项,可能会导致函数在非常接近1或者非常接近0时,学习得很慢,所以不能用。我对这种说法存在疑问,K(1-K)出现的本质是sig求导,难道就不能用sig了吗?但是使用CE作为损失函数时,和sig链式求导可以使得其中一项消去,比MSE更方便学习。

        另外,以上程序跑出来准确率结果很差,不明白是哪里出现错误。目前我认为是参数调的不对,而不是损失函数或者tf使用上的问题。

4. 例子二

    第二个例子是经典的神经网络训练入门例子MNIST。在此我们选用三层神经网络:第一层神经网络,即输入层784个神经元;第二层500个神经元,激活函数为relu函数;第三层,即输入层10个神经元。这里是分类问题,所以使用交叉熵作为损失函数,并且注意10个输出神经元应该输出对应的概率,所以需要softmax。

import tensorflow as tf 
from tensorflow.examples.tutorials.mnist import input_data

# 数据集
mnist = input_data.read_data_sets(r"E:\Learn\Codes\tensorflow实战Google深度学习框架\MNIST\dataset", one_hot = True)

# 网络结构
num_input_node = 784
num_lay1_node = 500
num_output_node = 10

input_node = tf.placeholder(tf.float32, (None, num_input_node), name = "input_node")
output_node = tf.placeholder(tf.float32, (None, num_output_node), name = "output_node")
label_node = tf.placeholder(tf.float32, (None, num_output_node), name = "label_node")

w1 = tf.Variable(tf.random_normal([num_input_node, num_lay1_node], stddev = 1, seed = 1))
b1 = tf.Variable(tf.constant(0.1, shape = [num_lay1_node]))

w2 = tf.Variable(tf.random_normal([num_lay1_node, num_output_node], stddev = 1, seed = 1))
b2 = tf.Variable(tf.constant(0.1, shape = [num_output_node]))

a = tf.nn.relu(tf.matmul(input_node, w1) + b1)
logits = tf.matmul(a, w2) + b2

cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits = logits, labels = tf.argmax(label_node, 1)))
train_step = tf.train.AdamOptimizer(0.01).minimize(cross_entropy)

# 开始训练
correction_prediction_average = tf.equal(tf.argmax(logits, 1), tf.argmax(label_node, 1))
# 将布尔类型转成实数,转换后的平均值就是准确率
accuracy = tf.reduce_mean(tf.cast(correction_prediction_average, tf.float32))

batch_size = 100
num_train_step = 30000
with tf.Session() as sess :
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    validate_feed = {input_node : mnist.validation.images, label_node : mnist.validation.labels}
    test_feed = {input_node : mnist.test.images, label_node : mnist.test.labels}

    for i in range(num_train_step) :
        # 生成一个batch数据
        xs, ys = mnist.train.next_batch(batch_size)
        sess.run(train_step, feed_dict = {input_node : xs, label_node : ys})

        if i % 1000 == 0 :
            validate_acc, total_cross_entropy = sess.run([accuracy, cross_entropy], feed_dict = validate_feed)
            print("After %d training step(s), validation accuracy is %g, and loss is %g" % (i, validate_acc, total_cross_entropy))

    test_acc = sess.run(accuracy, feed_dict = test_feed)
    print("THE LAST : this model %g in test dataset" % test_acc)

    本博文的主要目的在于建立一个普适性的tf框架,以上两个例子虽然简单,但是也足够说明目的。但是显然我们还可以在这个框架上加上更深入的思考,例如选用什么激活函数,什么损失函数,网络结构怎么样,需不需要正则化等等。考虑成熟之后,就可以进入tf的函数调用,这些都脱离了本博文的内容,因此也就不再叙述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值