บทช่วยสอน RNN (โครงข่ายประสาทเทียมที่เกิดซ้ำ): ตัวอย่าง TensorFlow

⚡ สรุปอย่างชาญฉลาด

โครงข่ายประสาทเทียมแบบวนซ้ำ (Recurrent Neural Networks) เพิ่มสถานะหน่วยความจำให้กับเซลล์ประสาททั่วไป โดยป้อนเอาต์พุตแต่ละตัวกลับเข้าไปเป็นอินพุตถัดไป เพื่อให้แบบจำลองสามารถเรียนรู้ตามลำดับได้ บทแนะนำนี้จะสร้างโครงข่ายดังกล่าวใน TensorFlow และพยากรณ์อนุกรมเวลา

  • 🔘 สถานะหน่วยความจำ: เซลล์ประสาทแบบวนซ้ำแต่ละตัวจะได้รับเอาต์พุตก่อนหน้าควบคู่ไปกับอินพุตใหม่ ซึ่งเป็นสิ่งที่ทำให้เครือข่ายมีความสามารถในการรับรู้เชิงเวลา
  • ☑️ รูปแบบผลลัพธ์: การรันเครือข่ายจำลองจะส่งคืนเทนเซอร์ (3, 2, 6) ซึ่งประกอบด้วยชุดข้อมูล ขั้นตอนเวลา และเซลล์ประสาท ตามลำดับ
  • ✅ กฎการจัดกลุ่ม: ข้อมูลอนุกรมเวลาจะถูกปรับรูปร่างใหม่ให้เป็นสามมิติ และอาร์เรย์ป้ายกำกับจะเป็นอนุกรมเดียวกันแต่เลื่อนไปข้างหน้าหนึ่งช่วงเวลา
  • 🧪 โครงกระดูกจำลอง: ตัวยึดตำแหน่ง, BasicRNNCell ที่ห่อหุ้มด้วย dynamic_rnn, การฉายภาพแบบหนาแน่น, จากนั้นจึงคำนวณค่าความคลาดเคลื่อนกำลังสองเฉลี่ยโดยใช้ตัวเพิ่มประสิทธิภาพ Adam
  • 🛠️ การวิ่งฝึกซ้อม: การฝึกฝน 1,500 รอบบนเซลล์ประสาทแบบวนซ้ำ 120 เซลล์ ทำให้ค่าความสูญเสียลดลงจาก 502,893 เหลือประมาณ 1,385
  • 📉 ขีดจำกัดที่ทราบ: ลำดับยาวๆ ทำให้เกิดปัญหาการลดลงของความชัน ซึ่งเป็นปัญหาที่ LSTM และรูปแบบที่มีเกตถูกออกแบบมาเพื่อแก้ไข

RNN (Recurrent Neural Network)

เหตุใดเราจึงต้องมี Recurrent Neural Network (RNN)

Recurrent Neural Network (RNN) ช่วยให้คุณสามารถจำลองหน่วยหน่วยความจำเพื่อคงข้อมูลและสร้างแบบจำลองการพึ่งพาระยะสั้น นอกจากนี้ยังใช้ในการพยากรณ์อนุกรมเวลาเพื่อระบุความสัมพันธ์และรูปแบบของข้อมูล นอกจากนี้ยังช่วยสร้างผลลัพธ์เชิงคาดการณ์สำหรับข้อมูลตามลำดับโดยนำเสนอพฤติกรรมที่คล้ายคลึงกันกับสมองของมนุษย์

โครงสร้างของ an โครงข่ายประสาทเทียม กระบวนการนี้ค่อนข้างง่ายและส่วนใหญ่เกี่ยวข้องกับการคูณเมทริกซ์ ในขั้นตอนแรก ข้อมูลนำเข้าจะถูกคูณด้วยน้ำหนักและค่าไบแอสแบบสุ่มในตอนเริ่มต้น จากนั้นจะถูกแปลงด้วยฟังก์ชันการกระตุ้น และค่าเอาต์พุตจะถูกนำมาใช้ในการทำนาย ขั้นตอนนี้จะช่วยให้เห็นภาพว่าเครือข่ายอยู่ห่างจากความเป็นจริงมากน้อยเพียงใด

เมตริกที่ใช้คือการสูญเสีย ยิ่งฟังก์ชันการสูญเสียมีค่าสูงขึ้น โมเดลก็จะยิ่งโง่ลง หากต้องการปรับปรุงความรู้เกี่ยวกับเครือข่าย จำเป็นต้องปรับค่าถ่วงน้ำหนักของเน็ตให้เหมาะสม การลดระดับความชันแบบสุ่มเป็นวิธีที่ใช้ในการเปลี่ยนค่าของน้ำหนักในทิศทางที่ถูกต้อง เมื่อปรับแล้ว เครือข่ายสามารถใช้ชุดข้อมูลอื่นเพื่อทดสอบความรู้ใหม่

โชคดีที่ค่าความคลาดเคลื่อนลดลงกว่าเดิม แต่ก็ยังไม่น้อยพอ ขั้นตอนการปรับให้เหมาะสมจะทำซ้ำไปเรื่อยๆ จนกว่าค่าความคลาดเคลื่อนจะลดลงเหลือน้อยที่สุด กล่าวคือ จะไม่มีข้อมูลเพิ่มเติมใดๆ ที่สามารถหาได้อีกแล้วtracเท็ด

ปัญหาของรุ่นนี้คือไม่มีหน่วยความจำใดๆ หมายความว่าอินพุตและเอาต์พุตเป็นอิสระ กล่าวอีกนัยหนึ่ง โมเดลไม่สนใจว่าอะไรจะเกิดขึ้นก่อนหน้านี้ ทำให้เกิดคำถามขึ้นเมื่อคุณต้องการคาดเดาอนุกรมเวลาหรือประโยค เนื่องจากเครือข่ายจำเป็นต้องมีข้อมูลเกี่ยวกับข้อมูลในอดีตหรือคำในอดีต

