Skip to content

Commit

Permalink
#33: 2차 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
wjddlsy committed Oct 25, 2018
1 parent f52be3e commit 643a49c
Showing 1 changed file with 32 additions and 23 deletions.
55 changes: 32 additions & 23 deletions 33_LSTM 을 이용한 텍스트 generation, keras 와 gpu 사용.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

원문: https://medium.freecodecamp.org/applied-introduction-to-lstms-for-text-generation-380158b29fb3

> 이 문서는 Keras와 GPU-enabled Kaggle Kernel을 이용한 텍스트 생성 LSTM 모델에 대해 설명합니다. freeCodeCampe Gitter 채팅 메시지를 LSTM network 모델에 학습시켜 새로운 텍스트를 생성합니다.
> 이 문서는 Keras와 GPU-enabled Kaggle Kernel을 이용한 텍스트 생성 LSTM 모델에 대해 설명합니다. freeCodeCampe Gitter 채팅 로그 데이터셋을 LSTM network 모델에 학습시켜 새로운 텍스트를 생성합니다.
- Keras
- GPU-enabled Kaggle Kernel
Expand All @@ -18,7 +18,9 @@ Kaggle은 최근 데이터 과학자들에게 Kernel (Kaggle의 클라우드 기

![img](https://cdn-images-1.medium.com/max/2000/1*msHP2gE21HCHibUquIAB1A.png)

​ <freeCodeCamp’s dataset on Kaggle Datasets>
*freeCodeCamp’s dataset on Kaggle Datasets*



이 블로그 포스트에서는 Kaggle 데이터셋에 게시 된 [freeCodeCamp의 Gitter 채팅 로그 데이터셋](https://www.kaggle.com/freecodecamp/all-posts-public-main-chatroom)에서 새로운 텍스트 출력을 생성하는 LSTM network를 학습하는 방법에 대해 설명하겠습니다.

Expand All @@ -40,9 +42,9 @@ config.gpu_options.allow_growth = True

## Part 1: 데이터 준비

파트 1에서는, 먼저 데이터를 읽어보고 작업 내용을 충분히 이해해보겠습니다. 비상호적인 튜토리얼(예를 들어, GitHub의 코드 공유)를 따라하는 것에서어려움 중 하나는 작업하고 싶은 데이터가 샘플 코드와 어떻게 다른지 알기 어렵다는 것입니다. 직접 다운받아 비교해보아야합니다.
파트 1에서는, 먼저 데이터를 읽어보고 작업 내용을 충분히 이해해보겠습니다. 비상호적인 튜토리얼(예를 들어, GitHub의 코드 공유)을 따라할 때,어려움은 중 하나는 작업하고 싶은 데이터가 샘플 코드의 것과 어떻게 다른지 알기 어렵다는 것입니다. 이를 알기 위해서는 직접 다운받아 비교해보아야합니다.

Kernel을 사용해서 이 튜토리얼을 따라하는 것이 좋은 2가지 점은 다음과 같습니다. 1) 모든 주요 단계에서 데이터를 훑어볼 수 있습니다. 2) 언제든지 이 notebook을 fork할 수 있으며, 제 환경, 데이터, Docker 이미지 및 필요한 모든 것을 다운로드나 설치 없이 얻을 수 있습니다. 특히, 딥러닝을 위해 GPU를 사용하는 CUDA 환경을 설치한 경험이 있다면, 이러한 환경이 이미 준비되어 있는 것이 얼마나 좋은지 경험하셨을 것입니다.
Kernel을 사용해서 이 튜토리얼을 따라하는 것이 좋은 2가지 점은 다음과 같습니다. 1) 모든 주요 단계에서 데이터를 훑어볼 수 있습니다. 2) 언제든지 이 notebook을 fork할 수 있으며, 제 환경, 데이터, Docker 이미지 및 필요한 모든 것을 다운로드나 설치 없이 얻을 수 있습니다. 특히, 딥러닝을 위해 GPU를 사용하는 CUDA 환경을 설치한 경험이 있다면, 이러한 환경이 이미 준비되어 있는 것이 얼마나 좋은지 알 수 있겠지요.

