超简单理解卷积神将网络

人工智能发展到现在,出现了很多很多的神将网络,但是神经网络再多,不变的还是全连接层

全连接层成为了一种通用的数学模型用于回归和分类任务,它本质上就是拟合曲线(决策边界曲线或回归曲线),其拟合曲线的方式是基于连续的定义的,就跟微积分一样,任意一条曲线都可以由无数直线构成,任何曲线在其局部无穷小时都是线性的,而神经网络通过非线性变换产生了很多条这样的直线,这些直线组合起来就构成了目标曲线,对于每一个输入都会最终对应一条直线,这条直线将用于分类或者回归的结果计算,所以可以说神经网络也是基于统计的结果,通常我们看到全连接层都有多层,多层的目的是为了增加直线的数量,但是如果没有**函数的分线性变换,再多的层数也无法增加线性函数的数量,以上说法都是基于relu**函数的解释,对于神将网络还有另一种比较普遍的解释,网络的每一次分线性变换都是一次特征向高维的映射,映射的目的就是为了特征在高维变得线性可分,所以在某个维度这些特征就变得线性可分了,这种理解方式和SVM一样,但是神经网络似乎更加强大,它不需要核函数就能拟合任意曲线,需要说明的是这里所说的曲线并不一定就是xy坐标系的曲线,可能特征有很多维度,那么这个曲线就可能是超体,决策边界可能就是无数超平面,但是不管它有多么复杂都无所谓,我们只需要理解在xy,或者xyz坐标系的情况即可,高维也是一样的道理。

说完了全连接层,接下来就到了卷积神将网络了,卷积神经网络就是由卷积层和全连接层构成。什么是卷积呢,图像中的卷积可不是物理中的卷积,图像本质就是一个数组,可以是1、3、4维等,卷积的操作就是用一个卷积核在图像数组上滑动并进行一些操作,卷积核也就是一个维度和图像一样的数组,但是它一般是方形数组(如3x3、5x5等),滑动的时候是从左上角开始,每次右移或下移n个像素,每到一个位置后用卷积核的数值乘以原图对应的数值然后相加,将结果赋到卷积核中心在原图中对应的位置。

效果图如下:

超简单理解卷积神将网络

超简单理解卷积神将网络

一个图片经过卷积处理可以去燥,变平滑,变模糊,某些特征更加明显等等,所以卷积的目的就是提取特征(滤波),将图片输入到卷积层后,输出的就是图片用于分类或者回归的特征,神将网络的目的就是分类或者回归,对于分类任务,人类在分类事物的时候往往也是根据事物的明显特征,那么神经网络也不例外,如果直接将图片输入到全连接曾进行分类,那么效果不会很好,因为一张图片有很多干扰点,这些干扰点会干扰网络的分类结果,导致有些图效果不佳,网络的泛化能力也差,因此为了解决这个问题,就使用了卷积,在分类之前先将图像输入到卷积层进行重要特征的提取(滤波)清除部分干扰,再送入到全连接网络进行分类,这样正确率就得到了很大的提升。

下面给出一个例子AlexNet:

模型简介

AlexNet中包含了几个比较新的技术点,也首次在CNN中成功应用了ReLU、Dropout和LRN等Trick。同时AlexNet也使用了GPU进行运算加速。

AlexNet将LeNet的思想发扬光大,把CNN的基本原理应用到了很深很宽的网络中。AlexNet主要使用到的新技术点如下:

(1)成功使用ReLU作为CNN的**函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。虽然ReLU**函数在很久之前就被提出了,但是直到AlexNet的出现才将其发扬光大。

(2)训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。

(3)在CNN中使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。

(4)提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。

(5)使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵运算。AlexNet使用了两块GTX 580 GPU进行训练,单个GTX 580只有3GB显存,这限制了可训练的网络的最大规模。因此作者将AlexNet分布在两个GPU上,在每个GPU的显存中储存一半的神经元的参数。因为GPU之间通信方便,可以互相访问显存,而不需要通过主机内存,所以同时使用多块GPU也是非常高效的。同时,AlexNet的设计让GPU之间的通信只在网络的某些层进行,控制了通信的性能损耗。 

(6)数据增强,随机地从256*256的原始图像中截取224*224大小的区域(以及水平翻转的镜像),相当于增加了2*(256-224)^2=2048倍的数据量。如果没有数据增强,仅靠原始的数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能力。进行预测时,则是取图片的四个角加中间共5个位置,并进行左右翻转,一共获得10张图片,对他们进行预测并对10次结果求均值。同时,AlexNet论文中提到了会对图像的RGB数据进行PCA处理,并对主成分做一个标准差为0.1的高斯扰动,增加一些噪声,这个Trick可以让错误率再下降1%。 

AlexNet特点

使用了Relu**函数

Relu函数:

 超简单理解卷积神将网络

超简单理解卷积神将网络图四

基于ReLU的深度卷积网络比基于tanh和sigmoid的网络训练快数倍。

标准化

使用ReLU 后,会发现**函数之后的值没有了tanh、sigmoid函数那样有一个值域区间,所以一般在ReLU之后会做一个normalization,LRU就是稳重提出一种方法,在神经科学中有个概念叫“Lateral inhibition”,讲的是活跃的神经元对它周边神经元的影响。

Dropout

