{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using TensorFlow backend.\n" ] }, { "data": { "text/plain": [ "'2.2.4'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import keras\n", "keras.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 단어와 문자의 원-핫 인코딩\n", "\n", "이 노트북은 [케라스 창시자에게 배우는 딥러닝](https://tensorflow.blog/케라스-창시자에게-배우는-딥러닝/) 책의 6장 1절의 코드 예제입니다. 책에는 더 많은 내용과 그림이 있습니다. 이 노트북에는 소스 코드에 관련된 설명만 포함합니다. 이 노트북의 설명은 케라스 버전 2.2.2에 맞추어져 있습니다. 케라스 최신 버전이 릴리스되면 노트북을 다시 테스트하기 때문에 설명과 코드의 결과가 조금 다를 수 있습니다.\n", "\n", "----\n", "\n", "원-핫 인코딩은 토큰을 벡터로 변환하는 가장 일반적이고 기본적인 방법입니다. 3장에서 IMDB와 로이터 예제에서 이를 보았습니다(단어의 원-핫 인코딩을 사용했습니다). 모든 단어에 고유한 정수 인덱스를 부여하고 이 정수 인덱스 i를 크기가 N(어휘 사전의 크기)인 이진 벡터로 변환합니다. 이 벡터는 i번째 원소만 1이고 나머지는 모두 0입니다.\n", "\n", "물론 원-핫 인코딩은 문자 수준에서도 적용할 수 있습니다. 원-핫 인코딩이 무엇이고 어떻게 구현하는지 명확하게 설명하기 위해 단어와 문자에 대한 간단한 예를 만들었습니다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "단어 수준의 원-핫 인코딩(간단한 예):" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "# 초기 데이터: 각 원소가 샘플입니다\n", "# (이 예에서 하나의 샘플이 하나의 문장입니다. 하지만 문서 전체가 될 수도 있습니다)\n", "samples = ['The cat sat on the mat.', 'The dog ate my homework.']\n", "\n", "# 데이터에 있는 모든 토큰의 인덱스를 구축합니다\n", "token_index = {}\n", "for sample in samples:\n", " # split() 메서드를 사용해 샘플을 토큰으로 나눕니다.\n", " # 실전에서는 구둣점과 특수 문자도 사용합니다.\n", " for word in sample.split():\n", " if word not in token_index:\n", " # 단어마다 고유한 인덱스를 할당합니다.\n", " token_index[word] = len(token_index) + 1\n", " # 인덱스 0은 사용하지 않습니다.\n", "\n", "# 샘플을 벡터로 변환합니다.\n", "# 각 샘플에서 max_length 까지 단어만 사용합니다.\n", "max_length = 10\n", "\n", "# 결과를 저장할 배열입니다\n", "results = np.zeros((len(samples), max_length, max(token_index.values()) + 1))\n", "for i, sample in enumerate(samples):\n", " for j, word in list(enumerate(sample.split()))[:max_length]:\n", " index = token_index.get(word)\n", " results[i, j, index] = 1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "문자 수준 원-핫 인코딩(간단한 예)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import string\n", "\n", "samples = ['The cat sat on the mat.', 'The dog ate my homework.']\n", "characters = string.printable # 출력 가능한 모든 아스키(ASCII) 문자\n", "token_index = dict(zip(characters, range(1, len(characters) + 1)))\n", "\n", "max_length = 50\n", "results = np.zeros((len(samples), max_length, max(token_index.values()) + 1))\n", "for i, sample in enumerate(samples):\n", " for j, character in enumerate(sample[:max_length]):\n", " index = token_index.get(character)\n", " results[i, j, index] = 1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "케라스에는 원본 텍스트 데이터를 단어 또는 문자 수준의 원-핫 인코딩으로 변환해주는 유틸리티가 있습니다. 특수 문자를 제거하거나 빈도가 높은 N개의 단어만을 선택(입력 벡터 공간이 너무 커지지 않도록 하기 위한 일반적인 제한 방법입니다)하는 등 여러 가지 중요한 기능들이 있기 때문에 이 유틸리티를 사용하는 것이 좋습니다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "케라스를 사용한 단어 수준의 원-핫 인코딩:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 9 unique tokens.\n" ] } ], "source": [ "from keras.preprocessing.text import Tokenizer\n", "\n", "samples = ['The cat sat on the mat.', 'The dog ate my homework.']\n", "\n", "# 가장 빈도가 높은 1,000개의 단어만 선택하도록 Tokenizer 객체를 만듭니다.\n", "tokenizer = Tokenizer(num_words=1000)\n", "# 단어 인덱스를 구축합니다.\n", "tokenizer.fit_on_texts(samples)\n", "\n", "# 문자열을 정수 인덱스의 리스트로 변환합니다.\n", "sequences = tokenizer.texts_to_sequences(samples)\n", "\n", "# 직접 원-핫 이진 벡터 표현을 얻을 수 있습니다.\n", "# 원-핫 인코딩 외에 다른 벡터화 방법들도 제공합니다!\n", "one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')\n", "\n", "# 계산된 단어 인덱스를 구합니다.\n", "word_index = tokenizer.word_index\n", "print('Found %s unique tokens.' % len(word_index))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "원-핫 인코딩의 변종 중 하나는 원-핫 해싱 기법입니다. 이 방식은 어휘 사전에 있는 고유한 토큰의 수가 너무 커서 모두 다루기 어려울 때 사용합니다. 각 단어에 명시적으로 인덱스를 할당하고 이 인덱스를 딕셔너리에 저장하는 대신에 단어를 해싱하여 고정된 크기의 벡터로 변환합니다. 일반적으로 간단한 해싱 함수를 사용합니다. 이 방식의 주요 장점은 명시적인 단어 인덱스가 필요 없기 때문에 메모리를 절약하고 온라인 방식으로 데이터를 인코딩할 수 있습니다(전체 데이터를 확인하지 않고 토큰을 생성할 수 있습니다). 한 가지 단점은 해시 충돌입니다. 두 개의 단어가 같은 해시를 만들면 이를 바라보는 머신 러닝 모델은 단어 사이의 차이를 인식하지 못합니다. 해싱 공간의 차원이 해싱될 고유 토큰의 전체 개수보다 훨씬 크면 해시 충돌의 가능성은 감소합니다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "해싱 기법을 사용한 단어 수준의 원-핫 인코딩(간단한 예):" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "samples = ['The cat sat on the mat.', 'The dog ate my homework.']\n", "\n", "# 단어를 크기가 1,000인 벡터로 저장합니다.\n", "# 1,000개(또는 그이상)의 단어가 있다면 해싱 충돌이 늘어나고 인코딩의 정확도가 감소될 것입니다\n", "dimensionality = 1000\n", "max_length = 10\n", "\n", "results = np.zeros((len(samples), max_length, dimensionality))\n", "for i, sample in enumerate(samples):\n", " for j, word in list(enumerate(sample.split()))[:max_length]:\n", " # 단어를 해싱하여 0과 1,000 사이의 랜덤한 정수 인덱스로 변환합니다.\n", " index = abs(hash(word)) % dimensionality\n", " results[i, j, index] = 1." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.6.6" } }, "nbformat": 4, "nbformat_minor": 2 }