การสร้างชุดเบราว์เซอร์ที่ปรับขนาดให้เหมาะสมด้วย TensorFlow.js

ภาพรวม

TensorFlow.js 3.0 ให้การสนับสนุนสำหรับการสร้าง ชุดเบราว์เซอร์ที่เน้นการใช้งานจริงและปรับขนาดให้เหมาะสม กล่าวอีกนัยหนึ่ง เราต้องการทำให้คุณจัดส่ง JavaScript ไปยังเบราว์เซอร์น้อยลงได้ง่ายขึ้น

คุณลักษณะนี้เหมาะสำหรับผู้ใช้ที่มีกรณีการใช้งานจริงซึ่งจะได้รับประโยชน์เป็นพิเศษจากการลดจำนวนไบต์ออกจากเพย์โหลด (และยินดีที่จะพยายามอย่างเต็มที่เพื่อให้บรรลุเป้าหมายนี้) หากต้องการใช้คุณลักษณะนี้ คุณควรคุ้นเคยกับ ES Modules เครื่องมือการรวมกลุ่ม JavaScript เช่น webpack หรือ rollup และแนวคิดต่างๆ เช่น การกำจัดแบบ tree-shaking/dead-code

บทช่วยสอนนี้สาธิตวิธีสร้างโมดูล tensorflow.js แบบกำหนดเองที่สามารถใช้กับบันเดิลเพื่อสร้างบิลด์ที่ปรับขนาดให้เหมาะสมสำหรับโปรแกรมที่ใช้ tensorflow.js

คำศัพท์เฉพาะทาง

ในบริบทของเอกสารนี้มีคำศัพท์สำคัญบางประการที่เราจะใช้:

ES Modules - ระบบโมดูล JavaScript มาตรฐาน เปิดตัวใน ES6/ES2015 สามารถระบุได้โดยใช้คำสั่ง นำเข้า และ ส่งออก

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

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

การดำเนินการ (Ops) - การดำเนินการทางคณิตศาสตร์กับเทนเซอร์ตั้งแต่หนึ่งตัวขึ้นไปที่สร้างเทนเซอร์ตั้งแต่หนึ่งตัวขึ้นไปเป็นเอาท์พุต Ops คือโค้ด 'ระดับสูง' และสามารถใช้ ops อื่นเพื่อกำหนดตรรกะได้

เคอร์เนล - การใช้งานเฉพาะของ op ที่เชื่อมโยงกับความสามารถของฮาร์ดแวร์เฉพาะ เคอร์เนลเป็น 'ระดับต่ำ' และเฉพาะแบ็กเอนด์ ops บางตัวมีการแมปแบบหนึ่งต่อหนึ่งจาก op ไปยังเคอร์เนล ในขณะที่ ops อื่น ๆ ใช้หลายเคอร์เนล

ขอบเขตและกรณีการใช้งาน

การอนุมานเฉพาะแบบจำลองกราฟเท่านั้น

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

การจัดการเทนเซอร์ระดับต่ำด้วย tfjs-core

กรณีการใช้งานอื่นๆ ที่เรารองรับคือโปรแกรมที่ใช้แพ็คเกจ @tensorflow/tjfs-core โดยตรงสำหรับการจัดการเทนเซอร์ระดับล่าง

แนวทางของเราในการสร้างแบบกำหนดเอง

หลักการสำคัญของเราเมื่อออกแบบฟังก์ชันการทำงานนี้ประกอบด้วยดังต่อไปนี้:

  • ใช้ประโยชน์สูงสุดจากระบบโมดูล JavaScript (ESM) และอนุญาตให้ผู้ใช้ TensorFlow.js ทำเช่นเดียวกัน
  • ทำให้ TensorFlow.js สั่นไหวแบบทรีได้มากที่สุดเท่าที่จะเป็นไปได้ โดยบันเดิลที่มีอยู่ (เช่น webpack, rollup ฯลฯ) สิ่งนี้ทำให้ผู้ใช้สามารถใช้ประโยชน์จากความสามารถทั้งหมดของบันเดิลเหล่านั้น รวมถึงคุณสมบัติต่างๆ เช่น การแยกโค้ด
  • รักษา ความสะดวกในการใช้งานให้มากที่สุดเท่าที่จะเป็นไปได้สำหรับผู้ใช้ที่ไม่ไวต่อขนาดบันเดิล นี่หมายความว่าบิลด์ที่ใช้งานจริงจะต้องใช้ความพยายามมากขึ้น เนื่องจากค่าเริ่มต้นจำนวนมากในไลบรารีของเรารองรับความสะดวกในการใช้งานมากกว่าบิลด์ที่ปรับขนาดให้เหมาะสม