เพื่อเอาชนะปัญหานี้ จึงมีการพัฒนาสถาปัตยกรรมประเภทใหม่: เครือข่ายประสาทเทียมแบบเรียกซ้ำ (RNN ต่อไปนี้)

โครงข่ายประสาทเทียมที่เกิดซ้ำ (RNN) คืออะไร?

โครงข่ายประสาทเทียมแบบวนซ้ำ (Recurrent Neural Network หรือ RNN) เป็นโครงข่ายประสาทเทียมประเภทหนึ่งที่การเชื่อมต่อระหว่างโหนดต่างๆ ก่อให้เกิดกราฟแบบมีทิศทาง เพื่อให้ได้พฤติกรรมแบบไดนามิกตามเวลา ช่วยในการจำลองข้อมูลลำดับที่ได้มาจากโครงข่ายแบบส่งต่อ (feedforward networks) โดยทำงานคล้ายกับสมองของมนุษย์เพื่อให้ได้ผลลัพธ์ที่คาดการณ์ได้

โครงข่ายประสาทที่เกิดซ้ำมีลักษณะค่อนข้างคล้ายกับโครงข่ายประสาทเทียมแบบเดิม ยกเว้นว่ามีการเพิ่มสถานะหน่วยความจำลงในเซลล์ประสาท การคำนวณเพื่อรวมหน่วยความจำนั้นทำได้ง่าย

ลองนึกภาพแบบจำลองอย่างง่ายที่มีเพียงเซลล์ประสาทเดียวที่รับข้อมูลเป็นชุด ในโครงข่ายประสาทเทียมแบบดั้งเดิม แบบจำลองจะสร้างเอาต์พุตโดยการคูณอินพุตกับน้ำหนักและฟังก์ชันการกระตุ้น แต่ใน RNN เอาต์พุตนี้จะถูกส่งกลับไปยังตัวเองจำนวนครั้งหนึ่ง เราเรียกสิ่งนี้ว่า... ขั้นตอนเวลา ระยะเวลาที่ผลลัพธ์กลายเป็นอินพุตของการคูณเมทริกซ์ครั้งถัดไป

ตัวอย่างเช่น ในภาพด้านล่าง คุณจะเห็นว่าเครือข่ายประกอบด้วยเซลล์ประสาทหนึ่งอัน เครือข่ายจะคำนวณการคูณเมทริกซ์ระหว่างอินพุตและน้ำหนัก และเพิ่มความไม่เป็นเชิงเส้นด้วยฟังก์ชันการเปิดใช้งาน มันกลายเป็นเอาท์พุตที่ t-1 เอาต์พุตนี้เป็นอินพุตของการคูณเมทริกซ์ตัวที่สอง

นิวรอนแบบวนซ้ำเดี่ยวส่งเอาต์พุตที่ t-1 กลับไปเป็นอินพุตของการคูณครั้งถัดไป

โครงข่ายประสาทกำเริบ (RNN)

ด้านล่างนี้ เราเขียนโค้ด RNN แบบง่ายๆ ใน TensorFlow เพื่อทำความเข้าใจขั้นตอนและรูปร่างของเอาต์พุตด้วย

เครือข่ายประกอบด้วย:

  • สี่อินพุต
  • หกเซลล์ประสาท
  • ขั้นตอน 2 ครั้ง

เครือข่ายจะดำเนินการตามภาพด้านล่าง

RNN ที่คลี่ออกแล้ว มีอินพุตสี่ตัว นิวรอนหกตัว และขั้นตอนเวลาสองขั้นตอน

เครือข่ายนี้เรียกว่า 'แบบเรียกซ้ำ' เนื่องจากเครือข่ายดำเนินการแบบเดียวกันในแต่ละช่องเปิดใช้งาน เครือข่ายจะคำนวณน้ำหนักของอินพุตและเอาต์พุตก่อนหน้าเพื่อใช้ฟังก์ชันการเปิดใช้งาน

หมายเหตุเกี่ยวกับเวอร์ชัน: โค้ดในบทช่วยสอนนี้มุ่งเป้าไปที่ TensorFlow 1.x Graph API บน TensorFlow 2.x สคริปต์เดียวกันนี้ยังคงทำงานได้หลังจากนั้น import tensorflow.compat.v1 as tf บวก tf.disable_v2_behavior()เนื่องจากตัวแปร placeholder, เซสชัน และโมดูล rnn_cell ตอนนี้อยู่ภายใต้ tf.compat.v1สำนวนที่เทียบเท่าในเวอร์ชัน 2.x คือ tf.keras.layers.SimpleRNN.

import numpy as np
import tensorflow as tf
n_inputs = 4
n_neurons = 6
n_timesteps = 2
The data is a sequence of a number from 0 to 9 and divided into three batches of data.
## Data 
X_batch = np.array([
        [[0, 1, 2, 5], [9, 8, 7, 4]], # Batch 1
        [[3, 4, 5, 2], [0, 0, 0, 0]], # Batch 2
        [[6, 7, 8, 5], [6, 5, 4, 2]], # Batch 3
    ])

เราสามารถสร้างเครือข่ายที่มีตัวยึดตำแหน่งสำหรับข้อมูล ระยะการเกิดซ้ำ และเอาต์พุต

กำหนดตัวยึดตำแหน่งสำหรับข้อมูล

X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs])

ที่นี่:

  • ไม่มี: ไม่ทราบ และจะใช้ขนาดของชุดงาน
  • n_timesteps: จำนวนครั้งที่เครือข่ายจะส่งเอาต์พุตกลับไปยังเซลล์ประสาท
  • n_inputs: จำนวนอินพุตต่อแบตช์

