For the complete documentation index, see llms.txt. This page is also available as Markdown.

워크플로우 실행

워크플로우가 실행되는 방식: 컴파일, 배치 지향 데이터, 차원 수준, 조건부 실행, 출력 구성.

워크플로 실행은 복잡한 주제이지만, 효과적으로 시작하려면 모든 세부 사항을 이해할 필요는 없습니다. 몇 가지 기본 개념을 이해하면 Workflows 생태계에서의 학습 속도를 크게 높일 수 있습니다. 이 문서는 기본 개념을 빠르게 파악하고 더 강력한 애플리케이션을 만드는 데 도움이 되도록 설계된, 명확하고 간단한 개요를 제공합니다.

더 깊은 기술적 이해에 관심이 있는 분들을 위해, 더 자세한 정보를 담은 개발자 가이드를 살펴보시길 권합니다.

컴파일

워크플로 실행은 Workflow 정의를 컴파일하는 것에서 시작합니다. 아시다시피 Workflow 정의는 입력, 단계, 출력, 그리고 요소 간 연결을 설명하는 JSON 문서입니다. 이 문서를 실행 가능한 형식으로 바꾸려면 컴파일해야 합니다.

실행 엔진의 관점에서 이 과정은 계산 그래프를 만들고 그 무결성과 정확성을 검사하는 것을 의미합니다. 이 검증 단계는 오류를 조기에 찾아 알려 주어 문제를 더 쉽고 빠르게 디버깅할 수 있게 해주므로 매우 중요합니다. 예를 들어, 호환되지 않는 블록을 연결하거나, 잘못된 셀렉터를 사용하거나, 워크플로에 루프를 만들면 컴파일러가 오류 메시지로 알려 줍니다.

컴파일이 완료되면 워크플로를 실행할 준비가 되었다는 뜻입니다. 이는 다음을 확인해 줍니다:

  • 워크플로가 현재 환경의 실행 엔진 버전과 호환됩니다.

  • 워크플로의 모든 블록이 성공적으로 로드되고 초기화되었습니다.

  • 블록 간 연결이 유효합니다.

  • 워크플로에 대해 제공한 입력 데이터가 검증되었습니다.

이 시점에서 실행 엔진은 워크플로 실행을 시작할 수 있습니다.

워크플로 실행에서의 데이터

워크플로를 실행할 때마다 입력 데이터를 제공하게 됩니다. 프로그래밍의 함수가 서로 다른 입력 값을 처리할 수 있는 것처럼, 워크플로도 실행할 때마다 서로 다른 데이터 조각을 처리할 수 있습니다. 워크플로 실행을 시작하면 데이터에 어떤 일이 일어나는지 살펴봅시다.

워크플로에 정의된 입력 자리표시자를 대체하는 입력 데이터를 제공합니다. 이러한 자리표시자는 셀렉터를 사용해 워크플로의 단계에서 참조됩니다. 단계가 실행될 때 그 순간 제공된 실제 데이터 조각이 계산에 사용됩니다. 그 출력은 이후 워크플로 정의에 선언된 단계 출력 셀렉터를 바탕으로 다른 단계에서 사용할 수 있으며, 워크플로가 완료되고 모든 출력이 생성될 때까지 이 과정이 계속됩니다.

워크플로 정의의 고정된 값 매개변수를 제외하면, 정의 자체에는 실제 데이터 값이 포함되지 않습니다. 그것은 단지 입력으로 제공한 데이터를 실행 엔진이 어떻게 전달하고 처리할지 알려 줄 뿐입니다.

데이터란 무엇인가?

워크플로의 입력 데이터는 두 가지 유형으로 나눌 수 있습니다:

  • 처리할 배치 지향 데이터: 결과를 도출할 것으로 기대하는, 처리 대상의 주 데이터(예: 모델로 추론 수행)

  • 스칼라: 특정 설정이나 구성에 사용되는 단일 값입니다.

아래에 제시된 것 같은 표준 데이터 처리 방식을 생각해 보면, 스칼라와 배치 지향 데이터의 구분이 인위적으로 느껴질 수 있습니다.

값이 다른 매개변수로 손쉽게 제출할 수 있으며 그 매개변수를 두 범주 중 하나에 굳이 넣어 생각하지 않아도 됩니다.

머신러닝 모델에서는 상황이 더 복잡해집니다. is_even(...)와 같은 단순한 함수는 한 번에 하나의 숫자만 처리하지만, ML 모델은 종종 여러 데이터 조각을 한꺼번에 처리합니다. 예를 들어 분류 모델에 이미지 한 장만 제공하는 대신, 보통 이미지 목록을 제출하고 모든 이미지에 대한 예측을 한 번에 받을 수 있으며, 이는 각 이미지에 대해 동일한 연산을 수행하는 것입니다.

