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

647 lines
26 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tarefa de classificação de texto\n",
"\n",
"Neste módulo, vamos começar com uma tarefa simples de classificação de texto baseada no conjunto de dados **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)**: vamos classificar manchetes de notícias em uma das 4 categorias: Mundo, Desporto, Negócios e Ciência/Tecnologia.\n",
"\n",
"## O Conjunto de Dados\n",
"\n",
"Para carregar o conjunto de dados, utilizaremos a API **[TensorFlow Datasets](https://www.tensorflow.org/datasets)**.\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": [
"Agora podemos aceder às partes de treino e teste do conjunto de dados utilizando `dataset['train']` e `dataset['test']`, respetivamente:\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": [
"Vamos imprimir as primeiras 10 novas manchetes do nosso conjunto de dados:\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": [
"## Vetorização de texto\n",
"\n",
"Agora precisamos converter texto em **números** que podem ser representados como tensores. Se quisermos uma representação ao nível de palavras, precisamos fazer duas coisas:\n",
"\n",
"* Usar um **tokenizador** para dividir o texto em **tokens**.\n",
"* Construir um **vocabulário** desses tokens.\n",
"\n",
"### Limitar o tamanho do vocabulário\n",
"\n",
"No exemplo do conjunto de dados AG News, o tamanho do vocabulário é bastante grande, com mais de 100 mil palavras. De um modo geral, não precisamos de palavras que raramente aparecem no texto — apenas algumas frases as terão, e o modelo não aprenderá com elas. Assim, faz sentido limitar o tamanho do vocabulário para um número menor, passando um argumento ao construtor do vetorizador:\n",
"\n",
"Ambos os passos podem ser realizados utilizando a camada **TextVectorization**. Vamos instanciar o objeto vetorizador e, em seguida, chamar o método `adapt` para percorrer todo o texto e construir um vocabulário:\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": [
"> **Nota** que estamos a utilizar apenas um subconjunto do conjunto de dados completo para construir um vocabulário. Fazemos isto para acelerar o tempo de execução e não te deixar à espera. No entanto, corremos o risco de que algumas palavras do conjunto de dados completo não sejam incluídas no vocabulário e sejam ignoradas durante o treino. Assim, utilizar o tamanho total do vocabulário e percorrer todo o conjunto de dados durante o `adapt` deverá aumentar a precisão final, mas não de forma significativa.\n",
"\n",
"Agora podemos aceder ao vocabulário real:\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": [
"Usando o vetorizador, podemos facilmente codificar qualquer texto num conjunto de números:\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": [
"## Representação de texto com Bag-of-words\n",
"\n",
"Como as palavras representam significado, às vezes conseguimos entender o significado de um texto apenas olhando para as palavras individuais, independentemente da ordem em que aparecem na frase. Por exemplo, ao classificar notícias, palavras como *meteorologia* e *neve* provavelmente indicam *previsão do tempo*, enquanto palavras como *ações* e *dólar* seriam associadas a *notícias financeiras*.\n",
"\n",
"A representação vetorial **Bag-of-words** (BoW) é a forma tradicional mais simples de entender uma representação vetorial. Cada palavra está associada a um índice no vetor, e um elemento do vetor contém o número de ocorrências de cada palavra em um determinado documento.\n",
"\n",
"![Imagem mostrando como uma representação vetorial bag-of-words é armazenada na memória.](../../../../../lessons/5-NLP/13-TextRep/images/bag-of-words-example.png) \n",
"\n",
"> **Note**: Também pode pensar no BoW como a soma de todos os vetores one-hot codificados para as palavras individuais no texto.\n",
"\n",
"Abaixo está um exemplo de como gerar uma representação bag-of-words utilizando a biblioteca python Scikit Learn:\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": [
"Podemos também usar o vetorizador do Keras que definimos acima, convertendo cada número de palavra numa codificação one-hot e somando todos esses vetores:\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": [
"> **Nota**: Pode ser surpreendente que o resultado seja diferente do exemplo anterior. A razão para isso é que, no exemplo do Keras, o comprimento do vetor corresponde ao tamanho do vocabulário, que foi construído a partir de todo o conjunto de dados AG News, enquanto no exemplo do Scikit Learn construímos o vocabulário a partir do texto de exemplo de forma dinâmica.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Treinar o classificador BoW\n",
"\n",
"Agora que aprendemos a construir a representação bag-of-words do nosso texto, vamos treinar um classificador que a utilize. Primeiro, precisamos converter o nosso conjunto de dados para uma representação bag-of-words. Isto pode ser feito utilizando a função `map` da seguinte forma:\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": [
"Agora vamos definir uma rede neural de classificação simples que contém uma camada linear. O tamanho da entrada é `vocab_size`, e o tamanho da saída corresponde ao número de classes (4). Como estamos a resolver uma tarefa de classificação, a função de ativação final é **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": [
"Como temos 4 classes, uma precisão acima de 80% é um bom resultado.\n",
"\n",
"## Treinar um classificador como uma única rede\n",
"\n",
"Como o vetorizador também é uma camada do Keras, podemos definir uma rede que o inclua e treiná-la de ponta a ponta. Desta forma, não precisamos vetorizar o conjunto de dados usando `map`, podemos simplesmente passar o conjunto de dados original para a entrada da rede.\n",
"\n",
"> **Nota**: Ainda precisaríamos aplicar mapeamentos ao nosso conjunto de dados para converter campos de dicionários (como `title`, `description` e `label`) em tuplos. No entanto, ao carregar os dados do disco, podemos construir um conjunto de dados com a estrutura necessária desde o início.\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": [
"## Bigramas, trigramas e n-gramas\n",
"\n",
"Uma limitação da abordagem de saco de palavras é que algumas palavras fazem parte de expressões compostas, por exemplo, a palavra 'hot dog' tem um significado completamente diferente das palavras 'hot' e 'dog' em outros contextos. Se representarmos as palavras 'hot' e 'dog' sempre usando os mesmos vetores, isso pode confundir o nosso modelo.\n",
"\n",
"Para resolver isso, **representações de n-gramas** são frequentemente utilizadas em métodos de classificação de documentos, onde a frequência de cada palavra, bi-palavra ou tri-palavra é uma característica útil para treinar classificadores. Em representações de bigramas, por exemplo, adicionamos todos os pares de palavras ao vocabulário, além das palavras originais.\n",
"\n",
"Abaixo está um exemplo de como gerar uma representação de saco de palavras com bigramas usando Scikit Learn:\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": [
"A principal desvantagem da abordagem n-gram é que o tamanho do vocabulário começa a crescer extremamente rápido. Na prática, precisamos combinar a representação n-gram com uma técnica de redução de dimensionalidade, como *embeddings*, que discutiremos na próxima unidade.\n",
"\n",
"Para usar uma representação n-gram no nosso conjunto de dados **AG News**, precisamos passar o parâmetro `ngrams` para o nosso construtor `TextVectorization`. O tamanho de um vocabulário de bigramas é **significativamente maior**; no nosso caso, são mais de 1,3 milhões de tokens! Por isso, faz sentido limitar também os tokens de bigramas a um número razoável.\n",
"\n",
"Poderíamos usar o mesmo código acima para treinar o classificador, no entanto, isso seria muito ineficiente em termos de memória. Na próxima unidade, treinaremos o classificador de bigramas utilizando embeddings. Enquanto isso, pode experimentar treinar o classificador de bigramas neste notebook e verificar se consegue obter uma precisão maior.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calculando automaticamente os vetores BoW\n",
"\n",
"No exemplo acima, calculámos os vetores BoW manualmente somando as codificações one-hot de palavras individuais. No entanto, a versão mais recente do TensorFlow permite-nos calcular os vetores BoW automaticamente ao passar o parâmetro `output_mode='count` para o construtor do vetorizador. Isto torna a definição e o treino do nosso modelo significativamente mais simples:\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": [
"## Frequência de termos - frequência inversa de documentos (TF-IDF)\n",
"\n",
"Na representação BoW, as ocorrências de palavras são ponderadas usando a mesma técnica, independentemente da palavra em si. No entanto, é evidente que palavras frequentes como *a* e *em* são muito menos importantes para a classificação do que termos especializados. Na maioria das tarefas de PLN, algumas palavras são mais relevantes do que outras.\n",
"\n",
"**TF-IDF** significa **frequência de termos - frequência inversa de documentos**. É uma variação do modelo bag-of-words, onde, em vez de um valor binário 0/1 que indica a presença de uma palavra num documento, é utilizado um valor em ponto flutuante, relacionado com a frequência de ocorrência da palavra no corpus.\n",
"\n",
"Mais formalmente, o peso $w_{ij}$ de uma palavra $i$ no documento $j$ é definido como:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"onde\n",
"* $tf_{ij}$ é o número de ocorrências de $i$ em $j$, ou seja, o valor BoW que vimos anteriormente\n",
"* $N$ é o número de documentos na coleção\n",
"* $df_i$ é o número de documentos que contêm a palavra $i$ em toda a coleção\n",
"\n",
"O valor TF-IDF $w_{ij}$ aumenta proporcionalmente ao número de vezes que uma palavra aparece num documento e é ajustado pelo número de documentos no corpus que contêm a palavra, o que ajuda a compensar o facto de algumas palavras aparecerem mais frequentemente do que outras. Por exemplo, se a palavra aparecer em *todos* os documentos da coleção, $df_i=N$, e $w_{ij}=0$, e esses termos seriam completamente desconsiderados.\n",
"\n",
"Pode criar facilmente uma vetorização TF-IDF de texto utilizando o Scikit Learn:\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": [
"Em Keras, a camada `TextVectorization` pode calcular automaticamente as frequências TF-IDF ao passar o parâmetro `output_mode='tf-idf'`. Vamos repetir o código que usamos acima para ver se usar TF-IDF aumenta a precisão:\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": [
"## Conclusão\n",
"\n",
"Embora as representações TF-IDF atribuam pesos de frequência a diferentes palavras, elas não conseguem representar o significado ou a ordem. Como disse o famoso linguista J. R. Firth em 1935: \"O significado completo de uma palavra é sempre contextual, e nenhum estudo de significado fora do contexto pode ser levado a sério.\" Mais à frente no curso, aprenderemos como capturar informações contextuais de texto utilizando modelagem de linguagem.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Aviso Legal**: \nEste documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, é importante ter em conta que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes da utilização desta tradução.\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-31T12:04:40+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "pt"
}
},
"nbformat": 4,
"nbformat_minor": 4
}