กำหนดเครือข่ายที่เกิดซ้ำ

ดังที่ได้กล่าวไว้ในภาพด้านบน เครือข่ายประกอบด้วยเซลล์ประสาท 6 ตัว เครือข่ายจะคำนวณผลิตภัณฑ์ดอทสองอัน:

  • ป้อนข้อมูลด้วยน้ำหนักชุดแรก (เช่น 6: เท่ากับจำนวนเซลล์ประสาท)
  • ผลลัพธ์ก่อนหน้าพร้อมชุดน้ำหนักที่สอง (เช่น 6: ซึ่งสอดคล้องกับจำนวนผลลัพธ์)

โปรดทราบว่าในระหว่างการป้อนล่วงหน้าครั้งแรก ค่าของเอาต์พุตก่อนหน้าจะเท่ากับศูนย์เนื่องจากเราไม่มีค่าใดๆ ที่มีอยู่

วัตถุที่จะสร้าง RNN คือ tf.contrib.rnn.BasicRNNCell พร้อมอาร์กิวเมนต์ num_units เพื่อกำหนดจำนวนอินพุต

basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)

เมื่อกำหนดเครือข่ายแล้ว คุณสามารถคำนวณเอาต์พุตและสถานะได้

outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)

วัตถุนี้ใช้การวนซ้ำภายในเพื่อคูณเมทริกซ์ตามจำนวนครั้งที่เหมาะสม

โปรดสังเกตว่าเซลล์ประสาทแบบวนซ้ำเป็นฟังก์ชันของข้อมูลนำเข้าทั้งหมดจากช่วงเวลาที่ผ่านมา นี่คือวิธีการที่เครือข่ายสร้างหน่วยความจำของตัวเอง ข้อมูลจากช่วงเวลาที่ผ่านมาสามารถส่งต่อไปยังช่วงเวลาในอนาคตได้ นี่คือความมหัศจรรย์ของเครือข่ายประสาทแบบวนซ้ำ

## Define the shape of the tensor
X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs])
## Define the network
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
init = tf.global_variables_initializer()
init = tf.global_variables_initializer()
with tf.Session() as sess:
    init.run()
    outputs_val = outputs.eval(feed_dict={X: X_batch})
print(states.eval(feed_dict={X: X_batch}))
[[ 0.38941205 -0.9980438   0.99750966  0.7892596   0.9978241   0.9999997 ]
 [ 0.61096436  0.7255889   0.82977575 -0.88226104  0.29261455 -0.15597084]
 [ 0.62091285 -0.87023467  0.99729395 -0.58261937  0.9811445   0.99969864]]

เพื่อความเข้าใจง่ายขึ้น ลองพิมพ์ค่าของสถานะก่อนหน้าดู ผลลัพธ์ที่แสดงด้านบนคือผลลัพธ์จากสถานะสุดท้าย ทีนี้ลองพิมพ์ผลลัพธ์ทั้งหมดดู คุณจะสังเกตได้ว่าสถานะต่างๆ คือผลลัพธ์ก่อนหน้าของแต่ละชุดข้อมูล นั่นหมายความว่าผลลัพธ์ก่อนหน้าจะมีข้อมูลเกี่ยวกับลำดับทั้งหมด

print(outputs_val)    
print(outputs_val.shape)    
[[[-0.75934666 -0.99537754  0.9735819  -0.9722234  -0.14234993
   -0.9984044 ]
  [ 0.99975264 -0.9983206   0.9999993  -1.         -0.9997506
   -1.        ]]

 [[ 0.97486496 -0.98773265  0.9969686  -0.99950117 -0.7092863
   -0.99998885]
  [ 0.9326837   0.2673438   0.2808514  -0.7535883  -0.43337247
    0.5700631 ]]

 [[ 0.99628735 -0.9998728   0.99999213 -0.99999976 -0.9884324
   -1.        ]
  [ 0.99962527 -0.9467421   0.9997403  -0.99999714 -0.99929446
   -0.9999795 ]]]
(3, 2, 6)

ภาพแสดงรายละเอียดด้านล่างจะแยกข้อมูลเทนเซอร์ที่พิมพ์ออกมาชุดเดียวกันออกเป็นชุดย่อยๆ

เทนเซอร์เอาต์พุต RNN ที่มีคำอธิบายประกอบ ซึ่งติดป้ายกำกับชุดข้อมูลทั้งสามชุด เอาต์พุตของนิวรอนทั้งหก และรูปร่าง (3, 2, 6)

ผลลัพธ์มีรูปร่างเป็น (3, 2, 6):

  • 3: จำนวนแบทช์
  • 2: จำนวนการจับเวลา
  • 6: จำนวนเซลล์ประสาท

การเพิ่มประสิทธิภาพของโครงข่ายประสาทเทียมที่เกิดซ้ำนั้นเหมือนกับโครงข่ายประสาทเทียมแบบเดิม คุณจะเห็นรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการเพิ่มประสิทธิภาพโค้ดในส่วนถัดไปของบทช่วยสอน Recurrent Neural Network นี้

การประยุกต์ใช้ RNN

RNN มีประโยชน์หลายอย่าง โดยเฉพาะอย่างยิ่งเมื่อต้องทำนายอนาคต ในอุตสาหกรรมการเงิน RNN จะมีประโยชน์ในการทำนายราคาหุ้นหรือสัญญาณของทิศทางตลาดหุ้น (เช่น บวกหรือลบ)

RNN มีประโยชน์สำหรับรถยนต์ขับเคลื่อนอัตโนมัติเนื่องจากสามารถหลีกเลี่ยงอุบัติเหตุทางรถยนต์ได้ด้วยการคาดการณ์วิถีของยานพาหนะ

