กราฟ

กราฟของฟังก์ชัน

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

  1. การกําหนดกราฟย่อย

    # 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"
    }
    

    อินเทอร์เฟซสาธารณะของกราฟย่อยประกอบด้วย

    • เขียนกราฟสตรีมอินพุต
    • เขียนกราฟสตรีมเอาต์พุต
    • เขียนกราฟแพ็กเก็ตด้านข้างของอินพุต
    • เขียนกราฟของแพ็กเก็ตด้านข้างเอาต์พุต
  2. ลงทะเบียนกราฟย่อยโดยใช้กฎ 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",
        ],
    )
    
  3. ใช้กราฟย่อยในกราฟหลัก

    # 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'
}