본 논문은 텍스트 분류 성능을 향상시키기 위해 CNN(Convolutional Neural Network)를 사용한 다양한 feature들을 생성해내고, fine-tunning을 통해 개선한 내용을 담고 있다. 아래의 전체코드는 여기에 참조되어 있다.
Model
1) $n \times k$ representation of sentence
input_x
는 각 문장이 row, 그 문장에서 발생되는 단어들의 시퀀스가 column으로 표현된다.vocab size
가 되며 embedding size
는 위에서 언급한 $k$가 된다.input_x
$ = (\mathbb{x}_1,\mathbb{x}_2,…\mathbb{x}_8 ), \quad \mathbb{x}_i \in i^{th} \text{word index in the vocabulary}$with tf.device('/cpu:0'), tf.name_scope("embedding"):
W = tf.Variable(
tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
name="W")
embedded_chars = tf.nn.embedding_lookup(W, input_x)
embedded_chars_expanded = tf.expand_dims(embedded_chars, -1)
embedded_chars
$\rightarrow$ shape(batch_size
, fixed_length
, W2V_dim
)이다. print(embedded_chars)
<tf.Tensor 'embedding_lookup_5:0' shape=(2, 9, 6) dtype=float32>
tf.nn.conv2d
입력채널 1을 표현하기 위해 하나의 차원을 늘린 embedded_chars_expanded
를 사용하게 된다.
print(embedded_chars_expanded)
<tf.Tensor 'ExpandDims_5:0' shape=(2, 9, 6, 1) dtype=float32>
2) Convolutional layer with mutiple filter widths
filter_shape = [filter_size, embedding_size, 1, num_filters]
W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
conv = tf.nn.conv2d(
self.embedded_chars_expanded,
W,
strides=[1, 1, 1, 1],
padding="VALID",
name="conv")
# Apply nonlinearity
c = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
print(c)
# 위 그림에는 7개로 보이지만 잘못 그린 것 같다. output_size = (9-2)/1+1=8
<tf.Tensor 'conv-maxpool-2_4/relu:0' shape=(2, 8, 1, 4) dtype=float32>
width
,height
,channel_in
, channel_out
]로 생각할 수 있다. (위 그림에서는 아래로 슬라이딩하는데 tf.nn.conv2d
는 왼쪽에서 오른쪽으로 가므로 이것을 고려해준 설정값을 아래와 같다.)
filter_size(filter_height)
는 h(word subsequence)를 의미(위 그림에서 첫번째 필터사이즈는 2)embedding_size(filter_width)
는 k(w2v dim)를 의미(위 그림에서는 6으로 설정됨)channel_in
은 이미지에서 RGB같은 3개의 depth가 존재하지만, 텍스트는 그런 개념이 없기 때문에 1
로 입력채널을 설정해준다.channel_out(num_filters)
은 각 필터 사이즈종류마다 사용된 고정된 필터들의 수이며, 이 값을 4로 정할 경우 필터의 총 개수는 각 필터 종류의 4배로 표현 된다.filter_size
=[2,3]을 설정하면, size 2에 대한 필터가 4개, size 3에 대한 필터가 4개로 생각하면 된다.batch
=1, width
=1, height
=1, depth
=1]의 의미는 한 문장에 대해서(batch=1
), 가로방향에 대해서는 width=1
일지라도 embedding_size만큼의 filter와 input의 overlay이 되기 때문에 움직이지 않는다. 반면에 단어 sequence의 세로방향을 따라 stride를 하게되며(height=1
), 한 sentence는 3차원이 아니므로 depth는 1이다(depth=1
). e.g.이미지에서는 RGB $\rightarrow$ 3 depth를 가짐inputs: 1 2 3 4 5 6 7 8 9 10 11 (12 13)
|________________| dropped
|________________|
pad| |pad
inputs: 0 |1 2 3 4 5 6 7 8 9 10 11 12 13|0 0
|________________|
|_________________|
|______________|
3) Max-over-time pooling
\[\tilde{c} = max\{c\}\]sequence_length - filter_size + 1
)에 가장 큰값을 그 filter의 대표값 하나를 뽑는다.batch
= 1, width
= 1, height
=1, depth
= 1]로 설정하였다.
pooled = tf.nn.max_pool(
c,
ksize=[1, sequence_length - filter_size + 1, 1, 1],
strides=[1, 1, 1, 1],
padding='VALID',
name="pool")
num_filters * len(filter_sizes)
으로 표현된다.(위 그림으로는 정확하게 그리지 않아 식별하기 어려울것 같다.)
num_filters_total = num_filters * len(filter_sizes)
c_pool = tf.concat(pooled_outputs, 3)
c_pool_flat = tf.reshape(c_pool, [-1, num_filters_total])
c_pool
은 Max-over-time pooling이 이루어진 것으로 위에서 3번째 그림을 의미한다.
print(c_pool)
<tf.Tensor 'concat_7:0' shape=(2, 1, 1, 8) dtype=float32>
c_pool_flat
은 tensor의 shape을 2차원으로 바꿔주기 위한 reshape을 적용하였다.
print(c_pool_flat)
<tf.Tensor 'Reshape_1:0' shape=(2, 8) dtype=float32>
4) Fully connected layer with dropout and softmax output