RNN ถูกนำมาใช้กันอย่างแพร่หลายในการวิเคราะห์ข้อความ คำบรรยายภาพ การวิเคราะห์ความรู้สึก และการแปลด้วยคอมพิวเตอร์ ตัวอย่างเช่น เราสามารถใช้บทวิจารณ์ภาพยนตร์เพื่อทำความเข้าใจความรู้สึกที่ผู้ชมรับรู้หลังจากดูภาพยนตร์ การทำงานอัตโนมัตินี้มีประโยชน์มากเมื่อบริษัทภาพยนตร์ไม่มีเวลาเพียงพอในการทบทวน ติดป้ายกำกับ รวบรวม และวิเคราะห์บทวิจารณ์ เครื่องจักรสามารถทำงานได้ด้วยความแม่นยำในระดับที่สูงขึ้น

ข้อจำกัดของ RNN

ตามทฤษฎีแล้ว RNN ควรจะส่งข้อมูลบ่อยครั้ง อย่างไรก็ตาม การเผยแพร่ข้อมูลทั้งหมดนี้ค่อนข้างท้าทายเมื่อขั้นตอนเวลายาวเกินไป เมื่อเครือข่ายมีชั้นลึกมากเกินไป จะไม่สามารถฝึกได้ ปัญหานี้เรียกว่า: ปัญหาการไล่ระดับสีที่หายไป- หากคุณจำได้ โครงข่ายประสาทเทียมจะอัปเดตน้ำหนักโดยใช้อัลกอริธึมการไล่ระดับสี การไล่ระดับสีจะเล็กลงเมื่อเครือข่ายคืบหน้าลงไปยังเลเยอร์ที่ต่ำกว่า

โดยสรุปแล้ว ค่าความชันจะคงที่ หมายความว่าไม่มีพื้นที่สำหรับการปรับปรุง โมเดลเรียนรู้จากการเปลี่ยนแปลงของค่าความชัน การเปลี่ยนแปลงนี้ส่งผลต่อเอาต์พุตของเครือข่าย อย่างไรก็ตาม หากความแตกต่างของค่าความชันน้อยเกินไป (เช่น น้ำหนักเปลี่ยนแปลงเล็กน้อย) เครือข่ายจะไม่สามารถเรียนรู้สิ่งใดได้เลย และเอาต์พุตก็จะผิดเพี้ยนไปด้วย ดังนั้น เครือข่ายที่เผชิญกับปัญหาค่าความชันหายไปจึงไม่สามารถลู่เข้าสู่คำตอบที่ดีได้

การปรับปรุง LSTM

เพื่อแก้ไขปัญหาการลดลงของค่าความชัน (vanishing gradient) ที่อาจเกิดขึ้นใน RNN นักวิจัยสามคน ได้แก่ Hochreiter, Schmidhuber และ Bengio ได้ปรับปรุง RNN ด้วยสถาปัตยกรรมที่เรียกว่า Long Short-Term Memory (LSTM) โดยสรุปแล้ว LSTM จะให้ข้อมูลในอดีตที่เกี่ยวข้องกับช่วงเวลาปัจจุบันแก่เครือข่าย เครื่องจักรจะใช้สถาปัตยกรรมที่ดีกว่าในการเลือกและส่งข้อมูลย้อนกลับไปยังช่วงเวลาในอนาคต

สถาปัตยกรรม LSTM มีให้ใช้งานใน TensorFlow โดยใช้ tf.contrib.rnn.LSTMCell LSTM อยู่นอกขอบเขตของบทช่วยสอนนี้ คุณสามารถศึกษาเพิ่มเติมได้จากเอกสารทางการ เอกสาร สำหรับข้อมูลเพิ่มเติม

ใน TensorFlow 2.x เซลล์นั้นจะเข้าถึงได้ดังนี้ tf.compat.v1.nn.rnn_cell.LSTMCellในขณะที่โค้ดใหม่โดยทั่วไปจะใช้ tf.keras.layers.LSTM หรือไฟแช็ก tf.keras.layers.GRUซึ่งให้ความแม่นยำเทียบเท่ากันโดยใช้พารามิเตอร์น้อยกว่า

RNN ในอนุกรมเวลา

ในบทเรียน TensorFlow RNN นี้ คุณจะได้ใช้ RNN กับข้อมูลอนุกรมเวลา อนุกรมเวลาขึ้นอยู่กับช่วงเวลาก่อนหน้า ซึ่งหมายความว่าค่าในอดีตมีข้อมูลที่เกี่ยวข้องที่เครือข่ายสามารถเรียนรู้ได้ แนวคิดเบื้องหลังการทำนายอนุกรมเวลาคือการประมาณค่าในอนาคตของอนุกรม เช่น ราคาหุ้น อุณหภูมิ GDP เป็นต้น

การเตรียมข้อมูลสำหรับ Keras RNN และไทม์ซีรีส์อาจค่อนข้างยุ่งยาก ก่อนอื่น เป้าหมายคือการทำนายค่าถัดไปของซีรีส์ ซึ่งหมายความว่า คุณจะใช้ข้อมูลในอดีตเพื่อประมาณค่าที่ t + 1 ป้ายกำกับจะเท่ากับลำดับอินพุตและเลื่อนไปข้างหน้าหนึ่งช่วง ประการที่สอง จำนวนอินพุตจะถูกตั้งเป็น 1 นั่นคือ หนึ่งการสังเกตต่อเวลา สุดท้าย ขั้นเวลาจะเท่ากับลำดับของค่าตัวเลข ตัวอย่างเช่น หากคุณตั้งขั้นเวลาเป็น 10 ลำดับอินพุตจะส่งคืนสิบครั้งติดต่อกัน

