您当前的位置:首页 > IT编程 > cnn卷积神经网络
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:keras自定义优化器

51自学网 2020-02-28 21:16:12
  cnn卷积神经网络
这篇教程keras自定义优化器写得很实用,希望能帮到您。

keras自定义优化器

自定义一个SGD优化器

from keras.legacy import interfaces
from keras.optimizers import Optimizer
from keras import backend as K
class SGD(Optimizer):
    def __init__(self,lr=0.01,**kwargs):
        super(SGD,self).__init__(**kwargs)
        with K.name_scope(self.__name__):
            self.iterations = K.variable(0, dtype='int64', name='iterations')
            self.lr = K.variable(lr, name='lr')
            
    @interfaces.legacy_get_updates_support
    def get_updates(self, loss, params):
        grads = self.get_gradients(loss, params) #获取梯度
        self.updates = [K.update_add(self.iterations, 1)] # 定义赋值算子集合
        self.weights = [self.iterations] #优化器带来的权重,在保存模型时会被保存
        for p, g in zip(params, grads):
            new_p =p -self.lr*g
            #如果有约束,对参数加上约束
            if getattr(p, 'constraint', None) is not None: 
                new_p = p.constraint(new_p)
            #添加赋值
            self.updates.append(K.update(p, new_p))
        return self.updates
    def get_config(self):
        config = {'lr': float(K.get_value(self.lr))}
        base_config = super(SGD,self).get_config()
        return dict(list(base_config.items()) + list(config.items()))
 

实现“软batch”

假如模型比较庞大,自己的显卡最多也就能跑 batch size=16,但又想起到 batch size=64 的效果,那可以怎么办呢?
每次算 batch size=16,然后把梯度缓存起来,4 个 batch 后才更新参数。也就是说,每个小batch 都算梯度,但每 4 个 batch 才更新一次参数。

class MySGD(Optimizer):
    """
    Keras中简单自定义SGD优化器每隔一定的batch才更新一次参数
    """
    def __init__(self, lr=0.01, steps_per_update=1, **kwargs):
        super(MySGD, self).__init__(**kwargs)
        
        with K.name_scope(self.__class__.__name__):
            self.iterations = K.variable(0, dtype='int64', name='iterations')
            self.lr = K.variable(lr, name='lr')
            self.steps_per_update = steps_per_update #多少batch才更新一次
            
    @interfaces.legacy_get_updates_support
    def get_updates(self, loss, params):
        """
        主要的参数更新算法
        """
        shapes = [K.int_shape(p) for p in params]
        sum_grads = [K.zeros(shape) for shape in shapes] # 平均梯度,用来梯度下降
        grads = self.get_gradients(loss, params) # 当前batch梯度
        self.updates = [K.update_add(self.iterations, 1)] # 定义赋值算子集合
        self.weights = [self.iterations] + sum_grads # 优化器带来的权重,在保存模型时会被保存
        for p, g, sg in zip(params,grads,sum_grads):
            #梯度下降
            new_p = p - self.lr * sg / float(self.steps_per_update)
            
            #如果有约束,对参数加上约束
            if getattr(p, 'constraint', None) is not None:
                new_p = p.constraint(new_p)
            cond = K.equal(self.iterations % self.steps_per_update, 0)
            
            #满足条件才更新参数
            self.updates.append(K.switch(cond, K.update(p, new_p), p))
            
            #满足条件就要重新累积,不满足条件直接累积
            self.updates.append(K.switch(cond, K.update(sg, g), K.update(sg, sg+g)))
            
        return self.updates
    def get_config(self):
        config = {'lr': float(K.get_value(self.lr)),'steps_per_update': self.steps_per_update}
        base_config = super(MySGD, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

“侵入式”优化器


image.png

其中 ,p 是参数向量,g 是梯度, 表示 p 的第 i 次迭代时的结果。
这个算法需要走两步,大概意思就是普通的梯度下降先走一步(探路),然后根据探路的结果取平均,得到更精准的步伐,等价地可以改写为:

image.png

但是实现这类算法却有个难题,要计算两次梯度,一次对参数 ,另一次对参数。而前面的优化器定义中 get_updates 这个方法却只能执行一步(对应到 tf 框架中,就是执行一步 sess.run,熟悉 tf 的朋友知道单单执行一步 sess.run 很难实现这个需求),因此实现不了这种算法。

 

class HeunOptimizer:
    """
    自定义Keras的侵入式优化器
    """
    def __init__(self, lr):
        self.lr = lr
    def __call__(self, model):
        """
        需要传入模型,直接修改模型的训练函数,而不按常规流程使用优化器,所以称为“侵入式”
        下面的大部分代码,都是直接抄自keras的源码:
        https://github.com/keras-team/keras/blob/master/keras/engine/training.py#L491
        也就是keras中的_make_train_function函数
        """
        params = model._collected_trainable_weights
        loss = model.total_loss
        inputs = (model._feed_inputs + model._feed_targets + model._feed_sample_weights)
        inputs += [K.learning_phase()]
        with K.name_scope('training'):
            
            with K.name_scope('heun_optimizer'):
                
                old_grads = [[K.zeros(K.int_shape(p)) for p in params]]
                update_functions = []
                
                for i,step in enumerate([self.step1, self.step2]):
                    updates = (model.updates + step(loss, params, old_grads) + model.metrics_updates)
                    #给每一步定义一个K.function
                    updates = K.function(inputs,[model.total_loss]+model.metrics_tensors,updates=updates,name='train_function_%s'%i,**model._function_kwargs)
                    update_functions.append(updates)
                def F(ins):
                    # 将多个K.function封装为一个单独的函数
                    # 一个K.function就是一次sess.run
                    for f in update_functions:
                        _ = f(ins)
                    return _
                # 最后只需要将model的train_function属性改为对应的函数
                model.train_function = F
    def step1(self, loss, params, old_grads):
        ops = []
        grads = K.gradients(loss, params)
        for p,g,og in zip(params, grads, old_grads[0]):
            ops.append(K.update(og, g))
            ops.append(K.update(p, p - self.lr * g))
        return ops
    def step2(self, loss, params, old_grads):
        ops = []
        grads = K.gradients(loss, params)
        for p,g,og in zip(params, grads, old_grads[0]):
            ops.append(K.update(p, p - 0.5 * self.lr * (g - og)))
        return ops

Pytorch torch.optim优化器个性化使用
【深度学习框架Keras】在小数据集上训练图片分类模型的技巧
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。