深度学习:dropout和BN的实现

文章目录

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

猜你喜欢

转载自blog.csdn.net/weixin_40759186/article/details/86524093