Dropout也是经常说的一个概念,能够比较有效地防止神经网络的过拟合。 相对于一般如线性模型使用正则的方法来防止模型过拟合,而在神经网络中Dropout通过修改神经网络本身结构来实现。对于某一层神经元,通过定义的概率来随机删除一些神经元,同时保持输入层与输出层神经元的个人不变,然后按照神经网络的学习方法进行参数更新,下一次迭代中,重新随机删除一些神经元,直至训练结束。

AlexNet的TensorFlow实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

# -*- coding=UTF-8 -*-  

import sys  

import os  

import random  

import cv2  

import math  

import time  

import numpy as np  

import tensorflow as tf  

import linecache  

import string  

import skimage  

import imageio  

# 输入数据  

import input_data  

mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)  

# 定义网络超参数  

learning_rate = 0.001  

training_iters = 200000  

batch_size = 64  

display_step = 20  

# 定义网络参数  

n_input = 784  # 输入的维度  

n_classes = 10 # 标签的维度  

dropout = 0.8  # Dropout 的概率  

# 占位符输入  

= tf.placeholder(tf.types.float32, [None, n_input])  

= tf.placeholder(tf.types.float32, [None, n_classes])  

keep_prob = tf.placeholder(tf.types.float32)  

# 卷积操作  

def conv2d(name, l_input, w, b):  

    return tf.nn.relu(tf.nn.bias_add( \  

    tf.nn.conv2d(l_input, w, strides=[1111], padding='SAME'),b) \  

    , name=name)  

# 最大下采样操作  

def max_pool(name, l_input, k):  

    return tf.nn.max_pool(l_input, ksize=[1, k, k, 1], \  

    strides=[1, k, k, 1], padding='SAME', name=name)  

# 归一化操作  

def norm(name, l_input, lsize=4):  

    return tf.nn.lrn(l_input, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name)  

# 定义整个网络   

def alex_net(_X, _weights, _biases, _dropout):  

    _X = tf.reshape(_X, shape=[-128281]) # 向量转为矩阵  

    # 卷积层  

    conv1 = conv2d('conv1', _X, _weights['wc1'], _biases['bc1'])  

    # 下采样层  

    pool1 = max_pool('pool1', conv1, k=2)  

    # 归一化层  

    norm1 = norm('norm1', pool1, lsize=4)  

    # Dropout  

    norm1 = tf.nn.dropout(norm1, _dropout)  

   

    # 卷积  

    conv2 = conv2d('conv2', norm1, _weights['wc2'], _biases['bc2'])  

    # 下采样  

    pool2 = max_pool('pool2', conv2, k=2)  

    # 归一化  

    norm2 = norm('norm2', pool2, lsize=4)  

    # Dropout  

    norm2 = tf.nn.dropout(norm2, _dropout)  

   

    # 卷积  

    conv3 = conv2d('conv3', norm2, _weights['wc3'], _biases['bc3'])  

    # 下采样  

    pool3 = max_pool('pool3', conv3, k=2)  

    # 归一化  

    norm3 = norm('norm3', pool3, lsize=4)  

    # Dropout  

    norm3 = tf.nn.dropout(norm3, _dropout)  

   

    # 全连接层,先把特征图转为向量  

    dense1 = tf.reshape(norm3, [-1, _weights['wd1'].get_shape().as_list()[0]])   

    dense1 = tf.nn.relu(tf.matmul(dense1, _weights['wd1']) + _biases['bd1'], name='fc1')   

    # 全连接层  

    dense2 = tf.nn.relu(tf.matmul(dense1, _weights['wd2']) + _biases['bd2'], name='fc2'

    # Relu activation  

    # 网络输出层  

    out = tf.matmul(dense2, _weights['out']) + _biases['out']  

    return out  

   

# 存储所有的网络参数  

weights = {  

    'wc1': tf.Variable(tf.random_normal([33164])),  

    'wc2': tf.Variable(tf.random_normal([3364128])),  

    'wc3': tf.Variable(tf.random_normal([33128256])),  

    'wd1': tf.Variable(tf.random_normal([4*4*2561024])),  

    'wd2': tf.Variable(tf.random_normal([10241024])),  

    'out': tf.Variable(tf.random_normal([102410]))  

}  

biases = {  

    'bc1': tf.Variable(tf.random_normal([64])),  

    'bc2': tf.Variable(tf.random_normal([128])),  

    'bc3': tf.Variable(tf.random_normal([256])),  

    'bd1': tf.Variable(tf.random_normal([1024])),  

    'bd2': tf.Variable(tf.random_normal([1024])),  

    'out': tf.Variable(tf.random_normal([n_classes]))  

}  

# 构建模型  

pred = alex_net(x, weights, biases, keep_prob)  

# 定义损失函数和学习步骤  

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))  

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)  

# 测试网络  

correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))  

accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))  

# 初始化所有的共享变量  

init = tf.initialize_all_variables()  

# 开启一个训练  

with tf.Session() as sess:  

    sess.run(init)  

    step = 1  

    # Keep training until reach max iterations  

    while step * batch_size < training_iters:  

        batch_xs, batch_ys = mnist.train.next_batch(batch_size)  

        # 获取批数据  

        sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, keep_prob: dropout})  

        if step % display_step == 0:  

            # 计算精度  

            acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})  

            # 计算损失值  

            loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})  

            print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc)  

        step += 1  

    print "Optimization Finished!"  

    # 计算测试精度  

    print "Testing Accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images[:256], y: mnist.test.labels[:256], keep_prob: 1.})  

 

以上代码忽略了部分卷积层,全连接层使用了特定的权重。