Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

Tensorflow基础学习CNN卷积神经网络

$
0
0
作者: Sam (甄峰) sam_code@hotmail.com

0. 传统神经网络存在的问题:
全连接深度神经网络,顾名思义,就是每个神经元都与相邻层的每个神经元相连接。
以图片为例:
有一组100x100的图片要训练。则输入层拥有100x100=10000个神经元。假设只有一个隐藏层,也是10000个神经元。则W1的shape为[10000, 10000]. 有10000x10000个权值要确认。

我们知道,数据量(样本数)要和网络复杂度相匹配。 
数据少,但网络太复杂,容易过拟合。
数据过多,网络太简单,则容易欠拟合。 
一些人说:数据量要相当于未知权值的5-30倍。 

那10000x10000个未知权值,需要的数据量太大,且计算量也太大。

1. 卷积神经网络的三个基本思想:
A. 局部感受野(Local receptive fields)
B. 权值共享。
C. 池化(pooling)

1.1: 局部感受野:
1962年,哈佛医学眼的几位教授在研究猫眼神经时。发现目标在猫的眼睛不同区域移动时,猫眼神经对应区域会活跃。提出局部感受野概念。
在全连接神经网络中,把输入层的每个神经元都与第一隐藏层的每个神经元相连接。
在CNN中,第一隐藏层的神经元,只与局部区域输入层的神经元相连接。这个局部区域即为局部感受野
可以认为,隐藏层的神经元,学会了只分析它的视野范围内的特征。

举例说明:
MNIST的每张图片大小为28x28. 每个数字代表一个像素。则CNN的输入层有28x28个神经元,第一隐藏层中,每个神经元只与5x5个输入层神经元相连接。隐藏层的每个神经元,都有一个5x5的权值与之对应。

1.2:权值共享:
隐藏层的神经元,权值和偏移值是共享的。
所以可以这样看待:每个权值(例如:5x5)像一个滑动窗, 依次从输入层滑过,把获取的值填入后面的层.
这个滑动窗提测(detect)到的是输入层同一种特征(feature). 在滑动中,把整个输入层的特征记录到后面的隐藏层中了。

一个窗口只能学到一种特征
窗口还可以称为: 卷积核(Kernel),  过滤器(filter).

在图像识别中,光学习一个特征,肯定不够。要学习多个特征,就要有更多的卷积核(kernel)。
卷积操作时,使用不同的卷积核,能得到不同的特征图,多个特征图对分类有极大提升。
例如:可以使用32个卷积核,生成32份特征。 窗口和窗口之间的W和b不是共享的。 32个窗口,就表示有32个W(5x5)矩阵和B。

权值共享可以极大地减少模型参数个数。

1.3:池化(pooling):
池化层通常接在卷积层后面。 池化的目的在于简化卷积层的输出。

例如:pooling窗口为2x2. 采用max pooling. 在卷积层后,每2x2个数据,去除最大值作为池化层的一个数据。

常用的池化有三种:
A: max pooling, 取窗口中最大值。 
B: mean pooling, 取平均值。
C: 随机 pooling.   随机取某一个。

2. 卷积和池化时的padding方式:
卷积的Padding方式:
A. SAME  Padding:
给输入层的周边补0,让特征图的大小与采样图的大小相同。
B. VALID Padding:
输入层的周边不会补0,卷积核不会超出采样图边界。 
所以特征图的大小为: N-n+1.  
N: 采样图长度。 
n: 卷积核长度。

池化的Padding:
池化的Padding与卷积的Padding分类相同,但涵义不同。
A. SAME Padding:
可能会补0. 当采样图的大小不够时,会补0.
B. VALID  Padding:
绝对不会补0. 

3. Data_format:
所谓Data_format,其实是指4维数组的排列方式。它定义了一批图片数据的存储顺序。分NCHW, NHWC两种。Tensorflow中,默认的是NHWC.
N:  Batch数。
H:  Height
W: Width
C:  Channel数目。灰度图:1.  RGB:3

这个问题的实质是:将一张二维张量(h*w)拼接成三维(w*h*c)时,有两种做法:
NHWC: [batch, height, width, channels]
NCHW: [batch, channels, height, width]

