ग्राफ़

ग्राफ़

CalculatorGraphConfig प्रोटो की मदद से टोपोलॉजी और फ़ंक्शन के बारे में पता चलता है MediaPipe ग्राफ़. ग्राफ़ का हर node एक खास कैलकुलेटर दिखाता है या सबग्राफ़ है और ज़रूरी कॉन्फ़िगरेशन तय करता है, जैसे कि रजिस्टर किया गया कैलकुलेटर/सबग्राफ़ टाइप, इनपुट, आउटपुट, और वैकल्पिक फ़ील्ड, जैसे कि नोड-विशिष्ट विकल्प, इनपुट नीति और कार्यकर्ता, जिन पर चर्चा की गई है सिंक्रोनाइज़ेशन.

CalculatorGraphConfig में ग्लोबल ग्राफ़-लेवल को कॉन्फ़िगर करने के लिए कई अन्य फ़ील्ड हैं सेटिंग, जैसे ग्राफ़ एक्ज़िक्यूटर कॉन्फ़िगरेशन, थ्रेड की संख्या, और सूची का ज़्यादा से ज़्यादा साइज़ का इस्तेमाल किया जा सकता है. ट्यूनिंग के लिए कई ग्राफ़-लेवल सेटिंग उपयोगी होती हैं अलग-अलग प्लैटफ़ॉर्म पर ग्राफ़ की परफ़ॉर्मेंस रिपोर्ट देखी जा सकती है. उदाहरण के लिए, डेस्कटॉप बनाम मोबाइल. इसके लिए उदाहरण के लिए, मोबाइल पर, एक भारी मॉडल-अनुमान कैलकुलेटर को किसी निष्पादनकर्ता इस तरह से रीयल-टाइम ऐप्लिकेशन के प्रदर्शन को बेहतर बना सकता है थ्रेड के इलाके को चालू करता है.

यहां CalculatorGraphConfig का एक छोटा सा उदाहरण दिया गया है, जिसमें हमारे पास पासथ्रू कैलकुलेटर :

# 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++ को दिखाता है. जैसे, एमएल पाइपलाइन, मॉडल मेटाडेटा को हैंडल करना, वैकल्पिक नोड वगैरह. ऊपर दिया गया ग्राफ़ ऐसा दिख सकता है:

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. बिल्ड नियम 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"
    }
    

ग्राफ़ विकल्प

"ग्राफ़ विकल्प" तय किया जा सकता है MediaPipe ग्राफ़ के लिए प्रोटोबफ़ Calculator Options से मिलता-जुलता MediaPipe Calculator के लिए तय किया गया Protobuf. ये "ग्राफ़ विकल्प" हो सकता है बताया गया हो कि किस जगह पर ग्राफ़ शुरू किया जाता है. साथ ही, इसका इस्तेमाल कैलकुलेटर के विकल्पों को पॉप्युलेट करने के लिए किया जाता है और ग्राफ़ के अंदर सबग्राफ़ विकल्प.

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 प्रोटोबफ़ में, फ़ील्ड node_options: और option_value: साथ मिलकर किसी कैलकुलेटर के लिए विकल्प का मान तय करता है, जैसे कि ImageToTensorCalculator. node_options: फ़ील्ड, लिटरल वैल्यू के किसी सेट के बारे में बताता है टेक्स्ट प्रोटोबफ़ सिंटैक्स का इस्तेमाल करने वाले कॉन्स्टेंट वैल्यू. हर option_value: फ़ील्ड यह पास में मौजूद जानकारी का इस्तेमाल करके, एक प्रोटोबफ़ फ़ील्ड की वैल्यू बताता है ग्राफ़, विशेष रूप से एनक्लोज़िंग के ग्राफ़ विकल्पों की फ़ील्ड वैल्यू से ग्राफ़. ऊपर दिए गए उदाहरण में, option_value: "output_tensor_width:options/tensor_width" फ़ील्ड के बारे में बताता है ImageToTensorCalculatorOptions.output_tensor_width इसका मान इस्तेमाल करके FaceDetectionOptions.tensor_width.

option_value: का सिंटैक्स और input_stream: का सिंटैक्स एक ही है. कॉन्टेंट बनाने सिंटैक्स option_value: "LHS:RHS" है. बाईं ओर मौजूद कॉलम से, कैलकुलेटर के विकल्प की पहचान की जाती है फ़ील्ड और RHS, ग्राफ़ विकल्प फ़ील्ड की पहचान करता है. खास तौर पर, बाईं ओर और दाईं ओर, हर साइट में नेस्ट किए गए प्रोटोबफ़ फ़ील्ड के नाम की सीरीज़ होती है. प्रोटोबफ़ मैसेज और फ़ील्ड को '/' से अलग किया जाता है. इसे "प्रोटोपाथ" के नाम से जाना जाता है सिंटैक्स. बाईं ओर मौजूद या दाईं ओर मौजूद, नेस्ट किए गए मैसेज पहले से ही ऐसे होने चाहिए को पास के प्रोटोबफ़ में परिभाषित किया गया है, ताकि इसका पता लगाया जा सके option_value:.

