멀티캠퍼스 RNN수업5_자연어처리

12 minute read


1. Bag of words with CountVector(어간추출/멀티스레드/ Pipeline/wordcloud 추가)

어제 수행했던 내용에 어간추출을 수행하는 SnowballStemmer을 추가한 코드

#어제 작업
import re
from bs4 import BeautifulSoup
import pandas as pd       
train = pd.read_csv("labeledTrainData.tsv", header=0, \
                    delimiter="\t", quoting=3)

train.shape
#(25000, 3)

train['sentiment'].value_counts()

# remove tag
rows = []
for t in train["review"]:
    soup = BeautifulSoup(t, "html.parser")
    for s in soup.select('br'):
        s.extract()
    rows.append(soup.get_text())
train["review"] = rows

example1 = train["review"][0]

letters_only = re.sub("[^a-zA-Z]",          
                      " ",example1 ) 

lower_case = letters_only.lower()    

words = lower_case.split()   

import nltk
nltk.download('stopwords')
nltk.download('wordnet') # 온톨로지(보고 들은 모든 것)
from nltk.corpus import stopwords

stopwords.words("english")[:10]
#['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're"]

words = [w for w in words if not w in stopwords.words("english")]
# 처리 전
words[:10]
['stuff',
 'going',
 'moment',
 'mj',
 'started',
 'listening',
 'music',
 'watching',
 'odd',
 'documentary']

추가 실행

## 추가
from nltk.stem.snowball import SnowballStemmer

stemmer = SnowballStemmer('english')
words = [stemmer.stem(w) for w in words]
# 처리 후 단어
words[:10]
['stuff',
 'go',
 'moment',
 'mj',
 'start',
 'listen',
 'music',
 'watch',
 'odd',
 'documentari']

## 변경
def review_to_words( raw_review ):
    # 1. HTML 제거
    review_text = BeautifulSoup(raw_review).get_text() 
    # 2. 영문자가 아닌 문자는 공백으로 전환
    letters_only = re.sub("[^a-zA-Z]", " ", review_text) 
    # 3. 소문자 변환
    words = letters_only.lower().split()                             
    # 4. 파이썬에서는 리스트보다 set으로 검색하는게 빠름
    stops = set(stopwords.words("english"))                  
    # 5. Stopwords 불용어 제거
    meaningful_words = [w for w in words if not w in stops]   
    # 6. 어간추출(새롭게 추가)
    stemming_words = [stemmer.stem(w) for w in meaningful_words]
    # 7. 공백으로 구분된 문자열로 결합하여 결과를 반환
    return( " ".join( meaningful_words ))   

clean_review = review_to_words( train["review"][0] )
num_reviews = train["review"].size
num_reviews
#25000

## 추가
from multiprocessing import Pool
import numpy as np

#멀티쓰레드를 사용하기 위해서 2개 함수를 추가함
def _apply_df(args):
    df, func, kwargs = args
    return df.apply(func, **kwargs)

def apply_by_multiprocessing(df, func, **kwargs):
    # 키워드 항목 중 workers 파라메터를 꺼냄
    workers = kwargs.pop('workers')
    # 위에서 가져온 workers 수로 프로세스 풀을 정의
    pool = Pool(processes=workers)
    # 실행할 함수와 데이터프레임을 워커의 수 만큼 나눠 작업
    result = pool.map(_apply_df, [(d, func, kwargs)
            for d in np.array_split(df, workers)])
    pool.close()
    # 작업 결과를 합쳐서 반환
    return pd.concat(list(result))

## 추가(오래걸림)
%time clean_train_reviews = apply_by_multiprocessing(train['review'], review_to_words, workers=4)  
%time clean_test_reviews = apply_by_multiprocessing(test['review'], review_to_words, workers=4) 



사이킷런의 CountVectorizer를 통해 피처 생성

# create bag of words with scikit-learn
# 변경
print ("Creating the bag of words...\n")
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(analyzer = "word",   \
                             tokenizer = None,    \
                             preprocessor = None, \
                             stop_words = None,   \
                             ngram_range=(1,3), # 1글자~3글자까지 포함된 내용 적용
                             max_features = 5000)  # set the features number to 5000
