슬기로운 연구생활

Classification - [2] 분류란? 본문

슬기로운 NLP 생활

Classification - [2] 분류란?

vhrehfdl 2020. 8. 22. 15:28

이전 글

[1] 자연어처리란?

 

들어가며

이전 글에서는 자연어처리 정의와 Task에 대해 알아보았습니다.

자연어처리의 Task는 크게 Classification과 Generation로 구분될 수 있습니다.

이번 글에서는 Classification의 전체 흐름과 구현 방법에 대해 알아보겠습니다.

 

Classification 이란 무엇인가?

Classification은 Input 값을 특정 Class로 분류하는 것을 의미합니다.

Classification은 간단하지만 다양한 분야에 적용할 수 있습니다.

- 영화 리뷰 긍부정 분류

- 가짜 뉴스 분류

- 질문의 의도 분류

- 사용자의 발화 의도 분류

 

Classification으로 챗봇까지 구현할 수 있습니다.

 

Chat1 : 오늘 밥 먹자

Chat2 : 좋아 뭐 먹을래?

Chat3 : 스테이크 먹자!

Label : 1

 

Chat1 : 오늘 밥 먹자

Chat2 : 애버랜드 갈까?

Chat3 : 노트북 샀어

Label : 0

 

이와 같이, Classification은 응용하기에 따라 정말 다양한 일을 할 수 있습니다.

 

Overall Architecture

Classification의 전체 구조는 위의 그림과 같이 구분될 수 있습니다.

전체 코드는 Github에 공개되어 있습니다.

https://github.com/vhrehfdl/Blog_code/blob/master/%EC%8A%AC%EA%B8%B0%EB%A1%9C%EC%9A%B4%20%EC%9E%90%EC%97%B0%EC%96%B4%EC%B2%98%EB%A6%AC/%EC%8A%AC%EA%B8%B0%EB%A1%9C%EC%9A%B4_%EC%9E%90%EC%97%B0%EC%96%B4%EC%B2%98%EB%A6%AC_%5B2%5D_Classification_Task.ipynb

 

vhrehfdl/Blog_code

Contribute to vhrehfdl/Blog_code development by creating an account on GitHub.

github.com

 

1. Load Data

Load Data는 학습에 사용할 데이터를 불러오는 과정입니다. 

데이터는 Train(학습), Validation(검증), Test(평가)로 구분됩니다.

각 데이터는 목적에 따라 엄격하게 구분되어 사용되어야 합니다.

 

Train(학습) 데이터는 학습에만 사용이 되며 검증 과정이나 평가 과정에 사용되면 안됩니다.

Validation(검증) 데이터는 Train(훈련) 데이터를 사용해, 한 epoch 학습이 끝났을 때 모델의 성능을 평가합니다.

Test(평가) 데이터는 가장 성능이 좋은 모델의 최종 성능을 평가할 때 사용합니다.

def load_data(train_dir, test_dir):
    train = pd.read_csv(train_dir)
    test = pd.read_csv(test_dir)

    train, val = train_test_split(train, test_size=0.1, random_state=42)

    train_x, train_y = train["text"], train["label"]
    test_x, test_y = test["text"], test["label"]
    val_x, val_y = val["text"], val["label"]

    return train_x, train_y, test_x, test_y, val_x, val_y

 

2. Pre Processing

Sentence 혹은 Document 같은 Sequence 데이터를 단어(Token)로 분리하고, 품사 태깅(POS Tagging)을 하거나 원형 복원(Stemming)을 하는 등의 전처리 작업을 하는 과정입니다.

 

한국어에서 한 문장은 여러개의 형태소로 구성되어져 있고, 영어에서는 단어들로 구성되어 있습니다.

이러한 단어들의 집합 중, 학습에 불필요한 단어들을 제외해 성능을 높이는 과정이 Pre Processing입니다. 

 

각 데이터들에 대해 Preprocessing 과정을 거치면 "정말 재미없다라" -> "정말", "재미없", "더라"로 분류 될 수 있습니다.

만약 여기에 주요 품사인 동사만 사용한다고 가정하면, VA인 "재미없"만 데이터로 정제해 사용할 수 있습니다.

품사외에도 다양한 전처리 방법이 존재하며, 다른 프로젝트 실습을 소개할 때 적용하는 예를 보여드리겠습니다.

def pre_procissing(train_x, test_x, val_x):
    CHARS_TO_REMOVE = r'!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n“”’\'∞θ÷α•à−β∅³π‘₹´°£€\×™√²—'

    train_x = train_x.tolist()
    test_x = test_x.tolist()
    val_x = val_x.tolist()

    tokenizer = text.Tokenizer(filters=CHARS_TO_REMOVE)
    tokenizer.fit_on_texts(train_x + test_x + val_x)  # Make dictionary

    # Text match to dictionary.
    train_x = tokenizer.texts_to_sequences(train_x)
    test_x = tokenizer.texts_to_sequences(test_x)
    val_x = tokenizer.texts_to_sequences(val_x)

    temp_list = []
    total_list = list(train_x) + list(test_x) + list(val_x)

    for i in range(0, len(total_list)):
        temp_list.append(len(total_list[i]))

    max_len = max(temp_list)

    train_x = sequence.pad_sequences(train_x, maxlen=max_len, padding='post')
    test_x = sequence.pad_sequences(test_x, maxlen=max_len, padding='post')
    val_x = sequence.pad_sequences(val_x, maxlen=max_len, padding='post')

    return train_x, test_x, val_x, tokenizer

 

