前提介绍
本文通过TensorFlow利用Mnist数据集来实现CNN,因为大名鼎鼎的Mnist被收录在了TensorFlow中,所以我们只需要直接调用就可以使用该数据集了。
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets('mnist_data',one_hot=True)#是否为one_hot向量,其中one_hot向量是只有0和1编码的
这里one_hot为True其实就是说每一条数据对应的标签是一个向量。因为是手写数字,数字只有0,1,2,3,4,5,6,7,8,9
相对应的one-hot就是
0: [1,0,0,0,0,0,0,0,0,0]
1: [0,1,0,0,0,0,0,0,0,0]
2: [0,0,1,0,0,0,0,0,0,0]
3: [0,0,0,1,0,0,0,0,0,0]
4: [0,0,0,0,1,0,0,0,0,0]
5: [0,0,0,0,0,1,0,0,0,0]
6: [0,0,0,0,0,0,1,0,0,0]
7: [0,0,0,0,0,0,0,1,0,0]
8: [0,0,0,0,0,0,0,0,1,0]
9: [0,0,0,0,0,0,0,0,0,1]
CNN结构
输入层
因为输入是一个28×28的黑白图像。所以在二维空间上面是28×28。一个像素点相当于一个神经元。那么输入层的维度就是三维[28,28,1]
卷积层
卷积核的尺寸是5×5,也就是滑窗是5×5。深度为32。也就是说filter的个数是32个。相当于32个滑窗扫描图像。那么图像就会变成28×28×32的图像。
池化层
这里使用的是max pooling。池化也是一个滑窗对图像进行扫描。这里的滑窗尺寸是2×2。因为池化的原理经过池化之后图像的尺寸会变小但是厚度不会发生变化,输出变成了14×14×32。
全连接层
Flatten的作用
全连接层的神经元个数为1024
在第五层和第六层之间做了一个flatten(平坦化)操作,也就是说把7×7×64的数据压成[7×7×64,1]。也就是3136个神经元与1024个神经元进行全连接。
第七层还是一个全连接层
因为标签一个one-hot向量,我们的输出应该也是一个1×1×10的向量,这里的unit的个数就是10个。1024个神经元与10个神经元进行全连接。
实现
使用tensorflow搭建神经网络就跟我们小时候搭积木是一样的,不需要关注过多逻辑实现的问题。关注点应该转移到模型的设计上面来,这也是tensorflow这么火的原因。废话不多说我们开始搭积木。
这里要知道使用tensorflow是先“画”出网络结构,之后再进行数据的输入。所以在搭建神经网络的时候不需要关注数据的输入和输出的逻辑。就好比如C\C++这类的非动态语言在真正写业务逻辑之前就要把要用到的变量先定义出来。tensorflow就是先定义出网络结构。
定义输入输出
input_x=tf.placeholder(tf.float32,[None,28*28])/255.
#输出是一个one hot的向量
output_y=tf.placeholder(tf.int32,[None,10])
#None means tensor 的第一维度可以是任意维度
#/255. 做均一化。因为图像像素值是(0~255)如果用原始数据计算出来的东西会很大。
输入层
#输入层 [28*28*1]
input_x_images=tf.reshape(input_x,[-1,28,28,1]) #因为第一个维度不知道是多少,所以我们用-1表示
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
卷积层1
#conv1 5*5*32
#layers.conv2d parameters
#inputs 输入,是一个张量
#filters 卷积核个数,也就是卷积层的厚度
#kernel_size 卷积核的尺寸
#strides: 扫描步长
#padding: 边边补0 valid不需要补0,same需要补0,为了保证输入输出的尺寸一致,补多少不需要知道
#activation: 激活函数
conv1=tf.layers.conv2d(
inputs=input_x_images,
filters=32,
kernel_size=[5,5],
strides=1,
padding='same',
activation=tf.nn.relu
)
池化层1
#tf.layers.max_pooling2d
#inputs 输入,张量必须要有四个维度
#pool_size: 过滤器的尺寸
pool1=tf.layers.max_pooling2d(
inputs=conv1,
pool_size=[2,2],
strides=2
)
卷积层2&池化层2
#conv2 5*5*64
conv2=tf.layers.conv2d(
inputs=pool1,
filters=64,
kernel_size=[5,5],
strides=1,
padding='same',
activation=tf.nn.relu
)
#输出变成了 [?,14,14,64]
#pool2 2*2
pool2=tf.layers.max_pooling2d(
inputs=conv2,
pool_size=[2,2],
strides=2
)
#输出变成了[?,7,7,64]
flat(平坦化)
#flat(平坦化)
flat=tf.reshape(pool2,[-1,7*7*64])
#形状变成了[?,3136]
全连接层
#densely-connected layers 全连接层 1024
#tf.layers.dense
#inputs: 张量
#units: 神经元的个数
#activation: 激活函数
dense=tf.layers.dense(
inputs=flat,
units=1024,
activation=tf.nn.relu
)
dropout层
#dropout
#tf.layers.dropout
#inputs 张量
#rate 丢弃率
#training 是否是在训练的时候丢弃
dropout=tf.layers.dropout(
inputs=dense,
rate=0.5,
)
输出层
#输出层,不用激活函数(本质就是一个全连接层)
logits=tf.layers.dense(
inputs=dropout,
units=10
)
#输出形状[?,10]
网络结构我们搭出来了,就要开始写各种op(操作)。我们已经知道神经网络最重要的就是前向传播和反向传递。
在tensorflow里我们不需要进行复杂的求导然后进行梯度更新,这些tensorflow统统帮我们搞定。
损失
#计算误差 cross entropy(交叉熵),再用Softmax计算百分比的概率
#tf.losses.softmax_cross_entropy
#onehot_labels: 标签值
#logits: 神经网络的输出值
loss=tf.losses.softmax_cross_entropy(onehot_labels=output_y,
logits=logits)
训练
这里使用Adam优化器
#计算误差 cross entropy(交叉熵),再用Softmax计算百分比的概率
#tf.losses.softmax_cross_entropy
#onehot_labels: 标签值
#logits: 神经网络的输出值
loss=tf.losses.softmax_cross_entropy(onehot_labels=output_y,
logits=logits)
准确率
#精度。计算预测值和实际标签的匹配程度
#tf.metrics.accuracy
#labels:真实标签
#predictions: 预测值
#Return: (accuracy,update_op)accuracy 是一个张量准确率,update_op 是一个op可以求出精度。
#这两个都是局部变量
accuracy_op=tf.metrics.accuracy(
labels=tf.argmax(output_y,axis=1),
predictions=tf.argmax(logits,axis=1)
)[1] #为什么是1 是因为,我们这里不是要准确率这个数字。而是要得到一个op
图初始化
#创建会话
sess=tf.Session()
#初始化变量
#group 把很多个操作弄成一个组
#初始化变量,全局,和局部
init=tf.group(tf.global_variables_initializer(),
tf.local_variables_initializer())
sess.run(init)
数据流动
for i in range(20000):
batch=mnist.train.next_batch(50) #从Train(训练)数据集中取‘下一个’样本
train_loss,train_op_=sess.run([loss,train_op],{input_x:batch[0],output_y:batch[1]})
if i%100==0:
test_accuracy=sess.run(accuracy_op,{input_x:test_x,output_y:test_y})
print("Step=%d, Train loss=%.4f,[Test accuracy=%.2f]"%(i,train_loss,test_accuracy))
#测试: 打印20个预测值和真实值 对
test_output=sess.run(logits,{input_x:test_x[:20]})
inferenced_y=np.argmax(test_output,1)
print(inferenced_y,'Inferenced numbers')#推测的数字
print(np.argmax(test_y[:20],1),'Real numbers')
sess.close()
全部代码实现:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 5/21/2018 11:21 AM
# @Author : SkullFang
# @Contact : yzhang.private@gmail.com
# @File : CNN_demo1.py
# @Software: PyCharm
import numpy as np
import tensorflow as tf
#download mnist datasets
#55000 * 28 * 28 55000image
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets('mnist_data',one_hot=True)#参数一:文件目录。参数二:是否为one_hot向量
#one_hot is encoding format
#None means tensor 的第一维度可以是任意维度
#/255. 做均一化
input_x=tf.placeholder(tf.float32,[None,28*28])/255.
#输出是一个one hot的向量
output_y=tf.placeholder(tf.int32,[None,10])
#输入层 [28*28*1]
input_x_images=tf.reshape(input_x,[-1,28,28,1])
#从(Test)数据集中选取3000个手写数字的图片和对应标签
test_x=mnist.test.images[:3000] #image
test_y=mnist.test.labels[:3000] #label
#隐藏层
#conv1 5*5*32
#layers.conv2d parameters
#inputs 输入,是一个张量
#filters 卷积核个数,也就是卷积层的厚度
#kernel_size 卷积核的尺寸
#strides: 扫描步长
#padding: 边边补0 valid不需要补0,same需要补0,为了保证输入输出的尺寸一致,补多少不需要知道
#activation: 激活函数
conv1=tf.layers.conv2d(
inputs=input_x_images,
filters=32,
kernel_size=[5,5],
strides=1,
padding='same',
activation=tf.nn.relu
)
print(conv1)
#输出变成了 [28*28*32]
#pooling layer1 2*2
#tf.layers.max_pooling2d
#inputs 输入,张量必须要有四个维度
#pool_size: 过滤器的尺寸
pool1=tf.layers.max_pooling2d(
inputs=conv1,
pool_size=[2,2],
strides=2
)
print(pool1)
#输出变成了[?,14,14,32]
#conv2 5*5*64
conv2=tf.layers.conv2d(
inputs=pool1,
filters=64,
kernel_size=[5,5],
strides=1,
padding='same',
activation=tf.nn.relu
)
#输出变成了 [?,14,14,64]
#pool2 2*2
pool2=tf.layers.max_pooling2d(
inputs=conv2,
pool_size=[2,2],
strides=2
)
#输出变成了[?,7,7,64]
#flat(平坦化)
flat=tf.reshape(pool2,[-1,7*7*64])
#形状变成了[?,3136]
#densely-connected layers 全连接层 1024
#tf.layers.dense
#inputs: 张量
#units: 神经元的个数
#activation: 激活函数
dense=tf.layers.dense(
inputs=flat,
units=1024,
activation=tf.nn.relu
)
#输出变成了[?,1024]
print(dense)
#dropout
#tf.layers.dropout
#inputs 张量
#rate 丢弃率
#training 是否是在训练的时候丢弃
dropout=tf.layers.dropout(
inputs=dense,
rate=0.5,
)
print(dropout)
#输出层,不用激活函数(本质就是一个全连接层)
logits=tf.layers.dense(
inputs=dropout,
units=10
)
#输出形状[?,10]
print(logits)
#计算误差 cross entropy(交叉熵),再用Softmax计算百分比的概率
#tf.losses.softmax_cross_entropy
#onehot_labels: 标签值
#logits: 神经网络的输出值
loss=tf.losses.softmax_cross_entropy(onehot_labels=output_y,
logits=logits)
# 用Adam 优化器来最小化误差,学习率0.001 类似梯度下降
print(loss)
train_op=tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(loss)
#精度。计算预测值和实际标签的匹配程度
#tf.metrics.accuracy
#labels:真实标签
#predictions: 预测值
#Return: (accuracy,update_op)accuracy 是一个张量准确率,update_op 是一个op可以求出精度。
#这两个都是局部变量
accuracy_op=tf.metrics.accuracy(
labels=tf.argmax(output_y,axis=1),
predictions=tf.argmax(logits,axis=1)
)[1] #为什么是1 是因为,我们这里不是要准确率这个数字。而是要得到一个op
#创建会话
sess=tf.Session()
#初始化变量
#group 把很多个操作弄成一个组
#初始化变量,全局,和局部
init=tf.group(tf.global_variables_initializer(),
tf.local_variables_initializer())
sess.run(init)
for i in range(20000):
batch=mnist.train.next_batch(50) #从Train(训练)数据集中取‘下一个’样本
train_loss,train_op_=sess.run([loss,train_op],{input_x:batch[0],output_y:batch[1]})
if i%100==0:
test_accuracy=sess.run(accuracy_op,{input_x:test_x,output_y:test_y})
print("Step=%d, Train loss=%.4f,[Test accuracy=%.2f]"%(i,train_loss,test_accuracy))
#测试: 打印20个预测值和真实值 对
test_output=sess.run(logits,{input_x:test_x[:20]})
inferenced_y=np.argmax(test_output,1)
print(inferenced_y,'Inferenced numbers')#推测的数字
print(np.argmax(test_y[:20],1),'Real numbers')
sess.close()