이는 결과 목록을 얻기 위해 각 숫자마다 별도로 호출해야 하는 우리 is_even(...) 함수와는 다릅니다. 그 차이는 ML 모델이 작동하는 방식, 특히 GPU가 데이터를 처리하는 방식에서 비롯됩니다. 즉, 동일한 연산을 여러 데이터 조각에 동시에 적용하여 단일 명령 다중 데이터 연산을 실행하는 것입니다.

is_even(...) 함수는 다음과 같이 루프를 사용하여 배치 데이터 처리를 하도록 조정할 수 있습니다:

Workflows에서는 보통 브로드캐스팅된 연산을 배치 데이터에 대해 걱정할 필요가 없습니다. 실행 엔진이 뒤에서 대신 처리해 주기 때문입니다. 하지만 배치 지향 우리가

분류 모델에서 예측을 만드는 표준 방식은 다음 의사 코드로 설명할 수 있습니다:

아마도 이미지confidence_threshold의 차이를 알아차렸을 것입니다. 앞의 것은 단일 연산(모델의 예측)을 적용할 데이터 배치이고, 뒤의 것은 배치의 모든 요소에 대한 처리를 좌우하는 매개변수이며, 우리는 이런 유형의 데이터를 스칼라.

그 차이를 설명하기 위해, Workflow 정의는 두 범주의 입력을 포함합니다:

  • 스칼라 입력 - 예를 들면 WorkflowParameter

  • 배치 입력 - 예를 들면 WorkflowImage, WorkflowVideoMetadata 또는 WorkflowBatchInput

단일 이미지를 WorkflowImage 입력으로 제공하면 자동으로 배치로 확장됩니다. Workflow 정의에 여러 WorkflowImage 자리표시자가 포함되어 있다면, 실행을 위해 제공하는 실제 데이터는 이 모든 입력에 대해 동일한 배치 크기를 가져야 합니다. 유일한 예외는 단일 이미지를 제출하는 경우인데, 이 이미지는 다른 입력의 배치 크기 요구 사항에 맞도록 브로드캐스트됩니다.

단계와 데이터의 상호작용

다음 상황에서 단계 출력의 본질에 대해 묻는다면:

  • A: 단계가 입력으로 스칼라 매개변수만 받습니다.

  • B: 단계가 입력으로 배치 지향 데이터를 받습니다.

  • C: 단계가 입력으로 스칼라 매개변수와 배치 지향 데이터를 모두 받습니다.

아마 이렇게 답할 것입니다:

  • 옵션 A에서는 출력이 비배치입니다.

  • 옵션 B와 C에서는 출력이 배치가 됩니다. 옵션 C에서는 비배치 지향 매개변수가 데이터의 배치 크기에 맞도록 브로드캐스트됩니다.

그리고 그 답은 맞습니다. 이것만 알면 Workflows 전문가가 되기 위해 이해해야 할 개념이 두 개 더 남았습니다.

예를 들어 다음 단계들로 구성된 Workflow를 만들고 싶다고 해봅시다:

  1. 입력 이미지 배치에서 객체를 탐지합니다.

  2. 이미지에서 탐지된 각 객체를 잘라냅니다.

  3. 두 번째 모델로 각 잘린 객체를 분류하여 자세한 레이블을 추가합니다.

자르기 단계에서 데이터에 어떤 일이 일어나는지 살펴보겠습니다:

  1. 이미지 배치로 시작합니다. 예를 들어 n 장의 이미지가 있다고 합시다.

  2. 객체 탐지 모델은 각 이미지에서 서로 다른 수의 객체를 찾습니다.

  3. 그다음 자르기 단계는 탐지된 각 객체마다 새 이미지를 생성하여, 원본 이미지마다 새로운 이미지 배치를 만듭니다.

따라서 중첩된 이미지 목록을 얻게 되며, 크기는 [(k[1], ), (k[2], ), ... (k[n])],와 같고, 각 k[i] 는 탐지 수에 따라 크기가 달라지는 이미지 배치입니다. 두 번째 모델(분류기)은 이 중첩된 잘린 이미지 배치를 처리합니다. 더 깊은 중첩 배치 구조로 나아가는 것을 막는 것도 없습니다.

여기서 복잡해지지만, 실행 엔진이 이 복잡성을 단순화합니다. 실행 엔진은 데이터의 중첩을 가상적으로 관리하므로, 블록은 항상 평평하고 비중첩된 형식으로 데이터를 받습니다. 덕분에 객체 탐지 모델이나 분류기처럼 같은 블록을 데이터가 얼마나 깊게 중첩되어 있든 상관없이 더 쉽게 적용할 수 있습니다. 하지만 그 대가로 차원 수준 어떤 단계들을 연결할 수 있고 어떤 단계들은 연결할 수 없는지를 결정하는

