使用TensorFlow构建一个神经网络来实现二分类,主要包括输入数据格式、隐藏层数的定义、损失函数的选择、优化函数的选择、输出层。下面通过numpy来随机生成一组数据,通过定义一种正负样本的区别,通过TensorFlow来构造一个神经网络来实现二分类。
一、神经网络结构
输入数据:定义输入一个二维数组(x1,x2),数据通过numpy来随机产生,将输出定义为0或1,如果x1+x2<1,则y为1,否则y为0。
隐藏层:定义两层隐藏层,隐藏层的参数为(2,3),两行三列的矩阵,输入数据通过隐藏层之后,输出的数据为(1,3),t通过矩阵之间的乘法运算可以获得输出数据。
损失函数:使用交叉熵作为神经网络的损失函数,常用的损失函数还有平方差。
优化函数:通过优化函数来使得损失函数最小化,这里采用的是Adadelta算法进行优化,常用的还有梯度下降算法。
输出数据:将隐藏层的输出数据通过(3,1)的参数,输出一个一维向量,值的大小为0或1。
二、TensorFlow代码的实现
-
import tensorflow as tf
-
from numpy.random import RandomState
-
if __name__ == "__main__":
-
#定义每次训练数据batch的大小为8,防止内存溢出
-
batch_size = 8
-
#定义神经网络的参数
-
w1 = tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
-
w2 = tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))
-
#定义输入和输出
-
x = tf.placeholder(tf.float32,shape=(None,2),name="x-input")
-
y_ = tf.placeholder(tf.float32,shape=(None,1),name="y-input")
-
#定义神经网络的前向传播过程
-
a = tf.matmul(x,w1)
-
y = tf.matmul(a,w2)
-
#定义损失函数和反向传播算法
-
#使用交叉熵作为损失函数
-
#tf.clip_by_value(t, clip_value_min, clip_value_max,name=None)
-
#基于min和max对张量t进行截断操作,为了应对梯度爆发或者梯度消失的情况
-
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y,1e-10,1.0)))
-
# 使用Adadelta算法作为优化函数,来保证预测值与实际值之间交叉熵最小
-
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
-
#通过随机函数生成一个模拟数据集
-
rdm = RandomState(1)
-
# 定义数据集的大小
-
dataset_size = 128
-
# 模拟输入是一个二维数组
-
X = rdm.rand(dataset_size,2)
-
#定义输出值,将x1+x2 < 1的输入数据定义为正样本
-
Y = [[int(x1+x2 < 1)] for (x1,x2) in X]
-
#创建会话运行TensorFlow程序
-
with tf.Session() as sess:
-
#初始化变量 tf.initialize_all_variables()
-
init = tf.initialize_all_variables()
-
sess.run(init)
-
#设置神经网络的迭代次数
-
steps = 5000
-
for i in range(steps):
-
#每次选取batch_size个样本进行训练
-
start = (i * batch_size) % dataset_size
-
end = min(start + batch_size,dataset_size)
-
#通过选取样本训练神经网络并更新参数
-
sess.run(train_step,feed_dict={x:X[start:end],y_:Y[start:end]})
-
#每迭代1000次输出一次日志信息
-
if i % 1000 == 0 :
-
# 计算所有数据的交叉熵
-
total_cross_entropy = sess.run(cross_entropy,feed_dict={x:X,y_:Y})
-
# 输出交叉熵之和
-
print("After %d training step(s),cross entropy on all data is %g"%(i,total_cross_entropy))
-
#输出参数w1
-
print(w1.eval(session=sess))
-
#输出参数w2
-
print(w2.eval(session=sess))
-
'''
-
After 0 training step(s),cross entropy on all data is 0.0674925
-
After 1000 training step(s),cross entropy on all data is 0.0163385
-
After 2000 training step(s),cross entropy on all data is 0.00907547
-
After 3000 training step(s),cross entropy on all data is 0.00714436
-
After 4000 training step(s),cross entropy on all data is 0.00578471
-
[[-1.96182752 2.58235407 1.68203771]
-
[-3.46817183 1.06982315 2.11788988]]
-
[[-1.82471502]
-
[ 2.68546653]
-
[ 1.41819501]]
-
'''
--------------------------------2018/9/16更新(非常感谢al_kk的提醒)---------------------------------------
上面的TensorFlow二分类我是参考Google深度学习框架,al_kk评论说这个损失函数的定义存在问题,之前没有仔细的去考虑这个问题,al_kk提醒之后,我发现这个损失函数的定义的确存在问题,经过测试发现这个模型也存在一些问题。其实,我们的主要目的是想去学习一个x1+x2=1的直线,来区分0和1两类不同的类别,下面我对这个模型进行了一些修改并说明一下为什么这个损失函数的定义存在问题。
一、为什么说这个损失函数的定义存在问题呢?
上面程序中定义的输入的y的shape为[1],也就是y的类别为0或1,对于单分类问题交叉熵损失函数的定义应该为
其中n为y的种类,在上面的例子中需要包含0和1的y_*log(y)(y_表示真实类别,y表示预测类别),而上面的例子中只包含了一个y_*log(y),在上例中正确的损失函数定义应该为loss = y_*log(y) + (1-y_) * log(1-y)。为了便于大家理解,我引用al_kk:“如果只有一个类别的交叉熵即y_ * log(y),如果真实类别y_为0,那么无论预测值y为任何值的时候,损失函数始终为0”。除此之外,大家可以想一下,当预测值始终为1的时候,那么损失函数是不是就会一直为0,这也是为什么输出预测值y的时候,y的值都是大于1的。如果将y的shape改为[2]的话,就可以使用y_*log(y)。
二、修改之后的二分类程序
-
import tensorflow as tf
-
import numpy as np
-
from numpy.random import RandomState
-
import matplotlib.pyplot as plt
-
if __name__ == "__main__":
-
#定义神经网络的参数
-
w = tf.Variable(tf.random_normal([2,1],stddev=1,seed=1))
-
b = tf.Variable(tf.random_normal([1],stddev=1,seed=1))
-
#定义输入和输出
-
x = tf.placeholder(tf.float32,shape=(None,2),name="x-input")
-
y_ = tf.placeholder(tf.float32,shape=(None,1),name="y-input")
-
#定义神经网络的前向传播过程
-
y = tf.matmul(x,w) + b
-
#定义损失函数和反向传播算法
-
#使用交叉熵作为损失函数
-
#tf.clip_by_value(t, clip_value_min, clip_value_max,name=None)
-
#基于min和max对张量t进行截断操作,为了应对梯度爆发或者梯度消失的情况
-
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y,1e-10,1.0))+(1-y_) * tf.log(tf.clip_by_value(1-y,1e-10,1.0)))
-
# 使用Adadelta算法作为优化函数,来保证预测值与实际值之间交叉熵最小
-
train_step = tf.train.AdamOptimizer(0.01).minimize(cross_entropy)
-
#通过随机函数生成一个模拟数据集
-
rdm = RandomState(1)
-
# 定义数据集的大小
-
dataset_size = 100
-
# 模拟输入是一个二维数组
-
X = rdm.rand(dataset_size,2)
-
#定义输出值,将x1+x2 < 1的输入数据定义为正样本
-
Y = [[int(x1+x2 < 1)] for (x1,x2) in X]
-
#创建会话运行TensorFlow程序
-
with tf.Session() as sess:
-
#初始化变量 tf.initialize_all_variables()
-
init = tf.initialize_all_variables()
-
sess.run(init)
-
#设置神经网络的迭代次数
-
steps = 1000
-
for i in range(steps):
-
#通过选取样本训练神经网络并更新参数
-
for (input_x,input_y) in zip(X,Y):
-
input_x = np.reshape(input_x,(1,2))
-
input_y = np.reshape(input_y,(1,1))
-
sess.run(train_step,feed_dict={x:input_x,y_:input_y})
-
#每迭代1000次输出一次日志信息
-
if i % 100 == 0:
-
# 计算所有数据的交叉熵
-
total_cross_entropy = sess.run(cross_entropy,feed_dict={x:X,y_:Y})
-
# 输出交叉熵之和
-
print("After %d training step(s),cross entropy on all data is %g"%(i,total_cross_entropy))
-
#预测输入X的类别
-
pred_Y = sess.run(y,feed_dict={x:X})
-
#绘制图像
-
for input_x,output_y in zip(X,pred_Y):
-
if output_y < 1:
-
plt.scatter(input_x[0],input_x[1],marker="o",color="blue",s=20)
-
else:
-
plt.scatter(input_x[0],input_x[1],marker="^",color="red",s=20)
-
#绘制x1+x2 = 1直线
-
plt.plot([0,1],[1,0])
-
plt.show()