例如:
NCHW: [batch, channels, height, width]
[4, 3, 128, 128]: 每份数据为4张图,RGB, 128x128.
[[[R:128x128],[G:128x128],[B:128x128]],
[[R:128x128],[G:128x128],[B:128x128]],
[[R:128x128],[G:128x128],[B:128x128]]
[[R:128x128],[G:128x128],[B:128x128]]]
共有4分,每份中分三个数据块,每个数据块是一个channel的数据。(RGB各个色度的数据集中存放)

NHWC:[batch, height,width, channels]
[4, 128, 128, 3]:每份数据为4张图, 128x128, RGB
[[RGB]....128x128个,
[RGB]....128x128个,
[RGB]....128x128个,
[RGB]....128x128个,
]


4. Tensorflow 中 和CNN相关的几个几个接口:
4.1:卷积:
tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',dilations=[1, 1, 1, 1],
    name=None,
)
使用4-D输入 input(样本), filter(卷积核), 计算一个2-D卷积。

参数讲解:
data_format: 一个string. 有两个选项:"NHWC"/"NCHW". 用来指定输入--input(样本)和输出的数据格式。
input: 一个tensor. 作为输入。 数据类型必须为:half, bfloat16, float32, float64. 它是个4-D Tensor. 维度顺序由data_format决定。
filter: 卷积核。是个4-D Tensor,数据类型与input相同。[filter_height, filter_width, in_channels, out_channels]
strides: 卷积核滑动步长。一个List, 长度为4. 在每个维度上的滑动步长。维度顺序由data_format决定。如果为NHWC,则[1,n,n,1]. 0和3位置一定为1.
padding: 一个string. 卷积的padding模式。“VALID”/ "SAME"

4.2: 池化(Pooling)
tf.nn.max_pool(
    value,
    ksize,
    strides,
    padding,
    data_format='NHWC',
    name=None,
)
在input(value)上执行max pooling.
参数讲解:
value: 一个4-D Tensor. 格式由data_format决定。它将用来被池化。
ksize: 一个List或数组。4个int值。 用来描述各个维度上的池化核的大小.[1, n, n, 1]. 0,3位一定为1.
strides: 一个List或数组。4个int值。 描述每个维度的滑动步长。[1,n,n, 1]
padding: 池化 padding模式。 “VALID”/ "SAME"
data_format: 一个string. 有两个选项:"NHWC"/"NCHW".


5. CNN卷积,池化的实际使用
5.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)

要创建的权值和偏移量,都必须是个Variable. 才可以被训练。
偏移量,可以设定为0.1
权值,则采用正则分布的值。

tf.truncated_normal(
    shape,
    mean=0.0,
    stddev=1.0,
    dtype=tf.float32,
    seed=None,
    name=None,
)
输出random的值,为一段正则分布数据。
shape: shape.
mean:分布的中间值。
stddev:标准偏移量。
seed:种子。

5.2:Tensor的重新设置shape:
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])

x_image = tf.reshape(x, [-1, 28,28,1])
可以看到,x本来分配了一个Nx784的placeholder. 可现在需要把这个2-D Tensor转成 4-D Tensor。
可以使用reshape. 但数据一定要能够对上。 比如None份,就是batch. 
28x28=784. 

5.3:以MNIST为例,查看CNN训练过程
5.3.1:第一层卷积
# 5x5. 1 ch.  32 conv kernel=32 output
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
首先创建第一层隐藏层的权值和偏移量。
卷积核:5x5. 
卷积核个数:32个。 则输出32个特征图。

池化
#h_conv1. [100, 28,28,32]
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
#h_pool1: [100, 14, 14, 32]
h_pool1 = max_pool_2x2(h_conv1)

5.3.2:第二层卷积:
第一层卷积池化后,有32张特征图,每张图大小14x14.

第二层卷积层,卷积核大小为:5x5, 第一层卷积层的输出是32个特征图,此处作为输入,输出则为64个特征图
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])

#h_conv2 [100, 14,14,64]
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
#h_pool2 [100, 7, 7, 64]
h_pool2 = max_pool_2x2(h_conv2)
第二层卷积池化后, 每份数据有64张特征图, 每张图7x7 大小。



两个全连接层,一个神经元个数为1000,另一个为输出层,神经元个数为10.
#全连接
W_fc1 = weight_variable([7*7*64, 1024])# 第一个全连接层, 1024个神经元
b_fc1 = bias_variable([1024])

#
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])

h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

#dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)

#交叉熵代价函数
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=prediction))

train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)


 

Viewing all articles
Browse latest Browse all 158

Trending Articles