vectorizer

# 추가
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
    ('vect', vectorizer),
]) 

# 변경
# train_data_features = vectorizer.fit_transform(clean_train_reviews)
%time train_data_features = pipeline.fit_transform(clean_train_reviews)
print (train_data_features.shape)

# 변경
vocab = vectorizer.get_feature_names()
# print (vocab)
print(len(vocab))
vocab[:10]

# 추가
dist = np.sum(train_data_features, axis=0)

for tag, count in zip(vocab, dist):
    print(count, tag)
    
# df = pd.DataFrame(train_data_features) 
df = pd.DataFrame(dist, columns=vocab) 
df

# 추가
pd.DataFrame(train_data_features[:10].toarray(), columns=vocab).head()

Random Forest

# Random Forest
print("Training the random forest...")
from sklearn.ensemble import RandomForestClassifier
# Initialize
# forest = RandomForestClassifier(n_estimators = 100) 
forest = RandomForestClassifier(n_estimators = 100, n_jobs=-1, random_state=2020) 

# forest = forest.fit( train_data_features, train["sentiment"] )
forest

# 분리
# Traing of Random Forest
%time forest = forest.fit( train_data_features, train["sentiment"] )

# 추가
from sklearn.model_selection import cross_val_score
%time score = np.mean(cross_val_score(
                        forest, train_data_features, train['sentiment'], 
                        cv=10, scoring='roc_auc'))
score

TEST

clean_test_reviews[0]
%time test_data_features = pipeline.transform(clean_test_reviews)
test_data_features = test_data_features.toarray()

# 추가
test_data_features

# 추가
# 벡터화 된 단어로 숫자가 문서에서 등장하는 횟수를 나타낸다
test_data_features[5][:100]

# 추가
# 벡터화 하며 만든 사전에서 해당 단어가 무엇인지 찾아볼 수 있다.
# vocab = vectorizer.get_feature_names()
vocab[8], vocab[2558], vocab[2559], vocab[2560]


# Predict test data with trained random forest model
result = forest.predict(test_data_features)
# 추가
result[:10]

# Display predict result (convert thr result to csv file for submit to the Kaggle)
output = pd.DataFrame( data={"id":test["id"], "sentiment":result} )
output.to_csv( "Bag_of_Words_model.csv", index=False, quoting=3 )

output

Wordcloud

pip install wordcloud

# 추가
# newStopWords = ['one', 'movie', 'film'] 
# stopwords_pychan.extend(newStopWords)

from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
# %matplotlib inline 설정을 해주어야지만 노트북 안에 그래프가 디스플레이 된다.
%matplotlib inline

def displayWordCloud(data = None, backgroundcolor = 'white', width=800, height=600 ):
    wordcloud = WordCloud(stopwords = STOPWORDS, 
                          background_color = backgroundcolor, 
                           width = width, height = height).generate(data)
    plt.figure(figsize = (15 , 10))
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show() 
    
    # 추가
# 학습 데이터의 모든 단어에 대한 워드 클라우드를 그려본다.
%time displayWordCloud(' '.join(clean_train_reviews))

# 추가
# 단어 수
train['num_words'] = clean_train_reviews.apply(lambda x: len(str(x).split()))
# 중복을 제거한 단어 수
train['num_uniq_words'] = clean_train_reviews.apply(lambda x: len(set(str(x).split())))

# 테스트 데이터의 모든 단어에 대한 워드 클라우드를 그려본다.
%time displayWordCloud(' '.join(clean_test_reviews))

# 추가
# 불용어 추가 테스트
newStopWords = ['one', 'movie', 'film'] 
my_stopwords = stopwords.words("english")
my_stopwords.extend(newStopWords)

# 추가
def newDisplayWordCloud(data = None, backgroundcolor = 'white', width=800, height=600 ):
    wordcloud = WordCloud(stopwords = my_stopwords, 
                          background_color = backgroundcolor, 
                           width = width, height = height).generate(data)
    plt.figure(figsize = (15 , 10))
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show() 
    
# 추가
# 테스트 데이터의 모든 단어에 대한 워드 클라우드를 그려본다.
%time newDisplayWordCloud(' '.join(clean_test_reviews))


Imgur

Word2Vec_CBOW방식

Imgur

Word2Vec_Skip-gram방식

Imgur

Imgur

from gensim.models import Word2Vec
embedding_model = Word2Vec(tokenized_contents, size=100,
window = 2, min_count=50, workers=4, iter=100, sg=1) #window=2는 sg=0 시보, sg=1 skipgram
print(embedding_model.most_similar(positive=["디자인"], topn=100)) #디자인이라는 단어가 100차원의 데이터로 보관되어 있음


네이버영화

IDB

3. Bag of words with Word2Vec(실습)

Part 1 - Preprocessing

데이터전처리 과정을 전처리할 수 있는 Word2VecUtil이라는 class를 만들어 사용함(해당파일의 class명에 문제가 있어서 수정함) KaggleWord2VecUtility -> Word2VecUtil

import warnings
warnings.filterwarnings('ignore')

import re
from bs4 import BeautifulSoup
import pandas as pd       
train = pd.read_csv("labeledTrainData.tsv", header=0, delimiter="\t", quoting=3)
test = pd.read_csv("testData.tsv", header=0, delimiter="\t", quoting=3)
unlabeled_train = pd.read_csv("unlabeledTrainData.tsv", header=0, delimiter="\t", quoting=3)

print(train.shape)
print(test.shape)
print(unlabeled_train.shape)

print(train['review'].size)
print(test['review'].size)
print(unlabeled_train['review'].size)
#(25000, 3)
#(25000, 2)
#(50000, 2)
#25000
#25000
#50000

train['sentiment'].value_counts()
#1    12500
#0    12500

part2- word2vec

from Word2VecUtil import Word2VecUtil

Word2VecUtil.review_to_wordlist(train['review'][0])[:10]
#['with', 'all', 'this', 'stuff', 'go', 'down', 'at', 'the', 'moment', 'with']

import nltk
nltk.download('punkt')

sentences = []
for review in train["review"]:
    sentences += Word2VecUtil.review_to_sentences(
        review, remove_stopwords=False)
    
for review in unlabeled_train["review"]:
    sentences += Word2VecUtil.review_to_sentences(
        review, remove_stopwords=False)

len(sentences)
#약 80만개의 문장이 포함됨
#795538

sentences[0][:10]
#['with', 'all', 'this', 'stuff', 'go', 'down', 'at', 'the', 'moment', 'with']


#gemsim 설치
#pip install gensim

import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', 
                    level=logging.INFO)



# 파라메터값 지정
num_features = 300 # 문자 벡터 차원 수
min_word_count = 40 # 최소 문자 수
num_workers = 4 # 병렬 처리 스레드 수
context = 10 # 문자열 창 크기 #문자열 창 크기(전후) 한번에 익식하는 데이터의 수
downsampling = 1e-3 # 문자 빈도 수 Downsample

from gensim.models import word2vec

# 모델 학습
model = word2vec.Word2Vec(sentences, 
                          workers=num_workers, 
                          size=num_features, 
                          min_count=min_word_count,
                          window=context,
                          sample=downsampling)
model

# 학습이 완료 되면 필요없는 메모리를 unload 시킨다.
model.init_sims(replace=True)

model_name = '300features_40minwords_10text'
# model_name = '300features_50minwords_20text'
model.save(model_name)

모델 결과 탐색

#여러 단어 중 관련 없는 단어(거리가 가장 먼 단어)는?
model.wv.doesnt_match("man woman child kitchen".split())
#'kitchen'


model.wv.doesnt_match("france england germany berlin".split())
#'berlin'


#가장 관련 높은 단어(거리가 가장 가까운 단어)는?
model.wv.most_similar("film")
#[('movi', 0.8592548370361328),
# ('flick', 0.6085095405578613),
# ('documentari', 0.5563615560531616),
# ('cinema', 0.5387020707130432),
# ('pictur', 0.5238867998123169),
# ('masterpiec', 0.4901632070541382),
# ('sequel', 0.48803427815437317),
# ('it', 0.4877569079399109),
# ('genr', 0.4808454215526581),
# ('effort', 0.4701777696609497)]

