<jupyter_start><jupyter_text>Character-level Language ModelLanguage Model의 목표는 입력 문서와 비슷한 새로운 텍스트를 생성하는 모델을 개발하는 것이다. 문자 단위의 모델링에서 입력은 글자의 시퀀스를 나누어 한 번에 글자 하나씩 입력된다. 모형은 이 전 입력된 글자와 함께 새로 입력된 글자를 처리하여 다음 문자를 예측한다. ![IMG](../assets/text_generator.png)  필요 라이브러리 importGPU를 활성화하면 훈련 속도를 높일 수 있다.<jupyter_code>import tensorflow as tf
print(tf.__version__)
print(tf.test.gpu_device_name())

import numpy as np
import os
import time<jupyter_output>2.3.0<jupyter_text>데이터 전처리다음의 링크에서 세익스피어의 비극 텍스트 파일을 다운로드 하여 텍스트를 적절히 전처리 한다.<jupyter_code>path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')
# 읽은 다음 파이썬 2와 호환되도록 디코딩합니다.
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')
# 텍스트의 길이는 그 안에 있는 문자의 수입니다.
print ('텍스트의 길이: {}자'.format(len(text)))
# 텍스트의 처음 250자를 살펴봅니다
print(text[:250])
# 파일의 고유 문자수를 출력합니다.
vocab = sorted(set(text))
print ('고유 문자수 {}개'.format(len(vocab)))<jupyter_output>고유 문자수 65개<jupyter_text>텍스트 벡터화읽어들인 텍스트의 개별 문자들을 숫자로 변환한다.* char2idx : 문자를 숫자로 매핑* idx2char : 숫자를 문자로 매핑<jupyter_code># 고유 문자에서 인덱스로 매핑 생성
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)

text_as_int = np.array([char2idx[c] for c in text])
print('{')
for char,_ in zip(char2idx, range(20)):
    print('  {:4s}: {:3d},'.format(repr(char), char2idx[char]))
print('  ...\n}')
# 텍스트에서 처음 13개의 문자가 숫자로 어떻게 매핑되었는지를 보여줍니다
print ('{} -------- > {}'.format(repr(text[:13]), text_as_int[:13]))<jupyter_output>'First Citizen' -------- > [18 47 56 57 58  1 15 47 58 47 64 43 52]<jupyter_text>훈련 데이터 생성입력 데이터 : 텍스트를 `seq_length`로 나눈다.타깃 데이터 : 각 입력 시퀀스에서, 해당 타깃은 한 문자를 오른쪽으로 이동하여 동일한 길이의 텍스트로 생성한다. 예를 들어, `seq_length`는 4이고 텍스트를 "Hello"이라고 가정해 봅시다. 입력 시퀀스는 "Hell"이고 타깃 시퀀스는 "ello"가 된다.이렇게 하기 위해 먼저 `tf.data.Dataset.from_tensor_slices` 함수를 사용해 텍스트 벡터를 문자 인덱스의 스트림으로 변환한다.<jupyter_code># 단일 입력에 대해 원하는 문장의 최대 길이
seq_length = 100
examples_per_epoch = len(text)//seq_length

# 훈련 샘플/타깃 만들기
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)

for i in char_dataset.take(5):
    print(idx2char[i.numpy()])<jupyter_output>F
i
r
s
t<jupyter_text>`batch` 메서드는 이 개별 문자들을 원하는 크기의 시퀀스로 쉽게 변환<jupyter_code>sequences = char_dataset.batch(seq_length+1, drop_remainder=True)

for item in sequences.take(5):
    print(repr(''.join(idx2char[item.numpy()])))<jupyter_output>'First Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou '
'are all resolved rather to die than to famish?\n\nAll:\nResolved. resolved.\n\nFirst Citizen:\nFirst, you k'
"now Caius Marcius is chief enemy to the people.\n\nAll:\nWe know't, we know't.\n\nFirst Citizen:\nLet us ki"
"ll him, and we'll have corn at our own price.\nIs't a verdict?\n\nAll:\nNo more talking on't; let it be d"
'one: away, away!\n\nSecond Citizen:\nOne word, good citizens.\n\nFirst Citizen:\nWe are accounted poor citi'<jupyter_text>각 시퀀스에서, `map` 메서드를 사용해 각 배치에 간단한 함수를 적용하고 입력 텍스트와 타깃 텍스트를 복사 및 이동:<jupyter_code>def split_input_target(chunk):
    input_text = chunk[:-1]
    target_text = chunk[1:]
    return input_text, target_text

dataset = sequences.map(split_input_target)
for input_example, target_example in  dataset.take(1):
    print ('입력 데이터: ', repr(''.join(idx2char[input_example.numpy()])))
    print ('타깃 데이터: ', repr(''.join(idx2char[target_example.numpy()])))<jupyter_output>입력 데이터:  'First Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou'