เป้าหมายหลักของขั้นตอนการทำงานของเราคือการสร้าง โมดูล JavaScript ที่กำหนดเองสำหรับ TensorFlow.js ที่มีเพียงฟังก์ชันที่จำเป็นสำหรับโปรแกรมที่เราพยายามเพิ่มประสิทธิภาพเท่านั้น เราอาศัยบันเดิลที่มีอยู่เพื่อทำการเพิ่มประสิทธิภาพจริง

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

  • ข้อมูลจำเพาะของโมเดลจัดเก็บไว้ในไฟล์ model.json
  • ทางเลือกของระบบการจัดส่งเคอร์เนลเฉพาะแบ็กเอนด์ที่เราใช้

สิ่งนี้ทำให้การสร้าง tfjs แบบกำหนดเองมีความเกี่ยวข้องมากกว่าการชี้บันเดิลไปยังแพ็คเกจ @tensorflow/tfjs ปกติ

วิธีสร้างชุดรวมแบบกำหนดเองที่ปรับขนาดให้เหมาะสม

ขั้นตอนที่ 1: พิจารณาว่าโปรแกรมของคุณใช้เคอร์เนลใด

ขั้นตอนนี้ช่วยให้เราระบุเคอร์เนลทั้งหมดที่ใช้โดยโมเดลใดๆ ที่คุณเรียกใช้หรือโค้ดก่อน/หลังการประมวลผลโดยให้แบ็กเอนด์ที่คุณเลือกไว้

ใช้ tf.profile เพื่อเรียกใช้ส่วนต่างๆ ของแอปพลิเคชันของคุณที่ใช้ tensorflow.js และรับเคอร์เนล มันจะมีลักษณะเช่นนี้

const profileInfo = await tf.profile(() => {
  // You must profile all uses of tf symbols.
  runAllMyTfjsCode();
});

const kernelNames = profileInfo.kernelNames
console.log(kernelNames);

คัดลอกรายการเมล็ดนั้นไปยังคลิปบอร์ดของคุณสำหรับขั้นตอนต่อไป

คุณต้องกำหนดโปรไฟล์โค้ดโดยใช้แบ็กเอนด์เดียวกันกับที่คุณต้องการใช้ในบันเดิลที่กำหนดเอง

คุณจะต้องทำซ้ำขั้นตอนนี้หากโมเดลของคุณเปลี่ยนแปลงหรือโค้ดก่อน/หลังการประมวลผลของคุณเปลี่ยนแปลง

ขั้นตอนที่ 2 เขียนไฟล์การกำหนดค่าสำหรับโมดูล tfjs ที่กำหนดเอง

นี่คือตัวอย่างไฟล์ปรับแต่ง

ดูเหมือนว่านี้:

{
  "kernels": ["Reshape", "_FusedMatMul", "Identity"],
  "backends": [
      "cpu"
  ],
  "models": [
      "./model/model.json"
  ],
  "outputPath": "./custom_tfjs",
  "forwardModeOnly": true
}
  • เมล็ดพืช: รายการเมล็ดพืชที่จะรวมไว้ในชุดรวม คัดลอกสิ่งนี้จากผลลัพธ์ของขั้นตอนที่ 1
  • แบ็กเอนด์: รายการแบ็กเอนด์ที่คุณต้องการรวม ตัวเลือกที่ถูกต้อง ได้แก่ "cpu", "webgl" และ "wasm"
  • โมเดล: รายการไฟล์ model.json สำหรับโมเดลที่คุณโหลดในแอปพลิเคชันของคุณ สามารถเว้นว่างได้หากโปรแกรมของคุณไม่ได้ใช้ tfjs_converter เพื่อโหลดโมเดลกราฟ
  • outputPath: เส้นทางไปยังโฟลเดอร์เพื่อใส่โมดูลที่สร้างขึ้น
  • forwardModeOnly: ตั้งค่านี้เป็นเท็จหากคุณต้องการรวมการไล่ระดับสีสำหรับเคอร์เนลที่แสดงไว้ก่อนหน้า