साइकल

डिफ़ॉल्ट रूप से, MediaPipe के लिए ज़रूरी है कि कैलकुलेटर ग्राफ़ एक साइक्लिक हो और उसे एक साइकल के तौर पर इस्तेमाल किया जाता हो दिखाया जा सकता है. अगर किसी ग्राफ़ में साइकल शामिल किए जाने के लिए बनाया गया है, तो साइकल में ग्राफ़ कॉन्फ़िगरेशन में व्याख्या की जाएगी. इस पेज पर बताया गया है कि इसे कैसे किया जा सकता है.

ध्यान दें: यह तरीका एक्सपेरिमेंट के तौर पर उपलब्ध है और इसमें बदलाव हो सकता है. आपका स्वागत है आपका सुझाव/राय या शिकायत.

कृपया इसमें CalculatorGraphTest.Cycle यूनिट टेस्ट का इस्तेमाल करें सैंपल कोड के तौर पर mediapipe/framework/calculator_graph_test.cc. नीचे दिखाया गया है साइकलिक ग्राफ़ को टेस्ट किया जा सकता है. ऐडर का sum आउटपुट पूर्णांक सोर्स कैलकुलेटर से जनरेट किए गए पूर्णांक.

साइक्लिक ग्राफ़, जो पूर्णांकों की स्ट्रीम जोड़ता है

यह सामान्य ग्राफ़, सहायक साइक्लिक ग्राफ़ की सभी समस्याओं को दिखाता है.

पिछले किनारे की व्याख्या

हम चाहते हैं कि हर साइकल के किसी किनारे को पिछले किनारे के तौर पर एनोटेट किया जाए. इससे आपको पिछले सभी किनारों को हटाने के बाद, MediaPipe की टोपोलॉजिकल सॉर्टिंग सुविधा काम करती है.

आम तौर पर, पीछे के किनारों को चुनने के कई तरीके होते हैं. कौन-से किनारे चिह्नित हैं क्योंकि पिछले किनारे इस बात पर असर डालते हैं कि कौनसे नोड अपस्ट्रीम माने जाते हैं और कौनसे नोड डाउनस्ट्रीम माना जाता है. इससे MediaPipe के असाइन की गई प्राथमिकताओं पर असर पड़ता है को नोड तक पहुंचाना है.

उदाहरण के लिए, CalculatorGraphTest.Cycle टेस्ट old_sum के किनारे को पिछला किनारे, इसलिए देरी नोड को ऐडर का डाउनस्ट्रीम नोड माना जाता है नोड को ज़्यादा प्राथमिकता दी जाती है और उसे ज़्यादा प्राथमिकता दी जाती है. इसके अलावा, हम sum को मार्क भी कर सकते हैं देरी नोड को पिछले किनारे के रूप में इनपुट करता है, जिस स्थिति में देरी नोड होगा इसे ऐडर नोड का अपस्ट्रीम नोड माना जाता है और इसे कम प्राथमिकता दी जाती है.

शुरुआती पैकेट

पूर्णांक में से पहला पूर्णांक होने पर ऐडर कैलकुलेटर को चलाया जा सके सोर्स आ गया, तो हमें 0 और उसी वैल्यू वाले शुरुआती पैकेट की ज़रूरत है जोड़ने वाले में old_sum इनपुट स्ट्रीम पर टाइमस्टैंप. यह शुरुआती पैकेट Open() तरीके में देरी कैलकुलेटर के हिसाब से आउटपुट होना चाहिए.

लूप में देरी

पिछले sum आउटपुट के साथ अलाइन करने के लिए, हर लूप में एक देरी होनी चाहिए पूर्णांक इनपुट. ऐसा देरी नोड से भी किया जाता है. इसलिए देरी नोड को इंटीजर सोर्स कैलकुलेटर के टाइमस्टैंप के बारे में यहां दी गई जानकारी देखें:

  • पहले आउटपुट का टाइमस्टैंप.

  • लगातार आने वाले आउटपुट के बीच का टाइमस्टैंप डेल्टा.

हम शेड्यूल करने से जुड़ी एक ऐसी वैकल्पिक नीति जोड़ने जा रहे हैं जिसमें सिर्फ़ पैकेट के बारे में जानकारी हो पैकेट टाइमस्टैंप को क्रम में लगाता है और उन्हें अनदेखा करता है. इससे आपको यह परेशानी नहीं होगी.

एक इनपुट स्ट्रीम पूरी हो जाने पर, कैलकुलेटर का पहले ही खत्म कर देना

डिफ़ॉल्ट रूप से, MediaPipe, नॉन-सोर्स कैलकुलेटर के Close() तरीके को तब कॉल करता है, जब इसकी सभी इनपुट स्ट्रीम कर दी जाती हैं. उदाहरण वाले ग्राफ़ में, हम इंटीजर सोर्स पूरा होते ही ऐडर नोड. यह काम इस तरह किया जाता है: किसी अन्य इनपुट स्ट्रीम हैंडलर के साथ ऐडर नोड को कॉन्फ़िगर करना, 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'
}