그래프

그래프

CalculatorGraphConfig proto는 서비스의 토폴로지와 기능을 MediaPipe 그래프 그래프의 각 node는 특정 계산기 또는 하위 그래프를 표시하고, 필요한 구성(예: 등록된 계산기/하위 그래프 유형, 입력, 출력 및 선택적 필드(예: 노드별 옵션, 입력 정책 및 실행자에 대한 설명은 동기화.

CalculatorGraphConfig에는 전역 그래프 수준을 구성하는 다른 여러 필드가 있음 설정(예: 그래프 실행기 구성, 스레드 수 및 최대 큐 크기 학습합니다. 여러 그래프 수준 설정은 다양한 플랫폼 (예: 데스크톱 vs. 모바일)에서의 그래프 실적 대상 예를 들어 모바일에서는 강력한 모델 추론 계산기를 실행자는 실시간 애플리케이션의 성능을 개선할 수 있습니다. 스레드 지역을 사용 설정합니다.

다음은 사소한 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는 복잡한 그래프 (예: ML 파이프라인, 모델 메타데이터 처리, 선택적 노드 등)를 위한 대체 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. 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"
    }
    

그래프 옵션

'그래프 옵션'을 지정할 수 있는데 MediaPipe 그래프용 protobuf Calculator Options와 유사 MediaPipe 계산기에 지정된 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는 그래프 옵션 protobuf를 허용합니다. FaceDetectionOptions입니다. FaceDetectionOptions는 일부 필드를 정의하는 데 사용됩니다. 계산기 옵션 ImageToTensorCalculatorOptions의 값 및 일부 필드 하위 그래프 옵션 InferenceCalculatorOptions의 값 필드 값 option_value: 구문을 사용하여 정의됩니다.

CalculatorGraphConfig::Node protobuf에서 필드 node_options:option_value:는 다음과 같은 계산기의 옵션 값을 함께 정의합니다. ImageToTensorCalculator입니다. node_options: 필드는 리터럴 집합을 정의합니다. 상수 값을 지원합니다. 각 option_value: 필드 는 인클로징의 정보를 사용하여 하나의 protobuf 필드의 값을 정의합니다. 특히 인클로징의 그래프 옵션 필드 값에서 그래프에서 찾을 수 있습니다. 위의 예에서 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는 계산기 그래프가 비순환적이어야 하며 주기를 처리합니다. 오류를 그래프로 표현합니다. 그래프에 주기가 있는 경우, 주기는 다음과 같아야 합니다. 그래프 구성에 주석을 달아야 합니다. 이 페이지에서는 그 방법을 설명합니다.

참고: 현재 접근 방식은 실험 단계이며 변경될 수 있습니다. Google은 의견을 보내주세요

CalculatorGraphTest.Cycle 단위 테스트를 사용하세요. mediapipe/framework/calculator_graph_test.cc를 샘플 코드로 사용합니다. 아래에 표시된 것은 순환 그래프에 사용됩니다. 가산기의 sum 출력은 정수 소스 계산기에 의해 생성된 정수입니다.

정수의 스트림을 추가하는 순환 그래프

이 간단한 그래프는 순환 그래프를 지원하는 모든 문제를 보여줍니다.

후면 가장자리 주석

각 주기의 에지에 백 에지로 주석을 달아야 합니다. 이렇게 하면 후면 가장자리를 모두 삭제한 후 MediaPipe의 위상 정렬이 작동합니다.

일반적으로 뒷면 가장자리를 선택하는 방법에는 여러 가지가 있습니다. 표시된 가장자리 어떤 노드가 업스트림으로 간주되는지, 어떤 노드가 다운스트림으로 간주되어 MediaPipe가 할당하는 우선순위에 영향을 미칩니다. 노드에 배포해야 합니다

예를 들어 CalculatorGraphTest.Cycle 테스트는 old_sum 가장자리를 따라서 지연 노드는 가산기의 다운스트림 노드로 간주됩니다. 더 높은 우선순위가 부여됩니다. 또는 sum를 표시할 수 있습니다. 백 에지로 지연 노드 입력을 전달하는 경우, 이 경우 지연 노드는 가산기 노드의 업스트림 노드로 간주되며 우선순위가 더 낮습니다.

초기 패킷

정수의 첫 번째 정수가 소스가 도착하면 값이 0이고 동일한 가산기에 대한 old_sum 입력 스트림의 타임스탬프 이 초기 패킷은 Open() 메서드의 지연 계산기로 출력되어야 합니다.

루프의 지연

각 루프에서 이전 sum 출력을 다음 출력과 정렬하기 위해 지연이 발생해야 합니다. 정수 입력 이 작업은 지연 노드에 의해서도 이루어집니다. 따라서 지연 노드는 정수 소스 계산기의 타임스탬프에 대해 다음 사항을 알아두시기 바랍니다.

  • 첫 번째 출력의 타임스탬프입니다.

  • 연속 출력 간의 타임스탬프 델타입니다.

패킷에만 관심을 갖는 대체 예약 정책을 추가할 계획입니다. 패킷 타임스탬프를 무시하므로 이러한 불편을 줄일 수 있습니다.

입력 스트림 1개 완료 시 계산기 조기 종료

기본적으로 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'
}