从Keras源码看模型实现

从Keras源码看模型实现

本文以Keras自带的examples/addtion_rnn.py为例,theano为后台,分析Keras深度学习框架的源码,梳理模型训练的过程。(由于本人强行学习速成Keras,如有疏漏还望指出。)


从addtion_rnn运行至

model.fit(X_train,y_train,batch_size=BATCH_SIZE,nb_epoch=1,validation_data=(X_val,y_val))

函数(即开始训练)开始,我们来看看发生了什么。
model.fit实际上是class Sequential(Model)中的fit函数,分别再调用继承来的Model.fit函数。
Model.fit函数中比较重要的代码是下面几个部分:

  • self._make_train_function()

    该代码调用了class Model中的_make_train_function函数,这个函数的两个重要功能在于其中调用了

    1. training_updates=self.optimizer.get_updates(self._collected_trainable_weights,self.constraints,self.total_loss)
      其实看到optimizer就可以大概知道这个函数可以追溯到计算梯度和梯度下降,self.optimizer是在compile时赋值的。这个函数间接调用了例如SGD.get_updates一类的递归下降函数,而get_updates函数通过调用optimizer.get_gradients计算梯度之后做一次梯度下降,即反向传播公式。
    2. self.train_function=K.function(inputs,[self.total_loss]+self.metrics_tensors,updates=updates,**self._function_kwargs)
      这个函数要记好,因为虽然_make_train_function在这里结束了,但是这个K.function被赋给了self.train_function,后面仍然会有调用。那么这个函数是做啥子的呢。如果用的backend后端是theano的话,K.function其实就是一个theano.function,而theano.function的格式是这样的function(inputs,outputs,mode=None,updates=None,givens=None...)
      所以这个K.function实际上是根据inputs计算self.total_loss和self.metrics_tensors,对应一个mini-batch的损失和系统评估(至于为什么是基于mini-batch的,后面调用这个函数的时候会讲到)。total_loss函数是在compile函数中定义的因变量,是weighted_loss函数(处理后的损失函数)计算得到的一个mini-batch的output_loss和loss_weight的乘积:

      # compute total loss
      total_loss = None
      for i in range(len(self.outputs)):
          y_true = self.targets[i]
          y_pred = self.outputs[i]
          weighted_loss = weighted_losses[i]
          sample_weight = sample_weights[i]
          mask = masks[i]
          loss_weight = loss_weights_list[i]
          output_loss = weighted_loss(y_true, y_pred,
                                      sample_weight, mask)
          if len(self.outputs) > 1:
              self.metrics_tensors.append(output_loss)
              self.metrics_names.append(self.output_names[i] + '_loss')
          if total_loss is None:
              total_loss = loss_weight * output_loss
          else:
              total_loss += loss_weight * output_loss
      

      如果你在这一段代码之后尝试打印theano.pp(total_loss)就
      懵了,因为屏幕会不断地刷刷刷刷公式,total_loss背后的封装实在是一个复杂的过程。同时,利用这一段代码,我们可以尝试定义一个因变量并打印出y_true和y_pred。

      y_pred的打印结果:

      从Keras源码看模型实现_第1张图片

      y_true的打印结果:

      从Keras源码看模型实现_第2张图片

  • f=self.train_function()

    这一部分把在_make_train_function中的函数也就是K.function赋给了f。

  • return self._fit_loop(f,ins,out_labels=out_labels,batch_size=batch_size,nb_epoch=nb_epoch,verbose=verbose,callbacks=callbacks,val_f=val_f,val_ins=val_ins,shuffle=shuffle,callback_metrics=callback_metrics)

    在这里我们可以看到f被作为参数传入了_fit_loop函数。_fit_loop函数代码比较长在这里不贴出来,同样只讲它重要的部分。
    for batch_index, (batch_start, batch_end) in enumerate(batches)循环开始,是把所有的数据分成mini-batch后存入insbatch,再outs = f(ins_batch),可以看到我们之前的f函数其实也就是K.function函数在这里被调用了。这一步实际上做的就是前向传导,计算一个mini-batch的loss也就是total_loss,并保存在outs中,再贴上标签后存储在batch_logs中,最后返回history给fit,整个训练过程结束。

    尝试打印outs:

    从Keras源码看模型实现_第3张图片


暂时就做笔记做到这里了。

你可能感兴趣的:(深度学习,深度学习,源码,Keras)