ดูกราฟด้านล่าง เราได้แสดงข้อมูลอนุกรมเวลาทางด้านซ้ายและลำดับอินพุตที่สมมติขึ้นทางด้านขวา คุณสร้างฟังก์ชันเพื่อส่งคืนชุดข้อมูลที่มีค่าสุ่มในแต่ละวันตั้งแต่เดือนมกราคม 2001 ถึงธันวาคม 2016

# To plot pretty figures
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
def create_ts(start = '2001', n = 201, freq = 'M'):
    rng = pd.date_range(start=start, periods=n, freq=freq)
    ts = pd.Series(np.random.uniform(-18, 18, size=len(rng)), rng).cumsum()
    return ts
ts= create_ts(start = '2001', n = 192, freq = 'M')
ts.tail(5)

เอาท์พุต

2016-08-31    -93.459631
2016-09-30    -95.264791
2016-10-31    -95.551935
2016-11-30   -105.879611
2016-12-31   -123.729319
Freq: M, dtype: float64
ts = create_ts(start = '2001', n = 222)

# Left
plt.figure(figsize=(11,4))
plt.subplot(121)
plt.plot(ts.index, ts)
plt.plot(ts.index[90:100], ts[90:100], "b-", linewidth=3, label="A training instance")
plt.title("A time series (generated)", fontsize=14)

# Right
plt.subplot(122)
plt.title("A training instance", fontsize=14)
plt.plot(ts.index[90:100], ts[90:100], "b-", markersize=8, label="instance")
plt.plot(ts.index[91:101], ts[91:101], "bo", markersize=10, label="target", markerfacecolor='red')
plt.legend(loc="upper left")
plt.xlabel("Time")

plt.show()

ด้านซ้ายคืออนุกรมเวลาที่สร้างขึ้น และด้านขวาคือตัวอย่างการฝึกฝนสิบขั้นตอนที่มีเป้าหมายที่เลื่อนไป

ส่วนด้านขวาของกราฟแสดงอนุกรมข้อมูลทั้งหมด โดยเริ่มตั้งแต่ปี 2001 และสิ้นสุดในปี 2019 การป้อนข้อมูลทั้งหมดลงในเครือข่ายโดยตรงนั้นไม่มีประโยชน์ ดังนั้น คุณควรสร้างชุดข้อมูลที่มีความยาวเท่ากับช่วงเวลา (time step) แทน ชุดข้อมูลนี้จะเป็นตัวแปร X ส่วนตัวแปร Y จะเหมือนกับตัวแปร X แต่เลื่อนไปหนึ่งช่วงเวลา (เช่น คุณต้องการพยากรณ์ค่า t+1)

เวกเตอร์ทั้งสองมีความยาวเท่ากัน คุณสามารถดูได้ในส่วนด้านขวาของกราฟด้านบน เส้นนี้แสดงถึงค่าสิบค่าของอินพุต X ในขณะที่จุดสีแดงคือค่าสิบค่าของป้ายกำกับ Y โปรดทราบว่าป้ายกำกับจะเริ่มต้นก่อน X หนึ่งช่วงและสิ้นสุดหนึ่งช่วงหลังจากนั้น

สร้าง RNN เพื่อทำนายอนุกรมเวลาใน TensorFlow

ในการฝึกอบรม RNN นี้ ถึงเวลาสร้าง RNN แรกของคุณเพื่อทำนายซีรีส์ข้างต้น คุณต้องระบุไฮเปอร์พารามิเตอร์บางตัว (พารามิเตอร์ของโมเดล เช่น จำนวนเซลล์ประสาท ฯลฯ) สำหรับโมเดล:

  • จำนวนอินพุต: 1
  • ช่วงเวลา (หน้าต่างในซีรีส์เวลา): 10
  • จำนวนเซลล์ประสาท: 120
  • จำนวนเอาต์พุต: 1

เครือข่ายของคุณจะเรียนรู้จากลำดับเหตุการณ์ 10 วัน และประกอบด้วยเซลล์ประสาทแบบวนซ้ำ 120 เซลล์ คุณป้อนข้อมูลให้กับโมเดลด้วยข้อมูลหนึ่งวัน คุณสามารถเปลี่ยนค่าต่างๆ เพื่อดูว่าโมเดลพัฒนาขึ้นหรือไม่

ก่อนสร้างแบบจำลอง คุณต้องแบ่งชุดข้อมูลออกเป็นชุดฝึกฝนและชุดทดสอบ ชุดข้อมูลทั้งหมดมีจุดข้อมูล 222 จุด คุณจะใช้จุดข้อมูล 201 จุดแรกในการฝึกฝนแบบจำลอง และใช้จุดข้อมูล 21 จุดสุดท้ายในการทดสอบแบบจำลองของคุณ

หลังจากกำหนดชุดข้อมูลฝึกฝนและชุดข้อมูลทดสอบแล้ว คุณต้องสร้างอ็อบเจ็กต์ที่มีแบทช์อยู่ภายใน ในแต่ละแบทช์จะมีค่า X และค่า Y โปรดจำไว้ว่าค่า X นั้นล่าช้าไปหนึ่งช่วงเวลา ดังนั้น คุณจึงใช้ข้อมูล 200 ตัวแรก และช่วงเวลาจะเท่ากับ 10 อ็อบเจ็กต์ X_batches ควรมี 20 แบทช์ ขนาด 10*1 ส่วนอ็อบเจ็กต์ y_batches มีรูปร่างเหมือนกับอ็อบเจ็กต์ X_batches แต่ล้ำหน้าไปหนึ่งช่วงเวลา

ขั้นตอน 1) สร้างรถไฟและทดสอบ

ก่อนอื่น คุณแปลงอนุกรมให้เป็น นำพาย อาร์เรย์ จากนั้นคุณกำหนดหน้าต่าง (เช่น จำนวนเวลาที่เครือข่ายจะเรียนรู้จาก) จำนวนอินพุต เอาต์พุต และขนาดของชุดฝึกตามที่แสดงในตัวอย่าง TensorFlow RNN ด้านล่าง

