[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🛠 기타/Data & AI

뉴스 헤드라인 기반 카테고리 분류하기

inu 2020. 8. 14. 12:56
반응형

데이터 전처리

import pandas as pd

# json 읽어오기
news_data = pd.read_json('../data/News_Category_Dataset_v2.json', lines=True)
news_data = news_data.loc[:, ["category", "headline"]]

# 카테고리 정수 인코딩
category_list = pd.factorize(news_data['category'])[1]
news_data['category'] = pd.factorize(news_data['category'])[0]

# 정규표현식 사용
news_data['headline'] = news_data['headline'].str.replace("[^\w]", " ")
  • 뉴스 카테고리 데이터를 활용한다. (https://www.kaggle.com/rmisra/news-category-dataset)
  • lines=True로 주어 각 줄별로 데이터를 받아올 수 있도록 한다.
  • 우리는 headline을 기반으로 category를 분류하는 기능을 만들 것이기 때문에 그 둘만 받아온다.
  • 판다스의 factorize는 시리즈데이터를 받아 그를 기반으로 [[인덱싱데이터],[각 인덱싱의 의미]]를 반환한다. 신경망학습에 문자열데이터를 사용하기란 매우 어렵다. 따라서 인덱싱한 데이터를 사용하도록 한다. 각 인덱싱의 의미도 추후 확인을 위해 필요하므로 category_list에 따로 저장해놓는다.
  • headline에 구별이 어려운 문자(이모티콘 등)이 존재할 수 있으므로 문자열이 아닌 데이터(^\w)는 정규표현식을 활용해 공백으로 만든다.

학습, 테스트 데이터 생성 및 결과데이터(category) 원핫벡터화

from sklearn.model_selection import train_test_split
from tensorflow.keras.utils import to_categorical

# split하면서 shuffle 적용
news_train, news_test, y_train, y_test = train_test_split(news_data['headline'], news_data['category'], test_size=0.2, shuffle=True, random_state=23)

# 원핫벡터로 만들어줍시다! (num_classes로 카테고리 수 명시 가능)
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
  • train_test_split를 활용해 학습 데이터와 테스트 데이터로 분류한다.
  • category를 인덱싱하여 숫자화했지만, 아직 데이터로 활용하기엔 부적절하다. 신경망이 각 인덱싱에 의미를 부여하여 제대로된 학습을 수행하기란 어렵기 때문이다.
  • 따라서 이를 원핫벡터화한다. to_categorical() 메소드가 이를 간단하게 수행해준다. (인덱싱된 데이터를 모두 원핫벡터화)

입력데이터(headline) 토큰화

# 토큰화 진행
stopwords = ['a', 'an']

X_train = []
for stc in news_train:
    token = []
    words = stc.split()
    for word in words:
        if word not in stopwords:
            token.append(word)
    X_train.append(token)

X_test = []
for stc in news_test:
    token = []
    words = stc.split()
    for word in words:
        if word not in stopwords:
            token.append(word)
    X_test.append(token)
  • 입력데이터도 문자열 그대로인 상태여선 처리할 수 없다.
  • 결과데이터와 마찬가지로 인덱싱화를 해주어야하는데, 입력데이터인 headline은 문장이기 때문에 그대로 인덱싱화해선 안된다.
  • 따라서 각 데이터를 split하여 단어로 나누고, stopwords를 제거한다. (임의로 a와 an만 넣었지만 더 많은 stopword를 제대로 설정해주는 것이 좋다.)

입력데이터 인덱싱

from tensorflow.keras.preprocessing.text import Tokenizer

tokenizer = Tokenizer(25000)
tokenizer.fit_on_texts(X_train)

X_train = tokenizer.texts_to_sequences(X_train)
X_test = tokenizer.texts_to_sequences(X_test)
  • 빈도수별로 25000개의 단어를 인덱싱화한다. (데이터를 살펴보고 일정 빈도수 이상의 단어만 인덱싱되도록 적절한 숫자를 설정해준 것이다.)
  • X_train만 fit하여 이를 기준으로 학습 데이터와 테스트 데이터를 인덱싱화한다.

입력 데이터 패딩

max_len = 15
X_train = pad_sequences(X_train, maxlen=max_len)
X_test = pad_sequences(X_test, maxlen=max_len)
  • headline은 모두 단어갯수가 달라 학습에 어려움이 있다.
  • 따라서 그를 맞춰준다. max_len보다 더 긴 문장은 잘려지고, 더 짧은 문장은 데이터를 0으로 채운다.

모델 생성 및 학습

model = Sequential()
model.add(Embedding(25000, 128))
model.add(LSTM(128))
model.add(Dense(41, activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
model.fit(X_train, y_train, validation_data=(X_test, y_test), batch_size=64, epochs=1)
  • 모델을 만들고 학습 데이터를 기반으로 학습시킨다.
  • Embedding은 각 단어를 word embedding하고, LSTM이 이를 분석, Dense가 결과를 출력한다.
  • 우리는 여러 데이터를 분류할 것이기 때문에 활성화함수는 'softmax'를 사용하고, 출력의 갯수도 41개로 나눈다. 이 레이어의 출력값은 카테고리별 확률이 될 것이다.
  • 빠른 확인을 위해 epochs를 1로 주었지만, 실사용에선 epochs를 적절히 큰 수를 주는 것이 좋다.

결과확인

sentence = input()
token_stc = sentence.split()
encode_stc = tokenizer.texts_to_sequences([token_stc])
pad_stc = pad_sequences(encode_stc, maxlen=15)

score = model.predict(pad_stc)
print(category_list[score.argmax()], score[0, score.argmax()])
  • 사용자로부터 데이터를 받고 그를 모델에 활용할 수 있도록 전처리(split, 인덱싱, 패딩)해준다.
  • 그를 기반으로 결과(각 카테고리에 대한 확률값)을 얻고, 그 중 가장 높은 확률의 인덱스가 어떤 카테고리에 포함되는지 처음에 선언한 category_list를 기반으로 알아낸다.
  • 확률값과 함께 결과를 출력한다.
반응형