TensorFlow.js ทำงานในเบราว์เซอร์และ Node.js และในทั้งสองแพลตฟอร์มก็มีการกำหนดค่าที่แตกต่างกันมากมาย แต่ละแพลตฟอร์มมีชุดข้อควรพิจารณาที่แตกต่างกันซึ่งจะส่งผลต่อวิธีการพัฒนาแอปพลิเคชัน
ในเบราว์เซอร์ TensorFlow.js รองรับอุปกรณ์เคลื่อนที่และอุปกรณ์เดสก์ท็อป อุปกรณ์แต่ละชิ้นมีชุดข้อจำกัดเฉพาะ เช่น WebGL API ที่พร้อมใช้งาน ซึ่งจะกำหนดและกำหนดค่าให้คุณโดยอัตโนมัติ
ใน Node.js นั้น TensorFlow.js รองรับการเชื่อมโยงโดยตรงกับ TensorFlow API หรือทำงานโดยใช้ CPU แบบวานิลลาที่ช้ากว่า
สภาพแวดล้อม
เมื่อดำเนินการโปรแกรม TensorFlow.js การกำหนดค่าเฉพาะจะเรียกว่าสภาพแวดล้อม สภาพแวดล้อมประกอบด้วยแบ็กเอนด์ส่วนกลางเดียว รวมถึงชุดแฟล็กที่ควบคุมฟีเจอร์ที่ละเอียดของ TensorFlow.js
แบ็กเอนด์
TensorFlow.js รองรับแบ็กเอนด์ต่างๆ มากมายที่ใช้พื้นที่เก็บข้อมูลเทนเซอร์และการดำเนินการทางคณิตศาสตร์ ในเวลาใดก็ตาม มีแบ็กเอนด์เดียวเท่านั้นที่ใช้งานอยู่ โดยส่วนใหญ่แล้ว TensorFlow.js จะเลือกแบ็กเอนด์ที่ดีที่สุดสำหรับคุณโดยอัตโนมัติตามสภาพแวดล้อมปัจจุบัน อย่างไรก็ตาม บางครั้งสิ่งสำคัญคือต้องทราบว่ามีการใช้แบ็กเอนด์ใดและจะเปลี่ยนอย่างไร
หากต้องการค้นหาแบ็กเอนด์ที่คุณใช้:
console.log(tf.getBackend());
หากคุณต้องการเปลี่ยนแบ็กเอนด์ด้วยตนเอง:
tf.setBackend('cpu');
console.log(tf.getBackend());
แบ็กเอนด์ WebGL
ปัจจุบันแบ็กเอนด์ WebGL หรือ 'webgl' เป็นแบ็กเอนด์ที่ทรงพลังที่สุดสำหรับเบราว์เซอร์ แบ็กเอนด์นี้เร็วกว่าแบ็กเอนด์ CPU วานิลลาถึง 100 เท่า เทนเซอร์จะถูกจัดเก็บเป็นพื้นผิว WebGL และการดำเนินการทางคณิตศาสตร์ถูกนำมาใช้ในเชเดอร์ WebGL ต่อไปนี้เป็นสิ่งที่มีประโยชน์บางประการที่ควรทราบเมื่อใช้แบ็กเอนด์นี้: \
หลีกเลี่ยงการบล็อกเธรด UI
เมื่อเรียกใช้การดำเนินการ เช่น tf.matMul(a, b) ผลลัพธ์ tf.Tensor จะถูกส่งกลับพร้อมกัน อย่างไรก็ตาม การคำนวณการคูณเมทริกซ์อาจยังไม่พร้อมจริงๆ ซึ่งหมายความว่า tf.Tensor ที่ส่งคืนเป็นเพียงส่วนจัดการในการคำนวณ เมื่อคุณเรียก x.data()
หรือ x.array()
ค่าต่างๆ จะถูกแก้ไขเมื่อการคำนวณเสร็จสิ้นจริง สิ่งนี้ทำให้การใช้เมธอด x.data()
และ x.array()
แบบอะซิงโครนัสมีความสำคัญเหนือ x.dataSync()
และ x.arraySync()
แบบซิงโครนัส เพื่อหลีกเลี่ยงการบล็อกเธรด UI ในขณะที่การคำนวณเสร็จสิ้น
การจัดการหน่วยความจำ
ข้อแม้ประการหนึ่งเมื่อใช้แบ็กเอนด์ WebGL คือความจำเป็นในการจัดการหน่วยความจำที่ชัดเจน WebGLTextures ซึ่งเป็นที่จัดเก็บข้อมูล Tensor ในท้ายที่สุด จะไม่ถูกรวบรวมโดยเบราว์เซอร์โดยอัตโนมัติ
หากต้องการทำลายหน่วยความจำของ tf.Tensor
คุณสามารถใช้เมธอด dispose()
ได้:
const a = tf.tensor([[1, 2], [3, 4]]);
a.dispose();
เป็นเรื่องปกติมากที่จะเชื่อมโยงการดำเนินการหลายอย่างเข้าด้วยกันในแอปพลิเคชัน การเก็บการอ้างอิงถึงตัวแปรระดับกลางทั้งหมดเพื่อกำจัดตัวแปรเหล่านั้นสามารถลดความสามารถในการอ่านโค้ดได้ เพื่อแก้ไขปัญหานี้ TensorFlow.js ได้จัดเตรียม tf.tidy()
ซึ่งจะล้างข้อมูล tf.Tensor
ทั้งหมดที่ไม่ได้ส่งคืนโดยฟังก์ชันหลังจากดำเนินการแล้ว คล้ายกับวิธีการล้างตัวแปรในเครื่องเมื่อมีการเรียกใช้ฟังก์ชัน:
const a = tf.tensor([[1, 2], [3, 4]]);
const y = tf.tidy(() => {
const result = a.square().log().neg();
return result;
});
ความแม่นยำ
บนอุปกรณ์เคลื่อนที่ WebGL อาจรองรับพื้นผิวจุดลอยตัว 16 บิตเท่านั้น อย่างไรก็ตาม โมเดลแมชชีนเลิร์นนิงส่วนใหญ่ได้รับการฝึกฝนด้วยน้ำหนักจุดลอยตัว 32 บิตและการเปิดใช้งาน ซึ่งอาจทำให้เกิดปัญหาด้านความแม่นยำเมื่อย้ายโมเดลสำหรับอุปกรณ์เคลื่อนที่ เนื่องจากตัวเลขลอยตัว 16 บิตสามารถแสดงเฉพาะตัวเลขในช่วง [0.000000059605, 65504]
เท่านั้น ซึ่งหมายความว่าคุณควรระวังว่าน้ำหนักและการเปิดใช้งานในโมเดลของคุณไม่เกินช่วงนี้ หากต้องการตรวจสอบว่าอุปกรณ์รองรับพื้นผิว 32 บิตหรือไม่ ให้ตรวจสอบค่าของ tf.ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE')
หากเป็นเท็จ แสดงว่าอุปกรณ์รองรับเฉพาะพื้นผิวจุดลอยตัว 16 บิตเท่านั้น คุณสามารถใช้ tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED')
เพื่อตรวจสอบว่า TensorFlow.js กำลังใช้พื้นผิว 32 บิตอยู่หรือไม่
การรวบรวม Shader และการอัปโหลดพื้นผิว
TensorFlow.js ดำเนินการดำเนินการบน GPU โดยการเรียกใช้โปรแกรมเชเดอร์ WebGL เชเดอร์เหล่านี้ถูกรวบรวมและคอมไพล์อย่างเกียจคร้านเมื่อผู้ใช้ขอให้ดำเนินการ การคอมไพล์เชเดอร์เกิดขึ้นบน CPU บนเธรดหลักและอาจช้า TensorFlow.js จะแคชเชเดอร์ที่คอมไพล์แล้วโดยอัตโนมัติ ทำให้การเรียกครั้งที่สองไปยังการดำเนินการเดียวกันด้วยเทนเซอร์อินพุตและเอาท์พุตที่มีรูปร่างเหมือนกันเร็วขึ้นมาก โดยทั่วไปแล้ว แอปพลิเคชัน TensorFlow.js จะใช้การดำเนินการเดียวกันหลายครั้งตลอดอายุการใช้งานของแอปพลิเคชัน ดังนั้นการส่งผ่านโมเดลแมชชีนเลิร์นนิงครั้งที่สองจึงเร็วกว่ามาก
TensorFlow.js ยังจัดเก็บข้อมูล tf.Tensor เป็น WebGLTextures เมื่อมีการสร้าง tf.Tensor
เราจะไม่อัปโหลดข้อมูลไปยัง GPU ทันที แต่เราเก็บข้อมูลไว้ใน CPU จนกว่า tf.Tensor
จะถูกนำมาใช้ในการดำเนินการ หากใช้ tf.Tensor
ครั้งที่สอง ข้อมูลจะอยู่ใน GPU แล้ว ดังนั้นจึงไม่มีค่าใช้จ่ายในการอัปโหลด ในโมเดลแมชชีนเลิร์นนิงทั่วไป หมายความว่าน้ำหนักจะถูกอัปโหลดระหว่างการคาดการณ์ครั้งแรกผ่านโมเดล และการส่งผ่านโมเดลครั้งที่สองจะเร็วขึ้นมาก
หากคุณสนใจประสิทธิภาพของการคาดการณ์ครั้งแรกผ่านโมเดลของคุณหรือโค้ด TensorFlow.js เราขอแนะนำให้อุ่นโมเดลโดยส่ง Tensor อินพุตที่มีรูปร่างเดียวกันก่อนที่จะใช้ข้อมูลจริง
ตัวอย่างเช่น:
const model = await tf.loadLayersModel(modelUrl);
// Warmup the model before using real data.
const warmupResult = model.predict(tf.zeros(inputShape));
warmupResult.dataSync();
warmupResult.dispose();
// The second predict() will be much faster
const result = model.predict(userData);
แบ็กเอนด์ Node.js TensorFlow
ในแบ็กเอนด์ TensorFlow Node.js "โหนด" นั้น TensorFlow C API ใช้เพื่อเร่งการดำเนินการ ซึ่งจะใช้การเร่งด้วยฮาร์ดแวร์ที่มีอยู่ของเครื่อง เช่น CUDA หากมี
ในแบ็กเอนด์นี้ เช่นเดียวกับแบ็กเอนด์ WebGL การดำเนินการส่งคืน tf.Tensor
แบบซิงโครนัส อย่างไรก็ตาม การดำเนินการจะเสร็จสิ้นก่อนที่คุณจะได้รับเทนเซอร์กลับคืนมา ซึ่งต่างจากแบ็กเอนด์ WebGL ซึ่งหมายความว่าการเรียก tf.matMul(a, b)
จะบล็อกเธรด UI
ด้วยเหตุนี้ หากคุณตั้งใจจะใช้สิ่งนี้ในแอปพลิเคชันที่ใช้งานจริง คุณควรรัน TensorFlow.js ในเธรดของผู้ปฏิบัติงานเพื่อไม่ให้บล็อกเธรดหลัก
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Node.js โปรดดูคู่มือนี้
แบ็กเอนด์ WASM
TensorFlow.js มี แบ็กเอนด์ WebAssembly ( wasm
) ซึ่งให้การเร่งความเร็วของ CPU และสามารถใช้เป็นทางเลือกแทนแบ็กเอนด์ vanilla JavaScript CPU ( cpu
) และ WebGL เร่ง ( webgl
) หากต้องการใช้:
// Set the backend to WASM and wait for the module to be ready.
tf.setBackend('wasm');
tf.ready().then(() => {...});
หากเซิร์ฟเวอร์ของคุณให้บริการไฟล์ .wasm
บนพาธอื่นหรือชื่ออื่น ให้ใช้ setWasmPath
ก่อนที่จะเริ่มต้นแบ็กเอนด์ ดูส่วน "การใช้ Bundlers" ใน README สำหรับข้อมูลเพิ่มเติม:
import {setWasmPath} from '@tensorflow/tfjs-backend-wasm';
setWasmPath(yourCustomPath);
tf.setBackend('wasm');
tf.ready().then(() => {...});
ทำไมต้อง WASM?
WASM เปิดตัวในปี 2558 ในรูปแบบไบนารีบนเว็บใหม่ โดยให้โปรแกรมที่เขียนด้วย JavaScript, C, C++ และอื่นๆ เป็นเป้าหมายในการคอมไพล์สำหรับการทำงานบนเว็บ WASM ได้รับ การสนับสนุน โดย Chrome, Safari, Firefox และ Edge ตั้งแต่ปี 2017 และได้รับการสนับสนุนโดย อุปกรณ์ 90% ทั่วโลก
ผลงาน
แบ็กเอนด์ WASM ใช้ประโยชน์จาก ไลบรารี XNNPACK เพื่อการใช้งานที่เหมาะสมที่สุดของผู้ให้บริการโครงข่ายประสาทเทียม
เมื่อเทียบกับ JavaScript : โดยทั่วไปไบนารีของ WASM จะเร็วกว่าชุด JavaScript มากสำหรับเบราว์เซอร์ในการโหลด แยกวิเคราะห์ และดำเนินการ JavaScript ถูกพิมพ์แบบไดนามิกและรวบรวมขยะ ซึ่งอาจทำให้รันไทม์ช้าลง
เทียบกับ WebGL : WebGL เร็วกว่า WASM สำหรับรุ่นส่วนใหญ่ แต่สำหรับรุ่นเล็ก WASM สามารถทำงานได้ดีกว่า WebGL เนื่องจากต้นทุนค่าใช้จ่ายคงที่ในการรัน WebGL shaders ส่วน “ฉันควรใช้ WASM เมื่อใด” ด้านล่างจะกล่าวถึงการวิเคราะห์พฤติกรรมในการตัดสินใจครั้งนี้
การพกพาและความเสถียร
WASM มีเลขคณิตทศนิยมแบบ 32 บิตแบบพกพา ซึ่งให้ความเท่าเทียมกันที่แม่นยำในทุกอุปกรณ์ ในทางกลับกัน WebGL เป็นฮาร์ดแวร์เฉพาะ และอุปกรณ์ที่แตกต่างกันอาจมีความแม่นยำที่แตกต่างกัน (เช่น ทางเลือกสำรองเป็น 16 บิตแบบลอยบนอุปกรณ์ iOS)
เช่นเดียวกับ WebGL WASM ได้รับการสนับสนุนอย่างเป็นทางการจากเบราว์เซอร์หลักๆ ทั้งหมด WASM ต่างจาก WebGL ตรงที่สามารถทำงานใน Node.js และใช้งานฝั่งเซิร์ฟเวอร์ได้โดยไม่จำเป็นต้องคอมไพล์ไลบรารีดั้งเดิม
ฉันควรใช้ WASM เมื่อใด
ขนาดโมเดลและความต้องการในการคำนวณ
โดยทั่วไป WASM เป็นตัวเลือกที่ดีเมื่อโมเดลมีขนาดเล็กกว่าหรือคุณสนใจอุปกรณ์ระดับล่างที่ไม่รองรับ WebGL (ส่วนขยาย OES_texture_float
) หรือมี GPU ที่ทรงพลังน้อยกว่า แผนภูมิด้านล่างแสดงเวลาการอนุมาน (ณ TensorFlow.js 1.5.2) ใน Chrome บน MacBook Pro ปี 2018 สำหรับ 5 รุ่น ที่รองรับอย่างเป็นทางการของเราในแบ็กเอนด์ WebGL, WASM และ CPU:
รุ่นเล็ก
แบบอย่าง | WebGL | WASM | ซีพียู | หน่วยความจำ |
---|---|---|---|---|
BlazeFace | 22.5 มิลลิวินาที | 15.6 มิลลิวินาที | 315.2 มิลลิวินาที | .4 เมกะไบต์ |
เฟซเมช | 19.3 มิลลิวินาที | 19.2 มิลลิวินาที | 335 มิลลิวินาที | 2.8 ลบ |
รุ่นใหญ่
แบบอย่าง | WebGL | WASM | ซีพียู | หน่วยความจำ |
---|---|---|---|---|
โพเซเน็ต | 42.5 มิลลิวินาที | 173.9 มิลลิวินาที | 1514.7 มิลลิวินาที | 4.5 ลบ |
บอดี้พิกซ์ | 77 มิลลิวินาที | 188.4 มิลลิวินาที | 2683 ม | 4.6 ลบ |
โมบายเน็ต เวอร์ชั่น 2 | 37 น | 94 มิลลิวินาที | 923.6 มิลลิวินาที | 13 เมกะไบต์ |
ตารางด้านบนแสดงให้เห็นว่า WASM เร็วกว่าแบ็กเอนด์ JS CPU ธรรมดาถึง 10-30 เท่าในรุ่นต่างๆ และสามารถแข่งขันกับ WebGL สำหรับรุ่นเล็ก ๆ เช่น BlazeFace ซึ่งมีน้ำหนักเบา (400KB) แต่มีจำนวนการดำเนินการที่เหมาะสม (~ 140) เนื่องจากโปรแกรม WebGL มีต้นทุนค่าใช้จ่ายคงที่ต่อการดำเนินการ สิ่งนี้อธิบายว่าทำไมโมเดลอย่าง BlazeFace จึงเร็วกว่าบน WASM
ผลลัพธ์เหล่านี้จะแตกต่างกันไปขึ้นอยู่กับอุปกรณ์ของคุณ วิธีที่ดีที่สุดในการพิจารณาว่า WASM เหมาะกับแอปพลิเคชันของคุณหรือไม่คือการทดสอบบนแบ็กเอนด์ต่างๆ ของเรา
การอนุมานและการฝึกอบรม
เพื่อจัดการกับกรณีการใช้งานหลักสำหรับการปรับใช้โมเดลที่ได้รับการฝึกอบรมล่วงหน้า การพัฒนาแบ็กเอนด์ WASM จะจัดลำดับ ความสำคัญของการอนุมาน มากกว่าการสนับสนุน การฝึกอบรม ดู รายการ Ops ที่รองรับล่าสุด ใน WASM และ แจ้งให้เราทราบ หากโมเดลของคุณมี Ops ที่ไม่รองรับ สำหรับโมเดลการฝึก เราขอแนะนำให้ใช้แบ็กเอนด์ Node (TensorFlow C++) หรือแบ็กเอนด์ WebGL
แบ็กเอนด์ซีพียู
แบ็คเอนด์ CPU หรือ 'cpu' เป็นแบ็กเอนด์ที่มีประสิทธิภาพน้อยที่สุด แต่ก็เป็นวิธีที่ง่ายที่สุด การดำเนินการทั้งหมดถูกนำไปใช้ใน vanilla JavaScript ซึ่งทำให้การทำงานแบบขนานน้อยลง พวกเขายังบล็อกเธรด UI ด้วย
แบ็กเอนด์นี้มีประโยชน์มากสำหรับการทดสอบหรือบนอุปกรณ์ที่ WebGL ไม่พร้อมใช้งาน
ธง
TensorFlow.js มีชุดแฟล็กสภาพแวดล้อมที่ได้รับการประเมินโดยอัตโนมัติและกำหนดการกำหนดค่าที่ดีที่สุดในแพลตฟอร์มปัจจุบัน แฟล็กเหล่านี้ส่วนใหญ่เป็นแบบภายใน แต่แฟล็กส่วนกลางบางส่วนสามารถควบคุมได้ด้วย API สาธารณะ
-
tf.enableProdMode():
เปิดใช้งานโหมดการผลิต ซึ่งจะลบการตรวจสอบโมเดล การตรวจสอบ NaN และการตรวจสอบความถูกต้องอื่นๆ ที่เป็นประโยชน์ต่อประสิทธิภาพ -
tf.enableDebugMode()
: เปิดใช้งานโหมดดีบัก ซึ่งจะบันทึกลงในคอนโซลทุกการดำเนินการที่ดำเนินการ เช่นเดียวกับข้อมูลประสิทธิภาพรันไทม์ เช่น ขนาดหน่วยความจำ และเวลาดำเนินการเคอร์เนลทั้งหมด โปรดทราบว่าการดำเนินการนี้จะทำให้แอปพลิเคชันของคุณช้าลงอย่างมาก โปรดอย่าใช้สิ่งนี้ในการผลิต