: 자연어 처리(NLP)는 검색 엔진, 대화식 인터페이스, 문서 처리처럼 인간의 언어를 이해하고 처리해줘야 하는 시스템에서 중요하고 광범위하게 사용되는 기술로 인간이 자유롭게 작성한 텍스트나 언어를 컴퓨터가 이해하도록 도와주는 것을 목표로 한다. 이를 위해 머신러닝을 통해 개발된 응용 프로그램들은 텍스트 분류, 감성 분석, 주제 모델링 등 알고리즘을 대용량의 말뭉치(corpus)를 이용해 학습하고 입력 텍스트 데이터의 패턴을 탐지해 문맥을 파악할 수 있도록 했다.
$ pip3 install nltk
$ pip3 install gensim
$ pip3 install pattern
: 효율적인 텍스트 분석을 위해 텍스트를 단어나 문장과 같은 작은 조각으로 나누는 작업을 토큰화 (tokenization)라고 하며 이때 나뉜 조각들을 토큰이라고 한다.
from nltk.tokenize import sent_tokenize, \
word_tokenize, WordPunctTokenizer
# Define input text
input_text = "Do you know how tokenization works? It's actually quite interesting! Let's analyze a couple of sentences and figure it out."
# Sentence tokenizer
print("\nSentence tokenizer:")
print(sent_tokenize(input_text))
# Word tokenizer
print("\nWord tokenizer:")
print(word_tokenize(input_text))
# WordPunct tokenizer
print("\nWord punct tokenizer:")
print(WordPunctTokenizer().tokenize(input_text))
: 스테머(stemmer)는 어간을 추출하기 위한 방법 중 하나로 서로 다른 형태로 표현된 단어에서 공통된 어간을 추출해 기본형으로 사용하는 것을 목적으로 한다.
from nltk.stem.porter import PorterStemmer
from nltk.stem.lancaster import LancasterStemmer
from nltk.stem.snowball import SnowballStemmer
input_words = ['writing', 'calves', 'be', 'branded', 'horse', 'randomize',
'possibly', 'provision', 'hospital', 'kept', 'scratchy', 'code']
# Create various stemmer objects
porter = PorterStemmer()
lancaster = LancasterStemmer()
snowball = SnowballStemmer('english')
#Create a list of stemmer names for display
stemmer_names = ['PORTER', 'LANCASTER', 'SNOWBALL']
formatted_text = '{:>16}' * (len(stemmer_names) + 1)
print('\n', formatted_text.format('INPUT WORD', *stemmer_names),
'\n', '='*68)
# Stem each word and display the output
for word in input_words:
output = [word, porter.stem(word),
lancaster.stem(word), snowball.stem(word)]
print(formatted_text.format(*output))
: 표제화는 단어를 기본형으로 줄이는 또 다른 방법으로 어휘와 단어에 형태소 분석을 적용해 단어에서 어미를 제거하는 방식으로 단어를 기본형으로 바꾼다.
from nltk.stem import WordNetLemmatizer
input_words = ['writing', 'calves', 'be', 'branded', 'horse', 'randomize',
'possibly', 'provision', 'hospital', 'kept', 'scratchy', 'code']
# Create lemmatizer object
lemmatizer = WordNetLemmatizer()
# Create a list of lemmatizer names for display
lemmatizer_names = ['NOUN LEMMATIZER', 'VERB LEMMATIZER']
formatted_text = '{:>24}' * (len(lemmatizer_names) + 1)
print('\n', formatted_text.format('INPUT WORD', *lemmatizer_names),
'\n', '='*75)
# Lemmatize each word and display the output
for word in input_words:
output = [word, lemmatizer.lemmatize(word, pos='n'),
lemmatizer.lemmatize(word, pos='v')]
print(formatted_text.format(*output))
: 심화 분석을 위해 텍스트 데이터를 단어 묶음(chunk)로 나누기도 하며 이러한 작업을 청킹(chunking)이라고 한다.
import numpy as np
from nltk.corpus import brown
# Split the input text into chunks, where
# each chunk contains N words
def chunker(input_data, N):
input_words = input_data.split(' ')
output = []
cur_chunk = []
count = 0
for word in input_words:
cur_chunk.append(word)
count += 1
if count == N:
output.append(' '.join(cur_chunk))
count, cur_chunk = 0, []
output.append(' '.join(cur_chunk))
return output
if __name__=='__main__':
# Read the first 12000 words from the Brown corpus
input_data = ' '.join(brown.words()[:12000])
# Define the number of words in each chunk
chunk_size = 700
chunks = chunker(input_data, chunk_size)
print('\nNumber of text chunks =', len(chunks), '\n')
for i, chunk in enumerate(chunks):
print('Chunk', i+1, '==>', chunk[:50])
: 백오브워드 모델은 문서에서 사용된 모든 단어가 포함된 어휘 목록을 만들고, 문서별로 단어 빈도 정보가 포함된 문서 단어 행렬(document term matrix)을 만들어 단어의 집합을 표현한다. 이 모델은 빈도만을 분석하고 문법적 세부 사항과 단어의 순서는 무시한다.
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from nltk.corpus import brown
from text_chunker import chunker
# Read the data from the Brown corpus
input_data = ' '.join(brown.words()[:5400])
# Number of words in each chunk
chunk_size = 800
text_chunks = chunker(input_data, chunk_size)
# Convert to dict items
chunks = []
for count, chunk in enumerate(text_chunks):
d = {'index': count, 'text': chunk}
chunks.append(d)
# Extract the document term matrix
count_vectorizer = CountVectorizer(min_df=7, max_df=20)
document_term_matrix = count_vectorizer.fit_transform([chunk['text'] for chunk in chunks])
# Extract the vocabulary and display it
vocabulary = np.array(count_vectorizer.get_feature_names())
print("\nVocabulary:\n", vocabulary)
# Generate names for chunks
chunk_names = []
for i in range(len(text_chunks)):
chunk_names.append('Chunk-' + str(i+1))
# Print the document term matrix
print("\nDocument term matrix:")
formatted_text = '{:>12}' * (len(chunk_names) + 1)
print('\n', formatted_text.format('Word', *chunk_names), '\n')
for word, item in zip(vocabulary, document_term_matrix.T):
# 'item' is a 'csr_matrix' data structure
output = [word] + [str(freq) for freq in item.data]
print(formatted_text.format(*output))
: 카테고리 예측기는 특정 텍스트의 카테고리를 예측하는 도구로, 텍스트 문서를 분류하기 위헤 자주 사용된다. 단어 빈도와 문서 빈도 역수를 곱한 값을 이용해 문서를 분류할 수 있다. 단어 빈도는 특정 문서에서 특정 단어가 사용된 횟수를 나타내고 문서 빈도 역수는 특정 문서에 대한 특정 단어의 고유성 척도로 문서 빈도의 역수값이다.
from sklearn.datasets import fetch_20newsgroups
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
# Define the category map
category_map = {'talk.politics.misc': 'Politics', 'rec.autos': 'Autos',
'rec.sport.hockey': 'Hockey', 'sci.electronics': 'Electronics',
'sci.med': 'Medicine'}
# Get the training dataset
training_data = fetch_20newsgroups(subset='train',
categories=category_map.keys(), shuffle=True, random_state=5)
# Build a count vectorizer and extract term counts
count_vectorizer = CountVectorizer()
train_tc = count_vectorizer.fit_transform(training_data.data)
print("\nDimensions of training data:", train_tc.shape)
# Create the tf-idf transformer
tfidf = TfidfTransformer()
train_tfidf = tfidf.fit_transform(train_tc)
# Define test data
input_data = [
'You need to be careful with cars when you are driving on slippery roads',
'A lot of devices can be operated wirelessly',
'Players need to be careful when they are close to goal posts',
'Political debates help us understand the perspectives of both sides
# Train a Multinomial Naive Bayes classifier
classifier = MultinomialNB().fit(train_tfidf, training_data.target)
# Transform input data using count vectorizer
input_tc = count_vectorizer.transform(input_data)
# Transform vectorized data using tfidf transformer
input_tfidf = tfidf.transform(input_tc)
# Predict the output categories
predictions = classifier.predict(input_tfidf)
# Print the outputs
for sent, category in zip(input_data, predictions):
print('\nInput:', sent, '\nPredicted category:', \
category_map[training_data.target_names[category]])
+) 위 기법들을 활용해 성별 분류기, 감성 분석기를 만들 수 있다.
: 주제 모델링은 특정 주제의 텍스트 데이터 패턴을 찾아내는 방법이다. 하나의 텍스트에 여러 주제가 포함된 경우 이 기술을 활용해 입력 텍스트 내에 표현된 주제를 식별하고 구분할 수 있다. 주제 모델링 알고리즘의 중요한 특징 중 하나는 레이블이 태깅된 데이터가 필요하지 않다는 것이다.
from nltk.tokenize import RegexpTokenizer
from nltk.corpus import stopwords
from nltk.stem.snowball import SnowballStemmer
from gensim import models, corpora
# Load input data
def load_data(input_file):
data = []
with open(input_file, 'r') as f:
for line in f.readlines():
data.append(line[:-1])
return data
# Processor function for tokenizing, removing stop
# words, and stemming
def process(input_text):
# Create a regular expression tokenizer
tokenizer = RegexpTokenizer(r'\w+')
# Create a Snowball stemmer
stemmer = SnowballStemmer('english')
# Get the list of stop words
stop_words = stopwords.words('english')
# Tokenize the input string
tokens = tokenizer.tokenize(input_text.lower())
# Remove the stop words
tokens = [x for x in tokens if not x in stop_words]
# Perform stemming on the tokenized words
tokens_stemmed = [stemmer.stem(x) for x in tokens]
return tokens_stemmed
if __name__=='__main__':
# Load input data
data = load_data('data.txt')
# Create a list for sentence tokens
tokens = [process(x) for x in data]
# Create a dictionary based on the sentence tokens
dict_tokens = corpora.Dictionary(tokens)
# Create a document-term matrix
doc_term_mat = [dict_tokens.doc2bow(token) for token in tokens]
# Define the number of topics for the LDA model
num_topics = 2
# Generate the LDA model
ldamodel = models.ldamodel.LdaModel(doc_term_mat,
num_topics=num_topics, id2word=dict_tokens, passes=25)
num_words = 5
print('\nTop ' + str(num_words) + ' contributing words to each topic:')
for item in ldamodel.print_topics(num_topics=num_topics, num_words=num_words):
print('\nTopic', item[0])
# Print the contributing words along with their relative contributions
list_of_strings = item[1].split(' + ')
for text in list_of_strings:
weight = text.split('*')[0]
word = text.split('*')[1]
print(word, '==>', str(round(float(weight) * 100, 2)) + '%')