一、回归神经网络
1、神经网络结构
定义一个简单的回归神经网络结构:
- 数据集为(xi,yi),数据的特征数为1,所以x的维度为1。
- 输入层1个神经元。
- 隐藏层数为1,4个神经元。
- 输出层1个神经元。
- 隐藏层的激活函数为f(x)=x,输出层的激活函数为ReLU
结构图如下:
2、代码示例
相关函数说明:
- tf.random_normal :用于生成正太分布随机数矩阵的tensor,tensorFlow有很多随机数函数,可以查找官方文档获得。
- tf.zeros :用于生成0矩阵的tensor,tf.ones可以用来获得单位矩阵。
- tf.nn.relu :tensorflow定义的用来实现ReLU激活函数的方法。
- tf.reduce_sum :求和函数,通过axis来控制在哪个方向上求和,axis=[0]表示按行求和,axis=[1]表示按列求和。
- tf.train.GradientDescentOptimizer(learning_rate).minimize(loss) :梯度下降优化函数,learning_rate表示学习率,minimize表示最小化,loss是优化的损失函数。tensorFlow有很多优化函数,可以查找官方文档获得。
代码:
import tensorflow as tfimport numpy as npimport matplotlib.pyplot as plt# 创建数据训练数据集,x_data = np.linspace(-1, 1, 500).reshape(500, 1)noise = np.random.normal(0, 0.05, [500, 1]) # 制作噪音y_data = np.square(x_data) + 0.5 + noise# 创建占位符用于minibatch的梯度下降训练,建议数据类型使用tf.float32、tf.float64等浮点型数据x_in = tf.placeholder(tf.float32, [None, 1])y_in = tf.placeholder(tf.float32, [None, 1])# 定义一个添加层的函数def add_layer(input_, in_size, out_size, activation_funtion=None): ''' :param input_: 输入的tensor :param in_size: 输入的维度,即上一层的神经元个数 :param out_size: 输出的维度,即当前层的神经元个数即当前层的 :param activation_funtion: 激活函数 :return: 返回一个tensor ''' weight = tf.Variable(tf.random_normal([in_size, out_size])) # 权重,随机的in_size*out_size大小的权重矩阵 biase = tf.Variable(tf.zeros([1, out_size]) + 0.01) # 偏置,1*out_size大小的0.01矩阵,不用0矩阵避免计算出错 if not activation_funtion: # 根据是否有激活函数决定输出 output = tf.matmul(input_, weight) + biase else: output = activation_funtion(tf.matmul(input_, weight) + biase) return output# 定义隐藏层,输入为原始数据,特征为1,所以输入为1个神经元,输出为4个神经元layer1 = add_layer(x_in, 1, 4, tf.nn.relu)# 定义输出层,输入为layer1返回的tensor,输入为4个神经元,输出为1个神经元,激活函数为ReLUpredict = add_layer(layer1, 4, 1)# 定义损失函数loss = tf.reduce_mean(tf.reduce_sum(tf.square(y_in - predict), axis=[1])) # tf.reduce_sum的axis=[1]表示按列求和# 定义训练的优化方式为梯度下降train = tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 学习率为0.1init = tf.global_variables_initializer()with tf.Session() as sess: sess.run(init) # 训练1000次 for step in range(1000): # 执行训练,因为有占位符所以要传入字典,占位符的好处是可以用来做minibatch训练,这里数据量小,直接传入全部数据来训练 sess.run(train, feed_dict={x_in: x_data, y_in: y_data}) # 每50步输出一次loss if step % 49 == 0: print(sess.run(loss, feed_dict={x_in: x_data, y_in: y_data})) # 最后画出实际的散点图与拟合的折线图进行对比 predict_value = sess.run(predict, feed_dict={x_in: x_data}) # 先要获得预测值 plt.figure() plt.scatter(x_data, y_data, c='r', marker='o') plt.plot(x_data, predict_value, '--', lw=2, c='b') plt.show()
二、分类神经网络——mnist手写字数据
1、网络结构与数据来源
数据的详细说明请查看:。
结构说明:
- 数据集(xi,yi):数据共有60000张图。其中xi是表示的是每一个图片的数据,长度为28x28 = 784,即一张图片特征为784列;yi有0-9共10种结果,由于是分类,所以使用softmax函数,则yi最后对应的输出层需要有10个神经元对应,有几个类就有几个神经元。
- 隐藏层:个数为0,定义隐藏层拟合效果较差,所以这里不定义。
- 输出层:为10个神经元,激活函数为softmax。
- 损失函数loss:交叉熵损失函数。(交叉熵损失请查阅:)
- 迭代方法:由于数据集过大,使用minbtach方法。
- 准确度的计算 :中有详细说明。
2、代码示例
相关函数说明:
-
input_data.read_data_sets(train_dir='MNIST_data',one_hot=True) :函数作用说明,读取数据,代码会自动下载数据(若网络原因可自行下载),下载的是数据文件夹,在当前工作目录下,里面包含训练集mnist.train(包含特征mnist.train.images与标签mnist.train.labels)与测试集mnist.test(包含特征mnist.test.images与标签mnist.test.labels)。参数说明,train_dir:数据相对路径,one_hot:是否为独热编码。mnist.train.next_batch。
- tf.argmax(input,dimension) :返回input张量的最大值所在的位置,dimension=1为列最大。
- tf.equal(x,y) :返回一个bool数组,当x==y则值为True。
-
mnist.train.next_batch(batch_size) :用来获取mnist每批次的数据,每次使用会自动获取下一批batch_size大小的数据集。
代码:
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data # 读取数据,代码会自动下载数据,下载的是数据文件夹,在当前工作目录下,里面包含训练集与测试集,train_dir:数据路径,one_hot:是否为独热编码。 mnist = input_data.read_data_sets(train_dir='MNIST_data', one_hot=True) # 数据较大,因此用minbatch,batch_size每个批次的数据个数,batch_num为批次个数 batch_size = 1000 batch_num = mnist.train.num_examples // batch_size # 创建占位符用于minibatch的梯度下降训练,建议数据类型使用tf.float32、tf.float64等浮点型数据 x_in = tf.placeholder(tf.float32, [None, 784]) y_in = tf.placeholder(tf.float32, [None, 10]) # 定义一个添加层的函数 def add_layer(input_, in_size, out_size, activation_funtion=None): ''' :param input_: 输入的tensor :param in_size: 输入的维度,即上一层的神经元个数 :param out_size: 输出的维度,即当前层的神经元个数即当前层的 :param activation_funtion: 激活函数 :return: 返回一个tensor ''' weight = tf.Variable(tf.random_normal([in_size, out_size])) # 权重,随机的in_size*out_size大小的权重矩阵 biase = tf.Variable(tf.zeros([1, out_size]) + 0.01) # 偏置,1*out_size大小的0.01矩阵,不用0矩阵避免计算出错 if not activation_funtion: # 根据是否有激活函数决定输出 output = tf.matmul(input_, weight) + biase else: output = activation_funtion(tf.matmul(input_, weight) + biase) return output # 定义输出层,输入为layer1返回的tensor,输入为784个神经元,输出为10个神经元,激活函数为softmax。 prediction = add_layer(x_in, 784, 10) # 这个对应下一步定义交叉熵损失函数的method1,不需要激活函数softmax # 定义交叉熵损失函数,这里用method1 # method1:用自带的函数,这个函数会自动对prediction进行softmax操作所以不需要前面定义prediction不需要激活函数 cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_in, logits=prediction)) # #method2:手动计算,这里tf.clip_by_value函数的作用是为了避免输入为负数或者0,log函数的定义域是大于0的,不这么做会出现LOSS=NAN且模型准确度不变的情况,这个坑会在代码示例后面说明。 # cross_entropy = -tf.reduce_sum(y_in * tf.log(tf.clip_by_value(prediction,1e-8,1.0))) # 定义训练的优化方式为梯度下降 train = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy) # 学习率为0.1 # 准确度,先得到bool型的数组correct_prediction ,再计算值为True的平均值即为准确率 correct_prediction = tf.equal(tf.argmax(y_in, 1), tf.argmax(prediction, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) # 训练20代 for epoch in range(20): # 每代对数据进行一轮minibatch for batch in range(batch_num): batch_x, batch_y = mnist.train.next_batch(batch_size) # 每个循环读取batch_size大小批次的数据 sess.run(train, feed_dict={x_in: batch_x, y_in: batch_y}) acc = sess.run(accuracy, feed_dict={x_in: mnist.test.images, y_in: mnist.test.labels}) # 用测试数据计算准确度 print('第%d代%d批次,准确率为%.6f' % (epoch + 1, batch + 1, acc))
3、 使用交叉商的坑
第一个坑:
使用method1计算较差熵时,定义prediction不能有激活函数tf.nn.softmax,因为tf.nn.softmax_cross_entropy_with_logits函数会自动对prediction进行softmax处理,所以正确的代码如下:
#定义输出层prediction = add_layer(x_in, 784, 10) #定义交叉熵cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_in, logits=prediction))
可以尝试使用method1计算交叉熵,并定义prediction的时候传入激活函数,会发现拟合效果变差,代码如下
#定义输出层prediction = add_layer(x_in, 784, 10, tf.nn.softmax) #定义交叉熵cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_in, logits=prediction))
第二个坑:
先来看一下,当使用method2手动计算交叉熵时,不使用tf.clip_by_value时候的代码的时候运行是什么样子的,代码修改如下:
#method2:手动计算,这里tf.clip_by_value函数的作用是为了避免输入为负数或者0,log函数的定义域是大于0的,不这么做会出现LOSS=NAN且模型准确度不变的情况,这个坑会在代码示例后面说明。cross_entropy = -tf.reduce_sum(y_in * tf.log(prediction))
运行结果为:
C:\Users\EDZ\PycharmProjects\DY\Scripts\python.exe C:/Users/EDZ/.PyCharm2019.1/config/scratches/tf.pyExtracting MNIST_data\train-images-idx3-ubyte.gzExtracting MNIST_data\train-labels-idx1-ubyte.gzExtracting MNIST_data\t10k-images-idx3-ubyte.gzExtracting MNIST_data\t10k-labels-idx1-ubyte.gz第1代1批次,准确率为0.098000第1代2批次,准确率为0.098000第1代3批次,准确率为0.098000第1代4批次,准确率为0.098000第1代5批次,准确率为0.098000第1代6批次,准确率为0.098000第1代7批次,准确率为0.098000
可以看出准确率完全没有变化,准确率没有变化说明cross_entropy没有变,没有进行训练,于是输出每次迭代cross_entropy的值,代码修改如下:
# print('第%d代%d批次,准确率为%.6f' % (epoch + 1, batch + 1, acc)) loss = sess.run(cross_entropy, feed_dict={x_in: mnist.test.images, y_in: mnist.test.labels}) print('第%d代%d批次,loss为%.6f' % (epoch + 1, batch + 1, loss))
运行结果为:
C:\Users\EDZ\PycharmProjects\DY\Scripts\python.exe C:/Users/EDZ/.PyCharm2019.1/config/scratches/tf.pyExtracting MNIST_data\train-images-idx3-ubyte.gzExtracting MNIST_data\train-labels-idx1-ubyte.gzExtracting MNIST_data\t10k-images-idx3-ubyte.gzExtracting MNIST_data\t10k-labels-idx1-ubyte.gz第1代1批次,loss为nan第1代2批次,loss为nan第1代3批次,loss为nan第1代4批次,loss为nan第1代5批次,loss为nan第1代6批次,loss为nan第1代7批次,loss为nan
出现这样的结果是因为,log函数的定义域是x>0,但是传入的数据predicttion可能含有0或者负数,此时就会出现计算结果为NAN的情况,为了避免这个情况就需要用到tf.clip_by_value函数将数据限制在0-1之间,下面是这个函数的介绍:
tf.clip_by_value(t,clip_value_min,clip_value_max,name=None)
t
:Tensor
orIndexedSlices
.clip_value_min
: A 0-D (scalar)Tensor
, or aTensor
with the same shape ast
. The minimum value to clip by.clip_value_max
: A 0-D (scalar)Tensor
, or aTensor
with the same shape ast
. The maximum value to clip by.name
: A name for the operation (optional).
作用:截断tensor数据的值,让数据的值在区间 [clip_value_min , clip_value_max] 内。若tensor的值value<clip_value_min,则返回value=clip_value_min;若clip_value_min<=value<=clip_value_max,则返回value;若clip_value_max<value,则返回clip_value_max。