กราฟของฟังก์ชัน
Proto ของ CalculatorGraphConfig
ระบุโทโพโลยีและฟังก์ชันการทำงานของ
กราฟ MediaPipe node
แต่ละรายการในกราฟแสดงถึงเครื่องคิดเลขหนึ่งๆ หรือ
ย่อย และระบุการกำหนดค่าที่จำเป็น เช่น
ประเภทเครื่องคิดเลข/รหัสย่อย อินพุต เอาต์พุต และช่องที่ไม่บังคับ เช่น
ตัวเลือกเฉพาะโหนด นโยบายอินพุต และโปรแกรมดำเนินการ ซึ่งจะกล่าวถึงใน
การซิงค์
CalculatorGraphConfig
มีช่องอื่นๆ อีกหลายช่องสำหรับกำหนดค่าระดับกราฟทั่วโลก
การตั้งค่า เช่น การกำหนดค่าผู้ดำเนินการกราฟ จำนวนชุดข้อความ และขนาดคิวสูงสุด
ของสตรีมอินพุต การตั้งค่าระดับกราฟหลายอย่างมีประโยชน์ในการปรับแต่ง
ประสิทธิภาพของกราฟบนแพลตฟอร์มต่างๆ (เช่น เดสก์ท็อปเทียบกับอุปกรณ์เคลื่อนที่) สำหรับ
บนมือถือ การติดเครื่องคำนวณการอนุมานแบบจำลองขนาดใหญ่ไว้กับ
สามารถปรับปรุงประสิทธิภาพของแอปพลิเคชันแบบเรียลไทม์ได้
เปิดใช้งานที่ตั้งของชุดข้อความ
ด้านล่างเป็นตัวอย่าง CalculatorGraphConfig
ที่ไม่สำคัญซึ่งเรามี
เครื่องคำนวณ Passthrough :
# This graph named main_pass_throughcals_nosubgraph.pbtxt contains 4
# passthrough calculators.
input_stream: "in"
output_stream: "out"
node {
calculator: "PassThroughCalculator"
input_stream: "in"
output_stream: "out1"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out1"
output_stream: "out2"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out2"
output_stream: "out3"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out3"
output_stream: "out"
}
MediaPipe มีการนำเสนอ C++
ทางเลือกสำหรับกราฟที่ซับซ้อน (เช่น ไปป์ไลน์ ML, ข้อมูลเมตาของโมเดลการจัดการ, โหนดที่ไม่บังคับ เป็นต้น) กราฟด้านบนอาจมีลักษณะดังนี้
CalculatorGraphConfig BuildGraphConfig() {
Graph graph;
// Graph inputs
Stream<AnyType> in = graph.In(0).SetName("in");
auto pass_through_fn = [](Stream<AnyType> in,
Graph& graph) -> Stream<AnyType> {
auto& node = graph.AddNode("PassThroughCalculator");
in.ConnectTo(node.In(0));
return node.Out(0);
};
Stream<AnyType> out1 = pass_through_fn(in, graph);
Stream<AnyType> out2 = pass_through_fn(out1, graph);
Stream<AnyType> out3 = pass_through_fn(out2, graph);
Stream<AnyType> out4 = pass_through_fn(out3, graph);
// Graph outputs
out4.SetName("out").ConnectTo(graph.Out(0));
return graph.GetConfig();
}
ดูรายละเอียดเพิ่มเติมในการสร้างกราฟใน C++
กราฟย่อย
เพื่อทำให้ CalculatorGraphConfig
แยกเป็นโมดูลย่อยและช่วยให้ใช้งานซ้ำได้
ของโซลูชันการรับรู้ กราฟ MediaPipe สามารถกำหนดเป็น Subgraph
อินเทอร์เฟซสาธารณะของกราฟย่อยประกอบด้วยชุดสตรีมอินพุตและเอาต์พุต
คล้ายกับอินเทอร์เฟซสาธารณะของเครื่องคิดเลข จึงสามารถรวมกราฟย่อยไว้ใน
CalculatorGraphConfig
ราวกับว่าเป็นเครื่องคิดเลข เมื่อกราฟ MediaPipe คือ
ที่โหลดจาก CalculatorGraphConfig
โหนดกราฟย่อยแต่ละโหนดจะถูกแทนที่ด้วย
กราฟที่เกี่ยวข้องของเครื่องคำนวณ ด้วยเหตุนี้ อรรถศาสตร์และประสิทธิภาพการทำงาน
ของกราฟย่อยจะเหมือนกับกราฟเครื่องคิดเลขที่ตรงกัน
ด้านล่างคือตัวอย่างวิธีการสร้างกราฟย่อยชื่อ TwoPassThroughSubgraph
การกําหนดกราฟย่อย
# This subgraph is defined in two_pass_through_subgraph.pbtxt # and is registered as "TwoPassThroughSubgraph" type: "TwoPassThroughSubgraph" input_stream: "out1" output_stream: "out3" node { calculator: "PassThroughCalculator" input_stream: "out1" output_stream: "out2" } node { calculator: "PassThroughCalculator" input_stream: "out2" output_stream: "out3" }
อินเทอร์เฟซสาธารณะของกราฟย่อยประกอบด้วย
- เขียนกราฟสตรีมอินพุต
- เขียนกราฟสตรีมเอาต์พุต
- เขียนกราฟแพ็กเก็ตด้านข้างของอินพุต
- เขียนกราฟของแพ็กเก็ตด้านข้างเอาต์พุต
ลงทะเบียนกราฟย่อยโดยใช้กฎ BUILD
mediapipe_simple_subgraph
พารามิเตอร์register_as
จะระบุชื่อคอมโพเนนต์สำหรับกราฟย่อยใหม่# Small section of BUILD file for registering the "TwoPassThroughSubgraph" # subgraph for use by main graph main_pass_throughcals.pbtxt mediapipe_simple_subgraph( name = "twopassthrough_subgraph", graph = "twopassthrough_subgraph.pbtxt", register_as = "TwoPassThroughSubgraph", deps = [ "//mediapipe/calculators/core:pass_through_calculator", "//mediapipe/framework:calculator_graph", ], )
ใช้กราฟย่อยในกราฟหลัก
# This main graph is defined in main_pass_throughcals.pbtxt # using subgraph called "TwoPassThroughSubgraph" input_stream: "in" node { calculator: "PassThroughCalculator" input_stream: "in" output_stream: "out1" } node { calculator: "TwoPassThroughSubgraph" input_stream: "out1" output_stream: "out3" } node { calculator: "PassThroughCalculator" input_stream: "out3" output_stream: "out4" }
ตัวเลือกกราฟ
สามารถระบุ "ตัวเลือกกราฟ" Protobuf สำหรับกราฟ MediaPipe
คล้ายกับ Calculator Options
Protobuf ที่ระบุไว้สำหรับเครื่องคำนวณ MediaPipe "ตัวเลือกกราฟ" เหล่านี้ สามารถเป็น
ระบุเมื่อมีการเรียกใช้กราฟ และใช้เพื่อป้อนข้อมูลตัวเลือกเครื่องคำนวณและ
ตัวเลือกกราฟย่อยภายในกราฟ
ใน CalculatorGraphConfig
คุณสามารถระบุตัวเลือกกราฟสำหรับกราฟย่อยได้
เหมือนกับตัวเลือกเครื่องคิดเลข ดังที่แสดงด้านล่าง
node {
calculator: "FlowLimiterCalculator"
input_stream: "image"
output_stream: "throttled_image"
node_options: {
[type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
max_in_flight: 1
}
}
}
node {
calculator: "FaceDetectionSubgraph"
input_stream: "IMAGE:throttled_image"
node_options: {
[type.googleapis.com/mediapipe.FaceDetectionOptions] {
tensor_width: 192
tensor_height: 192
}
}
}
ใน CalculatorGraphConfig
ระบบสามารถยอมรับและใช้ตัวเลือกกราฟเพื่อป้อนข้อมูล
เครื่องคิดเลข ดังที่แสดงด้านล่าง
graph_options: {
[type.googleapis.com/mediapipe.FaceDetectionOptions] {}
}
node: {
calculator: "ImageToTensorCalculator"
input_stream: "IMAGE:image"
node_options: {
[type.googleapis.com/mediapipe.ImageToTensorCalculatorOptions] {
keep_aspect_ratio: true
border_mode: BORDER_ZERO
}
}
option_value: "output_tensor_width:options/tensor_width"
option_value: "output_tensor_height:options/tensor_height"
}
node {
calculator: "InferenceCalculator"
node_options: {
[type.googleapis.com/mediapipe.InferenceCalculatorOptions] {}
}
option_value: "delegate:options/delegate"
option_value: "model_path:options/model_path"
}
ในตัวอย่างนี้ FaceDetectionSubgraph
ยอมรับ Protobuf ตัวเลือกกราฟ
FaceDetectionOptions
ใช้ FaceDetectionOptions
เพื่อกำหนดช่อง
ค่าในตัวเลือกเครื่องคิดเลข ImageToTensorCalculatorOptions
และบางช่อง
ในตัวเลือกกราฟย่อย InferenceCalculatorOptions
ค่าของช่อง
ถูกกำหนดโดยใช้ไวยากรณ์ option_value:
ใน CalculatorGraphConfig::Node
Protobuf ช่องnode_options:
และ
option_value:
ร่วมกันกำหนดค่าตัวเลือกสำหรับเครื่องคิดเลข เช่น
ImageToTensorCalculator
ฟิลด์ node_options:
กำหนดชุดลิเทอรัล
ค่าคงที่โดยใช้ไวยากรณ์ Probuf ของข้อความ ช่อง option_value:
แต่ละช่อง
กำหนดค่าของฟิลด์ proftobuf โดยใช้ข้อมูลจากฟิลด์
โดยเฉพาะจากค่าฟิลด์ของตัวเลือกกราฟที่ล้อมรอบอยู่
กราฟ ในตัวอย่างข้างต้น option_value:
"output_tensor_width:options/tensor_width"
กำหนดฟิลด์
ImageToTensorCalculatorOptions.output_tensor_width
โดยใช้ค่าของ
FaceDetectionOptions.tensor_width
ไวยากรณ์ของ option_value:
คล้ายกับไวยากรณ์ของ input_stream:
ไวยากรณ์คือ option_value: "LHS:RHS"
LHS จะระบุตัวเลือกเครื่องคำนวณ
และ RHS จะระบุฟิลด์ตัวเลือกกราฟ กล่าวอย่างเจาะจงคือ LHS
และ RHS แต่ละรายการประกอบด้วยชุดชื่อฟิลด์ Protobuf ที่ระบุค่าที่ฝังไว้
ข้อความและฟิลด์ Protobuf ที่คั่นด้วย "/" เครื่องมือนี้รู้จักกันในชื่อ "ProtoPath"
ไวยากรณ์ ข้อความที่ซ้อนกันที่อ้างอิงใน LHS หรือ RHS ต้องเป็นข้อความ
ที่กำหนดไว้ใน Protobuf ที่ล้อมรอบอยู่ เพื่อให้ข้ามผ่านได้โดยใช้
option_value:
ช่วงเวลา
โดยค่าเริ่มต้น MediaPipe ต้องการให้กราฟเครื่องคิดเลขเป็นวงจรและแสดงเป็นวงจร ในกราฟเป็นข้อผิดพลาด หากตั้งใจให้มีรอบ รอบจะต้อง ลงในการกำหนดค่ากราฟ หน้านี้อธิบายวิธีดำเนินการดังกล่าว
หมายเหตุ: วิธีการปัจจุบันอยู่ในขั้นทดลองและอาจมีการเปลี่ยนแปลง เรายินดีต้อนรับ ความคิดเห็นของคุณ
โปรดใช้การทดสอบหน่วย CalculatorGraphTest.Cycle
ใน
mediapipe/framework/calculator_graph_test.cc
เป็นโค้ดตัวอย่าง ด้านล่างคือ
กราฟแบบวนซ้ำในการทดสอบ เอาต์พุต sum
ของแอดเดอร์คือผลรวมของ
จำนวนเต็มที่สร้างโดยเครื่องคำนวณแหล่งที่มาจำนวนเต็ม
กราฟแบบง่ายนี้แสดงให้เห็นถึงปัญหาทั้งหมดในการสนับสนุนกราฟแบบวนซ้ำ
หมายเหตุของขอบด้านหลัง
เราต้องการให้ขอบในแต่ละรอบมีคำอธิบายประกอบเป็นขอบด้านหลัง วิธีนี้ช่วยให้ การจัดเรียงโทโพโลยีของ MediaPipe ทำงานได้หลังจากที่นำขอบด้านหลังทั้งหมดออก
โดยปกติแล้วการเลือกขอบด้านหลังทำได้หลายวิธี ขอบที่ทำเครื่องหมายไว้ เนื่องจากขอบหลังส่งผลต่อโหนดที่ถือว่าเป็นอัปสตรีม และโหนดใด ถือเป็นปลายทาง ซึ่งก็ส่งผลต่อลำดับความสำคัญที่ MediaPipe กำหนด กับโหนด
ตัวอย่างเช่น การทดสอบ CalculatorGraphTest.Cycle
จะทำเครื่องหมาย EDGE old_sum
เป็น
ย้อนกลับ ดังนั้นโหนดหน่วงเวลาจึงถือเป็นโหนดดาวน์สตรีมของแอดเดอร์
โหนดและมีลำดับความสำคัญสูงกว่า หรือเราอาจทำเครื่องหมาย sum
เข้ากับโหนดการหน่วงเวลาเป็นขอบด้านหลัง ซึ่งในกรณีนี้โหนดความล่าช้าจะเป็น
ถือว่าเป็นโหนดอัปสตรีมของโหนด Adder และมีลำดับความสำคัญต่ำกว่า
แพ็กเก็ตเริ่มต้น
สำหรับให้เครื่องคำนวณแอดเดอร์เรียกใช้ได้เมื่อจำนวนเต็มแรกจากจำนวนเต็ม
ต้นทางมาถึง เราต้องการแพ็กเก็ตเริ่มต้นที่มีค่า 0 และ
การประทับเวลาในสตรีมอินพุต old_sum
ไปยังแอดเดอร์ แพ็กเก็ตเริ่มต้นนี้
ควรเอาต์พุตโดยเครื่องคำนวณความล่าช้าในเมธอด Open()
ความล่าช้าในลูป
การวนซ้ำแต่ละรายการควรมีการหน่วงเวลาเพื่อปรับเอาต์พุต sum
ก่อนหน้าให้ตรงกับรายการถัดไป
การป้อนจำนวนเต็ม โหนดการหน่วงเวลาก็ทำได้เช่นกัน ดังนั้นโหนดการหน่วงเวลาจะต้อง
รู้ข้อมูลต่อไปนี้เกี่ยวกับการประทับเวลาของเครื่องคำนวณแหล่งที่มาจำนวนเต็ม
การประทับเวลาของเอาต์พุตแรก
เดลต้าของการประทับเวลาระหว่างเอาต์พุตที่ต่อเนื่องกัน
เราวางแผนที่จะเพิ่มนโยบายการกำหนดเวลาอื่นๆ ซึ่งให้ความสำคัญกับแพ็กเก็ตเท่านั้น การจัดเรียงและไม่ต้องคำนึงถึงการประทับเวลาแพ็กเก็ต ซึ่งจะช่วยขจัดความไม่สะดวกเช่นนี้
การสิ้นสุดเครื่องคิดเลขล่วงหน้าเมื่อสตรีมอินพุต 1 รายการเสร็จสิ้น
โดยค่าเริ่มต้น MediaPipe จะเรียกเมธอด Close()
ของเครื่องคำนวณที่ไม่ใช่แหล่งที่มาเมื่อ
สตรีมอินพุตทั้งหมดเสร็จแล้ว ในกราฟตัวอย่าง เราต้องการหยุด
Adder ทันทีเมื่อแหล่งข้อมูลจำนวนเต็มเสร็จสิ้น โดยสามารถทำได้ด้วย
กำหนดค่าโหนดแอดเดอร์ด้วยเครื่องจัดการสตรีมอินพุตสำรอง
EarlyCloseInputStreamHandler
ซอร์สโค้ดที่เกี่ยวข้อง
เครื่องคำนวณความล่าช้า
จดบันทึกโค้ดใน Open()
ที่เอาต์พุตแพ็กเก็ตเริ่มต้นและโค้ดใน
Process()
ที่เพิ่มความล่าช้า (หน่วย) ลงในแพ็กเก็ตอินพุต ตามที่ระบุไว้ข้างต้น
โหนดดีเลย์จะถือว่ามีการใช้สตรีมเอาต์พุตควบคู่ไปกับสตรีมอินพุตที่มี
การประทับเวลาแพ็กเก็ต 0, 1, 2, 3, ...
class UnitDelayCalculator : public Calculator {
public:
static absl::Status FillExpectations(
const CalculatorOptions& extendable_options, PacketTypeSet* inputs,
PacketTypeSet* outputs, PacketTypeSet* input_side_packets) {
inputs->Index(0)->Set<int>("An integer.");
outputs->Index(0)->Set<int>("The input delayed by one time unit.");
return absl::OkStatus();
}
absl::Status Open() final {
Output()->Add(new int(0), Timestamp(0));
return absl::OkStatus();
}
absl::Status Process() final {
const Packet& packet = Input()->Value();
Output()->AddPacket(packet.At(packet.Timestamp().NextAllowedInStream()));
return absl::OkStatus();
}
};
การกำหนดค่ากราฟ
จดคำอธิบายประกอบ back_edge
และ input_stream_handler
สำรอง
node {
calculator: 'GlobalCountSourceCalculator'
input_side_packet: 'global_counter'
output_stream: 'integers'
}
node {
calculator: 'IntAdderCalculator'
input_stream: 'integers'
input_stream: 'old_sum'
input_stream_info: {
tag_index: ':1' # 'old_sum'
back_edge: true
}
output_stream: 'sum'
input_stream_handler {
input_stream_handler: 'EarlyCloseInputStreamHandler'
}
}
node {
calculator: 'UnitDelayCalculator'
input_stream: 'sum'
output_stream: 'old_sum'
}