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

647 lines
26 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tekstklassificeringsopgave\n",
"\n",
"I dette modul starter vi med en simpel tekstklassificeringsopgave baseret på **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)**-datasættet: vi vil klassificere nyhedsoverskrifter i en af 4 kategorier: Verden, Sport, Erhverv og Videnskab/Teknologi.\n",
"\n",
"## Datasættet\n",
"\n",
"For at indlæse datasættet vil vi bruge **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** API'en.\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": [
"Vi kan nu få adgang til trænings- og testdelene af datasættet ved at bruge `dataset['train']` og `dataset['test']` henholdsvis:\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": [
"Lad os udskrive de første 10 nye overskrifter fra vores datasæt:\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": [
"## Tekstvektorisering\n",
"\n",
"Nu skal vi konvertere tekst til **tal**, der kan repræsenteres som tensorer. Hvis vi ønsker repræsentation på ordniveau, skal vi gøre to ting:\n",
"\n",
"* Brug en **tokenizer** til at opdele tekst i **tokens**.\n",
"* Byg et **ordforråd** af disse tokens.\n",
"\n",
"### Begrænsning af ordforrådets størrelse\n",
"\n",
"I eksemplet med AG News-datasættet er ordforrådets størrelse ret stor, mere end 100.000 ord. Generelt set har vi ikke brug for ord, der sjældent forekommer i teksten — kun få sætninger vil indeholde dem, og modellen vil ikke lære af dem. Derfor giver det mening at begrænse ordforrådets størrelse til et mindre antal ved at angive et argument til vektoriseringskonstruktøren:\n",
"\n",
"Begge disse trin kan håndteres ved hjælp af **TextVectorization**-laget. Lad os instantiere vektoriseringsobjektet og derefter kalde `adapt`-metoden for at gennemgå al tekst og opbygge et ordforråd:\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": [
"> **Bemærk** at vi kun bruger et udsnit af hele datasættet til at opbygge et ordforråd. Vi gør dette for at fremskynde udførelsestiden og undgå at holde dig ventende. Dog tager vi risikoen for, at nogle af ordene fra det samlede datasæt ikke bliver inkluderet i ordforrådet og vil blive ignoreret under træningen. Derfor bør brug af hele ordforrådets størrelse og gennemgang af hele datasættet under `adapt` øge den endelige nøjagtighed, men ikke væsentligt.\n",
"\n",
"Nu kan vi få adgang til det faktiske ordforråd:\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": [
"Ved hjælp af vektorisatoren kan vi nemt kode enhver tekst til et sæt tal:\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 tekstrepræsentation\n",
"\n",
"Fordi ord repræsenterer betydning, kan vi nogle gange forstå meningen med en tekst ved blot at se på de enkelte ord, uanset deres rækkefølge i sætningen. For eksempel, når man klassificerer nyheder, er ord som *vejr* og *sne* sandsynligvis indikatorer for *vejrudsigter*, mens ord som *aktier* og *dollar* peger på *finansielle nyheder*.\n",
"\n",
"**Bag-of-words** (BoW) vektorrepræsentation er den mest simple og letforståelige traditionelle vektorrepræsentation. Hvert ord er knyttet til en vektorindeks, og et vektorelement indeholder antallet af forekomster af hvert ord i et givent dokument.\n",
"\n",
"![Billede, der viser, hvordan en bag-of-words vektorrepræsentation er repræsenteret i hukommelsen.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.da.png) \n",
"\n",
"> **Note**: Du kan også tænke på BoW som en sum af alle one-hot-enkodede vektorer for de enkelte ord i teksten.\n",
"\n",
"Nedenfor er et eksempel på, hvordan man genererer en bag-of-words repræsentation ved hjælp af Scikit Learn python-biblioteket:\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": [
"Vi kan også bruge Keras-vectorizeren, som vi definerede ovenfor, til at konvertere hvert ordnummer til en one-hot encoding og lægge alle disse vektorer sammen:\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": [
"> **Bemærk**: Du kan blive overrasket over, at resultatet adskiller sig fra det tidligere eksempel. Årsagen er, at i Keras-eksemplet svarer længden af vektoren til størrelsen på ordforrådet, som blev opbygget ud fra hele AG News-datasættet, mens vi i Scikit Learn-eksemplet opbyggede ordforrådet direkte ud fra prøve-teksten.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Træning af BoW-klassifikatoren\n",
"\n",
"Nu hvor vi har lært, hvordan man opbygger bag-of-words-repræsentationen af vores tekst, lad os træne en klassifikator, der bruger den. Først skal vi konvertere vores datasæt til en bag-of-words-repræsentation. Dette kan opnås ved at bruge `map`-funktionen på følgende måde:\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": [
"Lad os nu definere et simpelt klassifikations-neuralt netværk, der indeholder ét lineært lag. Inputstørrelsen er `vocab_size`, og outputstørrelsen svarer til antallet af klasser (4). Fordi vi løser en klassifikationsopgave, er den endelige aktiveringsfunktion **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": [
"Da vi har 4 klasser, er en nøjagtighed på over 80% et godt resultat.\n",
"\n",
"## Træning af en klassifikator som ét netværk\n",
"\n",
"Fordi vectorizeren også er et Keras-lag, kan vi definere et netværk, der inkluderer det, og træne det fra ende til anden. På denne måde behøver vi ikke at vectorisere datasættet ved hjælp af `map`, vi kan blot give det originale datasæt som input til netværket.\n",
"\n",
"> **Note**: Vi skal stadig anvende maps på vores datasæt for at konvertere felter fra ordbøger (såsom `title`, `description` og `label`) til tuples. Men når vi indlæser data fra disk, kan vi fra starten opbygge et datasæt med den nødvendige struktur.\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": [
"## Bigrammer, trigrammer og n-grammer\n",
"\n",
"En begrænsning ved bag-of-words-metoden er, at nogle ord indgår i flertalsudtryk, for eksempel har ordet 'hot dog' en helt anden betydning end ordene 'hot' og 'dog' i andre sammenhænge. Hvis vi altid repræsenterer ordene 'hot' og 'dog' med de samme vektorer, kan det forvirre vores model.\n",
"\n",
"For at løse dette bruges **n-gram repræsentationer** ofte i metoder til dokumentklassifikation, hvor frekvensen af hvert ord, to-ords eller tre-ords udtryk er en nyttig funktion til at træne klassifikatorer. I bigram-repræsentationer, for eksempel, tilføjer vi alle ordpar til ordforrådet, ud over de oprindelige ord.\n",
"\n",
"Nedenfor er et eksempel på, hvordan man genererer en bigram bag-of-words repræsentation ved hjælp af 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": [
"Den største ulempe ved n-gram-metoden er, at ordforrådets størrelse begynder at vokse ekstremt hurtigt. I praksis er vi nødt til at kombinere n-gram-repræsentationen med en dimensionalitetsreduktionsteknik, såsom *embeddings*, som vi vil diskutere i næste enhed.\n",
"\n",
"For at bruge en n-gram-repræsentation i vores **AG News**-datasæt, skal vi angive `ngrams`-parameteren til vores `TextVectorization`-konstruktør. Længden af et bigram-ordforråd er **betydeligt større**, i vores tilfælde er det mere end 1,3 millioner tokens! Derfor giver det mening også at begrænse bigram-tokens til et rimeligt antal.\n",
"\n",
"Vi kunne bruge den samme kode som ovenfor til at træne klassifikatoren, men det ville være meget hukommelsesineffektivt. I næste enhed vil vi træne bigram-klassifikatoren ved hjælp af embeddings. I mellemtiden kan du eksperimentere med træning af bigram-klassifikatoren i denne notebook og se, om du kan opnå højere nøjagtighed.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Automatisk beregning af BoW-vektorer\n",
"\n",
"I eksemplet ovenfor beregnede vi BoW-vektorer manuelt ved at summere one-hot-enkodningerne af individuelle ord. Den nyeste version af TensorFlow giver os dog mulighed for automatisk at beregne BoW-vektorer ved at angive parameteren `output_mode='count` til vektoriseringens konstruktør. Dette gør det betydeligt lettere at definere og træne vores model:\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": [
"## Termfrekvens - invers dokumentfrekvens (TF-IDF)\n",
"\n",
"I BoW-repræsentationen vægtes ordforekomster ved hjælp af den samme teknik, uanset hvilket ord der er tale om. Det er dog tydeligt, at hyppige ord som *en* og *i* er langt mindre vigtige for klassifikation end specialiserede termer. I de fleste NLP-opgaver er nogle ord mere relevante end andre.\n",
"\n",
"**TF-IDF** står for **termfrekvens - invers dokumentfrekvens**. Det er en variation af bag-of-words, hvor man i stedet for en binær 0/1-værdi, der angiver, om et ord optræder i et dokument, bruger en flydende værdi, som relaterer sig til hyppigheden af ordets forekomst i korpuset.\n",
"\n",
"Mere formelt defineres vægten $w_{ij}$ af et ord $i$ i dokumentet $j$ som:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"hvor\n",
"* $tf_{ij}$ er antallet af forekomster af $i$ i $j$, dvs. den BoW-værdi, vi har set tidligere\n",
"* $N$ er antallet af dokumenter i samlingen\n",
"* $df_i$ er antallet af dokumenter, der indeholder ordet $i$ i hele samlingen\n",
"\n",
"TF-IDF-værdien $w_{ij}$ stiger proportionalt med, hvor mange gange et ord optræder i et dokument, og justeres i forhold til antallet af dokumenter i korpuset, der indeholder ordet. Dette hjælper med at tage højde for, at nogle ord optræder hyppigere end andre. For eksempel, hvis ordet optræder i *alle* dokumenter i samlingen, er $df_i=N$, og $w_{ij}=0$, og disse termer vil blive fuldstændigt ignoreret.\n",
"\n",
"Du kan nemt oprette TF-IDF-vektorisering af tekst ved hjælp af 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": [
"I Keras kan `TextVectorization`-laget automatisk beregne TF-IDF-frekvenser ved at angive parameteren `output_mode='tf-idf'`. Lad os gentage koden, vi brugte ovenfor, for at se, om brug af TF-IDF øger nøjagtigheden:\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": [
"## Konklusion\n",
"\n",
"Selvom TF-IDF-repræsentationer giver vægt til forskellige ord baseret på deres hyppighed, er de ikke i stand til at repræsentere betydning eller rækkefølge. Som den berømte lingvist J. R. Firth sagde i 1935: \"Den fulde betydning af et ord er altid kontekstuel, og ingen undersøgelse af betydning uden for konteksten kan tages seriøst.\" Senere i kurset vil vi lære, hvordan man fanger kontekstuel information fra tekst ved hjælp af sprogmodellering.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Ansvarsfraskrivelse**: \nDette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på at sikre nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os ikke ansvar for eventuelle misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.\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-28T17:56:14+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "da"
}
},
"nbformat": 4,
"nbformat_minor": 4
}