ขั้นตอนที่ 3 สร้างโมดูล tfjs แบบกำหนดเอง

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

npx tfjs-custom-module  --config custom_tfjs_config.json

สิ่งนี้จะสร้างโฟลเดอร์ที่ outputPath พร้อมไฟล์ใหม่บางไฟล์

ขั้นตอนที่ 4 กำหนดค่า Bundler ของคุณเป็นนามแฝง tfjs ไปยังโมดูลที่กำหนดเองใหม่

ในบันเดิลเช่น webpack และ rollup เราสามารถเรียกการอ้างอิงที่มีอยู่ไปยังโมดูล tfjs เพื่อชี้ไปที่โมดูล tfjs แบบกำหนดเองที่สร้างขึ้นใหม่ของเรา มีสามโมดูลที่ต้องมีนามแฝงเพื่อการประหยัดขนาดบันเดิลสูงสุด

นี่คือตัวอย่างสิ่งที่ดูเหมือนใน webpack ( ตัวอย่างเต็มที่นี่ ):

...

config.resolve = {
  alias: {
    '@tensorflow/tfjs$':
        path.resolve(__dirname, './custom_tfjs/custom_tfjs.js'),
    '@tensorflow/tfjs-core$': path.resolve(
        __dirname, './custom_tfjs/custom_tfjs_core.js'),
    '@tensorflow/tfjs-core/dist/ops/ops_for_converter': path.resolve(
        __dirname, './custom_tfjs/custom_ops_for_converter.js'),
  }
}

...

และนี่คือข้อมูลโค้ดที่เทียบเท่าสำหรับการยกเลิก ( ตัวอย่างเต็ม ที่นี่ ):

import alias from '@rollup/plugin-alias';

...

alias({
  entries: [
    {
      find: /@tensorflow\/tfjs$/,
      replacement: path.resolve(__dirname, './custom_tfjs/custom_tfjs.js'),
    },
    {
      find: /@tensorflow\/tfjs-core$/,
      replacement: path.resolve(__dirname, './custom_tfjs/custom_tfjs_core.js'),
    },
    {
      find: '@tensorflow/tfjs-core/dist/ops/ops_for_converter',
      replacement: path.resolve(__dirname, './custom_tfjs/custom_ops_for_converter.js'),
    },
  ],
}));

...

หากบันเดิลของคุณไม่รองรับโมดูลนามแฝง คุณจะต้องเปลี่ยนคำสั่ง import ของคุณเพื่อนำเข้า tensorflow.js จาก custom_tfjs.js ที่สร้างขึ้นซึ่งสร้างขึ้นในขั้นตอนที่ 3 คำจำกัดความของ Op จะไม่ถูกเขย่าแบบทรี แต่เคอร์เนลยังคงเป็นแบบต้นไม้ -เขย่า โดยทั่วไปเมล็ดที่เขย่าต้นไม้จะช่วยประหยัดขนาดมัดสุดท้ายได้มากที่สุด

หากคุณใช้เพียงแพ็คเกจ @tensoflow/tfjs-core คุณจะต้องใช้นามแฝงแพ็คเกจเดียวเท่านั้น

ขั้นตอนที่ 5 สร้างชุดรวมของคุณ

รัน Bundler ของคุณ (เช่น webpack หรือ rollup ) เพื่อสร้าง Bundle ของคุณ ขนาดของบันเดิลควรเล็กกว่าถ้าคุณรันบันเดิลโดยไม่มีนามแฝงโมดูล คุณยังสามารถใช้วิชวลไลเซอร์เช่น นี้ เพื่อดูว่าอะไรทำให้มันกลายเป็นชุดสุดท้ายของคุณ

ขั้นตอนที่ 6 ทดสอบแอปของคุณ

อย่าลืมทดสอบว่าแอปของคุณทำงานตามที่คาดไว้!