{ "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": [ "เรายังต้องการใช้โทเค็นพิเศษหนึ่งตัวเพื่อระบุ **จุดสิ้นสุดของลำดับ** ซึ่งเราจะเรียกว่า `` มาลองเพิ่มมันเข้าไปในคลังคำด้วยตนเอง:\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": [] }, { "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", "สำหรับตัวอักษรสุดท้ายของลำดับ เราจะให้เครือข่ายสร้างโทเค็น `` \n", "\n", "ความแตกต่างหลักของ RNN เชิงกำเนิดที่เราใช้ในที่นี้คือ เราจะนำผลลัพธ์จากแต่ละขั้นตอนของ RNN มาใช้ ไม่ใช่แค่จากเซลล์สุดท้ายเท่านั้น ซึ่งสามารถทำได้โดยการกำหนดพารามิเตอร์ `return_sequences` ให้กับเซลล์ RNN\n", "\n", "ดังนั้น ในระหว่างการฝึก ข้อมูลที่ป้อนเข้าเครือข่ายจะเป็นลำดับของตัวอักษรที่ถูกเข้ารหัสในความยาวที่กำหนด และผลลัพธ์จะเป็นลำดับที่มีความยาวเท่ากัน แต่ถูกเลื่อนออกไปหนึ่งตำแหน่งและสิ้นสุดด้วย `` มินิแบตช์จะประกอบด้วยลำดับหลายชุด และเราจำเป็นต้องใช้ **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 ตัวอักษรทั้งหมด รวมถึงการเลื่อนและเพิ่ม `` เราจะได้เห็นในไม่ช้าว่าทำไมเราถึงต้องการ 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` เพื่อสร้างมาสก์ที่จะละเลยส่วนที่เติมเต็มของสตริงได้ แม้ว่าสิ่งนี้จะไม่จำเป็นอย่างเคร่งครัด เพราะเราไม่ได้สนใจอะไรมากนักที่เกินโทเค็น `` แต่เราจะใช้มันเพื่อให้ได้ประสบการณ์กับชั้นประเภทนี้ `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", "\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 <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": [ "เราได้แนะนำพารามิเตอร์อีกตัวหนึ่งที่เรียกว่า **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 }