Tensorflow实现CNN

定义变量 => 搭建网络 => 选定优化目标 => 如何达到目标 => 启动 => 做记录/看效果

定义变量是为了承载网络的输入,tf 里面,可以是 Variable,也可以是 Placeholder,两者的区别是,在开始训练网络时,后者需要你喂给它数据,也就是在 run 的时候都需要加一个 feed_dict = {} 来指定一下输入的数据。

搭建网络比较直接,通过一些 api 就可以构建了。

选定优化目标就是划定一个 loss,就是损失函数,用来监测我的网络预测的值跟真实值之间是否存在代沟。

如何达到目标,就是在已经选定优化目标的前提之下,选择一个怎么达到这个目标的途径/实现办法。比如是要用 sgd 还是 adam 还是怎么地,总得选一个方法来达到目标。

启动,是一个仪式性的步骤,就是写几句代码,符合 tf 的内部运行原理,这部机器才能正确启动。

做必要的记录,通常是设定在一定的步数内,监测网络运行的效果,要怎么 看效果呢,通常伴随着打印一些指标,比如 loss 之类的,进一步地,可以将你关注的指标可视化一下,以更直观地感受一下,你的这部机器现在运行的效果到底咋样,有可能在哪里出现了问题。

在打码的过程中,如果遇到功能不清的情况,可以直接看源代码的解释(或者直接在百度上搜索博主),这样就不容易出错,更清楚他们是干什么用的。

[En]

In the process of typing the code, if you encounter unclear functions, you can directly look at the interpretation of the source code (or directly search bloggers on Baidu), so it will not be easy to make mistakes and have a better understanding of what they are for.

下面是完整的tf实现cnn的代码,注释为查阅资料后所写。

import tensorflow as tf # 导入tensorflow
from tensorflow.examples.tutorials.mnist import input_data # 导入自带数据集

声明两个placeholder,用于存储神经网络的输入,输入包括image和label。这里加载的image是(784,)的shape。
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True) # 设置数据集
x = tf.placeholder(tf.float32,[None, 784]) # placeholder image
y_ = tf.placeholder(tf.float32, [None, 10]) # placeholder label
所以placeholder()函数是在神经网络构建graph的时候在模型中的占位,此时并没有把要输入的数据传入模型,它只会分配必要的内存。
等建立session,在会话中,运行模型的时候通过feed_dict()函数向占位符喂入数据。

----Weight Initialization--- #
One should generally initialize weights with a small amount of noise
for symmetry breaking,
and to prevent 0 gradients.

def weight_variable(shape): # 权重
    # mean,均值,默认0  stddev,标准差,默认1
    # 截断的产生正态分布的随机数,即随机数与均值的差值若大于两倍的标准差,则重新生成
    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)

Convolution and Pooling
Our convolutions uses a stride of one and are zero padded
so that the output is the same size as the input.

Our pooling is plain old max pooling over 2x2 blocks
# padding='SAME'表示使用padding,不改变图片的大小
# same padding:进行填充,允许卷积核超出原始图像边界,并使得卷积后结果的大小与原来的一致
每次滑动的行数和列数称为Stride
卷积过程中,有时需要通过padding来避免信息损失,有时也要在卷积时通过设置的步长(Stride)来压缩一部分信息,或者使输出的尺寸小于输入的尺寸
def conv2d(x, W):
    # tf.nn.conv2d (input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
    return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
def max_pool_2x2(x):
    # h : 需要池化的输入,一般池化层接在卷积层后面,
    # 所以输入通常是feature map,依然是[batch_size, height, width, channels]这样的shape
    # k_size : 池化窗口的大小,取一个四维向量,一般是[1, height, width, 1],
    # 因为我们不想在batch和channels上做池化,所以这两个维度设为了1
    return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

为了神经网络的layer可以使用image数据,我们要将其转化成4d的tensor: (Number, width, height, channels)
To apply the layer, we first reshape x to a 4d tensor,
with the second and third dimensions corresponding to image width and height,
and the final dimension corresponding to the number of color channels.

x_image = tf.reshape(x, [-1,28,28,1])

下面开始搭建CNN结构
#----first convolution layer----#
#the convolution will compute 32 features for each 5x5 patch. Its weight tensor will have a shape of [5, 5, 1, 32].

#The first two dimensions are the patch size,
#the next is the number of input channels, and the last is the number of output channels.

W_conv1 = weight_variable([5,5,1,32]) # 使用32个5x5的filter,然后通过maxpooling。

#We will also have a bias vector with a component for each output channel.

b_conv1 = bias_variable([32])

#We then convolve x_image with the weight tensor, add the bias, apply the ReLU function, and finally max pool.

#The max_pool_2x2 method will reduce the image size to 14x14.

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # relu:将输入小于0的值幅值为0,输入大于0的值不变。
h_pool1 = max_pool_2x2(h_conv1)

使用64个5x5的filter。
#----second convolution layer----#
#The second layer will have 64 features for each 5x5 patch and input size 32.

W_conv2 = weight_variable([5,5,32,64]) # 这里的32是因为第一个卷积层有 32 个卷积核
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

需要将上一层的输出,展开成1d的神经层。
#----fully connected layer----#
#Now that the image size has been reduced to 7x7,
#we add a fully-connected layer with 1024 neurons to allow processing on the entire image
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) # reshape后的数字参数,就是多维度的行列数
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1) + b_fc1)