### Read in the data

Expand All @@ -67,7 +69,7 @@ chat.head()

### Explore the data

밑의 그림에서 freeCodeCamp의 Gitter에서 사용자 id별로 가장 활동적인 채팅 참가자 상위 10명의 게시물 수를 볼 수 있습니다.
밑의 그림을 보면, freeCodeCamp의 Gitter에서 사용자 id별로 가장 활동적인 채팅 참가자 상위 10명의 게시물 수를 볼 수 있습니다.

```python
import matplotlib.pyplot as plt
Expand All @@ -92,17 +94,17 @@ chat[chat['fromUser.id'] == "55a7c9e08a7b72f55c3f991e"].text.head(20)

![img](https://cdn-images-1.medium.com/max/2000/1*xM5SOB2YS0oJLzBluhILmg.png)

"documentation", "pair coding", "BASH", "Bootstrap", "CSS" 와 같은 단어와 구문을 볼 수 있습니다. 그리고 "With all of the various frameworks…"으로 시작되는 문장은 JavaScript를 JavaScript를 가리킨다고 가정할수 있습니다. 맞습니다, freeCodeCamp에서 있을만한 주제들입니다. 따라서 만약 우리의 결과가 성공적이라면 이러한 문장들이 생성될 것으로 기대할 수 있습니다.
"documentation", "pair coding", "BASH", "Bootstrap", "CSS" 와 같은 단어와 구문을 볼 수 있습니다. 그리고 "With all of the various frameworks…"으로 시작되는 문장은 JavaScript를 JavaScript를 가리킨다고 가정할수 있습니다. 맞습니다, freeCodeCamp에서 있을만한 주제들입니다. 따라서 만약 우리의 결과가 성공적이라면 이러한 문장들이 생성될 것입니다.



### LSTM의 입력값에 대한 시퀀스 데이터 준비

현재, 우리는 사용자id와 메시지 텍스트에 해당하는 컬럼을 가진 데이터 프레임이 있습니다. 여기서 각 행은 전송된 하나의 메시지입니다. 이는 LSTM network의 입력 레이어에 필요한 3차원 형태와는 거리가 멉니다: `model.add(LSTM(batch_size, input_shape=(time_steps, features)))` 에서 `batch_size`는 각 샘플에서 시퀀스의 수이고, `time_steps`은 각 샘플에서 관측치의 크기입니다. `feature`은 관찰 가능한 특징(feature)의 수로 우리의 경우 문자를 의미합니다.
현재, 우리는 사용자id와 메시지 텍스트에 해당하는 열을 가진 데이터 프레임이 있습니다. 여기서 각 행은 전송된 하나의 메시지입니다. 이는 LSTM network의 입력 레이어에 필요한 3차원 형태와는 거리가 멉니다: `model.add(LSTM(batch_size, input_shape=(time_steps, features)))` 에서 `batch_size`는 각 샘플에서 시퀀스의 수이고, `time_steps`은 각 샘플에서 관측치의 크기입니다. `feature`은 관찰 가능한 특징(feature)의 수로 우리의 경우 문자를 의미합니다.

어떻게 데이터 프레임을 올바른 형태인 연속 데이터로 만들 수 있을까요? 3단계로 해볼 수 있습니다.

1. 말뭉치로부터 데이터를 뽑아냅니다.
1. 말뭉치(corpus)로부터 데이터를 뽑아냅니다.

2. #1의 말뭉치를 균일한 길이를 가지고 다음 문자와 일부 겹치는 시퀀스의 행렬로 만듭니다.

Expand All @@ -111,7 +113,7 @@ chat[chat['fromUser.id'] == "55a7c9e08a7b72f55c3f991e"].text.head(20)

### 말뭉치로부터 데이터 뽑기

다음의 두 셀에서, `55a7c9e08a7b72f55c3f991e` (`'fromUser.id' == '55a7c9e08a7b72f55c3f991e'`)의 메시지만 가져와 데이터를 뽑아내고, 문자열 벡터를 단일 문자열로 만들겠습니다. 우리 모델이 생성하는 텍스트가 올바른 대문자화를 하는지 신경쓰지 않을 것이기 때문에, `tolower()`함수를 사용하여 모두 소문자로 바꾸겠습니다. 이는 학습해야할 차원을 하나 축소시켜줍니다.
다음의 두 셀에서, `55a7c9e08a7b72f55c3f991e` (`'fromUser.id' == '55a7c9e08a7b72f55c3f991e'`)의 메시지만 가져와 데이터를 뽑아내고, 문자열 벡터를 단일 문자열로 변환하겠습니다. 우리 모델이 생성하는 텍스트가 올바른 대문자화를 하는지 신경쓰지 않을 것이기 때문에, `tolower()`함수를 사용하여 문자를 모두 소문자로 바꾸겠습니다. 이는 학습해야할 차원을 하나 축소시켜줍니다.

또한, 데이터의 처음 20%를 샘플로 사용하겠습니다. 중간 길이 정도의 텍스트를 생성하는데 그 이상은 필요없기 때문입니다. 이 kernel을 fork하여 원한다면 더 많은(혹은 적은) 데이터로 실험해볼 수 있습니다.

Expand Down Expand Up @@ -151,7 +153,7 @@ indices_char = dict((i, c) for i, c in enumerate(chars))

![img](https://cdn-images-1.medium.com/max/2000/1*3vGVqm30QNs8SqNNTKae6g.png)

다음 셀에서 말뭉치 `user`로부터 3문자씩 잘라낸 `maxlen`(40) 개의 문자 시퀀스로 이루어진 `sentences` 행렬을 얻을 수 있습니다. 또한 각 `i`에 대한 `i+maxlen`에서 `user`의 단일 문자 배열인 `next_chars`를 얻습니다. 배열의 처음 10개의 문자열을 출력한 것을 보면 우리가 부분적으로 겹치며 동일한 길이를 가지는 "sentences"를 말뭉치에서 잘라냈음을 알 수 있습니다.
다음 셀에서 말뭉치 `user`로부터 3문자씩 건너 뛴 `maxlen`(40) 개의 문자 시퀀스로 이루어진 `sentences` 행렬을 얻을 수 있습니다. 또한 각 `i`에 대한 `i+maxlen`에서 `user`의 단일 문자 배열인 `next_chars`를 얻습니다. 배열의 처음 10개의 문자열을 출력한 것을 보면 우리가 부분적으로 겹치며 동일한 길이를 가지는 "sentences"를 말뭉치에서 얻어냈음을 알 수 있습니다.

```python
maxlen = 40
Expand All @@ -169,9 +171,11 @@ print(next_chars[:10])