series = np.array(ts)
n_windows = 20   
n_input =  1
n_output = 1
size_train = 201

หลังจากนั้น คุณเพียงแค่แบ่งอาร์เรย์ออกเป็นสองชุดข้อมูล

## Split data
train = series[:size_train]
test = series[size_train:]
print(train.shape, test.shape)
(201,) (21,)

ขั้นตอน 2) สร้างฟังก์ชันเพื่อส่งคืน X_batches และ y_batches

เพื่อให้ง่ายขึ้น คุณสามารถสร้างฟังก์ชันที่ส่งคืนอาร์เรย์ที่แตกต่างกันสองชุด ชุดหนึ่งสำหรับ X_batches และอีกชุดหนึ่งสำหรับ y_batches

มาเขียนฟังก์ชัน RNN TensorFlow เพื่อสร้างแบตช์กัน

โปรดทราบว่าแบตช์ X จะล้าหลังหนึ่งช่วง (เราใช้ค่า t-1) ผลลัพธ์ของฟังก์ชันควรมีสามมิติ มิติแรกเท่ากับจำนวนแบตช์ มิติที่สองเท่ากับขนาดของหน้าต่าง และมิติสุดท้ายเท่ากับจำนวนอินพุต

ส่วนที่ยุ่งยากคือการเลือกจุดข้อมูลให้ถูกต้อง สำหรับจุดข้อมูล X คุณจะเลือกการสังเกตตั้งแต่ t = 1 ถึง t =200 ในขณะที่สำหรับจุดข้อมูล Y คุณจะคืนค่าการสังเกตจาก t = 2 ถึง 201 เมื่อคุณมีจุดข้อมูลที่ถูกต้องแล้ว ก็สามารถปรับเปลี่ยนรูปร่างใหม่ได้ทันที ซีรี่ย์.

หากต้องการสร้างออบเจ็กต์ด้วยแบทช์ คุณจะต้องแบ่งชุดข้อมูลออกเป็น 20 แบทช์ที่มีความยาวเท่ากัน (เช่น 1) คุณสามารถใช้วิธีการปรับรูปร่างใหม่และส่งผ่าน -20 เพื่อให้ซีรีส์คล้ายกับขนาดแบทช์ ค่า 1 คือจำนวนการสังเกตต่อชุด และ XNUMX คือจำนวนอินพุต

คุณต้องทำขั้นตอนเดียวกันแต่สำหรับฉลาก

โปรดทราบว่า คุณต้องเลื่อนข้อมูลให้ตรงกับจำนวนครั้งที่คุณต้องการพยากรณ์ ตัวอย่างเช่น หากคุณต้องการพยากรณ์ล่วงหน้า 1 วัน คุณจะต้องเลื่อนข้อมูลไป 1 ครั้ง หากคุณต้องการพยากรณ์ล่วงหน้า 2 วัน คุณจะต้องเลื่อนข้อมูลไป 2 ครั้ง

x_data = train[:size_train-1]: Select all the training instance minus one day
X_batches = x_data.reshape(-1, windows, input): create the right shape for the batch e.g (10, 20, 1)
def create_batches(df, windows, input, output):
    ## Create X         
        x_data = train[:size_train-1] # Select the data
        X_batches = x_data.reshape(-1, windows, input)  # Reshape the data 
    ## Create y
        y_data = train[n_output:size_train]
        y_batches = y_data.reshape(-1, windows, output)
        return X_batches, y_batches

เมื่อกำหนดฟังก์ชันแล้ว คุณสามารถเรียกฟังก์ชันนี้เพื่อสร้างแบตช์ได้ดังแสดงในตัวอย่าง RNN ด้านล่าง

X_batches, y_batches = create_batches(df = train,
                                      windows = n_windows,
                                      input = n_input,
                                      output = n_output)

คุณสามารถพิมพ์รูปร่างเพื่อให้แน่ใจว่าขนาดถูกต้อง

print(X_batches.shape, y_batches.shape)
(10, 20, 1) (10, 20, 1)

คุณต้องสร้างชุดทดสอบที่มีข้อมูลเพียงชุดเดียวและการสังเกต 20 รายการ

โปรดทราบว่าคุณคาดการณ์วันแล้ววันเล่า หมายความว่าค่าที่คาดการณ์ที่สองจะขึ้นอยู่กับค่าจริงของวันแรก (t+1) ของชุดข้อมูลทดสอบ ความจริงก็จะรู้คุณค่าที่แท้จริง

หากคุณต้องการพยากรณ์ t+2 (เช่น ล่วงหน้า 1 วัน) คุณต้องใช้ค่าพยากรณ์ t+3 หากคุณกำลังจะทำนาย t+1 (สามวันข้างหน้า) คุณจะต้องใช้ค่าทำนาย t+2 และ t+XNUMX มันสมเหตุสมผลแล้วที่เป็นการยากที่จะคาดการณ์ล่วงหน้าอย่างแม่นยำในช่วง t+n วันข้างหน้า

X_test, y_test = create_batches(df = test, windows = 20,input = 1, output = 1)
print(X_test.shape, y_test.shape)
(10, 20, 1) (10, 20, 1)

โอเค ขนาดแบตช์ของคุณพร้อมแล้ว คุณสามารถสร้างสถาปัตยกรรม RNN ได้ โปรดจำไว้ว่าคุณมีนิวรอนแบบเรียกซ้ำ 120 นิวรอน

ขั้นตอน 3) สร้างโมเดล

ในการสร้างโมเดล คุณต้องกำหนดสามส่วน:

  1. ตัวแปรที่มีเทนเซอร์
  2. อาร์เอ็นเอ็น
  3. การสูญเสียและการเพิ่มประสิทธิภาพ

