计算机视觉系列1 LeNet-5网络的思路和简单实现

计算机视觉系列1 LeNet-5网络的思路和简单实现

本文链接:https://blog.csdn.net/weixin_44633882/article/details/86716096

1.LeNet网络简介

LeNet-5是Yann LeCun提出的,对MNIST数据集的分识别准确度能够达到99.2%
LeNet-5网络结构

层名输出
输入层INPUT32×32
卷积层C128×28×6
下采样层S214×14×6
卷积层C310×10×16
下采样层S45×5×16
全连接层C5120
全连接层F684
输出层OUTPUT10

2.LeNet每层网络的计算

(1) INPUT层

  • 输出:32×32×1

(2) C1层(卷积层)

  • 输入:32×32×1 输出:28×28×6 (32-5+1)
  • 卷积核:大小:5×5×6
  • 分析:
    神经元数量:28×28×6
    可训练参数:(5×5+1)× 6 = 156 (卷积核中feature个数+bias)
    连接数:(5×5+1)×6×28×28 = 122304(每个卷积核都有一个bias,每次卷积运算的结果都会加上bias)
    虽然由122304个连接,但只需要学习156个参数。(权值共享)

(3) S2层(下采样层)非池化层

  • 输入:28×28×6 输出:14×14×6
  • 感受野:2×2 (将4个数相加,乘上一个可训练的系数,加上bias,输入sigmoid)
  • 分析:
    神经元数量:14×14×6
    可训练参数:12 (权值+bias)×6
    连接数:(2×2 + 1)×6×14×14 = 5880 (感受野中的四个元素加上bias,很好奇为什么没有加上乘的权重)

(4) C3层(卷积层)

  • 输入:14×14×6 输出:10×10×6 (14-5+1)
  • 卷积核:大小:5×5×16
  • 分析:
    神经元数量:10×10×16
    可训练参数:(5×5×6+1)×16 6×(3×5×5+1)+6×(4×5×5+1)+3×(4×5×5+1)+1×(6×5×5+1) = 1516
    连接数:(5×5+1)×10×10×16 10×10×1516 = 151600(连接数计算应该反向计算,1516是一个feature所需要的连接数)
  • 说明:
    C3中每个feature map不都是与S2中的所有feature map相连接的
    C3这层,将6个14×14的feature map转换为 16个10×10的feature map
    这边使用特殊组合来完成

    列表示C3中的feature map序列号
    行表示S2中的feature map序列号
  • 第一个红框:
    C3中的前6个feature map每个都是与3个S2中的feature map相连接
    可训练参数:(3×5×5+1)×6
    这张图就很容易理解了
  • 第二个红框:
    C3中的6-11的feature map每个都是与4个S2中的feature map相连接
    可训练参数:(4×5×5+1)×6
  • 第三个红框:
    C3中的12-14的feature map每个都是与3个S2中的feature map相连接(不连续的子集)
    可训练参数:(4×5×5+1)×3
  • 第四个红框:
    C3中的15 feature map与S2中所有的feature map相连接
    可训练参数:(6×5×5+1)×1
    为什么要采用上面的连接方案,而不是直接与所有的feature map相连接?
    1.不完全的链接可以减少参数和连接数。
    2.每个特征图都是从原来的特征中提取更抽象的特征,采用这样不对称的链接,可以使提取出不同的更高级的特征。

(5) S4层(下采样层)

  • 输入:10×10×6 输出:5×5×16
  • 感受野:2×2
  • 分析:
    过程和S2的步骤相似
    神经元数量:5×5×16
    可训练参数:32 (2×6)2表示乘的权重加上bias
    连接数: (2×2+1)×5×5×16 = 2000

(6) C5层(卷积层)用卷积层实现全连接层的功能

  • 输入:5×5×16 输出:1×1×120
  • 卷积核:大小:5×5×120
  • 分析:
    可训练参数:(5×5×16 + 1)×120 =48120(每个卷积核对应一个bias)

(7) F6层(全连接层)

  • 输入:1×1×120 输出:1×1×84
  • 计算:向量点积,加上bias,输入tanh激活函数
    tanh函数: f ( a ) = A t a n h ( S a ) f(a)=Atanh(Sa) f(a)=Atanh(Sa)
    A为振幅,S可认为斜率,论文中,A=1.7159
  • 分析:
    可训练参数:(120+1)×84 = 10164

