AI-For-Beginners/translations/ko/lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb

647 lines
27 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 텍스트 분류 작업\n",
"\n",
"이 모듈에서는 **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** 데이터셋을 기반으로 간단한 텍스트 분류 작업을 시작합니다. 우리는 뉴스 헤드라인을 세계, 스포츠, 비즈니스, 과학/기술 중 하나의 카테고리로 분류할 것입니다.\n",
"\n",
"## 데이터셋\n",
"\n",
"데이터셋을 로드하기 위해 **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** API를 사용할 것입니다.\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",
"\n",
"# In this tutorial, we will be training a lot of models. In order to use GPU memory cautiously,\n",
"# we will set tensorflow option to grow GPU memory allocation when required.\n",
"physical_devices = tf.config.list_physical_devices('GPU') \n",
"if len(physical_devices)>0:\n",
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
"\n",
"dataset = tfds.load('ag_news_subset')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"이제 `dataset['train']` 및 `dataset['test']`를 사용하여 데이터셋의 학습 및 테스트 부분에 각각 접근할 수 있습니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Length of train dataset = 120000\n",
"Length of test dataset = 7600\n"
]
}
],
"source": [
"ds_train = dataset['train']\n",
"ds_test = dataset['test']\n",
"\n",
"print(f\"Length of train dataset = {len(ds_train)}\")\n",
"print(f\"Length of test dataset = {len(ds_test)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"우리의 데이터셋에서 새로운 헤드라인 10개를 출력해 봅시다:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3 (Sci/Tech) -> b'AMD Debuts Dual-Core Opteron Processor' b'AMD #39;s new dual-core Opteron chip is designed mainly for corporate computing applications, including databases, Web services, and financial transactions.'\n",
"1 (Sports) -> b\"Wood's Suspension Upheld (Reuters)\" b'Reuters - Major League Baseball\\\\Monday announced a decision on the appeal filed by Chicago Cubs\\\\pitcher Kerry Wood regarding a suspension stemming from an\\\\incident earlier this season.'\n",
"2 (Business) -> b'Bush reform may have blue states seeing red' b'President Bush #39;s quot;revenue-neutral quot; tax reform needs losers to balance its winners, and people claiming the federal deduction for state and local taxes may be in administration planners #39; sights, news reports say.'\n",
"3 (Sci/Tech) -> b\"'Halt science decline in schools'\" b'Britain will run out of leading scientists unless science education is improved, says Professor Colin Pillinger.'\n",
"1 (Sports) -> b'Gerrard leaves practice' b'London, England (Sports Network) - England midfielder Steven Gerrard injured his groin late in Thursday #39;s training session, but is hopeful he will be ready for Saturday #39;s World Cup qualifier against Austria.'\n"
]
}
],
"source": [
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
"\n",
"for i,x in zip(range(5),ds_train):\n",
" print(f\"{x['label']} ({classes[x['label']]}) -> {x['title']} {x['description']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 텍스트 벡터화\n",
"\n",
"이제 텍스트를 **숫자**로 변환하여 텐서로 표현해야 합니다. 단어 수준의 표현을 원한다면, 두 가지 작업이 필요합니다:\n",
"\n",
"* **토크나이저**를 사용하여 텍스트를 **토큰**으로 분리합니다.\n",
"* 이러한 토큰의 **어휘집**을 만듭니다.\n",
"\n",
"### 어휘 크기 제한하기\n",
"\n",
"AG News 데이터셋 예제를 보면, 어휘 크기가 상당히 큽니다. 10만 개 이상의 단어가 포함되어 있습니다. 일반적으로 텍스트에 거의 등장하지 않는 단어는 필요하지 않습니다. 이런 단어들은 몇 문장에만 나타나며, 모델이 학습하는 데 큰 도움이 되지 않습니다. 따라서 벡터화 생성자에 인자를 전달하여 어휘 크기를 더 작은 숫자로 제한하는 것이 합리적입니다.\n",
"\n",
"이 두 단계는 모두 **TextVectorization** 레이어를 사용하여 처리할 수 있습니다. 이제 벡터화 객체를 생성한 다음, `adapt` 메서드를 호출하여 모든 텍스트를 처리하고 어휘집을 만들어 보겠습니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"vocab_size = 50000\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size)\n",
"vectorizer.adapt(ds_train.take(500).map(lambda x: x['title']+' '+x['description']))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **참고** 우리는 전체 데이터셋의 일부만 사용하여 어휘를 구축하고 있습니다. 이렇게 하면 실행 시간을 단축시켜 기다리는 시간을 줄일 수 있습니다. 하지만 전체 데이터셋의 일부 단어가 어휘에 포함되지 않아 학습 중 무시될 위험이 있습니다. 따라서 `adapt` 과정에서 전체 어휘 크기를 사용하고 모든 데이터셋을 처리하면 최종 정확도가 약간 향상될 수 있지만, 그 차이는 크지 않을 것입니다.\n",
"\n",
"이제 실제 어휘에 접근할 수 있습니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['', '[UNK]', 'the', 'to', 'a', 'in', 'of', 'and', 'on', 'for']\n",
"Length of vocabulary: 5335\n"
]
}
],
"source": [
"vocab = vectorizer.get_vocabulary()\n",
"vocab_size = len(vocab)\n",
"print(vocab[:10])\n",
"print(f\"Length of vocabulary: {vocab_size}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"벡터라이저를 사용하여 우리는 텍스트를 숫자 집합으로 쉽게 인코딩할 수 있습니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<tf.Tensor: shape=(7,), dtype=int64, numpy=array([ 112, 3695, 3, 304, 11, 1041, 1], dtype=int64)>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer('I love to play with my words')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Bag-of-words 텍스트 표현\n",
"\n",
"단어는 의미를 나타내기 때문에, 때로는 문장에서 단어의 순서를 고려하지 않고 개별 단어만을 살펴보는 것으로 텍스트의 의미를 파악할 수 있습니다. 예를 들어, 뉴스 분류를 할 때 *weather*와 *snow* 같은 단어는 *날씨 예보*를 나타낼 가능성이 높고, *stocks*와 *dollar* 같은 단어는 *금융 뉴스*에 해당할 것입니다.\n",
"\n",
"**Bag-of-words** (BoW) 벡터 표현은 가장 이해하기 쉬운 전통적인 벡터 표현 방식입니다. 각 단어는 벡터 인덱스에 연결되며, 벡터 요소는 주어진 문서에서 각 단어가 나타난 횟수를 포함합니다.\n",
"\n",
"![Bag-of-words 벡터 표현이 메모리에서 어떻게 표현되는지 보여주는 이미지.](../../../../../lessons/5-NLP/13-TextRep/images/bag-of-words-example.png) \n",
"\n",
"> **Note**: BoW를 텍스트 내 개별 단어에 대한 모든 원-핫 인코딩 벡터의 합으로 생각할 수도 있습니다.\n",
"\n",
"아래는 Scikit Learn 파이썬 라이브러리를 사용하여 bag-of-words 표현을 생성하는 예제입니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"sc_vectorizer = CountVectorizer()\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"sc_vectorizer.fit_transform(corpus)\n",
"sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"우리는 위에서 정의한 Keras 벡터라이저를 사용하여 각 단어 번호를 원-핫 인코딩으로 변환하고, 그 벡터들을 모두 더할 수도 있습니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def to_bow(text):\n",
" return tf.reduce_sum(tf.one_hot(vectorizer(text),vocab_size),axis=0)\n",
"\n",
"to_bow('My dog likes hot dogs on a hot day.').numpy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **참고**: 결과가 이전 예제와 다르다는 점이 놀라울 수 있습니다. 그 이유는 Keras 예제에서는 벡터의 길이가 전체 AG News 데이터셋에서 생성된 어휘 크기에 해당하지만, Scikit Learn 예제에서는 샘플 텍스트에서 즉석으로 어휘를 생성했기 때문입니다.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## BoW 분류기 학습하기\n",
"\n",
"이제 텍스트의 bag-of-words 표현을 만드는 방법을 배웠으니, 이를 사용하는 분류기를 학습시켜 봅시다. 먼저, 데이터셋을 bag-of-words 표현으로 변환해야 합니다. 이는 다음과 같이 `map` 함수를 사용하여 수행할 수 있습니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"batch_size = 128\n",
"\n",
"ds_train_bow = ds_train.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)\n",
"ds_test_bow = ds_test.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"이제 하나의 선형 계층을 포함하는 간단한 분류기 신경망을 정의해 봅시다. 입력 크기는 `vocab_size`이고, 출력 크기는 클래스 수(4)에 해당합니다. 분류 작업을 해결하고 있으므로 최종 활성화 함수는 **softmax**입니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c70a947f0>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Dense(4,activation='softmax',input_shape=(vocab_size,))\n",
"])\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train_bow,validation_data=ds_test_bow)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"우리가 4개의 클래스를 가지고 있기 때문에, 80% 이상의 정확도는 좋은 결과입니다.\n",
"\n",
"## 하나의 네트워크로 분류기 학습시키기\n",
"\n",
"벡터라이저가 Keras 레이어이기 때문에, 이를 포함한 네트워크를 정의하고 처음부터 끝까지 학습시킬 수 있습니다. 이렇게 하면 `map`을 사용해 데이터셋을 벡터화할 필요가 없으며, 원본 데이터셋을 네트워크의 입력으로 바로 전달할 수 있습니다.\n",
"\n",
"> **Note**: 여전히 데이터셋의 필드(예: `title`, `description`, `label`)를 딕셔너리에서 튜플로 변환하기 위해 `map`을 적용해야 합니다. 하지만 디스크에서 데이터를 로드할 때 처음부터 필요한 구조를 가진 데이터셋을 생성할 수 있습니다.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_1 (InputLayer) [(None, 1)] 0 \n",
" \n",
" text_vectorization (TextVec (None, None) 0 \n",
" torization) \n",
" \n",
" tf.one_hot (TFOpLambda) (None, None, 5335) 0 \n",
" \n",
" tf.math.reduce_sum (TFOpLam (None, 5335) 0 \n",
" bda) \n",
" \n",
" dense_2 (Dense) (None, 4) 21344 \n",
" \n",
"=================================================================\n",
"Total params: 21,344\n",
"Trainable params: 21,344\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"938/938 [==============================] - 73s 77ms/step - loss: 0.6057 - acc: 0.8414 - val_loss: 0.4202 - val_acc: 0.8736\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c721521f0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"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",
"inp = keras.Input(shape=(1,),dtype=tf.string)\n",
"x = vectorizer(inp)\n",
"x = tf.reduce_sum(tf.one_hot(x,vocab_size),axis=1)\n",
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
"model = keras.models.Model(inp,out)\n",
"model.summary()\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 바이그램, 트라이그램 및 n-그램\n",
"\n",
"Bag-of-words 접근법의 한계 중 하나는 일부 단어가 다단어 표현의 일부라는 점입니다. 예를 들어, 'hot dog'이라는 단어는 다른 문맥에서 'hot'과 'dog'이라는 단어와 완전히 다른 의미를 가집니다. 'hot'과 'dog'을 항상 동일한 벡터로 표현하면 모델이 혼란스러워질 수 있습니다.\n",
"\n",
"이를 해결하기 위해, **n-그램 표현**이 문서 분류 방법에서 자주 사용됩니다. 여기서 각 단어, 두 단어 또는 세 단어의 빈도는 분류기를 학습시키는 데 유용한 특징이 됩니다. 예를 들어, 바이그램 표현에서는 원래 단어 외에도 모든 단어 쌍을 어휘에 추가합니다.\n",
"\n",
"아래는 Scikit Learn을 사용하여 바이그램 bag-of-words 표현을 생성하는 방법의 예입니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary:\n",
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
]
},
{
"data": {
"text/plain": [
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int64)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"bigram_vectorizer.fit_transform(corpus)\n",
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"n-gram 접근법의 주요 단점은 어휘 크기가 매우 빠르게 증가한다는 점입니다. 실제로는 *임베딩(embeddings)*과 같은 차원 축소 기법과 n-gram 표현을 결합해야 합니다. 이에 대해서는 다음 단원에서 다룰 예정입니다.\n",
"\n",
"**AG News** 데이터셋에서 n-gram 표현을 사용하려면, `TextVectorization` 생성자에 `ngrams` 매개변수를 전달해야 합니다. 바이그램 어휘의 길이는 **상당히 더 커지며**, 우리의 경우 130만 개 이상의 토큰에 달합니다! 따라서 합리적인 숫자로 바이그램 토큰의 수를 제한하는 것이 타당합니다.\n",
"\n",
"위와 동일한 코드를 사용하여 분류기를 훈련할 수도 있지만, 이는 메모리 효율성이 매우 낮을 것입니다. 다음 단원에서는 임베딩을 사용하여 바이그램 분류기를 훈련할 것입니다. 그동안 이 노트북에서 바이그램 분류기 훈련을 실험해 보고 더 높은 정확도를 얻을 수 있는지 확인해 보세요.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## BoW 벡터 자동 계산\n",
"\n",
"위 예제에서는 개별 단어의 원-핫 인코딩을 합산하여 BoW 벡터를 수작업으로 계산했습니다. 하지만 TensorFlow의 최신 버전을 사용하면 벡터라이저 생성자에 `output_mode='count` 매개변수를 전달하여 BoW 벡터를 자동으로 계산할 수 있습니다. 이를 통해 모델을 정의하고 학습시키는 과정이 훨씬 간단해집니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c725217c0>"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='count'),\n",
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
"])\n",
"print(\"Training vectorizer\")\n",
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 용어 빈도 - 역문서 빈도 (TF-IDF)\n",
"\n",
"BoW 표현에서는 단어 자체와 상관없이 동일한 방식으로 단어 발생 빈도가 가중치로 사용됩니다. 하지만 *a*나 *in* 같은 자주 등장하는 단어들은 전문 용어에 비해 분류에 훨씬 덜 중요하다는 것은 명백합니다. 대부분의 NLP 작업에서는 특정 단어들이 다른 단어들보다 더 중요합니다.\n",
"\n",
"**TF-IDF**는 **용어 빈도 - 역문서 빈도**를 의미합니다. 이는 단순히 문서 내 단어의 존재 여부를 0/1로 나타내는 BoW 방식과 달리, 단어 발생 빈도와 관련된 부동 소수점 값을 사용하는 BoW의 변형입니다.\n",
"\n",
"좀 더 공식적으로, 문서 $j$에서 단어 $i$의 가중치 $w_{ij}$는 다음과 같이 정의됩니다:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"여기서\n",
"* $tf_{ij}$는 $j$에서 $i$가 등장한 횟수, 즉 이전에 본 BoW 값입니다.\n",
"* $N$은 컬렉션 내 문서의 총 개수입니다.\n",
"* $df_i$는 컬렉션 전체에서 단어 $i$를 포함하는 문서의 개수입니다.\n",
"\n",
"TF-IDF 값 $w_{ij}$는 단어가 문서에 등장한 횟수에 비례하여 증가하며, 해당 단어를 포함하는 코퍼스 내 문서 수에 따라 조정됩니다. 이는 일부 단어가 다른 단어보다 더 자주 등장하는 사실을 보정하는 데 도움을 줍니다. 예를 들어, 특정 단어가 컬렉션 내 *모든* 문서에 등장한다면, $df_i=N$이 되고 $w_{ij}=0$이 되어 해당 단어는 완전히 무시됩니다.\n",
"\n",
"Scikit Learn을 사용하면 텍스트의 TF-IDF 벡터화를 쉽게 생성할 수 있습니다:\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
" 0. , 0. , 0. , 0. , 0. ,\n",
" 0. ]])"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Keras에서 `TextVectorization` 레이어는 `output_mode='tf-idf'` 매개변수를 전달하여 TF-IDF 빈도를 자동으로 계산할 수 있습니다. TF-IDF를 사용하면 정확도가 향상되는지 확인하기 위해 위에서 사용한 코드를 반복해 봅시다:\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c729dfd30>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='tf-idf'),\n",
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
"])\n",
"print(\"Training vectorizer\")\n",
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 결론\n",
"\n",
"TF-IDF 표현이 각 단어에 빈도 가중치를 부여하더라도, 의미나 순서를 표현할 수는 없습니다. 유명한 언어학자 J. R. 퍼스가 1935년에 말했듯이, \"단어의 완전한 의미는 항상 맥락적이며, 맥락을 벗어난 의미 연구는 진지하게 받아들일 수 없다.\" 이후 강의에서 언어 모델링을 사용하여 텍스트에서 맥락 정보를 포착하는 방법을 배우게 될 것입니다.\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": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"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"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "19b43951d55b377a76209c24c1f017e4",
"translation_date": "2025-08-31T14:11:40+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "ko"
}
},
"nbformat": 4,
"nbformat_minor": 4
}