ขั้นตอน 3.1) ตัวแปร

คุณต้องระบุตัวแปร X และ y ด้วยรูปร่างที่เหมาะสม ขั้นตอนนี้เป็นเรื่องเล็กน้อย เมตริกซ์มีมิติเดียวกันกับวัตถุ X_batches และ y_batches

ตัวอย่างเช่น เทนเซอร์ X เป็นตัวแทน (ตรวจสอบบทช่วยสอนเกี่ยวกับเรื่องนี้ได้ที่นี่) รู้เบื้องต้นเกี่ยวกับ TensorFlow เพื่อให้จิตใจสดชื่นกับการประกาศตัวแปร) มีสามมิติ คือ

  • หมายเหตุ: ขนาดของชุด
  • n_windows: ความยาวของหน้าต่าง กล่าวคือ จำนวนครั้งที่โมเดลมองย้อนกลับไป
  • n_input: จำนวนอินพุต

ผลลัพธ์คือ:

tf.placeholder(tf.float32, [None, n_windows, n_input])
## 1. Construct the tensors
X = tf.placeholder(tf.float32, [None, n_windows, n_input])   
y = tf.placeholder(tf.float32, [None, n_windows, n_output])

ขั้นตอน 3.2) สร้าง RNN

ในส่วนที่สองของตัวอย่าง RNN TensorFlow นี้ คุณจะต้องกำหนดสถาปัตยกรรมของเครือข่าย เช่นเดียวกับก่อนหน้านี้ คุณจะใช้ BasicRNNCell และ dynamic_rnn จากตัวประมาณค่า TensorFlow

## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)   
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)   

ส่วนถัดไปอาจซับซ้อนกว่าเล็กน้อย แต่ช่วยให้คำนวณได้เร็วขึ้น คุณต้องแปลงเอาต์พุตที่รันเป็นเลเยอร์หนาแน่น จากนั้นแปลงอีกครั้งเพื่อให้มีมิติเดียวกันกับอินพุต

stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron])          
stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output)       
outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])  

ขั้นตอน 3.3) สร้างการสูญเสียและการเพิ่มประสิทธิภาพ

การปรับแต่งโมเดลขึ้นอยู่กับงานที่คุณกำลังทำ ในบทเรียนก่อนหน้านี้เกี่ยวกับ ซีเอ็นเอ็นในบทเรียน RNN นี้ เป้าหมายของคุณคือการจำแนกภาพ แต่เป้าหมายจะแตกต่างออกไปเล็กน้อย คุณได้รับมอบหมายให้ทำนายค่าตัวแปรต่อเนื่อง เทียบกับการทำนายค่าของแต่ละคลาส

ความแตกต่างนี้มีความสำคัญเนื่องจากจะเปลี่ยนปัญหาการปรับให้เหมาะสม ปัญหาการหาค่าเหมาะที่สุดสำหรับตัวแปรต่อเนื่องคือการลดค่าคลาดเคลื่อนกำลังสองเฉลี่ยให้เหลือน้อยที่สุด หากต้องการสร้างหน่วยวัดเหล่านี้ใน TF คุณสามารถใช้:

tf.reduce_sum(tf.square(เอาท์พุต – y))

รหัส RNN ที่เหลือจะเหมือนเดิม คุณใช้เครื่องมือเพิ่มประสิทธิภาพ Adam เพื่อลดการสูญเสีย (เช่น MSE):

tf.train.AdamOptimizer(learning_rate=learning_rate)

เครื่องมือเพิ่มประสิทธิภาพ.ย่อเล็กสุด(ขาดทุน)

เพียงเท่านี้ คุณก็สามารถรวบรวมทุกอย่างเข้าด้วยกัน และโมเดลของคุณก็พร้อมที่จะฝึกแล้ว

tf.reset_default_graph()
r_neuron = 120    

## 1. Construct the tensors
X = tf.placeholder(tf.float32, [None, n_windows, n_input])   
y = tf.placeholder(tf.float32, [None, n_windows, n_output])

## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)   
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)              

stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron])          
stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output)       
outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])   

## 3. Loss + optimization
learning_rate = 0.001  
 
loss = tf.reduce_sum(tf.square(outputs - y))    
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)         
training_op = optimizer.minimize(loss)                                          

init = tf.global_variables_initializer() 

คุณจะฝึกโมเดลโดยใช้ 1500 ยุคและพิมพ์การสูญเสียทุกๆ 150 รอบ เมื่อโมเดลได้รับการฝึกฝนแล้ว คุณจะประเมินโมเดลบนชุดทดสอบและสร้างออบเจ็กต์ที่มีการคาดคะเนดังที่แสดงในตัวอย่าง Recurrent Neural Network ด้านล่าง

iteration = 1500 

with tf.Session() as sess:
    init.run()
    for iters in range(iteration):
        sess.run(training_op, feed_dict={X: X_batches, y: y_batches})
        if iters % 150 == 0:
            mse = loss.eval(feed_dict={X: X_batches, y: y_batches})
            print(iters, "\tMSE:", mse)
    
    y_pred = sess.run(outputs, feed_dict={X: X_test})
0 	MSE: 502893.34
150 	MSE: 13839.129
300 	MSE: 3964.835
450 	MSE: 2619.885
600 	MSE: 2418.772
750 	MSE: 2110.5923
900 	MSE: 1887.9644
1050 	MSE: 1747.1377
1200 	MSE: 1556.3398
1350 	MSE: 1384.6113

ในที่สุดในบทช่วยสอนการเรียนรู้เชิงลึกของ RNN นี้ คุณสามารถพล็อตมูลค่าที่แท้จริงของชุดข้อมูลด้วยค่าที่คาดการณ์ไว้ได้ หากแบบจำลองของคุณได้รับการแก้ไข ค่าที่คาดการณ์ไว้ควรวางทับค่าจริง

