{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "t09eeeR5prIJ" }, "source": [ "##### Copyright 2019 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2022-12-14T22:04:54.702605Z", "iopub.status.busy": "2022-12-14T22:04:54.702326Z", "iopub.status.idle": "2022-12-14T22:04:54.706660Z", "shell.execute_reply": "2022-12-14T22:04:54.706066Z" }, "id": "GCCk8_dHpuNf" }, "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "hcD2nPQvPOFM" }, "source": [ "# 순환 신경망을 활용한 문자열 생성\n", "\n", "
\n",
" \n",
" ![]() | \n",
" \n",
" \n",
" ![]() | \n",
" \n",
" \n",
" ![]() | \n",
" \n",
" ![]() | \n",
"
\n", "QUEENE:\n", "I had thought thou hadst a Roman; for the oracle,\n", "Thus by All bids the man against the word,\n", "Which are so weak of care, by old care done;\n", "Your children were in your holy love,\n", "And the precipitation through the bleeding throne.\n", "\n", "BISHOP OF ELY:\n", "Marry, and will, my lord, to weep in such a one were prettiest;\n", "Yet now I was adopted heir\n", "Of the world's lamentable day,\n", "To watch the next way with his father with his face?\n", "\n", "ESCALUS:\n", "The cause why then we are all resolved more sons.\n", "\n", "VOLUMNIA:\n", "O, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, it is no sin it should be dead,\n", "And love and pale as any will to that word.\n", "\n", "QUEEN ELIZABETH:\n", "But how long have I heard the soul for this world,\n", "And show his hands of life be proved to stand.\n", "\n", "PETRUCHIO:\n", "I say he look'd on, if I must be content\n", "To stay him from the fatal of our country's bliss.\n", "His lordship pluck'd from this sentence then for prey,\n", "And then let us twain, being the moon,\n", "were she such a case as fills m\n", "\n", "\n", "문장 중 일부는 문법적으로 맞지만 대부분 자연스럽지 않습니다. 이 모델은 단어의 의미를 학습하지는 않았지만, 고려해야 할 점으로:\n", "\n", "* 모델은 문자 기반입니다. 훈련이 시작되었을 때, 이 모델은 영어 단어의 철자를 모르거나 심지어 텍스트의 단위가 단어라는 것도 모릅니다.\n", "\n", "* 출력의 구조는 대본과 유사합니다. 즉, 텍스트 블록은 대개 화자의 이름으로 시작하고 이 이름들은 모든 데이터셋에서 대문자로 씌여 있습니다.\n", "\n", "* 아래에 설명된 것처럼 이 모델은 작은 텍스트 배치(각 100자)로 훈련되었으며 논리적인 구조를 가진 더 긴 텍스트 시퀀스를 생성할 수 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "srXC6pLGLwS6" }, "source": [ "## 설정" ] }, { "cell_type": "markdown", "metadata": { "id": "WGyKZj3bzf9p" }, "source": [ "### 텐서플로와 다른 라이브러리 임포트" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:54.710622Z", "iopub.status.busy": "2022-12-14T22:04:54.710339Z", "iopub.status.idle": "2022-12-14T22:04:57.619764Z", "shell.execute_reply": "2022-12-14T22:04:57.618435Z" }, "id": "yG_n40gFzf9s" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[31mERROR: Could not find a version that satisfies the requirement tensorflow-gpu==2.0.0 (from versions: 2.5.0, 2.5.1, 2.5.2, 2.5.3, 2.6.0, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.7.0rc0, 2.7.0rc1, 2.7.0, 2.7.1, 2.7.2, 2.7.3, 2.7.4, 2.8.0rc0, 2.8.0rc1, 2.8.0, 2.8.1, 2.8.2, 2.8.3, 2.8.4, 2.9.0rc0, 2.9.0rc1, 2.9.0rc2, 2.9.0, 2.9.1, 2.9.2, 2.9.3, 2.10.0rc0, 2.10.0rc1, 2.10.0rc2, 2.10.0rc3, 2.10.0, 2.10.1, 2.11.0rc0, 2.11.0rc1, 2.11.0rc2, 2.11.0)\u001b[0m\u001b[31m\r\n", "\u001b[0m\u001b[31mERROR: No matching distribution found for tensorflow-gpu==2.0.0\u001b[0m\u001b[31m\r\n", "\u001b[0m" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 22:04:56.538297: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", "2022-12-14 22:04:56.538431: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", "2022-12-14 22:04:56.538442: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n" ] } ], "source": [ "!pip install tensorflow-gpu==2.0.0\n", "import tensorflow as tf\n", "\n", "import numpy as np\n", "import os\n", "import time" ] }, { "cell_type": "markdown", "metadata": { "id": "EHDoRoc5PKWz" }, "source": [ "### 셰익스피어 데이터셋 다운로드\n", "\n", "다음 코드를 실행하여 데이터를 불러오세요." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.625286Z", "iopub.status.busy": "2022-12-14T22:04:57.624206Z", "iopub.status.idle": "2022-12-14T22:04:57.629346Z", "shell.execute_reply": "2022-12-14T22:04:57.628451Z" }, "id": "pD_55cOxLkAb" }, "outputs": [], "source": [ "path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')" ] }, { "cell_type": "markdown", "metadata": { "id": "UHjdCjDuSvX_" }, "source": [ "### 데이터 읽기\n", "\n", "먼저, 텍스트를 살펴봅시다:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.633957Z", "iopub.status.busy": "2022-12-14T22:04:57.633276Z", "iopub.status.idle": "2022-12-14T22:04:57.640316Z", "shell.execute_reply": "2022-12-14T22:04:57.639408Z" }, "id": "aavnuByVymwK" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "텍스트의 길이: 1115394자\n" ] } ], "source": [ "# 읽은 다음 파이썬 2와 호환되도록 디코딩합니다.\n", "text = open(path_to_file, 'rb').read().decode(encoding='utf-8')\n", "# 텍스트의 길이는 그 안에 있는 문자의 수입니다.\n", "print ('텍스트의 길이: {}자'.format(len(text)))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.644172Z", "iopub.status.busy": "2022-12-14T22:04:57.643509Z", "iopub.status.idle": "2022-12-14T22:04:57.647985Z", "shell.execute_reply": "2022-12-14T22:04:57.647122Z" }, "id": "Duhg9NrUymwO" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First Citizen:\n", "Before we proceed any further, hear me speak.\n", "\n", "All:\n", "Speak, speak.\n", "\n", "First Citizen:\n", "You are all resolved rather to die than to famish?\n", "\n", "All:\n", "Resolved. resolved.\n", "\n", "First Citizen:\n", "First, you know Caius Marcius is chief enemy to the people.\n", "\n" ] } ], "source": [ "# 텍스트의 처음 250자를 살펴봅니다\n", "print(text[:250])" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.651361Z", "iopub.status.busy": "2022-12-14T22:04:57.650906Z", "iopub.status.idle": "2022-12-14T22:04:57.667785Z", "shell.execute_reply": "2022-12-14T22:04:57.666909Z" }, "id": "IlCgQBRVymwR" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "고유 문자수 65개\n" ] } ], "source": [ "# 파일의 고유 문자수를 출력합니다.\n", "vocab = sorted(set(text))\n", "print ('고유 문자수 {}개'.format(len(vocab)))" ] }, { "cell_type": "markdown", "metadata": { "id": "rNnrKn_lL-IJ" }, "source": [ "## 텍스트 처리" ] }, { "cell_type": "markdown", "metadata": { "id": "LFjSVAlWzf-N" }, "source": [ "### 텍스트 벡터화\n", "\n", "훈련 전, 문자들을 수치화할 필요가 있습니다. 두 개의 조회 테이블(lookup table)을 만듭니다: 하나는 문자를 숫자에 매핑하고 다른 하나는 숫자를 문자에 매핑하는 것입니다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.671540Z", "iopub.status.busy": "2022-12-14T22:04:57.670998Z", "iopub.status.idle": "2022-12-14T22:04:57.790960Z", "shell.execute_reply": "2022-12-14T22:04:57.789883Z" }, "id": "IalZLbvOzf-F" }, "outputs": [], "source": [ "# 고유 문자에서 인덱스로 매핑 생성\n", "char2idx = {u:i for i, u in enumerate(vocab)}\n", "idx2char = np.array(vocab)\n", "\n", "text_as_int = np.array([char2idx[c] for c in text])" ] }, { "cell_type": "markdown", "metadata": { "id": "tZfqhkYCymwX" }, "source": [ "이제 각 문자에 대한 정수 표현을 만들었습니다. 문자를 0번 인덱스부터 고유 문자 길이까지 매핑한 것을 기억합시다." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.795153Z", "iopub.status.busy": "2022-12-14T22:04:57.794385Z", "iopub.status.idle": "2022-12-14T22:04:57.799941Z", "shell.execute_reply": "2022-12-14T22:04:57.799047Z" }, "id": "FYyNlCNXymwY" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " '\\n': 0,\n", " ' ' : 1,\n", " '!' : 2,\n", " '$' : 3,\n", " '&' : 4,\n", " \"'\" : 5,\n", " ',' : 6,\n", " '-' : 7,\n", " '.' : 8,\n", " '3' : 9,\n", " ':' : 10,\n", " ';' : 11,\n", " '?' : 12,\n", " 'A' : 13,\n", " 'B' : 14,\n", " 'C' : 15,\n", " 'D' : 16,\n", " 'E' : 17,\n", " 'F' : 18,\n", " 'G' : 19,\n", " ...\n", "}\n" ] } ], "source": [ "print('{')\n", "for char,_ in zip(char2idx, range(20)):\n", " print(' {:4s}: {:3d},'.format(repr(char), char2idx[char]))\n", "print(' ...\\n}')" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.803758Z", "iopub.status.busy": "2022-12-14T22:04:57.802984Z", "iopub.status.idle": "2022-12-14T22:04:57.808202Z", "shell.execute_reply": "2022-12-14T22:04:57.807188Z" }, "id": "l1VKcQHcymwb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'First Citizen' ---- 문자들이 다음의 정수로 매핑되었습니다 ---- > [18 47 56 57 58 1 15 47 58 47 64 43 52]\n" ] } ], "source": [ "# 텍스트에서 처음 13개의 문자가 숫자로 어떻게 매핑되었는지를 보여줍니다\n", "print ('{} ---- 문자들이 다음의 정수로 매핑되었습니다 ---- > {}'.format(repr(text[:13]), text_as_int[:13]))" ] }, { "cell_type": "markdown", "metadata": { "id": "bbmsf23Bymwe" }, "source": [ "### 예측 과정" ] }, { "cell_type": "markdown", "metadata": { "id": "wssHQ1oGymwe" }, "source": [ "주어진 문자나 문자 시퀀스가 주어졌을 때, 다음 문자로 가장 가능성 있는 문자는 무엇일까요? 이는 모델을 훈련하여 수행할 작업입니다. 모델의 입력은 문자열 시퀀스가 될 것이고, 모델을 훈련시켜 출력을 예측합니다. 이 출력은 현재 타임 스텝(time step)의 다음 문자입니다.\n", "\n", "RNN은 이전에 본 요소에 의존하는 내부 상태를 유지하고 있으므로, 이 순간까지 계산된 모든 문자를 감안할 때, 다음 문자는 무엇일까요?" ] }, { "cell_type": "markdown", "metadata": { "id": "hgsVvVxnymwf" }, "source": [ "### 훈련 샘플과 타깃 만들기\n", "\n", "다음으로 텍스트를 샘플 시퀀스로 나눕니다. 각 입력 시퀀스에는 텍스트에서 나온 `seq_length`개의 문자가 포함될 것입니다.\n", "\n", "각 입력 시퀀스에서, 해당 타깃은 한 문자를 오른쪽으로 이동한 것을 제외하고는 동일한 길이의 텍스트를 포함합니다.\n", "\n", "따라서 텍스트를`seq_length + 1`개의 청크(chunk)로 나눕니다. 예를 들어, `seq_length`는 4이고 텍스트를 \"Hello\"이라고 가정해 봅시다. 입력 시퀀스는 \"Hell\"이고 타깃 시퀀스는 \"ello\"가 됩니다.\n", "\n", "이렇게 하기 위해 먼저 `tf.data.Dataset.from_tensor_slices` 함수를 사용해 텍스트 벡터를 문자 인덱스의 스트림으로 변환합니다." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:04:57.812287Z", "iopub.status.busy": "2022-12-14T22:04:57.811598Z", "iopub.status.idle": "2022-12-14T22:05:01.511067Z", "shell.execute_reply": "2022-12-14T22:05:01.510135Z" }, "id": "0UHJDA39zf-O" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "F\n", "i\n", "r\n", "s\n", "t\n" ] } ], "source": [ "# 단일 입력에 대해 원하는 문장의 최대 길이\n", "seq_length = 100\n", "examples_per_epoch = len(text)//seq_length\n", "\n", "# 훈련 샘플/타깃 만들기\n", "char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)\n", "\n", "for i in char_dataset.take(5):\n", " print(idx2char[i.numpy()])" ] }, { "cell_type": "markdown", "metadata": { "id": "-ZSYAcQV8OGP" }, "source": [ "`batch` 메서드는 이 개별 문자들을 원하는 크기의 시퀀스로 쉽게 변환할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:05:01.514800Z", "iopub.status.busy": "2022-12-14T22:05:01.514454Z", "iopub.status.idle": "2022-12-14T22:05:01.540606Z", "shell.execute_reply": "2022-12-14T22:05:01.539876Z" }, "id": "l4hkDU3i7ozi" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'First Citizen:\\nBefore we proceed any further, hear me speak.\\n\\nAll:\\nSpeak, speak.\\n\\nFirst Citizen:\\nYou '\n", "'are all resolved rather to die than to famish?\\n\\nAll:\\nResolved. resolved.\\n\\nFirst Citizen:\\nFirst, you k'\n", "\"now Caius Marcius is chief enemy to the people.\\n\\nAll:\\nWe know't, we know't.\\n\\nFirst Citizen:\\nLet us ki\"\n", "\"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\"\n", "'one: away, away!\\n\\nSecond Citizen:\\nOne word, good citizens.\\n\\nFirst Citizen:\\nWe are accounted poor citi'\n" ] } ], "source": [ "sequences = char_dataset.batch(seq_length+1, drop_remainder=True)\n", "\n", "for item in sequences.take(5):\n", " print(repr(''.join(idx2char[item.numpy()])))" ] }, { "cell_type": "markdown", "metadata": { "id": "UbLcIPBj_mWZ" }, "source": [ "각 시퀀스에서, `map` 메서드를 사용해 각 배치에 간단한 함수를 적용하고 입력 텍스트와 타깃 텍스트를 복사 및 이동합니다:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:05:01.544760Z", "iopub.status.busy": "2022-12-14T22:05:01.544106Z", "iopub.status.idle": "2022-12-14T22:05:01.588766Z", "shell.execute_reply": "2022-12-14T22:05:01.588001Z" }, "id": "9NGu-FkO_kYU" }, "outputs": [], "source": [ "def split_input_target(chunk):\n", " input_text = chunk[:-1]\n", " target_text = chunk[1:]\n", " return input_text, target_text\n", "\n", "dataset = sequences.map(split_input_target)" ] }, { "cell_type": "markdown", "metadata": { "id": "hiCopyGZymwi" }, "source": [ "첫 번째 샘플의 타깃 값을 출력합니다:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:05:01.593190Z", "iopub.status.busy": "2022-12-14T22:05:01.592551Z", "iopub.status.idle": "2022-12-14T22:05:01.622050Z", "shell.execute_reply": "2022-12-14T22:05:01.621231Z" }, "id": "GNbw-iR0ymwj" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "입력 데이터: 'First Citizen:\\nBefore we proceed any further, hear me speak.\\n\\nAll:\\nSpeak, speak.\\n\\nFirst Citizen:\\nYou'\n", "타깃 데이터: 'irst Citizen:\\nBefore we proceed any further, hear me speak.\\n\\nAll:\\nSpeak, speak.\\n\\nFirst Citizen:\\nYou '\n" ] } ], "source": [ "for input_example, target_example in dataset.take(1):\n", " print ('입력 데이터: ', repr(''.join(idx2char[input_example.numpy()])))\n", " print ('타깃 데이터: ', repr(''.join(idx2char[target_example.numpy()])))" ] }, { "cell_type": "markdown", "metadata": { "id": "_33OHL3b84i0" }, "source": [ "이 벡터의 각 인덱스는 하나의 타임 스텝(time step)으로 처리됩니다. 타임 스텝 0의 입력으로 모델은 \"F\"의 인덱스를 받고 다음 문자로 \"i\"의 인덱스를 예측합니다. 다음 타임 스텝에서도 같은 일을 하지만 RNN은 현재 입력 문자 외에 이전 타임 스텝의 컨텍스트**(context)**를 고려합니다." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:05:01.626099Z", "iopub.status.busy": "2022-12-14T22:05:01.625454Z", "iopub.status.idle": "2022-12-14T22:05:01.644025Z", "shell.execute_reply": "2022-12-14T22:05:01.643234Z" }, "id": "0eBu9WZG84i0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0단계\n", " 입력: 18 ('F')\n", " 예상 출력: 47 ('i')\n", " 1단계\n", " 입력: 47 ('i')\n", " 예상 출력: 56 ('r')\n", " 2단계\n", " 입력: 56 ('r')\n", " 예상 출력: 57 ('s')\n", " 3단계\n", " 입력: 57 ('s')\n", " 예상 출력: 58 ('t')\n", " 4단계\n", " 입력: 58 ('t')\n", " 예상 출력: 1 (' ')\n" ] } ], "source": [ "for i, (input_idx, target_idx) in enumerate(zip(input_example[:5], target_example[:5])):\n", " print(\"{:4d}단계\".format(i))\n", " print(\" 입력: {} ({:s})\".format(input_idx, repr(idx2char[input_idx])))\n", " print(\" 예상 출력: {} ({:s})\".format(target_idx, repr(idx2char[target_idx])))" ] }, { "cell_type": "markdown", "metadata": { "id": "MJdfPmdqzf-R" }, "source": [ "### 훈련 배치 생성\n", "\n", "텍스트를 다루기 쉬운 시퀀스로 분리하기 위해 `tf.data`를 사용했습니다. 그러나 이 데이터를 모델에 넣기 전에 데이터를 섞은 후 배치를 만들어야 합니다." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:05:01.647849Z", "iopub.status.busy": "2022-12-14T22:05:01.647270Z", "iopub.status.idle": "2022-12-14T22:05:01.659086Z", "shell.execute_reply": "2022-12-14T22:05:01.658428Z" }, "id": "p2pGotuNzf-S" }, "outputs": [ { "data": { "text/plain": [ "