일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 블로그 이전했어요
- 문자열 조작
- 머신러닝
- leetcode125
- leetcode 121
- leetcode 344
- docker로 airflow 설치하기
- Hadoop
- leetcode 238
- Hortonworks Sandbox
- wargame.kr
- leetcode 561
- Python
- leetcode
- leetcode 5
- 데이터레이크와 데이터웨어하우스
- 올바른 변수명 짓기
- airflow docker
- 컴퓨터구조
- leetcode 49
- MapReduce 실습
- 스파크 완벽 가이드
- leetcode 15
- ctf-d
- leetcode 234
- webcrawler
- leetcode 937
- 빅데이터를 지탱하는 기술
- leetcode 819
- 배열
- Today
- Total
HyeM
6부_6장 마르코프 체인과 LSTM으로 문장 생성하기 본문
01. 마르코프 체인과 LSTM/RNN
이번 챕터에서는 문장 자동 생성을 하는 것이 목표이다.
마르코프체인과 LSTM/RNN은 서로 다른 방식의 문장 생성 방식이다.
- 마르코프체인 : 확률을 기반으로 문장을 이어 붙임
- LSTM/RNN : 머신러닝으로 다음에 위치한 문장 예측하여 문장 생성.
02. 마르코프 체인
마르코프체인 (워드 샐러드) : 확률을 기반으로 하는 방법
-> 마르코프체인을 이용하면 기존 문장을 기반으로 문장을 자동으로 생성가능함.
# 마르코프 성질이란 ?
- 의미 :
과거의 상태를 무시하고, 현재의 상태만을 기반으로 다음 상태를 선택하는 것
- 표현 :
현재 상태 : qi 다음상태 : qj
다음상태로 이동할 확률 : P(qj | qi ) #현재상태와 다음상태만을 기준으로 결정됨.
# 마르코프 체인의 기능 :
주기능 : 문장 생성 // 부기능 : 문장요약하기 ( 기계적으로 문장생성하고, twitter에 자동으로 등록하는 트위터 봇에 사용됨.)
# 문장 생성 과정 (예. 사전만들기_"그는 고양이를 좋아합니다")
- 문장을 단어로 분할 (형태소 분석) (예. 그|는|고양이|를|좋아|합니다)
- 단어의 전후연결을 딕셔너리에 등록
(예. 그|는|고양이
는|고양이|를
고양이|를|좋아
를|좋아|합니다. ) <- N-gram과 다르게 문자가 아닌 단어 단위로 처리함 - 사전을 이용에 임의의 문장을 생성
( 예. *만약 "개"와 관련된 속담을 등록했다 가정*
개|도|닷새|가|되면|주인|을|안다|.
기르던|개|에게|다리|가|물렸다|.
닭|쫓던|개|지붕|쳐다|보듯|한다|.
똥|묻은|개|가|겨|묻은|개|나무란다|.
"개"로 문장을 시작했다면, "개"뒤에 올 수있는 것은 "도/에게/지붕/가"이다/
예를 들어 "가"를 연결하고 그 뒤를 마저 연결하면,
"개 + 가 +되면 + 주인 + 을 + 안다 + . "라는 독창적인 문장이 만들어진다.
=> 마르코프체인은 단어의 실질적인 의미 연관성을 생각하지 않음. -> 이상한 문장이 만들어지기도 한다.
[실습1] 마르코프체인 구현
실제로 마르코프 체인으로 문장을 만드는 프로그램을 짜본다.
- 코드 흐름: 형태소 분석(koNLPy) -> 사전 만들기 -> 사전 기반 문장 만들기
- 테스트 파일 : "토지"의 텍스트 파일
- 띄어쓰기 규칙 : 한국어의 띄어쓰기 규칙은 복잡하므로, 네이버 맞춤법 검사기를 이용한다.
<코드 : markov.py >
import os
import codecs
from bs4 import BeautifulSoup
from konlpy.tag import Twitter
import urllib.request
import os, re, json, random
#현재 디렉토리 변경하기
os.chdir('C:/Users/xxx/Desktop/python 코드/machineLearning/chatbot6')
#네이버 맞춤법 검사 요청에 user-agent 헤더 추가
import requests
headers = {
'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 6.1; ko; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 IPMS/A640400A-14D460801A1-000000426571', # firefox UserAgent
}
# 마르코프 체인 딕셔너리 만들기 --- (※1)
def make_dic(words):
tmp = ["@"] #문장의 시작은 @
dic = {} #사전은 딕셔너리 자료형
for word in words:
tmp.append(word)
if len(tmp) < 3: continue #세 단어가 한 세트
if len(tmp) > 3: tmp = tmp[1:]
set_word3(dic, tmp) #함수 호출
if word == ".":
tmp = ["@"]
continue
return dic
# 딕셔너리에 데이터 등록하기 --- (※2)
def set_word3(dic, s3):
w1, w2, w3 = s3
if not w1 in dic: dic[w1] = {}
if not w2 in dic[w1]: dic[w1][w2] = {}
if not w3 in dic[w1][w2]: dic[w1][w2][w3] = 0
dic[w1][w2][w3] += 1
# 문장 만들기 --- (※3)
def make_sentence(dic):
ret = []
if not "@" in dic: return "no dic"
top = dic["@"]
w1 = word_choice(top) #단어 무작위 가져옴.
w2 = word_choice(top[w1])
ret.append(w1)
ret.append(w2)
while True:
w3 = word_choice(dic[w1][w2])
ret.append(w3)
if w3 == ".": break
w1, w2 = w2, w3
ret = "".join(ret)
# 띄어쓰기
params = urllib.parse.urlencode({ #params는 뒤에서 맞춤법 검사기에 들어간다.
#urllib.parse.urlencode : str(byte)객체를 포함할 수 있는 매핑 객체나 두 요소 튜플의 시퀀스를 아스키텍스트 "문자열"로 변환한다.
"_callback": "", #callback : 다른 코드의 인수로서 넘겨주는 실행 가능한 코드
"q": ret
})
# 네이버 맞춤법 검사기를 사용합니다.
data = urllib.request.urlopen("https://m.search.naver.com/p/csearch/ocontent/spellchecker.nhn?" + params) #책의 url을 바꿈
data = data.read().decode("utf-8")[1:-2]
data = json.loads(data)
data = data["message"]["result"]["html"]
data = soup = BeautifulSoup(data, "html.parser").getText()
# 리턴
return data
def word_choice(sel):
keys = sel.keys()
return random.choice(list(keys)) #sel의 key값들 중 랜덤으로 1개 고름
# 문장 읽어 들이기 --- (※4)
toji_file = "toji.txt"
dict_file = "markov-toji.json"
if not os.path.exists(dict_file): #딕셔너리 파일이 없다면, 아래 코드로 만듦
# 토지 텍스트 파일 읽어 들이기
fp = codecs.open("BEXX0003.txt", "r", encoding="utf-16")
soup = BeautifulSoup(fp, "html.parser")
body = soup.select_one("body > text")
text = body.getText()
text = text.replace("…", "") # 현재 koNLPy가 …을 구두점으로 잡지 못하는 문제 임시 해결
# 형태소 분석
twitter=okt() #원래는 # twitter = Twitter() 인데, okt가 최신 코드임.
malist = twitter.pos(text, norm=True) #형태소 분석
words = []
for word in malist:
# 구두점 등은 대상에서 제외(단 마침표는 포함)
if not word[1] in ["Punctuation"]:
words.append(word[0])
if word[0] == ".":
words.append(word[0])
# 딕셔너리 생성
dic = make_dic(words) #함수호출
json.dump(dic, open(dict_file,"w", encoding="utf-8")) #사전 dic을 dict_file이름으로 저장(생성)
else: #딕셔너리가 존재한다면, load함.
dic = json.load(open(dict_file,"r"))
# 문장 만들기 --- (※6)
for i in range(3):
s = make_sentence(dic) #함수호출
print(s)
print("---")
- !코드변경!
1. 네이버 맞춤법 사이트 url 변경
2. 네이버 맞춤법 검사 요청에서 User-Agent헤더 추가 (https://www.askcompany.kr/vod/crawling/51/)
- 코드 구성 :
- make_dic(words) 함수
- set_word3(dic, s3) 함수
- make_sentence(dic) 함수
- word_choice(sel) 함수
- 문장 읽어들이기
- 문장 만들기
- 코드 흐름 :
- 문장 읽어들이기(#4)
- 딕셔너리 파일이 없다면, 형태소 분석후 딕셔너리를 생성한다.(make_dic호출)
- make_dic함수에서는 문장을 읽어들이고, 세 단어가 한 세트가 되도록 하여 딕셔너리에 데이터를 등록한다. (set_word3호출)
- 딕셔너리 생성완료함. (오른쪽 캡쳐 : 딕셔너리 구조)
- 문장을 만든다. (make_sentence호출)
- make_sentence에서는 단어를 무작위로 가져와(word_choice함수) 단어를 구성하고,
띄어쓰기를 해주고, 네이버 맞춤법 검사기를 이용하여 문장을 만든다.
- 실행화면 : 어색한 문장들이 생성된 것을 확인할 수 있다.
03. LSTM/RNN
RNN(Recurrent Neural Network) : 재귀신경망; 신경망을 재귀적으로 사용해 시간 순서를 가진 데이터를 다룸
KSTM(Long Short Term-Memory) : RNN을 개량한 것으로, 장기적으로 데이터를 기억할 수 있게 여러가지 기능을 추가함.
-> 시간 순서를 기반으로 데이터를 다룰 수 있어, 문장 쉽게 생성 가능하다.
(Ex. '오늘'이라고 입력시, '아침','날씨'등이 이어질 것으로 예상하고 조합할 수 있다. )
[실습1] 마르코프체인 구현
keras의 샘플 중 "lstm_text_generation.py"(문장을 자동생성하는 프로그램) 라는 파일을 수정하여 문장을 자동 생성하는 프로그램을 짜본다. (LSTM)
- 테스트 파일 : "토지"의 텍스트 파일
<코드 : lstm-text-gen.py >
import codecs
from bs4 import BeautifulSoup
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.layers import LSTM
from keras.optimizers import RMSprop
from keras.utils.data_utils import get_file
import numpy as np
import random, sys
#'토지'파일 읽어오기_(#1)
fp = codecs.open("./BEXX0003.txt", "r", encoding="utf-16")
soup = BeautifulSoup(fp, "html.parser")
body = soup.select_one("body")
text = body.getText() + " "
print('코퍼스의 길이: ', len(text))
# 문자를 하나하나 읽어 들이고 ID 붙이기_(#2)
chars = sorted(list(set(text)))
print('사용되고 있는 문자의 수:', len(chars))
char_indices = dict((c, i) for i, c in enumerate(chars)) # 문자 → ID
indices_char = dict((i, c) for i, c in enumerate(chars)) # ID → 문자
# 텍스트를 maxlen개의 문자로 자르고 다음에 오는 문자 등록하기 _(#3)
maxlen = 20
step = 3
sentences = []
next_chars = []
for i in range(0, len(text) - maxlen, step):
sentences.append(text[i: i + maxlen])
next_chars.append(text[i + maxlen]) #텍스트를 maxlen개의 문자로 자름.
print('학습할 구문의 수:', len(sentences))
print('텍스트를 ID 벡터로 변환합니다...')
X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool) #np.zeros : 0으로 초기화된 shape차원의 ndarray배열 객체를 반환한다.
y = np.zeros((len(sentences), len(chars)), dtype=np.bool)
for i, sentence in enumerate(sentences): #enumerate : return값으로 인덱스를 포함하는 enumerate객체를 반환한다.
for t, char in enumerate(sentence):
X[i, t, char_indices[char]] = 1
y[i, char_indices[next_chars[i]]] = 1
# 모델 구축하기(LSTM)
print('모델을 구축합니다...')
model = Sequential() #모델은 Sequential
model.add(LSTM(128, input_shape=(maxlen, len(chars))))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))
optimizer = RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)
# 후보를 배열에서 꺼내기
def sample(preds, temperature=1.0):
preds = np.asarray(preds).astype('float64')
preds = np.log(preds) / temperature
exp_preds = np.exp(preds)
preds = exp_preds / np.sum(exp_preds)
probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas)
# 학습시키고 텍스트 생성하기 반복
for iteration in range(1, 60):
print()
print('-' * 50)
print('반복 =', iteration)
model.fit(X, y, batch_size=128, nb_epoch=1) #
# 임의의 시작 텍스트 선택하기
start_index = random.randint(0, len(text) - maxlen - 1)
# 다양한 다양성의 문장 생성
for diversity in [0.2, 0.5, 1.0, 1.2]:
print()
print('--- 다양성 = ', diversity)
generated = ''
sentence = text[start_index: start_index + maxlen]
generated += sentence
print('--- 시드 = "' + sentence + '"')
sys.stdout.write(generated)
# 시드를 기반으로 텍스트 자동 생성
for i in range(400):
x = np.zeros((1, maxlen, len(chars)))
for t, char in enumerate(sentence):
x[0, t, char_indices[char]] = 1.
# 다음에 올 문자를 예측하기
preds = model.predict(x, verbose=0)[0]
next_index = sample(preds, diversity)
next_char = indices_char[next_index]
# 출력하기
generated += next_char
sentence = sentence[1:] + next_char
sys.stdout.write(next_char)
sys.stdout.flush()
print()
- 실행화면 : 위의 마르코프체인으로 만든 문장 보단 더 자연스러운 것을 확인가능하다.
(직접하는 코드 실행은 툴 다운오류로 생략_ 책 실행결과로 대체)
위 글은 [(파이썬을 이용한)머신러닝, 딥러닝 실전 개발 입문] 을 읽고 정리한 글입니다.
'Study > AI&DeepLearning' 카테고리의 다른 글
Numpy로 Tensor 이해하기 (0) | 2021.01.11 |
---|---|
딥러닝 용어 정리 (0) | 2021.01.11 |
6부_5장 N-gram으로 문장유사도 분석하기 (0) | 2020.08.04 |
6부_4장 MLP 텍스트 분류하기 (2) | 2020.08.01 |
6부_3장 베이스정리로 텍스트 분류하기 (0) | 2020.07.25 |