<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>고양이 두 잔</title>
    <link>https://gnidinger.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 14 Jul 2026 18:51:16 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Vagabund.Gni</managingEditor>
    <image>
      <title>고양이 두 잔</title>
      <url>https://tistory1.daumcdn.net/tistory/2718013/attach/6299a44926be4288af5ee5ca5377d266</url>
      <link>https://gnidinger.tistory.com</link>
    </image>
    <item>
      <title>삼각측량이 다 잡았다면, 필터는 정말 값을 했는가: 학습형 SmoothNet과 파이프라인 배치 | 삼각측량 후처리 (4)</title>
      <link>https://gnidinger.tistory.com/entry/%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89%EC%9D%B4-%EB%8B%A4-%EC%9E%A1%EC%95%98%EB%8B%A4%EB%A9%B4-%ED%95%84%ED%84%B0%EB%8A%94-%EC%A0%95%EB%A7%90-%EA%B0%92%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80-%ED%95%99%EC%8A%B5%ED%98%95-SmoothNet%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%B0%EC%B9%98-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-4</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%B6%80%EB%93%9C%EB%9F%AC%EC%9B%80%EA%B3%BC-%EB%B0%98%EC%9D%91%EC%84%B1%EC%9D%80-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%97%86%EB%8B%A4-%EC%A7%80%ED%84%B0-%EC%A7%80%EC%97%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C%EC%98%A4%ED%94%84%EC%99%80-Accel%EB%A1%9C-%EB%96%A8%EB%A6%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;부드러움과 반응성은 동시에 가질 수 없다: 지터-지연 트레이드오프와 Accel로 떨림 측정하기 | 삼각측량 후처리 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%91%90-%EA%B0%9C%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4-One-Euro-Savitzky-Golay-%EC%98%81%EC%9C%84%EC%83%81-Butterworth-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;파라미터 두 개로 실시간을 잡는다: One Euro, Savitzky-Golay, 영위상 Butterworth | 삼각측량 후처리 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%93%B1%EC%86%8D-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%AC%B4%EB%84%88%EC%A7%88-%EB%95%8C-Kalman-%EC%83%81%ED%83%9C%EC%B6%94%EC%A0%95%EA%B3%BC-IK-%EB%BC%88%EA%B8%B8%EC%9D%B4-%EC%A0%9C%EC%95%BD-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;등속 가정이 무너질 때: Kalman 상태추정과 IK 뼈길이 제약 | 삼각측량 후처리 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89%EC%9D%B4-%EB%8B%A4-%EC%9E%A1%EC%95%98%EB%8B%A4%EB%A9%B4-%ED%95%84%ED%84%B0%EB%8A%94-%EC%A0%95%EB%A7%90-%EA%B0%92%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80-%ED%95%99%EC%8A%B5%ED%98%95-SmoothNet%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%B0%EC%B9%98-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼각측량이 다 잡았다면, 필터는 정말 값을 했는가: 학습형 SmoothNet과 파이프라인 배치 | 삼각측량 후처리 (4)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CW7N9/dJMcagTKynj/Hp8q4uBOLvgIiYDbAoS0P1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CW7N9/dJMcagTKynj/Hp8q4uBOLvgIiYDbAoS0P1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CW7N9/dJMcagTKynj/Hp8q4uBOLvgIiYDbAoS0P1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCW7N9%2FdJMcagTKynj%2FHp8q4uBOLvgIiYDbAoS0P1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;다운로드.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부부터 3부까지 떨림의 측정과 여러 후처리 필터, 그리고 Kalman과 IK를 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 시리즈의 배경에는 한 가지 물음이 계속 깔려 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 대의 카메라로 삼각측량까지 마쳤다면, 그 뒤의 후처리 필터는 정말 값을 하는가이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4부는 이 물음에 답하기 위해, 먼저 삼각측량 단계가 이미 무엇을 정제하는지 짚는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 손으로 설계한 필터의 한계를 넘는 학습 기반 SmoothNet을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 필터를 파이프라인 어디에 거는지 정리하고, 마지막으로 필터의 실익을 측정으로 판단하는 방법을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하면, 필터의 값어치는 구조와 목적에 따라 달라지므로 측정으로 확인해야 한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;삼각측량은 후처리 전에 잡음을 걷어낸다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;여러 시점을 융합하는 삼각측량의 기하 제약&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;여러 시점을 융합하는 삼각측량의 기하 제약.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9PkyS/dJMcajphkIm/DMTIHXvKmdxW9PvHk2n3aK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9PkyS/dJMcajphkIm/DMTIHXvKmdxW9PvHk2n3aK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9PkyS/dJMcajphkIm/DMTIHXvKmdxW9PvHk2n3aK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9PkyS%2FdJMcajphkIm%2FDMTIHXvKmdxW9PvHk2n3aK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;여러 시점을 융합하는 삼각측량의 기하 제약.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼각측량은 여러 시점에서 관측한 같은 관절을 하나의 3차원 점으로 결합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 시점이 서로 다른 각도에서 같은 지점을 보므로, 광선들이 만나는 위치가 기하학적으로 제약된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 제약 덕분에 한 시점의 2차원 오차가 다른 시점들에 의해 상당 부분 상쇄된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 여러 뷰를 융합하는 것 자체가 단일 시점보다 잡음을 줄이는 효과를 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 후처리 필터를 걸기 전에 이미 삼각측량 단계에서 1차 정제가 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점이 후처리 필터의 역할을 따질 때 반드시 고려해야 할 출발점이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;RANSAC과 Huber 재가중으로 이상치를 걸러낸다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가림이나 오검출로 어떤 시점의 2차원 관절이 크게 어긋나면 이상치(outlier)가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RANSAC(Random Sample Consensus)은 최소 표본으로 모델 가설을 세우고, 재투영 오차로 지지도를 세어 이상치를 배제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 어긋난 관측을 딱 잘라 버리므로 소수의 큰 오차에 강건하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대안으로 IRLS(Iteratively Reweighted Least Squares, 반복 재가중 최소제곱)에 Huber 손실을 결합하면, 어긋난 관측의 가중치를 점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진적으로 낮춰 부드럽게 억제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상치를 걸러낸 뒤 번들 조정(bundle adjustment, 재투영 오차 최소화 최적화)으로 오차를 더 줄이기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 삼각측량 단계는 이상치 제거까지 포함해 상당한 정제를 이미 수행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783752380001&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

# 재투영 오차에 Huber 가중치를 매겨 어긋난 뷰를 부드럽게 억제
def huber_weight(residual, delta=2.0):
    # residual: 뷰별 재투영 오차(픽셀), delta: 임계값
    r = np.abs(residual)
    # 오차가 작으면 가중치 1, 크면 delta/r로 점점 낮춘다
    return np.where(r &amp;lt;= delta, 1.0, delta / r)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;신뢰도 게이팅으로 나쁜 관측을 배제한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;신뢰도 게이팅으로 나쁜 관측을 배제한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czlG4M/dJMcacqjnV7/EFn6doTJx7KHr5FoPEHNN1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czlG4M/dJMcacqjnV7/EFn6doTJx7KHr5FoPEHNN1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czlG4M/dJMcacqjnV7/EFn6doTJx7KHr5FoPEHNN1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczlG4M%2FdJMcacqjnV7%2FEFn6doTJx7KHr5FoPEHNN1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;신뢰도 게이팅으로 나쁜 관측을 배제한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 자세 추정기는 각 관절에 신뢰도(confidence) 점수를 함께 내놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰도가 낮은 관절은 가려졌거나 흐릿해 위치가 부정확할 가능성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일정 신뢰도 이상인 관측만 삼각측량에 넣는 게이팅을 흔히 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 시점 이상에서 신뢰도 있게 관측된 관절만 3차원으로 복원하는 식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 나쁜 관측이 3차원 결과를 오염시키는 것을 미리 막을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰도 게이팅은 이상치 제거의 앞단에서 입력 품질을 걸러 주는 장치이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;SmoothNet은 떨림 패턴을 데이터로 학습한다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;저역통과가 못 잡는 장기 떨림 문제&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;저역통과가 못 잡는 장기 떨림 문제.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjcnnn/dJMcacX53rF/6PLqdsY4sKcBStHsGnvK91/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjcnnn/dJMcacX53rF/6PLqdsY4sKcBStHsGnvK91/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjcnnn/dJMcacX53rF/6PLqdsY4sKcBStHsGnvK91/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjcnnn%2FdJMcacX53rF%2F6PLqdsY4sKcBStHsGnvK91%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;저역통과가 못 잡는 장기 떨림 문제.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세 추정기의 떨림은 프레임마다 고르지 않고 매우 불균형하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 프레임은 약한 떨림만 있지만, 가림이나 저화질 구간에서는 큰 떨림이 여러 프레임 연속으로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 오래 지속되는 큰 오차를 장기 떨림(long-term jitter)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro 같은 저역통과 필터나 기존 신경망은 이 장기 떨림을 제대로 다루지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구간 전체가 어긋나 있으면, 창 안의 값을 평활해도 참값으로 돌아오지 못하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한계가 학습 기반 정제망이 등장한 배경이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;관절별 장기 시간 관계를 학습하는 구조&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SmoothNet은 기존 추정기의 출력에 덧붙이는 시간축 전용 정제망이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관절 사이의 잡음 섞인 상관은 무시하고, 각 관절의 장기 시간 관계만 데이터로 학습한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 시간축을 따라 여러 완전연결(fully-connected, 모든 입력을 잇는 계층)을 잔차 연결로 쌓은 형태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넓은 시간 수용 영역을 가지므로, 멀리 떨어진 프레임의 신뢰할 만한 정보까지 참고할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 덕분에 떨림뿐 아니라 구간 전체가 치우친 편향 오차까지 함께 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 사람 움직임의 자연스러운 매끄러움을 데이터로 익혀 복원하는 방식이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;위치에 속도&amp;middot;가속도를 더한 모션 인식 설계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;위치에 속도&amp;amp;middot;가속도를 더한 모션 인식 설계.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzJyNq/dJMcahylObK/8RQJ96XZBkehILuNuyLJ3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzJyNq/dJMcahylObK/8RQJ96XZBkehILuNuyLJ3K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzJyNq/dJMcahylObK/8RQJ96XZBkehILuNuyLJ3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzJyNq%2FdJMcahylObK%2F8RQJ96XZBkehILuNuyLJ3K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;위치에 속도&amp;middot;가속도를 더한 모션 인식 설계.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SmoothNet의 핵심 목표는 떨림 패턴을 잡아 가속도 오차를 줄이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;떨림이 주로 가속도 오차(Accel)로 드러난다는 점은 1부에서 다룬 지표와 그대로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위치만이 아니라 인접 프레임으로부터 속도와 가속도를 명시적으로 모델링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 움직임의 미분 정보를 함께 넣는 설계를 모션 인식(motion-aware) 구조라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치와 그 변화량을 함께 보면 떨림과 실제 움직임을 더 잘 구분할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설계 덕분에 단순 저역통과보다 큰 떨림을 더 효과적으로 줄인다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;창 크기가 정하는 지연과 온라인성의 균형&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SmoothNet도 일정 크기의 시간 창을 보므로, 창 크기가 성능과 지연을 함께 좌우한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창이 크면 장기 문맥을 더 많이 참고하지만, 그만큼 필요한 미래 프레임이 늘어 지연이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원 논문은 기본 창을 64 프레임에서 근접 온라인용으로 32 프레임까지 줄였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8, 16, 32, 64프레임 사전학습 모델이 공개되어 용도에 맞게 고를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 시간축 전용이라 다양한 추정기와 데이터에 붙는 플러그앤플레이(사전 학습된 모듈을 그대로 가져다 붙이는 방식) 이식성이 강점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 SmoothNet은 학습으로 장기 떨림을 잡되, 창 크기로 지연과 온라인성을 절충한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;필터는 2D, 3D, 각도 어디에나 걸 수 있다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;삼각측량 전에 시점별 2D를 다듬는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;삼각측량 전에 시점별 2D를 다듬는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmVO9g/dJMcacDRyHz/sQgdYD7liImgtZ5QbLTFw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmVO9g/dJMcacDRyHz/sQgdYD7liImgtZ5QbLTFw1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmVO9g/dJMcacDRyHz/sQgdYD7liImgtZ5QbLTFw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmVO9g%2FdJMcacDRyHz%2FsQgdYD7liImgtZ5QbLTFw1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;삼각측량 전에 시점별 2D를 다듬는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터를 걸 수 있는 첫 번째 위치는 삼각측량 이전의 2차원 관절이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 시점의 2차원 궤적을 시간축으로 먼저 다듬어 프레임 사이 떨림을 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VNect는 2차원 예측을 One Euro로 필터링한 뒤 골격 피팅으로 넘기는 순서를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 단계에서 떨림을 줄이면 삼각측량으로 넘어가는 입력의 품질이 좋아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 시점마다 따로 걸어야 하므로 카메라 수만큼 필터를 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 위치의 필터는 삼각측량의 입력을 정돈하는 역할을 한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;삼각측량 후 3D 관절을 평활한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 위치는 삼각측량으로 얻은 3차원 관절이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 시점이 이미 융합된 뒤이므로, 여기서 거는 필터는 남은 3차원 떨림만 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 다중 카메라 시스템은 삼각측량 결과에 관절별 One Euro를 걸어 실시간으로 평활한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anipose 같은 도구는 삼각측량 최적화 안에 시간축 평활과 뼈 길이 제약을 함께 넣기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 제약을 삼각측량에 통합하면 후처리를 따로 두는 것보다 효과가 큰 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3차원 단계의 필터는 융합 이후의 잔여 오차를 정리하는 위치에 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;IK 이후 관절 각도를 다듬는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 위치는 역기구학(Inverse Kinematics, IK)으로 관절 각도를 구한 다음이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세 분석은 위치보다 관절 각도가 중요한 경우가 많아, 각도 자체를 평활하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anipose는 실제로 위치뿐 아니라 관절 각도의 오차도 함께 측정해 필터 효과를 평가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각도 단계에서 다듬으면 관절 회전의 궤적이 더 매끄러워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 각도는 위치에서 파생되므로, 앞 단계의 오차가 각도에 그대로 전파될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 각도 필터는 앞 단계 정제가 충분히 이뤄진 뒤에 걸어야 의미가 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;미디언, 삼각측량, 평활, IK의 순서&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;미디언, 삼각측량, 평활, IK의 순서.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbLY9i/dJMcajbLFc3/Kb8U4HUKtvt3DvIEjTRVZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbLY9i/dJMcajbLFc3/Kb8U4HUKtvt3DvIEjTRVZ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbLY9i/dJMcajbLFc3/Kb8U4HUKtvt3DvIEjTRVZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbLY9i%2FdJMcajbLFc3%2FKb8U4HUKtvt3DvIEjTRVZ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;미디언, 삼각측량, 평활, IK의 순서.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 필터를 조합할 때는 성격에 맞는 순서가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 튀는 값은 미디언 필터로 걷어내 이후 단계가 오염되지 않게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 삼각측량 단계에서 이상치를 제거하고 여러 시점을 융합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 시간축 평활로 남은 떨림을 줄이고, 필요하면 IK로 구조 제약을 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 이상치 제거를 앞에, 평활과 구조 제약을 뒤에 두는 배치가 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저역통과 필터를 겹쳐 거는 대신, 서로 다른 역할을 단계로 나누는 것이 핵심이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;필터의 값어치는 측정으로만 증명된다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;MPJPE와 Accel을 함께 보고 ablation한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터가 값을 했는지는 느낌이 아니라 지표로 판단해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확도는 MPJPE(Mean Per Joint Position Error, 관절별 평균 위치 오차)로 측정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매끄러움은 Accel(Acceleration Error, 가속도 오차)로 재며, 이는 3차원 관절의 2차 차분 크기로 떨림을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터를 넣은 경우와 뺀 경우를 비교하는 ablation(제거 실험)으로 실제 기여를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 한 지표만 보면 속기 쉬우므로, 정확도와 매끄러움을 반드시 짝으로 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과하게 평활하면 Accel은 낮아져도 MPJPE가 나빠지는지 함께 살펴야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 필터의 효과는 두 지표의 변화를 함께 봐야 제대로 드러난다.&lt;/p&gt;
&lt;pre id=&quot;code_1783752446774&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

def mpjpe(pred, gt):
    # 관절별 위치 오차의 평균 (mm)
    return np.linalg.norm(pred - gt, axis=-1).mean()

def accel(kps):
    # 2차 차분(가속도) 크기의 평균 &amp;rarr; 떨림 지표
    a = kps[2:] - 2 * kps[1:-1] + kps[:-2]
    return np.linalg.norm(a, axis=-1).mean()

