Tensorflow的青铜级举一反三应用
一、逻辑回归---二分类
1、任务描述
给定数据集,即给定二维平面内的坐标点及其所属类别标签,设计模型去拟合一个映射,得到,模型优化的目标是对每个点的预测类别都尽可能接近其真实标签。
2、数据集
训练集
200个均值为0、方差为0.5的随机散点构成x_data,每个样本代表一个坐标点,设计一个分类平面,比如当x1+x2>=0时归为0类,反之归为1类,得到训练样本的标签y_data。x_data的矩阵形式为[200,2],y_data也是[200,2],因为标签是onehot编码的形式(10为0类,01为1类)。
测试集
为了展示模型的决策平面,测试集取了横纵坐标都在[-2,2]之间的40000个网格点,得到每个网格坐标的预测值,就能在一定精度上反映出分类决策面。
3、模型参数
因为逻辑回归没有隐层,只有输入端经过一层映射得到输出,因此模型的参数就是一层运算的权重w和偏置b。由于输出的预测值是二分类的结果,因此两个参数的尺寸为[2,2]和[1,2],运算过程为:
类别概率分布值out=输入数据x_data*权重w+偏置b
[200,2]=[200,2]*[2,2]+[1,2]
4、损失函数
逻辑回归损失中通过sigmoid激活函数来引入非线性,输出的概率在0到1之间,采用交叉熵损失度量预测概率和真实标签之间的距离。
5、完整代码
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
#数据集大小设置
data_num=200
var_nums=2
#随机取坐标作为训练集
x_data=np.random.normal(0,0.5,[data_num,2])
y_data=np.zeros([data_num,2])
#为每个训练样本选择标签
for idx in range(data_num):
x1=x_data[idx,0]
x2=x_data[idx,1]
if (x1+x2<0):
y_data[idx,1]=1
else:
y_data[idx,0]=1
#在区域内选择网格点作为测试集
t1=np.linspace(-2, 2, 200)
t2=np.linspace(-2, 2, 200)
x1, x2=np.meshgrid(t1, t2)
x_test=np.dstack((x1.flat, x2.flat))[0]
#定义数据和标签的张量
x=tf.placeholder(tf.float32, [None, 2])
y=tf.placeholder(tf.float32, [None, 2])
#模型结构,参数的定义,采用sigmoid函数将回归值映射到分类概率
w=tf.Variable(tf.random_normal([var_nums,2]))
b=tf.Variable(tf.zeros([1,2]))
out=tf.nn.sigmoid(tf.matmul(x,w)+b)
#交叉熵损失
loss=tf.reduce_mean(-y*tf.log(out)-(1-y)*tf.log(1-out))
#准确率计算,这里将预测向量中最大概率所在的类别作为预测类别
correct=tf.equal(tf.argmax(out,axis=1), tf.argmax(y, axis=1))
acc=tf.reduce_mean(tf.cast(correct, tf.float32))
train_step=tf.train.GradientDescentOptimizer(0.2).minimize(loss)
#训练过程
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(2000):
sess.run(train_step, feed_dict={x:x_data, y:y_data})
#每迭代10次输出当前loss和准确率
if (i%10==0):
cur_loss=sess.run(loss, feed_dict={x:x_data, y:y_data})
cur_acc=sess.run(acc, feed_dict={x:x_data, y:y_data})
print("Epoch:" + str(i) + "Loss:" + str(cur_loss) + "Acc:" + str(cur_acc))
#测试数据
predict_value=sess.run(out, feed_dict={x:x_test})
predict_value=np.argmax(predict_value, axis=1)
#画图
plt.figure()
cm_light=mpl.colors.ListedColormap(['#bde1f5', '#f7cfc6'])
predict_value=predict_value.reshape(x1.shape)
plt.pcolormesh(x1, x2, predict_value, cmap=cm_light)
plt.scatter(x_data[y_data[:,0]==0][:,0], x_data[y_data[:,0]==0][:,1], s=50, marker='+', c='red')
plt.scatter(x_data[y_data[:,0]==1][:,0], x_data[y_data[:,0]==1][:,1], s=50, marker='x', c='blue')
plt.show()
输出效果
二、逻辑回归---三分类
在上述二分类任务的基础上,尝试实现三分类,主要改动的地方有:
1、数据集
x_data不变,还是在平面内随机取的点,但是每个样本对应的标签应该由三种情况,因此y_data的尺寸变为[200,3],选择分类平面的时候应该分为三种情况。代码中相应部分改动如下:
#生成训练集
x_data=np.random.normal(0,0.5,[data_num,2])
y_data=np.zeros([data_num,3])
for idx in range(data_num):
x1=x_data[idx,0]
x2=x_data[idx,1]
if (x1+x2<-0.5):
y_data[idx,0]=1
elif (x1+x2>0.5):
y_data[idx,1]=1
else:
y_data[idx,2]=1
2、激活函数改为softmax
二分类时选择sigmoid,因为sigmoid可以将输入值映射到[0,1]之间,刚好能用来表示两个类别的预测概率,而多分类时则需要用softmax,能够计算出每个类别预测值占所有预测值总和的比例,刚好作为当前类别的分类置信度。
#采用softmax函数将回归值映射到分类概率
w=tf.Variable(tf.random_normal([var_nums,3]))
b=tf.Variable(tf.zeros([1,3]))
out=tf.nn.softmax(tf.matmul(x,w)+b)
3、损失函数形式
损失函数还是选择交叉熵,但是由于是经过softmax之后的分类预测值,所以交叉熵的形式要改变一下。
因为经过softmax之后的向量可以看做一个整体置信度分布,所有值相加等于1,可以直接和经过onehot编码之后的真实标签分布做交叉熵;而经过sigmoid之后的向量不能看做一个整体分布,因为没有引入类别间的制约,所有值相加不等于1,所以这时候要将每个预测值单独看做一个分布,也就是0类和1类单独计算。
#多分类时的交叉熵loss
loss=tf.reduce_mean(-y*tf.log(out))
4、结果展示部分
画图展示决策面的时候也相应改动一下。
plt.figure()
cm_light=mpl.colors.ListedColormap(['#bde1f5', '#f7cfc6', '#a0a0ff'])
predict_value=predict_value.reshape(x1.shape)
plt.pcolormesh(x1, x2, predict_value, cmap=cm_light)
plt.scatter(x_data[y_data[:,0]==1][:,0], x_data[y_data[:,0]==1][:,1], s=50, marker='+', c='red')
plt.scatter(x_data[y_data[:,1]==1][:,0], x_data[y_data[:,1]==1][:,1], s=50, marker='x', c='blue')
plt.scatter(x_data[y_data[:,2]==1][:,0], x_data[y_data[:,2]==1][:,1], s=50, marker='1', c='orange')
plt.show()
输出效果
三、逻辑回归---数据线性不可分时
在上述二分类任务的基础上,将数据集设置为二维平面线形不可分的情况,例如把原来的分类边界改为和0.5之间的比较关系,当时设为0类,反之设为1类。这时显而易见分类平面是被一个小圆分割形成的,线性的模型不能够实现对它的分割,因此要在模型引入隐层,达到非线性映射的目的。
1、数据集
在定义数据集时选择一个非线性的切割面,如上所述用一个小圆形成分类切割面。
#生成训练集
x_data=np.random.normal(0,0.5,[data_num,2])
y_data=np.zeros([data_num,2])
for idx in range(data_num):
x1=x_data[idx,0]
x2=x_data[idx,1]
if (x1*x1+x2*x2<0.5):
y_data[idx,1]=1
else:
y_data[idx,0]=1
2、加入隐层
当模型包含两层计算时,可以根据拟合的困难程度设定第一层参数的数量,模型越复杂需要参数越多。两层网络中数据流计算的矩阵形式为:
out1=x*w1+b1 -> [200,20]=[200,2]*[2,20]+[1,20]
out2=out1*w2+b2 -> [200,2]=[200,20]*[20,2]+[1,2]
其中200是样本数量,20是参数数量。
#第一层
w1=tf.Variable(tf.random_normal([2,var_num]))
b1=tf.Variable(tf.zeros([1,var_num]))
out1=tf.nn.sigmoid(tf.matmul(x,w1)+b1)
#第二层
w2=tf.Variable(tf.random_normal([var_num,2]))
b2=tf.Variable(tf.zeros([1,2]))
out2=tf.nn.sigmoid(tf.matmul(out1,w2)+b2)
3、损失函数
激活函数还是选择sigmoid,损失函数是交叉熵。
#交叉熵损失
loss=tf.reduce_mean(-y*tf.log(out2)-(1-y)*tf.log(1-out2))
输出效果
参考
1、用tensorflow实现线线性回归和逻辑回归:https://www.jianshu.com/p/f1e971eab3fd
2、二分类与多分类的交叉熵损失函数:https://www.jianshu.com/p/5139f1166db7