타깃 데이터:  'irst Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou '<jupyter_text>이 벡터의 각 인덱스는 하나의 타임 스텝(time step)으로 처리된다. 타임 스텝 0의 입력으로 모델은 "F"의 인덱스를 받고 다음 문자로 "i"의 인덱스를 예측한다. 다음 타임 스텝에서도 같은 일을 하지만 RNN은 현재 입력 문자 외에 이전 타임 스텝을 고려한다.<jupyter_code>for i, (input_idx, target_idx) in enumerate(zip(input_example[:5], target_example[:5])):
    print("{:4d}단계".format(i))
    print("  입력: {} ({:s})".format(input_idx, repr(idx2char[input_idx])))
    print("  예상 출력: {} ({:s})".format(target_idx, repr(idx2char[target_idx])))<jupyter_output>0단계
  입력: 18 ('F')
  예상 출력: 47 ('i')
   1단계
  입력: 47 ('i')
  예상 출력: 56 ('r')
   2단계
  입력: 56 ('r')
  예상 출력: 57 ('s')
   3단계
  입력: 57 ('s')
  예상 출력: 58 ('t')
   4단계
  입력: 58 ('t')
  예상 출력: 1 (' ')<jupyter_text>훈련 배치 생성텍스트를 다루기 쉬운 시퀀스로 분리하기 위해 `tf.data`를 사용한다.<jupyter_code># 배치 크기
BATCH_SIZE = 64

BUFFER_SIZE = 10000

dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)
list(dataset.take(1))<jupyter_output><empty_output><jupyter_text>모델 설계 모델을 정의하려면 `tf.keras.Sequential`을 사용한다. 이 간단한 예제에서는 3개의 층을 사용하여 모델을 정의:* `tf.keras.layers.Embedding` : 입력층. `embedding_dim` 차원 벡터에 각 문자의 정수 코드를 매핑하는 훈련 가능한 검색 테이블.* `tf.keras.layers.GRU` : 크기가 `units = rnn_units`인 RNN의 유형(여기서 LSTM층을 사용할 수도 있다.)* `tf.keras.layers.Dense` : 크기가 `vocab_size`인 출력을 생성하는 출력층.<jupyter_code># 문자로 된 어휘 사전의 크기
vocab_size = len(vocab)

# 임베딩 차원
embedding_dim = 256

# RNN 유닛(unit) 개수
rnn_units = 1024
def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
    model = tf.keras.Sequential([
    tf.keras.layers.Embedding(vocab_size, embedding_dim,
                              batch_input_shape=[batch_size, None]),
    tf.keras.layers.LSTM(rnn_units,
                        return_sequences=True,
                        stateful=True,
                        recurrent_initializer='glorot_uniform'),
    tf.keras.layers.Dense(vocab_size)
  ])
    return model
model = build_model(
  vocab_size = len(vocab),
  embedding_dim=embedding_dim,
  rnn_units=rnn_units,
  batch_size=BATCH_SIZE)<jupyter_output><empty_output><jupyter_text>모델 사용이제 모델을 실행하여 원하는대로 동작하는지 확인한다.먼저 출력의 형태를 확인한다:<jupyter_code>for input_example_batch, target_example_batch in dataset.take(1):
    example_batch_predictions = model(input_example_batch)
    print(example_batch_predictions.shape, "# (배치 크기, 시퀀스 길이, 어휘 사전 크기)")<jupyter_output>(64, 100, 65) # (배치 크기, 시퀀스 길이, 어휘 사전 크기)<jupyter_text>위 예제에서 입력의 시퀀스 길이는 100이지만 모델은 임의 길이의 입력에서 실행될 수 있다.<jupyter_code>model.summary()<jupyter_output>Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (64, None, 256)           16640     
_________________________________________________________________
lstm (LSTM)                  (64, None, 1024)          5246976   
_________________________________________________________________
dense (Dense)                (64, None, 65)            66625     
=================================================================
Total params: 5,330,241
Trainable params: 5,330,241
Non-trainable params: 0
_________________________________________________________________<jupyter_text>모델로부터 예측 문자열을 얻으려면 출력 배열에서 샘플링하여 실제 문자 인덱스를 얻어야 한다.Note: 출력 배열에 argmax를 취하면 안되고 임의의 샘플링하는 것이 중요하다.일단 배치의 첫 번째를 샘플링 하는 형태로 출력해 본다:<jupyter_code>sampled_indices = tf.random.categorical(example_batch_predictions[0], num_samples=1)
sampled_indices = tf.squeeze(sampled_indices,axis=-1).numpy()
sampled_indices<jupyter_output><empty_output><jupyter_text>훈련되지 않은 모델에 의해 예측된 텍스트를 보기 위해 idx2char을 사용한다.<jupyter_code>print("입력: \n", repr("".join(idx2char[input_example_batch[0]])))
print()
print("예측된 다음 문자: \n", repr("".join(idx2char[sampled_indices ])))<jupyter_output>입력: 
 'First Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou'

예측된 다음 문자: 
 " Me'oNCMBkQ!KSTJKaEcclNL'eRvASSpkuX,r,mIevlNSBEXVarPu,wlrv;Yvz.'vKqhfJkM,umikNQmKS!\nWYrdL$\nqul;:33?D"<jupyter_text>모델 훈련  모형의 compile과 loss 함수 정의 표준 `tf.keras.losses.sparse_softmax_crossentropy` 손실 함수를 사용한다.이 모델은 로짓을 반환하기 때문에 `from_logits` 을 사용한다.<jupyter_code>def loss(labels, logits):
    return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)

