AI-For-Beginners/translations/km/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb

488 lines
41 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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",
"![Image showing an example RNN generation of the word 'HELLO'.](../../../../../translated_images/km/rnn-generate.56c54afb52f9781d.webp)\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 &lt;b&gt;...&lt;/b&gt;\n",
"Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\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 &lt;b&gt;...&lt;/b&gt;\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
}