💡 컴퓨터는 텍스트를 인식할 수 없다. 오로지 숫자만 처리가 가능하기 때문에 텍스트를 수치화 시키는 작업을 수행해야 한다.
import nltk
nltk.download('punkt')
text = """Isn't she lovely.
Isn't she wonderful.
Isn't she precious.
Less than one minute old.
I never thought through love we'd be.
Making one as lovely as she.
But isn't she lovely made from love.
Isn't she pretty.
Truly the angel's best.
Boy, I'm so happy.
We have been heaven blessed.
I can't believe what God has done.
Through us he's given life to one.
But isn't she lovely made from love.
Isn't she lovely.
Life and love are the same.
Life is Aisha.
The meaning of her name.
Londie, it could have not been done.
Without you who conceived the one.
That's so very lovely made from love."""
from nltk.tokenize import sent_tokenize
sent_tokens = sent_tokenize(text)
print(sent_tokens)
~~>
["Isn't she lovely.", "Isn't she wonderful.", "Isn't she precious.", 'Less than one minute old.', "I never thought through love we'd be.", 'Making one as lovely as she.', "But isn't she lovely made from love.", "Isn't she pretty.", "Truly the angel's best.", "Boy, I'm so happy.", 'We have been heaven blessed.', "I can't believe what God has done.", "Through us he's given life to one.", "But isn't she lovely made from love.", "Isn't she lovely.", 'Life and love are the same.', 'Life is Aisha.', 'The meaning of her name.', 'Londie, it could have not been done.', 'Without you who conceived the one.', "That's so very lovely made from love."]
nltk.download('stopwords')
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
# 단어 토큰화된 전체 문장을 가지고 있을 배열
sentences = []
stop_words = set(stopwords.words('english')) # 불용어 중복 제거
# 문장을 하나씩 꺼내서 단어 토큰화
for sent in sent_tokens:
word_tokens = word_tokenize(sent) # 단어 토큰화 수행
result = [] # 정제 작업이 완료된 단어를 추가할 배열
###### 단어 정제 작업 ######
for word in word_tokens: # 단어 토큰에서 단어를 하나씩 추출해 ㅜㅈ기
# 소문자화
word = word.lower() # 모든 단어를 소문자화 하여 정규화
if word not in stop_words: # 불용어 처리
# 단어 길이가 3개 이상인 단어만 사용
if len(word) > 2:
result.append(word)
sentences.append(result)
print(sentences)
~~>
[["n't", 'lovely'], ["n't", 'wonderful'], ["n't", 'precious'], ['less', 'one', 'minute', 'old'], ['never', 'thought', 'love'], ['making', 'one', 'lovely'], ["n't", 'lovely', 'made', 'love'], ["n't", 'pretty'], ['truly', 'angel', 'best'], ['boy', 'happy'], ['heaven', 'blessed'], ["n't", 'believe', 'god', 'done'], ['given', 'life', 'one'], ["n't", 'lovely', 'made', 'love'], ["n't", 'lovely'], ['life', 'love'], ['life', 'aisha'], ['meaning', 'name'], ['londie', 'could', 'done'], ['without', 'conceived', 'one'], ['lovely', 'made', 'love']]
# 각 배열에 등장한 횟수가 많을 수록 앞 번호에 배치
# 1. 문장 별로 모여있는 단어들을 하나로 풀어서 합치기
word = sum(sentences, []) # sentences에 들어있는 모든 배열을 비어있는 배열에 합치는 역할( extends )
print(words)
~~>
["n't", 'lovely', "n't", 'wonderful', "n't", 'precious', 'less', 'one', 'minute', 'old', 'never', 'thought', 'love', 'making', 'one', 'lovely', "n't", 'lovely', 'made', 'love', "n't", 'pretty', 'truly', 'angel', 'best', 'boy', 'happy', 'heaven', 'blessed', "n't", 'believe', 'god', 'done', 'given', 'life', 'one', "n't", 'lovely', 'made', 'love', "n't", 'lovely', 'life', 'love', 'life', 'aisha', 'meaning', 'name', 'londie', 'could', 'done', 'without', 'conceived', 'one', 'lovely', 'made', 'love']
# 2. 빈도수를 센 다음 배치
from collections import Counter # 횟수 세기 용도
vocab = Counter(words) # Counter 모듈을 활용하면 단어의 모든 빈도수를 손쉽게 계산 가능
print(vocab)
~~>
Counter({"n't": 8, 'lovely': 6, 'love': 5, 'one': 4, 'made': 3, 'life': 3, 'done': 2, 'wonderful': 1, 'precious': 1, 'less': 1, 'minute': 1, 'old': 1, 'never': 1, 'thought': 1, 'making': 1, 'pretty': 1, 'truly': 1, 'angel': 1, 'best': 1, 'boy': 1, 'happy': 1, 'heaven': 1, 'blessed': 1, 'believe': 1, 'god': 1, 'given': 1, 'aisha': 1, 'meaning': 1, 'name': 1, 'londie': 1, 'could': 1, 'without': 1, 'conceived': 1})
📍 빈도 내림차순으로 정렬
vocab_items = vocab.items()
vocab_items
~~>
dict_items([("n't", 8), ('lovely', 6), ('wonderful', 1), ('precious', 1), ('less', 1), ('one', 4), ('minute', 1), ('old', 1), ('never', 1), ('thought', 1), ('love', 5), ('making', 1), ('made', 3), ('pretty', 1), ('truly', 1), ('angel', 1), ('best', 1), ('boy', 1), ('happy', 1), ('heaven', 1), ('blessed', 1), ('believe', 1), ('god', 1), ('done', 2), ('given', 1), ('life', 3), ('aisha', 1), ('meaning', 1), ('name', 1), ('londie', 1), ('could', 1), ('without', 1), ('conceived', 1)])
📍 빈도수가 높은 순서대로 정렬
vocab_sorted = sorted(vocab_items, key = lambda x : x[1], reverse=True)
print(vocab_sorted)
~~>
[("n't", 8), ('lovely', 6), ('love', 5), ('one', 4), ('made', 3), ('life', 3), ('done', 2), ('wonderful', 1), ('precious', 1), ('less', 1), ('minute', 1), ('old', 1), ('never', 1), ('thought', 1), ('making', 1), ('pretty', 1), ('truly', 1), ('angel', 1), ('best', 1), ('boy', 1), ('happy', 1), ('heaven', 1), ('blessed', 1), ('believe', 1), ('god', 1), ('given', 1), ('aisha', 1), ('meaning', 1), ('name', 1), ('londie', 1), ('could', 1), ('without', 1), ('conceived', 1)]
📍 높은 빈도수를 가진 단어일 수록 낮은 정수 인덱스를 부여( 앞쪽에 배치 )
word2idx = {} # 비어있는 딕셔너리
i = 0
for word, frequency in vocab_sorted:
if frequency > 1: # 정제 작업, 빈도수가 너무 작은 단어는 제외
i += 1
word2idx[word] = i # 딕셔너리에 단어와 정수( 단어의 인덱스 ) 추가
print(word2idx)
~~>
{"n't": 1, 'lovely': 2, 'love': 3, 'one': 4, 'made': 5, 'life': 6, 'done': 7}
📍 빈도수가 가장 높은 상위 n개만 선택해서 단어집합으로 사용하기
# 단어 집합 상위 5개의 단어만 사용하겠다.( 사용할 단어집합의 크기 )
# 딥러닝 수행시 사용할 단어의 개수!!!
vocab_size = 5
# 단어 집합의 인덱스를 구해오기
word_frequency = [w for w, c in word2idx.items() if c >= vocab_size + 1]
print(word_frequency) # 단어 집합에서 제외할 단어들
~~>
['life', 'done']
# 단어 사전에서 버릴 단어들 제거
for w in word_frequency:
del word2idx[w]
print(word2idx)
~~>
{"n't": 1, 'lovely': 2, 'love': 3, 'one': 4, 'made': 5}
💡 Transformer, Seq2Seq, Attention( 바다나우 어텐션 ), BERT 등을 배우면 인코딩과 디코딩 작업용해서 텍스트를 분류할 수도 있고, 생성할 수도 있다.
📍 UNK(OOV) 토큰 만들기
word2idx['<oox>'] = 6
print(word2idx)
~~>
{"n't": 1, 'lovely': 2, 'love': 3, 'one': 4, 'made': 5, '<oov>': 6}
encoded = [] # 인코딩된 데이터를 저장하기 위한 배열
for s in sentences:
# 임시로 인코딩된 내용을 저장할 배열
temp = []
# 문장에서 단어 토큰을 하나씩 꺼내기
for w in s:
if w in word2idx: # 단어가 사전에 있으면
temp.append(word2idx[w]) # 단어에 해당하는 정수를 temp에 추가
else:
temp.append(word2idx['<oov>']) # 단어 집합에 없는 단어라면 oov에 해당하는 정수를 찾아서 추가
encoded.append(temp)
print('변환 전 : ', sentences[:5])
print('변환 후 : ', encoded[:5])
~~>
변환 전 : [["n't", 'lovely'], ["n't", 'wonderful'], ["n't", 'precious'], ['less', 'one', 'minute', 'old'], ['never', 'thought', 'love']]
변환 후 : [[1, 2], [1, 6], [1, 6], [6, 4, 6, 6], [6, 6, 3]]
📍 단어 집합 만들기
from tensorflow.keras.preprocessing.text import Tokenizer
t = Tokenizer()
# fit_on_texts() 함수에 코퍼스( sentences )를 집어 넣으면 바로 빈도수를 기준으로 단어 집합을 만들어 준다.
t.fit_on_texts(sentences) # 형태소 분리의 결과가 들어간다. 이 때 형태소 분리의 결과는 항상 2차원 배열이어야만 한다.
# 생성된 단어 집합 확인
print(t.word_index)
~~>
{"n't": 1, 'lovely': 2, 'love': 3, 'one': 4, 'made': 5, 'life': 6, 'done': 7, 'wonderful': 8, 'precious': 9, 'less': 10, 'minute': 11, 'old': 12, 'never': 13, 'thought': 14, 'making': 15, 'pretty': 16, 'truly': 17, 'angel': 18, 'best': 19, 'boy': 20, 'happy': 21, 'heaven': 22, 'blessed': 23, 'believe': 24, 'god': 25, 'given': 26, 'aisha': 27, 'meaning': 28, 'name': 29, 'londie': 30, 'could': 31, 'without': 32, 'conceived': 33}
📍 단어의 빈도수 확인하기
print(t.word_counts)
~~>
OrderedDict([("n't", 8), ('lovely', 6), ('wonderful', 1), ('precious', 1), ('less', 1), ('one', 4), ('minute', 1), ('old', 1), ('never', 1), ('thought', 1), ('love', 5), ('making', 1), ('made', 3), ('pretty', 1), ('truly', 1), ('angel', 1), ('best', 1), ('boy', 1), ('happy', 1), ('heaven', 1), ('blessed', 1), ('believe', 1), ('god', 1), ('done', 2), ('given', 1), ('life', 3), ('aisha', 1), ('meaning', 1), ('name', 1), ('londie', 1), ('could', 1), ('without', 1), ('conceived', 1)])
📍 인코딩 수행하기( text_to sequences )
print(t.text_to_sequences(sentences))
~~>
[[1, 2], [1, 8], [1, 9], [10, 4, 11, 12], [13, 14, 3], [15, 4, 2], [1, 2, 5, 3], [1, 16], [17, 18, 19], [20, 21], [22, 23], [1, 24, 25, 7], [26, 6, 4], [1, 2, 5, 3], [1, 2], [6, 3], [6, 27], [28, 29], [30, 31, 7], [32, 33, 4], [2, 5, 3]]
📍 디코딩 수행하기( sequences_to_texts )
print(t.sequences_to_texts([[1, 12],
[1, 17, 22]]))
~~>
["n't old", "n't truly heaven"]
# 설치 코드
!pip install konlpy
import pandas as pd
import numpy as np
import urllib.request
from tensorflow.keras.preprocessing.text import Tokenizer
urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt", filename="ratings_test.txt")
~~>
('ratings_test.txt', <http.client.HTTPMessage at 0x7f6723ad7b50>)
train_data = pd.read_table('ratings_text.txt', encoding='utf-8')
train_data.info()
~~>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 50000 entries, 0 to 49999
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 50000 non-null int64
1 document 49997 non-null object
2 label 50000 non-null int64
dtypes: int64(2), object(1)
memory usage: 1.1+ MB
train_data.head()
~~>
📍 1. nan값 제거
# 중복되지 않은 데이터의 개수 확인
train_data['document'].nunique()
~~>
49157
📍 2. 중복 문장 제거
# 중복 데이터 제거, nan값 제거
train_data.drop_duplicates(subset=['document'], inplace=True)
train_data = train_data.dropna(how='any') # nan값이 존재하는 행을 제거
📍 3. 특수문자 및 영어 제거
# 정규식을 활용해서 한글만 추출( 영문, 특수문자 제거 )
train_data['document'] = train_data['document'].str.replace('[^ㄱ-ㅎㅏ-ㅣ가-힣]', '') # 한글 및 공백이 아닌 문자는 모두, 마지막에 공백( space )
train_data.head()
~~>
📍 비어있는 문자열 정제
# 비어있는 문자열을 강제로 nan값으로 만들고, nan을 제거
train_data['document'].replace('', np.nan, inplace=True)
train_data.isnull().sum() # nan값이 몇개 인지 개수 세기
~~>
id 0
document 162
label 0
dtype: int64
train_data = train_data.dropna(how='any')
train_data.head()
~~>
📍 Stemming 작업
from konlpy.tag import Okt
okt = Okt()
# okt 테스트 하기
sample_text = "뭐야 이 평점들은 나쁘진 않지만 점 짜리는 더더욱 아니잖아"
print(okt.morphs(sample_text, stem=True))
~~>
['뭐', '야', '이', '평점', '들', '은', '나쁘다', '않다', '점', '짜다', '리', '는', '더', '더욱', '아니다']
📍 불용어( stopwords ) 선정
stop_words = ['의','가','이','은','들','는','좀','잘','걍','과','도','를','으로','자','에','와','한','하다', '것', '게']
x_train = []
for sentence iin train_data['document']:
temp_x = []
temp_x = okt.morphs(sentence, stem=True) # 토큰화
temp_x = [word for word in temp_x if not word in stop_words] # 불용어 제거
x_train.append(temp_x)
x_train[:3]
~~>
[['굳다', 'ㅋ'],
['뭐', '야', '평점', '나쁘다', '않다', '점', '짜다', '리', '더', '더욱', '아니다'],
['지루하다', '않다', '완전', '막장', '임', '돈', '주다', '보기', '에는']]
📍 토큰화
t = Tokenizer()
t.fit_on_texts(x_train)
t.texts_to_sequences(x_train)[:3]
~~>
[[611, 89],
[62, 165, 23, 392, 19, 20, 277, 773, 42, 858, 17],
[64, 19, 87, 377, 107, 109, 58, 148, 243]]