488 lines
41 KiB
Plaintext
488 lines
41 KiB
Plaintext
{
|
||
"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": [
|
||
"យើងក៏ចង់ប្រើតួអក្សរពិសេសមួយដើម្បីបង្ហាញ **ចប់សន្ទស្សន៍** ដែលយើងនឹងហៅវា \\<eos>។ មកដាក់វាដោយដៃទៅក្នុងវាក្យសព្ទ៖\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"eos_token = len(tokenizer.word_index)+1\n",
|
||
"tokenizer.word_index['<eos>'] = 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",
|
||
"\n",
|
||
"\n",
|
||
"សម្រាប់តួអក្សរចុងក្រោយនៃខ្សែស្រឡាយយើង នឹងស្នើឲ្យបណ្តាញបង្កើត `<eos>` token។\n",
|
||
"\n",
|
||
"ភាពខុសគ្នាចម្បងរវាង RNN បង្កើតដែលយើងកំពុងប្រើនៅទីនេះ គឺយើងនឹងយកលទ្ធផលចេញពីជំហាននីមួយៗរបស់ RNN មិនមែនពីកោសិការចុងក្រោយតែប៉ុណ្ណោះទេ។ នេះអាចធ្វើទៅបានដោយកំណត់ប៉ារ៉ាម៉ែត្រ `return_sequences` ទៅកាន់កោសិការនៃ RNN។\n",
|
||
"\n",
|
||
"ដូច្នេះ ក្នុងការបណ្តុះបណ្តាលផ្នែកបញ្ចូលទៅបណ្តាញនឹងជាខ្សែស្រឡាយតួអក្សរដែលបានកូដមួយចំនួននៃប្រវែងមួយ ហើយលទ្ធផលនឹងជាខ្សែស្រឡាយដែលមានប្រវែងដូចគ្នា ប៉ុន្តែប្រែប្រួលដោយផ្លាស់ទីមួយធាតុ ហើយបញ្ចប់ដោយ `<eos>`។ 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 និងបន្ថែម `<eos>` ផងដែរ។ យើងនឹងឃើញភ្លាមៗហេតុផលហេតុអ្វីយើងត្រូវការតួអក្សរត្រូវបានបំលែងជា 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` ដើម្បីបង្កើតម៉ាស ដែលនឹងមិនគិតផ្នែកដែលពុម្ភបន្ថែមនៅចុងជួរខ្សែអក្សរ។ នេះមិនចាំបាច់តឹងត្រាប់ទេ ពីព្រោះយើងមិនសម្លឹងទៅលើអ្វីៗទាំងអស់ដែលនៅក្រៅតួ `<eos>` ទេ ប៉ុន្តែក្នុងករណីនេះយើងនឹងប្រើវាសម្រាប់ទទួលបានបទពិសោធន៍ជាមួយប្រភេទជាន់នេះ។ `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": [
|
||
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
|
||
]
|
||
},
|
||
"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": [
|
||
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
|
||
]
|
||
},
|
||
"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<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-33-db32367a0feb>\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<ipython-input-10-3f5fa6130b1d>\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<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\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<!-- 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": "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
|
||
} |