(8) 输出层

输出层由10个欧几里得径向基函数(RBF)构成,每个RBF对应0-9中的一个类别。
RBF函数的输出:
y i = ∑ j ( x j − w i j ) 2 y_i=\sum_{j}{(x_j-w_{ij})^2} yi=j(xjwij)2
X → \overrightarrow{X} X 是F6层输出的向量, x i ∈ X → = { x 1 , . . . , x i , . . , x 84 } x_i∈\overrightarrow{X} = \{x_1,...,x_i,..,x_{84}\} xiX ={x1,...,xi,..,x84}
对于一个数字i,RBF保留一个常向量, w i j ∈ w i → = w i 1 , . . . , w i j , . . . , w i 84 w_{ij}∈\overrightarrow{w_i} = {w_{i1},...,w_{ij},...,w_{i84}} wijwi =wi1,...,wij,...,wi84
RBF函数的输出是输入 X → \overrightarrow{X} X w i → \overrightarrow{w_i} wi 的欧几里得距离。
RBF核的向量 w i → \overrightarrow{w_i} wi 中的元素是+1或-1。这些是人为设置的,这样可以在7*14的位图上将字符表示出来。

3. LeNet简单的代码复现

代码参考《TensorFlow实战Google深度学习框架》
代码结构

  • dataset
    • mnist_data
  • LeNet
    • mnist_inference,py
    • mnist_test.py
    • mnist_train.py
    • model
      • cheakpoint

(1)模型定义(mnist_inference.py)

import tensorflow as tf

INPUT_NODE = 784
OUT_NODE = 10

IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10
# Layer1
CONV1_DEEP = 32
CONV1_SIZE = 5
# Layer2
CONV2_DEEP = 64
CONV2_SIZE = 5
# 全连接层
FC_SIZE = 512


def inference(input_tensor, train, regularizer):
    # input_tensor: [batch, height, width, channels]

    with tf.variable_scope('l1-conv1'):
        # 输入:28*28*1
        # 输出:28*28*32
        # 卷积核(5,5,1,32) padding:same
        conv1_weights = tf.get_variable(
            "weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1)
        )
        conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer=tf.constant_initializer(0.0))
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))

    with tf.name_scope("l2-pool1"):
        # 输入:28*28*32
        # 输出:14*14*32
        pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

    with tf.variable_scope("l3-conv2"):
        # 输入:14*14*32
        # 输出:14*14*64
        # 卷积核(5,5,32,64)
        conv2_weights = tf.get_variable(
            "weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1)
        )
        conv2_biases = tf.get_variable("bias", [CONV2_DEEP], initializer=tf.constant_initializer(0.0))
        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))

    with tf.name_scope("l4-pool2"):
        # 输入:14*14*64
        # 输出:7*7*64
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')


    # 矩阵转换: 将pool2(7*7*64)转换为一维向量,作为后面的输入
    # 输入: (batch_size) 14*14*64
    # 输出: (batch_size, 7*7*64)
    pool_shape = pool2.get_shape().as_list()
    nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
    reshaped = tf.reshape(pool2, [pool_shape[0], nodes])


    with tf.variable_scope('l5-fc1'):
        # 输入: 7*7*64
        fc1_weights = tf.get_variable("weight", [nodes, FC_SIZE],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None:
            # 只有全连接层的权重需要加入正则化
            tf.add_to_collection('loss', regularizer(fc1_weights))
        fc1_biases = tf.get_variable('bias', [FC_SIZE], initializer=tf.constant_initializer(0.1))

        fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
        if train:
            fc1 = tf.nn.dropout(fc1, 0.5)

    with tf.variable_scope('l6-fc2'):
        fc2_weights = tf.get_variable("weight", [FC_SIZE, NUM_LABELS],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None:
            tf.add_to_collection('loss', regularizer(fc2_weights))
        fc2_biases = tf.get_variable("bias", [NUM_LABELS], initializer=tf.constant_initializer(0.1))
        logit = tf.matmul(fc1, fc2_weights) + fc2_biases

    return logit

if __name__ == '__main__':
    pass

(2)模型训练(mnist_train.py)

import sys
sys.path.append(r"C:\Users\11244\Desktop\项目\CNN\LeNet")
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_inference
import os
import numpy as np
import time


BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 6000
MOVING_AVERAGE_DECAY = 0.99

# 模型保存的路径和文件名
MODEL_SAVE_PATH = "model/"
MODEL_NAME = "model.ckpt"


def train(mnist):
    x = tf.placeholder(tf.float32, [
        BATCH_SIZE,
        mnist_inference.IMAGE_SIZE,
        mnist_inference.IMAGE_SIZE,
        mnist_inference.NUM_CHANNELS],
                       name='x-input')
    y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUT_NODE],
                        name='y-input')

    # a function to apply L2 regularization to weights
    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    y = mnist_inference.inference(x, True, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # define the loss function, learningrate, 滑动平均操作 and the process of train
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,
                                                          global_step)
    # 返回关于所有变量的滑动平均op
    variable_averages_op = variable_averages.apply(tf.trainable_variables())
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits=y, labels=tf.argmax(y_, 1))
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('loss'))
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE, LEARNING_RATE_DECAY,
        staircase=True
    )

    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(
        loss, global_step=global_step)

    with tf.control_dependencies([train_step, variable_averages_op]):
        train_op = tf.no_op(name='train')

    saver = tf.train.Saver()
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)

            reshaped_xs = np.reshape(xs, (
                BATCH_SIZE,
                mnist_inference.IMAGE_SIZE,
                mnist_inference.IMAGE_SIZE,
                mnist_inference.NUM_CHANNELS))

            _, loss_value, step = sess.run([train_op, loss, global_step],
                                           feed_dict={x: reshaped_xs, y_: ys})
            # 每1000个epoch保存一次模型
            if i % 1000 == 0:
                print(time.time())
                print("After %d training steps, loss on training batch is %g."
                      % (step, loss_value))
                # 保存格式 如: model.ckpt-1000
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)