#happy로 하면 오류가 남 (stemming작업으로 원래 단어가 변형됨)

#model.wv.most_similar("happy")
model.wv.most_similar("happi") # stemming 처리 시 
#[('unhappi', 0.4212590157985687),
# ('bitter', 0.41711732745170593),
# ('sad', 0.41446375846862793),
# ('afraid', 0.3979133367538452),
# ('satisfi', 0.39775803685188293),
# ('glad', 0.37727090716362),
# ('happier', 0.3730996251106262),
# ('upset', 0.3724191188812256),
# ('reward', 0.3672931492328644),
# ('sappi', 0.3646870255470276)]

Word2Vec으로 벡터화 한 단어를 t-SNE 를 통해 시각화

# 참고 https://stackoverflow.com/questions/43776572/visualise-word2vec-generated-from-gensim
from sklearn.manifold import TSNE
import matplotlib as mpl
import matplotlib.pyplot as plt
import gensim 
import gensim.models as g

# 그래프에서 마이너스 폰트 깨지는 문제에 대한 대처
# mpl.rcParams['axes.unicode_minus'] = False

model_name = '300features_40minwords_10text'
model = g.Doc2Vec.load(model_name)

vocab = list(model.wv.vocab)
X = model[vocab]

print(len(X))
print(X[0][:10])
tsne = TSNE(n_components=2)

# 100개의 단어에 대해서만 시각화
X_tsne = tsne.fit_transform(X[:100,:])
# X_tsne = tsne.fit_transform(X)

#11986
#[ 0.00980356  0.06309218  0.05619177 -0.07966041  0.05931453 -0.06673906 -0.02150377  0.06647947  0.08702882  0.00142203]

df = pd.DataFrame(X_tsne, index=vocab[:100], columns=['x', 'y'])
df.shape
#(100, 2)

df.head(10)
#x	y
#with	2.823911	-9.014259
#all	-3.183646	-6.294578
#this	-0.505597	-2.188253
#stuff	-0.794164	-6.949909
#go	5.887387	0.091774
#down	4.398484	-11.293688
#at	5.728839	-9.630657
#the	-2.743879	-7.748000
#moment	-5.781786	-5.001288
#mj	-1.763150	-12.215151

fig = plt.figure()
fig.set_size_inches(40, 20)
ax = fig.add_subplot(1, 1, 1)

ax.scatter(df['x'], df['y'])

for word, pos in df.iterrows():
    ax.annotate(word, pos, fontsize=30)
plt.show()

Imgur

import numpy as np

def makeFeatureVec(words, model, num_features):
    """
    주어진 문장에서 단어 벡터의 평균을 구하는 함수
    """
    # 속도를 위해 0으로 채운 배열로 초기화 한다.
    featureVec = np.zeros((num_features,),dtype="float32")

    nwords = 0.
    # Index2word는 모델의 사전에 있는 단어명을 담은 리스트이다.
    # 속도를 위해 set 형태로 초기화 한다.
    index2word_set = set(model.wv.index2word)
    # 루프를 돌며 모델 사전에 포함이 되는 단어라면 피처에 추가한다.
    for word in words:
        if word in index2word_set:
            nwords = nwords + 1.
            featureVec = np.add(featureVec,model[word])
    # 결과를 단어수로 나누어 평균을 구한다.
    featureVec = np.divide(featureVec,nwords)
    return featureVec

def getAvgFeatureVecs(reviews, model, num_features):
    # 리뷰 단어 목록의 각각에 대한 평균 feature 벡터를 계산하고 
    # 2D numpy 배열을 반환한다.
    
    # 카운터를 초기화 한다.
    counter = 0.
    # 속도를 위해 2D 넘파이 배열을 미리 할당한다.
    reviewFeatureVecs = np.zeros(
        (len(reviews),num_features),dtype="float32")
    
    for review in reviews:
       # 매 1000개 리뷰마다 상태를 출력
        if counter%1000. == 0.:
            print("Review %d of %d" % (counter, len(reviews)))
       # 평균 피처 벡터를 만들기 위해 위에서 정의한 함수를 호출한다.
        reviewFeatureVecs[int(counter)] = makeFeatureVec(review, model, \
           num_features)
       # 카운터를 증가시킨다.
        counter = counter + 1.
    return reviewFeatureVecs

