{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# បណ្តាញបង្កើត\n", "\n", "បណ្តាញប្រស่า (Recurrent Neural Networks - RNNs) និងបែបបទកោសិកាដែលមានទ្វារដូចជា ឧក្រិដ្ឋជាតិសម័យបណ្តោះអាសន្នបែបវែង (Long Short Term Memory Cells - LSTMs) និងឧក្រិដ្ឋជាតិសម័យជាតិត្រកូល (Gated Recurrent Units - GRUs) ផ្តល់មកនូវយន្តការសម្រាប់ការម៉ូដែលភាសា ដែលមានន័យថា ពួកគេអាចរៀនលំដាប់ពាក្យ និងផ្តល់ការព្យាករណ៍សម្រាប់ពាក្យបន្ទាប់ក្នុងលំដាប់។ នេះអនុញ្ញាតឲ្យយើងប្រើ RNN សម្រាប់ **ភារកិច្ចបង្កើត** ដូចជា ការបង្កើតអត្ថបទទូទៅ ការបកប្រែម៉ាស៊ីន និងឯងការបណ្តឹងរូបភាព។\n", "\n", "នៅក្នុងគ្រឹះសម្រាប់ការរចនាបណ្តាញ RNN ដែលយើងបានពិភាក្សានៅឯកត្តាមុន រាល់អង្គភាព RNN ក៏បានបង្កើតស្ថានភាពលាក់បន្ទាប់ជាលទ្ធផល។ ទោះជាយ៉ាងណា យើងក៏អាចបន្ថែមលទ្ធផលមួយទៀតទៅរាល់អង្គភាពប្រសាទដែលអាចអោយយើងបញ្ចេញជាលំដាប់ (ដែលមានប្រវែងស្មើនឹងលំដាប់ដើម)។ លើសពីនេះ យើងអាចប្រើអង្គភាព RNN ដែលមិនទទួលអ្នកបញ្ចូលនៅរាល់ជំហានទេ ហើយគ្រាន់តែទទួលវ៉ិចទ័រស្ថានភាពដំបូង ហើយបន្ទាប់មកបង្កើតលំដាប់លទ្ធផល។\n", "\n", "នៅក្នុងសៀវភៅកំណត់ត្រានេះ យើងនឹងផ្តោតទៅលើម៉ូដែលបង្កើតបណ្តាក់សាមញ្ញ ដែលជួយយើងបង្កើតអត្ថបទ។ ដើម្បីសម្រួល អាចសង់បណ្តាញ **កម្រិតតួអក្សរ** ដែលបង្កើតអត្ថបទតួអក្សរតាមតួអក្សរ។ នៅពេលហ្វឹកហាត់ យើងត្រូវយកអត្ថបទមួយ ហើយបំបែកវាទៅជា លំដាប់តួអក្សរ។\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", "import numpy as np\n", "\n", "ds_train, ds_test = tfds.load('ag_news_subset').values()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ការបង្កើតវាក្យសព្ទតួអក្សរ\n", "\n", "ដើម្បីបង្កើតបណ្តាញបង្កើតនៅតួអក្សររង្វែង, យើងត្រូវបំបែកអត្ថបទទៅជាតួអក្សរដូចខ្លួនតូចមួយមិនមែនជាពាក្យទេ។ ស្រទាប់ `TextVectorization` ដែលយើងបានប្រើមុនមិនអាចធ្វើបែបនេះបានទេ, ដូចនេះយើងមានជម្រើសពីរម៉េ៖\n", "\n", "* ដាក់អត្ថបទដោយដៃហើយធ្វើការកំណត់និយមន័យពាក្យ 'ដោយដៃ', ដូចដែលមាននៅក្នុង [គំរូផ្លូវការនៃ Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n", "* ប្រើថ្នាក់ `Tokenizer` សម្រាប់កំណត់និយមន័យតួអក្សររង្វែង។\n", "\n", "យើងនឹងជ្រើសរើសជម្រើសទីពីរ។ អាចប្រើ `Tokenizer` សម្រាប់កំណត់និយមន័យទៅជាពាក្យបានផងដែរ, ដូច្នេះអ្នកអាចប្ដូរពីការកំណត់និយមន័យតួអក្សរទៅកាន់ការកំណត់និយមន័យពាក្យបានយ៉ាងងាយស្រួល។\n", "\n", "ដើម្បីធ្វើការកំណត់និយមន័យតួអក្សររង្វែង, យើងត្រូវផ្តល់ប៉ារ៉ាម៉ែត្រ `char_level=True`៖\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "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", "tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n", "tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "យើងក៏ចង់ប្រើតួអក្សរពិសេសមួយដើម្បីបង្ហាញ **ចប់សន្ទស្សន៍** ដែលយើងនឹងហៅវា \\។ មកដាក់វាដោយដៃទៅក្នុងវាក្យសព្ទ៖\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "eos_token = len(tokenizer.word_index)+1\n", "tokenizer.word_index[''] = eos_token\n", "\n", "vocab_size = eos_token + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ឥឡូវនេះ ដើម្បីកូដអត្ថបទទៅជាអក្សរលេខ ជួរដេក យើងអាចប្រើបាន៖\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenizer.texts_to_sequences(['Hello, world!'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ការបណ្តុះបណ្តាល RNN បង្កើតស្លោកឲ្យបាន\n", "\n", "វិធីដែលយើងនឹងបណ្តុះបណ្តាល RNN ដើម្បីបង្កើតស្លោកព័ត៌មាន គឺដូចខាងក្រោម។ នៅក្នុងជំហានមួយៗ យើងនឹងយកស្លោកមួយ ដែលនឹងត្រូវបញ្ចូលចូលទៅក្នុង RNN ហើយសម្រាប់តួអក្សរបញ្ចូលមួយៗ យើងនឹងស្នើឲ្យបណ្តាញបង្កើតតួអក្សរចេញបន្ទាប់៖\n", "\n", "![Image showing an example RNN generation of the word 'HELLO'.](../../../../../translated_images/km/rnn-generate.56c54afb52f9781d.webp)\n", "\n", "សម្រាប់តួអក្សរចុងក្រោយនៃខ្សែស្រឡាយយើង នឹងស្នើឲ្យបណ្តាញបង្កើត `` token។\n", "\n", "ភាពខុសគ្នាចម្បងរវាង RNN បង្កើតដែលយើងកំពុងប្រើនៅទីនេះ គឺយើងនឹងយកលទ្ធផលចេញពីជំហាននីមួយៗរបស់ RNN មិនមែនពីកោសិការចុងក្រោយតែប៉ុណ្ណោះទេ។ នេះអាចធ្វើទៅបានដោយកំណត់ប៉ារ៉ាម៉ែត្រ `return_sequences` ទៅកាន់កោសិការនៃ RNN។\n", "\n", "ដូច្នេះ ក្នុងការបណ្តុះបណ្តាលផ្នែកបញ្ចូលទៅបណ្តាញនឹងជាខ្សែស្រឡាយតួអក្សរដែលបានកូដមួយចំនួននៃប្រវែងមួយ ហើយលទ្ធផលនឹងជាខ្សែស្រឡាយដែលមានប្រវែងដូចគ្នា ប៉ុន្តែប្រែប្រួលដោយផ្លាស់ទីមួយធាតុ ហើយបញ្ចប់ដោយ ``។ Minibatch នឹងមានខ្សែស្រឡាយប៉ុន្មានដង ដូច្នេះយើងត្រូវប្រើ **padding** ដើម្បីតម្រឹមខ្សែស្រឡាយទាំងអស់។\n", "\n", "ឲ្យយើងបង្កើតអនុគមន៍ដែលនឹងផ្លាស់ប្តូរព័ត៌មានសម្រាប់យើង។ ពីព្រោះយើងចង់បញ្ចូល padding នៅលើកម្រិត minibatch ជាមុនសិន យើងនឹងដំបូងធ្វើការ batch ដataset ដោយហៅ `.batch()` រួចបន្ទាប់មក `map` វាដើម្បីធ្វើការបម្លែង។ ដូច្នេះ អនុគមន៍បម្លែងនឹងទទួលបាន minibatch មួយពេញជា argument៖\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def title_batch(x):\n", " x = [t.numpy().decode('utf-8') for t in x]\n", " z = tokenizer.texts_to_sequences(x)\n", " z = tf.keras.preprocessing.sequence.pad_sequences(z)\n", " return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "មានអ្វីៗ​សំខាន់​ច្រើន​ដែល​យើងធ្វើនៅទីនេះ៖\n", "* យើងចាប់ដកអត្ថបទពិតពី string tensor ដំបូង\n", "* `text_to_sequences` ដំណើរការប្រែបញ្ជី string ទៅជា​បញ្ជី tensor ពីរៃងខុសៗគ្នា\n", "* `pad_sequences` បន្ថែម padding ទៅលើ tensor ទាំងនោះ ដល់កម្ពស់តែមួយ\n", "* ចុងក្រោយ យើងបំលែងតួអក្សរទាំងអស់ទៅជា one-hot និងធ្វើការ shifting និងបន្ថែម `` ផងដែរ។ យើងនឹងឃើញភ្លាមៗហេតុផលហេតុអ្វីយើងត្រូវការតួអក្សរត្រូវបានបំលែងជា one-hot encoded\n", "\n", "ទោះជាយ៉ាងណា មុខងារនេះគឺជាមុខងារ **Pythonic** មានន័យថា វាមិនអាចបម្លែងដោយស្វ័យប្រវត្តិទៅជា Tensorflow computational graph បានទេ។ យើងនឹងទទួលបានកំហុស ប្រសិនបើយើងខំប្រើមុខងារនេះដោយផ្ទាល់ក្នុង `Dataset.map` function។ យើងត្រូវបង្រួមការហៅ Pythonic នេះដោយប្រើ `py_function` wrapper:\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def title_batch_fn(x):\n", " x = x['title']\n", " a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n", " return a,b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **កំណត់ចំណាំ**: ការបែងចែករវាងមុខងារបម្លែង Pythonic និង Tensorflow អាចហាក់ដូចជាស្មុគស្មាញចំពោះមួយភាគតិច ហើយអ្នកអាចកំពុងសួរថាហេតុអ្វីយើងមិនបម្លែងឈុតទិន្នន័យដោយប្រើមុខងារពី Python តាមទម្លាប់មុនពេលផ្ទុកទៅកាន់ `fit` ទេ។ ខណៈដែលវាអាចធ្វើបានប្រាកដ មុខងារ `Dataset.map` មានអត្ថប្រយោជន៍យ៉ាងខ្លាំង ពីព្រោះបំពង់បម្លែងទិន្នន័យត្រូវបានអនុវត្តដោយប្រើតង់ស័រហ្វ្លូក្រិហ្វកំណត់គណនា ដែលអាចប្រើអត្ថប្រយោជន៍នៃកំណត់គណនាការជាGPU ហើយកាត់បន្ថយការចាំបាច់ផ្ទុកទិន្នន័យរវាងCPU/GPU។\n", "\n", "ឥឡូវនេះយើងអាចបង្កើតបណ្ដាញកំណើតរបស់យើង និងចាប់ផ្តើមបណ្តុះបណ្តាល។ វាអាចផ្អែកលើកោសិកា recurrent មួយណាមួយដែលយើងបានពិភាក្សានៅឯកត្តាមុន (សាមញ្ញ, LSTM ឬ GRU)។ ក្នុងឧទាហរណ៍របស់យើង យើងនឹងប្រើ LSTM។\n", "\n", "ដោយសារតែបណ្ដាញទទួលតួអក្សរជា input ហើយទំហំវាកាបូប៉ុន្មានតូច យើងមិនត្រូវការជាន់ embedding ទេ ការបញ្ចូលតួតែតែមួយ-hot-encoded អាចចូលទៅកោសិកា LSTM ដោយផ្ទាល់។ ជាន់ output នឹងជា `Dense` ប្រភេទចម្រាញ់ ដែលនឹងបម្លែងចេញពី LSTM ទៅជាលេខកូដតួតែតែមួយ-hot។\n", "\n", "បន្ថែមពីនេះ ពេលដែលយើងកំពុងដំណើរការជាមួយសេរីពហុបំណែង មួយ អាចប្រើជាន់ `Masking` ដើម្បីបង្កើតម៉ាស ដែលនឹងមិនគិតផ្នែកដែលពុម្ភបន្ថែមនៅចុងជួរខ្សែអក្សរ។ នេះមិនចាំបាច់តឹងត្រាប់ទេ ពីព្រោះយើងមិនសម្លឹងទៅលើអ្វីៗទាំងអស់ដែលនៅក្រៅតួ `` ទេ ប៉ុន្តែក្នុងករណីនេះយើងនឹងប្រើវាសម្រាប់ទទួលបានបទពិសោធន៍ជាមួយប្រភេទជាន់នេះ។ `input_shape` នឹងមាន `(None, vocab_size)` ដែល `None` មានន័យថាជាសំណុំជួរចំណងជើងបញ្ចេញផ្សេងៗ ហើយទំហំនៃប្រអប់គឺ `(None,vocab_size)` ផងដែរ ដូចដែលអ្នកអាចមើលឃើញពី `summary`:\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"sequential\"\n", "_________________________________________________________________\n", "Layer (type) Output Shape Param # \n", "=================================================================\n", "masking (Masking) (None, None, 84) 0 \n", "_________________________________________________________________\n", "lstm (LSTM) (None, None, 128) 109056 \n", "_________________________________________________________________\n", "dense (Dense) (None, None, 84) 10836 \n", "=================================================================\n", "Total params: 119,892\n", "Trainable params: 119,892\n", "Non-trainable params: 0\n", "_________________________________________________________________\n", "15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = keras.models.Sequential([\n", " keras.layers.Masking(input_shape=(None,vocab_size)),\n", " keras.layers.LSTM(128,return_sequences=True),\n", " keras.layers.Dense(vocab_size,activation='softmax')\n", "])\n", "\n", "model.summary()\n", "model.compile(loss='categorical_crossentropy')\n", "\n", "model.fit(ds_train.batch(8).map(title_batch_fn))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ការបង្កើតលទ្ធផល\n", "\n", "ឥឡូវនេះដែលយើងបានបណ្តុះម៉ូដែលហើយ យើងចង់ប្រើវាដើម្បីបង្កើតលទ្ធផលមួយចំនួន។ ជាការដំបូង យើងត្រូវការរបៀបមួយក្នុងការបកប្រែអក្សរដែលតំណាងដោយលំដាប់លេខសញ្ញាសម្គាល់។ ដើម្បីធ្វើការនេះ យើងអាចប្រើមុខងារ `tokenizer.sequences_to_texts` ប៉ុន្តែវាមិនដំណើរការល្អជាមួយការបំបែកសញ្ញាលេខនៅកម្រិតតួអក្សរទេ។ ដូច្នេះ យើងនឹងយកវចនានុក្រមនៃសញ្ញាសម្គាល់ពី tokenizer (ហៅថា `word_index`), សង់ផែនទីត្រឡប់ក្រោយមួយ, ហើយសរសេរមុខងារបកប្រែរបស់យើងផ្ទាល់៖\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n", "\n", "def decode(x):\n", " return ''.join([reverse_map[t] for t in x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ឥឡូវនេះ យើងនឹងធ្វើការបង្កើត។ យើងនឹងចាប់ផ្តើមដោយខ្សែអក្សរ `start` មួយ ប្រើបំលែងវាទៅជាមួក `inp` ហើយបន្ទាប់មកនៅគ្រប់ជំហានយើងនឹងហៅបណ្ដាញរបស់យើងដើម្បីប៉ាន់ស្មានតួអក្សរបន្ទាប់។ \n", "\n", "ចេញពីបណ្ដាញ `out` គឺជាវ៉ែថ័រមានធាតុ `vocab_size` ដែលតំណាងឱ្យប្រសិទ្ធភាពនៃតួអក្សរនីមួយៗ ហើយយើងអាចស្វែងរកលេខតួអក្សរដែលមានប្រសិទ្ធភាពខ្ពស់បំផុតដោយប្រើ `argmax`។ បន្ទាប់មក យើងបន្ថែមតួអក្សរនោះទៅក្នុងបញ្ជីតួអក្សរដែលបានបង្កើត ហើយបន្តការបង្កើត។ ដំណើរការនៃការបង្កើតតួអក្សរមួយនេះត្រូវបានធ្វើឡើងជាប្រសិទ្ធិភាព `size` ដងដើម្បីបង្កើតចំនួនតួអក្សរត្រូវការ ហើយយើងបញ្ចប់មុនពេលរបស់វានៅពេលជួប `eos_token`។\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def generate(model,size=100,start='Today '):\n", " inp = tokenizer.texts_to_sequences([start])[0]\n", " chars = inp\n", " for i in range(size):\n", " out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n", " nc = tf.argmax(out)\n", " if nc==eos_token:\n", " break\n", " chars.append(nc.numpy())\n", " inp = inp+[nc]\n", " return decode(chars)\n", " \n", "generate(model)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## លទ្ធផលសម្រាប់ការយកមាតិកាត្រង់ពេលបណ្តុះបណ្តាល\n", "\n", "ដោយសារតែយើងមិនមានមាត្រដ្ឋានណាមួយដែលមានប្រយោជន៍ដូចជា *ភាពត្រឹមត្រូវ* ទេ វិធីតែមួយដែលយើងអាចមើលឃើញថាគំរូរបស់យើងកាន់តែប្រសើរឡើងគឺដោយ **យកមាតិកា** ខ្សែអក្សរ​ដែលបង្កើតឡើងក្នុងពេលបណ្តុះបណ្តាល។ ដើម្បីបំពេញការនេះ យើងនឹងប្រើ **callbacks** ឧ. អនុគមន៍ដែលយើងអាចផ្តល់ទៅអនុគមន៍ `fit` ហើយវានឹងត្រូវបានហៅជាបន្តបន្ទាប់ក្នុងកំឡុងពេលបណ្តុះបណ្តាល។\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/3\n", "15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n", "Today #39;s a lead in the company for the strike\n", "Epoch 2/3\n", "15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n", "Today #39;s the Market Service on Security Start (AP)\n", "Epoch 3/3\n", "15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n", "Today #39;s a line on the strike to start for the start\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sampling_callback = keras.callbacks.LambdaCallback(\n", " on_epoch_end = lambda batch, logs: print(generate(model))\n", ")\n", "\n", "model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ឧទាហរណ៍នេះបានបង្កើតអត្ថបទល្អមួយចំនួនរួចហើយ ប៉ុន្តែវាអាចត្រូវបានបង្កើនកាន់តែប្រសើរឡើងនៅក្នុងវិធីជាច្រើន៖\n", "* **អត្ថបទច្រើនជាងនេះ**។ យើងបានប្រើតែចំណងជើងសម្រាប់ភារកិច្ចរបស់យើងប៉ុណ្ណោះ ប៉ុន្តែអ្នកអាចចង់សាកល្បងជាមួយអត្ថបទពេញលេញ។ ចូរចាំថា RNNs មិនខ្លាំងណាស់ក្នុងការដោះស្រាយបន្ទាត់វែងៗពីរណោះ ដូច្នេះវាជាសមរម្យក្នុងការបំបែកពួកវាចូលទៅជាឃ្លាសង្ខេបឬជាប្រចាំហ្វឹកហាត់នៅលើរយៈពេលជាកំណត់ដែលបានកំណត់ជាមុន `num_chars` (ឧ. ២៥៦)។ អ្នកអាចសាកល្បងបម្លែងឧទាហរណ៍ខាងលើទៅជារចនាសម្ព័ន្ធបែបនេះដោយប្រើ [មេរៀនផ្លូវការ Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) ជាការបំផុសគំនិត។\n", "* **LSTM ជាច្រើនស្រទាប់**។ វាផ្តល់ន័យក្នុងការសាកល្បង ២ ឬ ៣ ស្រទាប់នៃកោសិកា LSTM។ ដូចដែលយើងបានរៀបរាប់នៅក្នុងមុខវិជ្ជាពេលមុន ស្រទាប់នីមួយៗនៃ LSTM នឹងដកស្រង់គំរូខ្លះៗពីអត្ថបទ ហើយសម្រាប់អ្នកបង្កើតអត្ថបទតាមកម្រិតតួអក្សរ យើងអាចរំពឹងថា ស្រទាប់ LSTM កម្រិតទាបនឹងទទួលខុសត្រូវក្នុងការដកស្រង់ពាក្យបញ្ជប់ខ្លះៗ ហើយស្រទាប់ខ្ពស់ជាងនេះ - សម្រាប់ពាក្យ និងការរួមបញ្ចូលនៃពាក្យ។ វាអាចអនុវត្តបានយ៉ាងសាមញ្ញដោយផ្តល់ប៉ារ៉ាម៉ែត្រពីចំនួនស្រទាប់ទៅកាន់ការសង់ LSTM។\n", "* អ្នកក៏អាចចង់សាកល្បងជាមួយ **ឯកតា GRU** ហើយមើលថាតើឯកតាណាអនុវត្តល្អជាង និងជាមួយ **ទំហំស្រទាប់លាក់ផ្សេងៗ**។ ទំហំស្រទាប់លាក់ធំពេកអាចនាំឲ្យមានការបំផ្លាញ (ឧ. បណ្ដាញនឹងរៀនអត្ថបទជាក់លាក់) ហើយទំហំតូចជាងអាចមិនបង្កើតលទ្ធផលល្អបាន។\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ការបង្កើតអត្ថបទទន់និងសីតុណ្ហភាព\n", "\n", "នៅក្នុងការបញ្ជាក់ទ្រឹស្តី `generate` មុននេះ មនុស្សយើងបានយកតួអក្សរដែលមានប្រតិបត្តិភាពខ្ពស់បំផុតជាតួអក្សរបន្ទាប់ក្នុងអត្ថបទដែលបានបង្កើត។ នេះបណ្តាលឲ្យអត្ថបទមួយចំនួន \"វិលជាថ្មី\" រវាងលំដាប់តួអក្សរដូចគ្នាឡើងវិញ ជាញឹកញាប់ ដូចក្នុងឧទាហរណ៍នេះ៖\n", "```\n", "today of the second the company and a second the company ...\n", "```\n", "\n", "ដូច្នេះ ប្រសិនបើយើងមើលការបែងចែកប្រតិបត្តិភាពសម្រាប់តួអក្សរបន្ទាប់ វាអាចជា ភាពខុសគ្នារវាងប្រតិបត្តិភាពខ្ពស់បំផុតតិចណាស់ ឧទាហរណ៍ តួអក្សរមួយអាចមានប្រតិបត្តិភាព ០.២ ខណៈតួអក្សរមួយផ្សេងទៀត - ០.១៩ លី។ ឧទាហរណ៍ពេលស្វែងរកតួអក្សរបន្ទាប់ក្នុងលំដាប់ '*play*', តួអក្សរបន្ទាប់អាចជាស្ពាន (space) ឬ **e** (ដូចក្នុងពាក្យ *player*) បានស្មើ។\n", "\n", "នេះនាំឲ្យយើងសន្និដ្ឋានថា មិនមែនជារឿង \"ត្រឹមត្រូវ\" ទៅតែងតួអក្សរដែលមានប្រតិបត្តិភាពខ្ពស់ជានិច្ចទេ ពីព្រោះការជ្រើសរើសលេខពីរខ្ពស់បំផុតអាចនាំឲ្យយើងទទួលបានអត្ថបទមានអត្ថន័យទៀត។ វាជាការយល់ឃើញល្អក្នុងការធ្វើការគំរូបតួអក្សរពីការបែងចែកប្រតិបត្តិភាពដែលបានផ្ដល់ដោយលទ្ធផលបណ្តាញ។\n", "\n", "ការគំរូបនេះអាចធ្វើបានដោយប្រើមុខងារ `np.multinomial` ដែលអនុវត្តការបែងចែកប្រតិបត្តិភាពហៅថា **multinomial distribution**។ មុខងារដែលអនុវត្តការបង្កើតអត្ថបទប្រភេទ**ទន់**នេះត្រូវបានកំណត់ខាងក្រោម៖\n" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--- Temperature = 0.3\n", "Today #39;s strike #39; to start at the store return\n", "On Sunday PO to Be Data Profit Up (Reuters)\n", "Moscow, SP wins straight to the Microsoft #39;s control of the space start\n", "President olding of the blast start for the strike to pay <b>...</b>\n", "Little red riding hood ficed to the spam countered in European <b>...</b>\n", "\n", "--- Temperature = 0.8\n", "Today countie strikes ryder missile faces food market blut\n", "On Sunday collores lose-toppy of sale of Bullment in <b>...</b>\n", "Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n", "President Ol Luster for Profit Peaced Raised (AP)\n", "Little red riding hood dace on depart talks #39; bank up\n", "\n", "--- Temperature = 1.0\n", "Today wits House buiting debate fixes #39; supervice stake again\n", "On Sunday arling digital poaching In for level\n", "Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n", "President teps help of roubler stepted lessabul-Dhalitics (AFP)\n", "Little red riding hood signs on cash in Carter-youb\n", "\n", "--- Temperature = 1.3\n", "Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n", "On Sunday hround elitwing wint EU Powerburlinetien\n", "Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n", "President lost securitys from power Elections in Smiltrials\n", "Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n", "\n", "--- Temperature = 1.8\n", "Today #39;It: He deat: N.KA Asside\n", "On Sunday i arry Par aldeup patient Wo stele1\n" ] }, { "ename": "KeyError", "evalue": "0", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mKeyError\u001b[0m: 0" ] } ], "source": [ "def generate_soft(model,size=100,start='Today ',temperature=1.0):\n", " inp = tokenizer.texts_to_sequences([start])[0]\n", " chars = inp\n", " for i in range(size):\n", " out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n", " probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n", " probs = probs/np.sum(probs)\n", " nc = np.argmax(np.random.multinomial(1,probs,1))\n", " if nc==eos_token:\n", " break\n", " chars.append(nc)\n", " inp = inp+[nc]\n", " return decode(chars)\n", "\n", "words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n", " \n", "for i in [0.3,0.8,1.0,1.3,1.8]:\n", " print(f\"\\n--- Temperature = {i}\")\n", " for j in range(5):\n", " print(generate_soft(model,size=300,start=words[j],temperature=i))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "យើងបានណែនាំអង្កត់ផ្ចិតមួយទៀតដែលហៅថា **សីតុខ** ដែលប្រើសម្រាប់បង្ហាញពីរបៀបយើងគួរតែខិតខំយ៉ាងម៉េចក្នុងការបន្តតាមប្រាក់ប្រហែលខ្ពស់បំផុត។ ប្រសិនបើសីតុណ្ហភាពគឺ 1.0 យើងធ្វើការប៉ាន់ប្រមាណតាមម៉ុលទីណូម្យលាដោយសមរម្យ ហើយនៅពេលដែលសីតុណ្ហភាពទៅរកអនាម័យ - ប្រាក់ប្រហែលទាំងអស់នឹងមានតុល្យភាព ហើយយើងជ្រើសរើសតួអក្សរបន្ទាប់ដោយចៃដន្យ។ ក្នុងឧទាហរណ៍ខាងក្រោម យើងអាចសង្កេតឃើញថាអត្ថន័យអក្សរត្រូវបានបាត់បង់ពេលយើងបង្កើនសីតុណ្ហភាពច្រើនពេក ហើយវាបង្ហាញភាពស្រដៀងនឹងអត្ថបទដែលបានបង្កើតឡើងយ៉ាងម៉ត់ចត់ដែលមានលក្ខណៈ \"ចំណោត\" នៅពេលវាហាក់ដូចជាយឺនទៅកាន់ 0។\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": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f" }, "kernelspec": { "display_name": "Python 3.8.12 ('py38')", "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" } }, "nbformat": 4, "nbformat_minor": 4 }