차원 수준 개념은 배치의 중첩 수준을 가리킵니다. 배치 지향 Workflow 입력은 차원 수준 1, 우리 예시에서 설명한 크롭은 차원 수준 2 입니다. 특정 단계에 입력을 연결할 때 중요한 것은 다음입니다:

  • 차이 차원 수준 단계 입력 간의

  • 단계가 차원 수준 출력의 차원에 미치는 영향(단계는 차원을 낮추거나, 유지하거나, 높일 수 있음)

대부분의 블록은 동일한 차원 수준의 입력과 함께 작동하도록 설계되어 있으며, 출력 차원을 바꾸지 않지만, 이 규칙의 예외도 있습니다. 우리 예시에서 객체 탐지 모델의 예측은 차원 수준 1차원 수준 1을 차지하고, 분류 결과는 차원 수준 2차원 수준 2에 있습니다. 이는 자르기 단계가 새롭고 동적인 차원 수준을 도입했기 때문입니다.

이제 객체 탐지 예측과 분류 예측을 모두 받아들이는 블록을 찾을 수 있다면, 그 블록이 그러한 차원 수준의 조합을 명시적으로 허용한다고 적어 둔 경우에만 예측을 함께 사용할 수 있고, 그렇지 않으면 컴파일 오류가 발생합니다. 바라건대, 이 문맥에서 사용할 수 있는 블록이 있을 것입니다.

감지 클래스 대체

Detections Classes Replacement 블록은 첫 번째 모델이 예측한 바운딩 박스를 기준으로 원본 이미지의 크롭에서 수행한 분류 모델의 예측으로 바운딩 박스 클래스 레이블을 대체하도록 설계되었습니다.

Workflows 컴파일러는 데이터 계보 를 Workflow 정의에서 추적하여, 더 높은 차원 수준의 조합 에서 같은 출처에서 오지 않은 데이터를 서로 섞는 것을 불가능하게 만듭니다. 이 개념은 개발자 가이드에서 자세히 설명합니다. 사용자 관점에서 중요한 것은, 다른 모델의 예측을 기준으로 이미지가 잘리면(혹은 같은 모델이라도 자르기 단계를 두 번 사용하면) 자르기 출력은 동일한 차원 수준에 있더라도 같은 단계의 입력으로 사용할 수 없다는 점을 이해하는 것입니다.

조건부 실행

솔직히 말해, 프로그래머는 분기를 좋아합니다. 그리고 그럴 만한 이유가 있죠. 이는 프로그래밍 언어에서 흔하고 유용한 구조입니다.

예를 들어 다음 코드에서 무슨 일이 일어나는지 쉽게 이해할 수 있습니다:

하지만 이 코드는 어떨까요?

이 경우 문자열 배치에 대해 분기가 어떻게 작동할지 즉시 명확하지 않습니다. 단일 항목에 대한 결정을 처리하는 개념은 간단하지만, 배치를 다룰 때는 논리가 여러 입력을 한꺼번에 고려해야 합니다. 문제는 배치의 각 요소마다 독립적인 결정을 내려야 하기 때문에 발생하며, 이는 배치의 서로 다른 요소에 대해 서로 다른 실행 분기로 이어질 수 있습니다. 제시한 것처럼 이렇게 단순한 예에서는 쉽게 해결할 수 있습니다:

하지만 Workflows에서는 블록 내부에 조건문을 구현하고 병합된 결과를 반환하기보다, 블록이 실행 경로를 결정하기를 원합니다. 그래서 Workflows 실행 엔진에서 조건부 실행의 전체 메커니즘이 등장했습니다. 이 개념은 중요하며 자체적인 기술적 깊이가 있지만, 사용자 관점에서 이해해야 할 몇 가지 사항이 있습니다:

  • 일부 Workflows 블록은 실행 흐름에 영향을 미칠 수 있습니다. 그런 블록들로 이루어진 단계에는 다음 단계로 가능한 경로를 결정하는 여러 단계 셀렉터가 지정되며, 이는 배치의 각 요소 에 대해

  • 조건부 실행으로 인해 데이터 요소가 배치에서 제외되면, 처리 경로 아래의 영향을 받는 모든 단계에서 그 요소는 숨겨지고 출력에서는 None

  • 다수의 흐름 제어 단계가 단일 다음 단계에 영향을 줄 수 있으며, 조건부 실행 마스크의 합집합이 생성되어 동적으로 적용됩니다.

  • 조건부 실행 논리 평가 후 단계에 남아 있는 입력이 없다면 해당 단계는 실행되지 않을 수 있습니다.

  • 대체 실행 분기를 병합할 수 있는 특수 블록이 있으며, 그 분기의 데이터는 하나의 셀렉터로 참조될 수 있습니다(예: 출력 생성). 그러한 블록의 예는 첫 번째 비어 있지 않은 값 또는 기본값 입니다. 이 블록은 처음 만난 값을 취하거나, 값이 하나도 없으면 지정된 값으로 기본 설정하여 실행 분기를 접습니다.

  • 조건부 실행은 보통 Workflow 출력에 영향을 미칩니다. 분기에 의해 영향을 받는 모든 값은 실제로 선택적이며(빈 값을 채우는 특수 블록이 사용되지 않는 경우), 중첩된 결과는 데이터로 채워지지 않아 결과에 빈(잠재적으로 중첩된) 목록이 남을 수 있습니다. 자세한 내용은 출력 구성.

