จากการแก้สมการสู่การเรียนรู้เชิงลึก: บทช่วยสอน TensorFlow Python

เผยแพร่แล้ว: 2022-03-11

เมื่อเร็ว ๆ นี้มีการพัฒนาที่น่าทึ่งบางอย่างในโลกของปัญญาประดิษฐ์ตั้งแต่ความคืบหน้าในการเผยแพร่อย่างมากกับรถยนต์ที่ขับด้วยตนเองไปจนถึงเครื่องจักรที่แต่งเลียนแบบโชแปงหรือเพียงแค่เก่งในวิดีโอเกม

ศูนย์กลางของความก้าวหน้าเหล่านี้คือเครื่องมือจำนวนหนึ่งที่ช่วยให้เกิดการเรียนรู้เชิงลึกและโมเดลการเรียนรู้ของเครื่องอื่น ๆ โดยมี Torch, Caffe และ Theano อยู่ข้างหน้า อย่างไรก็ตาม เนื่องจาก Google Brain เปิดตัวโอเพ่นซอร์สในเดือนพฤศจิกายน 2558 ด้วยเฟรมเวิร์ก TensorFlow เราจึงเห็นความนิยมของไลบรารีซอฟต์แวร์นี้พุ่งสูงขึ้นเป็นเฟรมเวิร์กการเรียนรู้เชิงลึกที่ได้รับความนิยมมากที่สุด

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 = Ce -0.5y + Ce -y +Ce -1.5y +(C+P)e -2y

สำหรับ 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)

เราจะสร้างโครงข่ายประสาทเทียมที่มีคุณสมบัติดังต่อไปนี้:

  1. ประกอบด้วยชั้นอินพุตที่มีสองโหนด ซึ่งเราป้อนชุดเวกเตอร์สองมิติของเราที่อยู่ภายใน "random_spots" ซึ่งจะแสดงโดยตัวยึดตำแหน่งที่รอข้อมูลการฝึกอบรม
  2. เลเยอร์เอาต์พุตจะมีสองโหนดด้วย ดังนั้นเราจำเป็นต้องป้อนชุดป้ายกำกับการฝึกอบรม (“is_inside_circle”) ลงในตัวยึดตำแหน่งสำหรับสเกลาร์ แล้วแปลงค่าเหล่านั้นเป็นเวกเตอร์สองมิติแบบร้อนเดียว
  3. เราจะมีหนึ่งเลเยอร์ที่ซ่อนอยู่ซึ่งประกอบด้วยสามโหนด ดังนั้น เราจะต้องใช้ตัวแปรสำหรับเมทริกซ์ตุ้มน้ำหนักและเวกเตอร์อคติของเรา เนื่องจากสิ่งเหล่านี้เป็นค่าที่ต้องปรับปรุงเมื่อทำการฝึก
 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 สำหรับหลาย ๆ จุด เราจะได้รับแนวคิดว่าผู้เรียนมองเห็นภูมิภาคที่มีคะแนนที่จำแนกในเชิงบวกได้อย่างไร มันคุ้มค่าที่จะลองเล่นกับขนาดของชุดการฝึก อัตราการเรียนรู้ และพารามิเตอร์อื่นๆ เพื่อดูว่าเราจะเข้าใกล้วงกลมที่เราตั้งใจไว้ได้แค่ไหน

เกือบสามเหลี่ยม
ชุดฝึก : 100 แต้ม
อัตราการเรียนรู้: 0.01
ยุค: 1,000

สามเหลี่ยมเล็ก
ชุดฝึก: 1,000 แต้ม
อัตราการเรียนรู้: 0.01
ยุค: 1,000

สามเหลี่ยมใหญ่
ชุดฝึก: 1,000 แต้ม
อัตราการเรียนรู้: 0.01
ยุค: 10000

เกือบเป็นวงกลม
ชุดฝึก: 1,000 แต้ม
อัตราการเรียนรู้: 0.001
ยุค: 10000

สรุป

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

หวังว่าการสาธิตเหล่านี้จะทำให้คุณเข้าใจถึงหลักการสำคัญของ TensorFlow และให้รากฐานที่มั่นคงในการนำเทคนิคที่ซับซ้อนขึ้นไปใช้

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

ที่เกี่ยวข้อง: แอปพลิเคชั่น Gradient Descent มากมายใน TensorFlow