本文主要讲解一下 Dense 层的源码,Dense 层即最常用的全连接层,代码很简单,主要是重写了 build
与 call
方法,在我们自定义 Layer 时,也可以参考该层的实现。但是不需要这么复杂,只要写出必要的部分就可以了,参见下一篇博客。
1. Layer 类的相关说明
参考:TensorFlow函数:tf.layers.Layer —— W3Cschool TensorFlow 官方文档
参考:关于 Keras 网络层 —— keras 中文文档
基础层类。这是所有层都继承的类,实现了通用的基础结构功能。层是实现常见神经网络操作的类,例如卷积、批量规范等。这些操作需要管理变量、损失和更新,以及将 TensorFlow 操作应用于输入张量。用户只需实例化它,然后将其视为可调用的。
我们建议 Layer 的子代实现以下方法:
- __init__ ():在成员变量中保存配置
- build():当我们知道输入和 dtype 的形状时,从 __call__ 调用一次。应该有对 add_variable() 的调用,然后调用高级的 build() (设置为 self.built = True,这在用户想要在第一个 __call__ 之前手动调用 build() 时很好)。
- * call():确认 build() 已被调用一次后调用 __call__。实际上应该执行将层应用于输入张量的逻辑(应该作为第一个参数传入)。
2. Dense 源码解读
2.1 __init__ 函数重写
构造方法没什么好说的,就是一些简单的赋值。
from keras.layers import Layer
class Dense(Layer):
def __init__(self, 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,
**kwargs):
if 'input_shape' not in kwargs and 'input_dim' in kwargs:
kwargs['input_shape'] = (kwargs.pop('input_dim'),)
super(Dense, self).__init__(**kwargs)
self.units = units
self.activation = activations.get(activation)
self.use_bias = use_bias
self.kernel_initializer = initializers.get(kernel_initializer)
self.bias_initializer = initializers.get(bias_initializer)
self.kernel_regularizer = regularizers.get(kernel_regularizer)
self.bias_regularizer = regularizers.get(bias_regularizer)
self.activity_regularizer = regularizers.get(activity_regularizer)
self.kernel_constraint = constraints.get(kernel_constraint)
self.bias_constraint = constraints.get(bias_constraint)
self.input_spec = InputSpec(min_ndim=2)
self.supports_masking = True
2.2 build 函数重写
build 方法中定义了两个 Variable 即权重,最后把 built 参数置为 True。
def build(self, input_shape):
assert len(input_shape) >= 2
# 维度取 input_shape 的最后一维
# 正好进行后面的叉乘
input_dim = input_shape[-1]
# 设置权重矩阵,维度为 (input_dim, self.units),用于叉乘
self.kernel = self.add_weight(shape=(input_dim, self.units),
initializer=self.kernel_initializer,
name='kernel',
regularizer=self.kernel_regularizer,
constraint=self.kernel_constraint)
if self.use_bias:
# 设置偏置
self.bias = self.add_weight(shape=(self.units,),
initializer=self.bias_initializer,
name='bias',
regularizer=self.bias_regularizer,
constraint=self.bias_constraint)
else:
self.bias = None
self.input_spec = InputSpec(min_ndim=2, axes={-1: input_dim})
self.built = True
2.3 call 函数重写
call 方法把输入值与 build 方法中定义的权重进行了点积的操作,然后与 build 中的偏移量进行相加,最后经过激活函数返回最终的输出结果。
def call(self, inputs):
# 具体的 矩阵操作
output = K.dot(inputs, self.kernel)
if self.use_bias:
output = K.bias_add(output, self.bias, data_format='channels_last')
if self.activation is not None:
output = self.activation(output)
return output
2.4 compute_output_shape 函数重写
计算出输出tensor的维度并返回。
def compute_output_shape(self, input_shape):
assert input_shape and len(input_shape) >= 2
assert input_shape[-1]
output_shape = list(input_shape)
output_shape[-1] = self.units
return tuple(output_shape)
2.5 get_config 函数重写
保留一些中间值并以字典的形式返回。
def get_config(self):
config = {
'units': self.units,
'activation': activations.serialize(self.activation),
'use_bias': self.use_bias,
'kernel_initializer': initializers.serialize(self.kernel_initializer),
'bias_initializer': initializers.serialize(self.bias_initializer),
'kernel_regularizer': regularizers.serialize(self.kernel_regularizer),
'bias_regularizer': regularizers.serialize(self.bias_regularizer),
'activity_regularizer':
regularizers.serialize(self.activity_regularizer),
'kernel_constraint': constraints.serialize(self.kernel_constraint),
'bias_constraint': constraints.serialize(self.bias_constraint)
}
base_config = super(Dense, self).get_config()
return dict(list(base_config.items()) + list(config.items()))