티스토리 뷰

[업데이트 2018.04.26 10:49]


두번째 요약할 논문은 "Convolutional Neural Networks for Sentence Classification" (https://arxiv.org/abs/1408.5882) 입니다. 


간단한 CNN 모델과 약간의 하이퍼 파라메터 튜닝 및 정적 벡터 사용을 통해 여러 벤처마킹에서 우수한 결과를 보여줬다고 이야기 하고 있습니다. 또한 pre-trained된 정적 벡터(word2vec)를 sentence classification에 fine-tuning하여 사용함으로써 성능 향상을 할 수 있었다고 합니다.(non-static) 추가적으로 아키텍쳐의 간단한 변경을 통해 정적 벡터 및 특정 태스크에 대해 사용이 가능하도록 제안하고 있습니다. 본 논문에서는 감정 분석 및 질문 분류를 포함하는 7개의 태스크 중에 4개의 태스크가 CNN을 사용함으로써 성능이 개선 됨을 확인 할 수 있었습니다. 참고로 word2vec의 경우 Google에서 Google News 1000억개의 단어를 가지고 학습을 시켜 만들어진 단어 벡터입니다.


최근 몇 년간 딥러닝 모델은 컴퓨터 비전(Krizhevsky 외, 2012)과 음성 인식(Graves 외, 2013) 분야에서 주목할 만한 결과를 달성하고 있습니다. 자연어 처리에 있어서 많은 딥러닝 방법들은 자연어 모델을 통한 단어 벡터 표현에 대해 학습(Bengio 외, 2003; Yih 외, 2011; Mikolov 외, 2013)하는 것과 학습된 단어 벡터에 기반한 구성을 수행하는 것 입니다. (Collobert 외, 2011)


CNN 모델은 컴퓨터 비전을 위해 고안 되었지만, 그 이후 자연어 처리에 대해서도 효과적임을 보여주고 있습니다.


결국 본 논문에서 강조하는 것은 word2vec와 같이 pre-trained된 단어 벡터를 가지고 CNN을 통해 학습한 결과 우수한 성능을 얻을 수 있었다는 것입니다. 약간의 하이퍼 파라메터 변경을 했음에도 불구하고, 1개의 레이어만을 가진 간단한 CNN 모델로도 우수한 성능을 보였다는 것이고, NLP 딥러닝 분야에 있어서 결국 pre-trained된 단어 벡터가 중요한 재료인 점을 실험 결과를 통해 제시하고 있습니다.



위의 Figure1에서 알 수 있듯이 CNN 모델은 하나의 레이어(conv->max-pooling)를 가지고 2개의 채널(static, non-static)에 대해 convolution과 max pooling을 수행하고, fully connected 레이어를 통해 dropout/softmax를 적용하여 최종 분류를 수행합니다. 또한 각 채널 별로 convolution의 filter size를 [3, 4, 5] 이렇게 3가지로 구성하여 계산합니다. 따라서 3가지 feature map이 나오고 여기에 max pooling을 수행합니다. 여기서 filter row사이즈는 결국 convolution하려는 단어의 개수(vocabulary size)가 되고, column 사이즈는 단어 임베딩 사이즈가 됩니다.


input부터 word embedding size까지 정리해보면 다음과 같습니다.


- Input data X = [?, 56]

MR(Movie Review) 데이터 기준으로 column size는 56이 나옵니다. 주어진 문장 데이터수는 ?개 주어지고, maximum sequence length(문장 길이)는 56이 됩니다. 아래와 같이 Tensorflow에서 입력된 데이터의 최대 문장 길이를 구한 후. [?, 56] 사이즈로 Input data X를 만듭니다. 여기서 최대 길이에 맞춰 matrix의 나머지 부분은 0으로 채워집니다.

max_document_length = max([len(x.split(" ")) for x in x_text])
vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)
x = np.array(list(vocab_processor.fit_transform(x_text)))

Convolution을 위해 Input Data X는 다음과 같이 변환이 필요합니다.

self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x)
self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)


Tensorflow tf.nn.conv2d 함수의 파라메터중에 input과 filter tensor의 shape를 보면 다음과 같습니다.


Given an input tensor of shape [batch, in_height, in_width, in_channels] and a filter / kernel tensor of shape[filter_height, filter_width, in_channels, out_channels], this op performs the following:


따라서 다음과 같이 변환이 되어야 합니다.


self.input_X = [?, 56]

self.embedded_chars = [?, 56, 300]

self.embedded_chars_expanded = [?, 56, 300, 1]  // [batch, in_height, in_width, in_channels]


- Input data Y = [?, 2]

MR 데이터의 Label은 1 또는 0만 있으므로 [?, 2] 입니다.


- Word Embedding = [18758, 300]

word2vec으로부터 Input data X에 포함되어 있는 vocabulary size를 구할 수 있는데,  MR 데이터의 경우 18758개의 단어로 구성되며. Word Embedding의 차원은 word2vec가 300차원으로 구성되어 있으므로, 300차원으로 정의됩니다.

vocab_size=len(vocab_processor.vocabulary_)

initW = tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0)

- Convolution Filter = [ (3,4,5), 300, 1, 100]

그리고 Convolution을 위한 filter size는 다음과 같습니다. 여기서 filter_size는 논문에서 보면 총 3개(3, 4, 5)로 사용하면 됩니다. 또한 Convolution 결과로 얻는 feature map의 개수는 100개임을 알 수 있습니다. Stride는 1칸식 Convolution을 하기 때문에, [1,1,1,1]이 됩니다.


W = [filter_size, 300, 1, 100]

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")
다음의 공식에 의해 filter_size가 3일때, 다음과 같은 결과를 얻습니다.

Feature Map Size = (in_height - filter_size + 1)/stride
                        = (56 - 3 + 1)/1
                        = 54
conv = conv2d(self.embedded_chars_expanded, W, ...)
        = [?, 54, 1, 100]

여기에 non-linearity activation function으로 ReLU를 적용 후, Max Pooling을 적용합니다.

- Max Pooling = [?, 1, 1, 100]
Max Pooling size로 [1, sequence_size - filter_size + 1, 1, 1]로 지정합니다.
Max pooling window height사이즈는 결국 54가 되고, width는 1이므로 [?, 1, 1, 100]의 output shape를 가지게 됩니다.

- 이제 3,4,5 filter 3개의 데이터를 합친후, Fully-Connected Layer로 만듭니다.
num_filters_total = num_filters * len(filter_sizes)
self.h_pool = tf.concat(pooled_outputs, 3)
self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])
여기서 총 filter 합은 100 * 3이므로, 300이고 됩니다.

num_filters_total = num_filters * len(filter_sizes)

그리고 self.h_pool의 shape는 3개의 결과를 합치게 되므로, [?, 1, 1, 300]이 되고 이걸 Fully-Connected Layer로 만들면, 다음과 같습니다.

self.h_pool_flat = [?, 300]

- 최종적으로 2개의 class로 분류하기 위해
W = [300, 2]
b = [2]

를  앞의 self.h_pool_flat에 적용하게 되면, hypothesis의 shape는 다음과 같습니다.

hypotheiss = [?, 2] // 아래의 self.scores
self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")
- 최종적으로 softmax cross entropy를 loss function으로 사용, L2 loss 적용 후 training을 진행합니다.


구글 word2vec은 CBOW 네트워크 모델로 학습된 word embedding matrix이고, row size는 300만개의 단어수(영어기준)로, column size은 300차원의 embedding size로 구성됩니다. 실제로 word2vec 바이너리 파일 로딩후 첫번째 라인인 헤더를 읽어보면 b'3000000 300\n' 와 같음을 확인 할 수 있습니다. word2vec로부터 입력 데이터의 vocabulary matrix를 통해 최종적으로 n(vocabulary size) x k(embedding size)의 word embedding matrix를 weight의 초기값으로 학습을 시작하게 됩니다.


하이퍼 파라메터의 경우 filter window size를 [3, 4, 5] 3가지를 사용하며 100개의 feature map을 생성하도록 하였고, drop out 비율은 0.5, L2 regularization은 3, mini-batch size는 50으로 설정하였습니다. yoon kim님의 github에 theano로 구현된 코드를 보면 epoch은 25로 되어 있습니다. 


* 참고: https://github.com/yoonkim/CNN_sentence/blob/master/conv_net_sentence.py


또한 activation function으로 non-linear function인 ReLU를 사용하였습니다. 학습 데이터는 training/test로 구분되어 있는 경우는 각각을 학습과 평가를 위해 사용하고 구분이 없는 데이터셋의 경우 90%는 학습용으로 10%는 평가용으로 사용하였습니다. 그리고 SGD를 사용하였고(shuffled mini-batch), optimizer로 Adadelta를 사용하였다고 합니다.


논문이 제안한 CNN모델별로 차이는 다음과 같습니다.


- CNN-rand : n x k의 word embedding matrix를 random한 range(-a,a)에서 초기화 및 학습을 시킵니다. 

- CNN-static : n x k의 word embedding matrix를 word2vec로부터 초기화 및 해당 weight에 대해서 학습을 진행하지 않습니다.

- CNN-non-static : n x k의 word embedding matrix를 word2vec로부터 초기화 및 해당 weight에 대해서 학습을 진행합니다.

- CNN-multichannel : 2개의 static, non-static n x k word embedding matrix를 사용하며, 두가지 결과를 합쳐서 진행합니다. 

  (static 학습 미허용, non-static 학습 허용)


multichannel 모델을 사용하는 것이 single channel 모델을 사용하는 것보다 overfitting을 방지하고, 특히 적은 데이터셋에서 더 좋은 성능을 예상하였으나, 테스트 결과 테이블2의 벤치마킹 결과에서 볼 수 있듯이 multichannel 모델이 모든 데이터셋에서 좋은 성능을 보이진 않고 있습니다.


Static과 Non-static 결과를 보았을 때 SST-2 데이터셋 테스트 결과에서 알 수 있듯이 기존의 word2vec가 학습됨으로써 단어간에 좀 더 의미있는 것끼리 cosine similarity가 개선됨을 알 수 있습니다. 예를 들면 word2vec에서는 good은 great과 유사도가 가장 높은데, Non-static으로 word2vec를 학습 후 nice와 더 유사도가 높은 것을 알 수 있습니다. 감정 표현에 있어서 good-great보다 good-nice가 더 의미가 있기 때문입니다. 또한 느낌표(->감탄사)와 쉼표(->접속사)의 경우 좀 더 그 의미에 맞게 학습된 것을 확인 할 수 있습니다.


기존 Max-TDNN과 같은 Neural Network보다 CNN 모델이 더 좋은 성능을 보여줌을 확인했고, dropout 적용시 2-4% 상대적으로 성능 향상이 있었다고 합니다. word vector를 랜덤하게 초기화시 pre-trained된 것과 동일한 variance로 지정시 약간의 성능 향상을 얻을 수 있었다고 합니다. (U[-a, a] 초기화) 앞서 이야기 했듯이 word2vec를 사용함으로써 좋은 성능을 낼수 있었는데, 다만 Mikolov 외.(2013)의 아키텍쳐의 영향인지 아니면 1000억개의 데이터로 이루어진 Google News 데이터셋 때문인지 확실 하지는 않다고 합니다. 또한 Optimizer의 경우 Adadelta (Zeiler, 2012)를 조금 더 epoch수를 늘려 사용함으로써 Adagrad (Duchi 외, 2011)와 비슷한 결과를 보여주었다고 합니다.


* GitHub : https://github.com/asyncbridge/deep-learning/tree/master/CNNSentenceClassification

* 참고

[1] https://arxiv.org/abs/1408.5882/

[2] http://www.wildml.com/2015/12/implementing-a-cnn-for-text-classification-in-tensorflow/

[3] https://github.com/dennybritz/cnn-text-classification-tf/

[4] https://github.com/yoonkim/CNN_sentence/

[5] https://github.com/cahya-wirawan/cnn-text-classification-tf/

[6] https://github.com/harvardnlp/sent-conv-torch/

[7] https://github.com/mmihaltz/word2vec-GoogleNews-vectors/

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함