前言
用TensorFlow做分类任务,最基本的还是两个步骤,构建计算图和执行计算图。构建计算图中,涉及到数据的读取,变量、占位符的定义,神经网络的设计,激活函数、代价函数、优化器的定义,等把静态的计算图构建之后,就开始启动会话执行运算了,计算需要训练的节点,最后得到结果。
这里使用sklearn中的digits数据集进行分类任务,设计简单的两层神经网络,并用dropout减少过拟合,最后得到性能评估。
数据预处理
读取digits的数据很简单,首先引入sklearn相关的库
from sklearn.datasets import load_digits
digits = load_digits()
构建digits实例,里面data属性存放的数据样本,target属性存放样本对应的标签。可以观察到各自的shape
>>> digits.data.shape
(1797, 64)
>>> digits.target.shape
(1797,)
可以看到,data是由1797个8*8的图片组成,target是对应的label。
X, y = digits.data, digits.target
这里的每个样本对应一个标签,我们可以对标签进行独热码解析,把它二值化,这样方便以后softmax回归。在sklearn中,你可以选择OneHotEncoder或者LabelBinarizer对其进行解析。这里选择OneHotEncoder。
from sklearn.preprocessing import OneHotEncoder
y = OneHotEncoder().fit_transform(y.reshape(-1, 1)).toarray()
OneHotEncoder的输入必须是 2-D array,而y的shape是一维向量,所以先将其reshape,然后利用OneHotEncoder转换,注意,这样的输出,y最后变成了稀疏矩阵,需要利用toarray再转换成矩阵。
接下来就是对训练集,测试集的分割,sklearn有专门的函数,test_size参数可以选择分割的比例。
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
构建计算图
网络层的设计
由于这里是要使用两层神经网络,可以事先预定义网络的格式、输入输出的函数,然后直接调用。传入的参数有网络层的输入,最后通过激活函数计算输出,所以要传入激活函数,还要设计两层神经元的个数。
权重和偏置的定义通过tf.Variable实现。
使用dropout来减少过拟合,所以两层神经元使用tf.nn.dropout激活函数。
def add_layer(input, in_size, out_size, activation=None):
W = tf.Variable(tf.random_normal([in_size, out_size]), 'W')
b = tf.Variable(tf.zeros([out_size]) + 0.1, 'b')
output = tf.nn.dropout(tf.matmul(input, W) + b, keep_prob)
if activation is None:
return output
else:
return activation(output)
占位符
占位符用来定义传递进来的真实的数据样本,不必制定初始值,这里,来定义数据样本、数据标签和dropout概率。
keep_prob = tf.placeholder(tf.float32)
xs = tf.placeholder(tf.float32, [None, 64])
ys = tf.placeholder(tf.float32, [None, 10])
网络层定义
这里,使用100个神经元的第一层,激活函数使用sigmoid函数,第二层因为要输出0到9的数字,设计10个神经元,激活函数使用softmax函数。
l1 = add_layer(xs, 64, 100, tf.nn.tanh)
l2 = add_layer(l1, 100, 10)
代价函数与优化器
softmax函数一般与交叉熵代价函数相配合,TensorFlow有tf.nn.softmax_cross_entropy_with_logits直接实现,label参数传入真实标签,logits参数传入神经层的输出。
优化器使用梯度下降,学习率设定为0.2,最后的目的就是用梯度下降使参数按照梯度下降最快的方向变化从而使代价函数局部最优。
loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=ys, logits=l2))
optimizer = tf.train.GradientDescentOptimizer(0.2)
train = optimizer.minimize(loss)
tensorflow的激活函数可在tensorflow.nn里面,产生与输入shape相同的张量。
- tf.nn.relu
- tf.nn.relu6
- tf.nn.crelu
- tf.nn.elu
- tf.nn.softplus
- tf.nn.softsign
- tf.nn.dropout
- tf.nn.bias_add
- tf.sigmoid
- tf.tanh
根据不同的模型可以选择不同的激活函数,一般常用的是relu、sigmoid、tanh
TensorFlow的优化器也可以自行选择,提供了计算代价的梯度一些方法。
- tf.train.Optimizer
- tf.train.GradientDescentOptimizer
- tf.train.AdadeltaOptimizer
- tf.train.AdagradOptimizer
- tf.train.AdagradDAOptimizer
- tf.train.MomentumOptimizer
- tf.train.AdamOptimizer
- tf.train.FtrlOptimizer
- tf.train.ProximalGradientDescentOptimizer
- tf.train.ProximalAdagradOptimizer
- tf.train.RMSPropOptimizer
运行计算图
启动会话,首先初始化变量,然后设置训练迭代1000次,最后与真实标签比较,得到准确率。
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
for i in range(1000):
sess.run(train, {xs: X_train, ys: y_train, keep_prob: 0.5})
#print(y_pred.shape, ...)
result = tf.equal(tf.argmax(y_test, 1), tf.argmax(l2, 1))
acc = tf.reduce_mean(tf.cast(result, tf.float32))
#print(classification_report(y_test_res, y_pred_res))
print(sess.run(acc, {xs: X_test, keep_prob: 1}))
结论
当第一层使用sigmoid激活函数,最后准确率为0.875421.
当第一层使用tanh激活函数,最后准确率为0.858586.
使用0.5比例的dropout和sigmoid函数时,最后准确率为0.927609.
附上代码
import tensorflow as tf
from sklearn.datasets import load_digits
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import OneHotEncoder
#from sklearn.metrics import classification_report
digits = load_digits()
#print(digits.data.shape, ...)
X, y = digits.data, digits.target
y = OneHotEncoder().fit_transform(y.reshape(-1, 1)).toarray()
# y=OneHotEncoder().fit_transform(y[:,np.newaxis])
#print(y.shape, ...)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
def add_layer(input, in_size, out_size, activation=None):
W = tf.Variable(tf.random_normal([in_size, out_size]), 'W')
b = tf.Variable(tf.zeros([out_size]) + 0.1, 'b')
output = tf.nn.dropout(tf.matmul(input, W) + b, keep_prob)
if activation is None:
return output
else:
return activation(output)
keep_prob = tf.placeholder(tf.float32)
xs = tf.placeholder(tf.float32, [None, 64])
ys = tf.placeholder(tf.float32, [None, 10])
l1 = add_layer(xs, 64, 100, tf.nn.tanh)
l2 = add_layer(l1, 100, 10)
loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=ys, logits=l2))
optimizer = tf.train.GradientDescentOptimizer(0.2)
train = optimizer.minimize(loss)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
for i in range(1000):
sess.run(train, {xs: X_train, ys: y_train, keep_prob: 0.5})
#print(y_pred.shape, ...)
result = tf.equal(tf.argmax(y_test, 1), tf.argmax(l2, 1))
acc = tf.reduce_mean(tf.cast(result, tf.float32))
#print(classification_report(y_test_res, y_pred_res))
print(sess.run(acc, {xs: X_test, keep_prob: 1}))
# 第一层使用sigmoid,最后为0.875421
# 第一层使用tanh,最后为0.858586
# 使用0.5的dropout,最后为0.927609