ฉันมีซอฟต์แวร์เครือข่ายประสาทที่น่าสนุกมากในยุค 90 และฉันกังวลที่จะลองสร้างบางอย่างโดยใช้ tensorflow
เฟรมเวิร์กอัจฉริยะของเครื่องของ Google เป็นความร้อนใหม่ในขณะนี้ และเมื่อ Tensorflow ติดตั้งบน Raspberry Pi การทำงานกับมันกลายเป็นเรื่องง่ายมากที่จะทำ ในเวลาอันสั้นฉันสร้างเครือข่ายประสาทเทียมที่นับเป็นไบนารี ดังนั้นฉันคิดว่าฉันจะส่งต่อสิ่งที่ฉันได้เรียนรู้จนถึงตอนนี้ หวังว่าสิ่งนี้จะทำให้ง่ายขึ้นสำหรับคนอื่นที่ต้องการลองหรือสำหรับทุกคนที่ต้องการข้อมูลเชิงลึกเกี่ยวกับเครือข่ายประสาทเทียม
tensorflow คืออะไร
ในการอ้างถึงเว็บไซต์ Tensorflow Tensorflow เป็น “ไลบรารีซอฟต์แวร์โอเพ่นซอร์สสำหรับการคำนวณเชิงตัวเลขโดยใช้กราฟการไหลของข้อมูล” เราหมายถึงอะไร “กราฟการไหลของข้อมูล” นั่นเป็นส่วนที่น่าทึ่งจริงๆ แต่ก่อนที่เราจะสามารถตอบได้เราจะต้องพูดคุยเกี่ยวกับโครงสร้างสำหรับเครือข่ายประสาทที่เรียบง่าย
เครือข่ายประสาทเทียมไบนารี
พื้นฐานของเครือข่ายประสาท
เครือข่ายประสาทที่เรียบง่ายมีหน่วยป้อนข้อมูลบางอย่างที่อินพุตไป นอกจากนี้ยังมีหน่วยที่ซ่อนอยู่ซึ่งเรียกว่าเพราะจากมุมมองของผู้ใช้ที่ซ่อนอยู่อย่างแท้จริง และมีหน่วยงานที่เราได้รับผลลัพธ์ ออกไปด้านข้างเป็นหน่วยอคติซึ่งมีเพื่อช่วยควบคุมค่าที่ปล่อยออกมาจากหน่วยที่ซ่อนอยู่และเอาต์พุต การเชื่อมต่อทุกหน่วยเหล่านี้เป็นน้ำหนักจำนวนมากซึ่งเป็นเพียงตัวเลขซึ่งแต่ละอันมีความเกี่ยวข้องกับสองหน่วย
วิธีที่เราปลูกฝังความฉลาดในเครือข่ายประสาทนี้คือการกำหนดค่าให้กับน้ำหนักเหล่านั้นทั้งหมด นั่นคือสิ่งที่การฝึกอบรมเครือข่ายประสาทเทียมทำตามค่าที่เหมาะสมสำหรับน้ำหนักเหล่านั้น เมื่อผ่านการฝึกอบรมในตัวอย่างของเราเราจะตั้งค่าหน่วยอินพุตให้กับเลขฐานสอง 0, 0 และ 0 ตามลำดับ Tensorflow จะทำสิ่งที่มีทุกสิ่งในระหว่างนั้นและหน่วยงานจะมีตัวเลขไบนารี 0, 0 และ 1 ตามลำดับ ในกรณีที่คุณพลาดไปมันรู้ว่าหมายเลขต่อไปหลังจากไบนารี 000 เท่ากับ 001 สำหรับ 001 มันควรคาย 010 และมากถึง 111 ซึ่งมันจะคายออก 000 เมื่อน้ำหนักเหล่านั้นถูกตั้งค่าอย่างเหมาะสม จะรู้วิธีการนับ
เครือข่ายประสาทเคาน์เตอร์ไบนารีกับเมทริกซ์
ขั้นตอนเดียวใน “การทำงาน” เครือข่ายประสาทคือการคูณค่าของแต่ละน้ำหนักด้วยค่าของหน่วยอินพุตจากนั้นเก็บผลลัพธ์ในหน่วยซ่อนที่เกี่ยวข้อง
เราสามารถวาดหน่วยและน้ำหนักอีกครั้งเป็นอาร์เรย์หรือสิ่งที่เรียกว่ารายการใน Python จากมุมมองทางคณิตศาสตร์พวกเขาเป็นเมทริกซ์ เราวาดใหม่เพียงส่วนหนึ่งของพวกเขาในแผนภาพ การคูณเมทริกซ์อินพุตที่มีเมทริกซ์น้ำหนักเกี่ยวข้องกับการคูณเมทริกซ์ที่เรียบง่ายส่งผลให้เมทริกซ์ที่ซ่อนอยู่ห้าองค์ประกอบ / รายการ / อาร์เรย์
จากเมทริกซ์ไปยังเครื่องตักเต็ม
ใน tensorflow รายการเหล่านั้นเรียกว่าเทนเซอร์ และขั้นตอนการคูณเมทริกซ์เรียกว่าการดำเนินการหรือ OP ในโปรแกรมเมอร์ – พูดคำว่าคุณจะต้องคุ้นเคยกับการอ่านเอกสารการอ่าน Tensorflow การดำเนินการต่อไปเครือข่ายประสาททั้งหมดเป็นคอลเลกชันของเครื่องตักเฟ้อและ ops ที่ทำงานกับพวกเขา โดยสิ้นเชิงพวกเขาทำกราฟ
กราฟเต็มเคาน์เตอร์ไบนารี
Layer1 ขยาย
แสดงที่นี่เป็นสแน็ปช็อตที่ถ่ายจาก Tensorboard เป็นเครื่องมือสำหรับการมองเห็นกราฟรวมถึงการตรวจสอบค่าเท็นเซอร์ระหว่างและหลังการฝึกอบรม เครื่องตักเต็นเป็นเส้นและเขียนบนเส้นเป็นมิติของเทนเซอร์ การเชื่อมต่อเครื่องตักตภัยเป็น ops ทั้งหมดแม้ว่าบางสิ่งที่คุณเห็นสามารถดับเบิลคลิกเพื่อขยายเพื่อขยายรายละเอียดเพิ่มเติมตามที่เราได้ทำสำหรับ Layer1 ในสแนปชอตครั้งที่สอง
ที่ด้านล่างคือ x ชื่อที่เราได้มอบให้กับผู้ถือตัวแทนที่ช่วยให้เราสามารถให้ค่าสำหรับเทนเซอร์อินพุต เส้นที่ขึ้นไปและไปทางซ้ายจากมันเป็นเท็นเซอร์อินพุต ทำต่อไปตามบรรทัดนั้นต่อไปและคุณจะพบว่า Matmul OP ซึ่งการคูณเมทริกซ์กับเทนเซอร์อินพุตและเทนเซอร์ซึ่งเป็นสายอื่น ๆ ที่นำไปสู่ Matmul OP เทนเซอร์นั้นแสดงถึงน้ำหนัก
ทั้งหมดนี้เป็นเพียงเพื่อให้คุณรู้สึกว่ากราฟและเครื่องตักเต็นและ ops ของมันทำให้คุณมีความคิดที่ดีขึ้นเกี่ยวกับสิ่งที่เราหมายถึงโดย Tensorflow เป็น “Library ซอฟต์แวร์สำหรับการคำนวณเชิงตัวเลขโดยใช้กราฟการไหลของข้อมูล” แต่ทำไมเราต้องการสร้างกราฟเหล่านี้?
ทำไมต้องสร้างกราฟ?
API ที่มีความเสถียรในปัจจุบันคือหนึ่งสำหรับ Python ภาษาที่ตีความ เครือข่ายประสาทเทียมถูกคำนวณอย่างเข้มข้นและมีขนาดใหญ่อาจมีน้ำหนักหลายพันหรือหลายล้าน การคำนวณโดยการตีความทุกขั้นตอนจะใช้เวลาตลอดไป
ดังนั้นเราจึงสร้างกราฟที่ประกอบด้วยเครื่องตักเต็มและ ops อธิบายเค้าโครงของเครือข่ายประสาทการดำเนินงานทางคณิตศาสตร์ทั้งหมดและแม้แต่ค่าเริ่มต้นสำหรับตัวแปร หลังจากที่เราได้สร้างกราฟนี้แล้วเราจะส่งไปยังสิ่งที่ tensorflow เรียกเซสชั่น สิ่งนี้เรียกว่าการดำเนินการรอการตัดบัญชี เซสชั่นรันกราฟโดยใช้รหัสที่มีประสิทธิภาพมาก ไม่เพียงแค่นั้น แต่การดำเนินการหลายอย่างเช่นการคูณเมทริกซ์เป็นสิ่งที่สามารถทำได้บน GPU ที่รองรับ (หน่วยประมวลผลกราฟิก) และเซสชั่นจะทำเพื่อคุณ นอกจากนี้ Tensorflow เป็น BUilt เพื่อให้สามารถกระจายการประมวลผลในหลาย ๆ เครื่องและ / หรือ GPU ให้กราฟที่สมบูรณ์อนุญาตให้ทำเช่นนั้น
การสร้างกราฟเคาน์เตอร์ไบนารี
และนี่คือรหัสสำหรับเครือข่าย Neural Counter Binary Counter ของเรา คุณสามารถค้นหารหัสต้นฉบับแบบเต็มในหน้า GitHub นี้ โปรดทราบว่ามีรหัสเพิ่มเติมสำหรับการบันทึกข้อมูลสำหรับใช้กับ tensorboard
เราจะเริ่มต้นด้วยรหัสสำหรับการสร้างกราฟของเครื่องตักเต็มและ ops
นำเข้า tensorflow เป็น tf
Sess = tf. Interactivesession ()
num_inputs = 3
num_hidden = 5
num_outputs = 3
เรานำเข้าโมดูล Tensorflow สร้างเซสชันสำหรับใช้ในภายหลังและเพื่อให้รหัสของเราเข้าใจได้มากขึ้นเราสร้างตัวแปรสองสามตัวที่มีจำนวนหน่วยในเครือข่ายของเรา
x = tf.placeholder (tf.float32, รูปร่าง = [ไม่มี, num_inputs], name = ‘x’)
y_ = tf.pled.holder (tf.float32, รูปร่าง = [ไม่มี, num_outputs], name = ‘y_’)
จากนั้นเราสร้างตัวยึดตำแหน่งสำหรับหน่วยงานอินพุตและเอาต์พุตของเรา ตัวยึดตำแหน่งเป็น tensorflow op สำหรับสิ่งที่เราจะให้คุณค่าในภายหลัง X และ Y_ ตอนนี้มีความตักเต่นในกราฟใหม่และแต่ละคนมีตัวยึดตำแหน่งที่เชื่อมโยงกับมัน
คุณอาจสงสัยว่าทำไมเราถึงกำหนดรูปร่างเป็น [ไม่มี, num_inputs] และ [ไม่มี, num_outputs], สองมิติรายการและทำไมไม่มีมิติแรก? ในภาพรวมของเครือข่ายประสาทด้านบนดูเหมือนว่าเราจะให้ข้อมูลทีละครั้งและฝึกซ้อมเพื่อสร้างผลลัพธ์ที่กำหนด มันมีประสิทธิภาพมากกว่าถ้าเราให้คู่อินพุต / เอาท์พุทหลายคู่ในแต่ละครั้งสิ่งที่เรียกว่าชุด มิติแรกมีไว้สำหรับจำนวนของคู่อินพุต / เอาต์พุตในแต่ละแบทช์ เราจะไม่รู้ว่ามีกี่ชุดจนกว่าเราจะให้หนึ่งในภายหลัง และในความเป็นจริงเราใช้กราฟเดียวกันสำหรับการฝึกอบรมการทดสอบและสำหรับการใช้งานจริงดังนั้นขนาดแบทช์จะไม่เหมือนเดิมเสมอไป ดังนั้นเราจึงใช้วัตถุ Python Placeholder ไม่มีขนาดของมิติแรกในตอนนี้
w_fc1 = tf.truncated_normal ([num_inputs, num_hidden], mean = 0.5, stddev = 0.707)
w_fc1 = tf.variable (w_fc1, name = ‘w_fc1’)
b_fc1 = tf.trunced_normal ([num_hidden], mean = 0.5, stddev = 0.707)
b_fc1 = tf.variable (b_fc1, name = ‘b_fc1’)
H_FC1 = tf.nn.relu (tf.matmul (x, w_fc1) + b_fc1)
ตามด้วยการสร้างเลเยอร์หนึ่งในกราฟเครือข่ายประสาท: น้ำหนัก w_fc1, อคติ b_fc1 และหน่วยที่ซ่อนอยู่ h_fc1 “FC” เป็นความหมายการประชุม “เชื่อมต่ออย่างสมบูรณ์” เนื่องจากน้ำหนักเชื่อมต่อทุกหน่วยอินพุตไปยังทุกหน่วยที่ซ่อนอยู่
tf.truncated_normal ส่งผลให้ ops และ tensors จำนวนมากซึ่งในภายหลังจะกำหนดหมายเลขปกติแบบสุ่มให้กับน้ำหนักทั้งหมด
ตัวแปร ops จะได้รับค่าในการเริ่มต้นด้วยตัวเลขสุ่มในกรณีนี้และเก็บข้อมูลของพวกเขาในการทำงานหลายครั้ง พวกเขายังมีประโยชน์สำหรับการบันทึกเครือข่ายประสาทเทียมเป็นไฟล์สิ่งที่คุณต้องการทำเมื่อได้รับการฝึกฝน
คุณสามารถดูว่าเราจะทำอย่างไรกับการคูณเมทริกซ์โดยใช้ Matmul OP นอกจากนี้เรายังใส่ add op ซึ่งจะเพิ่มในน้ำหนักอคติ Relu op ดำเนินการสิ่งที่เราเรียกใช้ฟังก์ชั่นการเปิดใช้งาน การคูณเมทริกซ์และการเพิ่มเป็นเชิงเส้น มีหลายสิ่งหลายอย่างที่เครือข่ายประสาทสามารถเรียนรู้โดยใช้การดำเนินงานเชิงเส้นเพียงอย่างเดียว ฟังก์ชั่นการเปิดใช้งานให้บางส่วนที่ไม่ใช่เชิงเส้น ในกรณีของฟังก์ชั่นการเปิดใช้งาน Relu จะตั้งค่าใด ๆ ที่น้อยกว่าศูนย์เป็นศูนย์และค่าอื่น ๆ ทั้งหมดจะไม่เปลี่ยนแปลง เชื่อหรือไม่ว่าการทำเช่นนั้นเปิดโลกทั้งหมดของสิ่งต่าง ๆ ที่สามารถเรียนรู้ได้
w_fc2 = tf.truncated_normal ([num_hidden, num_outputs], mean = 0.5, stddev = 0.707)
w_fc2 = tf.variable (w_fc2, name = ‘w_fc2’)
b_fc2 = tf.truncated_normal ([num_outputs], mean = 0.5, stddev = 0.707)
b_fc2 = tf.variable (b_fc2, name = ‘b_fc2’)
y = tf.matmul (h_fc1, w_fc2) + b_fc2
น้ำหนักและอคติสำหรับเลเยอร์สองจะถูกตั้งค่าเช่นเดียวกับชั้นหนึ่ง แต่เลเยอร์เอาต์พุตจะแตกต่างกัน เราจะทำการคูณเมทริกซ์อีกครั้งคราวนี้คูณน้ำหนักและหน่วยที่ซ่อนอยู่แล้วเพิ่มน้ำหนักอคติ เราออกจากฟังก์ชั่นการเปิดใช้งานสำหรับรหัสถัดไป
ผลลัพธ์ = tf.sigmoid (y, name = ‘ผลลัพธ์’)
cross_entropy = tf.reduce_mean (
tf.nn.sigmoid_cross_entropy_with_logits (เข้าสู่ระบบ = y, ป้ายกำกับ = y_))
sigmoid เป็นฟังก์ชั่นการเปิดใช้งานอื่นเช่น relu ที่เราพบข้างต้นที่นั่นเพื่อให้การไม่เป็นเส้นตรง ฉันใช้ sigmoid ที่นี่บางส่วนเนื่องจากสมการ sigmoid ผลลัพธ์ในค่าระหว่าง 0 ถึง 1 เหมาะสำหรับตัวอย่างเคาน์เตอร์ไบนารีของเรา ฉันยังใช้มันเพราะมันดีสำหรับเอาต์พุตที่หน่วยเอาต์พุตมากกว่าหนึ่งหน่วยสามารถมีค่าใหญ่ได้ ในกรณีของเราเพื่อเป็นตัวแทนของเลขฐานสอง 111 ยูนิตเอาต์พุตทั้งหมดสามารถมีค่ามาก เมื่อทำการจำแนกภาพเราต้องการสิ่งที่แตกต่างกันมากเราต้องการเพียงหนึ่งหน่วยเอาต์พุตที่จะยิงด้วยค่าขนาดใหญ่ ตัวอย่างเช่นเราต้องการให้หน่วยเอาต์พุตที่แสดงถึงยีราฟเพื่อให้มีค่าจำนวนมากหากรูปภาพมียีราฟ บางอย่างเช่น Softmax จะเป็นตัวเลือกที่ดีสำหรับการจำแนกภาพ
ในการตรวจสอบอย่างใกล้ชิดดูเหมือนว่ามีการทำซ้ำบางอย่าง เราดูเหมือนจะใส่ sigmoid สองครั้ง จริงๆแล้วเรากำลังสร้างสองที่แตกต่างกันขนานutputs ที่นี่ tensor cross_entropy จะถูกใช้ในระหว่างการฝึกอบรมเครือข่ายที่เป็นกลาง ผลการตรวจสอบผลลัพธ์จะถูกนำมาใช้เมื่อเราดำเนินการเครือข่ายประสาทเทียมที่ผ่านการฝึกอบรมของเราในภายหลังสำหรับทุกวัตถุประสงค์ที่สร้างขึ้นเพื่อความสนุกในกรณีของเรา ฉันไม่รู้ว่านี่เป็นวิธีที่ดีที่สุดในการทำเช่นนี้ แต่มันเป็นวิธีที่ฉันมาด้วย
train_step = tf.train.rmspropoptimizer (0.25, โมเมนตัม = 0.5) .minimize (cross_entropy)
ชิ้นสุดท้ายที่เราเพิ่มลงในกราฟของเราคือการฝึกอบรม นี่คือ op หรือ ops ที่จะปรับน้ำหนักทั้งหมดตามข้อมูลการฝึกอบรม จำไว้ว่าเรายังคงสร้างกราฟที่นี่ การฝึกอบรมจริงจะเกิดขึ้นในภายหลังเมื่อเรารันกราฟ
มีตัวเพิ่มประสิทธิภาพสองสามตัวเลือก ฉันเลือก tf.train.rmspropoptimizer เพราะเช่น sigmoid มันใช้งานได้ดีสำหรับกรณีที่ค่าเอาต์พุตทั้งหมดสามารถมีขนาดใหญ่ สำหรับการจำแนกสิ่งต่าง ๆ เช่นเมื่อทำการจำแนกรูปภาพ TF.Train.GradientDesTionoptimizer อาจจะดีกว่า
การฝึกอบรมและการใช้เคาน์เตอร์ไบนารี
ต้องสร้างกราฟแล้วถึงเวลาที่จะทำการฝึกอบรมแล้ว เมื่อผ่านการฝึกอบรมแล้วเราสามารถใช้งานได้
InputVals = [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1] ,
[1, 1, 0], [1, 1, 1]]
Targetvals = [[0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0] ,
[1, 1, 1], [0, 0, 0]]
ก่อนอื่นเรามีข้อมูลการฝึกอบรม: อินพุตและเป้าหมาย InputVals มีอินพุตและสำหรับแต่ละรายการจะมีค่าเป้าหมาย TargetVals ที่สอดคล้องกัน สำหรับ Epervals [0] เรามี [0, 0, 0] และเอาต์พุตที่คาดหวังคือ Targetvals [0] ซึ่งเป็น [0, 0, 1] และอื่น ๆ
ถ้า do_training == 1:
sess.run (tf.global_variables_initializer ())
สำหรับฉันในช่วง (1,0001):
ถ้าฉัน% 100 == 0:
Train_Error = cross_entropy.eval (feed_dict = {x: inputvals, y_: targetvals})
พิมพ์ (“ขั้นตอน% D, ข้อผิดพลาดการฝึกอบรม% g”% (i, train_error))
ถ้า train_error <0.0005:
หยุดพัก
Sess.Run (train_step, feed_dict = {x: Inputvals, Y_: targetvals})
ถ้า Save_trased == 1:
พิมพ์ ("การบันทึกเครือข่ายประสาทเทียมเป็น% s. *"% (SAVE_FILE))
Saver = tf.train.saver ()
Saver.save (Sess, Save_file)
Do_training และ Save_trased สามารถเป็นฮาร์ดโค้ดและเปลี่ยนสำหรับการใช้งานแต่ละครั้งหรือสามารถตั้งค่าโดยใช้อาร์กิวเมนต์บรรทัดคำสั่ง
ก่อนอื่นเราไปผ่านตัวแปรทั้งหมดเหล่านั้นและให้พวกเขาเริ่มต้นเครื่องตักเต็มของพวกเขา
จากนั้นให้มากถึง 1,0001 ครั้งเราเรียกใช้กราฟจากล่างขึ้นไปที่ Trable_Step Tensor สิ่งสุดท้ายที่เราเพิ่มลงในกราฟของเรา เราผ่านอินพุตและ targetvals ไปยัง op หรือ ops ของ train_step ซึ่งเราจะเพิ่มโดยใช้ rmspropoptimizer นี่คือขั้นตอนที่ปรับน้ำหนักทั้งหมดเช่นอินพุตที่กำหนดจะส่งผลให้บางสิ่งบางอย่างใกล้เคียงกับเอาต์พุตเป้าหมายที่สอดคล้องกัน หากข้อผิดพลาดระหว่างเอาต์พุตเป้าหมายและเอาต์พุตจริงมีขนาดเล็กมากเร็วพอเราก็แยกออกจากลูป
หากคุณมีคู่อินพุต / เอาต์พุตหลายพันคู่คุณสามารถให้ชุดย่อยของพวกเขาในเวลาชุดที่เราพูดก่อนหน้านี้ แต่ที่นี่เรามีเพียงแปดเท่านั้นดังนั้นเราจึงให้ทุกคนในแต่ละครั้ง
หากเราต้องการเราสามารถบันทึกเครือข่ายไปยังไฟล์ได้ เมื่อผ่านการฝึกอบรมแล้วเราไม่จำเป็นต้องฝึกอีกครั้ง
อื่น: # ถ้าเราไม่ได้ฝึกอบรมเราต้องโหลดจากไฟล์
พิมพ์ ("โหลดเครือข่ายประสาทจาก% S"% (Save_File))
Saver = tf.train.saver ()
Saver.Restore (Sess, Save_file)
# หมายเหตุ: การคืนค่าโหลดทั้งสองและเริ่มต้นตัวแปร
หากเราไม่ได้ฝึกมันจากนั้นเราจะโหลดเครือข่ายที่ผ่านการฝึกอบรมจากไฟล์ ไฟล์มีเฉพาะค่าสำหรับเครื่องตักเฟ้อที่มีตัวแปร ops มันไม่มีโครงสร้างของกราฟ ดังนั้นแม้ในขณะที่ใช้กราฟที่ผ่านการฝึกอบรมแล้วเรายังต้องการรหัสเพื่อสร้างกราฟ มีวิธีการบันทึกและโหลดกราฟจากไฟล์โดยใช้ Metagraphs แต่เราไม่ได้ทำเช่นนั้นที่นี่
พิมพ์ ('\ ncounting เริ่มต้นด้วย: 0 0 0')
res = sess.run (ผลลัพธ์, feed_dict = {x: [[0, 0, 0]]})
พิมพ์ ('% g% g% g'% (res [0] [0], res [0] [1], res [0] [2]))
0