AI-For-Beginners/translations/ko/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb

495 lines
28 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 생성 네트워크\n",
"\n",
"순환 신경망(Recurrent Neural Networks, RNNs)과 Long Short Term Memory Cells(LSTMs), Gated Recurrent Units(GRUs)와 같은 게이트 셀 변형은 언어 모델링 메커니즘을 제공합니다. 즉, 이들은 단어의 순서를 학습하고, 시퀀스에서 다음 단어를 예측할 수 있습니다. 이를 통해 RNN을 **생성 작업**에 사용할 수 있습니다. 예를 들어, 일반 텍스트 생성, 기계 번역, 심지어 이미지 캡션 생성에도 활용할 수 있습니다.\n",
"\n",
"이전 단원에서 논의한 RNN 아키텍처에서는 각 RNN 유닛이 다음 숨겨진 상태를 출력으로 생성했습니다. 그러나 각 순환 유닛에 또 다른 출력을 추가하여 **시퀀스**를 출력할 수도 있습니다(이는 원래 시퀀스와 길이가 동일합니다). 더 나아가, 각 단계에서 입력을 받지 않고 초기 상태 벡터만 받아 시퀀스 출력을 생성하는 RNN 유닛도 사용할 수 있습니다.\n",
"\n",
"이 노트북에서는 텍스트 생성을 돕는 간단한 생성 모델에 초점을 맞출 것입니다. 간단히 말해, **문자 수준 네트워크**를 구축하여 텍스트를 한 글자씩 생성해 보겠습니다. 훈련 중에는 텍스트 코퍼스를 가져와 이를 문자 시퀀스로 나누어야 합니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 문자 단위 어휘 구축\n",
"\n",
"문자 단위 생성 네트워크를 구축하려면 텍스트를 단어가 아닌 개별 문자로 분리해야 합니다. 이전에 사용했던 `TextVectorization` 레이어는 이를 수행할 수 없으므로 두 가지 옵션이 있습니다:\n",
"\n",
"* 텍스트를 수동으로 로드하고 직접 토큰화하는 방법, [이 공식 Keras 예제](https://keras.io/examples/generative/lstm_character_level_text_generation/)처럼\n",
"* 문자 단위 토큰화를 위해 `Tokenizer` 클래스를 사용하는 방법\n",
"\n",
"우리는 두 번째 옵션을 선택할 것입니다. `Tokenizer`는 단어 단위로도 토큰화할 수 있으므로 문자 단위에서 단어 단위 토큰화로 쉽게 전환할 수 있습니다.\n",
"\n",
"문자 단위 토큰화를 수행하려면 `char_level=True` 매개변수를 전달해야 합니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n",
"tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"우리는 또한 **시퀀스의 끝**을 나타내는 특별한 토큰을 사용하고 싶으며, 이를 `<eos>`라고 부를 것입니다. 이를 어휘에 수동으로 추가해 봅시다:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"eos_token = len(tokenizer.word_index)+1\n",
"tokenizer.word_index['<eos>'] = eos_token\n",
"\n",
"vocab_size = eos_token + 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.texts_to_sequences(['Hello, world!'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 제목 생성을 위한 생성적 RNN 훈련\n",
"\n",
"RNN을 훈련시켜 뉴스 제목을 생성하는 방법은 다음과 같습니다. 각 단계에서 하나의 제목을 가져와 RNN에 입력으로 제공하고, 각 입력 문자에 대해 네트워크가 다음 출력 문자를 생성하도록 요청합니다:\n",
"\n",
"![단어 'HELLO'를 생성하는 RNN 예제를 보여주는 이미지.](../../../../../lessons/5-NLP/17-GenerativeNetworks/images/rnn-generate.png)\n",
"\n",
"시퀀스의 마지막 문자에 대해서는 네트워크가 `<eos>` 토큰을 생성하도록 요청할 것입니다.\n",
"\n",
"여기서 사용하는 생성적 RNN의 주요 차이점은 RNN의 최종 셀에서만 출력을 가져오는 것이 아니라 각 단계에서 출력을 가져온다는 점입니다. 이는 RNN 셀에 `return_sequences` 매개변수를 지정함으로써 가능합니다.\n",
"\n",
"따라서 훈련 중 네트워크에 대한 입력은 특정 길이의 인코딩된 문자 시퀀스가 되고, 출력은 동일한 길이의 시퀀스이지만 한 요소씩 이동되고 `<eos>`로 종료됩니다. 미니배치는 이러한 여러 시퀀스로 구성되며, 모든 시퀀스를 정렬하기 위해 **패딩**을 사용해야 합니다.\n",
"\n",
"이제 데이터셋을 변환하는 함수를 만들어 보겠습니다. 미니배치 수준에서 시퀀스를 패딩하고자 하므로 먼저 `.batch()`를 호출하여 데이터셋을 배치한 다음, 변환을 위해 `map`을 호출합니다. 따라서 변환 함수는 전체 미니배치를 매개변수로 받게 됩니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def title_batch(x):\n",
" x = [t.numpy().decode('utf-8') for t in x]\n",
" z = tokenizer.texts_to_sequences(x)\n",
" z = tf.keras.preprocessing.sequence.pad_sequences(z)\n",
" return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"우리가 여기서 하는 몇 가지 중요한 작업은 다음과 같습니다:\n",
"* 먼저 문자열 텐서에서 실제 텍스트를 추출합니다.\n",
"* `text_to_sequences`는 문자열 목록을 정수 텐서 목록으로 변환합니다.\n",
"* `pad_sequences`는 이러한 텐서를 최대 길이로 패딩합니다.\n",
"* 마지막으로 모든 문자를 원-핫 인코딩하고, 시프트 및 `<eos>` 추가 작업도 수행합니다. 원-핫 인코딩된 문자가 필요한 이유는 곧 알게 될 것입니다.\n",
"\n",
"하지만 이 함수는 **Pythonic**합니다, 즉 Tensorflow 계산 그래프로 자동 변환될 수 없습니다. 이 함수를 `Dataset.map` 함수에서 직접 사용하려고 하면 오류가 발생합니다. 이 Pythonic 호출을 `py_function` 래퍼를 사용하여 감싸야 합니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def title_batch_fn(x):\n",
" x = x['title']\n",
" a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n",
" return a,b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Note**: Pythonic 변환 함수와 Tensorflow 변환 함수의 차이를 구분하는 것이 다소 복잡하게 느껴질 수 있으며, 왜 데이터를 `fit`에 전달하기 전에 표준 Python 함수를 사용해 변환하지 않는지 의문이 들 수도 있습니다. 물론 이렇게 하는 것도 가능하지만, `Dataset.map`을 사용하는 데는 큰 장점이 있습니다. 데이터 변환 파이프라인이 Tensorflow 계산 그래프를 통해 실행되므로 GPU 계산을 활용할 수 있고, CPU와 GPU 간의 데이터 전송 필요성을 최소화할 수 있습니다.\n",
"\n",
"이제 생성기 네트워크를 구축하고 학습을 시작할 수 있습니다. 이는 이전 단원에서 논의한 임의의 순환 셀(단순 RNN, LSTM 또는 GRU)을 기반으로 할 수 있습니다. 이 예제에서는 LSTM을 사용할 것입니다.\n",
"\n",
"네트워크는 문자를 입력으로 받으며, 어휘 크기가 비교적 작기 때문에 임베딩 레이어가 필요하지 않습니다. 원-핫 인코딩된 입력을 LSTM 셀에 직접 전달할 수 있습니다. 출력 레이어는 LSTM 출력값을 원-핫 인코딩된 토큰 번호로 변환하는 `Dense` 분류기가 될 것입니다.\n",
"\n",
"또한, 가변 길이 시퀀스를 다루기 때문에 `Masking` 레이어를 사용하여 문자열의 패딩된 부분을 무시하는 마스크를 생성할 수 있습니다. 이는 엄밀히 말해 필수는 아닙니다. `<eos>` 토큰 이후의 모든 것에 대해 크게 관심이 있는 것은 아니기 때문입니다. 하지만 이 레이어 유형에 대한 경험을 쌓기 위해 사용해 보겠습니다. `input_shape`는 `(None, vocab_size)`가 되며, 여기서 `None`은 가변 길이의 시퀀스를 나타냅니다. 출력 형태도 `(None, vocab_size)`이며, 이는 `summary`에서 확인할 수 있습니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"masking (Masking) (None, None, 84) 0 \n",
"_________________________________________________________________\n",
"lstm (LSTM) (None, None, 128) 109056 \n",
"_________________________________________________________________\n",
"dense (Dense) (None, None, 84) 10836 \n",
"=================================================================\n",
"Total params: 119,892\n",
"Trainable params: 119,892\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Masking(input_shape=(None,vocab_size)),\n",
" keras.layers.LSTM(128,return_sequences=True),\n",
" keras.layers.Dense(vocab_size,activation='softmax')\n",
"])\n",
"\n",
"model.summary()\n",
"model.compile(loss='categorical_crossentropy')\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 출력 생성하기\n",
"\n",
"모델을 훈련시켰으니 이제 이를 사용해 출력을 생성해 보겠습니다. 먼저, 토큰 번호의 시퀀스로 표현된 텍스트를 디코딩할 방법이 필요합니다. 이를 위해 `tokenizer.sequences_to_texts` 함수를 사용할 수 있지만, 이 함수는 문자 수준 토큰화와 잘 맞지 않습니다. 따라서 토크나이저에서 가져온 토큰 딕셔너리(`word_index`라고 불림)를 사용해 역맵을 만들고, 직접 디코딩 함수를 작성할 것입니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n",
"\n",
"def decode(x):\n",
" return ''.join([reverse_map[t] for t in x])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"이제 생성을 시작해 봅시다. 우리는 문자열 `start`로 시작하여 이를 시퀀스 `inp`로 인코딩한 후, 각 단계마다 네트워크를 호출하여 다음 문자를 추론합니다.\n",
"\n",
"네트워크의 출력값 `out`은 각 토큰의 확률을 나타내는 `vocab_size` 요소로 이루어진 벡터입니다. 여기서 `argmax`를 사용하여 가장 확률이 높은 토큰 번호를 찾을 수 있습니다. 그런 다음 이 문자를 생성된 토큰 리스트에 추가하고 생성을 계속 진행합니다. 한 문자를 생성하는 이 과정은 필요한 문자 수를 생성하기 위해 `size` 횟수만큼 반복되며, `eos_token`이 나타나면 조기 종료됩니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def generate(model,size=100,start='Today '):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" nc = tf.argmax(out)\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc.numpy())\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
" \n",
"generate(model)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 훈련 중 출력 샘플링\n",
"\n",
"*정확도*와 같은 유용한 지표가 없기 때문에, 모델이 개선되고 있는지 확인할 수 있는 유일한 방법은 훈련 중에 생성된 문자열을 **샘플링**하는 것입니다. 이를 위해 우리는 **콜백(callbacks)**을 사용할 것입니다. 즉, `fit` 함수에 전달할 수 있는 함수로, 훈련 중 주기적으로 호출됩니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n",
"Today #39;s a lead in the company for the strike\n",
"Epoch 2/3\n",
"15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n",
"Today #39;s the Market Service on Security Start (AP)\n",
"Epoch 3/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n",
"Today #39;s a line on the strike to start for the start\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sampling_callback = keras.callbacks.LambdaCallback(\n",
" on_epoch_end = lambda batch, logs: print(generate(model))\n",
")\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"이 예제는 이미 꽤 괜찮은 텍스트를 생성하지만, 몇 가지 방법으로 더 개선할 수 있습니다:\n",
"\n",
"* **더 많은 텍스트**. 우리는 작업을 위해 제목만 사용했지만, 전체 텍스트로 실험해보는 것도 좋습니다. RNN은 긴 시퀀스를 처리하는 데 그다지 뛰어나지 않으므로, 이를 짧은 문장으로 나누거나, 항상 미리 정의된 값 `num_chars`(예: 256)의 고정된 시퀀스 길이로 학습하는 것이 합리적입니다. 위의 예제를 이러한 아키텍처로 변경해보는 것도 좋으며, [공식 Keras 튜토리얼](https://keras.io/examples/generative/lstm_character_level_text_generation/)을 참고로 활용할 수 있습니다.\n",
"\n",
"* **다층 LSTM**. LSTM 셀을 2층 또는 3층으로 시도해보는 것도 의미가 있습니다. 이전 단원에서 언급했듯이, LSTM의 각 층은 텍스트에서 특정 패턴을 추출하며, 문자 수준 생성기의 경우, 낮은 LSTM 층은 음절을 추출하고, 높은 층은 단어와 단어 조합을 담당할 것으로 기대할 수 있습니다. 이는 LSTM 생성자에 층 수 매개변수를 전달함으로써 간단히 구현할 수 있습니다.\n",
"\n",
"* **GRU 유닛**을 실험해보고 어떤 것이 더 나은 성능을 보이는지 확인하거나, **다양한 은닉층 크기**를 시도해보는 것도 좋습니다. 은닉층이 너무 크면 과적합(예: 네트워크가 정확한 텍스트를 학습)될 수 있고, 크기가 너무 작으면 좋은 결과를 내지 못할 수 있습니다.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 부드러운 텍스트 생성과 온도\n",
"\n",
"이전의 `generate` 정의에서는 항상 생성된 텍스트에서 다음 문자로 가장 높은 확률을 가진 문자를 선택했습니다. 이로 인해 텍스트가 종종 동일한 문자 시퀀스를 반복하는 결과를 낳았습니다. 예를 들어, 아래와 같은 경우입니다:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"하지만 다음 문자의 확률 분포를 살펴보면, 가장 높은 확률들 간의 차이가 크지 않을 수 있습니다. 예를 들어, 한 문자의 확률이 0.2이고, 다른 문자가 0.19인 경우처럼 말이죠. 예를 들어, '*play*'라는 시퀀스에서 다음 문자를 찾을 때, 다음 문자는 공백일 수도 있고, **e**일 수도 있습니다 (*player*라는 단어에서처럼).\n",
"\n",
"이로부터 우리는 항상 더 높은 확률을 가진 문자를 선택하는 것이 \"공정\"하지 않을 수 있다는 결론에 도달합니다. 두 번째로 높은 확률을 선택하더라도 여전히 의미 있는 텍스트를 생성할 수 있기 때문입니다. 따라서 네트워크 출력이 제공하는 확률 분포에서 문자를 **샘플링**하는 것이 더 현명합니다.\n",
"\n",
"이 샘플링은 **다항 분포**라고 불리는 것을 구현하는 `np.multinomial` 함수를 사용하여 수행할 수 있습니다. 이 **부드러운** 텍스트 생성을 구현하는 함수는 아래와 같이 정의됩니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Temperature = 0.3\n",
"Today #39;s strike #39; to start at the store return\n",
"On Sunday PO to Be Data Profit Up (Reuters)\n",
"Moscow, SP wins straight to the Microsoft #39;s control of the space start\n",
"President olding of the blast start for the strike to pay &lt;b&gt;...&lt;/b&gt;\n",
"Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\n",
"\n",
"--- Temperature = 0.8\n",
"Today countie strikes ryder missile faces food market blut\n",
"On Sunday collores lose-toppy of sale of Bullment in &lt;b&gt;...&lt;/b&gt;\n",
"Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n",
"President Ol Luster for Profit Peaced Raised (AP)\n",
"Little red riding hood dace on depart talks #39; bank up\n",
"\n",
"--- Temperature = 1.0\n",
"Today wits House buiting debate fixes #39; supervice stake again\n",
"On Sunday arling digital poaching In for level\n",
"Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n",
"President teps help of roubler stepted lessabul-Dhalitics (AFP)\n",
"Little red riding hood signs on cash in Carter-youb\n",
"\n",
"--- Temperature = 1.3\n",
"Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n",
"On Sunday hround elitwing wint EU Powerburlinetien\n",
"Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n",
"President lost securitys from power Elections in Smiltrials\n",
"Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n",
"\n",
"--- Temperature = 1.8\n",
"Today #39;It: He deat: N.KA Asside\n",
"On Sunday i arry Par aldeup patient Wo stele1\n"
]
},
{
"ename": "KeyError",
"evalue": "0",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m: 0"
]
}
],
"source": [
"def generate_soft(model,size=100,start='Today ',temperature=1.0):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n",
" probs = probs/np.sum(probs)\n",
" nc = np.argmax(np.random.multinomial(1,probs,1))\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc)\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
"\n",
"words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"\\n--- Temperature = {i}\")\n",
" for j in range(5):\n",
" print(generate_soft(model,size=300,start=words[j],temperature=i))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"우리는 **온도**라는 하나의 매개변수를 추가로 도입했으며, 이는 가장 높은 확률에 얼마나 강하게 고수해야 하는지를 나타내는 데 사용됩니다. 온도가 1.0이면 공정한 다항 샘플링을 수행하며, 온도가 무한대로 증가하면 모든 확률이 동일해지고 다음 문자를 무작위로 선택하게 됩니다. 아래 예시에서 온도를 너무 높이면 텍스트가 무의미해지고, 온도가 0에 가까워지면 \"순환된\" 강제 생성 텍스트와 유사해지는 것을 관찰할 수 있습니다.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**면책 조항**: \n이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있으나, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서의 원어 버전이 권위 있는 출처로 간주되어야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 책임을 지지 않습니다.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "9fbb7d5fda708537649f71f5f646fcde",
"translation_date": "2025-08-31T13:41:19+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "ko"
}
},
"nbformat": 4,
"nbformat_minor": 4
}