# 필터 유무를 나란히 비교하는 ablation
def ablation(raw, filtered, gt):
    print(f&quot;필터 전  MPJPE={mpjpe(raw, gt):.2f}  Accel={accel(raw):.3f}&quot;)
    print(f&quot;필터 후  MPJPE={mpjpe(filtered, gt):.2f}  Accel={accel(filtered):.3f}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;삼각측량이 깨끗하면 후처리 실익은 작다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 측정은 후처리 필터가 늘 이득은 아니라는 점을 보여 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anipose 연구는 4대 카메라로 사람을 추적하며 여러 3차원 필터의 효과를 정량화했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 RANSAC 삼각측량은 위치와 각도 오차를 개선하지 못했다고 보고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3차원 미디언 필터는 사람 데이터에서는 오차를 크게 줄였지만 초파리 데이터에서는 그렇지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 후처리 필터의 실익은 데이터와 파이프라인에 따라 크게 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼각측량과 상류 정제가 이미 깨끗하다면, 후처리가 더할 이득은 작아질 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;잔떨림과 실시간 표시엔 여전히 필요하다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 후처리 필터가 무의미한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Anipose 연구에서 시간축 평활과 뼈 길이 제약을 함께 적용했을 때 오차가 가장 크게 줄었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가림이나 저화질로 생기는 장기 떨림에는 SmoothNet 같은 학습형 정제가 여전히 유효하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간으로 궤적을 보여 줘야 하는 화면에서는 One Euro의 낮은 지연 평활이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 삼각측량이 잡지 못하는 잔여 떨림과 표시 품질에는 후처리가 제 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 필터를 쓰느냐 마느냐가 아니라, 어디에 어떤 필터가 필요한가이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;목적에 필터를 맞추는 것이 결론이다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;목적에 필터를 맞추는 것이 결론이다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvv5P0/dJMcafN8jFg/HgANZsT7x2ESkds3SwTxrk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvv5P0/dJMcafN8jFg/HgANZsT7x2ESkds3SwTxrk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvv5P0/dJMcafN8jFg/HgANZsT7x2ESkds3SwTxrk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvv5P0%2FdJMcafN8jFg%2FHgANZsT7x2ESkds3SwTxrk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;목적에 필터를 맞추는 것이 결론이다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시리즈의 결론은 필터를 반사적으로 쌓지 말고 목적에 맞추라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 표시에는 One Euro, 도함수 분석에는 Savitzky-Golay, 정밀 오프라인에는 영위상 Butterworth가 맞는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장기 떨림에는 SmoothNet, 구조적 위배에는 IK가 각자의 역할을 맡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다중 시점 삼각측량 구조에서는 상류 정제가 이미 많은 일을 한다는 점을 전제로 삼아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 어떤 필터를 넣기 전에, 그 필터가 줄이려는 오차가 무엇인지부터 정의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터의 값어치는 결국 목적과 측정으로 증명되는 것이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시리즈는 삼각측량 후처리에서 떨림을 다루는 방법을 네 편에 걸쳐 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부는 떨림의 원인과 측정을, 2부는 One Euro와 Savitzky-Golay, 영위상 Butterworth를 다뤘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3부는 Kalman의 등속 가정 한계와 IK의 구조 제약을, 4부는 SmoothNet과 파이프라인 배치, 필터의 실익을 살폈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관통하는 원칙은 떨림과 지연의 트레이드오프를 인정하고, 효과를 지표로 확인하며, 목적에 맞는 도구를 고르는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후처리 필터는 만능도 무용도 아니며, 파이프라인의 어느 자리에서 어떤 오차를 줄이는지에 따라 값어치가 갈린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼각측량이 이미 많은 일을 한다는 사실을 전제로, 남은 오차에 맞는 필터를 선택하는 것이 이 시리즈가 도달한 결론이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Zeng, A. et al. SmoothNet: A Plug-and-Play Network for Refining Human Poses in Videos, ECCV 2022. &lt;a href=&quot;https://arxiv.org/abs/2112.13715&quot;&gt;https://arxiv.org/abs/2112.13715&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SmoothNet 공식 구현(창 크기, MMPose 통합), GitHub cure-lab/SmoothNet. &lt;a href=&quot;https://github.com/cure-lab/SmoothNet&quot;&gt;https://github.com/cure-lab/SmoothNet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SmoothNet 프로젝트 페이지(장기 떨림, 플러그앤플레이). &lt;a href=&quot;https://ailingzeng.site/smoothnet&quot;&gt;https://ailingzeng.site/smoothnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Karashchuk, P. et al. Anipose: A toolkit for robust markerless 3D pose estimation, Cell Reports 2021. &lt;a href=&quot;https://www.cell.com/cell-reports/fulltext/S2211-1247(21)01179-7&quot;&gt;https://www.cell.com/cell-reports/fulltext/S2211-1247(21)01179-7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Anipose bioRxiv 프리프린트(RANSAC 삼각측량 효과, 시공간 정규화). &lt;a href=&quot;https://www.biorxiv.org/content/10.1101/2020.05.26.117325v2.full&quot;&gt;https://www.biorxiv.org/content/10.1101/2020.05.26.117325v2.full&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RANSAC 알고리즘 Recap(이상치 개념), 빈챙의 뚝딱뚝딱 개발 블로그. &lt;a href=&quot;https://techbless.github.io/2025/03/19/ransac/&quot;&gt;https://techbless.github.io/2025/03/19/ransac/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RANSAC을 이용한 이상치 제거, velog VSLAM 시리즈. &lt;a href=&quot;https://velog.io/@y2k4388/VSLAM-8-RANSAC을-이용한-이상치-제거&quot;&gt;https://velog.io/@y2k4388/VSLAM-8-RANSAC을-이용한-이상치-제거&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Efficient Outlier Removal in Large Scale Global Structure-from-Motion(RANSAC&amp;middot;번들 조정&amp;middot;재투영 오차). &lt;a href=&quot;https://arxiv.org/pdf/1808.03041&quot;&gt;https://arxiv.org/pdf/1808.03041&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;OmniRobotHome: A Multi-Camera Home Platform(삼각측량 + 관절별 One Euro, IRLS/Huber). &lt;a href=&quot;https://arxiv.org/pdf/2604.28197&quot;&gt;https://arxiv.org/pdf/2604.28197&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mehta, D. et al. VNect: Real-time 3D Human Pose Estimation with a Single RGB Camera(2D One Euro &amp;rarr; 골격 피팅). &lt;a href=&quot;https://arxiv.org/pdf/1705.01583&quot;&gt;https://arxiv.org/pdf/1705.01583&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Robust Uncertainty-Aware Multiview Triangulation(시차각과 삼각측량 불확실성). &lt;a href=&quot;https://arxiv.org/abs/2008.01258&quot;&gt;https://arxiv.org/abs/2008.01258&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>3D자세추정</category>
      <category>Accel</category>
      <category>MPJPE</category>
      <category>RANSAC</category>
      <category>SmoothNet</category>
      <category>모션스무딩</category>
      <category>삼각측량후처리</category>
      <category>이상치제거</category>
      <category>장기떨림</category>
      <category>파이프라인배치</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1358</guid>
      <comments>https://gnidinger.tistory.com/entry/%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89%EC%9D%B4-%EB%8B%A4-%EC%9E%A1%EC%95%98%EB%8B%A4%EB%A9%B4-%ED%95%84%ED%84%B0%EB%8A%94-%EC%A0%95%EB%A7%90-%EA%B0%92%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80-%ED%95%99%EC%8A%B5%ED%98%95-SmoothNet%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%B0%EC%B9%98-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-4#entry1358comment</comments>
      <pubDate>Sat, 11 Jul 2026 15:54:56 +0900</pubDate>
    </item>
    <item>
      <title>등속 가정이 무너질 때: Kalman 상태추정과 IK 뼈길이 제약 | 삼각측량 후처리 (3)</title>
      <link>https://gnidinger.tistory.com/entry/%EB%93%B1%EC%86%8D-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%AC%B4%EB%84%88%EC%A7%88-%EB%95%8C-Kalman-%EC%83%81%ED%83%9C%EC%B6%94%EC%A0%95%EA%B3%BC-IK-%EB%BC%88%EA%B8%B8%EC%9D%B4-%EC%A0%9C%EC%95%BD-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-3</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%B6%80%EB%93%9C%EB%9F%AC%EC%9B%80%EA%B3%BC-%EB%B0%98%EC%9D%91%EC%84%B1%EC%9D%80-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%97%86%EB%8B%A4-%EC%A7%80%ED%84%B0-%EC%A7%80%EC%97%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C%EC%98%A4%ED%94%84%EC%99%80-Accel%EB%A1%9C-%EB%96%A8%EB%A6%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;부드러움과 반응성은 동시에 가질 수 없다: 지터-지연 트레이드오프와 Accel로 떨림 측정하기 | 삼각측량 후처리 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%91%90-%EA%B0%9C%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4-One-Euro-Savitzky-Golay-%EC%98%81%EC%9C%84%EC%83%81-Butterworth-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;파라미터 두 개로 실시간을 잡는다: One Euro, Savitzky-Golay, 영위상 Butterworth | 삼각측량 후처리 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%93%B1%EC%86%8D-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%AC%B4%EB%84%88%EC%A7%88-%EB%95%8C-Kalman-%EC%83%81%ED%83%9C%EC%B6%94%EC%A0%95%EA%B3%BC-IK-%EB%BC%88%EA%B8%B8%EC%9D%B4-%EC%A0%9C%EC%95%BD-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;등속 가정이 무너질 때: Kalman 상태추정과 IK 뼈길이 제약 | 삼각측량 후처리 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89%EC%9D%B4-%EB%8B%A4-%EC%9E%A1%EC%95%98%EB%8B%A4%EB%A9%B4-%ED%95%84%ED%84%B0%EB%8A%94-%EC%A0%95%EB%A7%90-%EA%B0%92%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80-%ED%95%99%EC%8A%B5%ED%98%95-SmoothNet%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%B0%EC%B9%98-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼각측량이 다 잡았다면, 필터는 정말 값을 했는가: 학습형 SmoothNet과 파이프라인 배치 | 삼각측량 후처리 (4)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;썸네일.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4aOUC/dJMcaftIPmO/Wr7znGlyvIOfMsrycVjfq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4aOUC/dJMcaftIPmO/Wr7znGlyvIOfMsrycVjfq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4aOUC/dJMcaftIPmO/Wr7znGlyvIOfMsrycVjfq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4aOUC%2FdJMcaftIPmO%2FWr7znGlyvIOfMsrycVjfq1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;썸네일.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부는 떨림의 원인과 측정을, 2부는 One Euro와 Savitzky-Golay, 영위상 Butterworth를 다뤘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 필터들은 신호 자체만 보고 잡음을 누르는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3부는 성격이 다른 두 도구, 즉 운동 모델을 세우는 Kalman과 골격 구조를 강제하는 역기구학(IK)으로 넘어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman은 위치와 속도를 상태로 두고, 모델로 예측한 값과 측정값을 융합해 상태를 추정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 예측은 대개 등속 모델에 기대는데, 드리블처럼 급가속과 방향 전환이 잦은 동작에서는 그 가정이 흔들린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IK는 시간축 필터가 아니라 뼈 길이와 관절 가동범위 같은 해부학적 제약으로 다른 종류의 오차를 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 두 방법의 원리와 한계를 살펴본 뒤, 시간과 구조라는 서로 다른 역할로 어떻게 나뉘는지 정리한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;Kalman은 예측과 보정을 번갈아 상태를 추정한다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;위치와 속도를 함께 담는 상태 벡터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;위치와 속도를 함께 담는 상태 벡터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlsB29/dJMcahkQ9nB/k6kkPhqo6zQLcpDnyu4DH0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlsB29/dJMcahkQ9nB/k6kkPhqo6zQLcpDnyu4DH0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlsB29/dJMcahkQ9nB/k6kkPhqo6zQLcpDnyu4DH0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlsB29%2FdJMcahkQ9nB%2Fk6kkPhqo6zQLcpDnyu4DH0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;위치와 속도를 함께 담는 상태 벡터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman 필터(칼만 필터)는 잡음이 섞인 측정치로 시스템의 상태를 추정하는 재귀 필터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 상태는 보통 위치뿐 아니라 속도까지 함께 담은 벡터로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예컨대 한 관절의 상태를 위치와 속도의 조합으로 두면, 다음 순간의 위치를 속도로부터 예측할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 전이 행렬은 이전 상태에서 다음 상태로 넘어가는 규칙을 담은 선형 변환이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관측 행렬은 상태 벡터를 실제 측정값의 형태로 바꿔 주는 변환이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 위치와 속도를 함께 추적하기 때문에, 측정이 잠깐 튀어도 예측으로 이를 보완할 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;모델로 예측하고 측정으로 보정하는 순환&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman 필터는 예측과 보정(업데이트)이라는 두 단계를 번갈아 반복한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측 단계에서는 상태 전이 모델로 다음 상태와 그 불확실성을 미리 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보정 단계에서는 실제 측정값과 예측값의 차이인 잔차를 반영해 상태를 다듬는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보정으로 얻은 결과는 다음 시점의 예측을 위한 이전 상태가 되어 순환이 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복할수록 예측과 측정이 융합되어 더 정밀한 상태 추정에 수렴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 미래를 내다보는 모델과 현재를 알려 주는 측정을 매 프레임 결합하는 구조이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;예측과 측정을 저울질하는 칼만 이득&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;예측과 측정을 저울질하는 칼만 이득.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3ISun/dJMcahLQrPR/hz2AyXKNvDXp8hyeU7wSG0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3ISun/dJMcahLQrPR/hz2AyXKNvDXp8hyeU7wSG0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3ISun/dJMcahLQrPR/hz2AyXKNvDXp8hyeU7wSG0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3ISun%2FdJMcahLQrPR%2Fhz2AyXKNvDXp8hyeU7wSG0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;예측과 측정을 저울질하는 칼만 이득.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측과 측정 중 어느 쪽을 더 믿을지는 칼만 이득(Kalman gain)이 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;칼만 이득은 예측의 불확실성과 측정의 불확실성을 비교해 계산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정 잡음이 크면 예측을 더 신뢰하고, 예측 불확실성이 크면 측정을 더 신뢰한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 저울질 덕분에 필터는 상황에 따라 예측과 측정의 비중을 자동으로 조절한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가림으로 측정이 잠깐 사라지면 예측에 의존해 상태를 이어 갈 수 있는 것도 이 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 칼만 이득은 두 정보원의 신뢰도를 매 순간 균형 맞추는 값이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;선형과 가우시안일 때만 최적이라는 전제&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;선형과 가우시안일 때만 최적이라는 전제.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pksCt/dJMcabkC3bc/qU3VT95gpKrBXGw6uSwQmK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pksCt/dJMcabkC3bc/qU3VT95gpKrBXGw6uSwQmK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pksCt/dJMcabkC3bc/qU3VT95gpKrBXGw6uSwQmK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpksCt%2FdJMcabkC3bc%2FqU3VT95gpKrBXGw6uSwQmK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;선형과 가우시안일 때만 최적이라는 전제.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman 필터가 최적이라는 보장에는 분명한 전제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 전이와 측정이 모두 선형이고, 잡음이 가우시안 분포를 따라야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건에서는 Kalman 필터가 평균제곱오차를 최소화하는 최적의 선형 추정기가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 현실의 많은 시스템은 이 두 조건을 완전히 만족하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전제가 어긋나기 시작하는 대표적인 지점이 바로 다음 섹션에서 다룰 등속 모델의 한계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Kalman을 쓸 때는 이 전제가 얼마나 성립하는지를 먼저 따져야 한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;등속 모델은 폭발적 가속에서 무너진다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;등속 가정과 프로세스 잡음의 역할&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 널리 쓰이는 운동 모델은 대상이 일정한 속도로 움직인다고 보는 등속 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등속 모델은 모델로 설명되지 않는 가속을 프로세스 잡음(process noise, 모델과 실제 움직임의 차이)으로 흡수한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 잡음이 없으면 등속 Kalman 필터는 모든 측정값에 하나의 직선만 맞추려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 잡음을 두면 최근 측정에 더 큰 가중치를 주어 방향이나 속도 변화를 따라갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 프로세스 잡음은 모델의 경직성을 풀어 주는 여유값 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 여유 덕분에 완만하게 방향을 바꾸는 움직임은 등속 모델로도 충분히 추적된다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657234554&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

# 1D 등속 모델 칼만 필터 (상태: 위치, 속도)
def kalman_cv(z, dt=1 / 60, q=1.0, r=10.0):
    # z: (T,) 위치 측정값 시퀀스
    A = np.array([[1, dt], [0, 1]])          # 상태 전이 (등속)
    H = np.array([[1, 0]])                    # 관측: 위치만 측정
    Q = q * np.array([[dt**3 / 3, dt**2 / 2],  # 프로세스 잡음 공분산
                      [dt**2 / 2, dt]])
    R = np.array([[r]])                       # 측정 잡음 공분산
    x = np.array([[z[0]], [0.0]])             # 초기 상태
    P = np.eye(2)                             # 초기 오차 공분산
    out = []
    for zk in z:
        # 예측
        x = A @ x
        P = A @ P @ A.T + Q
        # 보정
        y = zk - (H @ x)                      # 잔차
        S = H @ P @ H.T + R
        K = P @ H.T @ np.linalg.inv(S)        # 칼만 이득
        x = x + K @ y
        P = (np.eye(2) - K @ H) @ P
        out.append(x[0, 0])
    return np.array(out)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;급가속과 방향 전환이 만드는 모델 불일치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;급가속과 방향 전환이 만드는 모델 불일치.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpgmzy/dJMcaa0fGkh/4pOxtfG9V0DVvUHX5tgok1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpgmzy/dJMcaa0fGkh/4pOxtfG9V0DVvUHX5tgok1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpgmzy/dJMcaa0fGkh/4pOxtfG9V0DVvUHX5tgok1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcpgmzy%2FdJMcaa0fGkh%2F4pOxtfG9V0DVvUHX5tgok1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;급가속과 방향 전환이 만드는 모델 불일치.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 드리블처럼 급가속과 급격한 방향 전환이 잦은 동작이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 기동은 백색 잡음이 아니라 시간적으로 강하게 상관된 결정적 움직임에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등속 모델은 가속을 백색 잡음으로 가정하므로, 이 상관된 기동과 근본적으로 어긋난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 모델이 실제와 어긋나 생기는 오차를 모델 불일치 오차라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 완화하려고 프로세스 잡음을 키우면 예측 신뢰가 낮아져 평활 효과가 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 등속 가정은 폭발적 가속 구간에서 예측이 뒤처지거나 과하게 흔들리는 딜레마에 빠진다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;비선형 회전을 위한 확장&amp;middot;무향 칼만 필터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;비선형 회전을 위한 확장&amp;amp;middot;무향 칼만 필터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bziOYQ/dJMcadJmMhI/ukdavo16bq0W9GVfLsP5Lk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bziOYQ/dJMcadJmMhI/ukdavo16bq0W9GVfLsP5Lk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bziOYQ/dJMcadJmMhI/ukdavo16bq0W9GVfLsP5Lk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbziOYQ%2FdJMcadJmMhI%2Fukdavo16bq0W9GVfLsP5Lk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;비선형 회전을 위한 확장&amp;middot;무향 칼만 필터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관절의 회전처럼 비선형인 양을 다루면 선형 Kalman 필터의 전제가 깨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장 칼만 필터(Extended Kalman Filter, EKF)는 추정치 주변에서 모델을 테일러급수로 선형화해 이 문제를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무향 칼만 필터(Unscented Kalman Filter, UKF)는 대표 표본점을 비선형 함수에 직접 통과시켜 통계를 근사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방법 모두 비선형 시스템에 Kalman의 틀을 확장하려는 시도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 EKF는 비선형성이 심하면 선형화 오차 때문에 성능이 나빠질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 비선형 대응은 가능하지만, 그만큼 구현과 안정성 관리가 까다로워진다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;Q와 R 튜닝이라는 경험적 부담&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Q와 R 튜닝이라는 경험적 부담.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yDLei/dJMcaftIQ2d/BOhuU60SSSNm5k7QgIda7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yDLei/dJMcaftIQ2d/BOhuU60SSSNm5k7QgIda7K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yDLei/dJMcaftIQ2d/BOhuU60SSSNm5k7QgIda7K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyDLei%2FdJMcaftIQ2d%2FBOhuU60SSSNm5k7QgIda7K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;Q와 R 튜닝이라는 경험적 부담.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman 필터의 성능은 프로세스 잡음 공분산 Q와 측정 잡음 공분산 R에 크게 좌우된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q와 R은 시스템 연구 결과나 측정에서 얻는 것이 원칙이지만, 실무에서는 경험적으로 정하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 값을 잘못 잡으면 추정이 발산하거나 과하게 둔감해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 관절이 많은 3차원 자세에서는 좌표마다 이 값들을 조율해야 해 부담이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기동이 심한 대상에서는 최대 가속에 맞춰 Q를 키우는 관계가 알려져 있지만, 이는 다시 평활과 상충한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Kalman은 이득이 분명한 만큼 튜닝 비용도 큰 도구로 평가된다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;IK는 뼈 길이와 관절 한계로 자세를 잡는다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;관절 값을 거꾸로 푸는 역기구학의 정의&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;관절 값을 거꾸로 푸는 역기구학의 정의.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0gVX/dJMcaalBBND/NDBnHhWiqBwY3XKIJ7yos1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0gVX/dJMcaalBBND/NDBnHhWiqBwY3XKIJ7yos1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0gVX/dJMcaalBBND/NDBnHhWiqBwY3XKIJ7yos1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0gVX%2FdJMcaalBBND%2FNDBnHhWiqBwY3XKIJ7yos1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;관절 값을 거꾸로 푸는 역기구학의 정의.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역기구학(Inverse Kinematics, IK)은 말단의 목표 위치와 방향으로부터 각 관절 값을 거꾸로 계산하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관절 각도로부터 말단 위치를 구하는 순기구학(정방향 운동학, forward kinematics)의 반대 방향에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IK는 순기구학보다 계산이 훨씬 복잡하며, 일반적으로 해가 하나로 정해지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예컨대 손끝을 한 지점에 두는 팔의 자세는 팔꿈치와 어깨의 조합에 따라 여러 가지가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 IK는 뼈 길이나 관절 한계 같은 제약을 걸어 가능한 해의 범위를 좁힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세 분석에서는 이 제약이 물리적으로 말이 되는 자세만 남기는 역할을 한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;뼈 길이 불변과 리타게팅으로 흔들림을 잡는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람의 뼈 길이는 움직이는 동안 변하지 않는다는 성질이 핵심 제약이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 프레임마다 독립적으로 추정한 3차원 관절은 인접 관절 사이 거리가 미세하게 출렁인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 출렁임을 없애려고 뼈 길이를 골격의 고정값으로 강제하는 방법을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 VNect는 추정된 뼈의 방향은 유지하되 길이만 골격 값으로 치환하는 리타게팅을 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 각 세그먼트가 단단한 막대처럼 유지되어 골격이 안정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 시간축 필터가 잡지 못하는 길이 방향의 흔들림을 구조 제약으로 억제하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1783657264740&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

# 부모&amp;rarr;자식 관절 방향은 유지하고 뼈 길이만 고정값으로 치환
def retarget_bone(parent, child, target_len):
    # parent, child: (3,) 3D 좌표, target_len: 골격의 뼈 길이
    vec = child - parent
    dist = np.linalg.norm(vec)
    if dist &amp;lt; 1e-8:
        return child
    direction = vec / dist                   # 방향 유지
    return parent + direction * target_len   # 길이만 교체&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;관절 가동범위로 불가능한 자세를 배제한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;관절 가동범위로 불가능한 자세를 배제한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xVvfR/dJMcaalBBO3/PjDO7fVIkmRqVkZsSw2O51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xVvfR/dJMcaalBBO3/PjDO7fVIkmRqVkZsSw2O51/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xVvfR/dJMcaalBBO3/PjDO7fVIkmRqVkZsSw2O51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxVvfR%2FdJMcaalBBO3%2FPjDO7fVIkmRqVkZsSw2O51%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;관절 가동범위로 불가능한 자세를 배제한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람의 관절은 아무 방향으로나 꺾이지 않고 정해진 범위 안에서만 움직인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한계를 관절 가동범위(Range of Motion, ROM)라고 하며, IK의 또 다른 제약으로 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가동범위를 넘어서는 각도를 배제하면 물리적으로 불가능한 자세가 결과에서 걸러진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 잡음이나 가림 때문에 관절이 비틀린 채로 추정되는 오류를 줄여 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자연스러운 사람의 자세는 완전히 임의적이지 않다는 점을 사전 지식으로 활용하는 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뼈 길이 제약이 거리를 지킨다면, 가동범위 제약은 각도를 지킨다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;위치만으로 못 푸는 회전, 트위스트 모호성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;위치만으로 못 푸는 회전, 트위스트 모호성.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9GTG6/dJMcacjqKDk/9zvSO2I06jKRxKK6ulUi4K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9GTG6/dJMcacjqKDk/9zvSO2I06jKRxKK6ulUi4K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9GTG6/dJMcacjqKDk/9zvSO2I06jKRxKK6ulUi4K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9GTG6%2FdJMcacjqKDk%2F9zvSO2I06jKRxKK6ulUi4K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;위치만으로 못 푸는 회전, 트위스트 모호성.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관절의 3차원 위치만으로는 뼈가 자기 축을 중심으로 얼마나 회전했는지를 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 축 회전이 확정되지 않는 문제를 트위스트 모호성이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위치만으로 관절 회전을 푸는 IK는 해가 유일하지 않은 부정 문제가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 회전 표현이 필요한 응용에서는 위치 대신 관절 회전 자체를 다루기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 3차원 키포인트에서 관절 회전과 전역 위치를 한 번에 추론하는 신경망 기반 IK도 제안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 IK는 구조를 강제하는 강력한 도구이지만, 위치 입력만으로는 회전의 모호성이 남는다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;Kalman은 시간을, IK는 구조를 맡는다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;시간축 잡음과 구조적 위배는 다른 오차다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;시간축 잡음과 구조적 위배는 다른 오차다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ynn1g/dJMcah6dT6S/gdQfp1bky2gVz5oIAcBScK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ynn1g/dJMcah6dT6S/gdQfp1bky2gVz5oIAcBScK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ynn1g/dJMcah6dT6S/gdQfp1bky2gVz5oIAcBScK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYnn1g%2FdJMcah6dT6S%2FgdQfp1bky2gVz5oIAcBScK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;시간축 잡음과 구조적 위배는 다른 오차다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman과 IK는 서로 다른 종류의 오차를 겨냥한다는 점을 먼저 구분해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman은 시간에 걸친 고주파 잡음, 즉 프레임 사이의 떨림을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IK는 뼈 길이가 흔들리거나 관절이 불가능하게 꺾이는 구조적 위배를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘은 성격이 다른 오차이므로, 하나를 잡아도 다른 하나는 그대로 남을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 두 방법은 경쟁 관계가 아니라 서로를 보완하는 관계에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 오차를 줄이려는지에 따라 적절한 도구가 갈리는 것이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;필터 뒤에 IK를 쌓는 계층 구조&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;필터 뒤에 IK를 쌓는 계층 구조.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AuyLh/dJMb99NK1xZ/PtdvQmDakFtTrxfBK0auC0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AuyLh/dJMb99NK1xZ/PtdvQmDakFtTrxfBK0auC0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AuyLh/dJMb99NK1xZ/PtdvQmDakFtTrxfBK0auC0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAuyLh%2FdJMb99NK1xZ%2FPtdvQmDakFtTrxfBK0auC0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;필터 뒤에 IK를 쌓는 계층 구조.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 파이프라인은 두 방법을 한 층씩 쌓아 결합하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예컨대 먼저 2차원 관절을 시간축 필터로 다듬어 떨림을 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 삼각측량으로 얻은 3차원 관절에 뼈 길이와 가동범위 제약을 IK로 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 시간축 평활이 남긴 구조적 오차를 IK가 다시 잡아 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VNect도 2차원 예측을 One Euro로 필터링한 뒤 골격 피팅을 거치는 순서를 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 시간축 필터와 구조 제약을 계층으로 배치하는 것이 자연스러운 설계이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;삼각측량이 예측 이득을 대신 회수하는 경우&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;삼각측량이 예측 이득을 대신 회수하는 경우.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ6KR1/dJMcadCyPEh/lTlyqxTYM4ClkS6oHgMNtK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ6KR1/dJMcadCyPEh/lTlyqxTYM4ClkS6oHgMNtK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ6KR1/dJMcadCyPEh/lTlyqxTYM4ClkS6oHgMNtK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ6KR1%2FdJMcadCyPEh%2FlTlyqxTYM4ClkS6oHgMNtK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;삼각측량이 예측 이득을 대신 회수하는 경우.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman의 큰 강점은 예측을 통해 가림 구간이나 단일 시점의 흔들림을 메우는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 여러 대의 동기화된 카메라로 삼각측량을 하면 이 이득의 상당 부분이 중복된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 시점이 같은 순간을 관측해 기하학적으로 3차원을 복원하면, 예측에 기대지 않아도 위치가 잡히기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 Kalman이 추가로 주는 이득은 작아지는 반면, Q와 R 튜닝 부담은 그대로 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다중 시점 삼각측량 구조에서는 Kalman의 실익을 신중히 따져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 판단, 즉 후처리 단계에 어떤 필터를 왜 두는가는 다음 4부에서 본격적으로 다룬다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3부는 운동 모델을 세우는 Kalman과 골격 구조를 강제하는 IK를 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kalman은 예측과 보정을 번갈아 상태를 추정하지만, 등속 가정은 폭발적 가속 앞에서 흔들린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비선형 대응으로 EKF와 UKF가 있으나, Q와 R 튜닝 부담은 여전히 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IK는 시간축 필터가 아니라 뼈 길이와 관절 가동범위로 구조적 오차를 잡는 상보적 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방법은 겹치기보다 시간과 구조라는 서로 다른 역할로 나뉘며, 계층으로 쌓아 결합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 4부에서는 학습 기반 SmoothNet과 파이프라인 배치, 그리고 필터가 정말 값을 했는지를 다룬다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;trackingKF - Linear Kalman filter for object tracking, MATLAB. &lt;a href=&quot;https://www.mathworks.com/help/driving/ref/trackingkf.html&quot;&gt;https://www.mathworks.com/help/driving/ref/trackingkf.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tuning Kalman Filter to Improve State Estimation, MATLAB. &lt;a href=&quot;https://www.mathworks.com/help/fusion/ug/tuning-kalman-filter-to-improve-state-estimation.html&quot;&gt;https://www.mathworks.com/help/fusion/ug/tuning-kalman-filter-to-improve-state-estimation.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;칼만 필터, 위키백과. &lt;a href=&quot;https://ko.wikipedia.org/wiki/칼만_필터&quot;&gt;https://ko.wikipedia.org/wiki/칼만_필터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;확장 칼만 필터, 위키백과. &lt;a href=&quot;https://ko.wikipedia.org/wiki/확장_칼만_필터&quot;&gt;https://ko.wikipedia.org/wiki/확장_칼만_필터&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kalman Filter 칼만 필터, 정보통신기술용어해설(ktword). &lt;a href=&quot;http://www.ktword.co.kr/test/view/view.php?no=6249&quot;&gt;http://www.ktword.co.kr/test/view/view.php?no=6249&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MSE Design of Nearly Constant Velocity Kalman Filters for Tracking Targets With Deterministic Maneuvers, IEEE. &lt;a href=&quot;https://ieeexplore.ieee.org/document/10032801/&quot;&gt;https://ieeexplore.ieee.org/document/10032801/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kalman Filter for Moving Object Tracking: Performance Analysis and Filter Design, IntechOpen. &lt;a href=&quot;https://www.intechopen.com/chapters/57673&quot;&gt;https://www.intechopen.com/chapters/57673&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;VNect: Real-time 3D Human Pose Estimation with a Single RGB Camera. &lt;a href=&quot;https://arxiv.org/pdf/1705.01583&quot;&gt;https://arxiv.org/pdf/1705.01583&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SkeletonPose: Exploiting human skeleton constraint for 3D human pose estimation, ScienceDirect. &lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S0950705122008541&quot;&gt;https://www.sciencedirect.com/science/article/abs/pii/S0950705122008541&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Akhter, I., Black, M. J. Pose-Conditioned Joint Angle Limits for 3D Human Pose Reconstruction, CVPR 2015. &lt;a href=&quot;https://openaccess.thecvf.com/content_cvpr_2015/papers/Akhter_Pose-Conditioned_Joint_Angle_2015_CVPR_paper.pdf&quot;&gt;https://openaccess.thecvf.com/content_cvpr_2015/papers/Akhter_Pose-Conditioned_Joint_Angle_2015_CVPR_paper.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Inverse Kinematics - 역기구학, 매스웍스 한국. &lt;a href=&quot;https://kr.mathworks.com/discovery/inverse-kinematics.html&quot;&gt;https://kr.mathworks.com/discovery/inverse-kinematics.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fast Neural Inverse Kinematics on Human Body Motions. &lt;a href=&quot;https://arxiv.org/pdf/2506.17996&quot;&gt;https://arxiv.org/pdf/2506.17996&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;HybrIK: A Hybrid Analytical-Neural Inverse Kinematics Solution for 3D Human Pose and Shape Estimation. &lt;a href=&quot;https://arxiv.org/pdf/2011.14672&quot;&gt;https://arxiv.org/pdf/2011.14672&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>3D자세추정</category>
      <category>IK</category>
      <category>관절가동범위</category>
      <category>등속모델</category>
      <category>무향칼만필터</category>
      <category>뼈길이제약</category>
      <category>역기구학</category>
      <category>칼만필터</category>
      <category>프로세스잡음</category>
      <category>확장칼만필터</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1357</guid>
      <comments>https://gnidinger.tistory.com/entry/%EB%93%B1%EC%86%8D-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%AC%B4%EB%84%88%EC%A7%88-%EB%95%8C-Kalman-%EC%83%81%ED%83%9C%EC%B6%94%EC%A0%95%EA%B3%BC-IK-%EB%BC%88%EA%B8%B8%EC%9D%B4-%EC%A0%9C%EC%95%BD-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-3#entry1357comment</comments>
      <pubDate>Fri, 10 Jul 2026 13:28:28 +0900</pubDate>
    </item>
    <item>
      <title>파라미터 두 개로 실시간을 잡는다: One Euro, Savitzky-Golay, 영위상 Butterworth | 삼각측량 후처리 (2)</title>
      <link>https://gnidinger.tistory.com/entry/%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%91%90-%EA%B0%9C%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4-One-Euro-Savitzky-Golay-%EC%98%81%EC%9C%84%EC%83%81-Butterworth-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-2</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%B6%80%EB%93%9C%EB%9F%AC%EC%9B%80%EA%B3%BC-%EB%B0%98%EC%9D%91%EC%84%B1%EC%9D%80-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%97%86%EB%8B%A4-%EC%A7%80%ED%84%B0-%EC%A7%80%EC%97%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C%EC%98%A4%ED%94%84%EC%99%80-Accel%EB%A1%9C-%EB%96%A8%EB%A6%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;부드러움과 반응성은 동시에 가질 수 없다: 지터-지연 트레이드오프와 Accel로 떨림 측정하기 | 삼각측량 후처리 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%91%90-%EA%B0%9C%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4-One-Euro-Savitzky-Golay-%EC%98%81%EC%9C%84%EC%83%81-Butterworth-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;파라미터 두 개로 실시간을 잡는다: One Euro, Savitzky-Golay, 영위상 Butterworth | 삼각측량 후처리 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%93%B1%EC%86%8D-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%AC%B4%EB%84%88%EC%A7%88-%EB%95%8C-Kalman-%EC%83%81%ED%83%9C%EC%B6%94%EC%A0%95%EA%B3%BC-IK-%EB%BC%88%EA%B8%B8%EC%9D%B4-%EC%A0%9C%EC%95%BD-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;등속 가정이 무너질 때: Kalman 상태추정과 IK 뼈길이 제약 | 삼각측량 후처리 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89%EC%9D%B4-%EB%8B%A4-%EC%9E%A1%EC%95%98%EB%8B%A4%EB%A9%B4-%ED%95%84%ED%84%B0%EB%8A%94-%EC%A0%95%EB%A7%90-%EA%B0%92%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80-%ED%95%99%EC%8A%B5%ED%98%95-SmoothNet%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%B0%EC%B9%98-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼각측량이 다 잡았다면, 필터는 정말 값을 했는가: 학습형 SmoothNet과 파이프라인 배치 | 삼각측량 후처리 (4)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;썸네일.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C0nmR/dJMcagGeRO7/GrFR8E427lKnFpskl4Gslk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C0nmR/dJMcagGeRO7/GrFR8E427lKnFpskl4Gslk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C0nmR/dJMcagGeRO7/GrFR8E427lKnFpskl4Gslk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC0nmR%2FdJMcagGeRO7%2FGrFR8E427lKnFpskl4Gslk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;썸네일.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부에서는 떨림이 왜 생기는지, 떨림과 지연이 어떤 관계인지, 어떻게 수치로 재는지를 다뤘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이동평균과 지수이동평균, 미디언 필터가 고정된 설정값이라는 한계를 가진다는 점도 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2부는 이 한계를 넘는 실전 필터 세 가지, One Euro와 Savitzky-Golay, 영위상 Butterworth를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro는 컷오프를 속도에 맞춰 바꿔 실시간에서 떨림과 지연을 함께 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Savitzky-Golay는 창 안에 다항식을 맞춰 형태를 지키고 속도와 가속도까지 뽑아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영위상 Butterworth는 정방향과 역방향으로 두 번 걸어 지연 없이 오프라인 분석에 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 세 필터의 원리와 코드를 살펴본 뒤, 실시간과 분석 용도로 어떻게 나눠 쓰는지까지 정리한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;One Euro는 속도에 따라 컷오프를 바꾼다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;속도로 컷오프를 여닫는 적응형 저역통과 필터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;속도로 컷오프를 여닫는 적응형 저역통과 필터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zcZIi/dJMcabx3L18/KkZjYnghAqhozrq5h4eyg1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zcZIi/dJMcabx3L18/KkZjYnghAqhozrq5h4eyg1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zcZIi/dJMcabx3L18/KkZjYnghAqhozrq5h4eyg1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzcZIi%2FdJMcabx3L18%2FKkZjYnghAqhozrq5h4eyg1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;380&quot; data-filename=&quot;속도로 컷오프를 여닫는 적응형 저역통과 필터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro 필터는 컷오프 주파수가 고정되지 않고 신호의 속도에 따라 변하는 저역통과 필터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 구조는 1부에서 다룬 지수이동평균과 같지만, 평활 계수를 속도로부터 매 프레임 다시 계산한다.&lt;br /&gt;&lt;br /&gt;움직임이 느릴 때는 컷오프를 낮춰 강하게 평활하므로 떨림이 줄어든다.&lt;br /&gt;&lt;br /&gt;움직임이 빠를 때는 컷오프를 높여 신호를 바짝 따라가므로 지연이 줄어든다.&lt;br /&gt;&lt;br /&gt;덕분에 마우스 입력이나 관절 좌표처럼 실시간 잡음 신호를 다루는 데 널리 쓰인다.&lt;br /&gt;&lt;br /&gt;한마디로 컷오프를 속도에 연동해 떨림과 지연을 상황에 맞게 절충하는 필터이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;최소 컷오프와 속도 계수, 단 두 개의 손잡이&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;최소 컷오프와 속도 계수, 단 두 개의 손잡이.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0TfMV/dJMcahZlCmP/7Xawbq3hRL7G2MYs7Kngw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0TfMV/dJMcahZlCmP/7Xawbq3hRL7G2MYs7Kngw1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0TfMV/dJMcahZlCmP/7Xawbq3hRL7G2MYs7Kngw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0TfMV%2FdJMcahZlCmP%2F7Xawbq3hRL7G2MYs7Kngw1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;최소 컷오프와 속도 계수, 단 두 개의 손잡이.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro가 조절하는 값은 최소 컷오프 주파수와 속도 계수(beta) 두 개뿐이다.&lt;br /&gt;&lt;br /&gt;컷오프는 fc = 최소 컷오프 + beta &amp;times; 속도의 크기로 계산된다.&lt;br /&gt;&lt;br /&gt;최소 컷오프는 속도가 0일 때의 기본 컷오프로, 정지 상태의 떨림 수준을 정한다.&lt;br /&gt;&lt;br /&gt;속도 계수는 속도가 커질 때 컷오프를 얼마나 빠르게 올릴지를 정한다.&lt;br /&gt;&lt;br /&gt;속도는 신호의 1차 차분을 다시 저역통과로 걸러 잡음을 줄인 값을 쓴다.&lt;br /&gt;&lt;br /&gt;이렇게 파라미터가 두 개뿐이라 튜닝이 직관적이라는 점이 이 필터의 큰 장점이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;느릴 땐 떨림, 빠를 땐 지연을 줄이는 원리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;느릴 땐 떨림, 빠를 땐 지연을 줄이는 원리.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTDBTk/dJMcahZlCmU/pHXJiRWMiEAtY1RZkN4wkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTDBTk/dJMcahZlCmU/pHXJiRWMiEAtY1RZkN4wkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTDBTk/dJMcahZlCmU/pHXJiRWMiEAtY1RZkN4wkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTDBTk%2FdJMcahZlCmU%2FpHXJiRWMiEAtY1RZkN4wkk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;느릴 땐 떨림, 빠를 땐 지연을 줄이는 원리.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설계는 사람이 느린 움직임에서는 지연보다 떨림에 민감하다는 관찰에서 출발한다.&lt;br /&gt;&lt;br /&gt;반대로 빠른 움직임에서는 떨림보다 지연에 더 민감하다는 점을 이용한다.&lt;br /&gt;&lt;br /&gt;그래서 느릴 때는 컷오프를 낮춰 떨림을 없애고, 빠를 때는 컷오프를 높여 지연을 없앤다.&lt;br /&gt;&lt;br /&gt;고정 컷오프 필터가 떨림과 지연을 동시에 잡지 못하던 한계를 이 방식으로 우회한다.&lt;br /&gt;&lt;br /&gt;실제로 3차원 자세 연구들은 빠른 동작의 지연을 작게 유지하면서 느린 동작의 잡음을 줄인다고 보고한다.&lt;br /&gt;&lt;br /&gt;드리블처럼 정지와 폭발적 가속이 뒤섞인 동작에서 이 적응성이 특히 유리하다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;드리블에 맞춘 min_cutoff와 beta 튜닝&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro의 튜닝 원칙은 두 손잡이의 역할만 기억하면 될 만큼 단순하다.&lt;br /&gt;&lt;br /&gt;정지 상태의 떨림이 거슬리면 최소 컷오프를 낮추고, 빠른 동작의 지연이 거슬리면 속도 계수를 키운다.&lt;br /&gt;&lt;br /&gt;속도 계수는 10배씩 곱하거나 나눠 가며 빠른 움직임에서 지연이 바뀌는 지점을 먼저 찾는 것이 편하다.&lt;br /&gt;&lt;br /&gt;참고로 SmoothNet 실험은 최소 컷오프 1e-4, 속도 계수 0.7을 썼으나, 적정값은 신호의 단위와 스케일에 따라 크게 달라진다.&lt;br /&gt;&lt;br /&gt;한 가지 주의할 점은 시간 간격의 단위로, 프레임과 초를 섞으면 평활 계수가 어긋나므로 구현을 확인해야 한다.&lt;br /&gt;&lt;br /&gt;아래는 논문 저자가 공개한 표준 구현을 정리한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1783587493919&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import math

def smoothing_factor(t_e, cutoff):
    # 컷오프와 샘플 간격으로 평활 계수 alpha를 구한다
    r = 2 * math.pi * cutoff * t_e
    return r / (r + 1)

def exponential_smoothing(a, x, x_prev):
    return a * x + (1 - a) * x_prev

class OneEuroFilter:
    def __init__(self, t0, x0, min_cutoff=1.0, beta=0.0, d_cutoff=1.0):
        self.min_cutoff = min_cutoff   # 최소 컷오프 주파수 (Hz)
        self.beta = beta               # 속도 계수
        self.d_cutoff = d_cutoff       # 속도(미분) 평활용 컷오프
        self.x_prev = float(x0)
        self.dx_prev = 0.0
        self.t_prev = float(t0)

    def __call__(self, t, x):
        t_e = t - self.t_prev          # 샘플 간격 (초)
        # 속도를 구해 저역통과로 다듬는다
        a_d = smoothing_factor(t_e, self.d_cutoff)
        dx = (x - self.x_prev) / t_e
        dx_hat = exponential_smoothing(a_d, dx, self.dx_prev)
        # 속도에 따라 컷오프를 정하고 위치를 평활한다
        cutoff = self.min_cutoff + self.beta * abs(dx_hat)
        a = smoothing_factor(t_e, cutoff)
        x_hat = exponential_smoothing(a, x, self.x_prev)
        self.x_prev, self.dx_prev, self.t_prev = x_hat, dx_hat, t
        return x_hat&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;Savitzky-Golay는 다항식을 맞춰 형태를 지킨다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;창마다 최소제곱으로 다항식을 적합한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;창마다 최소제곱으로 다항식을 적합한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X7Ott/dJMcaiYcWt5/yhFkQAcIvBiKh5WNfRF8G1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X7Ott/dJMcaiYcWt5/yhFkQAcIvBiKh5WNfRF8G1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X7Ott/dJMcaiYcWt5/yhFkQAcIvBiKh5WNfRF8G1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX7Ott%2FdJMcaiYcWt5%2FyhFkQAcIvBiKh5WNfRF8G1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;창마다 최소제곱으로 다항식을 적합한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Savitzky-Golay(사비츠키-골레이) 필터는 슬라이딩 창 안의 점들에 다항식을 맞추는 방식으로 동작한다.&lt;br /&gt;&lt;br /&gt;각 창에서 최소제곱법으로 다항식을 적합한 뒤, 그 다항식의 중앙점 값을 결과로 삼는다.&lt;br /&gt;&lt;br /&gt;이 다항식의 계수는 미리 계산해 두는 고정 가중치라, 실제로는 합성곱 한 번으로 처리된다.&lt;br /&gt;&lt;br /&gt;그래서 유한 임펄스 응답(FIR, 유한한 개수의 입력만으로 출력을 만드는 구조) 저역통과 필터로 분류된다.&lt;br /&gt;&lt;br /&gt;조절값은 다항식 차수와 창 길이 두 개의 정수이며, 창 길이는 홀수여야 한다.&lt;br /&gt;&lt;br /&gt;다항식 차수를 0으로 두면 단순 이동평균과 같아지므로, 이동평균의 일반화로 볼 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;이동평균과 달리 피크 모양을 보존한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;이동평균과 달리 피크 모양을 보존한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/44GpB/dJMcaiYcWui/bQFwq4FhXhDWbUgWQ76qvk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/44GpB/dJMcaiYcWui/bQFwq4FhXhDWbUgWQ76qvk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/44GpB/dJMcaiYcWui/bQFwq4FhXhDWbUgWQ76qvk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F44GpB%2FdJMcaiYcWui%2FbQFwq4FhXhDWbUgWQ76qvk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;이동평균과 달리 피크 모양을 보존한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 이동평균은 창 안을 고르게 평균 내므로 뾰족한 봉우리를 뭉개 버린다.&lt;br /&gt;&lt;br /&gt;반면 Savitzky-Golay는 국소 곡률을 반영하는 다항식을 쓰므로 봉우리의 높이와 폭, 위치를 잘 지킨다.&lt;br /&gt;&lt;br /&gt;이 성질 덕분에 분광학이나 크로마토그래피처럼 피크가 물리적으로 중요한 분야에서 표준 평활법으로 쓰인다.&lt;br /&gt;&lt;br /&gt;자세 분석에서도 급격한 방향 전환의 정점을 보존해야 할 때 이동평균보다 유리하다.&lt;br /&gt;&lt;br /&gt;다만 다항식 차수를 지나치게 높이면 잡음까지 따라 적합해 과적합이 생길 수 있다.&lt;br /&gt;&lt;br /&gt;따라서 차수와 창 길이는 신호 특성에 맞춰 함께 조절해야 한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;다항식 미분으로 속도와 가속도를 뽑는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Savitzky-Golay의 가장 강력한 기능은 평활과 미분을 동시에 수행한다는 점이다.&lt;br /&gt;&lt;br /&gt;창에 맞춘 다항식을 그대로 미분하면, 잡음이 억제된 1차와 2차 도함수를 바로 얻을 수 있다.&lt;br /&gt;&lt;br /&gt;즉 위치에서 속도와 가속도를 뽑을 때 별도의 차분 없이 필터가 한 번에 처리한다.&lt;br /&gt;&lt;br /&gt;단순 차분은 잡음을 크게 증폭하지만, 이 방식은 봉우리를 지키면서 도함수를 더 안정적으로 추정한다.&lt;br /&gt;&lt;br /&gt;scipy에서는 savgol_filter의 deriv 인자로 계산할 미분 차수를 지정하고, delta로 샘플 간격을 넘긴다.&lt;br /&gt;&lt;br /&gt;2차 도함수를 구하려면 다항식 차수를 최소 2 이상으로 잡아야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783587513139&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from scipy.signal import savgol_filter

fs = 60.0                    # 샘플링 레이트 (Hz)
win, order = 31, 3           # 창 길이(홀수), 다항식 차수

# 위치 평활 (deriv=0)
pos = savgol_filter(x, window_length=win, polyorder=order)

# 속도 추정 (1차 미분), delta는 샘플 간격(초)
vel = savgol_filter(x, window_length=win, polyorder=order, deriv=1, delta=1 / fs)

# 가속도 추정 (2차 미분)
acc = savgol_filter(x, window_length=win, polyorder=order, deriv=2, delta=1 / fs)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;비인과성과 창 절반만큼의 지연&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;비인과성과 창 절반만큼의 지연.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXcGRC/dJMcadP9aIw/ULTz7hLfBTDNBbpGwsZNt0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXcGRC/dJMcadP9aIw/ULTz7hLfBTDNBbpGwsZNt0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXcGRC/dJMcadP9aIw/ULTz7hLfBTDNBbpGwsZNt0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXcGRC%2FdJMcadP9aIw%2FULTz7hLfBTDNBbpGwsZNt0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;비인과성과 창 절반만큼의 지연.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Savitzky-Golay는 창의 중앙점을 계산하므로 미래의 값이 있어야 결과가 나온다.&lt;br /&gt;&lt;br /&gt;즉 현재 시점의 출력을 내려면 창의 절반만큼 뒤의 프레임이 필요한 비인과 필터이다.&lt;br /&gt;&lt;br /&gt;창 길이가 31이면 중앙까지 약 15 프레임을 기다려야 하므로 그만큼 지연이 생긴다.&lt;br /&gt;&lt;br /&gt;따라서 이 필터는 실시간 표시보다 전체 데이터를 확보한 오프라인 분석에 적합하다.&lt;br /&gt;&lt;br /&gt;경계에서는 미래 값이 없으므로 mode 설정으로 외삽 방식을 정해야 결과가 왜곡되지 않는다.&lt;br /&gt;&lt;br /&gt;정리하면 형태 보존과 깨끗한 미분을 얻는 대신, 실시간성을 내주는 필터이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;Butterworth를 양방향으로 걸어 지연을 지운다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;통과대역이 평탄한 IIR 저역통과, Butterworth&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;통과대역이 평탄한 IIR 저역통과, Butterworth.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MA3Ml/dJMcadP9aI7/pkZKeHHylITDzQqE9o9170/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MA3Ml/dJMcadP9aI7/pkZKeHHylITDzQqE9o9170/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MA3Ml/dJMcadP9aI7/pkZKeHHylITDzQqE9o9170/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMA3Ml%2FdJMcadP9aI7%2FpkZKeHHylITDzQqE9o9170%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;통과대역이 평탄한 IIR 저역통과, Butterworth.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Butterworth(버터워스) 필터는 통과대역이 최대한 평탄한 무한 임펄스 응답(IIR) 저역통과 필터이다.&lt;br /&gt;&lt;br /&gt;IIR(과거 출력을 되먹임에 쓰는 구조)라서 적은 차수로도 가파른 감쇄를 얻을 수 있다.&lt;br /&gt;&lt;br /&gt;통과대역에 잔물결이 없어 신호의 크기를 왜곡하지 않는다는 점이 핵심 장점이다.&lt;br /&gt;&lt;br /&gt;조절값은 필터 차수와 컷오프 주파수 두 개이며, 차수가 클수록 경계가 가팔라진다.&lt;br /&gt;&lt;br /&gt;다만 되먹임 구조 탓에 인과적으로 걸면 위상이 뒤로 밀리는 위상 지연이 생긴다.&lt;br /&gt;&lt;br /&gt;이 위상 지연을 그대로 두면 봉우리의 시점이 어긋나므로 반드시 보정해야 한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;정방향과 역방향 filtfilt로 만드는 영위상&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;정방향과 역방향 filtfilt로 만드는 영위상.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GjFDQ/dJMcacKBw2j/emUkTZK8PSoRmQkFhtZ2r0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GjFDQ/dJMcacKBw2j/emUkTZK8PSoRmQkFhtZ2r0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GjFDQ/dJMcacKBw2j/emUkTZK8PSoRmQkFhtZ2r0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGjFDQ%2FdJMcacKBw2j%2FemUkTZK8PSoRmQkFhtZ2r0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;정방향과 역방향 filtfilt로 만드는 영위상.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영위상(zero-phase)은 위상 지연이 0인 필터링을 뜻한다.&lt;br /&gt;&lt;br /&gt;이는 신호를 앞에서 뒤로 한 번 거른 뒤, 결과를 다시 뒤에서 앞으로 거르는 방식으로 만든다.&lt;br /&gt;&lt;br /&gt;앞으로 걸 때 생긴 위상 지연을 뒤로 걸 때 상쇄해, 어떤 주파수에서도 시점이 밀리지 않는다.&lt;br /&gt;&lt;br /&gt;scipy와 MATLAB에서는 이 정방향&amp;middot;역방향 필터링을 filtfilt 함수로 제공한다.&lt;br /&gt;&lt;br /&gt;두 번 거르므로 실효 차수가 두 배가 되어, 2차 필터를 걸면 영위상 4차 필터가 된다.&lt;br /&gt;&lt;br /&gt;전체 신호가 있어야 역방향으로 거를 수 있으므로 영위상은 오프라인에서만 가능하다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;차수와 컷오프, 생체역학의 4차 6Hz 관례&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생체역학에서는 2차 Butterworth를 filtfilt로 걸어 영위상 4차 필터를 만드는 방식이 전형이다.&lt;br /&gt;&lt;br /&gt;보행 분석에서는 6Hz 안팎의 컷오프를 쓰는 관례가 오래전부터 자리 잡았다.&lt;br /&gt;&lt;br /&gt;컷오프는 반드시 샘플링 레이트와의 관계, 즉 나이퀴스트 주파수(샘플링 레이트의 절반) 기준으로 정규화해 넣는다.&lt;br /&gt;&lt;br /&gt;차수를 지나치게 높이면 역방향 처리에서 신호가 울리는 링잉이 생기므로 보통 8차 안쪽으로 제한한다.&lt;br /&gt;&lt;br /&gt;또한 filtfilt는 신호 길이가 필터 길이의 최소 3배는 되어야 경계 왜곡이 적다.&lt;br /&gt;&lt;br /&gt;스파이크가 있으면 미디언 필터로 먼저 걷어낸 뒤 Butterworth를 거는 것이 안전하다.&lt;/p&gt;
&lt;pre id=&quot;code_1783587540128&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from scipy.signal import butter, filtfilt

fs = 60.0        # 샘플링 레이트 (Hz)
fc = 6.0         # 컷오프 주파수 (Hz)
order = 2        # 2차 &amp;rarr; filtfilt 양방향으로 실효 4차

# 정규화 컷오프 = 컷오프 / 나이퀴스트(fs/2)
b, a = butter(order, fc / (fs / 2), btype=&quot;low&quot;)

# 정방향+역방향 &amp;rarr; 영위상
x_smooth = filtfilt(b, a, x)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;잔차 분석으로 컷오프를 고르는 법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;잔차 분석으로 컷오프를 고르는 법.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qhR6h/dJMcabEPIjx/tCKrsJ8B3mlSrnugIu8Ts1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qhR6h/dJMcabEPIjx/tCKrsJ8B3mlSrnugIu8Ts1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qhR6h/dJMcabEPIjx/tCKrsJ8B3mlSrnugIu8Ts1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqhR6h%2FdJMcabEPIjx%2FtCKrsJ8B3mlSrnugIu8Ts1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;잔차 분석으로 컷오프를 고르는 법.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컷오프를 감으로 정하면 잡음을 남기거나 신호를 뭉갤 위험이 있다.&lt;br /&gt;&lt;br /&gt;생체역학에서는 원신호와 필터 결과의 차이인 잔차를 여러 컷오프에 대해 계산하는 잔차 분석을 쓴다.&lt;br /&gt;&lt;br /&gt;컷오프를 낮출수록 잔차가 커지는데, 잔차가 잡음 수준을 넘어 급격히 커지는 지점을 적정 컷오프로 본다.&lt;br /&gt;&lt;br /&gt;이 방법은 Winter의 인체 운동 역학 교재에서 제시된 이후 표준 절차로 쓰인다.&lt;br /&gt;&lt;br /&gt;다만 잔차 분석이 항상 최적값을 주지는 않으며, 샘플링 레이트가 높을 때는 과도하게 낮은 컷오프를 고르는 경향이 보고된다.&lt;br /&gt;&lt;br /&gt;따라서 잔차 분석 결과를 출발점으로 삼되, 2차 도함수의 안정성까지 함께 확인하는 것이 좋다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;실시간엔 One Euro, 분석엔 나머지 둘&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;저역통과를 겹쳐 걸면 지연만 쌓인다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;저역통과를 겹쳐 걸면 지연만 쌓인다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBlCEQ/dJMcaassmXW/yj6nCvLsO1GAXGiuYY29d0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBlCEQ/dJMcaassmXW/yj6nCvLsO1GAXGiuYY29d0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBlCEQ/dJMcaassmXW/yj6nCvLsO1GAXGiuYY29d0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBlCEQ%2FdJMcaassmXW%2Fyj6nCvLsO1GAXGiuYY29d0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;저역통과를 겹쳐 걸면 지연만 쌓인다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 필터는 모두 저역통과 계열이므로 같은 신호에 겹쳐 거는 것은 대개 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저역통과를 직렬로 쌓으면 잡음 억제 효과는 한계에 부딪히지만 지연은 그대로 더해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예컨대 One Euro로 이미 평활한 신호에 다시 이동평균을 걸면 반응만 느려진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 하나의 실시간 경로에는 원칙적으로 하나의 평활 필터만 두는 것이 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외는 성격이 다른 필터의 조합으로, 튀는 값을 미디언 필터로 먼저 제거한 뒤 평활하는 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 저역통과를 두 번 거는 것이 아니라 이상치 제거와 평활을 나눠 맡기는 것이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;표시용과 분석용 경로를 나누는 이유&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro는 인과 필터라 실시간 화면 표시에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Savitzky-Golay와 영위상 Butterworth는 비인과 필터라 전체 데이터가 필요한 오프라인 분석에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 같은 시스템이라도 실시간 피드백 경로와 사후 분석 경로를 나누는 설계가 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코치나 학부모에게 보여 줄 라이브 궤적은 One Euro로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 정밀한 자세 지표나 각도 분석은 오프라인에서 나머지 두 필터로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 필터를 동시에 겹치지 않고 목적별로 나눠 쓰는 것이 핵심이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;미분엔 SG, 정밀 오프라인엔 Butterworth&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;미분엔 SG, 정밀 오프라인엔 Butterworth.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfb9lq/dJMcafN6Lxe/Rjqy3arKfClL0EvaFWkVZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfb9lq/dJMcafN6Lxe/Rjqy3arKfClL0EvaFWkVZ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfb9lq/dJMcafN6Lxe/Rjqy3arKfClL0EvaFWkVZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfb9lq%2FdJMcafN6Lxe%2FRjqy3arKfClL0EvaFWkVZ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;미분엔 SG, 정밀 오프라인엔 Butterworth.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프라인 필터 두 개 사이에서도 용도에 따라 선택이 갈린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속도나 가속도 같은 도함수가 필요하면 미분을 직접 내주는 Savitzky-Golay가 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 위치 자체를 잡음 없이 매끄럽게 만드는 정밀 평활에는 영위상 Butterworth가 흔히 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 비교 연구들은 신호 종류와 주파수에 따라 어느 쪽이 나은지 달라진다고 보고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 하나를 정답으로 두기보다, 도함수 필요 여부와 신호 특성으로 나눠 판단하는 것이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 실시간은 One Euro, 도함수는 SG, 정밀 오프라인 평활은 Butterworth라는 역할 분담이 성립한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2부는 고정 필터의 한계를 넘는 세 가지 실전 필터를 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Euro는 컷오프를 속도에 연동해, 단 두 개의 파라미터로 실시간 떨림과 지연을 함께 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Savitzky-Golay는 창에 다항식을 맞춰 형태를 보존하고, 평활과 동시에 속도와 가속도를 뽑아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영위상 Butterworth는 정방향과 역방향으로 두 번 걸어 지연 없이 정밀한 오프라인 평활을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 필터는 겹쳐 쓰기보다 실시간과 분석, 도함수라는 용도로 나눠 쓸 때 제 성능을 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 3부에서는 운동 모델을 세우는 Kalman과 뼈 길이를 강제하는 IK로 넘어간다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Casiez, G., Roussel, N., Vogel, D. 1&amp;euro; Filter: A Simple Speed-based Low-pass Filter for Noisy Input in Interactive Systems. &lt;a href=&quot;https://gery.casiez.net/1euro/&quot;&gt;https://gery.casiez.net/1euro/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;jaantollander, OneEuroFilter (Python 표준 구현). &lt;a href=&quot;https://github.com/jaantollander/OneEuroFilter&quot;&gt;https://github.com/jaantollander/OneEuroFilter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mistake in one_euro_filter.py, open-mmlab/mmpose Issue #1176 (평활 계수 단위 문제). &lt;a href=&quot;https://github.com/open-mmlab/mmpose/issues/1176&quot;&gt;https://github.com/open-mmlab/mmpose/issues/1176&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SmoothNet: A Plug-and-Play Network for Refining Human Poses in Videos. &lt;a href=&quot;https://arxiv.org/pdf/2112.13715&quot;&gt;https://arxiv.org/pdf/2112.13715&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Beyond Weak Perspective for Monocular 3D Human Pose Estimation (OneEuro 후처리). &lt;a href=&quot;https://arxiv.org/pdf/2009.06549&quot;&gt;https://arxiv.org/pdf/2009.06549&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Half Pound Filter for Real-Time Animation Blending (Butterworth 오프라인, 1&amp;euro; 실시간). &lt;a href=&quot;https://arxiv.org/pdf/2602.21702&quot;&gt;https://arxiv.org/pdf/2602.21702&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;scipy.signal.savgol_filter &amp;mdash; SciPy Manual. &lt;a href=&quot;https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.savgol_filter.html&quot;&gt;https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.savgol_filter.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Filtering Biomechanical Signals in Movement Analysis (Butterworth&amp;middot;SG 비교). &lt;a href=&quot;https://www.mdpi.com/1424-8220/21/13/4580&quot;&gt;https://www.mdpi.com/1424-8220/21/13/4580&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yu, B., Gabriel, D., Noble, L., An, K. N. Estimate of the Optimum Cutoff Frequency for the Butterworth Low-Pass Digital Filter. J. Applied Biomechanics, 15(3). &lt;a href=&quot;https://journals.humankinetics.com/view/journals/jab/15/3/article-p318.xml&quot;&gt;https://journals.humankinetics.com/view/journals/jab/15/3/article-p318.xml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Determining Filter Cutoff Frequency with Residual Analysis, Engineered Athletics. &lt;a href=&quot;https://engineeredathletics.com/2020/08/31/determining-filter-cutoff-frequency-with-residual-analysis-for-variable-biomechanics-applications/&quot;&gt;https://engineeredathletics.com/2020/08/31/determining-filter-cutoff-frequency-with-residual-analysis-for-variable-biomechanics-applications/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Improved methods for determining the kinematics of coronal mass ejections (SG 도함수). &lt;a href=&quot;https://arxiv.org/pdf/1307.8155&quot;&gt;https://arxiv.org/pdf/1307.8155&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;filtfilt 영위상(Zero-Phase) 디지털 필터링, MATLAB 한국. &lt;a href=&quot;https://kr.mathworks.com/help/signal/ref/filtfilt.html&quot;&gt;https://kr.mathworks.com/help/signal/ref/filtfilt.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;버터워스 필터, 공돌이의 수학정리노트. &lt;a href=&quot;https://angeloyeo.github.io/2020/09/25/Butterworth.html&quot;&gt;https://angeloyeo.github.io/2020/09/25/Butterworth.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>3D자세추정</category>
      <category>filtfilt</category>
      <category>OneEuro필터</category>
      <category>savitzkygolay</category>
      <category>버터워스필터</category>
      <category>생체역학</category>
      <category>신호처리</category>
      <category>실시간스무딩</category>
      <category>영위상</category>
      <category>저역통과필터</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1356</guid>
      <comments>https://gnidinger.tistory.com/entry/%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%91%90-%EA%B0%9C%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4-One-Euro-Savitzky-Golay-%EC%98%81%EC%9C%84%EC%83%81-Butterworth-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-2#entry1356comment</comments>
      <pubDate>Thu, 9 Jul 2026 18:04:34 +0900</pubDate>
    </item>
    <item>
      <title>부드러움과 반응성은 동시에 가질 수 없다: 지터-지연 트레이드오프와 Accel로 떨림 측정하기 | 삼각측량 후처리 (1)</title>
      <link>https://gnidinger.tistory.com/entry/%EB%B6%80%EB%93%9C%EB%9F%AC%EC%9B%80%EA%B3%BC-%EB%B0%98%EC%9D%91%EC%84%B1%EC%9D%80-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%97%86%EB%8B%A4-%EC%A7%80%ED%84%B0-%EC%A7%80%EC%97%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C%EC%98%A4%ED%94%84%EC%99%80-Accel%EB%A1%9C-%EB%96%A8%EB%A6%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-1</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%B6%80%EB%93%9C%EB%9F%AC%EC%9B%80%EA%B3%BC-%EB%B0%98%EC%9D%91%EC%84%B1%EC%9D%80-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%97%86%EB%8B%A4-%EC%A7%80%ED%84%B0-%EC%A7%80%EC%97%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C%EC%98%A4%ED%94%84%EC%99%80-Accel%EB%A1%9C-%EB%96%A8%EB%A6%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;부드러움과 반응성은 동시에 가질 수 없다: 지터-지연 트레이드오프와 Accel로 떨림 측정하기 | 삼각측량 후처리 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%91%90-%EA%B0%9C%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%9E%A1%EB%8A%94%EB%8B%A4-One-Euro-Savitzky-Golay-%EC%98%81%EC%9C%84%EC%83%81-Butterworth-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;파라미터 두 개로 실시간을 잡는다: One Euro, Savitzky-Golay, 영위상 Butterworth | 삼각측량 후처리 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EB%93%B1%EC%86%8D-%EA%B0%80%EC%A0%95%EC%9D%B4-%EB%AC%B4%EB%84%88%EC%A7%88-%EB%95%8C-Kalman-%EC%83%81%ED%83%9C%EC%B6%94%EC%A0%95%EA%B3%BC-IK-%EB%BC%88%EA%B8%B8%EC%9D%B4-%EC%A0%9C%EC%95%BD-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;등속 가정이 무너질 때: Kalman 상태추정과 IK 뼈길이 제약 | 삼각측량 후처리 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89%EC%9D%B4-%EB%8B%A4-%EC%9E%A1%EC%95%98%EB%8B%A4%EB%A9%B4-%ED%95%84%ED%84%B0%EB%8A%94-%EC%A0%95%EB%A7%90-%EA%B0%92%EC%9D%84-%ED%96%88%EB%8A%94%EA%B0%80-%ED%95%99%EC%8A%B5%ED%98%95-SmoothNet%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%B0%EC%B9%98-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼각측량이 다 잡았다면, 필터는 정말 값을 했는가: 학습형 SmoothNet과 파이프라인 배치 | 삼각측량 후처리 (4)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;썸네일.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZVSMH/dJMcaf1vnNu/uGAKJIGmiGhOjiswXOUkz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZVSMH/dJMcaf1vnNu/uGAKJIGmiGhOjiswXOUkz1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZVSMH/dJMcaf1vnNu/uGAKJIGmiGhOjiswXOUkz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZVSMH%2FdJMcaf1vnNu%2FuGAKJIGmiGhOjiswXOUkz1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;썸네일.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드리블하는 선수를 여러 대의 카메라로 찍어 3차원 좌표로 복원하면, 관절의 궤적이 매끄럽게 이어지지 않고 프레임마다 미세하게 튄다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실제 움직임 위에 얹혀 잘게 진동하는 오차를 지터(jitter) 또는 떨림이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 드리블은 방향이 급변하고 폭발적으로 가속하는 동작이라, 이 떨림이 빠른 움직임을 정확히 따라가야 하는 요구와 정면으로 부딪친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연구자들은 이런 시간축 불일치가 위치 자체의 오차보다 사람이 체감하는 품질을 더 크게 떨어뜨린다고 지적한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 삼각측량으로 얻은 3차원 자세는 후처리 단계에서 떨림을 다듬는 과정이 거의 필수적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시리즈는 그 후처리 필터들을 입문자 관점에서 정리하는 것을 목표로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부인 이 글은 개별 필터를 다루기 전에 반드시 알아야 할 토대를 먼저 세운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적으로 떨림이 왜 생기는지, 떨림과 지연이 어떤 관계인지, 떨림을 어떻게 수치로 재는지, 가장 단순한 필터가 무엇인지 다룬다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;삼각측량한 3D 관절은 멈춰 있어도 떨린다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;프레임마다 새로 추론하는 2D 검출의 떨림&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;프레임마다 새로 추론하는 2D 검출의 떨림.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9PQbc/dJMcado3Snw/ITKhUpaEtwSkxpWs2Yz320/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9PQbc/dJMcado3Snw/ITKhUpaEtwSkxpWs2Yz320/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9PQbc/dJMcado3Snw/ITKhUpaEtwSkxpWs2Yz320/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9PQbc%2FdJMcado3Snw%2FITKhUpaEtwSkxpWs2Yz320%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;프레임마다 새로 추론하는 2D 검출의 떨림.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설령 선수가 완전히 멈춰 서 있다고 가정해도 관절 좌표는 여전히 미세하게 떨리는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 떨림이 선수의 움직임이 아니라 추정 과정 자체에서 나온다는 것을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 자세 추정 파이프라인은 각 프레임의 2차원 키포인트를 프레임마다 독립적으로 추론한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 눈으로 보기에 거의 동일한 연속 프레임에서도 키포인트 좌표가 눈에 띄게 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 신경망이 입력의 미세한 변화에 충분히 강건하지 못하기 때문에 발생하는 현상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 미세한 변화는 영상의 의미를 바꾸지 않지만 히트맵(heatmap)의 최고점 위치를 흔든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 각 프레임의 오차는 서로 독립적이라 상쇄되지 않고 시간축 떨림으로 그대로 드러난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 떨림의 1차적인 원인은 삼각측량 이전 단계인 2차원 검출 자체에 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;시차각이 만드는 깊이 방향의 떨림&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;시차각이 만드는 깊이 방향의 떨림.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kg4Y2/dJMcacDOSeA/9SLZlt2a3BLKl2Ku2yMfy0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kg4Y2/dJMcacDOSeA/9SLZlt2a3BLKl2Ku2yMfy0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kg4Y2/dJMcacDOSeA/9SLZlt2a3BLKl2Ku2yMfy0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKg4Y2%2FdJMcacDOSeA%2F9SLZlt2a3BLKl2Ku2yMfy0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;시차각이 만드는 깊이 방향의 떨림.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼각측량은 여러 카메라에서 나온 광선들이 만나는 지점을 3차원 좌표로 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 두 광선이 이루는 각도를 시차각(視差角, parallax angle)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시차각이 좁으면 광선들이 거의 평행해져 깊이 방향의 위치가 잘 정해지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 상태에서는 2차원의 작은 픽셀 오차가 깊이 방향으로 크게 증폭되어 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 3차원 떨림은 카메라 배치에 따라 특히 깊이 축에서 커지는 경향이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근본적으로 2차원 영상 하나를 3차원으로 되돌리는 문제 자체가 해가 유일하지 않은 불량 조건(ill-posed) 문제이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;동기화&amp;middot;캘리브레이션&amp;middot;가림이 만드는 순간 튐&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중 카메라 삼각측량은 모든 카메라가 같은 순간을 촬영했다는 가정 위에서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 카메라 간 시간이 밀리초 단위만 어긋나도 움직이는 관절에서는 큰 재투영 오차가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 정밀한 시스템은 하드웨어 트리거나 젠락(genlock) 같은 방식으로 셔터를 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카메라 내부&amp;middot;외부 파라미터를 구하는 캘리브레이션(calibration)의 오차도 3차원 좌표를 체계적으로 밀어낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가림(occlusion)이나 신뢰도가 낮은 키포인트는 이상치 검출을 만들어 순간적으로 값이 크게 튀게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 한 실험은 가장 가까운 프레임을 맞추는 방식과 하위 프레임 보간 방식의 삼각측량 오차가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 8.8픽셀과 4.4픽셀로 갈렸다고 보고한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;떨림을 누르면 지연이 튀어나온다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;스무딩이 떨림과 지연을 맞바꾸는 원리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스무딩이 떨림과 지연을 맞바꾸는 원리.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5vEZL/dJMcacDOSeN/a3zE8ohjHk87qrUtAQOYJK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5vEZL/dJMcacDOSeN/a3zE8ohjHk87qrUtAQOYJK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5vEZL/dJMcacDOSeN/a3zE8ohjHk87qrUtAQOYJK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5vEZL%2FdJMcacDOSeN%2Fa3zE8ohjHk87qrUtAQOYJK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;스무딩이 떨림과 지연을 맞바꾸는 원리.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스무딩(smoothing)은 현재 값과 과거 값을 함께 평균 내어 고주파 잡음을 눌러 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 과거 값을 섞는다는 것은 값이 변할 때 출력이 뒤늦게 따라간다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스무딩을 강하게 걸수록 떨림은 더 많이 사라지지만 그만큼 지연도 더 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 떨림과 지연을 맞바꾸는 관계는 저역통과 계열 필터 전반에 공통으로 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고정된 필터 하나로는 이 두 가지를 동시에 최소로 만들 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이 지점이 이 글의 제목이 말하는 부드러움과 반응성을 동시에 가질 수 없다는 명제의 실체이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;인과 필터의 숙명, 위상 지연&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인과(causal) 필터는 현재와 과거의 값만 사용하고 미래의 값은 쓰지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 시스템은 아직 오지 않은 미래 프레임을 알 수 없으므로 반드시 인과적이어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 값을 평균에 섞으면 출력의 위상이 뒤로 밀리는 위상 지연(phase lag)이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스무딩을 세게 걸수록 이 위상 지연도 함께 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 미래 값까지 쓰는 비인과 필터는 지연을 줄일 수 있지만 오프라인에서만 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실시간 처리에서 지연은 완전히 없앨 수 없고 관리의 대상이 된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;컷오프 주파수로 신호와 노이즈를 가른다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;컷오프 주파수로 신호와 노이즈를 가른다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CC729/dJMcaglPcaV/whOBs6TTKMngRxdHvWSq5k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CC729/dJMcaglPcaV/whOBs6TTKMngRxdHvWSq5k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CC729/dJMcaglPcaV/whOBs6TTKMngRxdHvWSq5k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCC729%2FdJMcaglPcaV%2FwhOBs6TTKMngRxdHvWSq5k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;컷오프 주파수로 신호와 노이즈를 가른다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저역통과 필터(low-pass filter)는 낮은 주파수는 통과시키고 높은 주파수는 감쇄시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 사람의 움직임은 대체로 낮은 주파수이고 떨림은 대체로 높은 주파수에 몰려 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컷오프(cutoff) 주파수는 어디까지를 신호로 남기고 어디부터를 잡음으로 자를지 가르는 경계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컷오프를 낮게 잡으면 더 매끄러워지지만 진짜 빠른 움직임까지 함께 지워질 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 컷오프는 반드시 프레임 속도, 즉 샘플링 레이트와의 관계 속에서 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 필터 설계는 이 경계를 어디에 둘 것인가의 문제로 요약된다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;재지 못하는 떨림은 고칠 수 없다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;정확도와 매끄러움은 서로 다른 축이다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;정확도와 매끄러움은 서로 다른 축이다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MK2jn/dJMcaglPca4/kzYVOFSSAhgL1PaHLLYmMK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MK2jn/dJMcaglPca4/kzYVOFSSAhgL1PaHLLYmMK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MK2jn/dJMcaglPca4/kzYVOFSSAhgL1PaHLLYmMK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMK2jn%2FdJMcaglPca4%2FkzYVOFSSAhgL1PaHLLYmMK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;정확도와 매끄러움은 서로 다른 축이다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세 후처리의 품질은 정확도와 매끄러움이라는 두 가지 축으로 나누어 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확도는 추정한 위치가 실제 위치에 얼마나 가까운지를 묻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매끄러움은 움직임이 시간에 걸쳐 얼마나 일관되게 이어지는지를 묻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘은 서로 다른 축이라 한쪽이 좋아질 때 다른 쪽이 나빠지기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 단일 프레임 모델은 학습이 진행되며 위치 오차는 줄지만 떨림은 오히려 커지는 경향을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 지표 하나만으로는 후처리의 좋고 나쁨을 온전히 판단할 수 없다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;2차 차분으로 떨림을 재는 Accel과 MPJVE&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세 추정에서 정확도는 보통 MPJPE(Mean Per Joint Position Error, 관절별 평균 위치 오차)로 잰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매끄러움은 흔히 Accel(Acceleration Error, 가속도 오차)이라는 지표로 잰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Accel은 인접한 프레임들 사이에서 3차원 키포인트의 2차 차분, 즉 가속도를 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 클수록 관절이 프레임마다 급격히 방향을 바꾼다는 뜻이므로 떨림이 심하다고 해석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷하게 MPJVE(Mean Per Joint Velocity Error, 관절별 평균 속도 오차)는 1차 차분으로 속도의 일관성을 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 연구는 위치의 3차 미분인 저크(jerk)를 매끄러움의 또 다른 지표로 함께 보고한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783491225646&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

# kps: (T, J, 3) &amp;mdash; T 프레임, J 관절, 3D 좌표(mm 가정)
def accel_magnitude(kps):
    # 2차 차분으로 가속도를 구한다 (단위: mm/frame^2)
    acc = kps[2:] - 2 * kps[1:-1] + kps[:-2]
    # 관절별 가속도 크기의 평균 &amp;rarr; 값이 클수록 떨림이 심하다
    return np.linalg.norm(acc, axis=-1).mean()

def mpjve(pred, gt):
    # 1차 차분으로 속도를 구해 정답과 비교한다
    vel_pred = pred[1:] - pred[:-1]
    vel_gt = gt[1:] - gt[:-1]
    return np.linalg.norm(vel_pred - vel_gt, axis=-1).mean()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;차분이란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차분(差分, difference)은 이웃한 두 값의 차이를 뜻하며, 연속 함수의 미분을 이산 데이터로 옮긴 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미분이 간격을 0으로 보내는 극한 연산이라면, 차분은 프레임처럼 띄엄띄엄 찍힌 값에서 그 미분을 근사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차 차분은 x[t] - x[t-1]로, 한 프레임 동안 값이 얼마나 변했는지, 즉 속도를 근사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 차분은 차분을 한 번 더 차분한 x[t] - 2&amp;middot;x[t-1] + x[t-2]로, 속도의 변화인 가속도를 근사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히는 2차 차분이 프레임 간격 &amp;Delta;t의 제곱을 곱한 2차 미분에 대응하지만, 떨림을 상대 비교할 때는 이를 생략하고 그대로 쓴다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;Accel만 보면 속는다, 과도한 스무딩의 함정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Accel만 보면 속는다, 과도한 스무딩의 함정.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3pyxi/dJMcajv7T8w/pHkTjxtmCM6l8f9Mo4fKSK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3pyxi/dJMcajv7T8w/pHkTjxtmCM6l8f9Mo4fKSK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3pyxi/dJMcajv7T8w/pHkTjxtmCM6l8f9Mo4fKSK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3pyxi%2FdJMcajv7T8w%2FpHkTjxtmCM6l8f9Mo4fKSK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;Accel만 보면 속는다, 과도한 스무딩의 함정.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Accel은 스무딩을 세게 걸수록 낮아지므로 얼핏 좋은 지표처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 출력을 완전히 평평하게 만들면 가속도가 0이 되어도 결과는 쓸모가 없어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Accel이 낮다는 사실 하나만으로는 좋은 결과라고 단정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 과도한 스무딩을 걸러내려면 반드시 MPJPE를 Accel과 함께 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 벤치마크도 여러 필터의 Accel을 비슷한 수준으로 맞춘 뒤 MPJPE를 비교하는 방식을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 떨림 지표와 정확도 지표는 언제나 짝으로 읽어야 한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;스무딩은 평균, 가중, 정렬에서 출발한다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;창 안을 평균 내는 이동평균과 그 지연&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;창 안을 평균 내는 이동평균과 그 지연.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ByU2N/dJMcag0oJ1j/5v4P10RO2ZMvtBdKIiS0T1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ByU2N/dJMcag0oJ1j/5v4P10RO2ZMvtBdKIiS0T1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ByU2N/dJMcag0oJ1j/5v4P10RO2ZMvtBdKIiS0T1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FByU2N%2FdJMcag0oJ1j%2F5v4P10RO2ZMvtBdKIiS0T1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;창 안을 평균 내는 이동평균과 그 지연.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동평균(moving average) 필터는 각 값을 고정된 창 안의 평균으로 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창을 넓게 잡으면 떨림은 더 많이 사라지지만 그만큼 지연도 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 필터는 창 안의 오래된 값과 최근 값에 모두 같은 가중치를 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 값이 최근 값과 똑같이 반영되기 때문에 변화에 대한 반응이 느리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 단순 이동평균은 빠르게 방향을 바꾸는 움직임에는 잘 맞지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 1차원 신호에 이동평균을 적용하는 가장 기본적인 형태이며, 실제로는 각 좌표 축마다 따로 적용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783491269279&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

def moving_average(x, window):
    # x: (T,) 1차원 신호, window: 홀수 창 크기
    kernel = np.ones(window) / window
    # 경계를 반사시켜 길이를 유지한다
    padded = np.pad(x, window // 2, mode=&quot;reflect&quot;)
    return np.convolve(padded, kernel, mode=&quot;valid&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;최근값에 무게를 싣는 지수이동평균, EMA&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;최근값에 무게를 싣는 지수이동평균, EMA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blFatb/dJMcag0oJ1F/RaBZXBYKpDN9nsgCjoBuA0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blFatb/dJMcag0oJ1F/RaBZXBYKpDN9nsgCjoBuA0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blFatb/dJMcag0oJ1F/RaBZXBYKpDN9nsgCjoBuA0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblFatb%2FdJMcag0oJ1F%2FRaBZXBYKpDN9nsgCjoBuA0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;최근값에 무게를 싣는 지수이동평균, EMA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지수이동평균(Exponential Moving Average, EMA)은 최근 값에 더 큰 가중치를 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 평활 계수 알파(alpha) 하나로 동작하는 1차 저역통과 필터에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 값일수록 가중치가 기하급수적으로 줄어들어 단순 평균보다 반응이 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알파를 작게 잡으면 더 강하게 매끄러워지고, 크게 잡으면 변화를 더 빨리 따라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 EMA도 고정된 알파 하나만 쓰기 때문에 움직임의 속도 변화에 스스로 맞추지는 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내 자료에서는 이 필터를 지수 가중 이동 평균 필터라고 부르기도 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783491280260&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

def ema(x, alpha):
    # x: (T,) 신호, alpha: 0~1, 클수록 최근값을 더 따라간다
    y = np.empty_like(x, dtype=float)
    y[0] = x[0]
    for t in range(1, len(x)):
        # 최근값과 직전 추정값의 가중합
        y[t] = alpha * x[t] + (1 - alpha) * y[t - 1]
    return y&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;튐을 정렬로 걷어내는 비선형 미디언 필터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;튐을 정렬로 걷어내는 비선형 미디언 필터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kLGnG/dJMcag0oJ1S/lwyvchtswDCMkDb9yQKksK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kLGnG/dJMcag0oJ1S/lwyvchtswDCMkDb9yQKksK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kLGnG/dJMcag0oJ1S/lwyvchtswDCMkDb9yQKksK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkLGnG%2FdJMcag0oJ1S%2FlwyvchtswDCMkDb9yQKksK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;튐을 정렬로 걷어내는 비선형 미디언 필터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미디언 필터(median filter)는 창 안의 값들을 정렬해 그 중앙값을 결과로 삼는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곱셈과 합으로 계산하는 선형 필터와 달리 정렬을 쓰기 때문에 비선형 필터로 분류된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 소금&amp;middot;후추 잡음처럼 갑자기 튀는 값을 제거하는 데 특히 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균과 달리 극단적인 이상치 하나가 결과를 끌고 가지 못한다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 미디언 필터는 흔히 매끄러움을 다듬는 필터 앞단에서 순간 튐을 먼저 걷어내는 데 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 scipy.signal.medfilt로 간단히 쓸 수 있지만, 동작을 이해하기 위해 직접 구현하면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1783491289298&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

def median_filter(x, window):
    # x: (T,) 신호, window: 홀수 창 크기
    half = window // 2
    padded = np.pad(x, half, mode=&quot;reflect&quot;)
    out = np.empty_like(x, dtype=float)
    for t in range(len(x)):
        # 창 안 값을 정렬해 중앙값을 고른다
        out[t] = np.median(padded[t:t + window])
    return out&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;고정 컷오프의 한계와 One Euro로 가는 길&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;고정 컷오프의 한계와 One Euro로 가는 길.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Q9EO/dJMcaasrf3U/TKclwDhiwEF7r5VygnihxK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Q9EO/dJMcaasrf3U/TKclwDhiwEF7r5VygnihxK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Q9EO/dJMcaasrf3U/TKclwDhiwEF7r5VygnihxK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Q9EO%2FdJMcaasrf3U%2FTKclwDhiwEF7r5VygnihxK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-filename=&quot;고정 컷오프의 한계와 One Euro로 가는 길.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동평균, EMA, 미디언 필터는 모두 하나의 고정된 설정값으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 고정된 설정값은 멈춰 있을 때 매끄럽고 빠를 때 민첩한 두 조건을 동시에 만족하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느린 움직임에서는 강한 스무딩이 필요하고 빠른 움직임에서는 낮은 지연이 필요하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 컷오프로는 이 둘을 함께 잡을 수 없다는 것이 고정 필터의 근본적인 한계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한계를 넘기 위해 컷오프를 움직임의 속도에 따라 바꾸는 발상이 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 그 필터가 다음 2부에서 다룰 One Euro 필터이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부는 개별 필터를 다루기 전에 필요한 토대를 세 가지 축으로 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째로 3차원 자세의 떨림은 프레임 독립적인 2차원 검출, 카메라 기하, 동기화 오차에서 비롯된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째로 어떤 스무딩이든 떨림을 줄이면 지연이 늘어나는 트레이드오프를 피할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셋째로 떨림은 느낌이 아니라 Accel과 MPJVE 같은 지표로 수치화해 정확도와 함께 읽어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동평균, EMA, 미디언 필터는 이 문제를 다루는 가장 단순한 출발점이지만 고정된 설정값이라는 한계를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 2부에서는 이 한계를 속도 적응으로 넘는 One Euro 필터를 코드와 함께 자세히 살펴본다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SmoothNet: A Plug-and-Play Network for Refining Human Poses in Videos. &lt;a href=&quot;https://arxiv.org/pdf/2112.13715&quot;&gt;https://arxiv.org/pdf/2112.13715&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Improving robustness for pose estimation via stable heatmap regression. &lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S0925231222004131&quot;&gt;https://www.sciencedirect.com/science/article/abs/pii/S0925231222004131&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Exploiting temporal information for 3D pose estimation. &lt;a href=&quot;https://arxiv.org/pdf/1711.08585&quot;&gt;https://arxiv.org/pdf/1711.08585&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Robust Uncertainty-Aware Multiview Triangulation. &lt;a href=&quot;https://arxiv.org/abs/2008.01258&quot;&gt;https://arxiv.org/abs/2008.01258&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RocSync: Millisecond-Accurate Temporal Synchronization for Heterogeneous Camera Systems. &lt;a href=&quot;https://arxiv.org/html/2511.14948&quot;&gt;https://arxiv.org/html/2511.14948&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A Synchronized Audio-Visual Multi-View Capture System. &lt;a href=&quot;https://arxiv.org/html/2603.23089v1&quot;&gt;https://arxiv.org/html/2603.23089v1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Evaluation of 3D Markerless Motion Capture Accuracy Using OpenPose With Multiple Video Cameras. &lt;a href=&quot;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7739760/&quot;&gt;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7739760/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Real-time human pose estimation and tracking on monocular videos: A systematic literature review. &lt;a href=&quot;https://www.sciencedirect.com/science/article/pii/S0925231225019812&quot;&gt;https://www.sciencedirect.com/science/article/pii/S0925231225019812&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;OmniRobotHome: A Multi-Camera Home Platform for Real-Time Human-Robot Interaction. &lt;a href=&quot;https://arxiv.org/pdf/2604.28197&quot;&gt;https://arxiv.org/pdf/2604.28197&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Anipose: a toolkit for robust markerless 3D pose estimation. &lt;a href=&quot;https://www.biorxiv.org/content/10.1101/2020.05.26.117325v1.full&quot;&gt;https://www.biorxiv.org/content/10.1101/2020.05.26.117325v1.full&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;저주파 통과 필터(low pass filter), gaussian37. &lt;a href=&quot;https://gaussian37.github.io/autodrive-ose-low_pass_filter/&quot;&gt;https://gaussian37.github.io/autodrive-ose-low_pass_filter/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝: 미디언 필터. &lt;a href=&quot;https://thebook.io/006939/0367/&quot;&gt;https://thebook.io/006939/0367/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;정확한 깊이 인식의 핵심, 스테레오 비전의 원리와 응용. &lt;a href=&quot;https://visionsystem.kr/technical-info&quot;&gt;https://visionsystem.kr/technical-info&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>3D자세추정</category>
      <category>Accel</category>
      <category>EMA</category>
      <category>모션스무딩</category>
      <category>미디언필터</category>
      <category>삼각측량</category>
      <category>이동평균</category>
      <category>지터억제</category>
      <category>컴퓨터비전</category>
      <category>후처리필터</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1355</guid>
      <comments>https://gnidinger.tistory.com/entry/%EB%B6%80%EB%93%9C%EB%9F%AC%EC%9B%80%EA%B3%BC-%EB%B0%98%EC%9D%91%EC%84%B1%EC%9D%80-%EB%8F%99%EC%8B%9C%EC%97%90-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%97%86%EB%8B%A4-%EC%A7%80%ED%84%B0-%EC%A7%80%EC%97%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%93%9C%EC%98%A4%ED%94%84%EC%99%80-Accel%EB%A1%9C-%EB%96%A8%EB%A6%BC-%EC%B8%A1%EC%A0%95%ED%95%98%EA%B8%B0-%EC%82%BC%EA%B0%81%EC%B8%A1%EB%9F%89-%ED%9B%84%EC%B2%98%EB%A6%AC-1#entry1355comment</comments>
      <pubDate>Wed, 8 Jul 2026 15:19:33 +0900</pubDate>
    </item>
    <item>
      <title>서버는 안 만졌는데, 파이프라인이 돈다 | 에어플로우 &amp;middot; MWAA 입문 (3)</title>
      <link>https://gnidinger.tistory.com/entry/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%95%88-%EB%A7%8C%EC%A1%8C%EB%8A%94%EB%8D%B0-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%B4-%EB%8F%88%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-3</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%81%AC%EB%A1%A0%ED%83%AD%EC%9C%BC%EB%A1%9C%EB%8F%84-%EB%90%98%EB%8A%94%EB%8D%B0-%EC%99%9C-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%B8%EA%B0%80-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;크론탭으로도 되는데, 왜 에어플로우인가 | 에어플로우 &amp;middot; MWAA 입문 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%95%A8%EC%88%98%EC%97%90-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%98%EB%82%98-%EA%B7%B8%EA%B2%8C-DAG%EC%98%80%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%85%EB%AC%B8-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;함수에 데코레이터 하나, 그게 DAG였다 | 에어플로우 &amp;middot; MWAA 입문 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%95%88-%EB%A7%8C%EC%A1%8C%EB%8A%94%EB%8D%B0-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%B4-%EB%8F%88%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;서버는 안 만졌는데, 파이프라인이 돈다 | 에어플로우 &amp;middot; MWAA 입문 (3)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 33957.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4EorA/dJMcaiDWYOj/Uc70OsvxdsDR1JJxvFetIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4EorA/dJMcaiDWYOj/Uc70OsvxdsDR1JJxvFetIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4EorA/dJMcaiDWYOj/Uc70OsvxdsDR1JJxvFetIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4EorA%2FdJMcaiDWYOj%2FUc70OsvxdsDR1JJxvFetIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;338&quot; data-filename=&quot;Group 33957.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 에어플로우로 만든 파이프라인을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마존 MWAA(Managed Workflows for Apache Airflow, 아마존의 관리형 에어플로우 서비스)로 운영하는 방법을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부와 2부에서는 에어플로우의 개념과 DAG 작성법을 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 실제 서비스에서는 에어플로우를 실행할 서버를 계속 운영해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 운영 부담을 덜어 주는 것이 관리형 서비스인 MWAA다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 편은 MWAA가 무엇을 대신 맡는지, 그리고 파이프라인을 어떻게 배포하는지 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 설명은 에어플로우 3.x와 2026년 기준 MWAA 기능을 바탕으로 한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;에어플로우를 직접 운영하는 일은 만만치 않다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;스케줄러, 웹 서버, 데이터베이스를 모두 직접 띄워야 한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스케줄러, 웹 서버, 데이터베이스를 모두 직접 띄워야 한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tF5rr/dJMcadWVg0b/584QulaofDhVMA3DVhEo70/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tF5rr/dJMcadWVg0b/584QulaofDhVMA3DVhEo70/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tF5rr/dJMcadWVg0b/584QulaofDhVMA3DVhEo70/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtF5rr%2FdJMcadWVg0b%2F584QulaofDhVMA3DVhEo70%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;스케줄러, 웹 서버, 데이터베이스를 모두 직접 띄워야 한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 여러 구성 요소가 함께 맞물려 동작하는 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프라인을 돌리려면 스케줄러(scheduler, 실행 시점을 판단하는 구성 요소)를 켜 두어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 실제로 실행하는 워커(worker, 태스크를 처리하는 프로세스)도 함께 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태를 눈으로 보는 웹 서버와 이력을 저장하는 데이터베이스도 갖춰야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구성 요소들을 직접 설치하고 서로 연결하는 일은 처음부터 손이 많이 간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 파이프라인을 짜기도 전에 인프라부터 준비해야 하는 부담이 생긴다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;확장과 보안, 버전 업그레이드까지 직접 책임져야 한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 운영하면 구성 요소를 켜 두는 것으로 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 몰릴 때 워커를 늘리는 확장도 직접 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 권한과 네트워크 같은 보안 설정도 스스로 챙겨야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우의 새 버전이 나오면 업그레이드 작업도 직접 수행해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 운영 업무는 파이프라인의 실제 목적과는 거리가 있는 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 데이터 작업보다 서버 관리에 시간을 더 쓰게 되는 상황이 생긴다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;이 운영 부담을 덜려고 나온 관리형 서비스, MWAA&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;이 운영 부담을 덜려고 나온 관리형 서비스, MWAA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMSqOp/dJMcabx0LFy/zkhAGyUH90CBdzf621KUy1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMSqOp/dJMcabx0LFy/zkhAGyUH90CBdzf621KUy1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMSqOp/dJMcabx0LFy/zkhAGyUH90CBdzf621KUy1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMSqOp%2FdJMcabx0LFy%2FzkhAGyUH90CBdzf621KUy1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;이 운영 부담을 덜려고 나온 관리형 서비스, MWAA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 운영 부담을 줄이기 위해 아마존은 MWAA를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 아마존 웹 서비스(AWS, 아마존의 클라우드 플랫폼)에서 제공하는 관리형 에어플로우다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리형(managed)이란 서버 운영을 서비스 제공자가 대신 맡는 방식을 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 서버를 직접 설치하거나 유지하지 않고 에어플로우를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 제목에서 말한 &quot;서버는 안 만졌는데 파이프라인이 도는&quot; 상태가 가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 섹션에서는 MWAA가 구체적으로 무엇을 대신 맡는지 살펴본다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;MWAA는 서버 운영을 대신 맡는다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;아마존이 인프라를 관리하는 완전 관리형 서비스&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA에서는 에어플로우의 구성 요소를 아마존이 대신 운영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러와 워커, 웹 서버, 데이터베이스가 모두 관리형으로 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구성 요소들은 내부적으로 아마존의 컨테이너 실행 환경 위에서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 이 서버들이 어디서 어떻게 도는지 신경 쓰지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애가 나면 아마존이 복구하고 부하가 늘면 자동으로 확장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 운영 부담이 크게 줄고 파이프라인 자체에 집중할 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;에어플로우 구성 요소를 담는 단위, 환경(environment)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;에어플로우 구성 요소를 담는 단위, 환경(environment).jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9KPq3/dJMcaaTrjpH/4fMmQj7t1pkuLXDHV1cOu0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9KPq3/dJMcaaTrjpH/4fMmQj7t1pkuLXDHV1cOu0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9KPq3/dJMcaaTrjpH/4fMmQj7t1pkuLXDHV1cOu0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9KPq3%2FdJMcaaTrjpH%2F4fMmQj7t1pkuLXDHV1cOu0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;에어플로우 구성 요소를 담는 단위, 환경(environment).jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA에서 사용의 기본 단위는 환경(environment)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경은 에어플로우 구성 요소와 설정을 하나로 묶은 실행 단위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 특정 지역(region, 데이터 센터가 위치한 권역)에 환경을 하나 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 환경을 만들 때 에어플로우 버전과 처리 용량 같은 설정을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 계정에서 개발용과 운영용 환경을 따로 만들어 쓸 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 환경은 파이프라인이 실제로 돌아가는 독립된 공간이 된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;사용자는 인프라 대신 DAG 작성에만 집중한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경을 한 번 만들고 나면 사용자가 할 일은 단순해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프라인을 정의한 DAG 파일을 준비해 환경에 올리기만 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 설치, 확장, 복구 같은 작업은 MWAA가 알아서 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 어떤 데이터를 어떤 순서로 다룰지에만 집중할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 앞선 편에서 배운 DAG 작성 지식이 그대로 쓰인다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 코드는 그대로 두고 운영만 관리형에 맡기는 구조다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;S3에 올리면 파이프라인이 배포된다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;DAG는 지정된 S3 버킷의 dags 폴더에 올린다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DAG는 지정된 S3 버킷의 dags 폴더에 올린다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8dVqr/dJMcaiRtwsP/vUsX6nkjaUSoWWeYqy4Dj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8dVqr/dJMcaiRtwsP/vUsX6nkjaUSoWWeYqy4Dj1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8dVqr/dJMcaiRtwsP/vUsX6nkjaUSoWWeYqy4Dj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8dVqr%2FdJMcaiRtwsP%2FvUsX6nkjaUSoWWeYqy4Dj1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;DAG는 지정된 S3 버킷의 dags 폴더에 올린다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 배포 창구로 아마존 S3(Simple Storage Service, 아마존의 객체 저장소)를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 환경은 지정된 S3 버킷(bucket, 파일을 담는 저장 공간)과 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG 파일은 이 버킷 안의 dags라는 폴더에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 이 폴더를 주기적으로 확인해 새 DAG를 자동으로 반영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에 따르면 이 동기화는 약 30초 간격으로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 로컬의 DAG 파일을 버킷의 dags 폴더로 올리는 명령의 예시다.&lt;/p&gt;
&lt;pre id=&quot;code_1783300235716&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws s3 cp dags/ s3://my-mwaa-bucket/dags/ --recursive&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;파이썬 라이브러리는 requirements.txt로 설치한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG가 외부 파이썬 라이브러리를 쓰면 그 목록을 함께 올려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 목록은 requirements.txt라는 파일에 적어 같은 버킷에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 이 파일을 읽어 필요한 라이브러리를 자동으로 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 버전 충돌을 막기 위해 제약 파일(constraint)을 함께 지정하는 것이 권장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제약 파일은 에어플로우 버전에 맞는 라이브러리 조합을 고정해 주며, URL의 버전은 환경의 에어플로우 버전에 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 제약 파일을 지정한 requirements.txt의 간단한 예시다.&lt;/p&gt;
&lt;pre id=&quot;code_1783300248528&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--constraint &quot;https://raw.githubusercontent.com/apache/airflow/constraints-3.0.6/constraints-3.12.txt&quot;

apache-airflow-providers-amazon
pandas&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;사용자 정의 기능은 plugins.zip으로 추가한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우의 기능을 확장하고 싶을 때는 플러그인을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인은 plugins.zip이라는 압축 파일로 묶어 같은 버킷에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 이 파일을 읽어 사용자 정의 기능을 환경에 반영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 plugins.zip은 주로 웹 화면을 확장하는 용도로 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 정의 오퍼레이터 같은 코드는 plugins.zip이 아니라 dags 폴더에 두는 것이 권장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 DAG, 라이브러리 목록, 플러그인을 각각의 파일로 올려 배포를 마친다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;환경 클래스와 오토스케일링으로 처리 용량을 정한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경의 처리 용량은 환경 클래스(environment class)로 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 클래스는 mw1.micro부터 mw1.2xlarge까지 여러 크기로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 클래스는 개발이나 시험에, 큰 클래스는 많은 작업 처리에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업량에 따라 워커 수를 자동으로 늘리는 오토스케일링(auto scaling, 자동 확장)도 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 워커 수는 1에서 25 사이로 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 대기 중인 작업이 많으면 워커를 늘리고 한가해지면 다시 줄인다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;MWAA는 이렇게 운영하고 확장한다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;VPC와 실행 역할로 네트워크와 권한을 설정한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;VPC와 실행 역할로 네트워크와 권한을 설정한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P5dCy/dJMcagGbCXt/06CGwI2uIpZfZqUkAGH3nk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P5dCy/dJMcagGbCXt/06CGwI2uIpZfZqUkAGH3nk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P5dCy/dJMcagGbCXt/06CGwI2uIpZfZqUkAGH3nk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP5dCy%2FdJMcagGbCXt%2F06CGwI2uIpZfZqUkAGH3nk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;VPC와 실행 역할로 네트워크와 권한을 설정한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA 환경은 사용자의 가상 네트워크 안에서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가상 네트워크를 VPC(Virtual Private Cloud, 가상 사설 클라우드)라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC 설정으로 웹 화면을 외부에 공개할지 내부로 제한할지 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 MWAA가 다른 AWS 서비스에 접근하려면 권한이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 권한은 실행 역할(execution role, MWAA에 부여하는 권한 집합)로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 네트워크는 VPC로, 권한은 실행 역할로 나누어 관리한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;로그와 지표는 클라우드워치로 확인한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 운영 상태를 아마존 클라우드워치(CloudWatch, 아마존의 모니터링 서비스)로 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러, 워커, 웹 서버, 작업 처리의 로그를 각각 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그의 상세 수준은 환경 설정에서 단계별로 조절할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 로그를 통해 파이프라인의 오류나 지연의 원인을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 실행 지표를 바탕으로 경보를 설정해 문제를 빠르게 감지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 문제 추적과 감시를 클라우드워치 한곳에서 처리한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;에어플로우 버전 업그레이드에는 정해진 경로가 있다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 에어플로우의 버전 업그레이드와 다운그레이드를 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 대의 버전 사이에서는 위아래로 버전을 바꿀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 2 버전에서 3 버전으로 올리는 큰 업그레이드에는 정해진 순서가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 최신 에어플로우 2 버전으로 올린 뒤에 3 버전으로 올려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 3 버전에서 2.11 버전으로 내리는 다운그레이드도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업그레이드 과정에서 MWAA는 메타데이터를 백업하고 구성 요소를 새 버전으로 교체한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;서버리스와 세이지메이커로 넓어지는 MWAA&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;서버리스와 세이지메이커로 넓어지는 MWAA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daBOsr/dJMcage28vN/uNT5AFkVmg8EOua6CtqcP0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daBOsr/dJMcage28vN/uNT5AFkVmg8EOua6CtqcP0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daBOsr/dJMcage28vN/uNT5AFkVmg8EOua6CtqcP0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaBOsr%2FdJMcage28vN%2FuNT5AFkVmg8EOua6CtqcP0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;서버리스와 세이지메이커로 넓어지는 MWAA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 설명한 방식은 환경을 직접 만드는 프로비저닝(provisioned) 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년에는 환경 없이 워크플로를 실행하는 MWAA 서버리스(Serverless)가 새로 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버리스는 작업이 실행될 때만 자원을 켜고 끝나면 반납해 사용한 만큼만 비용을 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 MWAA는 차세대 세이지메이커(SageMaker, 아마존의 머신러닝 플랫폼)와도 통합되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 통합으로 주피터 노트북 옆에서 DAG를 만들어 학습 작업까지 오케스트레이션 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 MWAA는 관리형을 넘어 서버리스와 머신러닝 환경으로 쓰임새를 넓히고 있다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 에어플로우 파이프라인을 MWAA로 운영하는 방법을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우를 직접 운영하려면 여러 구성 요소와 확장, 보안, 업그레이드를 스스로 맡아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA는 이 인프라 운영을 대신 맡아 사용자가 DAG에만 집중하게 해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포는 S3 버킷에 DAG, requirements.txt, plugins.zip을 올리는 방식으로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크와 권한은 VPC와 실행 역할로, 감시는 클라우드워치로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 세 편에 걸친 에어플로우와 MWAA 입문 시리즈를 마무리한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Amazon MWAA 제품 페이지 (서버리스, 세이지메이커 통합) - &lt;a href=&quot;https://aws.amazon.com/managed-workflows-for-apache-airflow/&quot;&gt;https://aws.amazon.com/managed-workflows-for-apache-airflow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Explore Amazon MWAA network architecture&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/migrationguide/mwaa-architecture.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/migrationguide/mwaa-architecture.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Adding or updating DAGs&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/configuring-dag-folder.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/configuring-dag-folder.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Installing Python dependencies&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/working-dags-dependencies.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/working-dags-dependencies.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Installing custom plugins&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/configuring-dag-import-plugins.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/configuring-dag-import-plugins.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Configuring the Amazon MWAA environment class&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/environment-class.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/environment-class.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Configuring Amazon MWAA worker automatic scaling&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/mwaa-autoscaling.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/mwaa-autoscaling.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA 사용 설명서, &quot;Changing the Apache Airflow version&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/upgrading-environment.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/upgrading-environment.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS Big Data Blog, &quot;Introducing Amazon MWAA micro environments for Apache Airflow&quot; - &lt;a href=&quot;https://aws.amazon.com/blogs/big-data/introducing-amazon-mwaa-micro-environments-for-apache-airflow/&quot;&gt;https://aws.amazon.com/blogs/big-data/introducing-amazon-mwaa-micro-environments-for-apache-airflow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS Big Data Blog, &quot;Introducing Apache Airflow 3 on Amazon MWAA&quot; - &lt;a href=&quot;https://aws.amazon.com/blogs/big-data/introducing-apache-airflow-3-on-amazon-mwaa-new-features-and-capabilities/&quot;&gt;https://aws.amazon.com/blogs/big-data/introducing-apache-airflow-3-on-amazon-mwaa-new-features-and-capabilities/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS What's New, &quot;Amazon SageMaker adds serverless workflows to Identity Center domains&quot; - &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2026/04/amazon-sagemaker-serverless-workflows/&quot;&gt;https://aws.amazon.com/about-aws/whats-new/2026/04/amazon-sagemaker-serverless-workflows/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon SageMaker Unified Studio 릴리스 노트 - &lt;a href=&quot;https://docs.aws.amazon.com/sagemaker-unified-studio/latest/userguide/release-notes.html&quot;&gt;https://docs.aws.amazon.com/sagemaker-unified-studio/latest/userguide/release-notes.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Data Engineering</category>
      <category>ApacheAirflow</category>
      <category>AWS</category>
      <category>dag</category>
      <category>mwaa</category>
      <category>MWAA서버리스</category>
      <category>관리형서비스</category>
      <category>데이터엔지니어링</category>
      <category>데이터파이프라인</category>
      <category>에어플로우</category>
      <category>워크플로우오케스트레이션</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1354</guid>
      <comments>https://gnidinger.tistory.com/entry/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%95%88-%EB%A7%8C%EC%A1%8C%EB%8A%94%EB%8D%B0-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%B4-%EB%8F%88%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-3#entry1354comment</comments>
      <pubDate>Mon, 6 Jul 2026 10:15:20 +0900</pubDate>
    </item>
    <item>
      <title>함수에 데코레이터 하나, 그게 DAG였다 | 에어플로우 &amp;middot; MWAA 입문 (2)</title>
      <link>https://gnidinger.tistory.com/entry/%ED%95%A8%EC%88%98%EC%97%90-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%98%EB%82%98-%EA%B7%B8%EA%B2%8C-DAG%EC%98%80%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%85%EB%AC%B8-2</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%81%AC%EB%A1%A0%ED%83%AD%EC%9C%BC%EB%A1%9C%EB%8F%84-%EB%90%98%EB%8A%94%EB%8D%B0-%EC%99%9C-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%B8%EA%B0%80-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;크론탭으로도 되는데, 왜 에어플로우인가 | 에어플로우 &amp;middot; MWAA 입문 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%95%A8%EC%88%98%EC%97%90-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%98%EB%82%98-%EA%B7%B8%EA%B2%8C-DAG%EC%98%80%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%85%EB%AC%B8-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;함수에 데코레이터 하나, 그게 DAG였다 | 에어플로우 &amp;middot; MWAA 입문 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%95%88-%EB%A7%8C%EC%A1%8C%EB%8A%94%EB%8D%B0-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%B4-%EB%8F%88%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;서버는 안 만졌는데, 파이프라인이 돈다 | 에어플로우 &amp;middot; MWAA 입문 (3)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 33957.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkgY4Y/dJMcadJiPGU/HhP5lux4bVGyPNAwtVEwV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkgY4Y/dJMcadJiPGU/HhP5lux4bVGyPNAwtVEwV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkgY4Y/dJMcadJiPGU/HhP5lux4bVGyPNAwtVEwV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkgY4Y%2FdJMcadJiPGU%2FHhP5lux4bVGyPNAwtVEwV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;338&quot; data-filename=&quot;Group 33957.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 에어플로우에서 DAG(Directed Acyclic Graph, 방향성 비순환 그래프)를 실제로 작성하는 방법을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부에서는 크론의 한계와 에어플로우의 핵심 개념을 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 그 개념을 코드로 옮겨 파이프라인을 직접 만들어 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성 방식은 데코레이터(decorator, 함수 위에 붙여 기능을 더하는 파이썬 문법)를 사용하는 TaskFlow API를 중심으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow API는 에어플로우 2.0에서 도입된, 함수를 그대로 작업으로 바꾸는 작성 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 예제와 설명은 에어플로우 3.x를 기준으로 한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;예전에는 DAG 하나 쓰기가 번거로웠다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;오퍼레이터를 직접 만들어 나열해야 했다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;오퍼레이터를 직접 만들어 나열해야 했다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rzhH3/dJMcab5SKQC/Ex5stQ188SyjOTxTl7Ezk0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rzhH3/dJMcab5SKQC/Ex5stQ188SyjOTxTl7Ezk0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rzhH3/dJMcab5SKQC/Ex5stQ188SyjOTxTl7Ezk0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrzhH3%2FdJMcab5SKQC%2FEx5stQ188SyjOTxTl7Ezk0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;오퍼레이터를 직접 만들어 나열해야 했다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow API가 나오기 전에는 오퍼레이터(operator, 특정 작업을 수행하는 템플릿)로 DAG를 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 함수를 작업으로 쓰려면 PythonOperator라는 오퍼레이터로 감싸야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 작업마다 오퍼레이터 객체를 만들고 고유한 이름을 붙여야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 오퍼레이터들을 DAG 안에 하나씩 나열하는 구조였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 늘어날수록 반복되는 설정 코드도 함께 늘어났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식은 동작에는 문제가 없었지만 코드가 장황해지는 단점이 있었다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;값을 넘기려면 XCom을 손으로 밀어 넣고 꺼냈다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적 방식에서는 작업 사이의 값 전달을 XCom(Cross-Communication, 교차 통신)으로 처리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 작업에서 값을 저장하려면 xcom_push라는 명령을 직접 호출해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 작업에서 그 값을 꺼내려면 xcom_pull 명령으로 작업 이름과 키를 지정해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 개발자가 값을 밀어 넣고 꺼내는 과정을 손으로 일일이 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 작업 이름이나 키를 잘못 적으면 값이 전달되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 전달을 위한 이런 반복 코드가 DAG를 복잡하게 만드는 원인이었다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;작업 순서도 화살표로 일일이 연결해야 했다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적&amp;nbsp;방식에서는&amp;nbsp;작업의&amp;nbsp;실행&amp;nbsp;순서를&amp;nbsp;개발자가&amp;nbsp;직접&amp;nbsp;지정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서를&amp;nbsp;정할&amp;nbsp;때는&amp;nbsp;화살표처럼&amp;nbsp;생긴&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;연산자를&amp;nbsp;사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를&amp;nbsp;들어&amp;nbsp;extract&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;transform&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;load처럼&amp;nbsp;작업을&amp;nbsp;이어&amp;nbsp;붙였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이&amp;nbsp;많아지면&amp;nbsp;이&amp;nbsp;연결&amp;nbsp;코드를&amp;nbsp;DAG&amp;nbsp;끝부분에&amp;nbsp;길게&amp;nbsp;늘어놓아야&amp;nbsp;했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값&amp;nbsp;전달과&amp;nbsp;순서&amp;nbsp;지정이&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;코드로&amp;nbsp;나뉘어&amp;nbsp;관리도&amp;nbsp;번거로웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국&amp;nbsp;작업&amp;nbsp;정의,&amp;nbsp;값&amp;nbsp;전달,&amp;nbsp;순서&amp;nbsp;지정이&amp;nbsp;모두&amp;nbsp;별도의&amp;nbsp;손질을&amp;nbsp;요구했다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;이 번거로움을 덜려고 등장한 TaskFlow API&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;이 번거로움을 덜려고 등장한 TaskFlow API.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biqAcP/dJMcacqeeww/uiDzq2axXPfQkMvdqi71RK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biqAcP/dJMcacqeeww/uiDzq2axXPfQkMvdqi71RK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biqAcP/dJMcacqeeww/uiDzq2axXPfQkMvdqi71RK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiqAcP%2FdJMcacqeeww%2FuiDzq2axXPfQkMvdqi71RK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;이 번거로움을 덜려고 등장한 TaskFlow API.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 반복 작업을 줄이기 위해 에어플로우 2.0에서 TaskFlow API가 도입되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow API는 데코레이터를 사용해 함수를 곧바로 작업으로 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 전달과 순서 지정도 개발자가 직접 쓰지 않고 에어플로우가 대신 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 자료에 따르면 같은 DAG를 TaskFlow로 쓰면 코드가 약 28퍼센트 짧아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식으로 만든 DAG의 의존성 그래프는 완전히 동일하게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 결과는 같으면서 작성은 훨씬 간결해진 것이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;함수에 데코레이터를 붙이면 DAG가 된다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;@dag 데코레이터로 파이프라인을 정의한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;@dag 데코레이터로 파이프라인을 정의한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPDqb/dJMcagzkACR/ouLPz2FRNVp2twiC6NQNm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPDqb/dJMcagzkACR/ouLPz2FRNVp2twiC6NQNm0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPDqb/dJMcagzkACR/ouLPz2FRNVp2twiC6NQNm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPDqb%2FdJMcagzkACR%2FouLPz2FRNVp2twiC6NQNm0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;@dag 데코레이터로 파이프라인을 정의한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow API에서 하나의 파이프라인은 @dag 데코레이터로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 파이썬 함수 위에 @dag를 붙이면 그 함수가 DAG가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 실행 주기나 시작일 같은 설정을 데코레이터의 인자로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.x에서는 이 데코레이터를 airflow.sdk라는 위치에서 가져오는 것이 표준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거의 airflow.decorators 경로는 지원이 중단될 예정이라 새 코드에는 권장되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 DAG 하나를 함수 하나로 깔끔하게 표현할 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;@task 데코레이터로 각 작업을 정의한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;@task 데코레이터로 각 작업을 정의한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T1PgD/dJMcafUMxOr/KEu3TV7Iu4gG9zALUSP9qK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T1PgD/dJMcafUMxOr/KEu3TV7Iu4gG9zALUSP9qK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T1PgD/dJMcafUMxOr/KEu3TV7Iu4gG9zALUSP9qK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT1PgD%2FdJMcafUMxOr%2FKEu3TV7Iu4gG9zALUSP9qK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;@task 데코레이터로 각 작업을 정의한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG 안의 각 작업은 @task 데코레이터로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업으로 만들고 싶은 파이썬 함수 위에 @task를 붙이면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 그 함수는 에어플로우가 관리하는 하나의 태스크(task, 작업)가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오퍼레이터 객체를 따로 만들거나 이름을 붙이는 과정이 필요하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 이름이 그대로 작업 이름으로 쓰이므로 코드가 직관적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 파이썬 함수를 쓰듯 자연스럽게 작업을 정의할 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;일반 파이썬 함수가 그대로 태스크가 된다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;일반 파이썬 함수가 그대로 태스크가 된다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Av6jt/dJMcaixbx6Y/J2JgkPqbPLxfkXcKElLJKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Av6jt/dJMcaixbx6Y/J2JgkPqbPLxfkXcKElLJKK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Av6jt/dJMcaixbx6Y/J2JgkPqbPLxfkXcKElLJKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAv6jt%2FdJMcaixbx6Y%2FJ2JgkPqbPLxfkXcKElLJKK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;일반 파이썬 함수가 그대로 태스크가 된다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow API의 핵심은 평범한 파이썬 함수를 그대로 작업으로 쓴다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 안에는 데이터를 다루는 일반적인 파이썬 코드를 그대로 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우 관련 문법이 함수 본문에 끼어들지 않아 가독성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 생성과 값 전달, 순서 연결은 데코레이터가 뒤에서 알아서 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 코드가 마치 보통의 파이썬 프로그램처럼 읽힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 제목에서 말한 &quot;함수에 데코레이터 하나로 DAG가 되는&quot; 방식이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;마지막에 함수를 호출해야 DAG로 등록된다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@dag를 붙인 함수는 정의만으로는 DAG로 등록되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일의 마지막에서 그 함수를 한 번 호출해야 DAG가 실제로 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 함수 이름이 pipeline이라면 pipeline()이라고 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 호출을 빠뜨리면 에어플로우가 DAG를 인식하지 못하는 흔한 실수가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출한 줄이 정의된 함수를 실행 가능한 DAG로 바꾸는 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 지금까지의 내용을 담은 최소한의 DAG 예시이다.&lt;/p&gt;
&lt;pre id=&quot;code_1783226863321&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pendulum
from airflow.sdk import dag, task


@dag(
    schedule=&quot;@daily&quot;,
    start_date=pendulum.datetime(2026, 1, 1, tz=&quot;UTC&quot;),
    catchup=False,
)
def pipeline():

    @task
    def extract():
        return [1, 2, 3]

    @task
    def transform(data: list):
        return [x * 2 for x in data]

    @task
    def load(result: list):
        print(result)

    load(transform(extract()))


pipeline()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;태스크는 함수를 호출하듯 연결된다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;반환값을 인자로 넘기면 의존성이 자동으로 연결된다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;반환값을 인자로 넘기면 의존성이 자동으로 연결된다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFIaBP/dJMcah59EBL/xgKZnSXSW94vDOd1bcVQ50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFIaBP/dJMcah59EBL/xgKZnSXSW94vDOd1bcVQ50/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFIaBP/dJMcah59EBL/xgKZnSXSW94vDOd1bcVQ50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFIaBP%2FdJMcah59EBL%2FxgKZnSXSW94vDOd1bcVQ50%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;반환값을 인자로 넘기면 의존성이 자동으로 연결된다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow에서는 한 작업의 결과를 다른 작업에 인자로 넘겨 순서를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 함수를 호출하면 그 자리에서 실행되지 않고 결과를 가리키는 객체가 반환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 객체를 다음 작업 함수의 인자로 전달하면 두 작업이 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 transform(extract())는 extract 다음에 transform이 오도록 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이 호출 관계를 보고 의존성을 자동으로 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 순서를 지정하는 별도의 코드를 쓰지 않아도 된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;태스크 사이의 값 전달을 XCom이 자동으로 처리한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;태스크 사이의 값 전달을 XCom이 자동으로 처리한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzEWpW/dJMcaccDAAQ/ZNrT6nqdSyzY2UwkA0Cg0K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzEWpW/dJMcaccDAAQ/ZNrT6nqdSyzY2UwkA0Cg0K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzEWpW/dJMcaccDAAQ/ZNrT6nqdSyzY2UwkA0Cg0K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzEWpW%2FdJMcaccDAAQ%2FZNrT6nqdSyzY2UwkA0Cg0K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;태스크 사이의 값 전달을 XCom이 자동으로 처리한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 함수가 반환한 값은 에어플로우가 XCom에 자동으로 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 작업은 그 값을 인자로 받아 별도의 명령 없이 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 xcom_push나 xcom_pull을 직접 호출할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 저장된 값은 웹 UI(Web UI, 웹 기반 사용자 인터페이스)의 XCom 탭에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 XCom으로 주고받는 값은 직렬화(serialization, 저장&amp;middot;전송 가능한 형태로 바꾸는 것)가 가능해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용량이 큰 데이터는 XCom 대신 파일이나 객체 저장소를 거치는 것이 권장된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;값 없이 순서만 정할 때는 화살표 연산자를 쓴다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;값 없이 순서만 정할 때는 화살표 연산자를 쓴다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DshpF/dJMcaccDAAX/aYIP5PhepXyk2w3tieJnO0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DshpF/dJMcaccDAAX/aYIP5PhepXyk2w3tieJnO0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DshpF/dJMcaccDAAX/aYIP5PhepXyk2w3tieJnO0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDshpF%2FdJMcaccDAAX%2FaYIP5PhepXyk2w3tieJnO0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;값 없이 순서만 정할 때는 화살표 연산자를 쓴다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 작업 사이에 주고받을 값이 없을 때도 순서는 정해야 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 값 전달 대신 &amp;gt;&amp;gt; 연산자로 순서만 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 task_a &amp;gt;&amp;gt; task_b는 task_a가 끝난 뒤 task_b를 실행하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 작업을 한 번에 이으려면 chain이라는 함수도 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 값의 흐름과 순서의 흐름을 명확히 구분해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 데이터가 오가지 않는 선후 관계는 화살표로 간단히 표현한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;여러 값을 반환할 때는 multiple_outputs를 쓴다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 여러 값을 딕셔너리(dictionary, 이름과 값을 짝지어 담는 자료형)로 반환할 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 @task(multiple_outputs=True)로 설정하면 편리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 딕셔너리의 각 항목을 별도의 XCom으로 나누어 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 다음 작업에서 필요한 값만 키로 골라 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 전체를 넘긴 뒤 다시 꺼내는 과정을 줄여 코드가 깔끔해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow와 기존 오퍼레이터를 한 DAG에서 섞어 쓰는 것도 가능하다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;스케줄을 정하면 알아서 돌아간다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;크론 표현식이나 프리셋으로 실행 주기를 정한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;크론 표현식이나 프리셋으로 실행 주기를 정한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eG2Hqh/dJMcaccDAA0/JKSCQDKZuWsDoYaBEeFTEK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eG2Hqh/dJMcaccDAA0/JKSCQDKZuWsDoYaBEeFTEK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eG2Hqh/dJMcaccDAA0/JKSCQDKZuWsDoYaBEeFTEK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeG2Hqh%2FdJMcaccDAA0%2FJKSCQDKZuWsDoYaBEeFTEK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;크론 표현식이나 프리셋으로 실행 주기를 정한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG의 실행 주기는 @dag의 schedule 인자로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 방법은 크론(cron, 시간 기반 예약 규칙) 표현식을 문자열로 넣는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &quot;0 9 * * *&quot;는 매일 오전 9시에 실행하라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는 주기는 @daily나 @hourly 같은 프리셋(preset, 미리 정의된 값)으로 간단히 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 timedelta(시간 간격 객체)를 넣어 일정 간격마다 실행할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우 3.0부터는 schedule의 기본값이 없음(None)으로 바뀌어 명시적으로 지정하는 것이 좋다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;시작일과 캐치업으로 과거 실행 범위를 정한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG는 언제부터 실행할지를 시작일(start_date)로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이 시작일과 실행 주기를 조합해 첫 실행 시점을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 실행은 해당 기간이 끝난 뒤에 시작되는 것이 기본 동작이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작일이 과거라면 그 사이의 실행을 자동으로 채울지 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 동작을 캐치업(catchup, 과거 구간을 자동으로 채워 실행하는 기능)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.x에서는 캐치업의 기본값이 거짓(False)이라 과거 실행은 기본적으로 채우지 않는다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;dags 폴더에 두면 웹 UI에서 실행을 확인할 수 있다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 DAG 파일은 에어플로우의 지정된 dags 폴더에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이 폴더를 주기적으로 훑어 새 DAG를 자동으로 인식한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인식된 DAG는 웹 UI 목록에 나타나며 켜고 끌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 실행은 DAG 런(DAG Run, 특정 시점의 한 번 실행)이라는 단위로 기록된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 UI에서는 각 작업의 상태와 로그를 화면으로 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 파이프라인이 제대로 도는지 눈으로 살피며 문제를 찾을 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;로컬 실행을 넘어 MWAA 배포로 나아간다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;로컬 실행을 넘어 MWAA 배포로 나아간다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ArtUa/dJMcaixbx88/02NLxfeStBxL6ENpDrdCq0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ArtUa/dJMcaixbx88/02NLxfeStBxL6ENpDrdCq0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ArtUa/dJMcaixbx88/02NLxfeStBxL6ENpDrdCq0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FArtUa%2FdJMcaixbx88%2F02NLxfeStBxL6ENpDrdCq0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;로컬 실행을 넘어 MWAA 배포로 나아간다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 과정은 로컬 환경에서도 그대로 시험해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 실제 서비스에서는 서버를 계속 켜 두고 운영해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 운영 부담을 덜어 주는 것이 관리형 서비스인 아마존 MWAA(Managed Workflows for Apache Airflow)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA에서는 같은 DAG 파일을 지정된 저장소에 올리는 방식으로 배포한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 이번 편에서 작성한 DAG를 그대로 관리형 환경으로 옮길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA를 이용한 배포와 운영은 다음 편에서 자세히 다룬다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 TaskFlow API로 DAG를 작성하는 방법을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적 방식은 오퍼레이터 나열, 수동 값 전달, 수동 순서 지정을 요구했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TaskFlow API는 이 세 가지를 데코레이터와 자동 연결로 크게 줄여 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업은 함수를 호출하듯 이어지고 값은 XCom으로 자동 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 주기는 스케줄과 캐치업 설정으로 조절하고 웹 UI에서 결과를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 편에서는 이렇게 만든 DAG를 아마존 MWAA에 배포하는 과정을 다룬다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;Pythonic DAGs with the TaskFlow API&quot; - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/tutorial/taskflow.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/tutorial/taskflow.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;TaskFlow&quot; (Core Concepts) - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/taskflow.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/taskflow.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;DAGs&quot; (의존성 선언) - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;Cron &amp;amp; Time Intervals&quot; - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/cron.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/cron.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;Timetables&quot; - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timetable.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timetable.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow Task SDK 문서 - &lt;a href=&quot;https://airflow.apache.org/docs/task-sdk/stable/index.html&quot;&gt;https://airflow.apache.org/docs/task-sdk/stable/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Astronomer, &quot;Introduction to the TaskFlow API and Airflow decorators&quot; - &lt;a href=&quot;https://www.astronomer.io/docs/learn/airflow-decorators&quot;&gt;https://www.astronomer.io/docs/learn/airflow-decorators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Astronomer, &quot;Schedule DAGs in Apache Airflow&quot; - &lt;a href=&quot;https://www.astronomer.io/docs/learn/scheduling-in-airflow&quot;&gt;https://www.astronomer.io/docs/learn/scheduling-in-airflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Astronomer, &quot;TaskFlow API vs. traditional operators&quot; - &lt;a href=&quot;https://www.astronomer.io/blog/apache-airflow-taskflow-api-vs-traditional-operators/&quot;&gt;https://www.astronomer.io/blog/apache-airflow-taskflow-api-vs-traditional-operators/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ferruzzi (Medium, Apache Airflow), &quot;Unleashing the power of TaskFlow API in Apache Airflow&quot; - &lt;a href=&quot;https://medium.com/apache-airflow/unleashing-the-power-of-taskflow-api-in-apache-airflow-371637089141&quot;&gt;https://medium.com/apache-airflow/unleashing-the-power-of-taskflow-api-in-apache-airflow-371637089141&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS Big Data Blog, &quot;Introducing Apache Airflow 3 on Amazon MWAA&quot; - &lt;a href=&quot;https://aws.amazon.com/blogs/big-data/introducing-apache-airflow-3-on-amazon-mwaa-new-features-and-capabilities/&quot;&gt;https://aws.amazon.com/blogs/big-data/introducing-apache-airflow-3-on-amazon-mwaa-new-features-and-capabilities/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Data Engineering</category>
      <category>ApacheAirflow</category>
      <category>dag</category>
      <category>mwaa</category>
      <category>TaskFlowAPI</category>
      <category>XCOM</category>
      <category>데이터엔지니어링</category>
      <category>데이터파이프라인</category>
      <category>에어플로우</category>
      <category>에어플로우3</category>
      <category>에어플로우데코레이터</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1353</guid>
      <comments>https://gnidinger.tistory.com/entry/%ED%95%A8%EC%88%98%EC%97%90-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%98%EB%82%98-%EA%B7%B8%EA%B2%8C-DAG%EC%98%80%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%85%EB%AC%B8-2#entry1353comment</comments>
      <pubDate>Sun, 5 Jul 2026 13:52:14 +0900</pubDate>
    </item>
    <item>
      <title>크론탭으로도 되는데, 왜 에어플로우인가 | 에어플로우 &amp;middot; MWAA 입문 (1)</title>
      <link>https://gnidinger.tistory.com/entry/%ED%81%AC%EB%A1%A0%ED%83%AD%EC%9C%BC%EB%A1%9C%EB%8F%84-%EB%90%98%EB%8A%94%EB%8D%B0-%EC%99%9C-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%B8%EA%B0%80-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-1</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%81%AC%EB%A1%A0%ED%83%AD%EC%9C%BC%EB%A1%9C%EB%8F%84-%EB%90%98%EB%8A%94%EB%8D%B0-%EC%99%9C-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%B8%EA%B0%80-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;크론탭으로도 되는데, 왜 에어플로우인가 | 에어플로우 &amp;middot; MWAA 입문 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%95%A8%EC%88%98%EC%97%90-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%98%EB%82%98-%EA%B7%B8%EA%B2%8C-DAG%EC%98%80%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%85%EB%AC%B8-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;함수에 데코레이터 하나, 그게 DAG였다 | 에어플로우 &amp;middot; MWAA 입문 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%95%88-%EB%A7%8C%EC%A1%8C%EB%8A%94%EB%8D%B0-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%B4-%EB%8F%88%EB%8B%A4-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;서버는 안 만졌는데, 파이프라인이 돈다 | 에어플로우 &amp;middot; MWAA 입문 (3)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 33957.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehZW9n/dJMcadCuNZV/HP9KPLgwpBtqyiYmb4AO91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehZW9n/dJMcadCuNZV/HP9KPLgwpBtqyiYmb4AO91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehZW9n/dJMcadCuNZV/HP9KPLgwpBtqyiYmb4AO91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehZW9n%2FdJMcadCuNZV%2FHP9KPLgwpBtqyiYmb4AO91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;338&quot; data-filename=&quot;Group 33957.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 파이프라인은 여러 작업을 정해진 순서대로 실행하는 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자는 처음에 크론(cron, 유닉스 계열 운영체제의 시간 기반 작업 예약 도구)으로 이 흐름을 자동화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론은 간단한 작업을 정해진 시각에 돌리는 데에는 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 작업이 서로 얽히고 그 수가 늘어나면 크론만으로는 관리가 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 크론의 한계에서 출발해 아파치 에어플로우(Apache Airflow)가 왜 필요한지 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이어지는 편에서 다룰 &lt;br /&gt;&lt;br /&gt;아마존 MWAA(Managed Workflows for Apache Airflow, 아마존의 관리형 에어플로우 서비스)의 위치도 함께 짚는다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;크론탭으로는 결국 벽에 부딪힌다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;시간은 맞춰도 작업의 순서와 의존성은 보장하지 못하는 크론&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;시간은 맞춰도 작업의 순서와 의존성은 보장하지 못하는 크론.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cggxVC/dJMcabrhrqw/q81EA7WiqWNtOpgLGEL6Ok/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cggxVC/dJMcabrhrqw/q81EA7WiqWNtOpgLGEL6Ok/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cggxVC/dJMcabrhrqw/q81EA7WiqWNtOpgLGEL6Ok/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcggxVC%2FdJMcabrhrqw%2Fq81EA7WiqWNtOpgLGEL6Ok%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;시간은 맞춰도 작업의 순서와 의존성은 보장하지 못하는 크론.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론은 각 작업을 정해진 시각에 독립적으로 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여러 작업을 특정 순서로 이어서 돌리는 일에는 맞지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 데이터를 내려받은 뒤에 가공하는 두 작업이 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론은 앞 작업이 끝났는지 확인하지 않고 뒤 작업을 시작할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 사이의 이런 선후 관계를 의존성(dependency, 한 작업이 다른 작업의 완료에 기대는 관계)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론은 이 의존성을 표현하거나 보장하는 기능을 갖고 있지 않다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;앞 작업의 결과를 다음 작업으로 넘길 방법이 없는 한계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;앞 작업의 결과를 다음 작업으로 넘길 방법이 없는 한계.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BpoZa/dJMcaiRsOH8/s9PjIgzgdalPQHHM5vQnmK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BpoZa/dJMcaiRsOH8/s9PjIgzgdalPQHHM5vQnmK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BpoZa/dJMcaiRsOH8/s9PjIgzgdalPQHHM5vQnmK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBpoZa%2FdJMcaiRsOH8%2Fs9PjIgzgdalPQHHM5vQnmK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;600&quot; data-filename=&quot;앞 작업의 결과를 다음 작업으로 넘길 방법이 없는 한계.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프라인에서는 앞 작업이 만든 값을 뒤 작업이 이어받는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론으로 실행되는 스크립트들은 서로 독립된 프로세스로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 한 스크립트의 결과를 다른 스크립트로 직접 전달하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 이를 우회하려고 파일이나 데이터베이스를 임시 저장소로 쓰곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이런 방식은 코드를 복잡하게 만들고 오류가 생길 여지를 늘린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 주고받는 표준화된 통로가 없다는 점이 크론의 뚜렷한 한계다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;실패해도 조용하고, 어디서 멈췄는지 눈에 보이지 않는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;실패해도 조용하고, 어디서 멈췄는지 눈에 보이지 않는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1icYX/dJMcacKxzSL/JWFuHvOrBf6DxuZnAMOxZk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1icYX/dJMcacKxzSL/JWFuHvOrBf6DxuZnAMOxZk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1icYX/dJMcacKxzSL/JWFuHvOrBf6DxuZnAMOxZk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1icYX%2FdJMcacKxzSL%2FJWFuHvOrBf6DxuZnAMOxZk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;실패해도 조용하고, 어디서 멈췄는지 눈에 보이지 않는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론은 작업이 실패해도 기본적으로 알림을 보내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패를 알아차리려면 로그(log, 실행 기록)를 직접 열어 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 실패했을 때 자동으로 다시 시도하는 기능도 기본으로 제공되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 수가 수십 개로 늘어나면 어느 작업이 언제 멈췄는지 파악하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 흐름의 상태를 한눈에 보여 주는 화면도 크론에는 존재하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 가시성(visibility, 시스템 내부 상태를 관찰할 수 있는 정도)의 부족은 운영 부담을 키운다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;이 한계를 풀려고 에어비앤비에서 출발한 오케스트레이터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;이 한계를 풀려고 에어비앤비에서 출발한 오케스트레이터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lgZ6o/dJMcacKxzTC/Kk3KZE2LTDtbUzoet7mgPK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lgZ6o/dJMcacKxzTC/Kk3KZE2LTDtbUzoet7mgPK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lgZ6o/dJMcacKxzTC/Kk3KZE2LTDtbUzoet7mgPK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlgZ6o%2FdJMcacKxzTC%2FKk3KZE2LTDtbUzoet7mgPK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;254&quot; data-filename=&quot;이 한계를 풀려고 에어비앤비에서 출발한 오케스트레이터.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 2014년 숙박 공유 기업 에어비앤비(Airbnb)에서 만들어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 에어비앤비는 점점 복잡해지는 데이터 파이프라인을 관리할 도구가 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이후 오픈 소스로 공개되어 아파치 소프트웨어 재단으로 이관되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 도구의 목표는 여러 작업의 실행을 조율하는 오케스트레이션(orchestration, 작업의 순서와 시점을 조율하는 일)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 크론이 다루지 못한 의존성, 재시도, 모니터링을 기본 기능으로 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 데이터 엔지니어링 분야에서 가장 널리 쓰이는 오케스트레이션 도구가 되었다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;에어플로우는 파이프라인을 코드로 정의한다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;작업의 흐름을 방향 그래프로 그리는 단위, DAG&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;작업의 흐름을 방향 그래프로 그리는 단위, DAG.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sZ3UG/dJMcaiDWiow/oMjXRBwPWjD67FYQYhyog1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sZ3UG/dJMcaiDWiow/oMjXRBwPWjD67FYQYhyog1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sZ3UG/dJMcaiDWiow/oMjXRBwPWjD67FYQYhyog1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsZ3UG%2FdJMcaiDWiow%2FoMjXRBwPWjD67FYQYhyog1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;작업의 흐름을 방향 그래프로 그리는 단위, DAG.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우에서 하나의 파이프라인은 DAG라는 단위로 표현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG는 방향성 비순환 그래프(Directed Acyclic Graph)의 약자다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 방향성은 작업이 정해진 방향으로만 이어진다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비순환은 작업이 자기 자신으로 되돌아오는 고리가 없다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 이 DAG를 파이썬(Python) 코드로 직접 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업의 순서와 의존성을 코드로 명시하므로 파이프라인을 그대로 그래프로 그릴 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;그래프를 이루는 하나하나의 작업, 태스크&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그래프를 이루는 하나하나의 작업, 태스크.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byv4oh/dJMcabY9MPH/Ln64vaXDE4VgqWAY9gaheK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byv4oh/dJMcabY9MPH/Ln64vaXDE4VgqWAY9gaheK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byv4oh/dJMcabY9MPH/Ln64vaXDE4VgqWAY9gaheK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyv4oh%2FdJMcabY9MPH%2FLn64vaXDE4VgqWAY9gaheK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;그래프를 이루는 하나하나의 작업, 태스크.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG는 여러 개의 태스크(task, 작업)로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크는 파이프라인에서 실행되는 하나의 작업 단위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프에 비유하면 태스크는 각각의 점, 즉 노드(node)에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크와 태스크를 잇는 선은 작업 사이의 의존성을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이 의존성을 보고 어떤 태스크를 먼저 돌릴지 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 앞선 태스크가 모두 성공해야 다음 태스크가 실행된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;태스크가 할 일을 정하는 템플릿, 오퍼레이터와 훅&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;태스크가 할 일을 정하는 템플릿, 오퍼레이터와 훅.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wGfA4/dJMcaaTqDQi/jYIWMWqWtrJGj2QOiMi8jK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wGfA4/dJMcaaTqDQi/jYIWMWqWtrJGj2QOiMi8jK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wGfA4/dJMcaaTqDQi/jYIWMWqWtrJGj2QOiMi8jK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwGfA4%2FdJMcaaTqDQi%2FjYIWMWqWtrJGj2QOiMi8jK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;태스크가 할 일을 정하는 템플릿, 오퍼레이터와 훅.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크가 실제로 무엇을 하는지는 오퍼레이터(operator)가 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오퍼레이터는 특정 작업을 수행하도록 미리 만들어진 템플릿이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파이썬 함수를 실행하거나 셸 명령을 돌리는 오퍼레이터가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 시스템과 연결하는 일은 훅(hook)이 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훅은 데이터베이스나 클라우드 서비스 같은 외부 시스템에 접속하는 통로다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 다양한 오퍼레이터와 훅을 제공하여 여러 시스템을 폭넓게 연결한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;작업끼리 값을 주고받는 방법, XCom&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;작업끼리 값을 주고받는 방법, XCom.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIQ51C/dJMcabY9MPW/2eKk7CbOvUZz4KRasbl790/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIQ51C/dJMcabY9MPW/2eKk7CbOvUZz4KRasbl790/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIQ51C/dJMcabY9MPW/2eKk7CbOvUZz4KRasbl790/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIQ51C%2FdJMcabY9MPW%2F2eKk7CbOvUZz4KRasbl790%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;작업끼리 값을 주고받는 방법, XCom.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론에서 어려웠던 작업 사이의 값 전달을 에어플로우는 XCom으로 해결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XCom은 교차 통신(Cross-Communication)을 줄인 이름의 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 태스크가 작은 값을 XCom에 저장하면 다른 태스크가 그 값을 꺼내 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식으로 앞 작업의 결과를 뒤 작업이 자연스럽게 이어받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 XCom은 큰 데이터가 아니라 작은 값을 주고받도록 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용량이 큰 파일은 별도의 저장소를 거쳐 전달하는 것이 권장되는 방식이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;코드로 보는 최소한의 DAG 예시&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 지금까지 설명한 개념을 담은 간단한 DAG 예시이며, 에어플로우 3.x 기준으로 작성되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데코레이터(decorator, 함수 위에 붙여 기능을 더하는 파이썬 문법) @dag와 @task로 파이프라인과 작업을 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 태스크는 값을 추출하고 변환한 뒤 출력하는 순서로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 줄에서 함수를 호출하는 방식으로 작업의 순서와 값 전달이 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 전달과 의존성 연결은 개발자가 직접 쓰지 않아도 에어플로우가 자동으로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 작성법은 다음 편에서 예제와 함께 단계별로 다룬다.&lt;/p&gt;
&lt;pre id=&quot;code_1783149160134&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pendulum
from airflow.sdk import dag, task


@dag(
    schedule=&quot;@daily&quot;,
    start_date=pendulum.datetime(2026, 1, 1, tz=&quot;UTC&quot;),
    catchup=False,
)
def example_pipeline():

    @task
    def extract():
        return 1

    @task
    def transform(value: int):
        return value + 1

    @task
    def load(result: int):
        print(result)

    load(transform(extract()))


example_pipeline()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;흐름을 지키는 것은 스케줄러와 실행기다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;언제 무엇을 돌릴지 판단하는 심장, 스케줄러&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;언제 무엇을 돌릴지 판단하는 심장, 스케줄러.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kJzwI/dJMcaay92Zs/mSMo2lK65ARfCxwytY8DOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kJzwI/dJMcaay92Zs/mSMo2lK65ARfCxwytY8DOK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kJzwI/dJMcaay92Zs/mSMo2lK65ARfCxwytY8DOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkJzwI%2FdJMcaay92Zs%2FmSMo2lK65ARfCxwytY8DOK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;언제 무엇을 돌릴지 판단하는 심장, 스케줄러.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러(scheduler)는 에어플로우의 핵심 구성 요소다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러는 각 DAG의 실행 시점이 되었는지 계속 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행할 때가 되면 스케줄러는 어떤 태스크를 돌릴지 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 태스크 사이의 의존성을 확인하여 실행 가능한 태스크만 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고른 태스크는 실제 실행을 담당하는 구성 요소로 넘겨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러는 이 과정을 쉬지 않고 반복하며 전체 흐름을 이끈다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;태스크를 실제로 실행하는 실행기와 워커&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;태스크를 실제로 실행하는 실행기와 워커 .jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VFkiC/dJMcagTFb0r/kNpOOx2t7c4mh5M8sKEh9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VFkiC/dJMcagTFb0r/kNpOOx2t7c4mh5M8sKEh9K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VFkiC/dJMcagTFb0r/kNpOOx2t7c4mh5M8sKEh9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVFkiC%2FdJMcagTFb0r%2FkNpOOx2t7c4mh5M8sKEh9K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;태스크를 실제로 실행하는 실행기와 워커 .jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러가 넘긴 태스크를 실제로 실행하는 것은 실행기(executor)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행기는 태스크를 어디에서 어떻게 실행할지 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크를 직접 처리하는 프로세스는 워커(worker)라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소규모 환경에서는 실행기가 한 대의 기계 안에서 태스크를 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규모가 커지면 여러 워커에 태스크를 나누어 병렬로 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셀러리(Celery, 분산 작업 처리 도구)나 쿠버네티스(Kubernetes, 컨테이너 관리 플랫폼) 기반 실행기가 이런 분산 실행을 지원한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;실패하면 자동으로 다시 시도하고 알린다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;실패하면 자동으로 다시 시도하고 알린다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DPV3j/dJMb99Uvo6r/u2vx22APhfkk6eBKtxuz40/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DPV3j/dJMb99Uvo6r/u2vx22APhfkk6eBKtxuz40/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DPV3j/dJMb99Uvo6r/u2vx22APhfkk6eBKtxuz40/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDPV3j%2FdJMb99Uvo6r%2Fu2vx22APhfkk6eBKtxuz40%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;실패하면 자동으로 다시 시도하고 알린다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 태스크가 실패하면 설정에 따라 자동으로 다시 시도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시도 횟수와 간격은 개발자가 코드로 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일시적인 오류라면 이 재시도만으로 문제가 해결되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시도를 모두 소진하고도 실패하면 해당 태스크는 실패로 기록된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이런 실패를 이메일이나 메신저로 알리도록 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론이 제공하지 못한 재시도와 알림을 에어플로우는 기본 기능으로 갖추고 있다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;상태와 이력을 기억하고 보여주는 메타데이터 DB와 웹 UI&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;상태와 이력을 기억하고 보여주는 메타데이터 DB와 웹 UI.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvADiE/dJMb99Uvo6w/KvKnt0PruFVSTFeTeI0bW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvADiE/dJMb99Uvo6w/KvKnt0PruFVSTFeTeI0bW0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvADiE/dJMb99Uvo6w/KvKnt0PruFVSTFeTeI0bW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvADiE%2FdJMb99Uvo6w%2FKvKnt0PruFVSTFeTeI0bW0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;상태와 이력을 기억하고 보여주는 메타데이터 DB와 웹 UI.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 실행 상태와 이력을 메타데이터 DB에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타데이터 DB(Metadata Database, 메타데이터 데이터베이스)는 DAG와 태스크의 상태를 기록하는 저장소다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 저장소 덕분에 과거 실행 기록을 확인하고 실패한 작업을 다시 돌릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 웹 UI(Web UI, 웹 기반 사용자 인터페이스)도 함께 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 UI에서는 각 DAG의 실행 상태와 로그를 화면으로 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론에 없던 이런 가시성이 파이프라인 운영을 한결 수월하게 만든다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;에어플로우는 데이터 파이프라인의 표준이 되었다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;수많은 조직이 쓰는 사실상의 오케스트레이션 표준&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 오늘날 데이터 오케스트레이션의 사실상 표준으로 자리 잡았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 발표에 따르면 월간 내려받기 횟수는 3천만 회를 넘어섰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우를 쓰는 조직의 수도 8만 개를 넘는 것으로 집계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 주로 ETL(Extract Transform Load, 데이터 추출&amp;middot;변환&amp;middot;적재) 작업에 쓰였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 머신러닝과 생성형 인공지능 파이프라인으로 쓰임새가 넓어지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넓은 사용자 기반과 풍부한 확장 기능이 에어플로우의 표준 지위를 뒷받침한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;서버를 직접 운영하지 않는 관리형, AWS MWAA&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;서버를 직접 운영하지 않는 관리형, AWS MWAA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t80NO/dJMcabdE2xq/drUGyJxOfZYFNLfEbfudrk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t80NO/dJMcabdE2xq/drUGyJxOfZYFNLfEbfudrk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t80NO/dJMcabdE2xq/drUGyJxOfZYFNLfEbfudrk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft80NO%2FdJMcabdE2xq%2FdrUGyJxOfZYFNLfEbfudrk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;서버를 직접 운영하지 않는 관리형, AWS MWAA.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우를 직접 설치해 운영하려면 서버와 데이터베이스를 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마존 MWAA는 이 관리 부담을 대신 맡아 주는 관리형 서비스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 인프라 대신 파이프라인 자체에만 집중할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MWAA에서는 DAG 파일과 의존성 목록을 S3(Simple Storage Service, 아마존의 객체 저장소)에 올려 배포한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 확장이나 보안 설정 같은 작업은 아마존이 관리형으로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 편에서 다룬 개념은 뒤에서 MWAA를 다룰 때 그대로 이어진다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;2025년에 등장한 새 세대, 에어플로우 3.0&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우 3.0은 2025년 4월에 정식으로 공개되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 2020년의 2.0 이후 약 5년 만에 나온 대규모 개편이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.0에서는 같은 DAG의 여러 버전을 추적하는 기능이 추가되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 화면은 리액트(React)와 FastAPI 같은 최신 웹 기술로 새로 만들어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 도착하면 파이프라인을 실행하는 이벤트 기반 방식도 강화되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 기준 최신 버전은 3.2 계열이며 이 글도 3.x를 기준으로 설명한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;2026년, 지금 에어플로우를 배워야 할까&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우 2 버전은 2026년 4월에 공식 지원이 종료되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어의 지원 종료는 흔히 EOL(End of Life, 수명 종료)이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 지금 새로 배우는 사람은 자연스럽게 3.x 버전을 익히게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리형 서비스인 MWAA도 최신 3.2 버전을 지원하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넓은 채용 수요와 활발한 커뮤니티는 지금 학습의 가치를 높여 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 데이터 파이프라인을 다루려는 사람에게 에어플로우는 우선순위가 높은 도구다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 크론의 한계에서 시작해 에어플로우가 필요한 이유를 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크론은 의존성, 결과 전달, 재시도, 가시성에서 뚜렷한 약점을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 파이프라인을 DAG라는 코드 단위로 정의하여 이 문제를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러와 실행기, 메타데이터 DB와 웹 UI가 실행과 관찰을 책임진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에어플로우는 이제 데이터 오케스트레이션의 표준이 되었고 MWAA로 운영 부담까지 덜 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 편에서는 실제로 DAG를 작성하는 방법을 예제와 함께 다룬다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache Airflow 공식 블로그, &quot;Apache Airflow 3 is Generally Available!&quot; - &lt;a href=&quot;https://airflow.apache.org/blog/airflow-three-point-oh-is-here/&quot;&gt;https://airflow.apache.org/blog/airflow-three-point-oh-is-here/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 사이트, &quot;Announcements&quot; (3.2.2 릴리스 정보) - &lt;a href=&quot;https://airflow.apache.org/announcements/&quot;&gt;https://airflow.apache.org/announcements/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;Architecture Overview&quot; - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/overview.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/overview.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow 공식 문서, &quot;Pythonic DAGs with the TaskFlow API&quot; - &lt;a href=&quot;https://airflow.apache.org/docs/apache-airflow/stable/tutorial/taskflow.html&quot;&gt;https://airflow.apache.org/docs/apache-airflow/stable/tutorial/taskflow.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Airflow Task SDK 문서 - &lt;a href=&quot;https://airflow.apache.org/docs/task-sdk/stable/index.html&quot;&gt;https://airflow.apache.org/docs/task-sdk/stable/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS, &quot;Amazon MWAA now supports Apache Airflow 3.2&quot; - &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2026/04/amazon-mwaa-now-supports-apache-airflow-3-2/&quot;&gt;https://aws.amazon.com/about-aws/whats-new/2026/04/amazon-mwaa-now-supports-apache-airflow-3-2/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS Big Data Blog, &quot;Introducing Apache Airflow 3 on Amazon MWAA&quot; - &lt;a href=&quot;https://aws.amazon.com/blogs/big-data/introducing-apache-airflow-3-on-amazon-mwaa-new-features-and-capabilities/&quot;&gt;https://aws.amazon.com/blogs/big-data/introducing-apache-airflow-3-on-amazon-mwaa-new-features-and-capabilities/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amazon MWAA User Guide, &quot;Apache Airflow versions on Amazon MWAA&quot; - &lt;a href=&quot;https://docs.aws.amazon.com/mwaa/latest/userguide/airflow-versions.html&quot;&gt;https://docs.aws.amazon.com/mwaa/latest/userguide/airflow-versions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;DigitalOcean, &quot;Apache Airflow Explained: A Beginner-Friendly Guide&quot; - &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/apache-airflow-explained-beginner-guide&quot;&gt;https://www.digitalocean.com/community/tutorials/apache-airflow-explained-beginner-guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CROZ, &quot;Why do you need Apache Airflow?&quot; - &lt;a href=&quot;https://croz.net/apache-airflow/&quot;&gt;https://croz.net/apache-airflow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Milad Shiri (Medium), &quot;Cronjob vs Airflow&quot; - &lt;a href=&quot;https://medium.com/@miladshiri/cronjob-vs-airflow-why-we-use-airflow-instead-of-cronjob-f62edae67ec7&quot;&gt;https://medium.com/@miladshiri/cronjob-vs-airflow-why-we-use-airflow-instead-of-cronjob-f62edae67ec7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;endoflife.date, &quot;Apache Airflow&quot; - &lt;a href=&quot;https://endoflife.date/apache-airflow&quot;&gt;https://endoflife.date/apache-airflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Data Engineering</category>
      <category>ApacheAirflow</category>
      <category>dag</category>
      <category>mwaa</category>
      <category>데이터엔지니어링</category>
      <category>데이터파이프라인</category>
      <category>스케줄러</category>
      <category>에어플로우</category>
      <category>에어플로우3</category>
      <category>워크플로우오케스트레이션</category>
      <category>크론탭</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1352</guid>
      <comments>https://gnidinger.tistory.com/entry/%ED%81%AC%EB%A1%A0%ED%83%AD%EC%9C%BC%EB%A1%9C%EB%8F%84-%EB%90%98%EB%8A%94%EB%8D%B0-%EC%99%9C-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0%EC%9D%B8%EA%B0%80-%EC%97%90%EC%96%B4%ED%94%8C%EB%A1%9C%EC%9A%B0-%C2%B7-MWAA-%EC%9E%85%EB%AC%B8-1#entry1352comment</comments>
      <pubDate>Sat, 4 Jul 2026 16:19:34 +0900</pubDate>
    </item>
    <item>
      <title>91클래스 vs 2클래스, 파인튜닝은 정말 나았는가 | RF-DETR 파인튜닝 (추가)</title>
      <link>https://gnidinger.tistory.com/entry/91%ED%81%B4%EB%9E%98%EC%8A%A4-vs-2%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D%EC%9D%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%95%98%EB%8A%94%EA%B0%80-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%B6%94%EA%B0%80</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%9D%BC%EB%B2%A8%EB%9F%AC%EB%A1%9C-%EC%9E%90%EB%8F%99-%EB%9D%BC%EB%B2%A8%EB%A7%81%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%88%84%EC%88%98-%EC%B0%A8%EB%8B%A8-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로덕션 모델을 라벨러로: 자동 라벨링과 데이터 누수 차단 | RF-DETR 파인튜닝 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/modeltrain-%ED%95%9C-%EC%A4%84-%EB%92%A4-AdamW%EC%99%80-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98-%ED%97%9D%EA%B0%80%EB%A6%AC%EC%95%88-%EB%A7%A4%EC%B9%AD-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;model.train() 한 줄 뒤: AdamW와 손실함수, 헝가리안 매칭 | RF-DETR 파인튜닝 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/ONNX-%EB%B3%80%ED%99%98%EA%B3%BC-FP16-%EB%B6%95%EA%B4%B4-%EB%B0%B0%ED%8F%AC%EC%9D%98-%ED%95%A8%EC%A0%95-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ONNX 변환과 FP16 붕괴, 배포의 함정 | RF-DETR 파인튜닝 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/91%ED%81%B4%EB%9E%98%EC%8A%A4-vs-2%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D%EC%9D%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%95%98%EB%8A%94%EA%B0%80-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%B6%94%EA%B0%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;91클래스 vs 2클래스, 파인튜닝은 정말 나았는가 | RF-DETR 파인튜닝 (추가)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;들어가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bb3L9/dJMcajpbfJx/wwyHmxnJKSmJA162f4kKLK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bb3L9/dJMcajpbfJx/wwyHmxnJKSmJA162f4kKLK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bb3L9/dJMcajpbfJx/wwyHmxnJKSmJA162f4kKLK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBb3L9%2FdJMcajpbfJx%2FwwyHmxnJKSmJA162f4kKLK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;559&quot; data-filename=&quot;들어가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/bZEGWJ/dJMcabkxFSo/ciRPfRLz9Sn9be4EpFOV51/TRAINING_GUIDE.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;TRAINING_GUIDE.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/cpTYiR/dJMcajv34J6/HkcC3YAc3eifxcuXwyK9kk/pipeline_ball_test.py?attach=1&amp;amp;knm=tfile.py&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;pipeline_ball_test.py&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.00MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/0GYxf/dJMcabkxFSp/QjlBnoQxwsaURI5u019kpk/pipeline_ball_test_result.json?attach=1&amp;amp;knm=tfile.json&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;pipeline_ball_test_result.json&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.00MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/bXaZsm/dJMcadCtRw5/km4Erzk7ap9ZJmdeC1ox81/BASELINE_EVAL_91vs2class.md?attach=1&amp;amp;knm=tfile.md&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;BASELINE_EVAL_91vs2class.md&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부에서 데이터를 만들고, 2부에서 파인튜닝을 했으며, 3부에서 모델을 배포했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 별도 편은 한 가지 질문에 답한다. 기존 COCO 91 클래스 모델을 2 클래스로 파인튜닝한 것이 정말 나은 선택이었는가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문은 코드가 아니라 측정 수치로만 답할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 글은 기존 모델과 파인튜닝 모델을 동일한 조건에서 비교한 실측 결과를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교는 두 층위로 이뤄지는데, 실제 테스트 영상의 검출 성능이 메인이고 정적 벤치마크의 mAP가 보조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론을 미리 밝히면, 정적 지표에서는 두 모델이 사실상 동등하지만 실제 영상의 한 카메라에서 결정적 차이가 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문의 코드는 실제 평가 스크립트인 pipeline_ball_test.py에서 발췌했고, 수치는 그 원본 결과와 비교 리포트에서 가져왔다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;정적 mAP가 높은데 왜 다시 측정했는가&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;파인튜닝이 나아졌다는 주장은 무엇으로 증명해야 하는가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;파인튜닝이 나아졌다는 주장은 무엇으로 증명해야 하는가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uNBUX/dJMb99NFTpf/etwsy3RzK5Hf6yMdkARk1K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uNBUX/dJMb99NFTpf/etwsy3RzK5Hf6yMdkARk1K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uNBUX/dJMb99NFTpf/etwsy3RzK5Hf6yMdkARk1K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuNBUX%2FdJMb99NFTpf%2Fetwsy3RzK5Hf6yMdkARk1K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;파인튜닝이 나아졌다는 주장은 무엇으로 증명해야 하는가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;파인튜닝이 더 낫다&quot;는 주장은 근거 없이는 성립하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 낫다는 것은 8round의 실제 사용 조건에서 공을 더 잘 검출한다는 뜻이어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 모델은 COCO 91클래스로 사람과 스포츠 공을 이미 검출할 수 있었으므로, 단순히 검출이 되는지는 기준이 될 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 비교의 목표는 두 모델이 같은 입력을 받았을 때 검출 성능이 실제로 얼마나 다른지를 수치로 드러내는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 편은 그 수치를 실측하고, 파인튜닝이 어떤 조건에서 얼마나 나았는지를 구체적으로 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주장은 인상이 아니라 재현 가능한 측정으로만 뒷받침되어야 한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;정적 벤치마크의 함정: 쉬운 valid set의 착시&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 평가 방식은 검증셋(valid set)에 대한 정적 mAP 측정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 방식만으로는 실사용 성능을 판단하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 검증셋 533장은 선명하고 정지한 공이 다수인, 상대적으로 쉬운 조건이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 쉬운 조건에서는 기존 91 클래스 모델도 이미 높은 점수를 받으므로 두 모델의 차이가 잘 드러나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 뒤에서 보겠지만 정적 mAP에서는 두 모델이 거의 동등하게 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 정적 지표만 보면 파인튜닝이 무의미해 보이는 착시가 생긴다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;두 층위로 나눠 측정한 이유: 실영상과 정적 벤치마크&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;두 층위로 나눠 측정한 이유- 실영상과 정적 벤치마크.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck2msM/dJMcafHeFQ0/Z3IB83tBS6kdixZEAplt10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck2msM/dJMcafHeFQ0/Z3IB83tBS6kdixZEAplt10/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck2msM/dJMcafHeFQ0/Z3IB83tBS6kdixZEAplt10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck2msM%2FdJMcafHeFQ0%2FZ3IB83tBS6kdixZEAplt10%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;두 층위로 나눠 측정한 이유- 실영상과 정적 벤치마크.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 착시를 피하기 위해 평가를 두 층위로 나눴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 평가는 실제 테스트 영상에 대한 검출 성능으로, 서비스 파이프라인과 동일한 방식으로 측정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보조 평가는 검증셋에 대한 정적 mAP로, 표준 지표와의 정합성을 확인하는 교차검증 용도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 층위를 함께 보는 이유는, 정적 지표가 높아도 실영상에서 무너질 수 있고 그 반대도 가능하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인과 보조를 나란히 놓으면 어느 조건에서 파인튜닝이 효과를 냈는지가 분명해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 정적 mAP 하나로 성능을 단정하는 오류를 막는다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;공정한 비교의 조건: 같은 입력, 같은 후처리, 같은 임계값&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 모델의 비교가 공정하려면 모델 외의 모든 조건이 같아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 입력 전처리는 3부에서 다룬 대로 576 리사이즈와 ImageNet 정규화로 동일하게 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후처리도 동일하게, 모든 클래스 중 최댓값이 person이나 ball일 때만 채택하는 argmax 방식을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검출 채택 임계값(threshold)도 두 모델에 똑같이 0.5로 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 입력과 후처리와 임계값을 고정하면, 결과의 차이는 오직 모델 자체에서 비롯된 것이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 통제하지 않은 비교는 어느 쪽이 나은지를 말해 주지 못한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;실제 영상에서 무엇을 측정했는가&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;평가 대상: 4대 카메라 각 1,804 프레임의 테스트 영상&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;평가 대상- 4대 카메라 각 1,804프레임의 테스트 영상.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BJexR/dJMcagMVIcJ/r2X710dKPyLL8FK55uymO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BJexR/dJMcagMVIcJ/r2X710dKPyLL8FK55uymO1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BJexR/dJMcagMVIcJ/r2X710dKPyLL8FK55uymO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBJexR%2FdJMcagMVIcJ%2Fr2X710dKPyLL8FK55uymO1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;평가 대상- 4대 카메라 각 1,804프레임의 테스트 영상.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 평가의 대상은 실제 8round 세션을 촬영한 테스트 영상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상은 4대 카메라(cam0에서 cam3)로 구성되며, 각 카메라가 1,804 프레임을 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 한 모델당 총 7,216프레임에 대해 검출을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 프레임의 해상도는 1280&amp;times;960이고 초당 30 프레임으로 촬영되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 영상은 정지 이미지가 아니라 움직이는 드리블 장면이라, 공이 작고 빠르게 이동하며 가려지기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이런 실사용 조건이 정적 검증셋과의 결정적 차이를 만든다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;production과 동일한 argmax 후처리로 재현한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 스크립트는 서비스 검출기(detector.py)의 추론 로직을 그대로 복제한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783050354730&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def infer_frame(sess, img, person_ch, ball_ch):
    &quot;&quot;&quot;production infer(): argmax 후 person/ball 채널만 채택.&quot;&quot;&quot;
    dets, labels = sess.run(None, {&quot;input&quot;: pre(img)})
    scores_all = 1.0 / (1.0 + np.exp(-np.clip(labels[0], -500, 500)))
    cls = scores_all.argmax(axis=1)          # 모든 클래스 중 최댓값 채널
    mx = scores_all.max(axis=1)
    mask = mx &amp;gt;= THR                          # THR = 0.5
    cls, mx = cls[mask], mx[mask]
    ball_scores = mx[cls == ball_ch]          # ball 채널로 분류된 검출만
    person_scores = mx[cls == person_ch]
    return person_scores, ball_scores&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 argmax는 각 쿼리에서 모든 클래스 중 점수가 가장 높은 채널을 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 최댓값 채널이 person이나 ball일 때만 검출로 채택하는 것이 서비스와 동일한 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식이 중요한 이유는, 기존 91클래스 모델에서는 공이 다른 90개 클래스와 경쟁해야 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 공의 점수가 다른 클래스보다 낮으면 argmax에서 탈락하며, 이 지점이 뒤에서 드러날 약점의 원인이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;무엇을 셀 것인가: 공 검출률과 신뢰도 마진&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;무엇을 셀 것인가- 공 검출률과 신뢰도 마진.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpS9lW/dJMcagMVIdF/yKpjeGVa5b1e1PnfRpkWfk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpS9lW/dJMcagMVIdF/yKpjeGVa5b1e1PnfRpkWfk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpS9lW/dJMcagMVIdF/yKpjeGVa5b1e1PnfRpkWfk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpS9lW%2FdJMcagMVIdF%2FyKpjeGVa5b1e1PnfRpkWfk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;무엇을 셀 것인가- 공 검출률과 신뢰도 마진.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 평가의 핵심 지표는 프레임 단위 공 검출률이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공 검출률은 공이 하나 이상 검출된 프레임의 비율로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 더해 공의 평균 신뢰도(confidence, 검출에 대한 모델의 확신 정도)와 중앙값을 함께 측정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 서비스의 공 신뢰도 필터 기준인 0.7 이상으로 검출된 프레임의 비율도 센다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 0.7 통과율이 중요한 이유는, 삼각측량(triangulation, 여러 카메라로 3차원 위치를 복원하는 계산)에 투입되는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효한 공 관측치를 결정하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람 검출률도 함께 측정해 두 모델의 사람 검출이 문제가 없는지 확인한다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;두 모델의 클래스 채널을 맞춘다: 91 클래스와 2 클래스&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 모델은 출력 채널 구조가 다르므로 person과 ball의 채널 번호를 각각 지정해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783050380340&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기존 COCO 91-class 모델: labels [1, 300, 91]
eval_model(&quot;coco91&quot;, ..., person_ch=1, ball_ch=37)   # sports ball = 37
# 파인튜닝 2-class 모델: labels [1, 300, 3]
eval_model(&quot;2class&quot;, ..., person_ch=0, ball_ch=1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 모델은 91 클래스 체계라 person이 채널 1, sports ball이 채널 37에 위치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파인튜닝 모델은 2 클래스 체계라 person이 채널 0, ball이 채널 1에 위치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 채널 매핑을 정확히 맞춰야 두 모델의 같은 대상을 같은 기준으로 비교할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널을 잘못 지정하면 엉뚱한 클래스의 점수를 공으로 착각해 비교 자체가 무의미해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 매핑은 3부에서 다룬 클래스 ID 정렬 문제와 같은 맥락에 있다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;결과: cam3의 움직이는 공이 갈렸다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;전체 공 검출률: 0.937에서 0.995로&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 7,216 프레임에 대한 공 검출률은 기존 모델이 0.9371, 파인튜닝 모델이 0.9953이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 약 5.8퍼센트 포인트로, 파인튜닝 모델이 공을 더 자주 검출했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수치만 보면 두 모델 모두 0.9를 넘어 큰 차이가 아닌 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 전체 평균은 카메라별 편차를 가리고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cam0과 cam2에서는 두 모델 모두 검출률이 1.000으로 완벽했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 전체 평균의 차이는 특정 카메라에서 집중적으로 발생했으며, 그것이 다음 항목의 핵심이다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;카메라&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;기존 91 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;파인튜닝 2 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;개선&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;cam0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;1.000&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;1.000&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;동일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;cam1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;0.999&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;1.000&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;+0.1%p&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;cam2&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;1.000&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;1.000&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;동일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;cam3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;0.749&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;0.981&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;+23.2%p&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;전체&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;0.937&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;0.995&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;+5.8%p&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;cam3의 반전: 0.749에서 0.981로&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 차이의 대부분은 cam3 한 카메라에서 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 모델의 cam3 공 검출률은 0.7489로, 네 프레임 중 한 프레임꼴로 공을 놓쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파인튜닝 모델은 같은 cam3에서 검출률 0.9812를 기록해 약 23퍼센트 포인트 상승했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cam3는 공이 작게 보이고 가려지기 쉬운 시점이라, 기존 모델의 약점이 집중된 카메라였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 91 클래스 모델은 argmax 방식에서 움직이는 드리블 공을 다른 클래스에 밀려 놓친 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 파인튜닝의 실질 성과는 가장 약한 카메라인 cam3를 끌어올린 데 있다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;신뢰도 마진의 향상과 유효 관측치 증가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검출률뿐 아니라 신뢰도 마진도 함께 향상되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공 평균 신뢰도는 기존 0.8652에서 파인튜닝 0.9469로 올랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 0.7 필터를 통과한 프레임 비율은 0.9162에서 0.9845로 상승했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 삼각측량에 투입되는 유효한 공 관측치가 그만큼 늘었다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰도가 높아지면 필터 경계에서 탈락하던 검출이 살아남아 3차원 위치 복원의 안정성이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 파인튜닝은 검출 여부뿐 아니라 검출의 품질까지 끌어올렸다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;사람 검출은 처음부터 문제가 아니었다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람 검출률은 두 모델 모두 모든 카메라에서 1.000이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 사람은 애초에 어느 모델에서도 문제가 되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 파인튜닝이 사람 검출을 개선한 것이 아님을 분명히 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 모델의 문제는 사람이 아니라 도메인에 특화된 축구공, 특히 어려운 시점의 움직이는 공이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파인튜닝은 바로 그 특정 약점을 겨냥해 해결한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 작업의 가치는 전방위 개선이 아니라 병목의 정밀한 제거에 있다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;정적 벤치마크와 숫자 너머&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;COCOeval mAP로 교차검증한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실영상 결과를 표준 지표로 뒷받침하기 위해 정적 벤치마크를 함께 측정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정은 검증 셋 533장에 대해 pycocotools의 COCOeval로 수행했으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답 어노테이션은 3,773개(person 1,247개, ball 2,526개)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지표는 mAP(mean Average Precision, 평균 정밀도의 평균)로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IoU(Intersection over Union, 예측 박스와 정답 박스의 겹침 비율) 임계값 0.5부터 0.95까지 0.05 간격의 10개 값을 평균한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 측정은 FP32 ONNX로 진행했는데, 3부에서 확인했듯 FP16은 backbone을 붕괴시켜 모델을 무력화하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 여기서는 정밀도를 FP32로 고정한 채 오직 클래스 구성(91 대 2)만을 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 mAP@.5:.95는 기존 모델이 0.9602, 파인튜닝 모델이 0.9587로 사실상 동등했다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 127px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;지표&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;기존 91 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;파인튜닝 2 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;변화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;mAP@.5:.95&amp;nbsp;(전체)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;0.9602&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;0.9587&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;동등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;mAP@.5&amp;nbsp;(전체)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;0.9843&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;0.9882&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;+0.4%p&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;mAP@.75&amp;nbsp;(전체)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;0.9741&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;0.9870&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 19px;&quot;&gt;+1.3%p&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;person&amp;nbsp;mAP@.5:.95&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;0.9988&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;0.9845&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;&amp;minus;1.4%p&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;ball&amp;nbsp;mAP@.5:.95&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;0.9216&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;0.9330&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;+1.1%p&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;ball&amp;nbsp;mAP@.5&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;0.9687&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;0.9863&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;+1.8%p&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;오검출 감소: 예측 수가 3분의 1로 줄었다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 벤치마크에서 드러난 또 다른 차이는 예측 수의 감소다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 측정 임계값 0.05에서 기존 모델은 15,695개, 파인튜닝 모델은 5,259개를 예측했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 5,259개는 (2026-07-03, ONNX CUDA 실행) 기준이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3부에서 인용한 5,198개는 (2026-05-19, TensorRT FP32 실행) 기준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 값의 차이는 TensorRT와 ONNX CUDA의 FP32 커널 미세 차이로 임계값 0.05 경계의 예측 수만 흔들린 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mAP는 0.9587로 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측 수가 약 3분의 1로 줄었다는 것은 배경을 공으로 오인하는 거짓 양성이 크게 감소했다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오검출이 줄면 후처리 부담이 낮아지고 삼각측량에 잡음이 덜 섞인다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 76px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;실행&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;날짜&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;실행 환경&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;예측 수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;mAP@.5:.95&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;기존 91 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;2026-07-03&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;ONNX&amp;nbsp;CUDA,&amp;nbsp;thr&amp;nbsp;0.05&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;15,695&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;0.9602&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;파인튜닝 2 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;2026-07-03&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;ONNX&amp;nbsp;CUDA,&amp;nbsp;thr&amp;nbsp;0.05&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;5,259&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;0.9587&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;파인튜닝 2 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;2026-05-19&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;TensorRT&amp;nbsp;FP32&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;5,198&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 19px; text-align: center;&quot;&gt;0.9587&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;정적 mAP와 실영상 성능이 어긋난 이유&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 관찰은 정적 mAP와 실영상 성능이 서로 다른 이야기를 한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 벤치마크에서 기존 모델의 공 mAP@.5는 0.9687로 높게 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 같은 모델이 실영상 cam3에서는 공 검출률 0.749로 급락했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 검증 셋이 선명하고 정지한 공 위주(이미지당 평균 4.9개)라 기존 모델에도 유리한 쉬운 조건이었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 실영상의 움직이는 공은 작고 가려지며 다른 클래스와 경쟁해야 하는 어려운 조건이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사례는 정적 mAP만으로는 실사용 성능을 판단할 수 없음을 분명히 보여준다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;파인튜닝의 실질 성과와 남은 한계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종합하면 파인튜닝의 실질 성과는 취약 카메라 cam3의 움직이는 공을 살린 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 공 검출률은 0.937에서 0.995로, cam3는 0.749에서 0.981로 올랐고 신뢰도 마진도 함께 향상되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 벤치마크에서도 공 정확도가 유지되고 오검출이 3분의 2 줄어, 두 층위의 결과가 서로 모순되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 한계도 분명히 해야 한다. 이번 평가의 검증 셋과 테스트 영상은 모두 같은 도메인에서 나왔으므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전혀 다른 환경에 대한 일반화는 별도로 검증해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 참고 자료로 밝히면, backbone을 ConvNeXt로 적은 학습 가이드의 기록은 사실 오류이며 실제 backbone은 DINOv2다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 감안하더라도, 도메인 특화 공 검출이라는 목표에 한정하면 파인튜닝은 명확히 나은 선택이었다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;나가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAyaY2/dJMcagMVIez/pOybGReW60Gz1xQmiYMdO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAyaY2/dJMcagMVIez/pOybGReW60Gz1xQmiYMdO1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAyaY2/dJMcagMVIez/pOybGReW60Gz1xQmiYMdO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAyaY2%2FdJMcagMVIez%2FpOybGReW60Gz1xQmiYMdO1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;나가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 별도 편은 파인튜닝이 정말 나았는지를 실측으로 검증했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 mAP에서는 두 모델이 사실상 동등했지만, 실제 영상에서는 취약 카메라 cam3에서 결정적 차이가 드러났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 교훈은 정적 지표 하나로 모델의 실사용 성능을 단정해서는 안 된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 모델이 쉬운 검증 셋에서는 높은 점수를, 어려운 실영상에서는 낮은 검출률을 보일 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 데이터 구축, 학습, 배포, 그리고 성능 검증으로 이어진 RF-DETR 파인튜닝 시리즈를 마무리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관통하는 교훈은, 모델을 만드는 일만큼이나 그 모델이 실제로 나아졌는지를 정직하게 측정하는 일이 중요하다는 점이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RF-DETR 공식 문서 및 저장소: &lt;a href=&quot;https://rfdetr.roboflow.com&quot;&gt;https://rfdetr.roboflow.com&lt;/a&gt; , &lt;a href=&quot;https://github.com/roboflow/rf-detr&quot;&gt;https://github.com/roboflow/rf-detr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;COCO 평가 지표(mAP, AP50, AP75)와 IoU 임계값 체계: &lt;a href=&quot;https://cocodataset.org/#detection-eval&quot;&gt;https://cocodataset.org/#detection-eval&lt;/a&gt; , &lt;a href=&quot;https://learnopencv.com/mean-average-precision-map-object-detection-model-evaluation-metric/&quot;&gt;https://learnopencv.com/mean-average-precision-map-object-detection-model-evaluation-metric/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;mAP와 COCOeval 개념 해설: &lt;a href=&quot;https://www.picsellia.com/post/coco-evaluation-metrics-explained&quot;&gt;https://www.picsellia.com/post/coco-evaluation-metrics-explained&lt;/a&gt; , &lt;a href=&quot;https://www.ridgerun.ai/post/mean-average-precision-map-and-other-object-detection-metrics&quot;&gt;https://www.ridgerun.ai/post/mean-average-precision-map-and-other-object-detection-metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IoU와 정밀도&amp;middot;재현율 개념: &lt;a href=&quot;https://kharshit.github.io/blog/2019/09/20/evaluation-metrics-for-object-detection-and-segmentation&quot;&gt;https://kharshit.github.io/blog/2019/09/20/evaluation-metrics-for-object-detection-and-segmentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;pycocotools 저장소: &lt;a href=&quot;https://github.com/cocodataset/cocoapi&quot;&gt;https://github.com/cocodataset/cocoapi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;COCO 클래스 인덱스(sports ball 등): &lt;a href=&quot;https://docs.cvat.ai/docs/dataset_management/formats/format-coco/&quot;&gt;https://docs.cvat.ai/docs/dataset_management/formats/format-coco/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(측정값 주석) 전체 mAP@.5:.95의 원본 raw 값은 0.9587604이며, 본문은 0.9587로 통일했다. 4자리 반올림 시 0.9588이며, 3부는 학습 가이드 기준의 0.9588을 인용했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>COCOeval</category>
      <category>Map</category>
      <category>RFDETR</category>
      <category>객체검출</category>
      <category>검출률</category>
      <category>모델평가</category>
      <category>벤치마크</category>
      <category>성능비교</category>
      <category>컴퓨터비전</category>
      <category>파인튜닝</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1351</guid>
      <comments>https://gnidinger.tistory.com/entry/91%ED%81%B4%EB%9E%98%EC%8A%A4-vs-2%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D%EC%9D%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%95%98%EB%8A%94%EA%B0%80-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%B6%94%EA%B0%80#entry1351comment</comments>
      <pubDate>Fri, 3 Jul 2026 12:59:08 +0900</pubDate>
    </item>
    <item>
      <title>ONNX 변환과 FP16 붕괴, 배포의 함정 | RF-DETR 파인튜닝 (3)</title>
      <link>https://gnidinger.tistory.com/entry/ONNX-%EB%B3%80%ED%99%98%EA%B3%BC-FP16-%EB%B6%95%EA%B4%B4-%EB%B0%B0%ED%8F%AC%EC%9D%98-%ED%95%A8%EC%A0%95-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-3</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%9D%BC%EB%B2%A8%EB%9F%AC%EB%A1%9C-%EC%9E%90%EB%8F%99-%EB%9D%BC%EB%B2%A8%EB%A7%81%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%88%84%EC%88%98-%EC%B0%A8%EB%8B%A8-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로덕션 모델을 라벨러로: 자동 라벨링과 데이터 누수 차단 | RF-DETR 파인튜닝 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/modeltrain-%ED%95%9C-%EC%A4%84-%EB%92%A4-AdamW%EC%99%80-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98-%ED%97%9D%EA%B0%80%EB%A6%AC%EC%95%88-%EB%A7%A4%EC%B9%AD-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;model.train() 한 줄 뒤: AdamW와 손실함수, 헝가리안 매칭 | RF-DETR 파인튜닝 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/ONNX-%EB%B3%80%ED%99%98%EA%B3%BC-FP16-%EB%B6%95%EA%B4%B4-%EB%B0%B0%ED%8F%AC%EC%9D%98-%ED%95%A8%EC%A0%95-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ONNX 변환과 FP16 붕괴, 배포의 함정 | RF-DETR 파인튜닝 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/91%ED%81%B4%EB%9E%98%EC%8A%A4-vs-2%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D%EC%9D%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%95%98%EB%8A%94%EA%B0%80-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%B6%94%EA%B0%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;91클래스 vs 2클래스, 파인튜닝은 정말 나았는가 | RF-DETR 파인튜닝 (추가)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/dA2za7/dJMcahZgamE/1Icktc7gXtU8399kZpqks1/export_onnx.py?attach=1&amp;amp;knm=tfile.py&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;export_onnx.py&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/JU34L/dJMcabEKb7x/kixLui7mirqC1iOErg0ugk/detector.py?attach=1&amp;amp;knm=tfile.py&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;detector.py&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;들어가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cr2IA/dJMcag0kedm/vvd3Abrv67k5SAbbsd0CjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cr2IA/dJMcag0kedm/vvd3Abrv67k5SAbbsd0CjK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cr2IA/dJMcag0kedm/vvd3Abrv67k5SAbbsd0CjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCr2IA%2FdJMcag0kedm%2Fvvd3Abrv67k5SAbbsd0CjK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;들어가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부에서는 데이터셋을 만들었고, 2부에서는 그 데이터로 RF-DETR(Roboflow Detection Transformer) 모델을 파인튜닝했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 학습이 끝난 모델을 실제 서비스에 배포하는 과정을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습은 끝이 아니며, 모델을 내보내고 배포하는 단계에는 고유한 함정이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 환경과 서비스 환경이 서로 다르기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 잇는 표준 형식인 ONNX(Open Neural Network Exchange, 개방형 신경망 교환 형식)가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 ONNX 변환, 클래스 채널의 함정, 전처리 일치, FP16 붕괴의 네 단계로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 채널 하나의 어긋남이나 정밀도 한 끗의 선택이 잘 학습된 모델을 무력화할 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문의 코드는 실제 export_onnx.py와 서비스 추론 코드인 detector.py에서 발췌했다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;학습이 끝나도 배포는 끝나지 않는다: ONNX 변환&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;학습 환경과 서비스 환경이 다르다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;학습 환경과 서비스 환경이 다르다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ETRcC/dJMcabrfxRz/CuFSVi1Na4xonF11qTLbRK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ETRcC/dJMcabrfxRz/CuFSVi1Na4xonF11qTLbRK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ETRcC/dJMcabrfxRz/CuFSVi1Na4xonF11qTLbRK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FETRcC%2FdJMcabrfxRz%2FCuFSVi1Na4xonF11qTLbRK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;학습 환경과 서비스 환경이 다르다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습은 rfdetr 라이브러리가 요구하는 최신 파이토치(PyTorch) 환경에서 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 서비스 추론 환경은 그보다 낮은 파이토치 버전을 쓰며, 함부로 올릴 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 학습으로 얻은 체크포인트(.pth) 파일을 서비스 환경에서 그대로 불러오기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 서비스의 detector.py는 rfdetr 패키지에 의존하지 않고 onnxruntime(ONNX 추론 엔진)만으로 동작하도록 설계되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 학습한 모델은 서비스 환경이 실행할 수 있는 이식 가능한 형식으로 변환되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 변환의 다리 역할을 하는 것이 ONNX다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;ONNX란 무엇인가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ONNX란 무엇인가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGv9UQ/dJMcafN0Kuk/osX52KmzL2UuLvLNgKLKkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGv9UQ/dJMcafN0Kuk/osX52KmzL2UuLvLNgKLKkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGv9UQ/dJMcafN0Kuk/osX52KmzL2UuLvLNgKLKkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGv9UQ%2FdJMcafN0Kuk%2FosX52KmzL2UuLvLNgKLKkk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;ONNX란 무엇인가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONNX는 학습 프레임워크에 종속되지 않는 방식으로 모델을 표현하는 개방형 표준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2017년 페이스북과 마이크로소프트의 협업으로 공개되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONNX는 모델을 계산 그래프(computation graph, 연산은 노드, 텐서는 간선으로 표현)로 담으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 루프나 그래디언트 없이 순수한 수치 가중치만 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 덕분에 모델은 학습 프레임워크와 무관하게 저장하고 실행할 수 있는 독립적인 산출물이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 산출물은 ONNX Runtime이 실행하며, CUDA나 TensorRT 같은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 프로바이더(execution provider, 하드웨어별 실행 백엔드)를 붙일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 최신 환경에서 학습한 모델을 낮은 버전의 서비스에서 ONNX를 통해 실행할 수 있게 된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;model.export와 onnx-simplifier로 그래프를 단순화한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export_onnx.py는 학습된 체크포인트를 불러와 ONNX로 내보낸다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959763268&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from rfdetr import RFDETRMedium

model = RFDETRMedium(
    num_classes=2,
    pretrain_weights=str(args.checkpoint),   # 학습된 best.pth
)
model.export(output_dir=str(EXPORTS_DIR), simplify=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export 호출은 학습된 가중치를 ONNX 계산 그래프로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;simplify=True 옵션은 onnx-simplifier(ONNX 그래프 단순화 도구)를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onnx-simplifier는 전체 계산 그래프를 추론해 중복 연산자를 그 상수 출력으로 대체하는데, 이를 상수 폴딩(constant folding)이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정은 그래프를 더 작고 깨끗하게 만들어 추론 속도를 높인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;산출물은 inference_model.onnx 파일로 저장된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;내보낸 모델은 반드시 검증한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내보낸 ONNX가 올바른지 확인하지 않고 배포하면 조용한 오류로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;verify_onnx 함수는 모델의 메타데이터와 입출력 형태를 먼저 점검한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959781180&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model = onnx.load(str(onnx_path))
logger.info(f&quot;ONNX IR version: {model.ir_version}&quot;)
for out in model.graph.output:
    dims = [d.dim_value for d in out.type.tensor_type.shape.dim]
    logger.info(f&quot;Output: {out.name} {dims}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어 그래프에 저장된 가중치(initializer)에서 총 파라미터 수를 세어 모델 규모를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 샘플 이미지로 실제 추론을 돌려 끝에서 끝까지 정상 동작하는지 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 가장 중요한 검사는 출력 채널 수를 확인하는 것으로, 이는 다음 섹션에서 다룬다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span data-newtext-seq=&quot;4&quot;&gt;서비스 코드는 &lt;/span&gt;&lt;span data-newtext-seq=&quot;17&quot;&gt;rfdetr에 의존하지 않는다&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;서비스 코드는 rfdetr에 의존하지 않는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deAlTz/dJMcaasmxT0/UeLhAGCdRn1MhUA3Cft4p0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deAlTz/dJMcaasmxT0/UeLhAGCdRn1MhUA3Cft4p0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deAlTz/dJMcaasmxT0/UeLhAGCdRn1MhUA3Cft4p0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeAlTz%2FdJMcaasmxT0%2FUeLhAGCdRn1MhUA3Cft4p0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;서비스 코드는 rfdetr에 의존하지 않는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포의 목표는 학습 라이브러리 없이도 도는 가벼운 추론기다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;detector.py의 RFDETROnnxDetector는 rfdetr 패키지를 전혀 import 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 onnxruntime과 OpenCV(영상 처리 라이브러리), NumPy(수치 연산 라이브러리)만으로 추론을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 분리 덕분에 서비스 환경은 학습용 의존성의 버전 문제에서 자유롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론기는 ONNX 파일 경로와 임계값만 받아 초기화되고, 단일 프레임을 받아 검출 결과를 돌려주는 단순한 인터페이스를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 여러 요청이 동시에 들어와도 안전하도록 추론 구간을 잠금(lock)으로 보호한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;채널 하나가 어긋나면 클래스가 밀린다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;내보낸 모델은 무엇을 출력하는가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONNX 모델은 하나의 입력과 두 개의 출력을 가진다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959806488&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dets, labels = session.run(None, {&quot;input&quot;: tensor})
logger.info(f&quot;dets shape: {dets.shape}&quot;)     # [1, 300, 4]
logger.info(f&quot;labels shape: {labels.shape}&quot;) # [1, 300, 3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dets는 [1, 300, 4] 형태로, 300개의 박스를 정규화된 cxcywh(중심 x, 중심 y, 너비, 높이) 좌표로 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;labels는 [1, 300, 3] 형태로, 300개 쿼리 각각에 대한 원시 로짓(raw logit, 확률로 변환되기 전의 점수)을 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 300은 2부에서 설명한 쿼리(query, 검출 후보 슬롯) 수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스는 0에서 1로 정규화된 값이고 로짓은 아직 확률이 아니므로, 뒤에서 sigmoid를 거쳐 점수로 바뀐다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;헤드는 클래스 수보다 하나 많은 채널을 낸다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 num_classes=2로 학습했지만 labels의 채널 수는 3이다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959822150&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;labels_output = model.graph.output[1]
labels_dims = [d.dim_value for d in labels_output.type.tensor_type.shape.dim]
expected_classes = 3   # person + ball + background
if labels_dims[-1] != expected_classes:
    logger.error(f&quot;labels 출력 클래스 수 불일치: {labels_dims[-1]} (기대값: 3)&quot;)
    return False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rfdetr이 1부터 시작하는(1-indexed) COCO 데이터셋 관례에 따라 헤드를 num_classes+1 채널로 초기화하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 채널은 ch0이 person, ch1이 ball, ch2가 background(no-object, 객체 없음)로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 채널이 2개라고 가정하면 클래스 인덱스가 밀려 person과 ball의 해석이 어긋난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 verify_onnx는 labels의 마지막 차원이 3인지 명시적으로 검사해 이 함정을 사전에 잡는다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;추론 시 background 채널을 argmax에서 걸러낸다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론에서는 먼저 로짓에 sigmoid를 적용해 채널별 점수를 얻는다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959839959&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scores_all = 1.0 / (1.0 + np.exp(-np.clip(logits, -500, 500)))
class_ids = scores_all.argmax(axis=1)
max_scores = scores_all.max(axis=1)
valid_class_mask = class_ids != 2          # background(ch=2) 제외
mask = (max_scores &amp;gt;= 0.5) &amp;amp; valid_class_mask&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argmax(최댓값의 위치를 찾는 연산)로 각 쿼리의 예측 클래스를 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 ch2인 background로 분류된 쿼리는 반드시 제외해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 person(0)이나 ball(1)이면서 점수가 임계값 이상인 검출만 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;background를 걸러내지 않으면 객체가 없는 자리까지 검출로 잡혀 거짓 양성이 쏟아진다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span data-newtext-seq=&quot;4&quot;&gt;모델의 클래스 &lt;/span&gt;&lt;span data-newtext-seq=&quot;17&quot;&gt;ID를 다운스트림 코드에 맞게 다시 매핑한다&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 내는 내부 ID(person=0, ball=1)는 그 출력을 소비하는 후속 코드가 기대하는 ID와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 후속 코드란 검출 결과를 받아 처리하는 부분을 가리키며, detector.py 주석에서는 이를 다운스트림(downstream)이라 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;detector.py는 이 다운스트림 코드와의 호환을 위해 COCO 80-class 기준의 ID(person=0, ball=32)로 다시 매핑한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959854513&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class RFDETROnnxDetector:
    person_id = 0
    ball_id = 32   # 하류 코드 호환 ID (COCO 80-class 기준)
    # __init__ 인자: person_class_id=0, ball_class_id=1 (모델 내부 ID)

    def infer(self, img):
        # ... sigmoid, argmax, threshold 후 ...
        mapped = np.full(len(filtered_classes), -1, dtype=np.float32)
        mapped[filtered_classes == self._model_person_id] = self.person_id  # 0 &amp;rarr; 0
        mapped[filtered_classes == self._model_ball_id] = self.ball_id      # 1 &amp;rarr; 32&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 모델의 ball(1)을 다운스트림 코드가 쓰는 COCO 80-class의 ball(32)로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 다운스트림 코드가 COCO 80-class 인덱스를 전제로 작성되었기 때문에 이 매핑이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 재매핑을 빠뜨리면 ball 검출이 다운스트림에서 엉뚱한 클래스로 읽힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 출력 ID와 다운스트림의 소비 ID라는 두 층의 존재가 이 단계의 핵심 함정이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;전처리가 다르면 추론이 무너진다&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;학습과 추론의 전처리는 반드시 같아야 한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;학습과 추론의 전처리는 반드시 같아야 한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdxvDe/dJMcaasmxT8/9uWwFitDOA96sRfQhXRK1K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdxvDe/dJMcaasmxT8/9uWwFitDOA96sRfQhXRK1K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdxvDe/dJMcaasmxT8/9uWwFitDOA96sRfQhXRK1K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdxvDe%2FdJMcaasmxT8%2F9uWwFitDOA96sRfQhXRK1K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;학습과 추론의 전처리는 반드시 같아야 한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 특정 방식으로 전처리된 입력으로 학습되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론에서 전처리가 달라지면 입력 분포가 어긋나 예측 품질이 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 사전학습 모델은 학습 때와 다른 정규화를 쓰면 정확도가 조용히 10~30% 떨어지는 것으로 보고된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 detector.py의 전처리는 학습 때와 정확히 같아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export_onnx.py의 검증 코드도 이 점을 의식해 detector.py와 동일한 전처리를 의도적으로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리는 BGR을 RGB로 변환, 576 리사이즈, ImageNet 정규화, 채널 우선(CHW) 배열의 네 단계로 이뤄진다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;576 리사이즈와 ImageNet 정규화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리 전체는 detector.py의 _preprocess에 담겨 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959875548&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32).reshape(1, 1, 3)
_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32).reshape(1, 1, 3)

def _preprocess(self, img):
    orig_h, orig_w = img.shape[:2]
    rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)              # BGR &amp;rarr; RGB
    resized = cv2.resize(rgb, (self.input_size, self.input_size))  # 576&amp;times;576
    normalized = (resized.astype(np.float32) / 255.0 - self._MEAN) / self._STD
    tensor = normalized.transpose(2, 0, 1)[np.newaxis]     # HWC &amp;rarr; CHW, 배치 축 추가
    return tensor, orig_w, orig_h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 입력이 576&amp;times;576이므로 모든 프레임을 그 크기로 리사이즈한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV는 이미지를 BGR 순서로 읽으므로, 모델이 기대하는 RGB 순서로 채널을 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;픽셀을 0에서 1 범위로 나눈 뒤 ImageNet 평균과 표준편차로 정규화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 mean [0.485, 0.456, 0.406]과 std [0.229, 0.224, 0.225]는 백본인 DINOv2가 사전학습에 사용한 표준값이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 값을 쓰면 사전학습 백본과 어긋난다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;sigmoid 오버플로를 clip으로 막는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sigmoid는 1/(1+exp(-x)) 형태로, 입력이 크게 음수이면 exp 항이 매우 커진다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959891788&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;logits_clipped = np.clip(logits, -500, 500)
scores_all = 1.0 / (1.0 + np.exp(-logits_clipped))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;np.exp에 아주 큰 수가 들어가면 결과가 무한대(inf)로 넘쳐 오버플로가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 로짓을 -500에서 500 사이로 잘라(clip) 지수의 크기를 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sigmoid는 어차피 양 끝에서 포화하므로, 이 클리핑은 수학적 결과를 바꾸지 않는 수치 안전장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 장치가 없으면 극단적인 로짓이 무한대(inf)나 숫자가 아닌 값(NaN, Not a Number) 점수를 만들어 이후 계산을 오염시킬 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;cxcywh 박스를 xyxy로 바꾸고 원본 크기로 되돌린다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 박스를 정규화된 cxcywh(중심 좌표와 크기, 0에서 1)로 출력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959909385&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@staticmethod
def _cxcywh_to_xyxy(boxes):
    cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
    return np.stack([cx - w/2, cy - h/2, cx + w/2, cy + h/2], axis=1)

# infer() 내부
boxes_xyxy = self._cxcywh_to_xyxy(filtered_boxes)
boxes_xyxy[:, [0, 2]] *= orig_w   # x 좌표를 원본 너비로
boxes_xyxy[:, [1, 3]] *= orig_h   # y 좌표를 원본 높이로&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소비 코드는 보통 픽셀 단위의 xyxy(좌상단과 우하단 모서리) 좌표를 원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환식은 x1=cx-w/2, y1=cy-h/2, x2=cx+w/2, y2=cy+h/2로 중심과 크기를 모서리로 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 정규화 좌표에 원본 너비와 높이를 곱해 576 공간에서 원본 프레임 좌표로 되돌린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 크기로 되돌리는 이 과정을 빠뜨리면 박스가 엉뚱한 좌표 공간에 놓여 화면과 어긋난다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;FP16은 백본을 붕괴시킨다: 정밀도의 함정&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;FP16과 FP32란 무엇인가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;FP16와 FP32란 무엇인가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csjK6q/dJMcadP3v1B/GHrKRvbwFh2hNWIyVWZKT1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csjK6q/dJMcadP3v1B/GHrKRvbwFh2hNWIyVWZKT1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csjK6q/dJMcadP3v1B/GHrKRvbwFh2hNWIyVWZKT1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsjK6q%2FdJMcadP3v1B%2FGHrKRvbwFh2hNWIyVWZKT1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;FP16와 FP32란 무엇인가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP32(단정밀도, 32비트 부동소수점, 32-bit Floating Point)는 지수 8비트를 써서 약 7자리의 십진 정밀도를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP16(반정밀도, 16비트 부동소수점, 16-bit&amp;nbsp;Floating&amp;nbsp;Point)은 지수 5비트와 가수 10비트로 이뤄진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP16은 메모리를 절반으로 줄이고 텐서 코어(Tensor Core)에서 연산을 빠르게 하므로 추론에서 매력적으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 FP16의 최댓값은 65,504이고, 약 6e-5보다 작은 값은 0으로 사라진다(언더플로).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 FP16은 FP32보다 표현 가능한 값의 범위(dynamic range)가 훨씬 좁다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 좁은 범위가 바로 문제의 근원이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;실측 데이터: FP16에서 모델이 무력화된다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;실측 데이터- FP16에서 모델이 무력화된다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SChxx/dJMcahZgaAf/7hA0T5P70Q2WQwxUCXa0kk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SChxx/dJMcahZgaAf/7hA0T5P70Q2WQwxUCXa0kk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SChxx/dJMcahZgaAf/7hA0T5P70Q2WQwxUCXa0kk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSChxx%2FdJMcahZgaAf%2F7hA0T5P70Q2WQwxUCXa0kk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;실측 데이터- FP16에서 모델이 무력화된다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 모델을 두 정밀도로 추론해 결과를 비교했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP32에서는 mAP@.5:.95가 0.9588이고 검출이 5,198개였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP16에서는 mAP@.5:.95가 0.0042로 떨어지고 검출이 42개에 그쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 mAP(mean Average Precision, 평균 정밀도의 평균)는 검출 정확도를 종합한 지표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 FP16은 성능을 조금 떨어뜨리는 수준이 아니라 모델을 사실상 무력화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 미세한 정확도 하락이 아니라 치명적 실패다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;원인은 백본 LayerNorm의 수치 불안정이다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;원인은 백본 LayerNorm의 수치 불안정이다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzkaSs/dJMcadP3v1W/mfs7zzi6MazIihbTSEOuqK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzkaSs/dJMcadP3v1W/mfs7zzi6MazIihbTSEOuqK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzkaSs/dJMcadP3v1W/mfs7zzi6MazIihbTSEOuqK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzkaSs%2FdJMcadP3v1W%2Fmfs7zzi6MazIihbTSEOuqK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;원인은 백본 LayerNorm의 수치 불안정이다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 백본인 DINOv2의 LayerNorm(레이어 정규화)에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LayerNorm과 어텐션(attention)은 FP16에서 수치적으로 취약한 대표적 연산으로 알려져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊은 층에서 활성값이 크게 자라면 LayerNorm의 분산 계산이 FP16 최댓값을 넘어 무한대가 되고, 이것이 NaN으로 전파된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징 추출 단계에서 NaN이 생기면 이후 검출 전체가 무너진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 재학습으로 고칠 수 없는 아키텍처 수준의 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 모델은 FP16으로는 정상적으로 실행될 수 없다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;FP32로 배포하고 성능은 충분하다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 배포는 FP32로만 진행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782959944784&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trt_cache_dir = str(Path(onnx_path).parent / &quot;trt_cache_fp32&quot;)
providers = [
    (&quot;TensorrtExecutionProvider&quot;, {
        &quot;device_id&quot;: 0,
        &quot;trt_fp16_enable&quot;: False,            # FP16 비활성화
        &quot;trt_engine_cache_enable&quot;: True,
        &quot;trt_engine_cache_path&quot;: trt_cache_dir,
    }),
    (&quot;CUDAExecutionProvider&quot;, {&quot;device_id&quot;: 0}),   # TensorRT 미지원 시 fallback
]
self.session = ort.InferenceSession(str(onnx_path), providers=providers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorRT(NVIDIA의 추론 최적화 엔진)를 우선 실행 프로바이더로 쓰고, 지원되지 않는 연산은 CUDA로 넘긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;trt_fp16_enable을 False로 두고 엔진 캐시도 FP32 전용 디렉토리에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP32는 A2000에서 프레임당 약 26밀리 초(초당 약 38 프레임)로 동작해 서비스에 충분한 속도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 굳이 필요하지도 않은 속도 이득을 위해 FP16의 붕괴 위험을 감수할 이유가 없다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;나가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sCPiO/dJMcai4TgpR/I2cVogLtuWrC467txLHaG1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sCPiO/dJMcai4TgpR/I2cVogLtuWrC467txLHaG1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sCPiO/dJMcai4TgpR/I2cVogLtuWrC467txLHaG1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsCPiO%2FdJMcai4TgpR%2FI2cVogLtuWrC467txLHaG1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;나가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서는 모델을 ONNX로 내보내고 검증했으며, num_classes+1 채널과 클래스 ID 재매핑을 처리하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리를 학습과 일치시키고, FP16을 배제했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 항목은 작은 부주의 하나가 배포를 조용히 망가뜨릴 수 있는 지점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널의 어긋남과 클래스 재매핑은 잘 학습된 모델도 인터페이스에서 실패할 수 있음을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP16 붕괴는 정밀도 선택 하나가 모델을 통째로 무력화할 수 있음을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 데이터, 학습, 배포로 이어진 세 편의 시리즈를 마무리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관통하는 교훈은 신뢰할 수 있는 머신러닝이 모델 자체만큼이나 겉보기에 사소한 인터페이스에 달려 있다는 점이다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RF-DETR 공식 문서 및 저장소: &lt;a href=&quot;https://rfdetr.roboflow.com&quot;&gt;https://rfdetr.roboflow.com&lt;/a&gt; , &lt;a href=&quot;https://github.com/roboflow/rf-detr&quot;&gt;https://github.com/roboflow/rf-detr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ONNX 공식 사이트 및 개념 해설: &lt;a href=&quot;https://onnx.ai&quot;&gt;https://onnx.ai&lt;/a&gt; , &lt;a href=&quot;https://www.ultralytics.com/glossary/onnx-open-neural-network-exchange&quot;&gt;https://www.ultralytics.com/glossary/onnx-open-neural-network-exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ONNX 상호운용성 개요: &lt;a href=&quot;https://www.splunk.com/en_us/blog/learn/open-neural-network-exchange-onnx.html&quot;&gt;https://www.splunk.com/en_us/blog/learn/open-neural-network-exchange-onnx.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;onnx-simplifier 저장소(상수 폴딩): &lt;a href=&quot;https://github.com/daquexian/onnx-simplifier&quot;&gt;https://github.com/daquexian/onnx-simplifier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ONNX Runtime 그래프 최적화 및 TensorRT 실행 프로바이더: &lt;a href=&quot;https://onnxruntime.ai/docs/performance/model-optimizations/graph-optimizations.html&quot;&gt;https://onnxruntime.ai/docs/performance/model-optimizations/graph-optimizations.html&lt;/a&gt; , &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/TensorRT-ExecutionProvider.html&quot;&gt;https://onnxruntime.ai/docs/execution-providers/TensorRT-ExecutionProvider.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;FP16 정밀도와 오버플로&amp;middot;언더플로 개념: &lt;a href=&quot;https://www.hivenet.com/post/fp16-explained-16-bit-floating-point-precision-in-ai&quot;&gt;https://www.hivenet.com/post/fp16-explained-16-bit-floating-point-precision-in-ai&lt;/a&gt; , &lt;a href=&quot;https://www.emmtrix.com/wiki/Numerical_Precision_in_ONNX_and_AI_Inference&quot;&gt;https://www.emmtrix.com/wiki/Numerical_Precision_in_ONNX_and_AI_Inference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;TensorRT 정밀도 고려사항(FP16 오버플로와 NaN): &lt;a href=&quot;https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/accuracy-considerations.html&quot;&gt;https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/accuracy-considerations.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LayerNorm과 어텐션의 FP16 불안정 사례(CogView): &lt;a href=&quot;https://arxiv.org/pdf/2105.13290&quot;&gt;https://arxiv.org/pdf/2105.13290&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ImageNet 정규화 상수와 전처리 일치 원칙: &lt;a href=&quot;https://www.systemoverflow.com/learn/ml-cv-systems/image-preprocessing/normalization-and-input-standardization&quot;&gt;https://www.systemoverflow.com/learn/ml-cv-systems/image-preprocessing/normalization-and-input-standardization&lt;/a&gt; , &lt;a href=&quot;https://albumentations.ai/blog/2025/03-the-mystery-of-input-normalization/&quot;&gt;https://albumentations.ai/blog/2025/03-the-mystery-of-input-normalization/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;COCO 어노테이션 포맷과 클래스 인덱스: &lt;a href=&quot;https://docs.cvat.ai/docs/dataset_management/formats/format-coco/&quot;&gt;https://docs.cvat.ai/docs/dataset_management/formats/format-coco/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>fp16</category>
      <category>LayerNorm</category>
      <category>onnx</category>
      <category>onnxsimplifier</category>
      <category>RFDETR</category>
      <category>tensorRT</category>
      <category>객체검출</category>
      <category>추론최적화</category>
      <category>혼합정밀도</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1350</guid>
      <comments>https://gnidinger.tistory.com/entry/ONNX-%EB%B3%80%ED%99%98%EA%B3%BC-FP16-%EB%B6%95%EA%B4%B4-%EB%B0%B0%ED%8F%AC%EC%9D%98-%ED%95%A8%EC%A0%95-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-3#entry1350comment</comments>
      <pubDate>Thu, 2 Jul 2026 11:53:17 +0900</pubDate>
    </item>
    <item>
      <title>model.train() 한 줄 뒤: AdamW와 손실함수, 헝가리안 매칭 | RF-DETR 파인튜닝 (2)</title>
      <link>https://gnidinger.tistory.com/entry/modeltrain-%ED%95%9C-%EC%A4%84-%EB%92%A4-AdamW%EC%99%80-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98-%ED%97%9D%EA%B0%80%EB%A6%AC%EC%95%88-%EB%A7%A4%EC%B9%AD-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-2</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;ul id=&quot;toc&quot; style=&quot;list-style-type: none;&quot; data-ke-list-type=&quot;none&quot;&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;txc-textbox&quot; style=&quot;background-color: #eeeeee; padding: 10px; border-radius: 10px;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%9D%BC%EB%B2%A8%EB%9F%AC%EB%A1%9C-%EC%9E%90%EB%8F%99-%EB%9D%BC%EB%B2%A8%EB%A7%81%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%88%84%EC%88%98-%EC%B0%A8%EB%8B%A8-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로덕션 모델을 라벨러로: 자동 라벨링과 데이터 누수 차단 | RF-DETR 파인튜닝 (1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/modeltrain-%ED%95%9C-%EC%A4%84-%EB%92%A4-AdamW%EC%99%80-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98-%ED%97%9D%EA%B0%80%EB%A6%AC%EC%95%88-%EB%A7%A4%EC%B9%AD-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;model.train() 한 줄 뒤: AdamW와 손실함수, 헝가리안 매칭 | RF-DETR 파인튜닝 (2)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/ONNX-%EB%B3%80%ED%99%98%EA%B3%BC-FP16-%EB%B6%95%EA%B4%B4-%EB%B0%B0%ED%8F%AC%EC%9D%98-%ED%95%A8%EC%A0%95-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ONNX 변환과 FP16 붕괴, 배포의 함정 | RF-DETR 파인튜닝 (3)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gnidinger.tistory.com/entry/91%ED%81%B4%EB%9E%98%EC%8A%A4-vs-2%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D%EC%9D%80-%EC%A0%95%EB%A7%90-%EB%82%98%EC%95%98%EB%8A%94%EA%B0%80-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%B6%94%EA%B0%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;91클래스 vs 2클래스, 파인튜닝은 정말 나았는가 | RF-DETR 파인튜닝 (추가)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;들어가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgvtVm/dJMcaglJ6ig/CsTOkLKOMApAlQwEBtKS4K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgvtVm/dJMcaglJ6ig/CsTOkLKOMApAlQwEBtKS4K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgvtVm/dJMcaglJ6ig/CsTOkLKOMApAlQwEBtKS4K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgvtVm%2FdJMcaglJ6ig%2FCsTOkLKOMApAlQwEBtKS4K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;들어가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;들어가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부에서는 프로덕션 모델을 자동 라벨러로 재활용하고 선수 단위로 분할해 누수 없는 학습 데이터셋을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 그 데이터로 실제 학습이 어떻게 진행되는지를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 train.py의 핵심은 사실상 두 줄로, 모델을 생성하고 학습을 호출하는 것이 전부다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵티마이저(최적화기)와 손실함수와 매칭 같은 실제 학습 알고리즘은 내 코드가 아니라 rfdetr 라이브러리 내부에 구현되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내&amp;nbsp;코드는 그중 일부 하이퍼파라미터(사람이 정하는 학습 설정값)만 덮어쓰고 나머지는 라이브러리 기본값을 그대로 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 글은 그 한 줄 뒤에서 무엇이 도는지를, 내가 지정한 값과 라이브러리 기본값을 구분해 분해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명의 세 축은 AdamW 옵티마이저와 손실함수, 그리고 헝가리안 매칭(Hungarian matching)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문의 코드 블록은 내 train.py와 실제 rfdetr 소스에서 발췌했으며, 가독성을 위한 한글 주석만 덧붙였다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;model.train() 한 줄 뒤에 무엇이 있는가&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;내&amp;nbsp;코드는 두 줄, 학습 알고리즘은 라이브러리가 맡는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train.py에서 학습의 본체는 아래 두 줄로 압축된다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886454706&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from rfdetr import RFDETRMedium

model = RFDETRMedium(num_classes=2)   # person + ball
model.train(**train_kwargs)           # 학습 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 줄은 2 클래스 모델을 만들고, 둘째 줄은 설정을 담은 딕셔너리를 넘겨 학습을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 줄 뒤에서 옵티마이저 생성, 손실 계산, 예측과 정답의 매칭이 전부 라이브러리 내부에서 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 건드리는 것은 train_kwargs에 담긴 소수의 값뿐이고, 손실 가중치나 매칭 비용 같은 나머지는 라이브러리 기본값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 글의 근거 파일은 내 train.py와 함께 라이브러리의 criterion.py(손실), matcher.py(매칭),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;module_model.py(옵티마이저), param_groups.py(학습률 그룹)로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 파인튜닝을 이해한다는 것은 내가 덮어쓴 값과 라이브러리 기본값을 함께 이해한다는 뜻이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;RF-DETR의 뼈대: DINOv2 백본과 DETR 계열 검출기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RF-DETR의 뼈대- DINOv2 백본과 DETR 계열 검출기.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8zjOF/dJMcah56KHI/vvbkkUSO8Ku5DcORH2zyB0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8zjOF/dJMcah56KHI/vvbkkUSO8Ku5DcORH2zyB0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8zjOF/dJMcah56KHI/vvbkkUSO8Ku5DcORH2zyB0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8zjOF%2FdJMcah56KHI%2FvvbkkUSO8Ku5DcORH2zyB0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;RF-DETR의 뼈대- DINOv2 백본과 DETR 계열 검출기.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RF-DETR(Roboflow Detection Transformer)은 Roboflow가 만든 실시간 트랜스포머(Transformer) 기반 객체 검출 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모델은 자기지도학습(self-supervised learning, 사람 라벨 없이 데이터 자체 구조로 사전학습)으로 훈련된&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DINOv2 비전 트랜스포머를 백본(backbone, 특징 추출부)으로 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 위에 LW-DETR과 Deformable DETR의 설계를 결합해,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앵커 박스(anchor box, 미리 정한 후보 상자)와 NMS(비최대 억제) 없이 끝에서 끝까지 검출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DINOv2의 자기지도 사전학습은 새 도메인에 대한 적응력을 주므로, 내처럼 특정 도메인에 파인튜닝하기에 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RF-DETR은 아파치 2.0 라이선스로 공개되었고 ICLR 2026에 채택되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 쓰는 Medium 크기는 입력 해상도 576, 디코더(decoder) 레이어 4개, 쿼리(query, 검출 후보 슬롯) 300개로 설정된 구성이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;num_classes=2가 바꾸는 것: 검출 헤드 재구성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RFDETRMedium(num_classes=2)에서 num_classes=2가 91에서 2로의 전환의 본질이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인자는 모델의 검출 헤드(detection head, 클래스와 박스를 최종 예측하는 층)를 2 클래스에 맞게 재구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백본과 트랜스포머의 사전학습 가중치는 그대로 이어받고, 새 헤드만 내 데이터로 학습한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 전형적인 파인튜닝 구조로, 이미 학습된 특징 추출부는 보존하고 과제별 출력부만 새로 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 초기화된 헤드는 빠르게 학습하고 사전학습된 나머지는 적게 흔드는 전략은 섹션 2의 차등 학습률로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 헤드는 클래스 수보다 하나 많은 채널로 초기화되어 배경(no-object) 채널을 포함하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 3부의 배포 편에서 자세히 다룬다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;내가 덮어쓴 하이퍼파라미터와 라이브러리 기본값&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_kwargs에 내가 실제로 지정한 값은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886484270&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_kwargs = {
    &quot;dataset_dir&quot;: str(DATASET_DIR),
    &quot;epochs&quot;: args.epochs,             # 기본 80
    &quot;batch_size&quot;: args.batch_size,     # 4
    &quot;grad_accum_steps&quot;: args.grad_accum,  # 4
    &quot;lr&quot;: args.lr,                     # 1e-4
    &quot;lr_encoder&quot;: args.lr * 0.15,      # backbone은 15%로 낮춤
    &quot;lr_scheduler&quot;: &quot;cosine&quot;,
    &quot;gradient_checkpointing&quot;: True,
    &quot;early_stopping&quot;: True,
    &quot;early_stopping_patience&quot;: 15,
    &quot;checkpoint_interval&quot;: 5,
    &quot;num_workers&quot;: 4,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 내가 통제하는 것은 학습 길이, 배치 크기, 그래디언트 누적, 학습률, 백본 학습률, 스케줄러, 메모리 옵션, 조기 종료 정도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실 가중치, 포칼(focal) 파라미터, EMA 계수, weight decay(가중치 감쇠), 매칭 비용, 계층별 학습률 감쇠는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 라이브러리 기본값을 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배치 크기 4에 그래디언트 누적 4를 곱한 유효 배치 크기(effective batch size)는 16이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 값의 구체적 근거는 섹션 2 이후에서 하나씩 풀어 설명한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;IR 흑백 영상에 맞춘 보수적 증강&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내는 데이터 증강(augmentation, 학습 시 이미지를 변형해 다양성을 늘리는 기법)을 도메인에 맞춰 보수적으로 설정했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1782886504484&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_kwargs[&quot;aug_config&quot;] = {
    &quot;HorizontalFlip&quot;: {&quot;p&quot;: 0.5},
    &quot;RandomBrightnessContrast&quot;: {
        &quot;brightness_limit&quot;: 0.15, &quot;contrast_limit&quot;: 0.15, &quot;p&quot;: 0.4,
    },
    &quot;GaussianBlur&quot;: {&quot;blur_limit&quot;: 3, &quot;p&quot;: 0.2},
    &quot;GaussNoise&quot;: {&quot;std_range&quot;: (0.01, 0.03), &quot;p&quot;: 0.3},
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8round 카메라가 IR(적외선) 흑백 영상이라는 도메인 특성이 이 선택의 근거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌우 대칭은 사람과 공이 좌우 무관하므로 절반 확률로 적용하고, 밝기와 대비는 IR 노출 변화를 모사하되 폭을 0.15로 좁게 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가우시안 블러(Gaussian blur, 흐림)와 가우시안 노이즈(Gaussian noise, 무작위 잡음)는 각각 초점 흐림과 IR 센서 잡음을 모사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색상 계열 증강(HueSaturation 등)을 뺀 것은 의도적인데, 흑백 영상에서는 색 변형이 무의미하고 오히려 분포를 망칠 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밝기 폭을 0.15로 좁힌 것도 IR 영상의 실제 밝기 변동 범위를 넘지 않으려는 선택이다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;백본은 천천히, 헤드는 빠르게: AdamW와 층별 학습률&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;왜 SGD가 아니라 AdamW인가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵티마이저는 module_model.py의 configure_optimizers에서 AdamW로 생성된다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886522675&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optimizer = torch.optim.AdamW(
    param_dicts,
    lr=tc.lr,
    weight_decay=tc.weight_decay,
    fused=self._use_fused_optimizer,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SGD(확률적 경사하강법, 모든 파라미터에 같은 학습률을 적용)와 달리 Adam은 파라미터마다 적응형 학습률을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머는 파라미터별 그래디언트(gradient, 손실을 줄이는 방향과 크기) 스케일 편차가 커서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적응형 방식인 Adam이 사실상 표준이며, 실제로 ViT는 AdamW로 학습할 때 SGD보다 높은 정확도를 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AdamW는 일반 Adam과 달리 weight decay(L2 정규화, 가중치를 작게 유지해 과적합을 막는 기법)를 그래디언트가 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가중치에 직접 적용(decoupled)해, 정규화가 적응형 학습률에 왜곡되지 않게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lr은 내가 지정한 1e-4, weight_decay는 기본값 1e-4이며, fused(융합 커널)는 bf16(16비트 부동소수점)일 때만 켜지고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 FP32 학습에서는 자료형 불일치 크래시를 막기 위해 꺼진다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;층마다 다른 학습률: 계층별 학습률 감쇠와 차등 학습률&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 지정한 lr_encoder = lr &amp;times; 0.15보다 실제 동작은 더 정교하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;param_groups.py의 get_vit_lr_decay_rate가 백본을 층별로 다르게 감쇠시키기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886540736&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_vit_lr_decay_rate(name, lr_decay_rate=1.0, num_layers=12):
    layer_id = num_layers + 1
    if name.startswith(&quot;backbone&quot;):
        if &quot;.pos_embed&quot; in name or &quot;.patch_embed&quot; in name:
            layer_id = 0
        elif &quot;.blocks.&quot; in name and &quot;.residual.&quot; not in name:
            layer_id = int(name[name.find(&quot;.blocks.&quot;) :].split(&quot;.&quot;)[2]) + 1
    return lr_decay_rate ** (num_layers + 1 - layer_id)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터는 백본과 디코더와 나머지(헤드) 세 그룹으로 쪼개지며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백본은 lr_vit_layer_decay(기본 0.8)로 입력에 가까운 얕은 층일수록 학습률이 지수적으로 작아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 계층별 학습률 감쇠(LLRD, Layer-wise Learning Rate Decay)로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전학습이 배운 저수준 특징(엣지&amp;middot;질감)은 거의 건드리지 않고 출력에 가까운 상위 층만 내 도메인에 맞게 조정하려는 전략이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얕은 층이 급격히 바뀌면 그 위 층들이 표현을 다시 배워야 하므로, 아래를 천천히 흔드는 것이 파인튜닝의 표준 관행이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 lr_encoder로 백본 기준 학습률을 15%로 낮추고 그 위에 층별 0.8 감쇠가 또 곱해지며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 초기화된 헤드는 lr 전체값으로 가장 빠르게 학습한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;파라미터 종류별로 weight decay를 다르게 준다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;weight decay 역시 모든 파라미터에 똑같이 걸지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;param_groups.py의 get_vit_weight_decay_rate가 파라미터 종류별로 감쇠율을 다르게 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886559586&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_vit_weight_decay_rate(name, weight_decay_rate=1.0):
    if (&quot;gamma&quot; in name) or (&quot;pos_embed&quot; in name) or (&quot;rel_pos&quot; in name) \
            or (&quot;bias&quot; in name) or (&quot;norm&quot; in name):
        weight_decay_rate = 0.0
    return weight_decay_rate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규화층(norm), 편향(bias), 위치 임베딩(pos_embed, 토큰의 위치 정보),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케일 파라미터(gamma)에는 weight decay를 0으로 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 정규화 통계와 편향과 위치 정보에 L2 정규화를 걸면 학습이 망가지기 때문이며, 이는 트랜스포머 학습의 표준 관행이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 실제 가중치 행렬에만 weight decay 1e-4가 적용되고, 편향성 파라미터는 정규화에서 제외된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;워밍업 뒤 코사인으로 감쇠하는 학습률 스케줄&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습률 스케줄(learning rate schedule)은 학습 진행에 따라 학습률을 조절하는 규칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;module_model.py의 lr_lambda가 워밍업 뒤 코사인 감쇠를 스텝 단위로 적용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886571785&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def lr_lambda(current_step):
    if current_step &amp;lt; warmup_steps:                 # 선형 워밍업
        return float(current_step) / float(max(1, warmup_steps))
    if tc.lr_scheduler == &quot;cosine&quot;:                 # 코사인 감쇠
        progress = (current_step - warmup_steps) / max(1, total_steps - warmup_steps)
        return tc.lr_min_factor + (1 - tc.lr_min_factor) * 0.5 * (1 + math.cos(math.pi * progress))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워밍업(warmup)은 학습 초반에 학습률을 0에서 선형으로 끌어올려 초기 불안정을 줄이는 구간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코사인 감쇠(cosine annealing)는 그 이후 학습률을 코사인 곡선을 따라 부드럽게 낮추며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lr_min_factor가 0이라 학습 끝에서 0까지 감소해 후반의 미세 수렴과 진동 억제를 돕는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 warmup_epochs가 0이라 사실상 워밍업은 생략되는데, 파인튜닝은 이미 좋은 가중치에서 출발하므로 워밍업 없이도 안정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내는 lr_scheduler에 &quot;cosine&quot;을 지정했고, 스케줄은 스텝 단위로 갱신되어 전체 학습 구간을 코사인 한 주기로 덮는다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;작은 GPU로 큰 배치를 흉내 낸다: 그래디언트 누적과 체크포인팅&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A2000의 12GB VRAM(그래픽 메모리) 한계에 맞추기 위해 세 가지 기법을 함께 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째는 그래디언트 누적(gradient accumulation)으로, 배치 4의 그래디언트를 네 번 모은 뒤 한 번 갱신해 유효 배치 16을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 물리 배치는 그래디언트 노이즈가 크지만, 여러 번 누적하면 큰 배치의 안정적 그래디언트를 근사할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째는 그래디언트 체크포인팅(gradient checkpointing)으로, 순전파(forward)의 중간 활성값을 저장하지 않고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역전파(backward) 때 다시 계산해 메모리를 아낀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 메모리를 크게 줄이는 대신 재계산 때문에 속도를 약간 손해 보는 맞교환이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셋째는 그래디언트 클리핑(gradient clipping)으로, 그래디언트의 크기(norm)를 기본값 0.1로 제한해 폭주를 막는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머는 그래디언트가 튀기 쉬워 이 장치가 사실상 필수다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;무엇을 틀렸다고 가르치는가: 활성화와 손실함수&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;활성화 함수: 분류는 Sigmoid, FFN은 ReLU, 어텐션은 Softmax&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;활성화 함수- 분류는 Sigmoid, FFN은 ReLU, 어텐션은 Softmax.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4RVrd/dJMcaay7xCU/8zfqwjAaWgIe9vwEUEQtx0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4RVrd/dJMcaay7xCU/8zfqwjAaWgIe9vwEUEQtx0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4RVrd/dJMcaay7xCU/8zfqwjAaWgIe9vwEUEQtx0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4RVrd%2FdJMcaay7xCU%2F8zfqwjAaWgIe9vwEUEQtx0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;활성화 함수- 분류는 Sigmoid, FFN은 ReLU, 어텐션은 Softmax.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활성화 함수(activation function)는 신경망에 비선형성을 부여해 복잡한 패턴을 학습하게 하는 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류 헤드는 Softmax(합이 1이 되도록 묶는 함수)가 아니라 Sigmoid(각 값을 0과 1 사이로 독립 변환)를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 각 클래스를 독립된 이진 분류로 다루는 멀티라벨(multi-label) 방식이며, 이 때문에 배포 시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;person&amp;middot;ball&amp;middot;background 채널이 따로 나오고 배경 채널을 걸러내야 한다(3부에서 상술).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머의 피드포워드 신경망(FFN, Feed-Forward Network)은 ReLU(음수를 0으로 만드는 함수)를 활성화로 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어텐션(attention, 입력의 어느 부분에 집중할지 정하는 기제)의 가중치 정규화에는 표준 트랜스포머와 같이 Softmax를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백본인 DINOv2 내부에는 GELU와 LayerNorm(층 정규화)이 쓰이는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 LayerNorm이 FP16에서 붕괴하는 원인이며 이 또한 3부의 핵심 주제다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;분류 손실: IoU를 결합한 IA-BCE&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;분류 손실- IoU를 결합한 IA-BCE.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg8y7q/dJMcadoYDdW/J1NYvkmAUVXpRu6DKKYHZk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg8y7q/dJMcadoYDdW/J1NYvkmAUVXpRu6DKKYHZk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg8y7q/dJMcadoYDdW/J1NYvkmAUVXpRu6DKKYHZk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg8y7q%2FdJMcadoYDdW%2FJ1NYvkmAUVXpRu6DKKYHZk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;327&quot; data-filename=&quot;분류 손실- IoU를 결합한 IA-BCE.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류 손실(classification loss)은 예측한 클래스가 정답과 얼마나 다른지를 측정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 설정이 ia_bce_loss=True이므로 criterion.py의 loss_labels는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IA-BCE(IoU-aware Binary Cross Entropy, IoU 인식 이진 교차 엔트로피) 분기를 탄다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886608002&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;t = prob[tuple(pos_ind)].pow(alpha) * pos_ious.pow(1 - alpha)   # 분류확신 &amp;times; 위치정확도
t = torch.clamp(t, 0.01).detach()
pos_weights[tuple(pos_ind)] = t
neg_weights[tuple(pos_ind)] = 1 - t
# 표준 BCE를 logsigmoid로 재구성하여 수치 안정성 확보
loss_ce = neg_weights * src_logits - F.logsigmoid(src_logits) * (pos_weights + neg_weights)
loss_ce = loss_ce.sum() / num_boxes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 분류 점수를 예측 박스가 정답과 얼마나 겹치는지(IoU, Intersection over Union, 교집합을 합집합으로 나눈 겹침 비율)와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결합한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스를 잘 맞춘 예측에 더 높은 분류 목표를 부여함으로써 확신도와 위치 품질을 일치시켜, 좋은 박스가 살아남게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는 포칼 손실(focal loss, 2017년 RetinaNet 논문) 개념이 들어 있어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alpha는 0.25, gamma는 2로 설정되며, gamma=2는 쉬운 예제의 손실을 크게 줄여 어려운 예제에 집중시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;300개 쿼리 중 실제 객체는 몇 개뿐이라 배경이 압도적이므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 불균형에 대응하는 것이 분류 손실 설계의 핵심이고 logsigmoid 재구성은 수치 안정성을 위한 것이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;박스 손실: L1과 GIoU의 상보&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;박스 손실- L1과 GIoU의 상보.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oqMiy/dJMcadoYDeS/sT8TcWrtJLyX4BEXfxfHd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oqMiy/dJMcadoYDeS/sT8TcWrtJLyX4BEXfxfHd0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oqMiy/dJMcadoYDeS/sT8TcWrtJLyX4BEXfxfHd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoqMiy%2FdJMcadoYDeS%2FsT8TcWrtJLyX4BEXfxfHd0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;327&quot; data-filename=&quot;박스 손실- L1과 GIoU의 상보.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박스 회귀 손실(box regression loss)은 예측 박스의 좌표를 정답 박스에 맞추는 손실이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;criterion.py의 loss_boxes는 두 손실을 함께 쓴다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886628370&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;loss_bbox = F.l1_loss(src_boxes, target_boxes, reduction=&quot;none&quot;)   # 좌표 절대 오차
losses[&quot;loss_bbox&quot;] = loss_bbox.sum() / num_boxes

loss_giou = 1 - torch.diag(box_ops.generalized_box_iou(          # 겹침 품질
    box_ops.box_cxcywh_to_xyxy(src_boxes),
    box_ops.box_cxcywh_to_xyxy(target_boxes),
))
losses[&quot;loss_giou&quot;] = loss_giou.sum() / num_boxes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;L1 손실(L1 loss)은 중심좌표와 크기(cxcywh)의 절대 오차로, 박스 위치를 직접 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GIoU 손실(Generalized IoU, 일반화 교집합-합집합, 2019년 제안)은 IoU에 두 박스를 감싸는 최소 영역 패널티를 더한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;L1만 쓰면 박스 크기에 따라 손실 스케일이 달라지지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GIoU는 스케일에 불변이고 두 박스가 전혀 겹치지 않을 때도 그래디언트를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 상보적이라 함께 쓰며, 기본 가중치는 L1이 5, GIoU가 2로 위치 정확도에 더 큰 비중을 둔다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;보조 손실: 디코더 중간층까지 감독한다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;보조 손실- 디코더 중간 층까지 감독한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6yteE/dJMcajo9QD5/HqiPgK1DMhTuIOKlYsKtY1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6yteE/dJMcajo9QD5/HqiPgK1DMhTuIOKlYsKtY1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6yteE/dJMcajo9QD5/HqiPgK1DMhTuIOKlYsKtY1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6yteE%2FdJMcajo9QD5%2FHqiPgK1DMhTuIOKlYsKtY1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;327&quot; data-filename=&quot;보조 손실- 디코더 중간 층까지 감독한다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보조 손실(auxiliary loss)은 최종 출력뿐 아니라 디코더 중간층의 출력에도 같은 손실을 거는 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 심층 감독(deep supervision)이라 불리며, 최종 손실은 가중합으로 구성된다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886645168&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;weight_dict = {&quot;loss_ce&quot;: args.cls_loss_coef, &quot;loss_bbox&quot;: args.bbox_loss_coef}
weight_dict[&quot;loss_giou&quot;] = args.giou_loss_coef
# 디코더 각 중간 층과 인코더 제안에도 동일 가중치를 복제
aux_weight_dict = {}
for i in range(args.dec_layers - 1):
    aux_weight_dict.update({k + f&quot;_{i}&quot;: v for k, v in weight_dict.items()})
weight_dict.update(aux_weight_dict)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더 4개 레이어가 전부 직접 감독을 받으면 각 층이 개별 학습 신호를 얻어 수렴이 빠르고 안정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 two_stage 설정이 켜져 있어 인코더도 초기 박스를 제안하고 별도의 손실을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 가중치는 분류 손실 1.0, L1 손실 5, GIoU 손실 2로, 박스 손실 비중을 분류보다 크게 둔 것은 DETR 계열의 전통이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 쿼리가 많아 분류 신호는 충분한 반면 정밀한 박스 회귀가 더 어렵다는 판단에 근거한다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;NMS 없이 짝을 짓고 수렴을 다진다: 헝가리안 매칭&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;DETR은 왜 NMS가 필요 없는가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DETR은 왜 NMS가 필요 없는가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/297jv/dJMcadbqjml/4CFSs0UR5TrO6VtJBcyiF1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/297jv/dJMcadbqjml/4CFSs0UR5TrO6VtJBcyiF1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/297jv/dJMcadbqjml/4CFSs0UR5TrO6VtJBcyiF1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F297jv%2FdJMcadbqjml%2F4CFSs0UR5TrO6VtJBcyiF1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;327&quot; data-filename=&quot;DETR은 왜 NMS가 필요 없는가.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NMS(Non-Maximum Suppression, 비최대 억제)는 한 객체에 겹쳐 나온 중복 박스를 사후에 제거하는 후처리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적 검출기는 한 객체에 여러 박스를 내놓기 때문에 이 후처리가 반드시 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 DETR(DEtection TRansformer, 2020년 제안) 계열은 검출을 집합 예측(set prediction) 문제로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 300개의 쿼리를 정답과 1대 1로 강제 매칭하고, 매칭된 쿼리만 객체로, 나머지는 배경으로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 1대 1 매칭 덕분에 한 객체에 중복 박스가 원천적으로 생기지 않으므로 NMS가 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 매칭을 수행하는 것이 헝가리안 매처이며, 쿼리 수가 300이라 이미지당 최대 300개까지 검출한다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;비용 행렬과 헝가리안 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;비용 행렬과 헝가리안 알고리즘.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciIp5W/dJMcaicSZCC/k5hYg29HsXIiVQXeMDvON1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciIp5W/dJMcaicSZCC/k5hYg29HsXIiVQXeMDvON1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciIp5W/dJMcaicSZCC/k5hYg29HsXIiVQXeMDvON1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciIp5W%2FdJMcaicSZCC%2Fk5hYg29HsXIiVQXeMDvON1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;327&quot; data-filename=&quot;비용 행렬과 헝가리안 알고리즘.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헝가리안 알고리즘(Hungarian algorithm, 1955년 제안)은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 매칭(bipartite matching)에서 총비용이 최소가 되는 1대 1 배정을 찾는 최적화 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;matcher.py는 비용 행렬을 만든 뒤 이 알고리즘으로 예측과 정답을 짝짓는다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886668753&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 비용 행렬 C = 박스 L1 + 분류 + GIoU
C = self.cost_bbox * cost_bbox + self.cost_class * cost_class + self.cost_giou * cost_giou
C = C.view(bs, num_queries, -1).cpu()
# 총비용 최소가 되는 1:1 배정을 찾는다
indices_g = [linear_sum_assignment(c[i]) for i, c in enumerate(C_g.split(sizes, -1))]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용 행렬은 분류 비용, 박스 L1 비용, GIoU 비용의 가중합으로 구성되며, 기본 가중치는 분류 2, 박스 5, GIoU 2다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 배정은 scipy(과학 계산 라이브러리)의 linear_sum_assignment 함수가 총비용을 최소화하며 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 매칭은 학습 시에만, 그래디언트 없이(no_grad) 수행되어 어느 예측을 어느 정답에 대응시킬지만 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매칭되지 않은 쿼리는 자동으로 배경으로 처리되며, 이것이 배포 시 배경 채널이 존재하는 이유와 연결된다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;Group DETR로 수렴을 앞당긴다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5aa8fad4-06b9-4ba1-a2a3-1458e530cdc1.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EGgg5/dJMcahkJ7NE/jXkZqlWjK7PLbsS16ZdPDk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EGgg5/dJMcahkJ7NE/jXkZqlWjK7PLbsS16ZdPDk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EGgg5/dJMcahkJ7NE/jXkZqlWjK7PLbsS16ZdPDk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEGgg5%2FdJMcahkJ7NE%2FjXkZqlWjK7PLbsS16ZdPDk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;5aa8fad4-06b9-4ba1-a2a3-1458e530cdc1.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DETR의 1대 1 매칭은 정답 하나에 양성 예측이 하나뿐이라 학습 신호가 희소하고, 이 때문에 수렴이 느리다는 약점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Group DETR(2023년 제안)은 이 문제를 그룹 단위 일대다 매칭으로 완화한다.&lt;/p&gt;
&lt;pre id=&quot;code_1782886685862&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 학습 시 group_detr 그룹으로 나눠 각 그룹에서 1:1 매칭 &amp;rarr; 정답당 양성 신호 증가
g_num_queries = num_queries // group_detr
C_list = C.split(g_num_queries, dim=1)
for g_i in range(group_detr):
    C_g = C_list[g_i]
    # 각 그룹에서 독립적으로 헝가리안 매칭 수행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값 group_detr는 13으로, 300개 쿼리를 13개 그룹으로 복제해 각 그룹에서 1대 1 매칭을 수행하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답 하나가 13개의 양성 신호를 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 자동으로 학습되는 쿼리 증강처럼 작동하며, 같은 구조의 네트워크를 동시에 학습하는 것과 같아 감독 신호를 늘려 수렴을 앞당긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론 시에는 한 그룹의 쿼리만 사용하므로 구조나 추론 과정은 전혀 바뀌지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Group DETR은 학습 속도만 높이고 배포 모델에는 부담을 주지 않는 학습 전용 기법이다.&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #BB742B 8px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;EMA와 조기 종료로 학습을 매듭짓는다&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;EMA와 조기 종료로 학습을 매듭짓는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pMiPc/dJMcahZfHEY/Ewgma4lIZqrXzoKxyf1yX0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pMiPc/dJMcahZfHEY/Ewgma4lIZqrXzoKxyf1yX0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pMiPc/dJMcahZfHEY/Ewgma4lIZqrXzoKxyf1yX0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpMiPc%2FdJMcahZfHEY%2FEwgma4lIZqrXzoKxyf1yX0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-filename=&quot;EMA와 조기 종료로 학습을 매듭짓는다.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EMA(Exponential Moving Average, 지수이동평균)는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 중 가중치의 평균 사본을 지수적으로 감쇠하는 가중치로 천천히 따라가게 유지하는 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값은 ema_decay 0.993이며 use_ema가 켜져 있어 매 스텝 갱신된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 스텝 출렁이는 원본 가중치보다 EMA 가중치가 그래디언트 잡음을 줄여 더 평탄한 손실 지형과 넓은 최적점을 찾으므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반화와 검증 성능이 안정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기법은 YOLO나 SSD 같은 검출 모델에서도 박스 예측 안정화를 위해 널리 쓰이는 사실상 무료의 성능 향상 장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조기 종료(early stopping)는 검증 지표가 정해진 인내 구간 동안 개선되지 않으면 학습을 중단하는 규제 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 early_stopping을 켜고 인내 구간(patience)을 15로 지정해,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과적합에 진입하기 전에 학습을 멈추고 시간을 아끼며, 최종 best.pth는 검증 기준 최적 가중치로 저장된다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;나가며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;나가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rBgCj/dJMcadvIpnD/SFSPFnma8q0aOiHF0WKEq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rBgCj/dJMcadvIpnD/SFSPFnma8q0aOiHF0WKEq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rBgCj/dJMcadvIpnD/SFSPFnma8q0aOiHF0WKEq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrBgCj%2FdJMcadvIpnD%2FSFSPFnma8q0aOiHF0WKEq1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;335&quot; data-filename=&quot;나가는 글.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model.train() 한 줄 뒤에서는 AdamW와 계층별 학습률 감쇠, 코사인 스케줄, IA-BCE 분류 손실,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;L1과 GIoU 박스 손실, 보조 손실, 헝가리안 매칭, Group DETR, EMA, 조기 종료가 함께 돌고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가운데 내가 실제로 건드린 값은 학습 길이, 배치, 그래디언트 누적, 학습률, 백본 학습률, 스케줄러, 메모리 옵션, 조기 종료, 증강뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실함수와 매칭과 활성화와 EMA와 포칼 파라미터와 손실 가중치와 계층별 감쇠는 전부 라이브러리 기본값을 따른 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 파인튜닝의 대부분은 내 코드가 아니라 잘 설계된 라이브러리 기본값에 의존하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 생겼을 때 어디를 봐야 할지 알려면 그 기본값을 이해해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 3부에서는 이렇게 학습한 모델을 ONNX로 내보내고 배포할 때의 함정을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적으로는 클래스 채널 오프셋 문제와, 백본의 LayerNorm이 무너지는 FP16 붕괴 사례를 정량 데이터와 함께 분석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요컨대 한 줄 뒤를 이해하는 일은 학습이 잘못될 때 원인을 짚어내는 능력과 같다.&lt;/p&gt;
&lt;h2 style=&quot;box-sizing: border-box; border-right-width: 0px; border-top-width: 0px; border-left: #6DABE4 10px solid; border-bottom: #FFD8D8 0px solid; line-height: 1.7; margin-right: 0px; padding: 3px 5px 3px 10px;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RF-DETR 공식 문서 및 저장소: &lt;a href=&quot;https://rfdetr.roboflow.com&quot;&gt;https://rfdetr.roboflow.com&lt;/a&gt; , &lt;a href=&quot;https://github.com/roboflow/rf-detr&quot;&gt;https://github.com/roboflow/rf-detr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RF-DETR 소개, Roboflow Blog: &lt;a href=&quot;https://blog.roboflow.com/rf-detr/&quot;&gt;https://blog.roboflow.com/rf-detr/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AdamW 원논문(Decoupled Weight Decay Regularization, Loshchilov and Hutter): &lt;a href=&quot;https://arxiv.org/abs/1711.05101&quot;&gt;https://arxiv.org/abs/1711.05101&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AdamW 개념 해설: &lt;a href=&quot;https://optimization.cbe.cornell.edu/index.php?title=AdamW&quot;&gt;https://optimization.cbe.cornell.edu/index.php?title=AdamW&lt;/a&gt; , &lt;a href=&quot;https://theorempath.com/compare/adamw-vs-adam&quot;&gt;https://theorempath.com/compare/adamw-vs-adam&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;계층별 학습률 감쇠(LLRD) 개념: &lt;a href=&quot;https://mbrenndoerfer.com/writing/fine-tuning-learning-rates-llrd-warmup-decay-transformers&quot;&gt;https://mbrenndoerfer.com/writing/fine-tuning-learning-rates-llrd-warmup-decay-transformers&lt;/a&gt; , &lt;a href=&quot;https://towardsdatascience.com/advanced-techniques-for-fine-tuning-transformers-82e4e61e16e/&quot;&gt;https://towardsdatascience.com/advanced-techniques-for-fine-tuning-transformers-82e4e61e16e/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;포칼 손실 원논문(Focal Loss for Dense Object Detection, Lin et al.): &lt;a href=&quot;https://arxiv.org/pdf/1708.02002&quot;&gt;https://arxiv.org/pdf/1708.02002&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;포칼 손실 해설: &lt;a href=&quot;https://www.analyticsvidhya.com/blog/2020/08/a-beginners-guide-to-focal-loss-in-object-detection/&quot;&gt;https://www.analyticsvidhya.com/blog/2020/08/a-beginners-guide-to-focal-loss-in-object-detection/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GIoU 원논문(Generalized Intersection over Union): &lt;a href=&quot;https://arxiv.org/pdf/1902.09630&quot;&gt;https://arxiv.org/pdf/1902.09630&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;DETR 원논문 및 해설(End-to-End Object Detection with Transformers): &lt;a href=&quot;https://blog.roboflow.com/what-is-detr/&quot;&gt;https://blog.roboflow.com/what-is-detr/&lt;/a&gt; , &lt;a href=&quot;https://www.lightly.ai/blog/detr&quot;&gt;https://www.lightly.ai/blog/detr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;헝가리안 매칭과 DETR 리뷰: &lt;a href=&quot;https://www.mdpi.com/1424-8220/25/13/3952&quot;&gt;https://www.mdpi.com/1424-8220/25/13/3952&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Group DETR 원논문(Fast DETR Training with Group-Wise One-to-Many Assignment): &lt;a href=&quot;https://arxiv.org/abs/2207.13085&quot;&gt;https://arxiv.org/abs/2207.13085&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;EMA(가중치 지수이동평균) 연구(Dynamics and Benefits): &lt;a href=&quot;https://arxiv.org/abs/2411.18704&quot;&gt;https://arxiv.org/abs/2411.18704&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ML+DL/Vision AI</category>
      <category>adamw</category>
      <category>GIoU</category>
      <category>GroupDETR</category>
      <category>RFDETR</category>
      <category>객체검출</category>
      <category>딥러닝</category>
      <category>손실함수</category>
      <category>파인튜닝</category>
      <category>포칼로스</category>
      <category>헝가리안매칭</category>
      <author>Vagabund.Gni</author>
      <guid isPermaLink="true">https://gnidinger.tistory.com/1349</guid>
      <comments>https://gnidinger.tistory.com/entry/modeltrain-%ED%95%9C-%EC%A4%84-%EB%92%A4-AdamW%EC%99%80-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98-%ED%97%9D%EA%B0%80%EB%A6%AC%EC%95%88-%EB%A7%A4%EC%B9%AD-RF-DETR-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-2#entry1349comment</comments>
      <pubDate>Wed, 1 Jul 2026 15:28:44 +0900</pubDate>
    </item>
  </channel>
</rss>