example_batch_loss  = loss(target_example_batch, example_batch_predictions)
print("예측 배열 크기(shape): ", example_batch_predictions.shape, " # (배치 크기, 시퀀스 길이, 어휘 사전 크기")
print("Loss : ", example_batch_loss.numpy().mean())
model.compile(optimizer='adam', loss=loss)<jupyter_output><empty_output><jupyter_text>훈련 실행 훈련 시간이 너무 길지 않도록 모델을 훈련하는 데 에포크(Epoch)를 적절히 사용한다.<jupyter_code>EPOCHS=1
history = model.fit(dataset, epochs=EPOCHS)
#history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])
# 모형을 저장한다.
model.save_weights('epoch_1.h5')<jupyter_output><empty_output><jupyter_text>텍스트 생성이 예측 단계를 간단하게 하기 위해 배치 크기로 1을 사용한다.<jupyter_code>model = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)

# 저장된 모형을 불러들인다.
model.load_weights('epoch_20.h5')
model.build(tf.TensorShape([1, None]))
model.summary()<jupyter_output>Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (1, None, 256)            16640     
_________________________________________________________________
lstm_1 (LSTM)                (1, None, 1024)           5246976   
_________________________________________________________________
dense_1 (Dense)              (1, None, 65)             66625     
=================================================================
Total params: 5,330,241
Trainable params: 5,330,241
Non-trainable params: 0
_________________________________________________________________<jupyter_text>텍스트 생성하기다음 코드 블록은 텍스트를 생성합니다:* 시작 문자열 선택과 순환 신경망 상태를 초기화하고 생성할 문자 수를 설정한다.* 시작 문자열과 순환 신경망 상태를 사용하여 다음 문자의 예측 배열을 가져온다.* 다음, 범주형 배열을 사용하여 예측된 문자의 인덱스를 계산합니다. 이 예측된 문자를 모델의 다음 입력으로 활용한다.![IMG](https://tensorflow.org/tutorials/text/images/text_generation_sampling.png)<jupyter_code>def generate_text(model, start_string):
  # 평가 단계 (학습된 모델을 사용하여 텍스트 생성)

  # 생성할 문자의 수
    num_generate = 1000

  # 시작 문자열을 숫자로 변환(벡터화)
    input_eval = [char2idx[s] for s in start_string]
    input_eval = tf.expand_dims(input_eval, 0)

  # 결과를 저장할 빈 문자열
    text_generated = []

  # temperature 가 낮으면 더 예측 가능한 텍스트가 됩니다.
  # temperature 가 높으면 더 의외의 텍스트가 됩니다.
    temperature = 1.0

  # 여기에서 배치 크기 == 1
    model.reset_states()
    for i in range(num_generate):
        predictions = model(input_eval)
      # 배치 차원 제거
        predictions = tf.squeeze(predictions, 0)
      
      # 범주형 분포를 사용하여 모델에서 리턴한 단어 예측
        predictions = predictions / temperature
        predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()
  
      # 예측된 단어를 다음 입력으로 모델에 전달    
        input_eval = tf.expand_dims([predicted_id], 0)

        text_generated.append(idx2char[predicted_id])

    return (start_string + ''.join(text_generated))
print(generate_text(model, start_string="ROMEO: "))<jupyter_output>ROMEO: what tricklo's friend with him,
And here as <NAME> she did entrance
That something known by any other's dam.
Young, sir, that you must be gone.

CLAUDIO:
Now I speak of God, sir, had forth'd drawn:
Your praise to himself, to change and s
Some Servy ta'en full of them. What
after our confidence, stire.

PETRUCHIO:
Consider thanks, fair yru;
And I see thee holthy procure, gentlemen, be thus:
The citizens are quick'd.

COMINIUS:
will you summer's death, but suth away to my pleasur.

SEBASTIAN:
He's cloud, as thou hast n more disard.

MORCENDIO:
Tailor, office of men, by your love,
And the best of this unhoulder,
Under
But only Bona, give King Henry's death.
Nor no more in Perd her!

CLAUDIO:
Come, mighty supper, but they are strange.

MIRANDA:
O thrust, sir, to the state; and you, sir.

PROSPERO:
This is the hoped shall resign
once, why as my troth, and swear so still.

BIANCA:
Not at the bial.

BTHAND:
Heaptied Marian, as his old cout!

GONNASTE:

POMPEY:
None, now more nea[...]
{'fraction_non_alphanumeric': 0.13506625891946994, 'fraction_numerical': 0.021576622494053687, 'mean_word_length': 4.41785549930971, 'pattern_counts': {'":': 0, '<': 58, '<?xml version=': 0, '>': 60, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '227844', 'license': 'MIT', 'n_tokens_mistral': 5978, 'n_tokens_neox': 6628, 'n_words': 1173, 'path': '03RNN/06text_generation.ipynb', 'repo_name': 'aonekoda/tf_new', 'size': '11779'}