# 멀티스레드로 4개의 워커를 사용해 처리한다.
def getCleanReviews(reviews):
    clean_reviews = []
    clean_reviews = Word2VecUtil.apply_by_multiprocessing(\
        reviews["review"], Word2VecUtil.review_to_wordlist,\
        workers=4)
    return clean_reviews
%time trainDataVecs = getAvgFeatureVecs(\
    getCleanReviews(train), model, num_features ) 
%time testDataVecs = getAvgFeatureVecs(\
        getCleanReviews(test), model, num_features )

모델 생성

from sklearn.ensemble import RandomForestClassifier

forest = RandomForestClassifier(
    n_estimators = 100, n_jobs = -1, random_state=2018)


%time forest = forest.fit( trainDataVecs, train["sentiment"] )
#CPU times: user 1min 1s, sys: 166 ms, total: 1min 1s
#Wall time: 8.3 s

from sklearn.model_selection import cross_val_score
%time score = np.mean(cross_val_score(\
    forest, trainDataVecs, \
    train['sentiment'], cv=10, scoring='roc_auc'))

score
#0.9003752

Test

result = forest.predict( testDataVecs )
output = pd.DataFrame( data={"id":test["id"], "sentiment":result} )
output.to_csv('Word2Vec_AverageVectors_{0:.5f}.csv'.format(score), 
              index=False, quoting=3 )
output_sentiment = output['sentiment'].value_counts()
print(output_sentiment[0] - output_sentiment[1])
output_sentiment
#156
#0    12578
#1    12422

import seaborn as sns 
%matplotlib inline

fig, axes = plt.subplots(ncols=2)
fig.set_size_inches(12,5)
sns.countplot(train['sentiment'], ax=axes[0])
sns.countplot(output['sentiment'], ax=axes[1])

Imgur


4. NAVER_MOVIE 한글 텍스트처리

시간이 너무 오래걸려서 강사님이 코랩에서 돌릴 수 있는 코드를 제공해줌

import pandas as pd
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import re
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

train_data = pd.read_table('ratings_train.txt')
test_data = pd.read_table('ratings_test.txt')

train_data[:3]

#id	document	label
#0	9976970	아 더빙.. 진짜 짜증나네요 목소리	0
#1	3819312	흠...포스터보고 초딩영화줄....오버연기조차 가볍지 않구나	1
#2	10265843	너무재밓었다그래서보는것을추천한다	0

len(train_data)
#15000
len(test_data)
#5000

#데이터 중복 확인 -> nunique
train_data['document'].nunique(),train_data['label'].nunique()
#(146182, 2)

train_data.drop_duplicates(subset=['document'],inplace=True) #중복데이터 삭제
len(train_data)
#146183
train_data['label'].value_counts().plot(kind='bar')

Imgur

train_data.isnull().sum()
#id          0
#document    1
#label       0
#dtype: int64

print(train_data.isnull().sum())
print(train_data.document.isnull())
train_data.loc[train_data.document.isnull()]
train_data=train_data.dropna(how='any')

train_data['document']= train_data['document'].str.replace("[^ㄱ-하-ㅣ가-힣]","")
train_data.head()

train_data['document'].replace('',np.nan, inplace=True) #공란인 것을 nan으로 만들어라
train_data.loc[train_data.document.isnull()][:5]

test_data['document'].nunique()
#49157

test_data.drop_duplicates(subset=['document'],inplace=True)
test_data['document']= test_data['document'].str.replace("[^ㄱ-하-ㅣ가-힣]","")
test_data['document'].replace('',np.nan, inplace=True)
test_data=test_data.dropna(how='any')
print("테스트 데이터", len(test_data))

#테스트 데이터 48860