加入Dropout层,可以防止过拟合问题。注意,这里使用了另外一个placeholder,可以控制在训练和预测时是否使用Dropout。
#-----dropout------#
# tf.nn.dropout 中参数 keep_prob :每一个元素被保存下的概率。
# tf.layer.dropout 中参数 rate :每一个元素丢弃的概率。keep_prob = 1 - rate
# tf.layers.dropout(inputs,rate=0.5,noise_shape=None,seed=None,training=False,name=None)
#To reduce overfitting, we will apply dropout before the readout layer.

#We create a placeholder for the probability that a neuron's output is kept during dropout.

#This allows us to turn dropout on during training, and turn it off during testing.

tf.nn.dropout是tensorflow常用的函数,它的作用是为了减轻过拟合带来的问题而使用的函数,它一般用在每个连接层的输出。
Dropout就是在不同的训练过程中,按照一定概率使得某些神经元停止工作。
也就是说,让每个神经元按照一定的概率停止工作,这个训练过程不更新权值,也不参与神经网络的计算。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>That is, let each neuron stop working according to a certain probability, this training process does not update the weight, nor participate in the calculation of the neural network.</font>*</details>
但它的权重仍然存在,可能会在下一次更新中使用。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>But its weight still exists and may be used in the next update.</font>*</details>
# dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
# **x:**上一层传下载的tensor。(一般用于全连接层后面)
# keep_prob:保留keep_prob的神经元继续工作,其余的停止工作与更新。
# dropout必须设置概率keep_prob,keep_prob应初始化为占位符placeholder。
# train的时候才是dropout起作用的时候,test的时候不应该让dropout起作用。
keep_prob = tf.placeholder(tf.float32)
h_fc1_dropout = tf.nn.dropout(h_fc1, keep_prob) # 每一个元素被保存下的概率。

输出一个线性结果。
#----read out layer----#
# 函数:tf.matmul(a,b)  表示:将矩阵 a 乘以矩阵 b, 生成a * b
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.matmul(h_fc1_dropout, W_fc2) + b_fc2  # y = w * x + b  这里x被dropout

训练和评估
首先,需要指定一个cost function --cross_entropy,
在输出层使用softmax。然后指定optimizer--adam。需要特别指出的是,一定要记得
tf.global_variables_initializer().run()初始化变量
#------train and evaluate----#
# tf.reduce_mean(input_tensor, axis) 函数用于计算张量tensor沿着指定的数轴(tensor的某一维度)上的的平均值,
# 主要用作降维或者计算tensor(图像)的平均值。默认计算所有元素的均值、降低输出结果的维度
# 对于二维2*2的一个tensor,axis=None(得到四个数的平均),axis=0(列方向求mean),axis=1(行方向求mean)
# tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv)
# labels:真实标签,注意是一个onehot向量,且长度跟logits一样长,长度为类别数
# logits:模型最后一层的输出,注意不要过softmax函数,维度为[batch_size,numclass]
# 算出的结果表示真实标签和预测标签之间的差距,使用的是softmax交叉熵来进行计算
# 第一个参数logits:就是神经网络最后一层的输出,
#如果有batch的话,它的大小就是[batchsize,num_classes],单样本的话,大小就是num_classes
# 第二个参数labels:实际的标签,大小同上
第一步是先对网络最后一层的输出做一个softmax,
第二步是softmax的输出向量[Y1,Y2,Y3...]和样本的实际标签做一个交叉熵,
# minimize的内部存在两个操作:(1)计算各个变量的梯度 (2)用梯度更新这些变量的值
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) # 指定optimizer--adam
tf.cast()函数的作用是执行 tensorflow 中张量数据类型转换
tf.argmax(input,axis)根据axis取值的不同返回每行或者每列最大值的索引
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y_, 1), tf.argmax(y_conv, 1)), tf.float32))
Session提供了Operation执行和Tensor求值的环境
with tf.Session() as sess:
    tf.global_variables_initializer().run() # 初始化变量
    # 在你构建完整个模型并在会话中加载模型后,运行这个节点。能够将所有的变量一步到位的初始化,非常的方便
    for i in range(3000):
        batch = mnist.train.next_batch(50) # 通过next_batch来获取下一个数据集来对我们的参数进行调整
        if i % 100 == 0:
            # eval() 其实就是tf.Tensor的Session.run() 的另外一种写法 用来启动计算
            # eval()只能用于tf.Tensor类对象,也就是有输出的Operation。
            # 对于没有输出的Operation, 可以用.run()或者Session.run()。Session.run()没有这个限制。
            train_accuracy = accuracy.eval(feed_dict = {x: batch[0],
                                                       y_: batch[1],
                                                       keep_prob: 1.})
            print('setp {},the train accuracy: {}'.format(i, train_accuracy))
        train_step.run(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 0.5}) # run session

    test_accuracy = accuracy.eval(feed_dict = {x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.})
    print('the test accuracy :{}'.format(test_accuracy))

    saver = tf.train.Saver() # 保存和加载模型 会自动生成4个文件
    path = saver.save(sess, './my_net/mnist_deep.ckpt')
    print('save path: {}'.format(path))

参考文章:

相关函数知识解释:

Original: https://blog.csdn.net/hhr603894090/article/details/122072219
Author: 红颜如霜
Title: Tensorflow实现CNN

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/497628/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球