{ "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": [ "" ] }, "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", "![រូបភាពបង្ហាញរបៀប​ដែលការទ្រទ្រង់ជាវ៉ិចទ័រម៉ូដែល Bag-of-words ត្រូវបានបង្ហាញក្នុងអង្គចងចាំ។](../../../../../translated_images/km/bag-of-words-example.606fc1738f1d7ba9.webp) \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": [ "" ] }, "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": [ "" ] }, "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": [ "" ] }, "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": [ "" ] }, "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\n**ការ​ប្រកាស​ការពារ**៖ \nឯកសារ​នេះ​ត្រូវបាន​បកប្រែ​ដោយ​ប្រើសេវាកម្ម​បកប្រែ AI [Co-op Translator](https://github.com/Azure/co-op-translator)។ ខណៈពេល​យើងខ្ញុំ​ព្យាយាម​ធានា​ភាព​ត្រឹមត្រូវ សូមយល់ដឹងថា​ការ​បកប្រែ​ដោយ​ស្វ័យ​ប្រវត្តិ​អាច​មាន​កំហុស​ឬ​ភាព​មិន​ត្រឹមត្រូវ។ ឯកសារ​ដើម​ក្នុង​ភាសា​ដើម​គួរត្រូវ​បាន​គេចាត់ទុក​ជា​ប្រភព​ដើម​ដែលមាន​សិទិ្ធ​ផ្លូវការ។ សម្រាប់​ព័ត៌មាន​សំខាន់ៗ ការបកប្រែ​ដោយ​មនុស្ស​ជំនាញ​ជា​ការ​ពេញចិត្ត​ជាង។ យើង​មិន​ទទួលខុសត្រូវ​ចំពោះ​ការ​យល់ខុស ឬ​ការបកប្រែ​ខុស​ណាមួយ​ដែល​កើត​ឡើង​ពី​ការ​ប្រើប្រាស់​ការ​បកប្រែ​នេះទេ។\n\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 }