3. Text To Vector

Text To Vector는 Pre Processing 과정을 거친 Token들을 Vector 값으로 바꿔주는 과정입니다.

 

Machine Learning 혹은 Deep Learning을 적용하기 위해서는 텍스트가 아닌 숫자값이 필요합니다.

"hi", "안녕"과 같은 텍스트에는 수식을 적용할 수 없기 때문에, 텍스트를 숫자 값으로 변경해주는 작업이 반드시 필요합니다.

이 작업이 Text To Vector 과정이며 다양한 기법이 존재합니다.

 

Word2vec과 같은 Word Representation 기법을 적용하면 "정말", "재미없", "더라" 라는 문장은 

정말 -> [ 0.2, 2.91 ...  3.1, 2.4 ]

재미없 -> [ 3.2, 1.391 ...  2.191, 8.14 ]

더라 -> [ 1.987, 0.4211 ...  3.211, 7.324 ]

과 같이 2차원 행렬(MxD)로 변환 될 수 있습니다.

 

여기서 M 값은 보통 데이터의 최대 길이로 설정합니다. 만약 M을 5로 설정하면 위에 데이터와 같이 "정말", "재미없", "더라" 3개의 Token을 가진 데이터는 나머지 2개를 <PAD>로 채워 강제로 형태를 맞춥니다. 딥러닝 입력층에 노드개수가 고정되어 있기 때문에 M의 크기는 고정되어야 합니다.

 

Text To Vector는 Pre-Trained된 Vector를 사용하는 방법과 자체적으로 학습하여 Vector를 생성하는 방법이 존재합니다. Embedding 방법 또한 Word2vec, Fasttext, Glove, ELMo, BERT와 같은 다양한 방법이 있습니다. 이번 Classification 시리즈에서는 Word Representation 방법의 큰 흐름을 살펴볼 것입니다.

def get_coefs(word, *arr):
    return word, np.asarray(arr, dtype='float32')


def load_embeddings(path):
    with open(path, encoding="utf-8") as f:
        return dict(get_coefs(*line.strip().split(' ')) for line in f)


# Enter the Vector value that matches the token into the variable.
def text_to_vector(word_index, path, word_dimension):
    model = gensim.models.KeyedVectors.load_word2vec_format(path, binary=True)

    embedding_matrix = np.zeros((len(word_index) + 1, word_dimension))
    for word, i in word_index.items():
        try:
            embedding_matrix[i] = model.vectors[model.wv.vocab.get(word).index]
        except:
            pass

    return embedding_matrix

 

4. Build Model

모델은 통계기반의 Machine Learning Model과 신경망 기반의 Deep Learning Model을 적용할 수 있습니다. 여Keras를 이용해서 구현했으며 제 다른 github repo를 보면 pytorch로도 구현해두었습니다 .

def build_model(size, vocab_size):
    ### Hyper Parameter
    embedding_dim = 300
    hidden_units = 64

    ### Model Architecture
    input_layer = Input(shape=(size,))

    embedding_layer = Embedding(vocab_size, embedding_dim)(input_layer)

    dense_layer = Flatten()(embedding_layer)
    hidden_layer = Dense(hidden_units, activation='sigmoid')(dense_layer)
    output_layer = Dense(1, activation='sigmoid')(hidden_layer)

    model = Model(inputs=input_layer, outputs=output_layer)
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    model.summary()

    return model

 

5. Evaluation

(MxD) 형태의 데이터는 모델의 학습을 거쳐 최종 Output Layer에서 클래스 개수 만큼 확률 값을 출력합니다.

만약 클래스가 2개인 이진 분류일 경우에는 Output Layer의 노드 개수를 하나만 설정합니다.

노드에서 출력되는 Prediction 값이 0.5 이상이면 1, 0.5 미만이면 0으로 분류합니다.

 

클래스가 2개를 초과 할 경우, 노드 개수를 각 Class 개수만큼 설정합니다.

각 노드에서 출력되는 Prediction 값에 Softmax를 적용해 가장 큰 값을 1로 변환합니다.

ex) [0.1 0.2 0.5 0.1 0.1] -> [0 0 1 0 0] -> 3.

def evaluate(model, test_x, test_y):
    prediction = model.predict(test_x)
    y_pred = (prediction > 0.5)

    accuracy = accuracy_score(test_y, y_pred)
    print("Accuracy: %.2f%%" % (accuracy * 100.0))
    print(classification_report(test_y, y_pred, target_names=["0", "1"]))

 

마무리

이번 포스팅에서는 Classification Task의 전체 흐름에 대해서 알아보았습니다.

Load Data는 데이터를 불러오는 부분이기 때문에 따로 자세하게 설명하지 않겠습니다.

다음 포스팅부터는 Pre Processing 방법들에 대해 자세히 살펴보도록 하겠습니다. 

 

Comments