本文主要讲述在Keras中,Layer、Node与Tensor在网络构建中的关系。

一些关于keras.engine.base_layer的一些笔记核心的详见github

1.Layer与Node

如上图,考虑一个最简单的网络结构。想了很多方法来表达Layer与Node之间的关系,从我的个人理解来看,这种热狗结构的图最能表达他们之间的关系(真的好像热狗哈哈哈哈 我还特意给画成这个颜色)。

首先我们看keras.engine.base_layer.Layer()中初始化Node()的代码,在__add_inbound_node中(580行):

# Create node, add it to inbound nodes.
Node(
self,
inbound_layers=inbound_layers,
node_indices=node_indices,
tensor_indices=tensor_indices,
input_tensors=input_tensors,
output_tensors=output_tensors,
input_masks=input_masks,
output_masks=output_masks,
input_shapes=input_shapes,
output_shapes=output_shapes,
arguments=arguments
)

对比Node的__init__():

def __init__(self, outbound_layer,
inbound_layers, node_indices, tensor_indices,
input_tensors, output_tensors,
input_masks, output_masks,
input_shapes, output_shapes,
arguments=None):

可以发现,Layer中初始化Node时,将自身(self)传到Node中的outbound_layer,从而完成Node和Layer中的绑定。

随后就是其他的常规参数赋值。

那么Node中的这些参数都代表着什么呢?

在分别讲述各个参数之前,先让他们集合给我们参观一下:

  • outbound_layer
  • inbound_layers
  • node_indices
  • tensor_indices
  • tensor_indices
  • input_tensors
  • output_tensors
  • input_masks
  • output_masks
  • input_shapes
  • output_shapes
  • arguments

需要特别注意的是,除了outbound_layer,其他的参数均有s,说明除了outbound_layer,其他参数均允许多个,为复数形式。

1.1 outbound_layer

这个参数代表当前Node中负责输出的层。比如在上例Node 2中,该层就是Layer B,一个节点中只会有一个层会有输出(每个Node只会绑定一个Layer,但是Layer并不一定只能绑定一个Node,这个稍后会说到),因此output_layer有且只会有一个,因此在Layer中初始化Node时,这个参数会传入self,也就是该层本身。这个比较好理解。

1.2 inbound_layers

理解了上面的outbound_layer,这个inbound_layers也简单多了,就是当前Node的输入层。之所以允许为复数,是因为一个Node(Layer)自然允许多输入的情况(concatenate),上例中,Node 2的inbound_layers=[Layer A]。

1.3 node_indices

上面提到过,每个Node只能绑定一个Layer,但是每个Layer并不是只能绑定一个Node。那么什么情况下会绑定多个Node呢?就是该层为共享层的情况下。因此在Layer下可能有多Node,需要node_indices来指定当前Node是该Layer第几个Node。

在非共享层的情况下,因为每个Layer下只有一个Node,因此这个值为0。

1.4 input_tensors 与 output_tensors

分别表示当前Node(Layer)的输入tensor与输出tensor。在上例中,输入和输出tensor均有一个,在多输入(出)(concatenate)的情况下,会有多个input(output)。

需要注意的是,这些参数是层内所有Node共享的,对于不同的Node,只是用各种index来区分它们。

1.5 tensor_indices

类似于node_indices,只不过这里用来指定当前层的output_tensors中每个tensor的索引,对应多输出的情况。当为单输出时,为0。

1.6 input_shapes 与 output_shapes

分别表示input_tensors 与 output_tensors的shape。

到这里,其实我们可以发现,Node其实就是用来记录与连接Layer的一个工具,用来将多个Layer串联起来并且保存一些基本信息。因此,我觉得“热狗”形状更能够表达Layer与Node之间的关系。

那么上面提到的索引等参数,又是从哪里来的呢?我们观察Layer中的代码,在565行:

# Collect input tensor(s) coordinates.
inbound_layers = []
node_indices = []
tensor_indices = []
for x in input_tensors:
if hasattr(x, '_keras_history'):
inbound_layer, node_index, tensor_index = x._keras_history
inbound_layers.append(inbound_layer)
node_indices.append(node_index)
tensor_indices.append(tensor_index)
else:
inbound_layers.append(None)
node_indices.append(None)
tensor_indices.append(None)

不难发现,对于每一个输入tensor x,会检查它是不是keras_tensor,如果是,则会从_keras_history中获取 inbound_layer, node_index, tensor_index 三个值,分别为Node初始化时输入的对应三个值。

那么Tensor又是什么呢?

2.Tensor和Layer之间的关系

Tensor在Layer之间流动,每一个Tensor都是以一个占位符(placeholder)的形式存在,而每一个Tensor又都包含一些属性,用来描述这个Tensor的来历。

  • inbound_layer
  • node_index
  • tensor_index

三个,它们合起来描述了该Tensor的来源。inbound_layer描述了该Tensor所属的Layer,比如Layer B。node_index描述了Tensor在Layer中是第几个Node。tensor_index则描述了该Tensor在Node中是第几个tensor。根据这三个值,就可以从 Layer->Node->Tensor精确地定位到这个Tensor的由来。

而我们可以看到,Node在这里就起到了担任Tensor和Layer之间的一个桥梁作用。


0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注