dropout
dropout可以看成是正则化,也可以看成是ensemble
class Dropout(SubLayer):
# self._prob:训练过程中每个神经元被“留下”的概率
def __init__(self, parent, shape, drop_prob=0.5):
if drop_prob < 0 or drop_prob >= 1:
raise ValueError(
"(Dropout) Probability of Dropout should be a positive float smaller than 1")
SubLayer.__init__(self, parent, shape)
# 被“留下”的概率自然是1-被Drop的概率
self._prob = tf.constant(1-drop_prob, dtype=tf.float32)
self.description = "(Drop prob: {})".format(drop_prob)
def _activate(self, x, predict):
# 如果是在训练过程,那么就按照设定的、被“留下”的概率进行Dropout
if not predict:
return tf.nn.dropout(x, self._prop)
# 如果是在预测过程,那么直接返回输入值即可
return x
BN
简单地将每层得到的数据进行上述归一化操作显然是不可行的、因为这样会破坏掉每层自身学到的数据特征。为了使得中心化之后不破坏 Layer 本身学到的特征、BN 采取了一个简单却十分有效的方法:引入两个可以学习的“重构参数”以期望能够从中心化的数据重构出 Layer 本身学到的特征。
class Normalize(SubLayer):
"""
初始化结构
self._eps:记录增强数值稳定性所用的小值的属性
self._activation:记录自身的激活函数的属性,主要是为了兼容图7.17 A的情况
self.tf_rm、self.tf_rv:记录μ_run、σ_run^2的属性
self.tf_gamma、self.tf_beta:记录γ、β的属性
self._momentum:记录动量值m的属性
"""
def __init__(self, parent, shape, activation="Identical", eps=1e-8, momentum=0.9):
SubLayer.__init__(self, parent, shape)
self._eps = eps
self._activation = activation
self.tf_rm = self.tf_rv = None
self.tf_gamma = tf.Variable(tf.ones(self.shape[1]), name="norm_scale")
self.tf_beta = tf.Variable(tf.zeros(self.shape[1]), name="norm_beta")
self._momentum = momentum
self.description = "(eps:{}, momentum:{})".format(eps, momentum)
def _activate(self, x, predict):
if self.tf_rm is None or self.tf_rv is None:
shape = x.get_shape()[-1]
self.tf_rm = tf.Variable(tf.zeros(shape), trainable=False, name="norm_mean")
self.tf_rv = tf.Variable(tf.ones(shape), trainable=False, name="norm_var")
if not predict:
# tf.nn.moments获取原始的均值和误差
_sm, _sv = tf.nn.moments(x, list(range(len(x.get_shape())-1)))
# 定义操作,方便控制依赖
_rm = tf.assign(self.tf_rm, self._momentum*self.tf_rm + (1-self._momentum)*_sm)
_rv = tf.assign(self.tf_rv, self._momentum*self.tf_rv + (1-self._momentum)*_sv)
with tf.control_dependencies([_rm, _rv]):
# 按照算法描述,momentum版本应该必须使用动量更新后的均值和误差
# _norm = tf.nn.batch_normalization(x, _sm, _sv, self.tf_beta, self.tf_gamma, self._eps)
_norm = tf.nn.batch_normalization(x, self.tf_rm, self.tf_rv, self.tf_beta, self.tf_gamma, self._eps)
else:
_norm = tf.nn.batch_normalization(x, self.tf_rm, self.tf_rv, self.tf_beta, self.tf_gamma, self._eps)
# 如果指定了激活函数、就再用相应激活函数作用在BN结果上以得到最终结果
# 这里只定义了ReLU和Sigmoid两种,如有需要可以很方便地进行拓展
if self._activation == "ReLU":
return tf.nn.relu(_norm)
if self._activation == "Sigmoid":
return tf.nn.sigmoid(_norm)
return _norm