Keras框架中的Dense(),是Keras中的core中的一个重要的函数
用来对上一层的神经元进行全部连接,实现特征的非线性组合。
该函数所在windows目录的位置为:\Anaconda3\Lib\site-packages\keras\layers\core.py
(windows环境下配置Keras是在Anaconda3的环境下进行配置,减少了各种依赖包的频繁安装)
详细的可以参考Keras的中文社区文档
keras.layers.core.Dense(units,activation=None,use_bias=True, kernel_initializer='glorot_uniform',bias_initializer='zeros', kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,kernel_constraint=None,bias_constraint=None)
Dense就是常用的全连接层,所实现的运算是output = activation(dot(input, kernel)+bias)。其中activation是逐元素计算的激活函数,kernel`是本层的权值矩阵,bias为偏置向量,只有当use_bias=True
才会添加。
如果本层的输入数据的维度大于2,则会先被压为与kernel相匹配的大小。
#as first layer in a sequential model:
model = Sequential()
model.add(Dense(32, input_shape=(16,))) #input_shape=(16,)等价于input_dim=16
#now the model will take as input arrays of shape (*, 16) ,输入维度=16
#and output arrays of shape (*, 32) ,输出维度=32
#after the first layer, you don't need to specify
#the size of the input anymore:
model.add(Dense(32)) #这一层的input_shape=(32,)
全连接层(Fully Connected layer)就是使用了softmax激励函数作为输出层的多层感知机(Multi-Layer Perceptron),其他很多分类器如支持向量机也使用了softmax。“全连接”表示上一层的每一个神经元,都和下一层的每一个神经元是相互连接的。
卷积层和池化层那个的输出代表了输入图像的高级特征,全连接层的目的就是用这些特征进行分类,类别基于训练集。比如图14所示的图像分类任务,有四种可能的类别。(注意,图14没有显示出所有的神经元节点)
图14:全连接层——每个节点都与相邻层的所有节点相连
除了分类以外,加入全连接层也是学习特征之间非线性组合的有效办法。卷积层和池化层提取出来的特征很好,但是如果考虑这些特征之间的组合,就更好了。*
全连接层的输出概率之和为1,这是由激励函数Softmax保证的。Softmax函数把任意实值的向量转变成元素取之0-1且和为1的向量。
用来将输入shape转换为特定的shape
#as first layer in a Sequential model
model = Sequential()
model.add(Reshape((3, 4), input_shape=(12,)))
#now: model.output_shape == (None, 3, 4) ,None任意正整数
#note: `None` is the batch dimension ,批次大小,就是每次样本数量
#as intermediate layer in a Sequential model ,中间层
model.add(Reshape((6, 2)))
#now: model.output_shape == (None, 6, 2)
#also supports shape inference using `-1` as dimension ,推断inference ,自动判断大小
model.add(Reshape((-1, 2, 2)))
#now: model.output_shape == (None, 3, 2, 2),3*2*2=12
背景
由于在训练神经网络的过程中,每一层的 params是不断更新的,由于params的更新会导致下一层输入的分布情况发生改变,所以这就要求我们进行权重初始化,减小学习率。这个现象就叫做internal covariate shift。
idea思想
虽然可以通过whitening来加速收敛,但是需要的计算资源会很大。
而Batch Normalizationn的思想则是对于每一组batch,在网络的每一层中,分feature对输入进行normalization,对各个feature分别normalization,即对网络中每一层的单个神经元输入,计算均值和方差后,再进行normalization。
对于CNN来说normalize “Wx+b”而非 “x”,也可以忽略掉b,即normalize “Wx”,而计算均值和方差的时候,是在feature map的基础上(原来是每一个feature)
class BatchNormalization(Layer):
'''
Reference:
Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
http://arxiv.org/pdf/1502.03167v3.pdf
mode: 0 -> featurewise normalization
1 -> samplewise normalization (may sometimes outperform featurewise mode)
momentum: momentum term in the computation of a running estimate of the mean and std of the data
'''
def __init__(self, input_shape, epsilon=1e-6, mode=0, momentum=0.9, weights=None):
super(BatchNormalization, self).__init__()
self.init = initializations.get("uniform")
self.input_shape = input_shape
self.epsilon = epsilon
self.mode = mode
self.momentum = momentum
self.input = ndim_tensor(len(self.input_shape) + 1)
self.gamma = self.init((self.input_shape))
self.beta = shared_zeros(self.input_shape)
self.params = [self.gamma, self.beta]
self.running_mean = shared_zeros(self.input_shape)
self.running_std = shared_ones((self.input_shape))
if weights is not None:
self.set_weights(weights)
def get_weights(self):
return super(BatchNormalization, self).get_weights() + [self.running_mean.get_value(), self.running_std.get_value()]
def set_weights(self, weights):
self.running_mean.set_value(floatX(weights[-2]))
self.running_std.set_value(floatX(weights[-1]))
super(BatchNormalization, self).set_weights(weights[:-2])
def init_updates(self):
X = self.get_input(train=True)
m = X.mean(axis=0)
std = T.mean((X - m) ** 2 + self.epsilon, axis=0) ** 0.5
mean_update = self.momentum * self.running_mean + (1-self.momentum) * m
std_update = self.momentum * self.running_std + (1-self.momentum) * std
self.updates = [(self.running_mean, mean_update), (self.running_std, std_update)]
def get_output(self, train):
X = self.get_input(train)
if self.mode == 0:
X_normed = (X - self.running_mean) / (self.running_std + self.epsilon)
elif self.mode == 1:
m = X.mean(axis=-1, keepdims=True)
std = X.std(axis=-1, keepdims=True)
X_normed = (X - m) / (std + self.epsilon)
out = self.gamma * X_normed + self.beta
return out
def get_config(self):
return {"name": self.__class__.__name__,
"input_shape": self.input_shape,
"epsilon": self.epsilon,
"mode": self.mode}
为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时随机断开一定百分比(rate)的输入神经元,Dropout层用于防止过拟合。
上采样,扩大矩阵,可以用于复原图像等。
keras.layers;convolutional.UpSampling2D(size=(2,2),data_format=None)
将数据和行和列分量重复size[0]和size[1]次
keras.layers.convolutional.Conv2D(filters,kernel_size,strdes=(1,1),padding='valid',data_format=None,dilation_rate=(1,1),activation=None,use_bias=True,kernel_initialize='glorot_uniform',bias_initializer='zeros',kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,kernel_constraint=None,bias_constraint=None)
二维卷基层,即对图像的空间域卷积。该层对二维输入进行滑动窗卷积,当使用该层作为第一层时,应提供input_shape参数。例如input_shape = (128,128,3)代表128*128的彩色RGB图像(data_format = ‘channels_last’)
卷积的根本目的是从输入图片中提取特征
model.add(Conv2D(64,5,strides=2,input_shape=(28,28,1),padding='same',activation=LeakyReLU(alpha=0.2)))
其中5为卷积核的大小,共有64个卷积核(卷积核矩阵中的系数设置过程 ),strides=2 表示卷积矩阵每次移动的步长,28/2=14,用大小为5的卷积核移动14次得到的就是一个14*14的矩阵,采用64个不同的卷积核(为了抽取不同的特征)可以得到14*14*64的特征
model.add(Conv2D(128,5,strides=2,input_shape=(28,28,1),padding='same',activation=LeakyReLU(alpha=0.2)))
model.add(Conv2D(256,5,strides=2,padding='same',activation=LeakyReLU(alpha=0.2)))
model.add(Conv2D(512,5,strides=1,padding='same',activation=LeakyReLU(alpha=0.2)))
因为最后一层添加的步长strides=1,所以得到的大小还是4*4,共有512个特征,具体看下图
keras.layers.convolutional.Conv2DTranspose(filters,kernel_size,strides=(1,1),padding='valid',data_format=None, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
我们使用了卷积的倒数,即转置卷积(transposed convolution),从 100 维的噪声(满足 -1 至 1 之间的均匀分布)中生成了假图像。如在 DCGAN 模型中提到的那样,去掉微步进卷积,这里我们采用了模型前三层之间的上采样来合成更逼真的手写图像。在层与层之间,我们采用了批量归一化的方法来平稳化训练过程。以 ReLU 函数为每一层结构之后的激活函数。最后一层 Sigmoid 函数输出最后的假图像。第一层设置了 0.3-0.5 之间的 dropout 值来防止过拟合。具体代码如下。
self.G = Sequential()
dropout = 0.4
depth = 64+64+64+64
dim = 7
# In: 100
# Out: dim x dim x depth
self.G.add(Dense(dim*dim*depth, input_dim=100))
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
self.G.add(Reshape((dim, dim, depth)))
self.G.add(Dropout(dropout))
# In: dim x dim x depth
# Out: 2*dim x 2*dim x depth/2
self.G.add(UpSampling2D()) #扩大一倍14
self.G.add(Conv2DTranspose(int(depth/2), 5, padding='same'))
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
self.G.add(UpSampling2D()) #扩大一倍28
self.G.add(Conv2DTranspose(int(depth/4), 5, padding='same'))
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
self.G.add(Conv2DTranspose(int(depth/8), 5, padding='same')) #这层没有上采样
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
# Out: 28 x 28 x 1 grayscale image [0.0,1.0] per pix
self.G.add(Conv2DTranspose(1, 5, padding='same')) #卷积核数为1,还原为1张图片
self.G.add(Activation('sigmoid'))
self.G.summary()
return self.G
Flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。
model = Sequential()
model.add(Convolution2D(64,3,3,border_mode='same',input_shape=(3,32,32)))
#now:model.output_shape == (None,64,32,32)
model.add(Flatten())
#now:model.output_shape == (None,65536) = 64*32*32,经过这样的处理后,被压平了
keras.layers.advanced_activations.LeakyReLU(alpha=0.3)
LeakyRelU是修正线性单元(Rectified Linear Unit,ReLU)的特殊版本,当不激活时,LeakyReLU仍然会有非零输出值,从而获得一个小梯度,避免ReLU可能出现的神经元“死亡”现象。即,f(x)=alpha * x for x < 0, f(x) = x for x>=0
alpha:大于0的浮点数,代表激活函数图像中第三象限线段的斜率
称为:纠正线性单元(Rectified Linear Unit),是一种非线性操作。其输出如下:
ReLU是以像素为单位生效的,其将所有负值像素替换为0。ReLU的目的是向卷积网络中引入非线性,因为真实世界里大多数需要学习的问题都是非线性的(单纯的卷积操作时线性的——矩阵相乘、相加,所以才需要额外的计算引入非线性)。
其他非线性方程比如tanh或sigmoid也可以替代ReLU,但多数情况下ReLU的表现更好。
空间池化(也称为亚采样或下采样)降低了每个特征映射的维度,但是保留了最重要的信息,空间池可以有多种形式:最大(Max),平均(Average),求和(Sum)
以最大池化为例,我们定义了空间上的邻域(2x2的窗)并且从纠正特征映射中取出窗里最大的元素。除了取最大值以额外,我们也可以取平均值(平均池化)或者把窗里所有元素加起来。实际上,最大池化已经显示了最好的成效。
池化的功能室逐步减少输入表征的空间尺寸。特别地,池化
综上所述,卷积+池化是特征提取器,全连接层是分类器
输入图像是条船,所以目标概率对船是1,其他类别是0
- 输入图像 = 船
- 目标向量 = [0,0,1,0]
详细参见
Step 3: 计算输出层的总误差(4类别之和)
Step 4: 反向传播算法计算误差相对于所有权重的梯度,并用梯度下降法更新所有的滤波器/权重和参数的值,以使输出误差最小化。
以上步骤训练了卷积网络——本质上就是优化所有的权重和参数,使其能够正确地分类训练集里的图片。