647 lines
25 KiB
Plaintext
647 lines
25 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Tekstiklassifikatsiooni ülesanne\n",
|
|
"\n",
|
|
"Selles moodulis alustame lihtsa tekstiklassifikatsiooni ülesandega, mis põhineb **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** andmestikul: klassifitseerime uudiste pealkirjad ühte neljast kategooriast: Maailm, Sport, Äri ja Teadus/Tehnoloogia.\n",
|
|
"\n",
|
|
"## Andmestik\n",
|
|
"\n",
|
|
"Andmestiku laadimiseks kasutame **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** API-d.\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": [
|
|
"Nüüd saame kasutada andmekogumi treening- ja testimisosi, kasutades vastavalt `dataset['train']` ja `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": [
|
|
"Prindime välja meie andmekogumi esimesed 10 uut pealkirja:\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": [
|
|
"## Teksti vektorisatsioon\n",
|
|
"\n",
|
|
"Nüüd peame teksti teisendama **numbriteks**, mida saab esitada tensoritena. Kui soovime sõnatasemel esindust, peame tegema kahte asja:\n",
|
|
"\n",
|
|
"* Kasutama **tokeniseerijat**, et jagada tekst **tokeniteks**.\n",
|
|
"* Looma nende tokenite **sõnavara**.\n",
|
|
"\n",
|
|
"### Sõnavara suuruse piiramine\n",
|
|
"\n",
|
|
"AG News andmestiku näites on sõnavara suurus üsna suur, üle 100 000 sõna. Üldiselt ei vaja me sõnu, mis esinevad tekstis harva — ainult mõnes lauses on need olemas, ja mudel ei õpi neist midagi. Seega on mõistlik piirata sõnavara suurust väiksemaks, andes vektorisatsiooni konstruktorile vastava argumendi:\n",
|
|
"\n",
|
|
"Mõlemad sammud saab teostada **TextVectorization** kihiga. Loome vektorisatsiooni objekti ja kutsume seejärel `adapt` meetodi, et läbida kogu tekst ja koostada sõnavara:\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": [
|
|
"> **Märkus**: Kasutame ainult osa kogu andmestikust, et koostada sõnavara. Teeme seda, et kiirendada täitmise aega ja vältida teie ootamist. Siiski võtame riski, et mõned sõnad kogu andmestikust ei pruugi sõnavarasse jõuda ja jäävad treeningu ajal tähelepanuta. Seega, kasutades kogu sõnavara suurust ja läbides kogu andmestiku `adapt` käigus, peaks lõplik täpsus suurenema, kuid mitte märkimisväärselt.\n",
|
|
"\n",
|
|
"Nüüd saame ligi tegelikule sõnavarale:\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": [
|
|
"Vektorisaatori abil saame hõlpsasti kodeerida mis tahes teksti numbrite kogumiks:\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": [
|
|
"## Sõnade kogumi tekstiesitus\n",
|
|
"\n",
|
|
"Kuna sõnad kannavad tähendust, saame mõnikord teksti tähenduse välja selgitada, vaadates ainult üksikuid sõnu, sõltumata nende järjekorrast lauses. Näiteks uudiste klassifitseerimisel viitavad sõnad nagu *ilm* ja *lumi* tõenäoliselt *ilmaennustusele*, samas kui sõnad nagu *aktsiad* ja *dollar* viitavad *finantsuudistele*.\n",
|
|
"\n",
|
|
"**Sõnade kogumi** (BoW) vektor-esitus on kõige lihtsamini mõistetav traditsiooniline vektor-esitus. Iga sõna on seotud vektori indeksiga ning vektori element sisaldab iga sõna esinemiskordade arvu antud dokumendis.\n",
|
|
"\n",
|
|
" \n",
|
|
"\n",
|
|
"> **Note**: BoW-d võib mõelda ka kui kõigi teksti üksiksõnade ühekuumkooditud vektorite summana.\n",
|
|
"\n",
|
|
"Allpool on näide, kuidas luua sõnade kogumi esitust, kasutades Scikit Learn Python'i teeki:\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": [
|
|
"Me saame kasutada ka ülal määratletud Kerase vektoreerijat, teisendades iga sõna numbri ühekuumkoodiks ja liites kõik need vektorid kokku:\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": [
|
|
"> **Märkus**: Võib-olla üllatab teid, et tulemus erineb eelmisest näitest. Põhjus on selles, et Keras näites vastab vektori pikkus sõnavara suurusele, mis loodi kogu AG News andmestiku põhjal, samas kui Scikit Learn näites ehitasime sõnavara jooksvalt näidisteksti põhjal.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## BoW klassifikaatori treenimine\n",
|
|
"\n",
|
|
"Nüüd, kui oleme õppinud, kuidas luua tekstist sõnapilve (bag-of-words) esitus, treenime klassifikaatorit, mis seda kasutab. Kõigepealt peame oma andmestiku teisendama sõnapilve esituseks. Seda saab teha, kasutades `map` funktsiooni järgmiselt:\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": [
|
|
"Nüüd defineerime lihtsa klassifitseeriva närvivõrgu, mis sisaldab ühte lineaarset kihti. Sisendi suurus on `vocab_size` ja väljundi suurus vastab klasside arvule (4). Kuna lahendame klassifitseerimisülesannet, on lõplik aktivatsioonifunktsioon **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": [
|
|
"Kuna meil on 4 klassi, siis üle 80% täpsus on hea tulemus.\n",
|
|
"\n",
|
|
"## Klassifikaatori treenimine ühe võrguna\n",
|
|
"\n",
|
|
"Kuna vektoriseerija on samuti Keras kiht, saame defineerida võrgu, mis sisaldab seda, ja treenida seda algusest lõpuni. Sel viisil ei pea me andmekogumit vektoriseerima, kasutades `map`, vaid saame lihtsalt edastada algse andmekogumi võrgu sisendisse.\n",
|
|
"\n",
|
|
"> **Note**: Me peame siiski rakendama kaarte oma andmekogumile, et teisendada väljad sõnastikest (nagu `title`, `description` ja `label`) tuplideks. Kuid andmeid kettalt laadides saame kohe alguses luua andmekogumi vajaliku struktuuriga.\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": [
|
|
"## Bigrammid, trigrammid ja n-grammid\n",
|
|
"\n",
|
|
"Üks sõnakoti lähenemise piirangutest on see, et mõned sõnad on osa mitmesõnalistest väljenditest. Näiteks sõnal 'hot dog' on täiesti erinev tähendus võrreldes sõnadega 'hot' ja 'dog' teistes kontekstides. Kui me esindame sõnu 'hot' ja 'dog' alati samade vektoritega, võib see meie mudelit segadusse ajada.\n",
|
|
"\n",
|
|
"Selle probleemi lahendamiseks kasutatakse sageli **n-grammide esitusviise** dokumentide klassifitseerimise meetodites, kus iga sõna, kahe- või kolmesõnalise kombinatsiooni sagedus on kasulik tunnus klassifikaatorite treenimiseks. Näiteks bigrammide esituses lisame sõnavarasse kõik sõnapaarid lisaks algsetele sõnadele.\n",
|
|
"\n",
|
|
"Allpool on näide, kuidas luua bigrammide sõnakoti esitus Scikit Learn'i abil:\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-grammi lähenemise peamine puudus on see, et sõnavara suurus hakkab väga kiiresti kasvama. Praktikas peame kombineerima n-grammi esitusviisi dimensioonide vähendamise tehnikaga, näiteks *embeddings*, mida arutame järgmises osas.\n",
|
|
"\n",
|
|
"Et kasutada n-grammi esitusviisi meie **AG News** andmestikus, peame `ngrams` parameetri edastama `TextVectorization` konstruktorile. Bigrammi sõnavara pikkus on **märkimisväärselt suurem**, meie puhul üle 1,3 miljoni tokeni! Seetõttu on mõistlik piirata ka bigrammi tokenite arvu mõistliku piirini.\n",
|
|
"\n",
|
|
"Me võiksime kasutada sama koodi, mis ülalpool, et treenida klassifikaatorit, kuid see oleks väga mälumahukas. Järgmises osas treenime bigrammi klassifikaatorit kasutades embeddings. Seniks saate katsetada bigrammi klassifikaatori treenimist selles märkmikus ja vaadata, kas suudate saavutada kõrgema täpsuse.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## BoW vektorite automaatne arvutamine\n",
|
|
"\n",
|
|
"Eelmises näites arvutasime BoW vektorid käsitsi, summeerides üksiksõnade ühekuumkoodid. Kuid TensorFlow uusim versioon võimaldab meil BoW vektoreid automaatselt arvutada, kui edastame vektoriseerija konstruktorile parameetri `output_mode='count'`. See muudab meie mudeli defineerimise ja treenimise märkimisväärselt lihtsamaks:\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": [
|
|
"## Termi sagedus - pöörddokumendi sagedus (TF-IDF)\n",
|
|
"\n",
|
|
"BoW-esituses kaalutakse sõnade esinemist sama tehnikaga, sõltumata sõnast endast. Siiski on selge, et sagedased sõnad nagu *a* ja *in* on klassifitseerimise jaoks palju vähem olulised kui spetsialiseeritud terminid. Enamikus NLP-ülesannetes on mõned sõnad olulisemad kui teised.\n",
|
|
"\n",
|
|
"**TF-IDF** tähistab **termi sagedust - pöörddokumendi sagedust**. See on bag-of-words'i variatsioon, kus binaarse 0/1 väärtuse asemel, mis näitab sõna esinemist dokumendis, kasutatakse ujuvpunkti väärtust, mis on seotud sõna esinemissagedusega korpuses.\n",
|
|
"\n",
|
|
"Formaalsemalt on sõna $i$ kaal dokumendis $j$, tähistatud kui $w_{ij}$, defineeritud järgmiselt:\n",
|
|
"$$\n",
|
|
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
|
|
"$$\n",
|
|
"kus\n",
|
|
"* $tf_{ij}$ on sõna $i$ esinemiste arv dokumendis $j$, st BoW väärtus, mida oleme varem näinud\n",
|
|
"* $N$ on dokumentide arv kogumis\n",
|
|
"* $df_i$ on dokumentide arv, mis sisaldavad sõna $i$ kogu kogumis\n",
|
|
"\n",
|
|
"TF-IDF väärtus $w_{ij}$ suureneb proportsionaalselt sõna esinemiste arvuga dokumendis ja seda korrigeeritakse dokumentide arvu järgi korpuses, mis sisaldavad seda sõna. See aitab arvestada asjaolu, et mõned sõnad esinevad sagedamini kui teised. Näiteks, kui sõna esineb *iga* dokumendis kogumis, siis $df_i=N$ ja $w_{ij}=0$, ning need terminid jäetakse täielikult kõrvale.\n",
|
|
"\n",
|
|
"TF-IDF vektorisatsiooni teksti jaoks saab hõlpsasti luua Scikit Learn'i abil:\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": [
|
|
"Kerases saab `TextVectorization` kiht automaatselt arvutada TF-IDF sagedusi, kui edastada parameeter `output_mode='tf-idf'`. Kordame ülaltoodud koodi, et näha, kas TF-IDF kasutamine suurendab täpsust:\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": [
|
|
"## Kokkuvõte\n",
|
|
"\n",
|
|
"Kuigi TF-IDF esitlused annavad erinevatele sõnadele sageduskaalud, ei suuda need esitada tähendust ega järjekorda. Nagu kuulus lingvist J. R. Firth ütles 1935. aastal: \"Sõna täielik tähendus on alati kontekstuaalne ja ükski tähenduse uurimine väljaspool konteksti ei saa olla tõsiseltvõetav.\" Kursuse käigus õpime hiljem, kuidas tekstist kontekstuaalset teavet keelemudelite abil haarata.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Lahtiütlus**: \nSee dokument on tõlgitud AI tõlketeenuse [Co-op Translator](https://github.com/Azure/co-op-translator) abil. Kuigi püüame tagada täpsust, palume arvestada, et automaatsed tõlked võivad sisaldada vigu või ebatäpsusi. Algne dokument selle algses keeles tuleks pidada autoriteetseks allikaks. Olulise teabe puhul soovitame kasutada professionaalset inimtõlget. Me ei vastuta selle tõlke kasutamisest tulenevate arusaamatuste või valesti tõlgenduste eest.\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-10-11T12:45:28+00:00",
|
|
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
|
|
"language_code": "et"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |