647 lines
27 KiB
Plaintext
647 lines
27 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Gawain sa Pag-uuri ng Teksto\n",
|
|
"\n",
|
|
"Sa modyul na ito, magsisimula tayo sa isang simpleng gawain sa pag-uuri ng teksto gamit ang **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** dataset: iuuri natin ang mga headline ng balita sa isa sa 4 na kategorya: World, Sports, Business, at Sci/Tech.\n",
|
|
"\n",
|
|
"## Ang Dataset\n",
|
|
"\n",
|
|
"Upang mai-load ang dataset, gagamitin natin ang **[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": [
|
|
"Maaari na nating ma-access ang mga bahagi ng pagsasanay at pagsusulit ng dataset gamit ang `dataset['train']` at `dataset['test']` ayon sa pagkakabanggit:\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": [
|
|
"I-print natin ang unang 10 bagong headline mula sa ating dataset:\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": [
|
|
"## Pagvektor ng Teksto\n",
|
|
"\n",
|
|
"Ngayon, kailangan nating i-convert ang teksto sa **mga numero** na maaaring i-represent bilang tensors. Kung gusto natin ng word-level na representasyon, kailangan nating gawin ang dalawang bagay:\n",
|
|
"\n",
|
|
"* Gumamit ng **tokenizer** para hatiin ang teksto sa **mga token**.\n",
|
|
"* Bumuo ng **bokabularyo** ng mga token na iyon.\n",
|
|
"\n",
|
|
"### Paglilimita sa laki ng bokabularyo\n",
|
|
"\n",
|
|
"Sa halimbawa ng AG News dataset, ang laki ng bokabularyo ay medyo malaki, higit sa 100k na salita. Sa pangkalahatan, hindi natin kailangan ang mga salitang bihirang lumabas sa teksto — iilan lamang na mga pangungusap ang magkakaroon nito, at hindi matututo ang modelo mula sa mga ito. Kaya't may saysay na limitahan ang laki ng bokabularyo sa mas maliit na bilang sa pamamagitan ng pagpasa ng argumento sa vectorizer constructor:\n",
|
|
"\n",
|
|
"Ang parehong mga hakbang na iyon ay maaaring gawin gamit ang **TextVectorization** layer. I-instantiate natin ang vectorizer object, at pagkatapos ay tawagin ang `adapt` method upang dumaan sa lahat ng teksto at bumuo ng bokabularyo:\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": [
|
|
"> **Tandaan** na gumagamit lamang tayo ng bahagi ng buong dataset upang makabuo ng bokabularyo. Ginagawa natin ito upang mapabilis ang oras ng pagproseso at hindi ka maghintay nang matagal. Gayunpaman, may panganib na ang ilang mga salita mula sa buong dataset ay hindi maisasama sa bokabularyo at hindi mapapansin sa panahon ng pagsasanay. Kaya, ang paggamit ng buong laki ng bokabularyo at pagtakbo sa buong dataset habang ginagawa ang `adapt` ay maaaring magpataas ng huling katumpakan, ngunit hindi nang malaki.\n",
|
|
"\n",
|
|
"Ngayon, maaari na nating ma-access ang aktwal na bokabularyo:\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": [
|
|
"Gamit ang vectorizer, madali nating ma-encode ang anumang teksto sa isang set ng mga numero:\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": [
|
|
"## Representasyon ng Teksto gamit ang Bag-of-words\n",
|
|
"\n",
|
|
"Dahil ang mga salita ay may kahulugan, minsan maaari nating maunawaan ang ibig sabihin ng isang piraso ng teksto sa pamamagitan lamang ng pagtingin sa mga indibidwal na salita, kahit na hindi natin isaalang-alang ang pagkakasunod-sunod nito sa pangungusap. Halimbawa, kapag nag-uuri ng balita, ang mga salitang tulad ng *weather* at *snow* ay malamang na tumutukoy sa *weather forecast*, habang ang mga salitang tulad ng *stocks* at *dollar* ay maaaring magpahiwatig ng *financial news*.\n",
|
|
"\n",
|
|
"Ang **Bag-of-words** (BoW) na representasyon ng vector ay ang pinakasimple at madaling maunawaan na tradisyunal na representasyon ng vector. Ang bawat salita ay nauugnay sa isang index ng vector, at ang isang elemento ng vector ay naglalaman ng bilang ng paglitaw ng bawat salita sa isang partikular na dokumento.\n",
|
|
"\n",
|
|
" \n",
|
|
"\n",
|
|
"> **Note**: Maaari mo ring isipin ang BoW bilang kabuuan ng lahat ng one-hot-encoded vectors para sa bawat indibidwal na salita sa teksto.\n",
|
|
"\n",
|
|
"Narito ang isang halimbawa kung paano bumuo ng representasyon ng bag-of-words gamit ang Scikit Learn python library:\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": [
|
|
"Maaari rin nating gamitin ang Keras vectorizer na ating tinukoy sa itaas, na nagko-convert ng bawat numero ng salita sa isang one-hot encoding at pinagsasama-sama ang lahat ng mga vector na iyon:\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": [
|
|
"> **Tandaan**: Maaaring magulat ka na ang resulta ay naiiba mula sa naunang halimbawa. Ang dahilan nito ay sa halimbawa ng Keras, ang haba ng vector ay tumutugma sa laki ng bokabularyo, na binuo mula sa buong AG News dataset, samantalang sa halimbawa ng Scikit Learn, binuo natin ang bokabularyo mula sa sample na teksto nang direkta.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Pagsasanay sa BoW classifier\n",
|
|
"\n",
|
|
"Ngayon na natutunan na natin kung paano bumuo ng bag-of-words na representasyon ng ating teksto, magtuturo tayo ng isang classifier na gagamit nito. Una, kailangan nating i-convert ang ating dataset sa isang bag-of-words na representasyon. Magagawa ito gamit ang `map` function sa ganitong paraan:\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": [
|
|
"Ngayon, mag-define tayo ng isang simpleng classifier neural network na naglalaman ng isang linear layer. Ang input size ay `vocab_size`, at ang output size ay tumutukoy sa bilang ng mga klase (4). Dahil nagso-solve tayo ng isang classification task, ang huling activation function ay **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": [
|
|
"Dahil mayroon tayong 4 na klase, ang isang accuracy na higit sa 80% ay maituturing na magandang resulta.\n",
|
|
"\n",
|
|
"## Pagsasanay ng isang classifier bilang isang network\n",
|
|
"\n",
|
|
"Dahil ang vectorizer ay isa ring Keras layer, maaari tayong magtukoy ng isang network na kasama ito, at sanayin ito mula simula hanggang dulo. Sa ganitong paraan, hindi na natin kailangang i-vectorize ang dataset gamit ang `map`, maaari na lang nating ipasa ang orihinal na dataset sa input ng network.\n",
|
|
"\n",
|
|
"> **Note**: Kakailanganin pa rin nating mag-apply ng mga mapa sa ating dataset upang i-convert ang mga field mula sa mga diksyunaryo (tulad ng `title`, `description`, at `label`) patungo sa mga tuple. Gayunpaman, kapag naglo-load ng data mula sa disk, maaari tayong bumuo ng isang dataset na may kinakailangang istruktura mula sa simula.\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": [
|
|
"## Bigrams, trigrams at n-grams\n",
|
|
"\n",
|
|
"Isang limitasyon ng bag-of-words na pamamaraan ay ang ilang mga salita ay bahagi ng mga multi-word na ekspresyon. Halimbawa, ang salitang 'hot dog' ay may ganap na ibang kahulugan kumpara sa mga salitang 'hot' at 'dog' sa ibang mga konteksto. Kung palaging ire-represent natin ang mga salitang 'hot' at 'dog' gamit ang parehong mga vector, maaaring malito ang ating modelo.\n",
|
|
"\n",
|
|
"Upang tugunan ito, madalas na ginagamit ang **n-gram representations** sa mga pamamaraan ng klasipikasyon ng dokumento, kung saan ang dalas ng bawat salita, bi-word, o tri-word ay isang mahalagang tampok para sa pagsasanay ng mga classifier. Sa bigram representations, halimbawa, idinadagdag natin ang lahat ng pares ng salita sa bokabularyo, bukod pa sa mga orihinal na salita.\n",
|
|
"\n",
|
|
"Narito ang isang halimbawa kung paano bumuo ng bigram bag of word representation gamit ang 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": [
|
|
"Ang pangunahing kahinaan ng n-gram na pamamaraan ay ang mabilis na paglaki ng laki ng bokabularyo. Sa praktika, kailangan nating pagsamahin ang n-gram na representasyon sa isang teknik para sa pagbawas ng dimensyon, tulad ng *embeddings*, na tatalakayin natin sa susunod na yunit.\n",
|
|
"\n",
|
|
"Upang magamit ang n-gram na representasyon sa ating **AG News** dataset, kailangan nating ipasa ang `ngrams` na parameter sa ating `TextVectorization` constructor. Ang haba ng bigram na bokabularyo ay **mas malaki nang husto**, sa ating kaso, ito ay higit sa 1.3 milyong mga token! Kaya't makatuwiran na limitahan din ang bigram na mga token sa isang makatwirang bilang.\n",
|
|
"\n",
|
|
"Maaari nating gamitin ang parehong code tulad ng nasa itaas upang sanayin ang classifier, subalit, ito ay magiging napaka-inepisyente sa memorya. Sa susunod na yunit, sasanayin natin ang bigram classifier gamit ang embeddings. Sa ngayon, maaari kang mag-eksperimento sa pagsasanay ng bigram classifier sa notebook na ito at tingnan kung makakakuha ka ng mas mataas na accuracy.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Awtomatikong Pagkalkula ng BoW Vectors\n",
|
|
"\n",
|
|
"Sa halimbawa sa itaas, manu-mano nating kinalkula ang BoW vectors sa pamamagitan ng pagdaragdag ng one-hot encodings ng bawat salita. Gayunpaman, pinapayagan na tayo ng pinakabagong bersyon ng TensorFlow na awtomatikong kalkulahin ang BoW vectors sa pamamagitan ng pagpasa ng `output_mode='count` na parameter sa vectorizer constructor. Ginagawa nitong mas madali ang pagdedepina at pagsasanay ng ating modelo:\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": [
|
|
"## Term frequency - inverse document frequency (TF-IDF)\n",
|
|
"\n",
|
|
"Sa BoW na representasyon, ang pagbilang ng mga salita ay may parehong timbang anuman ang salita. Gayunpaman, malinaw na ang mga madalas na salita tulad ng *a* at *in* ay mas hindi mahalaga para sa klasipikasyon kumpara sa mga espesyalisadong termino. Sa karamihan ng mga gawain sa NLP, may mga salita na mas may kaugnayan kaysa sa iba.\n",
|
|
"\n",
|
|
"Ang **TF-IDF** ay nangangahulugang **term frequency - inverse document frequency**. Isa itong bersyon ng bag-of-words, kung saan sa halip na binary na 0/1 na halaga na nagpapakita ng paglitaw ng isang salita sa isang dokumento, isang floating-point na halaga ang ginagamit, na may kaugnayan sa dalas ng paglitaw ng salita sa corpus.\n",
|
|
"\n",
|
|
"Mas pormal, ang timbang $w_{ij}$ ng isang salita $i$ sa dokumento $j$ ay tinutukoy bilang:\n",
|
|
"$$\n",
|
|
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
|
|
"$$\n",
|
|
"kung saan\n",
|
|
"* $tf_{ij}$ ay ang bilang ng paglitaw ng $i$ sa $j$, ibig sabihin, ang BoW na halaga na nakita natin dati\n",
|
|
"* $N$ ay ang bilang ng mga dokumento sa koleksyon\n",
|
|
"* $df_i$ ay ang bilang ng mga dokumento na naglalaman ng salita $i$ sa buong koleksyon\n",
|
|
"\n",
|
|
"Ang halaga ng TF-IDF na $w_{ij}$ ay tumataas nang proporsyonal sa bilang ng beses na lumitaw ang isang salita sa isang dokumento at nababawasan batay sa bilang ng mga dokumento sa corpus na naglalaman ng salita, na tumutulong upang maiakma ang katotohanan na may mga salitang mas madalas lumitaw kaysa sa iba. Halimbawa, kung ang salita ay lumitaw sa *bawat* dokumento sa koleksyon, $df_i=N$, at $w_{ij}=0$, at ang mga terminong iyon ay ganap na hindi papansinin.\n",
|
|
"\n",
|
|
"Madali kang makakagawa ng TF-IDF vectorization ng teksto gamit ang 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": [
|
|
"Sa Keras, ang `TextVectorization` layer ay maaaring awtomatikong mag-compute ng TF-IDF frequencies sa pamamagitan ng pagpasa ng `output_mode='tf-idf'` na parameter. Ulitin natin ang code na ginamit natin sa itaas upang makita kung ang paggamit ng TF-IDF ay nagpapataas ng accuracy:\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": [
|
|
"## Konklusyon\n",
|
|
"\n",
|
|
"Kahit na nagbibigay ang mga representasyon ng TF-IDF ng mga timbang sa dalas ng iba't ibang salita, hindi nito kayang ipakita ang kahulugan o pagkakasunod-sunod. Tulad ng sinabi ng kilalang lingguwistang si J. R. Firth noong 1935, \"Ang kumpletong kahulugan ng isang salita ay palaging nakabatay sa konteksto, at walang pag-aaral ng kahulugan na hiwalay sa konteksto ang maaaring ituring na seryoso.\" Matututuhan natin kung paano makuha ang impormasyong nakabatay sa konteksto mula sa teksto gamit ang language modeling sa mga susunod na bahagi ng kurso.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Paunawa**: \nAng dokumentong ito ay isinalin gamit ang AI translation service na [Co-op Translator](https://github.com/Azure/co-op-translator). Bagama't sinisikap naming maging tumpak, tandaan na ang mga awtomatikong pagsasalin ay maaaring maglaman ng mga pagkakamali o hindi pagkakatugma. Ang orihinal na dokumento sa kanyang katutubong wika ang dapat ituring na opisyal na sanggunian. Para sa mahalagang impormasyon, inirerekomenda ang propesyonal na pagsasalin ng tao. Hindi kami mananagot sa anumang hindi pagkakaunawaan o maling interpretasyon na maaaring magmula sa paggamit ng pagsasaling ito.\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-28T04:40:19+00:00",
|
|
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
|
|
"language_code": "tl"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |