641 lines
39 KiB
Plaintext
641 lines
39 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# ការប្រភេទអត្ថបទ\n",
|
||
"\n",
|
||
"ក្នុងមូឌុលនេះ យើងនឹងចាប់ផ្តើមជាមួយការងារប្រភេទអត្ថបទរឹងមួយដែលមានមូលដ្ឋានលើ **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)**៖ យើងនឹងចាត់ថ្នាក់ចំណងជើងព័ត៌មានទៅក្នុងប្រភេទមួយពីចំនួន ៤ គឺពិភពលោក កីឡា អាជីវកម្ម និង វិទ្យាសាស្ត្រ/បច្ចេកវិទ្យា។\n",
|
||
"\n",
|
||
"## សំណុំទិន្នន័យ\n",
|
||
"\n",
|
||
"ដើម្បីផ្ទុកសំណុំទិន្នន័យ យើងនឹងប្រើអ្នកប្រើប្រាស់ 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": [
|
||
"ឥឡូវនេះយើងអាចចូលប្រើផ្នែកបណ្តុះបណ្តាល និងផ្នែកសាកល្បងនៃសំណុំទិន្នន័យដោយប្រើ `dataset['train']` និង `dataset['test']` តាមលំដាប់៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Length of train dataset = 120000\n",
|
||
"Length of test dataset = 7600\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"ds_train = dataset['train']\n",
|
||
"ds_test = dataset['test']\n",
|
||
"\n",
|
||
"print(f\"Length of train dataset = {len(ds_train)}\")\n",
|
||
"print(f\"Length of test dataset = {len(ds_test)}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"មកបោះពុម្ពចេញចំណងជើងព័ត៌មានថ្មី ១០ ប្រធានចុងពីកំណត់ត្រារបស់យើង៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"3 (Sci/Tech) -> b'AMD Debuts Dual-Core Opteron Processor' b'AMD #39;s new dual-core Opteron chip is designed mainly for corporate computing applications, including databases, Web services, and financial transactions.'\n",
|
||
"1 (Sports) -> b\"Wood's Suspension Upheld (Reuters)\" b'Reuters - Major League Baseball\\\\Monday announced a decision on the appeal filed by Chicago Cubs\\\\pitcher Kerry Wood regarding a suspension stemming from an\\\\incident earlier this season.'\n",
|
||
"2 (Business) -> b'Bush reform may have blue states seeing red' b'President Bush #39;s quot;revenue-neutral quot; tax reform needs losers to balance its winners, and people claiming the federal deduction for state and local taxes may be in administration planners #39; sights, news reports say.'\n",
|
||
"3 (Sci/Tech) -> b\"'Halt science decline in schools'\" b'Britain will run out of leading scientists unless science education is improved, says Professor Colin Pillinger.'\n",
|
||
"1 (Sports) -> b'Gerrard leaves practice' b'London, England (Sports Network) - England midfielder Steven Gerrard injured his groin late in Thursday #39;s training session, but is hopeful he will be ready for Saturday #39;s World Cup qualifier against Austria.'\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
|
||
"\n",
|
||
"for i,x in zip(range(5),ds_train):\n",
|
||
" print(f\"{x['label']} ({classes[x['label']]}) -> {x['title']} {x['description']}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## ការបម្លែងអត្ថបទទៅជាវិចទ័រ\n",
|
||
"\n",
|
||
"ឥឡូវនេះយើងត្រូវការបម្លែងអត្ថបទទៅជា **លេខ** ដែលអាចត្រូវបានតំណាងជាទង់សរ។ ប្រសិនបើយើងចង់បានការតំណាងនៅកម្រិតពាក្យ យើងត្រូវធ្វើរឿងពីរនេះ៖\n",
|
||
"\n",
|
||
"* ប្រើ **tokenizer** ដើម្បីបំបែកអត្ថបទទៅជាផ្នែកតូចៗ (**tokens**)។\n",
|
||
"* បង្កើត **វ៉ុកាប្យូលារី** សម្រាប់ those tokens នោះ។\n",
|
||
"\n",
|
||
"### ការកំណត់ទំហំវ៉ុកាប្យូលារី\n",
|
||
"\n",
|
||
"ក្នុងឧទាហរណ៍ឯកទេស AG News ទំហំវ៉ុកាប្យូលារីគឺធំបំផុត មានពាក្យលើស ១០០,០០០។ ជាទូទៅ យើងមិនត្រូវការពាក្យដែលមានកម្រិតតិចក្នុងអត្ថបទទេ — មានតែប៉ុន្មានវាក្សត្រដែលមានពាក្យនោះ ហើយម៉ូដែលនឹងមិនបានរៀនពីពួកវា។ ដូច្នេះ វាមានន័យថាត្រូវកំណត់ទំហំវ៉ុកាប្យូលារីឲ្យតូចជាងនេះដោយផ្ញើអាគឺម៉ង់ទៅកាន់អ្នកបង្កើត vectorizer:\n",
|
||
"\n",
|
||
"ការដំណើរការទាំងពីរនេះអាចត្រូវបានគ្រប់គ្រងដោយការស្រទាប់ **TextVectorization** ។ យើងមកបង្កើតវត្ថុ vectorizer ហើយបន្ទាប់មកហៅវិធី `adapt` ដើម្បីឆែកអត្ថបទទាំងអស់និងបង្កើតវ៉ុកាប្យូលារី។\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"vocab_size = 50000\n",
|
||
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size)\n",
|
||
"vectorizer.adapt(ds_train.take(500).map(lambda x: x['title']+' '+x['description']))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"> **ចំណាំ** ថាយើងកំពុងប្រើតែផ្នែកមួយនៃសំណុំទិន្នន័យទាំងមូល ដើម្បីបង្កើតពាក្យសម្បត្តិ បង្រៀនគ្នាដើម្បីចំណាយពេលតែ្វរហ័ស និងមិនបាច់ឲ្យអ្នករង់ចាំ។ ទោះយ៉ាងណាយើងកំពុងរក្សាឲ្យមានហានិភ័យថាពាក្យខ្លះពីសំណុំទិន្នន័យទាំងមូលនោះ နឹងមិនមាននៅក្នុងពាក្យសម្បត្តិនោះទេ ហើយនឹងត្រូវមិនគិតទៅវិញពេលបណ្ដុះបណ្ដាល។ ដូច្នេះការប្រើកំណត់ទំហំពាក្យសម្បត្តិទាំងមូល និងរត់តាមសំណុំទិន្នន័យទាំងមូលនៅពេល `adapt` គួរតែបង្កើនភាពត្រឹមត្រូវចុងក្រោយ ប៉ុន្តែមិនបានធំទេ។\n",
|
||
"\n",
|
||
"ឥឡូវនេះយើងអាចចូលដំណើរការពាក្យសម្បត្តិពិតប្រាកដ:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"['', '[UNK]', 'the', 'to', 'a', 'in', 'of', 'and', 'on', 'for']\n",
|
||
"Length of vocabulary: 5335\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"vocab = vectorizer.get_vocabulary()\n",
|
||
"vocab_size = len(vocab)\n",
|
||
"print(vocab[:10])\n",
|
||
"print(f\"Length of vocabulary: {vocab_size}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"ដោយប្រើឧបករណ៍បំលែងវ៉ិចទ័រ យើងអាចបំលែងអត្ថបទណាមួយទៅជាសំណុំលេខបានយ៉ាងងាយស្រួល៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<tf.Tensor: shape=(7,), dtype=int64, numpy=array([ 112, 3695, 3, 304, 11, 1041, 1], dtype=int64)>"
|
||
]
|
||
},
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"vectorizer('I love to play with my words')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## ការទ្រទ្រង់អត្ថបទដោយក្មេងពាក្យ\n",
|
||
"\n",
|
||
"ដោយសារពាក្យតំណាងឲ្យអត្ថន័យ កម្រិតខ្លះ យើងអាចរកឃើញអត្ថន័យរបស់អត្ថបទមួយតាមរយៈការមើលរូបមន្តពាក្យផ្តាច់មុខដោយមិនគិតពីរបៀបរៀបចំរបស់ពួកវាក្នុងប្រយោគ។ ឧទាហរណ៍ ដោយពិចារណាក្រោមបែបបទផ្សាយព័ត៌មាន ពាក្យដូចជា *អាកាសធាតុ* និង *ព្រិល* មានន័យប្រាប់ពី *ការព្យាករណ៍អាកាសធាតុ* ខណៈពេលដែលពាក្យដូចជា *ភាគហ៊ុន* និង *ដុល្លារ* នឹងរាប់បញ្ចូលទៅក្នុង *ព័ត៌មានហិរញ្ញវត្ថុ* ។\n",
|
||
"\n",
|
||
"**ការទ្រទ្រង់ជាម៉ូដែល Bag-of-words** (BoW) គឺជាការទ្រទ្រង់វ៉ិចទ័រដែលងាយស្រួលយល់បំផុតតាមលំនាំប្រពៃណី។ ពាក្យនីមួយៗត្រូវបានភ្ជាប់ទៅនឹងការរូបរាងវ៉ិចទ័រមួយ ហើយធាតុវ៉ិចទ័រមួយមានចំនួនករណីនៃពាក្យនីមួយៗដែលមាននៅក្នុងឯកសារដែលបានផ្តល់។\n",
|
||
"\n",
|
||
" \n",
|
||
"\n",
|
||
"> **ចំណាំ**៖ អ្នកក៏អាចគិតថា BoW គឺជាការបូកគណនារូបរាងវ៉ិចទ័រ one-hot-encoded សម្រាប់ពាក្យនីមួយៗនៅក្នុងអត្ថបទ។\n",
|
||
"\n",
|
||
"ខាងក្រោមជាឧទាហរណ៍នៃរបៀបបង្កើតការទ្រទ្រង់ bag-of-words ដោយប្រើ thư viện 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": [
|
||
"យើងក៏អាចប្រើប្រាស់កម្មវិធី Keras vectorizer ដែលយើងបានកំណត់ខាងលើ ដើម្បីបម្លែងលេខពាក្យនីមួយៗទៅជាការអ៊ិនកូដមួយកំឡុង ហើយបូកវ៉ិចទ័រទាំងអស់នោះឡើង៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)"
|
||
]
|
||
},
|
||
"execution_count": 9,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"def to_bow(text):\n",
|
||
" return tf.reduce_sum(tf.one_hot(vectorizer(text),vocab_size),axis=0)\n",
|
||
"\n",
|
||
"to_bow('My dog likes hot dogs on a hot day.').numpy()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"> **ចំណាំ**: អ្នកប្រហែលជាភ្ញាក់ផ្អើលថាលទ្ធផលខុសពីឧទាហរណ៍មុននេះ។ ការរឿងនោះស្ថិតនៅក្នុងឧទាហរណ៍ Keras ដែលប្រវែងនៃវ៉ិកទ័រត្រូវបានផ្គូរផ្គងនឹងទំហំវចនានុក្រម ដែលបានសាងសង់ពីទិន្នន័យ AG News ទាំងមូល ខណៈនៅក្នុងឧទាហរណ៍ Scikit Learn យើងបានសង់វចនានុក្រមពីអត្ថបទគំរូក្នុងពេលជាក់លាក់។\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## បណ្តុះបណ្តាលអ្នកចាត់ថ្នាក់ BoW\n",
|
||
"\n",
|
||
"ឥឡូវនេះដែលយើងបានរៀនយ៉ាងដឹងថាតើធ្វើដូចម្តេចដើម្បីបង្កើតការបង្ហាញ bag-of-words សម្រាប់អត្ថបទរបស់យើងហើយ ចូរបណ្តុះបណ្តាលអ្នកចាត់ថ្នាក់មួយដែលប្រើវា។ ជាដំបូង យើងត្រូវបំលែង dataset របស់យើងទៅជាការបង្ហាញ bag-of-words។ វាអាចត្រូវបានសំរេចដោយប្រើមុខងារ `map` ដូចខាងក្រោម៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"batch_size = 128\n",
|
||
"\n",
|
||
"ds_train_bow = ds_train.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)\n",
|
||
"ds_test_bow = ds_test.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"ឥឡូវយើងខ្លាំងកំណត់បណ្តាញសរសៃប្រស фотографииាទម្នាក់ជាកម្មវិធីចំណាត់ថ្នាក់សាមញ្ញមួយដែលមានថាសភ្ជាប់តែមួយ។ ទំហំបញ្ចូលគឺ `vocab_size` ហើយទំហំបញ្ចូលតំណាងឲ្យចំនួនថ្នាក់ (4)។ ព្រោះយើងកំពុងដោះស្រាយបញ្ហាការចំណាត់ថ្នាក់, មុខងារជំរុញចុងក្រោយគឺ **softmax**៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c70a947f0>"
|
||
]
|
||
},
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"model = keras.models.Sequential([\n",
|
||
" keras.layers.Dense(4,activation='softmax',input_shape=(vocab_size,))\n",
|
||
"])\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train_bow,validation_data=ds_test_bow)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Since we have 4 classes, an accuracy of above 80% is a good result.\n",
|
||
"\n",
|
||
"## ការបណ្តុះបណ្តាលព្យាករណ៍ជាបណ្ដាញតែមួយ\n",
|
||
"\n",
|
||
"ដោយសារតែ vectorizer ក៏ជាស្រទាប់ Keras ផងដែរ យើងអាចកំណត់បណ្ដាញមួយដែលរួមមានវា ហើយបណ្តុះបណ្តាលវាជាចុងទៅចុង។ ដូច្នេះយើងមិនចាំបាច់ vectorize សំណុំទិន្នន័យដោយប្រើ `map` ទេ អ្នកអាចផ្តល់សំណុំទិន្នន័យដើមទៅការបញ្ចូលរបស់បណ្ដាញ។\n",
|
||
"\n",
|
||
"> **កំណត់ចំណាំ**: យើងនៅតែត្រូវតែប្រើម៉ាប់ទៅលើសំណុំទិន្នន័យរបស់យើងដើម្បីផ្លាស់ប្ដូរបន្ទះពីវចនានុក្រម (ដូចជា `title`, `description` និង `label`) ទៅជា tuples។ ទោះយ៉ាងណា នៅពេលផ្ទុកទិន្នន័យពីឌីស អាចកសាងសំណុំទិន្នន័យជាមួយរចនាសម្ព័ន្ធដែលត្រូវការជាលើកដំបូង។\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 និង n-grams\n",
|
||
"\n",
|
||
"កំណត់ហេតុមួយនៃវិធីសាស្រ្ត bag-of-words គឺពាក្យខ្លះជាផ្នែកនៃអត្ថបទពេញលេញមួយដែលមានពាក្យច្រើន, ឧទាហរណ៍, ពាក្យ 'hot dog' មានអត្ថន័យខុសគ្នាឥតខ្ចោះពីពាក្យ 'hot' និង 'dog' នៅក្នុងបរិបទផ្សេងទៀត។ ប្រសិនបើយើងបង្ហាញពាក្យ 'hot' និង 'dog' ជារូបមន្តដូចគ្នាទាំងអស់ វាអាចធ្វើឱ្យម៉ូដែលរបស់យើងបូកស្រួល។\n",
|
||
"\n",
|
||
"ដើម្បីដោះស្រាយបញ្ហានេះ, **ការតំណាងនៃ n-gram** ត្រូវបានប្រើយ៉ាងធម្មតា ក្នុងវិធីសាស្រ្តចំណាត់ថ្នាក់ឯកសារ ដែលដែលបរិមាណនៃពាក្យនីមួយៗ bi-word ឬ tri-word ជាសមាសធាតុមានប្រយោជន៍សម្រាប់បណ្តុះបណ្តាលអ្នកចំណាត់ថ្នាក់។ នៅក្នុងការតំណាង bigram, ឧទាហរណ៍, យើងនឹងបន្ថែមគូពាក្យទាំងអស់ទៅវចនានុក្រម បន្ថែមផងដល់ពាក្យដើម។\n",
|
||
"\n",
|
||
"ខាងក្រោមជាឧទាហរណ៍នៃវិធីបង្កើតការតំណាង bag of word bigram ដោយប្រើ 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": [
|
||
"ចំនុចខ្សោយសំខាន់នៃវិធីសាស្រ្ត n-gram គឺទំហំវាក្យសម្ពន្ធក្នុងភាសាចាប់ផ្តើមតែបង្កើនយ៉ាងលឿនខ្លាំង។ ក្នុងការអនុវត្តថ្មីៗនេះ យើងត្រូវការរួមបញ្ចូលតំណាង n-gram ជាមួយនឹងបច្ចេកទេសកាត់បន្ថយវិមាត្រ មួយដូចជា *embeddings* ដែលយើងនឹងពិភាក្សានៅក្នុងឯកតាតទៅមុខ។\n",
|
||
"\n",
|
||
"ដើម្បីប្រើតំណាង n-gram ក្នុងឃ្លាំងទិន្នន័យ **AG News** របស់យើង យើងត្រូវបញ្ជូនចូលប៉ារ៉ាម៉ែត្រ `ngrams` ទៅក្នុងអ្នកបង្កើត `TextVectorization` របស់យើង។ ប្រវែងនៃវាក្យសម្ពន្ធ bigram គឺ **ធំជាងយ៉ាងសំខាន់** ក្នុងករណីរបស់យើងវាធ្វើបានលើស 1.3 លានសញ្ញានៅឡើយ! ដូចនេះវាអាចមានអត្ថប្រយោជន៍ក្នុងការរំលងត្រីកោណ bigram ដោយចំនួនដែលសមរម្យ។\n",
|
||
"\n",
|
||
"យើងអាចប្រើកូដដូចខាងលើដដែលដើម្បីបណ្តុះបណ្តាលអ្នកចាត់ថ្នាក់ ក៏ប៉ុន្តែ វាកើតមានការប្រើប្រាស់អង្គចងចាំមិនមានប្រសិទ្ធភាពទេ។ នៅឯកតាទៅមុខយើងនឹងបណ្តុះបណ្តាលអ្នកចាត់ថ្នាក់ bigram ប្រើ embeddings។ នៅពេលនេះ អ្នកអាចសាកល្បងបណ្តុះបណ្តាលអ្នកចាត់ថ្នាក់ bigram ក្នុងសៀវភៅកំណត់ត្រានេះ ហើយមើលថាតើអ្នកអាចទទួលបានភាពច្បាស់លាស់ខ្ពស់ជាងមុនទេ។\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## ការគណនាផ្ទាល់ដោយស្វ័យប្រវត្តិរបស់វ៉ិចទ័រប៊លស័រ BoW\n",
|
||
"\n",
|
||
"នៅក្នុងឧទាហរណ៍ខាងលើ យើងបានគណនាវ៉ិចទ័រប៊លស័រ BoW ដោយដៃ ដោយបូកសំឡេងបញ្ចូលមួយ-កម្រាស់នៃពាក្យមួយៗ។ ទោះបីជាយ៉ាងណា កំណែចុងក្រោយនៃ TensorFlow អនុញ្ញាតឱ្យយើងគណនាវ៉ិចទ័រប៊លស័រ BoW ដោយស្វ័យប្រវត្តិ ដោយផ្តល់ប៉ារ៉ាម៉ែត្រ `output_mode='count` ទៅដល់អ្នកបង្កើតវ៉ិចទ័រ។ វានាំឲ្យការកំណត់និងបណ្តុះបណ្តាលម៉ូដែលរបស់យើងក្លាយជាស្រួលយ៉ាងច្រើន៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training vectorizer\n",
|
||
"938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c725217c0>"
|
||
]
|
||
},
|
||
"execution_count": 15,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"model = keras.models.Sequential([\n",
|
||
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='count'),\n",
|
||
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
|
||
"])\n",
|
||
"print(\"Training vectorizer\")\n",
|
||
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## ការពេញនិយមពាក្យ - អត្រាប្រាក់ឯកសារវិទ្យុបញ្ច្រាស (TF-IDF)\n",
|
||
"\n",
|
||
"ក្នុងការបង្ហាញ BoW,ការ កើតឡើងនៃពាក្យត្រូវបានវាស់គ្រប់យ៉ាង ដោយប្រើប្រាស់បច្ចេកវិទ្យាដូចគ្នាដោយមិនគិតពីពាក្យ។ ទោះយ៉ាងណា វCLEARថាពាក្យដែលកើតឡើងជាញឹកញាប់ដូចជា *a* និង *in* មានសារៈសំខាន់តិចជាងសម្រាប់ការធ្វើចំណាត់ថ្នាក់ជាងពាក្យឯកទេស។ ក្នុងភារកិច្ច NLP ភាគច្រើនពាក្យខ្លះមានសារៈសំខាន់ជាងអ្វីៗផ្សេងទៀត។\n",
|
||
"\n",
|
||
"**TF-IDF** មានន័យថា **ការពេញនិយមពាក្យ - អត្រាប្រាក់ឯកសារវិទ្យុបញ្ច្រាស**។ វាជារបៀបបំលែង bag-of-words មួយ ដែលជំនួសជំនួសតម្លៃ 0/1 ដែលបង្ហាញពីការបង្ហាញខ្លួននៃពាក្យក្នុងឯកសារ ដោយប្រើតម្លៃទសភាគដែលពាក់ព័ន្ធនឹងការកើតឡើងច្រើននៃពាក្យនៅក្នុងសៀវភៅឯកសារ។\n",
|
||
"\n",
|
||
"យ៉ាងជាក់លាក់ ការវាស់ទំងន់ $w_{ij}$ របស់ពាក្យ $i$ នៅក្នុងឯកសារ $j$ ត្រូវបានកំណត់ជា៖\n",
|
||
"$$\n",
|
||
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
|
||
"$$\n",
|
||
"ដែល\n",
|
||
"* $tf_{ij}$ គឺចំនួនករណីកើតឡើងនៃ $i$ ក្នុង $j$ ឧ. តម្លៃ BoW ដែលយើងបានឃើញមុន\n",
|
||
"* $N$ គឺចំនួនឯកសារនៅក្នុងកម្រង\n",
|
||
"* $df_i$ គឺចំនួនឯកសារដែលមានពាក្យ $i$ ក្នុងកម្រងទាំងមូល\n",
|
||
"\n",
|
||
"តម្លៃ TF-IDF $w_{ij}$ កើនឡើងបរិមាណផ្គួសៗទៅនឹងចំនួននៃពេលដែលពាក្យមួយបង្ហាញក្នុងឯកសារ ហើយត្រូវបានលៃតម្រូវដោយចំនួនឯកសារនៅក្នុងសៀវភៅដែលមានពាក្យនោះ ដែលជួយឲ្យត្រូវតែចៀសវាងពីការពិតដែលមានពាក្យមួយចំនួនបង្ហាញជាញឹកញាប់ជាងពាក្យផ្សេងៗ។ ឧទាហរណ៍ ប្រសិនបើពាក្យបង្ហាញនៅក្នុង *គ្រប់* ឯកសារនៅក្នុងកម្រង, $df_i=N$ ហើយ $w_{ij}=0$, ហើយពាក្យទាំងនោះនឹងត្រូវបានមិនគិតទាំងស្រុង។\n",
|
||
"\n",
|
||
"អ្នកអាចបង្កើតវ៉ិចទ័រ TF-IDF នៃអត្ថបទបានយ៉ាងងាយស្រួលដោយប្រើ 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": [
|
||
"នៅក្នុង Keras ស្រទាប់ `TextVectorization` អាចគណនាប្រមាណភាព TF-IDF ដោយស្វ័យប្រវត្តិដោយផ្ទេរព៉ារ៉ាម៉ែត្រ `output_mode='tf-idf'`។ សូមធ្វើម្តងទៀតនូវកូដដែលយើងបានប្រើខាងលើ ដើម្បីមើលថា តើការប្រើ TF-IDF មានបង្កើនភាពត្រឹមត្រូវឬអត់៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training vectorizer\n",
|
||
"938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c729dfd30>"
|
||
]
|
||
},
|
||
"execution_count": 17,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"model = keras.models.Sequential([\n",
|
||
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='tf-idf'),\n",
|
||
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
|
||
"])\n",
|
||
"print(\"Training vectorizer\")\n",
|
||
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## សេចក្ដីសន្នឹម\n",
|
||
"\n",
|
||
"ទោះបីជារូបមន្ត TF-IDF ផ្ដល់ទម្ងន់ប្រេកង់ទៅពាក្យផ្សេងៗក៏ដោយ វាមិនអាចបង្ហាញអត្ថន័យ ឬលំដាប់បានឡើយ។ ដូចដែលអ្នកភាសាល្បី J. R. Firth បាននិយាយនៅឆ្នាំ 1935 ថា \"អត្ថន័យពេញលេញនៃពាក្យមួយតែងតែមានបរិបទ ហើយការសិក្សាអំពីអត្ថន័យដែលមិនពាក់ព័ន្ធនឹងបរិបទមិនអាចយកទៅពិចារណាឲ្យបានចាប់អារម្មណ៍ឡើយ។\" យើងនឹងរៀនពីរបៀបចាប់យកព័ត៌មានបរិបទពីអត្ថបទដោយប្រើម៉ូដែលភាសាបន្ថែមនៅពេលក្រោយក្នុងវគ្គសិក្សា។\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"---\n\n<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**ការប្រកាសការពារ**៖ \nឯកសារនេះត្រូវបានបកប្រែដោយប្រើសេវាកម្មបកប្រែ AI [Co-op Translator](https://github.com/Azure/co-op-translator)។ ខណៈពេលយើងខ្ញុំព្យាយាមធានាភាពត្រឹមត្រូវ សូមយល់ដឹងថាការបកប្រែដោយស្វ័យប្រវត្តិអាចមានកំហុសឬភាពមិនត្រឹមត្រូវ។ ឯកសារដើមក្នុងភាសាដើមគួរត្រូវបានគេចាត់ទុកជាប្រភពដើមដែលមានសិទិ្ធផ្លូវការ។ សម្រាប់ព័ត៌មានសំខាន់ៗ ការបកប្រែដោយមនុស្សជំនាញជាការពេញចិត្តជាង។ យើងមិនទទួលខុសត្រូវចំពោះការយល់ខុស ឬការបកប្រែខុសណាមួយដែលកើតឡើងពីការប្រើប្រាស់ការបកប្រែនេះទេ។\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\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"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
} |