1,为什么要做卷积操作
参数少一点,效果也很好,速度快,容易收敛。像下面这个图按10*10卷积后大小变成100*100,需要100个达到1M隐层,只需10M
的parameters
7,卷积方法
def conv2d(input, filter, strides, padding, use_cudnn_on_gpu=True, data_format="NHWC", name=None):
input:输入的tensor对象,会对该值进行卷积操作。默认形状为:[batch_size, height, width, channels], batch_size表示一个批次中的样本数目,height表示一个feature map的高度,width表示一个feature map的宽度,channels表示一个图像的feature map的数量。一般简写为NHWC。当data_format的格式为NHWC的时候,使用默认的形状,如果给给data_format给定位NCHW,那么input的格式为:[batch_size, channels, height, width]
filter: 卷积核Tensor对象(w),格式要求是一个4维的Tensor对象(一般为变量), 格式为: [height, width, in_channels, out_channels],height和width表示窗口的高度宽度,in_channels就是输入对象的通道数目,out_channels给定当前卷积之后的通道数目,也就是我们理论中所说的卷积核的数目。
strides: 给定卷积窗口的滑动步长,格式和data_format有关,是一个数组。格式: [batch, in_height, in_width, in_channels]或者[batch, in_channels, in_height, in_width], 其中batch和in_channels必须为1,in_height和in_width分别给定在高度和宽度上的滑动窗口大小。
padding:是否进行数据的填充操作,可选值:SAME表示进行最小填充,VALID表示不进行填充,直接删除多余的像素值。TensorFlow中有一个特征的值,当步长为1,padding为SAME的时候,卷积是不改变的feature map大小的。
data_format:给定数据格式是NHWC还是NCHW,默认是NHWC
2,卷积核
28*28大小的feature map 有1024个神经元。1个feature map,1024个神经元。 每个神经元提取一个高阶特征
卷积核有大小,输入通道(channel),和输出通道数目(输出channel)
一个卷积核对应一个输出通道。卷积可以增加输出层次,用多个卷积核即可得到多个输出,比如vgg。1*1的卷积相当于合并所有输出featuremap 通道上对应位置的特征值。卷积就是各个通道的信息融合,做的是区域级的特征融合。
3,局部感知
基于人脑的图片识别过程,我们可以认为图像的空间联系也是局部的像素联系比较紧密,而较远的像素相关性比较弱。所以每个神经元没有必要对全局图像进行感知,只要对局部进行感知,而在更高层次对局部的信息进行综合操作得出全局信息
4,神经网络参数
a,一般设置为均值为0,方差为2/n,n为权重数量,的高斯分布随机数列
b,归一化
5,BN理解
求均值,方差,归一化,e(epushion)防止标准差为0的情况,做一个保护,最后阿尔法和beta做一个缩放和移位
6,BN算法
BN一种方法时用tf.layers包下面的方法。一种是用tf.nn包的方法,如下面代码所示
n1 = tf.layers.batch_normalization(n1, training=is_training)
def batch_normalization(net, shape, is_training, moving_decay=0.9, eps=1e-8):
with tf.variable_scope("BN"):
# 缩放参数、平移参数y=gamma * x + beta
gamma = tf.get_variable('gamma', shape=shape[-1],
initializer=tf.constant_initializer(1))
beta = tf.get_variable('beta', shape=shape[-1],
initializer=tf.constant_initializer(0))
# 计算当前批次的均值和标准差
batch_mean, batch_variance = tf.nn.moments(net, axes=(0, 1, 2), keep_dims=True)
# 采用滑动平均计算训练阶段中各个批次的的均值和方差的累加值
# vmean = mean * decay + (1-decay) * batch_mean
# variance = variance * decay + (1-decay) * batch_ariance
ema = tf.train.ExponentialMovingAverage(decay=moving_decay)
# 获取均值和方差
def mean_and_variance_update():
# apply就是触发更新操作
ema_apply_op = ema.apply([batch_mean, batch_variance])
with tf.control_dependencies([ema_apply_op]):
# copy一份返回
return tf.identity(batch_mean), tf.identity(batch_variance)
mean, variance = tf.cond(is_training, mean_and_variance_update,
lambda: (ema.average(batch_mean), ema.average(batch_variance)))
# 执行批归一化操作
return tf.nn.batch_normalization(net, mean, variance,
offset=beta, scale=gamma, variance_epsilon=eps)
8,BN,LN,IN,GN
BN同一批次的所有样本拥有相同的方差和均值
LN 同层神经元输入拥有相同的均值和方差,针对一个样本计算均值和方差
IN 针对每个batch中的数据进行归一化操作。一个样本一个通道计算
GN 相较于BN,批次更小
在CNN中做BN的时候不是以神经元为单位,而是以feature map作为单位的。也就是针对一个批次中(一 个channel)的所有feature map计算一对参数γ、β。这样可以减 少模型参数的数目。
8,过拟合问题
通过加入L2正则和dropout
dropout加载FC1层和FC2层之间
l2_loss加在loss后面,重新赋值loss
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits))
# 获取所有参与模型训练的模型参数(也就是说这些参数会参与优化器的参数更新操作)
trainable_variables = tf.trainable_variables()
l2_loss = tf.constant(0.0, dtype=tf.float32, shape=[])
for variable in trainable_variables:
l2_loss += tf.nn.l2_loss(variable)
loss += regularization_rate * l2_loss
5,模型训练,构建优化器
Adam在深度学习模型中用来替代随机梯度下降
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) 对应参数,自适应学习率
initializer=tf.random_normal_initializer(0, 0.001)会有比较好的收敛
admin算法:https://www.sohu.com/a/156495506_465975
6,学习率衰减
一种是使用tf.train里面的方法,有很多种类型的衰减
tf.train.polynomial_decay 多项式衰减
def fetch_learning_rate(base_learning_rate, step, step_size=100, gamma=0.9):
"""
做一个学习率的衰减
:param base_learning_rate:
:param step:
:param step_size:
:param gamma:
:return:
"""
return base_learning_rate * gamma ** np.floor(step // step_size)
def f3():
num_epoch = tf.Variable(0, name='epoch', trainable=False)
assign_op = tf.assign_add(num_epoch, 1)
base_learning_rate = 0.1
decay_steps = 10
with tf.control_dependencies([assign_op]):
# decayed_learning_rate = learning_rate * exp(-decay_rate * global_step / decay_steps)
# learning_rate:初始学习率
# global_step:当前的训练批次
# decay_steps:衰减周期(每隔多少批次衰减一次)
# decay_rate: 衰减率系数
# staircase:是否做阶梯型的衰减还是连续衰减,默认False为连续衰减
learning_rate1 = tf.train.natural_exp_decay(
learning_rate=base_learning_rate,
global_step=num_epoch,
decay_steps=decay_steps,
decay_rate=0.9,
staircase=False
)
learning_rate2 = tf.train.natural_exp_decay(
learning_rate=base_learning_rate,
global_step=num_epoch,
decay_steps=decay_steps,
decay_rate=0.9,
staircase=True
)
N = 100
y1 = []
y2 = []
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(N):
lr1, lr2 = sess.run([learning_rate1, learning_rate2])
y1.append(lr1)
y2.append(lr2)
plt.plot(y1, 'r-')
plt.plot(y2, 'g-')
plt.show()
7,数据增强的集中方式
水平翻转;随机裁剪;fancy PCA ;增加小众数据;其他平移,旋转,模糊处理,颜色饱和度亮度等处理
8,几种经典卷积神经网络
LeNet-5:
关键学习点,C3层卷积的变种
最终输出的16个fetaure map中,对应的前六个卷积核是和s2中输出的六个feature map中的任意3个feature map做卷积, 中间九个卷积核是和s2中输出的六个feature map中的任意4个feature map做卷积,最后一个卷积核是和六个feature map做卷积
AlexNet
引入局部相应归一化(通过公式可得出,抑制反馈较小的神经元)
对局部神经元的活动创建了竞争机制,使得其中响应比较大的值变得相对更大, 并抑制其它反馈较小的神经元,增强了模型的泛化能力。本质上,LRN是仿造生物学上活跃 的神经元对于相邻神经元的抑制现象(侧抑制)。
ZFNet
基于AlexNet,修改窗口大小,使用Relu激活函数和交叉熵损失函数
VGGNet
引入重复卷积
GoogleNet
引入NIN思想,Network-in-Network,用全连接的多层感知机去代替传统的卷积过程。
引入Inception结构,改变CNN串行结构,改成并行。使用平均池化代替FC层,参数量仅为AlexNet的1/12,用softmax获取平均结果,性能比AlexNet好
而1*1是做的像素级的特征融合,1*1不改变featruemap 大小的,改变通道数的数目,升维或降维。3*3步长为1的卷积也可不改变大小,填充即可
ResNet
引入残差结构,使用shortcut connection,保留了原始数据的特征。resnet主要缓解vgg模型退化问题,vgg在很深的模型上学到的东西太抽象,
DenseNet
在ResNet的基础上,连接更加密集。能缓解梯度消失问题,极大的减少参数量
SeNet
特征压缩。两个fc层,一个降维,一个升维。全连接降维,x=n*c w=c*c/r ,相乘及得到n*c/r,实现了降维,
Residual Attention Networks
基于下采样-上采样的机制, 将特征权重加入到特征图中。
MobileNet
引入depthwise separable convolutions(纵向可分离卷积),分解为深度卷积和点卷积。主要降低参数量和计算量。
relu6的理解。relu是在(x,0)之间取最大值max(x,0),relu6是在max(x,0)和6之间取最小值 min(max(x,0),6)
左边传统卷积;右边深度卷积,再做点卷积,用来做通道间的信息融合。
ShuffleNet
在做group convolution之前,先做一个channel的shuffle操作,以保障信息的表达能力
9,为什么输入数据需要归一化(Normalized Data)?
归一化后有什么好处呢?原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。
对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。
10,rbf
from scipy.linalg import norm, pinv
def _basisfunc(self, c, d):
return np.exp(-self.beta * norm(c - d) ** 2)
# 计算权重(pinv API:计算矩阵的逆) ==> Y=GW ==> W = G^-1Y
self.W = np.dot(pinv(G), Y)
centers = tf.get_variable(name='centers', shape=[rbf_hidden_units, 784], dtype=tf.float32)
gamma = tf.get_variable(name='gamma', shape=[rbf_hidden_units], dtype=tf.float32)
# 进行运行
# 0. RBF系数计算
gamma2 = 2.0 * tf.square(gamma)
# a. 将input_x转换为[None,1,784]的形状,数据不变
x = tf.expand_dims(input_x, axis=1)
# b. 将x进行数据的重复操作,因为每个样本都需要计算到所有中心点的距离
x = tf.tile(x, [1, rbf_hidden_units, 1])
# c. 计算所有样本到所有中心点的距离
dist = tf.reduce_sum(tf.square(x - centers), axis=2)
# d. 做一个sigma的转换
dist = -dist / gamma2
# c. 做一个exp指数转换,得到rbf的输出
rbf_output = tf.exp(dist)
10,初始化器
with tf.variable_scope('net', initializer=tf.random_normal_initializer(0.0, 0.001)):
# 在variable_scope中如果给定了initializer初始化器,那么在使用tf.get_variable来构建变量的时 候,如果没有给定初始化器,那么使用variable_scope中定义的初始化器
11,训练好的模型mat下载
https://www.vlfeat.org/matconvnet/pretrained/