กราฟ
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" }
อินเทอร์เฟซสาธารณะต่อกราฟย่อยประกอบด้วย
- สตรีมอินพุตกราฟ
- เขียนกราฟสตรีมเอาต์พุต
- เขียนกราฟของแพ็กเก็ตด้านข้างของอินพุต
- เขียนกราฟของแพ็กเก็ตด้านเอาต์พุต
ลงทะเบียนกราฟย่อยโดยใช้กฎการสร้าง
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
ยอมรับตัวเลือกกราฟ โพรโทบุฟ FaceDetectionOptions
FaceDetectionOptions
ใช้เพื่อระบุค่าของช่องบางส่วนในตัวเลือกเครื่องคิดเลข ImageToTensorCalculatorOptions
และค่าของช่องบางค่าในตัวเลือกกราฟย่อย InferenceCalculatorOptions
ค่าในช่องจะกำหนดโดยใช้ไวยากรณ์ option_value:
ใน CalculatorGraphConfig::Node
Protobuf ช่อง node_options:
และ option_value:
ร่วมกันจะกำหนดค่าตัวเลือกสำหรับเครื่องคิดเลข เช่น ImageToTensorCalculator
ฟิลด์ node_options:
กำหนดชุดของค่าคงที่แบบลิเทอรัลโดยใช้ไวยากรณ์ Protobuf ของข้อความ ช่อง option_value:
แต่ละช่องจะกําหนดค่าให้กับช่อง Protobuf 1 ช่องโดยใช้ข้อมูลจากกราฟที่แนบมา โดยเฉพาะจากค่าในช่องของตัวเลือกกราฟของกราฟที่แนบมา ในตัวอย่างด้านบน 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
ของตัวเพิ่มคือผลรวมของจำนวนเต็มที่สร้างขึ้นจากเครื่องคำนวณแหล่งที่มาของจำนวนเต็ม
กราฟง่ายๆ นี้แสดงให้เห็นถึงปัญหาทั้งหมดในกราฟแบบวนซ้ำที่สนับสนุน
หมายเหตุเกี่ยวกับขอบหลัง
เรากำหนดให้ระบุ EDGE ในแต่ละรอบเป็นขอบย้อนกลับ วิธีนี้ช่วยให้การจัดเรียงแบบโทโพโลยีของ MediaPipe ทำงานได้หลังจากนำขอบด้านหลังออกทั้งหมด
โดยปกติแล้ว การเลือกขอบด้านหลังทำได้หลายวิธี ขอบที่มีการทำเครื่องหมายเป็นขอบกลับจะส่งผลต่อโหนดที่ถือว่าเป็นอัปสตรีมและโหนดที่ถือว่าเป็นดาวน์สตรีม ซึ่งจะส่งผลต่อลำดับความสำคัญที่ MediaPipe กำหนดให้กับโหนด
เช่น การทดสอบ CalculatorGraphTest.Cycle
จะทำเครื่องหมาย EDGE old_sum
เป็น Edge ด้านหลัง ดังนั้นโหนด Delay จึงถือเป็นโหนดดาวน์สตรีมของโหนด Adder และมีลำดับความสำคัญสูงกว่า หรืออาจทำเครื่องหมายอินพุต sum
ไปยังโหนดการหน่วงเวลาเป็นขอบด้านหลัง ซึ่งในกรณีนี้โหนดการหน่วงเวลาจะถือว่าเป็นโหนดอัปสตรีมของโหนดส่วนเสริมและมีลำดับความสำคัญต่ำกว่า
แพ็กเก็ตเริ่มต้น
เราต้องการแพ็กเก็ตเริ่มต้นที่มีค่า 0 และมีการประทับเวลาเดียวกันในสตรีมอินพุต old_sum
ไปยังส่วนเสริม เพื่อให้สามารถเรียกใช้เครื่องคำนวณ Adder ได้เมื่อมีจำนวนจำนวนเต็มแรกจากแหล่งที่มาของจำนวนเต็ม แพ็กเก็ตเริ่มต้นนี้ควรแสดงผลโดยเครื่องคำนวณความล่าช้าในเมธอด Open()
หน่วงเวลาวนซ้ำ
แต่ละลูปควรมีการหน่วงเวลาเพื่อปรับเอาต์พุต sum
ก่อนหน้ากับอินพุตจำนวนเต็มถัดไป ซึ่งโหนดล่าช้าก็ทำได้เช่นกัน โหนดการหน่วงเวลาจึงจำเป็นต้องทราบข้อมูลต่อไปนี้เกี่ยวกับการประทับเวลาของเครื่องคำนวณแหล่งที่มาของจำนวนเต็ม
การประทับเวลาของเอาต์พุตแรก
เดลต้าการประทับเวลาระหว่างเอาต์พุตต่อเนื่อง
เราวางแผนที่จะเพิ่มนโยบายการกำหนดเวลาทางเลือกที่ให้ความสำคัญกับการสั่งซื้อแพ็กเก็ตเท่านั้นและไม่สนใจการประทับเวลาแพ็กเก็ต ซึ่งจะช่วยลดความไม่สะดวกนี้
การสิ้นสุดเครื่องคิดเลขล่วงหน้าเมื่อสตรีมอินพุตหนึ่งเสร็จสิ้น
โดยค่าเริ่มต้น MediaPipe จะเรียกเมธอด Close()
ของเครื่องคำนวณที่ไม่ใช่แหล่งที่มาเมื่อสตรีมอินพุตทั้งหมดเสร็จสิ้นแล้ว ในกราฟตัวอย่าง เราต้องการหยุดโหนด Adder ทันทีที่ต้นทางที่เป็นจำนวนเต็มเสร็จ ซึ่งทำได้ด้วยการกำหนดค่าโหนด 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'
}