อย่างที่คุณเห็น โมเดลนี้ยังสามารถปรับปรุงได้อีกมาก ขึ้นอยู่กับคุณที่จะเปลี่ยนแปลงพารามิเตอร์ต่างๆ เช่น ช่วงเวลา (windows), ขนาดชุดข้อมูล (batch size) หรือจำนวนเซลล์ประสาทแบบวนซ้ำ (recurrent neurons)

plt.title("Forecast vs Actual", fontsize=14)
plt.plot(pd.Series(np.ravel(y_test)), "bo", markersize=8, label="Actual", color='green')
plt.plot(pd.Series(np.ravel(y_pred)), "r.", markersize=8, label="Forecast", color='red')
plt.legend(loc="lower left")
plt.xlabel("Time")

plt.show()

กราฟแสดงการเปรียบเทียบระหว่างผลการพยากรณ์และผลลัพธ์จริง โดยจุดสีเขียวคือจุดแสดงผลลัพธ์จริง และจุดสีแดงคือจุดที่คาดการณ์ไว้

การคาดการณ์เทียบกับจริง

คำถามที่พบบ่อย

เริ่มต้นด้วยหน้าต่างที่ครอบคลุมหนึ่งรอบเต็มของรูปแบบ จากนั้นปรับแต่ง หน้าต่างที่ยาวขึ้นจะเก็บบริบทได้มากขึ้น แต่จะทำให้การฝึกฝนช้าลงและทำให้การลดลงของความชันแย่ลง สิบขั้นตอนเหมาะสำหรับอนุกรมรายวันในที่นี้ ฤดูกาลรายสัปดาห์มักต้องการมากกว่านั้น

ใช่ครับ โดยใช้ shim แทนที่การนำเข้าด้วย tensorflow.compat.v1 และเรียก disable_v2_behavior() เพราะ placeholder, session และ rnn_cell ย้ายไปอยู่ภายใต้ namespace compat แล้ว อัปเดตใหม่ครับ TensorFlow โปรเจ็กต์ควรใช้ tf.keras.layers.SimpleRNN แทน

ผู้ช่วย AI จะแนะนำขนาดหน้าต่าง ตรวจจับความไม่ตรงกันของรูปร่างก่อนเริ่มการทำงาน และร่างโค้ดสำหรับการโหลดข้อมูลจากโครงสร้างข้อมูล พวกมันช่วยลดขั้นตอนการลองผิดลองถูกซึ่งเป็นส่วนสำคัญของงานโมเดลแบบวนซ้ำ แม้ว่าการเลือกพารามิเตอร์ขั้นสุดท้ายยังคงต้องได้รับการตรวจสอบความถูกต้องบนข้อมูลที่แยกไว้ต่างหากก็ตาม

Copilot จัดการกับโครงสร้างซ้ำๆ ได้ดี เช่น บล็อกเซสชัน พจนานุกรมฟีด และการพิมพ์ค่าความสูญเสียเป็นระยะๆ รูปทรงเทนเซอร์และการเลื่อนป้ายกำกับยังคงเป็นส่วนที่มีความเสี่ยง ดังนั้นควรตรวจสอบขนาดของชุดข้อมูลด้วยตนเองก่อนที่จะเชื่อถือลูปที่สร้างขึ้น

เลเยอร์แบบวนซ้ำคาดหวัง (ชุดข้อมูล, ขั้นตอนเวลา, คุณลักษณะ) การปรับรูปร่างเป็น (-1, หน้าต่าง, อินพุต) จะบอกเฟรมเวิร์กว่ามีลำดับอยู่กี่ลำดับ แต่ละลำดับย้อนกลับไปไกลแค่ไหน และมีค่าเข้ามากี่ค่าในแต่ละขั้นตอน มิติที่ขาดหายไปจะทำให้เกิดข้อผิดพลาดลำดับ

LSTM ใช้เกตสามตัวบวกสถานะเซลล์แยกต่างหาก ในขณะที่ GRU รวมเกตลืมและเกตอินพุตเข้าด้วยกันและตัดสถานะเซลล์ออกไป GRU ฝึกฝนได้เร็วกว่าด้วยพารามิเตอร์ที่น้อยกว่า ในขณะที่ LSTM มักจะทำได้ดีกว่าในกรณีที่มีความสัมพันธ์กันในระยะยาวมาก

การพยากรณ์แบบต่อเนื่องจะลดค่าความคลาดเคลื่อนกำลังสองเฉลี่ยให้เหลือน้อยที่สุด ดังที่ตัวอย่างนี้แสดงให้เห็นโดยใช้ reduce_sum of squared residuals การจำแนกประเภทลำดับ เช่น ป้ายกำกับความรู้สึก หรือ งานเกี่ยวกับรูปภาพที่กล่าวถึงใน ซีเอ็นเอ็น บทช่วยสอนนี้ใช้เอนโทรปีแบบไขว้แทน

โมเดล Transformer มีบทบาทสำคัญในงานด้านภาษาศาสตร์ เนื่องจากกลไก Attention สามารถทำงานแบบขนานกันได้ตลอดทั้งลำดับ ในขณะที่โมเดล Recurrent ยังคงมีความสามารถในการแข่งขันสูงสำหรับสัญญาณแบบสตรีมมิ่ง การพยากรณ์แบบตัวแปรเดียวในระยะเวลาสั้นๆ และอุปกรณ์ที่มีหน่วยความจำต่ำ ซึ่งการประมวลผลทีละขั้นตอนเป็นข้อได้เปรียบมากกว่าเป็นอุปสรรค

สรุปโพสต์นี้ด้วย: