จากการแก้สมการสู่การเรียนรู้เชิงลึก: บทช่วยสอน TensorFlow Python
เผยแพร่แล้ว: 2022-03-11เมื่อเร็ว ๆ นี้มีการพัฒนาที่น่าทึ่งบางอย่างในโลกของปัญญาประดิษฐ์ตั้งแต่ความคืบหน้าในการเผยแพร่อย่างมากกับรถยนต์ที่ขับด้วยตนเองไปจนถึงเครื่องจักรที่แต่งเลียนแบบโชแปงหรือเพียงแค่เก่งในวิดีโอเกม
ศูนย์กลางของความก้าวหน้าเหล่านี้คือเครื่องมือจำนวนหนึ่งที่ช่วยให้เกิดการเรียนรู้เชิงลึกและโมเดลการเรียนรู้ของเครื่องอื่น ๆ โดยมี Torch, Caffe และ Theano อยู่ข้างหน้า อย่างไรก็ตาม เนื่องจาก Google Brain เปิดตัวโอเพ่นซอร์สในเดือนพฤศจิกายน 2558 ด้วยเฟรมเวิร์ก TensorFlow เราจึงเห็นความนิยมของไลบรารีซอฟต์แวร์นี้พุ่งสูงขึ้นเป็นเฟรมเวิร์กการเรียนรู้เชิงลึกที่ได้รับความนิยมมากที่สุด
ทำไมสิ่งนี้จึงเกิดขึ้น? เหตุผลต่างๆ ได้แก่ การสนับสนุนและเอกสารที่มีอยู่มากมาย ความพร้อมในการผลิต ความง่ายในการกระจายการคำนวณในอุปกรณ์ต่างๆ และเครื่องมือสร้างภาพที่ยอดเยี่ยม: TensorBoard
ในท้ายที่สุด TensorFlow สามารถรวมชุดคุณสมบัติทางเทคนิคที่ครอบคลุมและยืดหยุ่นได้ด้วยการใช้งานง่ายมาก
ในบทความนี้ คุณจะเข้าใจกลไกของเครื่องมือนี้โดยใช้เครื่องมือนี้ในการแก้ปัญหาเชิงตัวเลขทั่วไป ซึ่งอยู่นอกเหนือสิ่งที่การเรียนรู้ของเครื่องมักเกี่ยวข้อง ก่อนที่จะแนะนำการใช้งานในการเรียนรู้เชิงลึกด้วยการใช้โครงข่ายประสาทเทียมอย่างง่าย
ก่อนที่คุณจะเริ่มต้น
ถือว่ามีความรู้พื้นฐานเกี่ยวกับวิธีการเรียนรู้ของเครื่อง หากคุณต้องการติดตามตรวจสอบโพสต์ที่มีประโยชน์มากนี้
ในขณะที่เราจะสาธิต Python API ความเข้าใจเกี่ยวกับ Numpy ก็มีประโยชน์เช่นกัน
ในการตั้งค่า TensorFlow โปรดปฏิบัติตามคำแนะนำที่นี่
หากคุณกำลังใช้ Windows ควรสังเกตว่า ณ เวลาที่เขียน คุณต้องใช้ Python 3.4+ ไม่ใช่ 2.7
เมื่อคุณพร้อม คุณควรสามารถนำเข้าไลบรารีด้วย:
import tensorflow as tf
ขั้นตอนที่ 1 จาก 2 ไปยังโซลูชัน TensorFlow: สร้างกราฟ
การสร้างโปรแกรม TensorFlow โดยทั่วไปประกอบด้วยสองขั้นตอนหลัก ขั้นแรกคือการสร้างกราฟการคำนวณ ซึ่งจะอธิบายการคำนวณที่คุณต้องการดำเนินการ แต่ไม่ได้ดำเนินการจริงหรือเก็บค่าใดๆ
เช่นเดียวกับกราฟอื่นๆ เรามีโหนดและขอบ ขอบเป็นตัวแทนของเทนเซอร์ เทนเซอร์แทนอาร์เรย์ n มิติ ตัวอย่างเช่น เทนเซอร์ที่มีมิติ (หรืออันดับใน TensorFlow speak) 0 คือสเกลาร์ อันดับ 1 ของเวกเตอร์ อันดับ 2 เมทริกซ์ และอื่นๆ
โหนดแสดงถึงการดำเนินการที่สร้างเทนเซอร์เอาต์พุต โดยใช้เทนเซอร์เป็นอินพุต หากจำเป็น การดำเนินการดังกล่าวรวมถึงการเพิ่มเติม ( tf.add
) การคูณเมทริกซ์ ( tf.matmul
) และการสร้างค่าคงที่ ( tf.constant
)
ลองรวมสองสามสิ่งเหล่านี้สำหรับกราฟแรกของเรา
a = tf.constant([2.5, 2]) b = tf.constant([3, 6], dtype=tf.float32) total = tf.add(a, b)
ที่นี่ เราได้สร้างการดำเนินการสามรายการ สองการดำเนินการเพื่อสร้างอาร์เรย์ 1-d คงที่
ชนิดข้อมูลจะอนุมานจากอาร์กิวเมนต์ค่าที่ส่งเข้ามา หรือคุณสามารถระบุค่าเหล่านี้ด้วยอาร์กิวเมนต์ dtype
ถ้าฉันไม่ได้ทำสิ่งนี้สำหรับ b
แล้ว int32
จะได้รับการอนุมานและมีข้อผิดพลาดเกิดขึ้นเนื่องจาก tf.add
จะพยายามกำหนดการเพิ่มในสองประเภทที่แตกต่างกัน
ขั้นตอนที่ 2 จาก 2 ไปยังโซลูชัน TensorFlow: ดำเนินการ Operations
กราฟถูกกำหนดไว้แล้ว แต่เพื่อที่จะคำนวณได้จริง (หรือส่วนใดส่วนหนึ่งของกราฟ) เราต้องตั้งค่าเซสชัน TensorFlow
sess = tf.Session()
อีกทางหนึ่ง หากเรากำลังเรียกใช้เซสชันในเชลล์แบบโต้ตอบ เช่น IPython เราจะใช้:
sess = tf.InteractiveSession()
วิธีการ run
บนวัตถุเซสชันเป็นวิธีหนึ่งในการประเมินเทนเซอร์
ดังนั้น ในการประเมินการคำนวณการบวกที่กำหนดไว้ข้างต้น เราจึงส่งผ่าน 'ผลรวม' ซึ่งเป็นเมตริกซ์ที่จะดึงข้อมูล ซึ่งแสดงถึงผลลัพธ์ของ tf.add
op
print(sess.run(total)) # [ 5.5 8. ]
ณ จุดนี้ เราขอแนะนำคลาสตัวแปรของ TensorFlow ในขณะที่ค่าคงที่เป็นส่วนคงที่ของคำจำกัดความของกราฟ ตัวแปรสามารถอัปเดตได้ ตัวสร้างคลาสต้องการค่าเริ่มต้น แต่ถึงอย่างนั้น ตัวแปรก็จำเป็นต้องมีการดำเนินการเพื่อเริ่มต้นพวกมันอย่างชัดเจนก่อนที่จะดำเนินการอื่นใดกับพวกมัน
ตัวแปรถือสถานะของกราฟในเซสชันใดเซสชันหนึ่ง ดังนั้นเราควรสังเกตว่าเกิดอะไรขึ้นกับหลายเซสชันโดยใช้กราฟเดียวกันเพื่อให้เข้าใจตัวแปรได้ดียิ่งขึ้น
# Create a variable with an initial value of 1 some_var = tf.Variable(1) # Create op to run variable initializers init_op = tf.global_variables_initializer() # Create an op to replace the value held by some_var to 3 assign_op = some_var.assign(3) # Set up two instances of a session sess1 = tf.Session() sess2 = tf.Session() # Initialize variables in both sessions sess1.run(init_op) sess2.run(init_op) print(sess1.run(some_var)) # Outputs 1 # Change some_var in session1 sess1.run(assign_op) print(sess1.run(some_var)) # Outputs 3 print(sess2.run(some_var)) # Outputs 1 # Close sessions sess1.close() sess2.close()
เราได้ตั้งค่ากราฟและสองเซสชัน
หลังจากดำเนินการเริ่มต้นในทั้งสองเซสชัน (หากเราไม่เรียกใช้สิ่งนี้แล้วประเมินตัวแปรที่เราพบข้อผิดพลาด) เราจะดำเนินการ assign op ในเซสชันเดียวเท่านั้น อย่างที่เห็น ค่าตัวแปรจะคงอยู่ แต่ไม่ข้ามเซสชัน
การป้อนกราฟเพื่อแก้ไขปัญหาเชิงตัวเลข
แนวคิดที่สำคัญอีกประการของ TensorFlow คือตัวยึดตำแหน่ง ในขณะที่ตัวแปรมีสถานะ ตัวยึดตำแหน่งจะถูกใช้เพื่อกำหนดว่าอินพุตใดที่กราฟสามารถคาดหวังและประเภทข้อมูล (และรูปร่างของตัวแปรหรือไม่ก็ได้) จากนั้นเราสามารถป้อนข้อมูลลงในกราฟโดยใช้ตัวยึดตำแหน่งเหล่านี้เมื่อเราเรียกใช้การคำนวณ
กราฟ TensorFlow เริ่มคล้ายกับโครงข่ายประสาทที่เราต้องการฝึกในที่สุด แต่ก่อนหน้านั้น ให้ใช้แนวคิดนี้ในการแก้ปัญหาเชิงตัวเลขทั่วไปจากโลกการเงิน
สมมติว่าเราต้องการหา y
ในสมการดังนี้:
สำหรับ v
ที่กำหนด (โดยมีค่าคงที่ C
และ P
)
นี่คือสูตรสำหรับคำนวณผลตอบแทนจนครบกำหนด ( y
) ของพันธบัตรที่มีมูลค่าตลาด v
, เงินต้น P
และคูปอง C
ที่จ่ายทุกครึ่งปี แต่ด้วยกระแสเงินสดคิดลดด้วยการทบต้นอย่างต่อเนื่อง
โดยพื้นฐานแล้วเราต้องแก้สมการแบบนี้ด้วยการลองผิดลองถูก และเราจะเลือกวิธีสองส่วนให้เป็นศูนย์ในค่าสุดท้ายสำหรับ y
อันดับแรก เราจะจำลองปัญหานี้เป็นกราฟ TensorFlow
C
และ P
เป็นค่าคงที่คงที่และเป็นส่วนหนึ่งของคำจำกัดความของกราฟของเรา เราต้องการให้มีกระบวนการที่ปรับแต่งขอบเขตล่างและบนของ y
ดังนั้นขอบเขตเหล่านี้ (แสดงว่า a
และ b
) เป็นตัวเลือกที่ดีสำหรับตัวแปรที่ต้องเปลี่ยนหลังจากการเดา y
แต่ละครั้ง (ถือเป็นจุดกึ่งกลางของ a
และ b
)
# Specify the values our constant ops will output C = tf.constant(5.0) P = tf.constant(100.0) # We specify the initial values that our lower and upper bounds will be when initialised. # Obviously the ultimate success of this algorithm depends on decent start points a = tf.Variable(-10.0) b = tf.Variable(10.0) # We expect a floating number to be inserted into the graph v_target = tf.placeholder("float") # Remember the following operations are definitions, # none are carried out until an operation is evaluated in a session! y = (a+b)/2 v_guess = C*tf.exp(-0.5*y) + C*tf.exp(-y) + C*tf.exp(-1.5*y) + (C + P)*tf.exp(-2*y) # Operations to set temporary values (a_ and b_) intended to be the next values of a and b. # eg if the guess results in av greater than the target v, # we will set a_ to be the current value of y a_ = tf.where(v_guess > v_target, y, a) b_ = tf.where(v_guess < v_target, y, b) # The last stage of our graph is to assign the two temporary vals to our variables step = tf.group( a.assign(a_), b.assign(b_) )
ตอนนี้เรามีรายการของการดำเนินการและตัวแปร ซึ่งสามารถประเมินกับเซสชันใดเซสชันหนึ่งได้ การดำเนินการเหล่านี้บางส่วนอาศัยการดำเนินการอื่นๆ ดังนั้นการวิ่ง พูด v_guess
จะสร้างปฏิกิริยาลูกโซ่เพื่อให้มีเทนเซอร์อื่นๆ เช่น C
และ P
มาประเมินก่อน
การดำเนินการเหล่านี้บางส่วนอาศัยตัวยึดตำแหน่งซึ่งจำเป็นต้องระบุค่า แล้วเราจะป้อนค่านั้นจริง ๆ ได้อย่างไร

สิ่งนี้ทำได้ผ่านอาร์กิวเมนต์ feed_dict
ในฟังก์ชันการ run
เอง
หากเราต้องการประเมิน a_
เราจะเสียบค่าสำหรับตัวยึดตำแหน่ง v_target
ของเรา เช่น:
sess.run(a_, feed_dict={v_target: 100})
ให้เรา 0.0
เสียบ v_target
ที่ 130 แล้วเราจะได้ -10.0
เป็นการดำเนินการ "ขั้นตอน" ของเราซึ่งจริง ๆ แล้วต้องมีการดำเนินการอื่น ๆ ทั้งหมดเป็นข้อกำหนดเบื้องต้นและดำเนินการตามกราฟทั้งหมด นอกจากนี้ยังเป็นการดำเนินการที่เปลี่ยนสถานะจริงในเซสชันของเราด้วย ดังนั้น ยิ่งเราดำเนินการตามขั้นตอนมากเท่าไร เรายิ่งเขยิบตัวแปร a
และ b
เข้าหาค่าที่แท้จริงของ y
มากขึ้นเท่านั้น
สมมุติว่าค่า v
ของเราในสมการเท่ากับ 95 มาตั้งค่าเซสชั่นและรันกราฟของเรา 100 ครั้ง
# Set up a session and initialize variables sess = tf.Session() tf.global_variables_initializer().run() # Run the step operation (and therefore whole graph) 100 times for i in range (100): sess.run(step, feed_dict={v_target:95})
ถ้าเราประเมินเทนเซอร์ y
ตอนนี้ เราจะได้คำตอบที่คล้ายคลึงกัน
print(sess.run(y)) # 0.125163
โครงข่ายประสาทเทียม
ตอนนี้ เรามีความเข้าใจเกี่ยวกับกลไกของ TensorFlow แล้ว เราก็สามารถนำสิ่งนี้ไปรวมกับการดำเนินการเพิ่มเติมของการเรียนรู้ด้วยเครื่องที่สร้างขึ้นใน TensorFlow เพื่อฝึกอบรมโครงข่ายประสาทเทียมอย่างง่าย
ในที่นี้ เราต้องการจัดประเภทจุดข้อมูลบนระบบพิกัด 2 มิติ โดยขึ้นอยู่กับว่าจุดเหล่านั้นอยู่ในพื้นที่ใดภูมิภาคหนึ่ง—วงกลมรัศมี 0.5 ที่มีศูนย์กลางอยู่ที่จุดกำเนิด
แน่นอนว่าสิ่งนี้สามารถตรวจสอบได้อย่างชัดเจนโดยเพียงแค่ตรวจสอบจุดที่กำหนด (a,b)
ถ้า a^2 + b^2 < 0.5
แต่สำหรับจุดประสงค์ของการทดลองแมชชีนเลิร์นนิงนี้ เราต้องการส่งผ่านใน ชุดฝึกอบรม: ชุดของคะแนนสุ่มและไม่ว่าจะอยู่ในพื้นที่ที่เราตั้งใจไว้หรือไม่ นี่เป็นวิธีหนึ่งในการสร้างสิ่งนี้:
import numpy as np NO_OF_RANDOM_POINTS = 100 CIRCLE_RADIUS = 0.5 random_spots = np.random.rand(NO_OF_RANDOM_POINTS, 2) * 2 - 1 is_inside_circle = (np.power(random_spots[:,0],2) + np.power(random_spots[:,1],2) < CIRCLE_RADIUS).astype(int)
เราจะสร้างโครงข่ายประสาทเทียมที่มีคุณสมบัติดังต่อไปนี้:
- ประกอบด้วยชั้นอินพุตที่มีสองโหนด ซึ่งเราป้อนชุดเวกเตอร์สองมิติของเราที่อยู่ภายใน "random_spots" ซึ่งจะแสดงโดยตัวยึดตำแหน่งที่รอข้อมูลการฝึกอบรม
- เลเยอร์เอาต์พุตจะมีสองโหนดด้วย ดังนั้นเราจำเป็นต้องป้อนชุดป้ายกำกับการฝึกอบรม (“is_inside_circle”) ลงในตัวยึดตำแหน่งสำหรับสเกลาร์ แล้วแปลงค่าเหล่านั้นเป็นเวกเตอร์สองมิติแบบร้อนเดียว
- เราจะมีหนึ่งเลเยอร์ที่ซ่อนอยู่ซึ่งประกอบด้วยสามโหนด ดังนั้น เราจะต้องใช้ตัวแปรสำหรับเมทริกซ์ตุ้มน้ำหนักและเวกเตอร์อคติของเรา เนื่องจากสิ่งเหล่านี้เป็นค่าที่ต้องปรับปรุงเมื่อทำการฝึก
INPUT_LAYER_SIZE = 2 HIDDEN_LAYER_SIZE = 3 OUTPUT_LAYER_SIZE = 2 # Starting values for weights and biases are drawn randomly and uniformly from [-1, 1] # For example W1 is a matrix of shape 2x3 W1 = tf.Variable(tf.random_uniform([INPUT_LAYER_SIZE, HIDDEN_LAYER_SIZE], -1, 1)) b1 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE], -1, 1)) W2 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE, OUTPUT_LAYER_SIZE], -1, 1)) b2 = tf.Variable(tf.random_uniform([OUTPUT_LAYER_SIZE], -1, 1)) # Specifying that the placeholder X can expect a matrix of 2 columns (but any number of rows) # representing random spots X = tf.placeholder(tf.float32, [None, INPUT_LAYER_SIZE]) # Placeholder Y can expect integers representing whether corresponding point is in the circle # or not (no shape specified) Y = tf.placeholder(tf.uint8) # An op to convert to a one hot vector onehot_output = tf.one_hot(Y, OUTPUT_LAYER_SIZE)
เพื่อให้คำจำกัดความของกราฟสมบูรณ์ เราจึงกำหนดการดำเนินการบางอย่างซึ่งจะช่วยให้เราฝึกตัวแปรเพื่อเข้าถึงตัวแยกประเภทที่ดีขึ้น ซึ่งรวมถึงการคำนวณเมทริกซ์ ฟังก์ชันการเปิดใช้งาน และตัวเพิ่มประสิทธิภาพ
LEARNING_RATE = 0.01 # Op to perform matrix calculation X*W1 + b1 hidden_layer = tf.add(tf.matmul(X, W1), b1) # Use sigmoid activation function on the outcome activated_hidden_layer = tf.sigmoid(hidden_layer) # Apply next weights and bias (W2, b2) to hidden layer and then apply softmax function # to get our output layer (each vector adding up to 1) output_layer = tf.nn.softmax(tf.add(tf.matmul(activated_hidden_layer, W2), b2)) # Calculate cross entropy for our loss function loss = -tf.reduce_sum(onehot_output * tf.log(output_layer)) # Use gradient descent optimizer at specified learning rate to minimize value given by loss tensor train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)
เมื่อตั้งค่ากราฟของเราแล้ว ก็ถึงเวลาตั้งค่าเซสชันและเรียกใช้ “train_step” (ซึ่งเรียกใช้การดำเนินการที่จำเป็นทั้งหมดด้วย) ops เหล่านี้บางส่วนใช้ตัวยึดตำแหน่ง ดังนั้นจึงต้องระบุค่าสำหรับสิ่งเหล่านั้น ขั้นตอนการฝึกอบรมนี้แสดงถึงหนึ่งยุคในอัลกอริธึมการเรียนรู้ของเรา และด้วยเหตุนี้ จึงวนรอบจำนวนยุคที่เราต้องการเรียกใช้ เราสามารถเรียกใช้ส่วนอื่นๆ ของกราฟได้ เช่น เทนเซอร์ "ขาดทุน" เพื่อจุดประสงค์ในการให้ข้อมูล
EPOCH_COUNT = 1000 sess = tf.Session() tf.global_variables_initializer().run() for i in range(EPOCH_COUNT): if i%100 == 0: print('Loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle}))) sess.run(train_step, feed_dict={X: random_spots, Y: is_inside_circle}) print('Final loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle})))
เมื่อเราฝึกอัลกอริทึมแล้ว เราสามารถฟีดในจุดหนึ่งและรับผลลัพธ์ของโครงข่ายประสาทเทียมดังนี้:
sess.run(output_layer, feed_dict={X: [[1, 1]]}) # Hopefully something close to [1, 0] sess.run(output_layer, feed_dict={X: [[0, 0]]}) # Hopefully something close to [0, 1]
เราสามารถจำแนกจุดออกจากวงกลมได้หากสมาชิกตัวแรกของเวกเตอร์เอาท์พุตมีค่ามากกว่า 0.5 ไม่เช่นนั้นจะอยู่ภายใน
โดยการรัน output_layer
tensor สำหรับหลาย ๆ จุด เราจะได้รับแนวคิดว่าผู้เรียนมองเห็นภูมิภาคที่มีคะแนนที่จำแนกในเชิงบวกได้อย่างไร มันคุ้มค่าที่จะลองเล่นกับขนาดของชุดการฝึก อัตราการเรียนรู้ และพารามิเตอร์อื่นๆ เพื่อดูว่าเราจะเข้าใกล้วงกลมที่เราตั้งใจไว้ได้แค่ไหน
อัตราการเรียนรู้: 0.01
ยุค: 1,000
อัตราการเรียนรู้: 0.01
ยุค: 1,000
อัตราการเรียนรู้: 0.01
ยุค: 10000
อัตราการเรียนรู้: 0.001
ยุค: 10000
สรุป
นี่เป็นบทเรียนที่ดีที่ชุดการฝึกอบรมที่เพิ่มขึ้นหรือจำนวนยุคไม่รับประกันว่าผู้เรียนที่ดีควรปรับอัตราการเรียนรู้อย่างเหมาะสม
หวังว่าการสาธิตเหล่านี้จะทำให้คุณเข้าใจถึงหลักการสำคัญของ TensorFlow และให้รากฐานที่มั่นคงในการนำเทคนิคที่ซับซ้อนขึ้นไปใช้
เราไม่ได้กล่าวถึงแนวคิดต่างๆ เช่น Tensorboard หรือการฝึกโมเดลของเราใน GPU แต่สิ่งเหล่านี้ครอบคลุมอย่างดีในเอกสารประกอบของ TensorFlow คุณสามารถหาสูตรอาหารจำนวนหนึ่งได้ในเอกสารประกอบที่สามารถช่วยให้คุณรับมือกับงานการเรียนรู้เชิงลึกที่น่าตื่นเต้นโดยใช้กรอบงานอันทรงพลังนี้!