NLP 트랜스포머 코드 스터디 리뷰 common_layers.py
안녕하세요
블레이즈 테크노트
블레이즈 입니다.
Attention is all you need 가 구글에서 발표한 논문인만큼
이 논문에서 사용된 코드가 tensorflow 공식으로 등록되어 있습니다.
아래는 그 코드의 내용입니다.
https://github.com/tensorflow/tensor2tensor
GitHub - tensorflow/tensor2tensor: Library of deep learning models and datasets designed to make deep learning more accessible a
Library of deep learning models and datasets designed to make deep learning more accessible and accelerate ML research. - GitHub - tensorflow/tensor2tensor: Library of deep learning models and data...
github.com
이 사이트를 보면 파일이 아주 많습니다.
오늘은 그 중 common_layers.py 를 함께 살펴보겠습니다.
https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/layers/common_layers.py
common_layers.py는 Transformer 모델 뿐 아니라 다양한 모델에서 사용되는 layer가 정의되어 있습니다.
코드가 4000줄이 넘기 때문에 저희가 앞선 포스팅에서 살펴본
transformer.py 와 transformer_layers.py에서 사용된 모듈 위주로 간단하게 살펴보도록 하겠습니다.
dropout_with_broadcast_dims() : 정규화를 위해 드롭아웃을 하는 함수.
가장 중요한 함수는 layer_postprocess() 아닐까 싶습니다.
아래가 그 코드인데요 hparams 중 필요한 정보를 불러서 layer_prepostprocess()로 넘겨줍니다.
def layer_postprocess(layer_input, layer_output, hparams):
"""Apply layer postprocessing.
See layer_prepostprocess() for details.
A hyperparameters object is passed for convenience. The hyperparameters
that may be used are:
layer_postprocess_sequence
layer_prepostprocess_dropout
norm_type
hidden_size
norm_epsilon
Args:
layer_input: a Tensor
layer_output: a Tensor
hparams: a hyperparameters object.
Returns:
a Tensor
"""
return layer_prepostprocess(
layer_input,
layer_output,
sequence=hparams.layer_postprocess_sequence,
dropout_rate=hparams.layer_prepostprocess_dropout,
norm_type=hparams.norm_type,
depth=None,
epsilon=hparams.norm_epsilon,
dropout_broadcast_dims=comma_separated_string_to_integer_list(
getattr(hparams, "layer_prepostprocess_dropout_broadcast_dims", "")),
default_name="layer_postprocess")
아래는 layer_prepostprocess() 코드입니다.
이 코드는 주어진 순서대로 작업을 수행합니다.
d n a z 중에 작업을 선택할 수 있는데 논문에서 제시된 작업은 dna입니다.
d 를 먼저 해서 dropout을 하고
n 으로 normalization,
a로 add previous_value 로 잔차 연결(residual connection)을 해줍니다.
def layer_prepostprocess(previous_value,
x,
sequence,
dropout_rate,
norm_type,
depth,
epsilon,
default_name,
name=None,
dropout_broadcast_dims=None,
layer_collection=None):
"""Apply a sequence of functions to the input or output of a layer.
The sequence is specified as a string which may contain the following
characters:
a: add previous_value
n: apply normalization
d: apply dropout
z: zero add
For example, if sequence=="dna", then the output is
previous_value + normalize(dropout(x))
Args:
previous_value: A Tensor, to be added as a residual connection ('a')
x: A Tensor to be transformed.
sequence: a string.
dropout_rate: a float
norm_type: a string (see apply_norm())
depth: an integer (size of last dimension of x).
epsilon: a float (parameter for normalization)
default_name: a string
name: a string
dropout_broadcast_dims: an optional list of integers less than 3
specifying in which dimensions to broadcast the dropout decisions.
saves memory.
layer_collection: A tensorflow_kfac.LayerCollection. Only used by the
KFAC optimizer. Default is None.
Returns:
a Tensor
"""
with tf.variable_scope(name, default_name=default_name):
if sequence == "none":
return x
for c in sequence:
if c == "a":
x += previous_value
elif c == "z":
x = zero_add(previous_value, x)
elif c == "n":
x = apply_norm(
x, norm_type, depth, epsilon, layer_collection=layer_collection)
else:
assert c == "d", ("Unknown sequence step %s" % c)
x = dropout_with_broadcast_dims(
x, 1.0 - dropout_rate, broadcast_dims=dropout_broadcast_dims)
return x
때로 전처리 과정에서 드롭아웃이 필요하면 이 함수를 다시 활용하기도 하는 것 같습니다.
다음으로는 ffn 에서 사용되었던 dense_relu_dense() 입니다.
def dense_relu_dense(inputs,
filter_size,
output_size,
output_activation=None,
dropout=0.0,
dropout_broadcast_dims=None,
layer_collection=None,
name=None):
"""Hidden layer with RELU activation followed by linear projection."""
# layer_name is appended with "conv1" or "conv2" in this method only for
# historical reasons. These are in fact dense layers.
layer_name = "%s_{}" % name if name else "{}"
h = dense(
inputs,
filter_size,
use_bias=True,
activation=tf.nn.relu,
layer_collection=layer_collection,
name=layer_name.format("conv1"))
if dropout != 0.0:
h = dropout_with_broadcast_dims(
h, 1.0 - dropout, broadcast_dims=dropout_broadcast_dims)
o = dense(
h,
output_size,
activation=output_activation,
use_bias=True,
layer_collection=layer_collection,
name=layer_name.format("conv2"))
return o
이 함수를 보면 첫 번째 dense 함수에는 인자로 inputs 와 filter_size가 들어가고 activation = tf.nn.relu 가 들어갑니다.
이를 통해 차원을 인풋 차원에서 filter_size 차원으로 늘리고
활성화 함수로 ReLU를 사용합니다.
그리고 이렇게 얻어진 h를 다시 output_size 차원으로 축소합니다.
중간에 활성화 함수로 ReLU를 거치기 때문에 비선형성이 증가합니다.
따라서 모델이 더 복잡한 데이터를 학습하기 좋습니다.
이 외에도 다양한 함수가 있지만 트랜스포머 모델에서 중요하게 사용되는 것은 더 이상 없는 것 같습니다.
트랜스포머에서 위 함수들이 사용되는 부분은 아래 그림과 같습니다.
이것으로 common_layers.py 설명을 마치도록 하겠습니다.
감사합니다.
블레이즈 테크노트
블레이즈.