출력 구성

가장 중요한 점은 Workflow의 출력이 배치 요소 순서와 관련하여 입력과 정렬된다는 것입니다. 즉, 출력은 항상 딕셔너리 목록이며, 각 딕셔너리는 입력 배치의 한 항목에 대응합니다. 이 구조는 결과를 파싱하고 반복적으로 처리하기 쉽게 만들어 주며, 출력과 입력을 맞춰 줍니다.

목록의 각 요소는 다음과 같은 선언을 통해 Workflow 정의에 지정된 키를 가진 딕셔너리입니다:

하지만 해당 키 아래에 기대할 수 있는 값은 워크플로의 구조에 따라 달라집니다. 모든 비배치 결과는 브로드캐스트되어 각 출력 딕셔너리에 동일한 값으로 들어갑니다. 차원 수준 1 에 있는 요소들은 차원 수준의 조합 균등하게 분배되며, 각 딕셔너리의 값은 입력 데이터의 정렬에 대응합니다(입력 이미지 3에 대한 예측은 세 번째 딕셔너리에 들어갑니다). 더 높은

에 있는 요소들은 "object_detection_predictions" 로 등록되고, 분류기의 결과가 "classifier_predictions"로 등록된다고 가정하면, 세 이미지를 입력으로 워크플로 실행에 제출했을 때 다음과 같은 출력을 기대할 수 있습니다:

보시다시피, "classifier_predictions" 필드는 결과 목록으로 채워지며, 그 크기는 "object_detection_predictions".

에 대한 바운딩 박스 수와 같습니다. 흥미롭게도, 워크플로에 바운딩 박스 수가 2가 아닐 때만 크롭과 분류기를 실행하는 ContinueIf 블록이 있다면, 첫 번째 딕셔너리의 classifier_predictions 를 빈 목록으로 바꿀 것입니다. 조건부 실행이 실행의 부작용으로 더 높은 차원 수준의 조합 에서 단계를 출력 생성에서 제외하면, 그 값을 선택하는 출력 필드는 빈 목록의 중첩 목록으로 표시되며, 깊이는 참조된 출력의 차원 수준 - 1 과 일치합니다.

Execution Engine v1.6.0또한 워크플로 내의 블록은 배치를 스칼라로 접을 수 있고, 반대로 스칼라 입력으로부터 새 배치를 만들 수도 있습니다. 첫 번째 시나리오는 이해하기 꽤 쉽습니다. 출력 목록의 각 딕셔너리에는 단순히 동일한 스칼라 값이 채워집니다. 발생하는 배치의 경우는 조금 더 복잡합니다. 이 경우 입력 배치와 모양이나 요소 순서가 일치하지 않는 차원 수준 1의 배치를 찾을 수 있습니다. 의미상의 모호성을 방지하기 위해, 우리는 그러한 배치를 차원 수준이 한 단계 더 높은 것처럼(마치 배치를 동적으로 만드는 블록의 입력에 크기 1의 추가 배치 지향 입력이 붙어 있는 것처럼). 이러한 가상적으로 중첩된 출력은 브로드캐스트되어 출력 목록의 각 딕셔너리마다 동일한 중첩 출력이 담긴 새 키가 주어집니다. 이러한 중첩 속성은 특정 워크플로에 입력에서 파생된 출력이 없더라도 유지됩니다. 그런 경우 출력은 중첩 출력을 포함한 딕셔너리 하나를 담은 크기 1의 목록입니다.

일부 출력은 Workflows 실행 엔진이 HTTP API 뒤에서 실행될 때 직렬화가 필요합니다. 우리는 다음과 같은 직렬화 전략을 사용합니다:

  • 이미지는 base64

  • 로 직렬화됩니다

  • numpy 배열은 목록으로 직렬화됩니다 inference sv.Detections는 sv.Detections.from_inference(...)

형식으로 직렬화되어, 와이어 반대편에서 JsonField 를 사용해 디코딩할 수 있습니다. sv.Detections는 탐지 기반 예측의 표준 표현으로, 출력 생성기에서 특별하게 처리됩니다. 출력 정의는 선택적으로 coordinates_system

마지막 업데이트

도움이 되었나요?