카테고리 없음

RNN으로 소설쓰기 (CHAR-RNN 오픈소스 실습)

Jin_z 2019. 6. 30. 23:34

RNN(Recurrent Neural Network) 이란

RNN(Recurrent Neural Network)은 연속성이 있는 또는 시간적인 순서가 있는 데이터를 다루는 신경망이다. 기존의 피드 포워드(Feed Forward) 신경망은 하나의 입력 데이터를 하나의 출력 데이터로 매핑한다. 반면 RNN에서는 입력 데이터의 시퀀스(sequence)를 출력 데이터의 시퀀스(sequence)로 매핑한다. 아래 그림을 보자.

 

그림. 여러가지 RNN 모델

(출처:The Unreasonable Effectiveness of Recurrent Neural Networks)

첫 번째 one-to-one의 경우 기존의 피드 포워드(Feed Forward) 신경망이다. 빨간색 네모상자는 하나의 입력 데이터 또는 하나의 벡터로, Torch의 경우 하나의 Tensor 객체에 해당한다. 녹색 네모상자는 네트워크 모델이다. 파란색 네모상자는 하나의 출력 데이터 또는 하나의 벡터, Torch의 경우 하나의 Tensor 객체에 해당한다. 즉 one-to-one의 경우 하나의 입력 Tensor를 하나의 출력 Tensor로 매핑하게 된다.

네 번째 many-to-many의 경우가 일반적인 RNN 모델로, 입력의 시퀀스를 출력의 시퀀스로 매핑한다. 예를 들어 구글의 기계 번역(machine translation)의 경우, 입력 단어의 시퀀스를 번역 단어의 시퀀스로 매핑한다.

(출처:  https://www.popit.kr/rnnrecurrent-neural-network%EA%B3%BC-torch%EB%A1%9C-%EB%B0%9C%EB%9D%BC%EB%93%9C%EA%B3%A1-%EC%9E%91%EC%82%AC%ED%95%98%EA%B8%B0/)


RNN(Recurrent Neural Network) 으로 글자 생성하는 방법

1. Preprocess :

텍스트 데이터셋을 글자(Character) 단위로 쪼개고 글자들의 사전(Dictionary)을 만들고 글자를 모두 할당된 숫자로 맵핑한다.

전처리로, 문장들을 모두 개별 글자(Character) 단위로 쪼개고, 각각의 글자를 나타내는 숫자를 하나씩 할당해준다. 이후, 데이터셋에 포함된 전체 글자들로 사전(Dictionary)을 만들고 모든 글자들을 할당된 숫자로 맵핑한다.

 

2. Training :

인풋 데이터(input data-x-)에서 글자(Character)를 하나 뒤로 민 타겟 데이터(target data-y-)로 RNNs을 학습한다.

이제, 전처리(Preprocess)가 끝난 데이터를 이용해서 RNNs을 이용해서 학습을 진행하자. 학습을 진행하기 위해서는 input data와 target data가 필요하다. 이때 input data와 target data를 아래와 같이 구성하자

Input Data : 전체 문장 중 일정 길이의 글자들의 배열

Target Data : 전체 데이터중 Input Data를 한글자 뒤로 민 배열

 

3. Sampling:

학습된 결과를 토대로 샘플링을 통해 임의의 글자를 생성한다.

이제 마지막으로, 학습된 결과를 토대로 샘플링(Sampling)을 통해 글자를 생성해보자. 샘플링이란 학습된 확률분포(Probability Distribution)에서 임의의 샘플들(Samples)을 추출하는 기법이다.

우리가 사용하는 코드에서는 첫 시작글자(‘ ‘-공백-)를 RNN의 input으로 넣고, 공백으로부터 다음 글자를 prediction한다. 이후, 다시 이 prediction 값을 RNN의 input으로 넣고 prediction을 진행하는 과정을 반복한다.

 

(출처: http://solarisailab.com/archives/1620)


* 실습 해보기 *

 

오픈소스https://github.com/solaris33/char-rnn-tensorflow

 

solaris33/char-rnn-tensorflow

코드를 간결하게 정리하고 한글주석을 추가한 Char-RNN. Contribute to solaris33/char-rnn-tensorflow development by creating an account on GitHub.

github.com

Requirements


RNN을 통해 train을 하려면 시간이 오래걸리므로 GOOGLE Colaboatory (구글 코랩)을 사용하여

GPU 환경에서 실행하였다. 코랩을 쓰면 설치하기 까다로운 tensorflow 와 같은 것도 쉽게 사용 할 수 있다. 

from google.colab import drive
drive.mount('/content/gdrive', force_remount=True)

위를 입력하여 구글 아이디로 로그인하면 구글 드라이브와 코랩을 연동시킬 수 있다.

import os
os.chdir("gdrive/My Drive/char_rnn")

폴더를 옮기고

!git clone https://github.com/solaris33/char-rnn-tensorflow.git

클론을 한다.

cd char-rnn-tensorflow

클론 된 파일이 있는 폴더로 가준다.

 

샘플 파일로 셰익스피어 소설이 저장되어 있다.  새로운 데이터를 넣으려면 data 폴더에 새로운 폴더를

만들고 그 폴더에 input.txt 파일을 만들어서 넣으면 된다.  그런 다음 train.py 파일을 실행시킨다. 

 

데이터는 input.txt에는 초록창에 '판타지 소설 다운' 이라고 쳐서 나오는 아무 소설들을 넣었다.

 


-input의 일부

 

동굴은 어두웠다. 정교한 세공이 된 황금 촛대의 가지 위에 놓인 다섯 개의 노란 초는 일렁거리는 그림자를 길게 드리우고 있었다. 그런 촛대가 여덟 개, 동서남북의 방위를 맞춰 놓여 있었다. 촛대의 바깥쪽은 커다란 원이, 그 안에는 촛대를 꼭지점으로 한 정팔각형이 그려져 있었다.

동서남북의 네 방위에는 제단이 각각 하나씩 자리잡고 있었다. 똑같은 재질의 흑청색 돌을 깎아 만든 제단들은 촛불 아래 매끄럽게 잘려진 단면을 번쩍이고 있었다. 북쪽의 제단과 서쪽의 제단에는 청년이, 동쪽에는 여인이 각각 누워 있었다. 
"드디어 시작인가..."

무겁게 감돌던 동굴의 적막을 깬 것은 낮고 부드러운, 굵직한 남자의 목소리였다. 남자는 옅은 노란색의 로브에 달린 망토를 깊이 눌러쓰고 있었다. 금실로 놓은 문양은 화려하면서도 세밀했다. 그의 오른손은 푸른 수정구를 꼭 쥐고 있었다. 후드 밑으로 굽슬거리는 금발이 길게 늘어져 있었다.

"... 어, 어째서...?"

 

-output

 

테이의 말이다. "사실 테이블 두지는, 잠어!" "나, 뭔가 따라오죠." "그러면서 의수, 곧 열일 수 있어! 그냥 무슨 쇠랑성이 찾기와서 해 지도처럼 귀여운 배 비슷한 분이군요. 뭐? 멍하니까, 아빠를 척 하는 거야. 약공께는 테어 이름을 참어요! 게다로 어딨지? ""名하니, 샤이를 싫다 하는 번 숲이 지려얌." 어쩐 그것도 아닌 섟이었기 때문이다. 난놔로 다행스지르에 그가 할러버밀까지 짧게 스피의 상태외출 하는 그녀의 일줄 마음 신표에도 반천박의 침청한 채. 어째서 이름이 아니면 알 거든 당연히 로브는 곧 더 못 챈 빛어 줄 수 없었다. 제 그런 잊지도 말라도 저 볼 까사으로 떨어졌고, 뭔가 쪽에서 나갔다. 그겠지?" "네, 아마, 작가에서 달이 안 때까지 있을까봐... ㅡㅡ;; 그녀의 먹을 많이 놓아들도 있을 거였다. 기운은 사성들이며 이미 가상한 다음적이에요. 그리고 아문이면 귀족들린 이가 접혹해서야 내가 떠아더 죽어나되는 것이- 네 테이였다. 공주님은 무슨 호야의 게 1월 음이 들 수 있는지도 천천히 바이슌은 이제 팔을 독투스럽게 한두 낙 바라.. 리 빛만 해 가릿했다. 그의 말에 기심적인 있다. 어리왔다는 것이다. 분명?’ "…, 근데.. 파랑해!" "알고 있어서 설마 자루이니까봐도 왼이 두통을 차남아 리기를 했다. 그 판이라며~... 서세계까지 우울고 대부분 미 때려고 가능한 놀이니까 대답해야 했을 것 같지요. 그랬다 날 헬리오스. 민료 소각. 이제 조글하내라고 는데?" "하저에 이제 사라지겠다…." "응," ".... 그런거야, 세 그 북직을 보이는 거지?" "그렇다'. 아무튼(한럴 이느지 좀 죽여왔다. 소년은 가끔 옆쳤다. 그 모양요. 혹은 이번엔 하.강인지. 녕’ 글긴 사랑이가 투른 굴 야겠다~ 너무 미생 게도... 뮤와 의자를 부르던 그의 문장을 중요하지 못했어!" "뭐가?" 카잔은 잊은 크로 입 맺히던 , 년이 돌의 듯 껍데력씩 듯 다는 것이었다. 시칭은 북쪽에는 그의 눈혐시스는 반드깝게 했던 검기격도 한어린 소투에 습으로 졌다. 제 일을 끌어들고 있었다. 그 듯 창사는 조각아가는 ""지. 아무것도 놈이 없었지 마계를 기사지계가요?" 든지 얀을 내전에 로브는 종림도에서 접제했다. 그리고 그녀는 루이즈가 살짝 웃었다. "…… '대천히. 기억을 하고. 나받지 않아? 흠. 혼자 없다는 건 호기였다. 내데삐 원뿔만큼..’ “어머니’ 이름까지도 듣고 있는 것도 이제 식장을 뽑아 올짝 가져갔어. 어디선 의사시구. 공이 나 안 된 것도 너무! 적어도 그것을 보살을 내」 쓰그렸다. 마른 입구간 경치 좀 떨구지면서 한다금 코불젖었다. "에?] "으음 가능하는데. 뭐라고... 그리고 인간하다.” “안돼.” 궁정없이 뒤에서 넘기면서 자신에게 과히를 흔들었다. "에....." 이번에 허공에 있나는 공간, 네르는 이판감이 어려울릴 바릅니다.

 


*후기*

 

1.  파일 경로를 제대로 잘 설정해주는 것이 중요하다. 파일 경로가 맞지 않아서 일어나는 오류들이 많았다.

    ls를 이용해 현재 경로가 어디인지 계속 확인해주어야 한다.

 

2.  ValueError: Variable rnn/basic_rnn_cell/kernel already exists, disallowed. Did you mean to set reuse=True or c  reuse=tf.AUTO_REUSE in VarScope?

이라는 오류가 났다. 코드 위쪽에 tf.reset_default_graph() 을 쓰니 해결돼었다.

 

3. 샘플 데이터에서 새로운 데이터로 바꿀 때 인코딩 문제로 계속 오류가 났다. 메모장에서 인코딩부분에서 utf-8으로 설정하면 해결된다.

 

4. 데이터의 크기가 너무 적으면 제대로 학습되지 않아 외계어가 나온다. 위의 input 데이터 파일은 5.76mb 이다.