※ 해당 내용은 Pytorch로 시작하는 딥러닝 입문을 참고했습니다.
이전 토크나이징에 대한 내용은 아래 참조
2021.05.11 - [Study/NLP] - [NLP/자연어처리 ]자연어 처리 전처리(2) - 분절(토큰화) 라이브러리 소개
자연어 처리 분석을 하기 위해서는 단어 집합, 즉 단어 사전이 필요하다.
오늘은 하나의 데이터로 단어의 집합을 만들고, 고유정수를 부여(indexing) 하고 문장의 길이만큼 패딩(padding)하는 방법에 대해 적어보려 한다.
1. 단어집합(vocabulary)
단어 집합(vocabulary)이란 중복을 제거한 텍스트의 총 단어의 집합(set)을 의미한다. 여러개의 코퍼스를 분절하고, unique한 단어들만 모아서 집합의 형태로 만드는 것이다. 아래 예제를 통해 실습해본다.
Pytorch로 시작하는 딥러닝 입문에서 네이버 영화 리뷰 데이터를 다뤄서 동일한 데이터로 다뤘다. 네이버 영화 리뷰 데이터는 총 20만개의 영화리뷰를 긍정 1, 부정 0으로 레이블링한 데이터다.
import urllib.request
import pandas as pd
from konlpy.tag import Mecab
from nltk import FreqDist
import numpy as np
import matplotlib.pyplot as plt
urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings.txt", filename="ratings.txt")
data = pd.read_table('ratings.txt') # 데이터프레임에 저장
data[:10]
sample_data['document'] = sample_data['document'].str.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]","")
# 한글과 공백을 제외하고 모두 제거
sample_data[:10]
정규표현식을 이용해서 공백과 한글을 제외하고 모두 제거한 결과 다음과 같은 차이를 볼 수 있다.
, . 과 같은 특수문자가 없어지고, 숫자 또한 제거된 것을 볼 수 있다.
다음으로 Mecab을 이용하여 토크나이징을 수행해본다. 그 전에 불용어를 우선 정의하고 제거하는 과정이 필요한데, 불용어란 자주 등장하지만 분석을 하는 것에 있어서는 큰 도움이 되지 않는 단어들을 말한다. 접사 , 조사 , 접미사 같은 것들을 의미한다.
# 불용어 정의
stopwords=['의','가','이','은','들','는','좀','잘','걍','과','도','를','으로','자','에','와','한','하다']
tokenizer = Mecab()
tokenized=[]
for sentence in sample_data['document']:
temp = tokenizer.morphs(sentence) # 토큰화
temp = [word for word in temp if not word in stopwords] # 불용어 제거
tokenized.append(temp)
print(tokenized[:10])
>>> [['어릴', '때', '보', '고', '지금', '다시', '봐도', '재밌', '어요', 'ㅋㅋ'], ['디자인', '을', '배우', '학생', '외국', '디자이너', '그', '일군', '전통', '을', '통해', '발전', '해', '문화', '산업', '부러웠', '는데', '사실', '우리', '나라', '에서', '그', '어려운', '시절', '끝', '까지', '열정', '을', '지킨', '노라노', '같', '전통', '있', '어', '저', '같', '사람', '꿈', '을', '꾸', '고', '이뤄나갈', '수', '있', '다는', '것', '감사', '합니다'], ['폴리스', '스토리', '시리즈', '부터', '뉴', '까지', '버릴', '께', '하나', '없', '음', '최고'], ['연기', '진짜', '개', '쩔', '구나', '지루', '할거', '라고', '생각', '했', '는데', '몰입', '해서', '봤', '다', '그래', '이런', '게', '진짜', '영화', '지'], ['안개', '자욱', '밤하늘', '떠', '있', '초승달', '같', '영화'], ['사랑', '을', '해', '본', '사람', '라면', '처음', '부터', '끝', '까지', '웃', '을', '수', '있', '영화'], ['완전', '감동', '입니다', '다시', '봐도', '감동'], ['개', '전쟁', '나오', '나요', '나오', '면', '빠', '로', '보', '고', '싶', '음'], ['굿'], ['바보', '아니', '라', '병', '쉰', '인', '듯']]
이제 토크나이징 된 단어들을 이용해서 단어집합을 만들어본다. NLTK에서 빈도수 계산 도구인 FreqDist()를 지원한다.
각 단어의 집합을 구해 단어마다의 빈도수를 리턴해주는 것이다.
vocab = FreqDist(np.hstack(tokenized))
print('단어 집합의 크기 : {}'.format(len(vocab)))
단어를 key로, 단어에 대한 빈도수를 value으로 딕셔너리 형태로 리턴한다.
dict(vocab)
{'어릴': 3,
'때': 7,
'보': 15,
'고': 27,
'지금': 5,
'다시': 6,
'봐도': 3,
'재밌': 10,
'어요': 7,
'ㅋㅋ': 7,
...
'절대': 1,
'디테일': 1,
'따': 1,
'봉': 1,
'임': 1}
most_common()을 이용하여 빈도수가 높은 단어들만 원하는 개수만큼 불러온다. 등장 빈도수가 상위 500개인 단어만 집합으로 저장한다.
vocab_size = 500
# 상위 vocab_size개의 단어만 보존
vocab = vocab.most_common(vocab_size)
print('단어 집합의 크기 : {}'.format(len(vocab)))
>>> 단어 집합의 크기 : 500
2. 각 단어에 대해 고유한 정수 부여하기(indexing)
enumerate()는 순서가 있는 자료형을 입력받아서 인덱스를 순차적으로 함께 리턴하는 함수이다. 반복문에서 자주 사용되는 함수이다. 인덱스 0과 1은 다른 용도로 남겨두고, 나머지 단어들은 2부터 501까지 순차적으로 인덱스를 부여해준다.
word_to_index = {word[0] : index + 2 for index, word in enumerate(vocab)}
word_to_index['pad'] = 1
word_to_index['unk'] = 0
그리고 기존의 토크나이징 된 훈련데이터에서 각 단어를 고유한 정수로 부여하는 작업을 실행한다.
encoded = []
for line in tokenized: #입력 데이터에서 1줄씩 문장을 읽음
temp = []
for w in line: #각 줄에서 1개씩 글자를 읽음
try:
temp.append(word_to_index[w]) # 글자를 해당되는 정수로 변환
except KeyError: # 단어 집합에 없는 단어일 경우 unk로 대체된다.
temp.append(word_to_index['unk']) # unk의 인덱스로 변환
encoded.append(temp)
print(encoded[:10])
[[78, 27, 9, 4, 50, 41, 79, 16, 28, 29], [188, 5, 80, 189, 190, 191, 42, 192, 114, 5, 193, 194, 21, 115, 195, 196, 13, 51, 81, 116, 30, 42, 197, 117, 118, 31, 198, 5, 199, 200, 17, 114, 7, 82, 52, 17, 43, 201, 5, 202, 4, 203, 14, 7, 83, 32, 204, 84], [205, 119, 206, 53, 207, 31, 208, 209, 54, 10, 25, 11], [44, 33, 120, 210, 211, 212, 213, 68, 45, 34, 13, 214, 121, 15, 2, 215, 69, 8, 33, 3, 35], [216, 217, 218, 219, 7, 220, 17, 3], [122, 5, 21, 36, 43, 123, 124, 53, 118, 31, 85, 5, 14, 7, 3], [125, 37, 221, 41, 79, 37], [120, 222, 55, 223, 55, 86, 224, 46, 9, 4, 47, 25], [56], [225, 87, 88, 226, 227, 57, 89]]
이렇게 해당하는 단어가 정수로 변환되어 들어가게 된다.
3. 문장의 길이 통일하기(padding)
딥러닝 분석을 할 대, 동일한 데이터의 길이가 들어가야 하듯, 비정형 데이터인 텍스트 데이터도 문장의 길이를 통일시켜주어야 한다. 문장의 길이를 동일하게 바꿔주는 작업을 패딩(padding)이라고 한다. 문장이 정의한 길이보다 짧을 경우 특정단어로 길이를 채워주고, 정의한 길이보다 길 경우 잘라내는 작업이다.
이제 길이가 다른 데이터를 패딩하는 작업을 실습해보려 한다. 앞에서 단어 집합을 위한 토큰으로 'pad' 라는 단어를 추가했다. 문장의 길이가 정해준 길이보다 짧은 샘플들은 'pad' 토큰을 추가하여 길이를 맞춰주는 작업이다.
max_len = max(len(l) for l in encoded)
print('리뷰의 최대 길이 : %d' % max_len)
print('리뷰의 최소 길이 : %d' % min(len(l) for l in encoded))
print('리뷰의 평균 길이 : %f' % (sum(map(len, encoded))/len(encoded)))
plt.hist([len(s) for s in encoded], bins=50)
plt.xlabel('length of sample')
plt.ylabel('number of sample')
plt.show()
리뷰의 최대 길이 : 63
리뷰의 최소 길이 : 1
리뷰의 평균 길이 : 13.900000
가장 길이가 긴 리뷰는 63이므로 모든 문장의 길이를 63으로 통일시켜준다.
for line in encoded:
if len(line) < max_len: # 현재 샘플이 정해준 길이보다 짧으면
line += [word_to_index['pad']] * (max_len - len(line)) # 나머지는 전부 'pad' 토큰으로 채운다.
print(encoded[:3])
[[78, 27, 9, 4, 50, 41, 79, 16, 28, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [188, 5, 80, 189, 190, 191, 42, 192, 114, 5, 193, 194, 21, 115, 195, 196, 13, 51, 81, 116, 30, 42, 197, 117, 118, 31, 198, 5, 199, 200, 17, 114, 7, 82, 52, 17, 43, 201, 5, 202, 4, 203, 14, 7, 83, 32, 204, 84, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [205, 119, 206, 53, 207, 31, 208, 209, 54, 10, 25, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
위와 같이 길이가 짧은 문장들은 pad인 1로 나머지 길이들이 채워짐을 볼 수 있다.
이제 각 정수를 고유한 단어 벡터로 바꾸는 작업이 필요하다. 벡터를 바꾸는 방법은 크게 원핫인코딩과 임베딩이 있는데, 주로 워드 임베딩을 사용한다. 해당 내용은 추후에 다룰 예정이다.
'AI Study > NLP' 카테고리의 다른 글
[NLP/자연어처리] 단어의 표현(1) - 원핫인코딩과 워드투벡터(Word2Vec) (0) | 2021.05.14 |
---|---|
[NLP/자연어처리] 자연어처리 전처리(4) - 토치텍스트(TorchText) (0) | 2021.05.13 |
[NLP/자연어처리 ]자연어 처리 전처리(2) - 분절(토큰화) 라이브러리 소개 (0) | 2021.05.11 |
[NLP/자연어처리] 자연어 처리 전처리(1) - 코퍼스와 텍스트 정제 (0) | 2021.05.10 |
[NLP/자연어처리] 자연어처리와 딥러닝의 역사, 발전과정 (0) | 2021.05.10 |