336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

목차

1. SKT-AI/KoGPT2와 자연처(NLP)

2. 데이터 전처리 (나무위키, 블로그)

3. KoGPT2와 기능구현 (인풋 아웃풋 조정)

4. 짧은 텍스트 / 문장유사도

5. 배포 (구글 애즈, GA, AWS)

 


KoGPT2로 슬로건, 광고문구를 생성하고 이를 어떻게 개선할 것인가에 대해서 이어서 작성하겠습니다. 처음에 포스팅 하나에 넣으려고 했는데 생각보다 길어져서 나눴습니다. 이번 포스팅은 아웃풋과 인풋을 어떻게 조정했는지에 대해서 쓰겠습니다.

 

 

 짧은 텍스트 / 문장 유사도 찾기 

사용 이유 와 목적 : 인풋 데이터에 '금융'과 관련된 설명을 넣었는데, 갑자기 '좋은 일자리 만들어주세요'라는 문구가 뜬금없이 튀어나오게 된다. 데이터가 충분하면 이런 일이 없겠지만 추가로 모을 수는 없어서.. 이러한 결과값들을 최대한 배제하는 방법에 대해서 생각했다. 

 

- TF-IDF, CNN을 활용을 활용한 슬로건 분류

- Word2Vec을 사용해서 자주 등장하는 단어와 유사한 값을 지닌 단어가 포함된 문장을 노출

- 문장 유사도 (키워드, 요약, sentence transformer)

 

 

 

 

4-3 파이썬 팀프로젝트 CNN 카테고리 분류 모델 학습 및 평가

네 번째 프로젝트 1. 간단한 intro 2. 웹 크롤링 및 전처리 3. 모델 학습 및 평가 프로젝트를 하면서 느낀 보완사항은 : -데이터의 길이가 너무 짧으면 단어를 추출하는데 한계가 있고, 과적합이 발

0goodmorning.tistory.com

TF-IDF, CNN 카테고리 분류

슬로건, 광고문구에 인풋 값과 관련 없는 결과가 나올 확률은 크지 않기 때문에(이후에 보여드립니다), 카테고리(y)와 슬로건(x)을 학습시켜서 모델링을 해봤다.(이전 코드 참고)

 

하지만 아무리 수정을 해도 CNN 모델의 성능은 좋아지지 않았다. 40% 정확도가 최선으로 나왔는데, 이를 생각해보면 카테고리와 슬로건의 상관관계가 거의 않아서 정확도 개선이 되지 않는 것으로 보인다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from konlpy.tag import Kkma
import collections 
 
slogan_list2 = []
for slogan in slogan_list :
    kkma = Kkma()
    slogan_ = kkma.nouns(slogan)
    slogan_list2.append(slogan_)
 
#중첩 리스트 제거
list_removed = sum(slogan_list2, []) 
 
#단어 카운팅
dict={} 
dict=collections.Counter(list_removed) 
dict = sorted(dict.items(), key=lambda x: x[1], reverse=True)
#print(dict)
 
#가장 많이 나오는 
keyword = next(iter(dict))[0#사전 첫번째 단어
print(keyword)
cs

꼬꼬마로 형태소 분류를 해서 가장 자주 나오는 명사들의 개수를 카운팅하고, 단어 : 개수를 딕셔너리로 만든다.

 

 

 

1
2
3
4
5
6
7
from gensim.models import Word2Vec
 
model = Word2Vec(slogan_list2, vector_size=100, window=4,
                           min_count=2, workers=4, epochs=50, sg=1
 
#가장 많이 나오는 단어와 유사한 단어
model.wv.most_similar(keyword,topn=10)
cs

결과값을 확인 했을 때, 나쁘지 않게 나왔지만.. 이 단어들로이 포함되지 않는 문구를 필터링한다면, 걸러져야할 광고문구보다 괜찮은 광고문구들도 대부분 걸러질 것으로 보여서 다른 방법을 또 고민했다.

 

텍스트 유사도를 구할 때, 추천 시스템에서 사용했던 TF-IDF, 코사인 유사도를 사용하려고 했다. 하지만 파이널 프로젝트에는 적용하기 힘들었던 이유가 비교할 문장이 길지 않고, 한 문장으로 이루어졌기 때문이다. 프로젝트가 끝나고 현재 Textrank나 문장 요약, 키워드 추출 등을 공부하고 있는데, 이 방법도 적합하지 않은 방법이었다.

 

 

 

그래서 찾게 된 것이 Sentence Transformer다. 그런데 생각보다 구글에는 예시가 많지 않았다. 영어 모델은 유사도 높게 나왔는데, 한글의 경우 위의 사진처럼 '한 남자가'라는 단어가 일치한다고 유사도 94퍼센트가 나오는 아이러니한 현상이 발견됐다. 구글링을 더 하다가 Ko-Sentence-BERT-SKTBERT 모델이 나왔는데 오류 때문에 잘 되지 않았다.

 

 

 

 

 

Pretrained Models — Sentence-Transformers documentation

We provide various pre-trained models. Using these models is easy: Multi-Lingual Models The following models generate aligned vector spaces, i.e., similar inputs in different languages are mapped close in vector space. You do not need to specify the input

www.sbert.net

더 검색하다가 발견한 모델! 유사도가 SKTBERT에서 테스트한 예제들과 결과가 비슷하게 나와서 사용하기로 했다.

 

 

 

 

결과가 좋다! 그리고 예상했던대로 인풋데이터와 관련이 없는 결과(슬로건, 광고문구)는 다른 문구들과 비교했을 때 전체적으로 유사도가 높지 않음을 확인할 수 있다. / RPG 게임에 웬 패션 스타일인가?

 

하지만 여기서도 또 문제 아닌 문제가 생겼다. 필터링 되는 슬로건 중에서도 키워드만 바꾸면 괜찮아보이는 슬로건들이 있다. 그래서 이걸 살리는 것도 좋지 않겠냐는 멘토님의 말씀이 있어서.. 사용자가 직접 민감도를 설정해 필터링의 할 수 있는 기능을 추가했다. 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#문장 유사도
#개별 추출
from sentence_transformers import SentenceTransformer, util
import numpy as np
import random
import pandas as pd
 
#모델 불러오기
model = SentenceTransformer('distiluse-base-multilingual-cased-v1')
 
#회사 리스트
company = pd.read_csv('datasets\company_list.csv')
company_list = company.company.values.tolist() #회사명을 리스트로
 
#비교할 슬로건 선택 
no_sim_list = [] #관련 없는 슬로건 추출
total_slogan = [] #슬로건 전체를 담는 리스트 / 중첩리스트용
= 0
try : #n이 증가하지 않을 경우 무한루프? 
    while n < 5 :
        #유사도 비교할 리스트
        corpus = kor_list
        corpus_embeddings = model.encode(corpus, convert_to_tensor=True)
        
        #유사도 비교할 문장
        query = random.sample(kor_list, 1)
        print("Query : ", query)
        
        #코사인 유사도 사용하여 5개 유사한 슬로건 찾기
        top_k = 6 #query 포함 top 5개
        query_embedding = model.encode(query, convert_to_tensor=True)
        cos_scores = util.pytorch_cos_sim(query_embedding, corpus_embeddings)[0]
        cos_scores = cos_scores.cpu()
        top_results = np.argpartition(-cos_scores, range(top_k))[0:top_k] # np 사용 이유 : 순위를 순서대로 맞추기 위함
        
        #민감도 비교하기 위한 유사도 더하기      
        sum = 0
        for idx in top_results[1:top_k]:
            sum += cos_scores[idx]
        f_sum = float(sum)/5 #tensor to float
        print(f_sum)
        
        #사용자 인풋 민감도 비교    
        sim_list = [] #유사 슬로건 담을 리스트
        sim_list2 = [] #수정된 슬로건 담을 리스트
        if f_sum >= input_sim / 100 :
            for idx in top_results[0:top_k-1]:
                copy_ = corpus[idx].strip()
                sim_list.append(copy_)
            
            print(sim_list)
            sim_list2 = sim_list    
            for i in range(len(sim_list2)) :
                for c in company_list :
                    if c in sim_list2[i] :
                        sim_list2[i] = sim_list2[i].replace(c,'*'*len(c))
       
            total_slogan.append(sim_list2)
            kor_list = differ_sets(kor_list, sim_list)
            n += 1
            #print(len(kor_list))
            
        else : 
            no_sim_list.append(query)
            kor_list = differ_sets(kor_list, query)  #kor_list에서 query를 제거 
            print('관련이 없는 슬로건 데이터 추가'
  
                
                
except :
    print('데이터가 부족합니다.')
    
print('완료')
#print(no_sim_list)
 
print(total_slogan)
cs

우선 슬로건 문구를 포함한 리스트에서 영어로만 이뤄진 슬로건을 제외해 kor_list를 만들었다. 영어가 포함된 문장의 경우 유사도가 얼추 비슷하게 나왔는데, 영어로만 이루어진 문장은 문장 유사도 성능이 떨어져서 아예 제외시켰다. 이후 한 개를 랜덤으로 뽑아서 유사도가 비슷한 값 5개를 뽑아서 평균을 냈을 때, input_sim과 크기를 비교해서 살릴지 버릴지 고민을 했다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#차집합 함수
def differ_sets(a,b) : 
    lst = list(set(a)-set(b))
    return lst
    
 
#영어 슬로건 따로 추출
import re
 
eng_list = []
for slogan in slogan_list :
    slogan_ = re.sub('[^A-Za-z가-힣]''',slogan) #영어 한글만 남기기
    slogan_ = re.sub('[^가-힣]',' ', slogan_) #영어는 공백으로 남긴다
    if slogan_.isspace():    #isalpha()는 영어 또는 한글 유무를 찾아서 안 됨
        eng_list.append(slogan)
        
print(eng_list)
 
#차집합 
kor_list = differ_sets(slogan_list, eng_list) #한국 슬로건만 있는 리스트
cs

영어로만 이루어진 문장을 뽑는데 애를 먹었다. isalpha()를 사용하게 되면 영어로만 이루어진게 아니라, 한글이 있을 때도 True를 반환하기 때문에 다른 방법을 사용해야했다. 우선 공백을 없애고, 한글만을 남게한다. 만약 영어로만 이루어졌으면 isspace()함수에서 True를 반환하기 때문에 영어만 포함된 문장을 뽑을 수 있다. 반대로 한글로만 이뤄진 문장이 필요하면 '^가-힣' 대신 '^A-Za-z'을 활용하면 된다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
company_list = company.company.values.tolist()
len(company_list)
 
for c in company_list :
    for i in range(len(total_slogan) :
           if c in total_slogan[i] :
            # print(c)
            slogan_edit = total_slogan[i].replace(c,'*'*len(c))
            # print('수정')
 
slogan_edit
cs
기업, 제품, 서비스 광고문구를 크롤링했기 때문에, 슬로건 자체에 회사명, 제품, 서비스가 들어가는 경우가 있다. 그래서 이를 별표처리를 해줬다. 여기서 문제가 끝난 줄 알았는데.... 아웃풋뿐만 아니라 인풋 데이터도 조정해야 했다. 토크나이징을 할 때 문제가 있었다. 우리는 손크롤링을 할 때 회사 설명에 주로 명사 위주의 키워드를 넣었는데, 토크나이징을 할 때 띄어쓰기 유무에 따라서 결과값이 많이 달라졌다.
 
 
예를 들어 '패션의류'의 경우, 우리는 '패션 의류'를 설명에 입력했는데 토크나이저가 패션(명사) 의(조사) 류(명사) 이런 식으로 인식하기도 하고 제각각 달랐다. 그래서 명사 띄어쓰기 필요성이 느껴져서 형태소 분류를 진행했다. mecab, okt, kkma를 사용해봤는데 전체적인 성능은 mecab이 좋았으나, 명사 추출은 kkma가 조금 더 딱딱하게 잘 끊어냈다.

 

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"""
** 가이드 **
-input_sim
0~15 : 가장 자유로움 
15~30 : 자유로움
30~45 : 조금씩 걸러짐
45~60 : 많이 걸러짐   / 강제로 기업 설명을 인식시켜서 더 제한적인 슬로건
60~ : 거의 다 걸러짐  / 슬로건 100개를 했을 경우 유사율 평균 70 이상은 거의 없음
 
-input_text
최대한 명사 위주의 설명
영어를 쓸경우 뒤에 나오는 단어와 붙여쓰면 더 좋은 결과 ex) 'LED 마스크'보다는 'LED마스크' 
"""
 
input_sim = 40  # input data 유사성 민감도 지정 / 숫자가 작을수록 관련 없는게 나올 확률이 커짐 / 최소 50이상 설정
input_text = '커피전문기업'
input_text_list = input_text.split(' '# input data 띄어쓰기로 나누기
eng_text = re.sub('[^a-zA-z]',' ',input_text).strip()
 
kkma = Kkma() #꼬마를 작용시 분모가 중복 되는 경우가 생김, 이를 제거해야 함
copy=[]
for txt in input_text_list :
    txt_ = kkma.nouns(txt)
    # print(txt_)
 
    if len(txt_) > 1 : #(명사가 쪼개졌을 경우)
        max_string = max(txt_, key=len#가장 긴 값을 제거 (중복값)
        txt_.remove(max_string)    
    
    copy += txt_
# print(copy)
 
if len(copy) >3 : 
    del_list = []
    for i in range(math.ceil(len(copy)-2)) : 
        overlap_txt = ''.join((itemgetter(i,i+2)(copy))) # abc를 kkma로 쪼갤 경우 =>  a, ab, abc, b, c => abc 제거 => ab를 제거하는 과정 
        if overlap_txt in copy :
            del_list.append(overlap_txt) 
    #print(del_list)
    [i for i in del_list if not i in copy or copy.remove(i)] #차집합인데 순서가 안 바뀜 
text = ' '.join(copy)
 
if input_sim > 45 :
    text += ',' #,를 넣을 경우 강제로 기업설명으로 인식시켜서 조금 더 제한적인 슬로건 등장 
 
#영어 슬로건이 포함 된 경우 초기상태로
if eng_text :
    if eng_text in input_text :
        text = input_text
    
print(text)
cs
이번에도 산 넘어 산이었다. kkma가 딱딱하게 명사를 잘 끊어내는 것과 달리, 만약 합성어가 ab이면 우리는 a와 b 결과만 나오면 되는데 a, ab, b로 쪼개지면서 다시 ab값이 등장해서 문제가 됐다. 그래서 단어 길이가 최대인 값을 지우면 되겠거니 했는데.... abc의 경우 a, ab, abc, b, c로 쪼개져서 abc 뿐만 아니라 ab를 지워야했다. 이를 어떻게 해야할까 하다가 저 코드가 나오게 됐다. 리스트끼리 뺐을 때도 리스트 순서는 바뀌지 않으면서 리스트를 유지하는 법도 배웠다.
 
또 영어도 한글과 붙어 있으면 값이 다르게 나왔는데, 이게 gpt2가 현재 단어 다음에 나올 단어의 확률을 예측하는 방식으로 학습했기 때문에 ㅠㅠ 붙여쓰는 것과 띄어쓰기를 하는 것은 결과가 달랐다. 원래 이것도 하나하나 코드를 짜려다가 조금 더 사용자에게 자율성을 주자고 조교님이 그러셔서 영어가 포함이 되면 인풋 데이터 그대로 모델에 입력이 됐다.
 
아직도 공부할 부분은 많은 것 같다..
 

 

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

목차

1. SKT-AI/KoGPT2와 자연처(NLP)

2. 데이터 전처리 (나무위키, 블로그)

3. KoGPT2와 기능구현 (인풋 아웃풋 조정)

4. 짧은 텍스트 / 문장유사도

5. 배포 (구글 애즈, GA, AWS)

 

모바일로 보니 너무 불편해서 수정 좀 했습니다 ㅠㅠ


 

결과물부터 보자면, kogpt2를 사용해서 슬로건을 생성하였다. 웹으로 서비스를 배포(AWS)하고, 구글애즈로 노출을 시키면서, GA(구글애널리틱스)로 트래픽을 분석하여 더 개선시키는 방향으로 진행했다.

 

 

 

 

 

 

GPT3 사용법, 설치부터 쉽게! 슬로건, 동화 만들기까지? KoGPT3 등장?

6-1 파이널 프로젝트 : 자연어처리, kogpt2를 이용한 슬로건 생성 목차 1. SKT-AI/KoGPT2와 자연처(NLP) 2. 데이터 전처리 (나무위키, 블로그) 3. KoGPT2와 기능구현 (인풋 아웃풋 조정) 4. 짧은 텍스트 / 문장

0goodmorning.tistory.com

읽으면 유용한 글 : GPT3

 

 

 

 

 

패키지, 툴 등 사용 기술

기간 : 2021.07.12 ~ 2021.08.18 (1 달 조금 넘게)

사용 기술은 그림을 참고. 

 

파이널 프로젝트라서 거창한 것을 해보려고 다양한 아이디어를 냈으나 쉽게 주제를 결정할 수 없었다. 이것저것해보다가 시간은 4주도 남지 않아서 처음에 진행하려고 했던 슬로건 생성을 해보자고 했다. 과연 슬로건처럼 간단하면서 임팩트 있는 문구를 인공지능이 생성할 수 있을까? 하는 의문이 있었지만 인공지능은 해냈다. (우리가 해냈다)

 

이번 시간을 통해서 자연어처리에 대해서 많이 공부를 할 수 있었고, gpt2와 프로젝트가 끝난 이후 gpt3에 대해서도 추가적으로 공부를 하였다. 주로 인공지능을 통해서 분류를 하거나 예측을 하였는데, 새로운 문장을 생성한다는게 참 신기했다.

 

 

 

GPT(Generative Pre-trained Transformer)는 언어모델로 '자연어 디코더 모델'이라고 생각하면 쉽다. 자연어 처리 기반이 되는 조건부 확률 예측 도구이며, 단어가 주어졌을 때 다음에 등장한 단어의 확률을 예측하는 방식으로 학습이 진행 된다. 문장 시작부터 순차적으로 계산한다는 점에서 일방향을 보인다.

 

 

 

 

 

KoGPT2 skt-ai에서 만든 한글 디코더 모델. KoGPT2에서는 기존 GPT의 부족한 한국어 성능을 극복하기 위해 많은 데이터(40g)로 학습된 언어 모델로서 한글 문장 생성기에서 좋은 효과를 보인다고 한다. 시 생성, 가사 생성, 챗봇 등의 서비스 구현한 사례가 있다.

 

단순히 모델 사용보다 자연어를 어떻게 처리할지 많은 공부를 하게 됐다. 간단하게 정리한 부분을 추가적으로 공유하려고 한다.

 

자연어 처리로 할 수 있는 것들 

-텍스트 분류 (스팸 메일)

-감성 분석 (긍/부)

-내용 요약 (추출/ 생성)

-기계 번역 (번역)

-챗봇

 

 

 

 

출처 : dreamstime

자연어 처리 과정

-Preprocessing (전처리) : stopwords 불용어 제거, 형태소 분석, 표제어 추출 / 컴퓨터가 자연어를 처리할 수 있게

-Vectorization (벡터화)  : 원핫인코딩, count vectorization, tfdif, padding

-Embedding : word2vec, doc2vec, glove, fasttext

-Modeling : gru, lstm. attention

 

*Transfer learning (전이 학습) : pretrain한 임베딩을 다른 문제를 푸는데 재사용 

*Fine-tuning (파인 튜닝) : pretrain된 모델을 업데이트하는 개념. / 엔드투엔드에서 발전

 

 

 

임베딩

- 자연어를 숫자의 나열인 벡터로 바꾼 결과 혹은 과정 전체 (벡터 공간에 끼워넣는다)

- 말뭉치의 의미, 문법 정보가 응축되어 있음, 단어/문서 관련도 계산 가능

- 품질이 좋으면 성능이 높고 converge(수렴)도 빠르다

- NPLM(최초) / Word2Vec(단어수준) / ELMo(문장수준), BERT, GPT

 

단어 문장간 관련도 예상 / t-SNE 차원 축소 100차원->2차원으로 / Word2Vec 개선 모델 FastText / 행렬 분해 모델 / 에측 기반 방법 / 토픽 기간 방법 등으로 나뉨

 

잠재의미분석 : 말뭉치의 통계량을 직접적으로 활용

희소 행렬 - 행렬 대부분의 요소 값 0

단어-문서행렬, TF-IDF, 단어-문맥 행렬, 점별 상호정보량 행렬

 

 

단어수준 임베딩 단점 : 동음이의어 분간 어려움 

=> ELMo, BERT, GPT 시퀀스 전체의 문맥적 의미 함축해서 전이학습 효과가 좋음

 

*다운스트림 태스크 : 풀고 싶은 자연어 처리의 구체적 문제들 

(품사 판별, 개체명 인식, 의미역 분석, 형태소 분석, 문장 성분 분석, 의존 관계 분석, 의미역 분석, 상호참조 해결)

*업스트팀 태스크 : 다운스트렘 태스크 이전에 해결해야할 괴제. 단어/문장 임베딩을 프리트레인하는 작업

 

토큰 : 단어, 형태소, 서브워드

토크나이즈 : 문장을 토큰 시퀀스로 분석

형태소 분석 : 문장을 형태소 시퀀스로 나누는 과정

 

 

 

 

출처 : 벡터가 어떻게 의미를 가질까?

TF-IDF : 백오브워즈 가정(어떤 단어가 많이 쓰였는가) / term frequency inverse document

순서 정보는 무시하는 특징이 있다. 주제가 비슷하면 단어 빈도 또는 단어 비슷할 것이다. (정보 검색 분야에서 많이 쓰인다.)

사용자 질의에 가장 적절한 문서 보여줄 때 코사인 유사도를 구해서 보여준다.

 

-TF : 특정 문서에 얼마나 많이 쓰이는지

-DF : 특정 단어가 나타난 문서의 수

-IDF : 전체 문서를 해당 단어의 DF로 나눈 뒤 로그를 취함. 값이 클수록 특이 단어

(단어의 주제 예측 능력과 직결 됨)

 

 

 

출처 : 브런치

ELMo, GPT : 단어가 어떤 순서로 쓰였는가? 주어진 단어 시퀀스 다음에 단어가 나올 확률이 어떤게 큰지? 

n-gram (말뭉치 내 단어들을 n개씩 묶어서 빈도를 학습), 다음 단어 나타날 확률 조건부확률의 정의를 활용해 최대우도추정법으로 유도 => 한 번도 나오지 않으면 확률이 0이 되므로 보완을 해줘야 한다

 

마코프 가정 (한 상태의 확률은 그 직전 상태에만 의존한다) => 그래도 등장하지 않았던 단어 나오면 0이 되기 때문에 백오프, 스무딩 방식 (높은 빈도를 가진 문자열 등장확률을 일부 깎고, 등장하지 않은 케이스에 확률 부여

 

뉴럴 네트워키 기반 언어 모델 : 다음 단어를 맞추는 과정 학습 (엘모,지피티) 

마스크 언어 모델 : 문장 전체를 보고 중간에 있는 맞추기 (BERT)

 

 

 

 

출처 : Towards Data Scince

Word2Vec : 어떤 단어가 같이 쓰였는가 (분포 가정) 단어의 의미는 곧 그 언어에서의 활용이다?

타깃단어와 그 주위에 등장하는 문맥단어 계산 / 분포 정보가 곧 의미? 의문점

형태소 분류 - 계열 관계 : 해당 형태소 자리에 다른 형태소가 대치 될 수 있는지

품사 분류 – 기능(주어, 서술어), 의미(같은 뜻), 형식(이름, 성질, 상태)

형태는 같지만 기능과 의미가 달라질 수 있다.(기능과 분포는 다르지만 밀접한 관련)

PMI 두 단어의 등장이 독립일 때 대비해 얼마나 자주 같이 등장하는지 (단어 가중치) 단어-문맥 행렬

 

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

다섯 번째 프로젝트

 

1. 추천 시스템에 대한 생각

2. 추천시스템(TFIDF, Word2Vec)

3. GUI로 앱처럼 사용할 수 있게 (PyQt5)


항상 과정이 비슷해서 앞에 부분은 코드는 생략합니다.

 

우선 로그인이 필요한 서비스라서 동적 크롤링으로 후기를 긁어왔다. 그리고 추가로 병원 이름, 클리닉명, 주소, 링크, 전화번호는 로그인 없이도 크롤링이 가능해서 BS4로 긁어왔다. 과정은 크롤링한 데이터를 전처리하고, 워드클라우드를 생성해서 불용어를 처리하고(중요), 하나의 문장으로 합쳤다.  

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.io import mmwrite, mmread #매트릭스 저장할 때 mmwrite, 읽을 땐 mmread
import pickle #변수 타입 그대로 저장
 
df_review_one_sentence = pd.read_csv('./preprocess/total_hospital_review_one_sentence.csv', index_col=0)
print(df_review_one_sentence.info())
 
Tfidf = TfidfVectorizer(sublinear_tf=True)      # sublinear_tf는 값의 스무딩 여부를 결정하는 파라미터
Tfidf_matrix = Tfidf.fit_transform(df_review_one_sentence['reviews'])       # fit_transform 된 Tfidf를 갖고 있으면 추후 데이터 추가 가능하므로 따로 저장
 
with open('./models/tfidf.pickle''wb'as f:
    pickle.dump(Tfidf, f)       # Tfidf 저장
 
mmwrite('./models/tfidf_hospital_review.mtx', Tfidf_matrix)        # 유사도 점수 매트릭스 저장
cs

TfidfVectorizer로 문서 내에서 단어 토큰을 생성하고, 각 단어의 수와 가중치를 조정하여 문자를 순서 벡터로 변환했다. 이 과정은 따로 건드리는 부분이 없다. 다만 데이터 전처리가 중요하다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pandas as pd
from sklearn.metrics.pairwise import linear_kernel
from scipy.io import mmwrite, mmread
import pickle
from gensim.models import Word2Vec
 
df_hospital_review_one_sentence = pd.read_csv('./preprocess/total_hospital_review_one_sentence.csv', index_col=0)       # 리뷰 읽어 df 생성
 
Tfidf_matrix = mmread('./models/tfidf_hospital_review.mtx').tocsr()        # matrix 불러오기
with open('./models/tfidf.pickle''rb'as f:      # tfidf 불러오기
    Tfidf = pickle.load(f)
 
def getRecommendation(cosine_sim):      # 코사인 유사도롤 활용하여 유사한 병원 추천하는 함수
    simScore = list(enumerate(cosine_sim[-1]))      # 각 코사인 유사도 값에 인덱스 붙임
    simScore = sorted(simScore, key=lambda x:x[1], reverse=True)        # simScore(코사인 유사도, x[1])가 큰 것부터 정렬. reverse=True 내림차순 정렬.
    simScore = simScore[1:11]       # 유사한 병원 10개 리스트. 0번 값은 자기 자신이므로 배제.
    hospital_idx = [i[0for i in simScore]     # 인덱스(i[0]) 뽑아서 리스트 생성
   recHospitalList = df_hospital_review_one_sentence.iloc[hospital_idx]        # df에서 해당 병원 리스트 추출
    return recHospitalList
 
 
#병원명 검색
hospital_idx = df_hospital_review_one_sentence[df_hospital_review_one_sentence['names']=='김이비인후과의원'].index[0]      # 병원 이름으로 인덱스 값 찾기
# hospital_idx = 127
# print(df_review_one_sentence.iloc[hospital, 0])
 
cosine_sim = linear_kernel(Tfidf_matrix[hospital_idx], Tfidf_matrix)      # linear_kernel은 각 Tfidf 값을 다차원 공간에 벡터(방향과 거리를 가짐)로 배치한 뒤, 코사인 유사도를 구해줌. cosine = 삼각형의 밑변 / 윗변
                                                                       # 비슷한 영화는 유사한 위치에 배치됨. 유사할수록 각이 줄어드므로 코사인 값이 1에 가까워짐. -1에 가까울수록 반대, 0에 가까울수록 무관
recommendation = getRecommendation(cosine_sim)
# print(recommendation)
print(recommendation.iloc[:, 1])
cs

pickle에 저장한 Tfidf를 불러와서 코사인 유사도를 활용하여 유사한 병원을 추천해준다. 우선 병원의 idx 값을 구한다. 그리고 추천시스템에 이와 유사한 병원들의 리스트를 반환 받아서 출력한다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
from gensim.models import Word2Vec
 
review_word = pd.read_csv('./preprocess/total_cleaned_review.csv', index_col=0)
print(review_word.info())
cleaned_token_review = list(review_word['cleaned_sentences'])
print(len(cleaned_token_review))
cleaned_tokens = []
count = 0
for sentence in cleaned_token_review:
    token = sentence.split(' ')     # 띄어쓰기 기준으로 각 단어 토큰화
    cleaned_tokens.append(token)
# print(len(cleaned_tokens))
# print(cleaned_token_review[0])
# print(cleaned_tokens[0])
embedding_model = Word2Vec(cleaned_tokens, vector_size=100, window=4,
                           min_count=20, workers=4, epochs=100, sg=1)   # vector_size는 몇차원으로 줄일지 지정, window는 CNN의 kernel_size 개념, 앞뒤로 고려하는 단어의 개수를 나타냄
                                                                        # min_count는 출현 빈도가 20번 이상인 경우만 word track에 추가하라는 의미(즉, 자주 안 나오는 단어는 제외)
                                                                        # workers는 cpu 스레드 몇개 써서 작업할 건지 지정, sg는 어떤 알고리즘 쓸건지 지정
embedding_model.save('./models/word2VecModel_hospital.model')
print(embedding_model.wv.vocab.keys())
print(len(embedding_model.wv.vocab.keys()))
cs

Word2Vec 단어의 의미를 다차원에 분산시켜 표현하여, 단어간 위치를 파악해서 유사도를 분석해준다. Skip-Gram의 경우 중심 단어를 보고 주변에 어떤 단어가 있을지 예측. CBoW는 주변에 있는 단어들로 중간에 있는 단어를 예측한다.

 

 

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#리뷰 기반 키워드 검색
embedding_model = Word2Vec.load('./models/word2VecModel_hospital.model')
key_word = '친절'
 
sim_word = embedding_model.wv.most_similar(key_word, topn=10)
labels = []
sentence = []
for label, _ in sim_word:
    labels.append(label)
print(labels)
for i, word in enumerate(labels):
    sentence += [word] * (9-i)
sentence = ' '.join(sentence)
#sentence = [key_word] * 10      # tf에서 높은 값을 가지도록 리스트 복사
print(sentence)
 
sentence_vec = Tfidf.transform([sentence])
cosine_sim = linear_kernel(sentence_vec, Tfidf_matrix)
recommendation = getRecommendation(cosine_sim)
print(recommendation)
cs

키워드를 기반으로 하여 병원을 추천해주는 시스템이다.

 

 

+ Recent posts