Natural Language Processing

oyoi·2024년 5월 4일

How to install relevant packages

: 자연어 처리(NLP)는 검색 엔진, 대화식 인터페이스, 문서 처리처럼 인간의 언어를 이해하고 처리해줘야 하는 시스템에서 중요하고 광범위하게 사용되는 기술로 인간이 자유롭게 작성한 텍스트나 언어를 컴퓨터가 이해하도록 도와주는 것을 목표로 한다. 이를 위해 머신러닝을 통해 개발된 응용 프로그램들은 텍스트 분류, 감성 분석, 주제 모델링 등 알고리즘을 대용량의 말뭉치(corpus)를 이용해 학습하고 입력 텍스트 데이터의 패턴을 탐지해 문맥을 파악할 수 있도록 했다.

  • Natural Language ToolKit 설치
$ pip3 install nltk
  • gensim 설치
$ pip3 install gensim
  • pattern 설치
$ pip3 install pattern

Tokenizing text data

: 효율적인 텍스트 분석을 위해 텍스트를 단어나 문장과 같은 작은 조각으로 나누는 작업을 토큰화 (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)) 

Converting words to their base forms using stemming

: 스테머(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)) 

Converting words to their base forms using lemmatization

: 표제화는 단어를 기본형으로 줄이는 또 다른 방법으로 어휘와 단어에 형태소 분석을 적용해 단어에서 어미를 제거하는 방식으로 단어를 기본형으로 바꾼다.

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)) 

Dividing text data into chunks

: 심화 분석을 위해 텍스트 데이터를 단어 묶음(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])

Extracting document term matrix using the Bag of Words model

: 백오브워드 모델은 문서에서 사용된 모든 단어가 포함된 어휘 목록을 만들고, 문서별로 단어 빈도 정보가 포함된 문서 단어 행렬(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)) 

Building a category predictor

: 카테고리 예측기는 특정 텍스트의 카테고리를 예측하는 도구로, 텍스트 문서를 분류하기 위헤 자주 사용된다. 단어 빈도문서 빈도 역수를 곱한 값을 이용해 문서를 분류할 수 있다. 단어 빈도는 특정 문서에서 특정 단어가 사용된 횟수를 나타내고 문서 빈도 역수는 특정 문서에 대한 특정 단어의 고유성 척도로 문서 빈도의 역수값이다.

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]]) 

+) 위 기법들을 활용해 성별 분류기, 감성 분석기를 만들 수 있다.

Topic modeling using Latent Dirichlet Allocation

: 주제 모델링은 특정 주제의 텍스트 데이터 패턴을 찾아내는 방법이다. 하나의 텍스트에 여러 주제가 포함된 경우 이 기술을 활용해 입력 텍스트 내에 표현된 주제를 식별하고 구분할 수 있다. 주제 모델링 알고리즘의 중요한 특징 중 하나는 레이블이 태깅된 데이터가 필요하지 않다는 것이다.

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)) + '%') 
profile
오이

0개의 댓글