![img](https://cdn-images-1.medium.com/max/2000/1*T_gKrW3LTAJpTne4gT4t4g.png)

`'hi folks. just doing the new signee stuf'`의 다음 문자가 단어 "stuff"의 마지막 글자인 `f`임을 알 수 있습니다. 그리고 시퀀스 `'folks. just doing the new signee stuff. '` 의 다음 문자는 "hello" 단어의 `h`입니다. 이런 방식으로, `next_chars``sentences` 내 시퀀스에 대한 "데이터 라벨" 또는 ground truth가 되도록 해야하고, 이 labled data에 대해 학습된 모델은 주어진 시퀀스 입력에 대한 *새로운 다음 문자* 를 예측으로 생성해낼 수 있습니다.
`'hi folks. just doing the new signee stuf'`의 다음 문자가 단어 "stuff"의 마지막 글자인 `f`임을 알 수 있습니다. 그리고 시퀀스 `'folks. just doing the new signee stuff. '` 의 다음 문자는 "hello" 단어의 `h`입니다. 이런 방식으로, `next_chars``sentences` 내 시퀀스에 대한 "데이터 라벨" 이 되도록 하고, 이 labled data에 대해 학습된 모델은 주어진 시퀀스 입력에 대한 *새로운 다음 문자* 를 예측하여 생성해낼 수 있습니다.


#### Represent the sequence data as sparse boolean tensors

### Represent the sequence data as sparse boolean tensors

다음 셀은 [kernael에서 상호적으로 따라갈 때](https://www.kaggle.com/mrisdal/intro-to-lstms-w-keras-gpu-for-text-generation/), 몇 초 정도 걸립니다. 우리가 훈련시키는 모델에 대한 입력으로 사용하기 위해 `sentence``next_chars`로 부터 문자수준의 특징(feature)들을 인코딩하는 sparse boolean tenser `x``y`를 생성합니다. 마지막 shape는 다음과 같습니다:`input_shape=(maxlen, len(chars)) ` 로 여기서 `maxlen`은 40이며 `len(chars)`는 특징(feature)의 수(즉, 말뭉치의 고유한 문자 수)입니다.

Expand All @@ -184,14 +188,18 @@ for i, sentence in enumerate(sentences):
y[i, char_indices[next_chars[i]]] = 1
```

### Part 2: Modeling


## Part 2: Modeling

파트 2에서는, 실제 모델을 훈련시키고 텍스트를 생성하겠습니다. 우리는 이미 데이터를 탐색하고 reshape하여 LSTM 모델에 올바른 입력값으로 사용할 수 있도록 만들었습니다. 이 파트에는 2가지 섹션이 있습니다.

1. LSTM network 모델 정의
2. 모델 훈련 및 예측 생성

#### LSTM network 모델 정의


### LSTM network 모델 정의

라이브러리를 읽는 것 부터 시작하겠습니다. Tensorflow backend에 대해 대중적이고 사용하기 쉬운 인터페이스인 Keras를 사용하겠습니다. [Keras를 딥러닝 프레임 워크로 사용하는 이유](https://keras.io/why-use-keras/)를 읽어보세요. 아래에서 우리가 사용할 모델, 레이어, 최적화도구 및 콜백을 볼 수 있습니다.

Expand Down Expand Up @@ -283,9 +291,11 @@ def on_epoch_end(epoch, logs):
generate_text = LambdaCallback(on_epoch_end=on_epoch_end)
```

#### 모델 트레이닝 및 예측값 생성

드디어 해냈습니다! 데이터가 준비되었어요(`x`는 시퀀스, `y`는 다음 문자). `batch_size`로 128을 선택하였습니다. 또한 우리는 매번 다른 5개의 `temperature` 설정을 가지는 매 5 epoch의 첫 epoch의 끝에서 `model.predict()`을 사용하여 생성된 텍스트를 출력할 콜백 함수를 정의하였습니다. 또 다른 콜백함수로 `ModelCheckpoint`를 사용하였는데, 이는 손실 값을 기준으로 손실 값이 개선된다면 각 epoch마다 최고의 모델을 저장합니다(kernel의 "Output" 탭에서 저장된 가중치 파일 `weights.hdf5`을 보십시오).

### 모델 트레이닝 및 예측값 생성

드디어 해냈습니다! 데이터가 준비되었습니다(`x`는 시퀀스, `y`는 다음 문자). `batch_size`로 128을 선택하였습니다. 또한 우리는 매번 다른 5개의 `temperature` 설정을 가지는 매 5 epoch의 첫 epoch의 끝에서 `model.predict()`을 사용하여 생성된 텍스트를 출력할 콜백 함수를 정의하였습니다. 또 다른 콜백함수로 `ModelCheckpoint`를 사용하였는데, 이는 손실 값을 기준으로 손실 값이 개선된다면 각 epoch마다 최고의 모델을 저장합니다(kernel의 "Output" 탭에서 저장된 가중치 파일 `weights.hdf5`을 보십시오).

이제 우리의 모델에 이러한 점들을 적용시키고, 훈련시킬 ephoch의 수를 `epochs = 15`로 정하겠습니다. 물론, GPU를 사용하는 것을 잊지마세요! 이렇게 하면 CPU를 사용하는 것보다 훨씬 더 빨리 학습/예측할 수 있습니다. 이 코드를 대화식으로 실행한다면 모델을 훈련시키고 예측을 생성하는 것을 기다리는 동안 점심을 먹거나 산책해도 되겠죠.

Expand Down Expand Up @@ -313,26 +323,26 @@ with tf.device('/gpu:0'):

![img](https://cdn-images-1.medium.com/max/2000/1*kxL8tbTxe3wFsKW7FU_Tiw.png)

​ < 첫 epoch 이후 출력 예 >
*첫 epoch 이후 출력 예시*



### 결론
## 결론

다했습니다! [Kaggle Kernels](https://www.kaggle.com/mrisdal/intro-to-lstms-w-keras-gpu-for-text-generation/)에서 이 notebook을 실행시키면, 생성된 텍스트를 문자별 출력하는 엄청난 모델을 얻어낼 수 있습니다.

텍스트 행을 갖는 데이터 프레임으로부터 LSTM 모델을 사용하여 GPU의 힘으로 새로운 문장을 생성하는 방법을 배운 과정이 즐거웠기를 바랍니다. 우리의 모델이 처음 epoch에서 마지막 epoch까지 어떻게 개선되었는지 볼 수 있습니다. 첫 번째 epoch에서 생성된 텍스트는 실제 영어와 비슷하지 않습니다. 전반적으로, 낮은 수준의 다양성은 많은 반복을 통하여 텍스트를 생성하는 반면, 높은 수준의 다양성은 알아듣기 힘든 말을 생성해내죠.

더 나은 텍스트를 생성하기 위해 모델 또는 hyperparameter를 조정할 수 있을까요? 다음을 클릭하여 직접해보세요! [forking this notebook kernel](https://www.kaggle.com/mrisdal/intro-to-lstms-w-keras-gpu-for-text-generation/) (상단의 "Fork Notebook" 클릭)
더 나은 텍스트를 생성하기 위해 모델 또는 hyperparameter를 조정할 수 있을까요? 다음을 클릭하여 직접 해보세요! [forking this notebook kernel](https://www.kaggle.com/mrisdal/intro-to-lstms-w-keras-gpu-for-text-generation/) (상단의 "Fork Notebook" 클릭)



#### 다음 단계로의 영감
### 다음 단계로의 영감

배운 것을 활용하여 확장하는 방법에 대한 아이디어입니다.

1. 훈련 데이터 양, 에폭(epoch), 배치(batch) 크기, `temperature`등 다른(hyper)-매개변수를 사용하여 실험해보세요.
2. 동일한 코드를 다른 데이터로 시도해보세요; 이 notebook을 fork하고 "Data" 탭으로 이동합니다. freeCodeCamp 데이터 소스를 제거하고 다른 데이터 셋([좋은 예제들]((https://www.kaggle.com/datasets?sortBy=hottest&group=public&page=1&pageSize=20&size=all&filetype=all&license=all&tagids=11208)))을 넣어봅시다.
2. 동일한 코드를 다른 데이터로 시도해보세요; 이 notebook을 fork하고 "Data" 탭으로 이동합니다. freeCodeCamp 데이터 소스를 제ㄴ거하고 다른 데이터 셋([좋은 예제들]((https://www.kaggle.com/datasets?sortBy=hottest&group=public&page=1&pageSize=20&size=all&filetype=all&license=all&tagids=11208)))을 넣어봅시다.
3. dropout 레이어 추가와 같은 복잡한 network 아키텍처를 사용해보세요.
4. Kernel에서 비디오 및 실습 notebook 튜토리얼이 있는 [Kaggle Learn](https://www.kaggle.com/learn/deep-learning) 에서 딥러닝에 대해 더 공부해보세요.
5. "출력"에서 `weights.hdf5`를 사용하여 이 튜토리얼의 사용자가 다른 사람의 문장을 완성하면 새로운 Kernel의 다른 데이터를 기반으로 예측할 수 있습니다.
Expand All @@ -342,5 +352,4 @@ with tf.device('/gpu:0'):

> 이 글은 2018 컨트리뷰톤에서 [Contribute to Keras](https://github.com/KerasKorea/KEKOxTutorial) 프로젝트로 진행했습니다.
> Translator : [윤정인](https://github.com/wjddlsy)
> Translator email : [[email protected]](mailto:[email protected])
> Translator email : [[email protected]](mailto:[email protected])

0 comments on commit 643a49c

Please sign in to comment.