# 불용어 제거, 한국어의 조사, 접속사 등 -> 지속적으로 검토하면서 추가해서 삭제
stopwords = ['의','가','이','은','들','는','좀','잘','걍','과','도','를','으로','자','에','와','한','하다']

from konlpy.tag import Okt
okt = Okt()
okt.morphs("2000년 이후 최고의 서스팬스 영화.. 라고 했지만, 이걸 왜 봤을까?")

# 불용어 제거
# 형태소 토큰화
X_train = []
for sentence in train_data['document']:
    temp_X = []
    temp_X = okt.morphs(sentence, stem=True) # 토큰화
    temp_X = [word for word in temp_X if not word in stopwords] # 불용어 제거
    X_train.append(temp_X)

print(X_train[:3])

X_test = []
for sentence in test_data['document']:
    temp_X = []
    temp_X = okt.morphs(sentence, stem=True) # 토큰화
    temp_X = [word for word in temp_X if not word in stopwords] # 불용어 제거
    X_test.append(temp_X)
 
# 정수 인코딩
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)

print(tokenizer.word_index)
#{'영화': 1, '보다': 2, '을': 3, '없다': 4, '이다': 5, '있다'...

threshold = 3 # 빈도수가 3회 미만은 제거
total_cnt = len(tokenizer.word_index) # 단어의 수
rare_cnt = 0 # 등장 빈도수가 threshold보다 작은 단어의 개수를 카운트
total_freq = 0 # 훈련 데이터의 전체 단어 빈도수 총 합
rare_freq = 0 # 등장 빈도수가 threshold보다 작은 단어의 등장 빈도수의 총 합

# 단어와 빈도수의 쌍(pair)을 key와 value로 받는다.
for key, value in tokenizer.word_counts.items():
    total_freq = total_freq + value

    # 단어의 등장 빈도수가 threshold보다 작으면
    if(value < threshold):
        rare_cnt = rare_cnt + 1
        rare_freq = rare_freq + value
        
print('단어 집합(vocabulary)의 크기 :',total_cnt)
print('등장 빈도가 %s번 이하인 희귀 단어의 수: %s'%(threshold - 1, rare_cnt))
print("단어 집합에서 희귀 단어의 비율:", (rare_cnt / total_cnt)*100)
print("전체 등장 빈도에서 희귀 단어 등장 빈도 비율:", (rare_freq / total_freq)*100)

#단어 집합(vocabulary)의 크기 : 43752
#등장 빈도가 2번 이하인 희귀 단어의 수: 24337
#단어 집합에서 희귀 단어의 비율: 55.62488571950996
#전체 등장 빈도에서 희귀 단어 등장 빈도 비율: 1.8715872104872904

vocab_size = total_cnt - rare_cnt + 1 
# 전체 단어 개수 중 빈도수 2이하인 단어 개수는 제거. 0번 패딩 토큰을 고려하여 +1
print('단어 집합의 크기 :',vocab_size)
#단어 집합의 크기 : 19416

# 토크나이저는 텍스트 시퀀스를 숫자 시퀀스로 변환하는 정수 인코딩 과정에서 
# 이보다 큰 숫자가 부여된 단어들은 아예 계산하지 않음
tokenizer = Tokenizer(vocab_size) 
tokenizer.fit_on_texts(X_train)
X_train = tokenizer.texts_to_sequences(X_train)
X_test = tokenizer.texts_to_sequences(X_test)

print(X_train[:3])
#[[50, 454, 16, 260, 659], [933, 457, 41, 602, 1, 214, 1449, 24, 961, 675, 19], [386, 2444, 2315, 5671, 2, 222, 9]]

y_train = np.array(train_data['label'])
y_test = np.array(test_data['label'])

빈샘플(Empty samples) 제거

drop_train = [index for index, sentence in enumerate(X_train) if len(sentence) < 1]
drop_test = [index for index, sentence in enumerate(X_test) if len(sentence) < 1]

# 빈 샘플들을 제거
X_train = np.delete(X_train, drop_train, axis=0)
y_train = np.delete(y_train, drop_train, axis=0)
print(len(X_train))
print(len(y_train))

#145162
#145162

X_test = np.delete(X_test, drop_test, axis=0)
y_test = np.delete(y_test, drop_test, axis=0)
print(len(X_test))
print(len(y_test))
#48745
#48745

패딩

print('리뷰의 최대 길이 :',max(len(l) for l in X_train))
print('리뷰의 평균 길이 :',sum(map(len, X_train))/len(X_train))
plt.hist([len(s) for s in X_train], bins=50)
plt.xlabel('length of samples')
plt.ylabel('number of samples')
plt.show()
#리뷰의 최대 길이 : 69
#리뷰의 평균 길이 : 10.812485361182679

Imgur

# 전체 샘플 중 길이가 max_len 이하인 샘플의 비율이 몇 %인지 확인하는 함수
def below_threshold_len(max_len, nested_list):
  cnt = 0
  for s in nested_list:
    if(len(s) <= max_len):
        cnt = cnt + 1
  print('전체 샘플 중 길이가 %s 이하인 샘플의 비율: %s'%(max_len, (cnt / len(nested_list))*100))

max_len = 30
below_threshold_len(max_len, X_train)
#전체 샘플 중 길이가 30 이하인 샘플의 비율: 94.31944999380003

X_train = pad_sequences(X_train, maxlen = max_len)
X_test = pad_sequences(X_test, maxlen = max_len)

LSTM으로 네이버 영화 리뷰 감성 분류

from tensorflow.keras.layers import Embedding, Dense, LSTM
from tensorflow.keras.models import Sequential
from tensorflow.keras.models import load_model
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint

T = X_train.shape[1]
from tensorflow.keras.layers import Input, Embedding, LSTM, Dense
from tensorflow.keras.models import Model
i = Input(shape=(T,))
x = Embedding(vocab_size, 100)(i)
x = LSTM(128)(x)
x = Dense(1, activation='sigmoid')(x)
model = Model(i, x)
# model.summary()

#Model: "model"
#_________________________________________________________________
#Layer (type)                 Output Shape              Param #   
#=================================================================
#input_1 (InputLayer)         [(None, 30)]              0         
#_________________________________________________________________
#embedding (Embedding)        (None, 30, 100)           1941600   
#_________________________________________________________________
#lstm (LSTM)                  (None, 128)               117248    
#_________________________________________________________________
#dense (Dense)                (None, 1)                 129       
#=================================================================

X_train.shape[1]
30

# 검증 데이터 손실(val_loss)이 증가하면, 과적합 징후
# 검증 데이터 손실이 4회 증가하면 학습을 조기 종료(Early Stopping)
# 검증 데이터의 정확도(val_acc)가 이전보다 좋아질 경우에만 모델을 저장
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4)
mc = ModelCheckpoint('best_model.h5', monitor='val_acc', mode='max', 
                     verbose=1, save_best_only=True)

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=15, 
                    callbacks=[es, mc], batch_size=60, validation_split=0.2)


# loaded_model = load_model('best_model.h5')
# print("\n 테스트 정확도: %.4f" % (loaded_model.evaluate(X_test, y_test)[1]))
print("\n 테스트 정확도: %.4f" % (model.evaluate(X_test, y_test)[1]))

#테스트 정확도: 0.8364


5.Naver 영화 관련 팀 프로젝트

네이버 무비 크롤링 코드

import requests
from bs4 import BeautifulSoup as bs

address = 'https://movie.naver.com/movie/point/af/list.nhn?&page=1'
res = requests.get(address)
res.encoding = None
parse = bs(res.text, 'html.parser')
td_list = parse.select('td.title')

score_list =[]
result_text = []
# print(td_list)
for td in td_list:
    em = td.select('em')
    for extract_tag in em:
        score_list.append(extract_tag.getText())
        extract_tag.extract() #em태크 관련 내용은 삭제
    a = td.select('a')
    for extract_tag in a:
        extract_tag.extract() #a태크 관련 내용은 삭제
    div = td.select('div')
    for extract_tag in div:
        extract_tag.extract() #div태크 관련 내용은 삭제
        
    result_text.append(td.getText().strip()) #최종적으로 남은 텍스트파일(리뷰)만 남음
    

print(result_text)
print(score_list)


Leave a comment