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

495 lines
40 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# เครือข่ายการสร้างสรรค์\n",
"\n",
"Recurrent Neural Networks (RNNs) และรูปแบบเซลล์ที่มีการควบคุม เช่น Long Short Term Memory Cells (LSTMs) และ Gated Recurrent Units (GRUs) ได้มอบกลไกสำหรับการสร้างแบบจำลองภาษา กล่าวคือ พวกมันสามารถเรียนรู้การเรียงลำดับคำและให้การคาดการณ์คำถัดไปในลำดับได้ สิ่งนี้ทำให้เราสามารถใช้ RNNs สำหรับ **งานสร้างสรรค์** เช่น การสร้างข้อความทั่วไป การแปลภาษา และแม้กระทั่งการสร้างคำบรรยายภาพ\n",
"\n",
"ในสถาปัตยกรรม RNN ที่เราได้พูดถึงในหน่วยก่อนหน้า แต่ละหน่วย 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` layer ที่เราเคยใช้ก่อนหน้านี้ไม่สามารถทำได้ ดังนั้นเรามีสองตัวเลือก:\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": []
},
{
"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",
"![ภาพแสดงตัวอย่างการสร้างคำว่า 'HELLO' ด้วย RNN](../../../../../translated_images/th/rnn-generate.56c54afb52f9781d.webp)\n",
"\n",
"สำหรับตัวอักษรสุดท้ายของลำดับ เราจะให้เครือข่ายสร้างโทเค็น `<eos>` \n",
"\n",
"ความแตกต่างหลักของ RNN เชิงกำเนิดที่เราใช้ในที่นี้คือ เราจะนำผลลัพธ์จากแต่ละขั้นตอนของ RNN มาใช้ ไม่ใช่แค่จากเซลล์สุดท้ายเท่านั้น ซึ่งสามารถทำได้โดยการกำหนดพารามิเตอร์ `return_sequences` ให้กับเซลล์ RNN\n",
"\n",
"ดังนั้น ในระหว่างการฝึก ข้อมูลที่ป้อนเข้าเครือข่ายจะเป็นลำดับของตัวอักษรที่ถูกเข้ารหัสในความยาวที่กำหนด และผลลัพธ์จะเป็นลำดับที่มีความยาวเท่ากัน แต่ถูกเลื่อนออกไปหนึ่งตำแหน่งและสิ้นสุดด้วย `<eos>` มินิแบตช์จะประกอบด้วยลำดับหลายชุด และเราจำเป็นต้องใช้ **padding** เพื่อจัดแนวลำดับทั้งหมดให้ตรงกัน\n",
"\n",
"เรามาสร้างฟังก์ชันที่จะเปลี่ยนรูปแบบชุดข้อมูลให้เรา เนื่องจากเราต้องการเติม padding ในระดับมินิแบตช์ เราจะทำการจัดชุดข้อมูลเป็นแบตช์โดยเรียกใช้ `.batch()` ก่อน จากนั้นจึงใช้ `map` เพื่อทำการเปลี่ยนรูปแบบ ดังนั้น ฟังก์ชันการเปลี่ยนรูปแบบจะรับมินิแบตช์ทั้งหมดเป็นพารามิเตอร์:\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` แปลงรายการของสตริงให้เป็นรายการของ integer tensors\n",
"* `pad_sequences` เติมเต็ม tensors เหล่านั้นให้มีความยาวสูงสุด\n",
"* สุดท้าย เราทำการ one-hot encode ตัวอักษรทั้งหมด รวมถึงการเลื่อนและเพิ่ม `<eos>` เราจะได้เห็นในไม่ช้าว่าทำไมเราถึงต้องการ one-hot-encoded characters\n",
"\n",
"อย่างไรก็ตาม ฟังก์ชันนี้เป็น **Pythonic** ซึ่งหมายความว่ามันไม่สามารถแปลงเป็น Tensorflow computational graph ได้โดยอัตโนมัติ เราจะพบข้อผิดพลาดหากพยายามใช้ฟังก์ชันนี้โดยตรงในฟังก์ชัน `Dataset.map` เราจำเป็นต้องครอบคลุมการเรียก Pythonic นี้โดยใช้ตัวห่อ `py_function`:\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` มีข้อได้เปรียบอย่างมาก เพราะกระบวนการแปลงข้อมูลจะถูกดำเนินการโดยใช้กราฟการคำนวณของ Tensorflow ซึ่งใช้ประโยชน์จากการคำนวณด้วย GPU และลดความจำเป็นในการส่งข้อมูลระหว่าง CPU/GPU\n",
"\n",
"ตอนนี้เราสามารถสร้างเครือข่าย generator ของเราและเริ่มการฝึกได้ มันสามารถอิงจาก recurrent cell ใด ๆ ที่เราได้พูดถึงในหน่วยก่อนหน้า (simple, LSTM หรือ GRU) ในตัวอย่างของเราจะใช้ LSTM\n",
"\n",
"เนื่องจากเครือข่ายรับตัวอักษรเป็นอินพุต และขนาดของคำศัพท์ค่อนข้างเล็ก เราไม่จำเป็นต้องมี embedding layer อินพุตที่ถูก one-hot-encoded สามารถส่งตรงไปยัง LSTM cell ได้เลย ชั้นเอาต์พุตจะเป็น `Dense` classifier ที่จะแปลงผลลัพธ์ของ LSTM ให้เป็นตัวเลขโทเค็นที่ถูก one-hot-encoded\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",
"\n",
"* **เพิ่มข้อความ** เราใช้เพียงแค่หัวข้อสำหรับงานนี้ แต่คุณอาจต้องการทดลองใช้ข้อความเต็มรูปแบบ โปรดจำไว้ว่า RNNs ไม่ค่อยเก่งในการจัดการกับลำดับที่ยาวมากนัก ดังนั้นจึงสมเหตุสมผลที่จะตัดข้อความออกเป็นประโยคสั้นๆ หรือฝึกอบรมด้วยความยาวลำดับที่กำหนดไว้ล่วงหน้า `num_chars` (เช่น 256) คุณอาจลองเปลี่ยนตัวอย่างข้างต้นให้เป็นสถาปัตยกรรมแบบนี้ โดยใช้ [ตัวอย่าง Keras อย่างเป็นทางการ](https://keras.io/examples/generative/lstm_character_level_text_generation/) เป็นแรงบันดาลใจ\n",
"\n",
"* **LSTM หลายชั้น** การลองใช้ LSTM 2 หรือ 3 ชั้นก็สมเหตุสมผลเช่นกัน ดังที่เราได้กล่าวถึงในหน่วยก่อนหน้า แต่ละชั้นของ LSTM จะดึงรูปแบบบางอย่างจากข้อความออกมา และในกรณีของตัวสร้างข้อความระดับตัวอักษร เราสามารถคาดหวังได้ว่าชั้น LSTM ระดับล่างจะรับผิดชอบการดึงพยางค์ และชั้นที่สูงขึ้นจะรับผิดชอบคำและการผสมคำ สิ่งนี้สามารถทำได้ง่ายๆ โดยการส่งพารามิเตอร์จำนวนชั้นไปยังตัวสร้าง LSTM\n",
"\n",
"* คุณอาจต้องการทดลองใช้ **GRU units** และดูว่าแบบใดทำงานได้ดีกว่า รวมถึง **ขนาดของชั้นซ่อนที่แตกต่างกัน** ชั้นซ่อนที่ใหญ่เกินไปอาจทำให้เกิดการ overfitting (เช่น เครือข่ายจะเรียนรู้ข้อความแบบเป๊ะๆ) และขนาดที่เล็กเกินไปอาจไม่สร้างผลลัพธ์ที่ดี\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",
"อย่างไรก็ตาม หากเราดูการแจกแจงความน่าจะเป็นสำหรับตัวอักษรถัดไป อาจพบว่าความแตกต่างระหว่างความน่าจะเป็นสูงสุดสองสามตัวไม่ได้มากนัก เช่น ตัวอักษรหนึ่งอาจมีความน่าจะเป็น 0.2 และอีกตัวหนึ่งมี 0.19 เป็นต้น ตัวอย่างเช่น เมื่อมองหาตัวอักษรถัดไปในลำดับ '*play*' ตัวอักษรถัดไปอาจเป็นช่องว่างหรือ **e** (เช่นในคำว่า *player*) ได้เท่าๆ กัน\n",
"\n",
"สิ่งนี้นำเราไปสู่ข้อสรุปว่าไม่ใช่เรื่อง \"ยุติธรรม\" เสมอไปที่จะเลือกตัวอักษรที่มีความน่าจะเป็นสูงกว่า เพราะการเลือกตัวอักษรที่มีความน่าจะเป็นรองลงมาอาจยังคงนำไปสู่ข้อความที่มีความหมายได้ การสุ่มเลือกตัวอักษรจากการแจกแจงความน่าจะเป็นที่ได้จากผลลัพธ์ของเครือข่ายจึงเป็นวิธีที่ชาญฉลาดกว่า\n",
"\n",
"การสุ่มนี้สามารถทำได้โดยใช้ฟังก์ชัน `np.multinomial` ซึ่งเป็นการใช้งาน **การแจกแจงแบบมัลติโนเมียล** ฟังก์ชันที่ใช้สร้างข้อความแบบ **นุ่มนวล** นี้ถูกกำหนดไว้ด้านล่าง:\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": [
"เราได้แนะนำพารามิเตอร์อีกตัวหนึ่งที่เรียกว่า **temperature** ซึ่งใช้เพื่อบ่งบอกว่าเราควรยึดติดกับความน่าจะเป็นสูงสุดมากแค่ไหน หาก temperature เท่ากับ 1.0 เราจะทำการสุ่มแบบ multinomial อย่างยุติธรรม และเมื่อ temperature เพิ่มขึ้นจนถึงค่าอนันต์ ความน่าจะเป็นทั้งหมดจะเท่ากัน และเราจะสุ่มเลือกตัวอักษรถัดไปแบบสุ่ม ในตัวอย่างด้านล่าง เราสามารถสังเกตได้ว่าข้อความจะไม่มีความหมายเมื่อเราเพิ่มค่า temperature มากเกินไป และข้อความจะคล้ายกับข้อความที่ถูกสร้างขึ้นแบบ \"วนซ้ำ\" เมื่อค่า temperature เข้าใกล้ 0\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**ข้อจำกัดความรับผิดชอบ**: \nเอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้องมากที่สุด แต่โปรดทราบว่าการแปลโดยอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราจะไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้\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"
},
"coopTranslator": {
"original_hash": "9fbb7d5fda708537649f71f5f646fcde",
"translation_date": "2025-08-29T10:38:37+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "th"
}
},
"nbformat": 4,
"nbformat_minor": 4
}