def main(argv=None):
    mnist = input_data.read_data_sets("../dataset/mnist_data", one_hot=True)
    train(mnist)

if __name__ == '__main__':
    main()

(3)模型测试(mnist_test.py)

import sys
sys.path.append(r"C:\Users\11244\Desktop\项目\CNN\LeNet")
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_inference
import mnist_train
import os
import numpy as np
import time

def test(mnist):
    with tf.Graph().as_default() as g:
        x = tf.placeholder(tf.float32, [
            mnist.validation.num_examples,  # 第一维表示样例的个数
            mnist_inference.IMAGE_SIZE,  # 第二维和第三维表示图片的尺寸
            mnist_inference.IMAGE_SIZE,
            mnist_inference.NUM_CHANNELS],  # 第四维表示图片的深度,对于RBG格式的图片,深度为5
                           name='x-input')
        y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUT_NODE], name='y-input')

        # feed_dict
        validate_feed = {x: np.reshape(mnist.validation.images, (
        mnist.validation.num_examples, mnist_inference.IMAGE_SIZE, mnist_inference.IMAGE_SIZE,
        mnist_inference.NUM_CHANNELS)),
                         y_: mnist.validation.labels}

        # 网络输出
        y = mnist_inference.inference(x, False, None)
        correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

        variable_averages = tf.train.ExponentialMovingAverage(mnist_train.MOVING_AVERAGE_DECAY)
        print(variable_averages.name)
        variable_to_restore = variable_averages.variables_to_restore()
        saver = tf.train.Saver(variable_to_restore)

        with tf.Session() as sess:
            ckpt = tf.train.get_checkpoint_state(mnist_train.MODEL_SAVE_PATH)
            if ckpt and ckpt.model_checkpoint_path:
                saver.restore(sess, ckpt.model_checkpoint_path)
                global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
                accuracy_score = sess.run(accuracy, feed_dict=validate_feed)
                print("After %s training step(s), validation accuracy = %f" % (global_step, accuracy_score))
            else:
                print("No checkpoint file found")

def main(argv=None):
    mnist = input_data.read_data_sets("../dataset/mnist_data", one_hot=True)
    test(mnist)

if __name__ == '__main__':
    tf.app.run()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值