Hidden Technical Debt in Machine Learning Systems

2020-02-16

ML 서비스를 Production 에 적용하기 시작하면서, CD4ML, ML Ops 에도 관심이 많이 생겨서 차근차근 공부해보려고 합니다.

나온지 꽤 되긴 했는데 MLOps 관련 많은 article 들에서 읽어볼 것을 추천하고 있는, Hidden Technical Debt in Machine Learning Systems (2015)(ML system 에서의 기술 부채에 대해 Google 에서 작성한 유명한 논문) 를 찬찬히 읽어보고 적당히 의역 및 요약해보았습니다. (정확한 내용은 원문을 참고해주세요.)

영어가 짧아서 논문을 읽고 저만의 언어(?)로 번역해보는 일은 시간이 꽤 걸렸지만, 그냥 슥 읽어보는 것보다 더 꼼꼼하고 깊게 읽어 보게 되어서 공부하는데 도움은 많이 되었습니다. :)

1. Introduction - ML 시스템에서의 기술적 부채

Technical Debt (기술 부채) 란?

Ward Cunningham 이 제시한 비유적 표현으로, software engineering 관점에서 기존의 시스템에 축적되어서 새로운 기능을 추가하거나, 변경 / 유지보수 할 때 이를 어렵게 만드는 요소들로, 금융에서의 부채와 비슷한 개념으로 바라볼 수 있습니다.

기술 부채는 대략 다음의 한 짤로 요약됩니다…

Software engineering에서와 마찬가지로 라이브 시스템에 ML 을 적용하기 시작하면서 ML system 에서도 개발과 배포에 걸리는 시간은 상대적으로 빠르고 저렴하지만, 이를 유지보수하는 것은 어렵고 비용이 많이 들게되는 현상이 광범위하게 발생하고 있습니다.

특히 ML system 에서는 전통적인 코드를 유지 보수하는 문제에 추가로 ML에 특정된 이슈들로 인한 기술적 부채들도 발생합니다. 그런데 이런 기술 부채들은 코드 레벨보다는 시스템 레벨에 존재하기 때문에 감지하기 어렵습니다. ML 시스템에서 코드 외 시스템에서 흘러가는 데이터 때문에 전통적인 추상화 경계(abstraction boundaries)가 깨지기 쉽고, 코드 레벨의 기술부채를 갚는 전형적인 방법들 (e.g. 리팩토링, 테스트코드 작성 등)로는 ML system의 부채를 해결하는데 충분하지 않습니다.

이 논문에서는 ML 기술 부채가 빠르게 축적될 수 있는 ML system 내의 시스템 레벨의 상호작용과 인터페이스에 집중하여 ML 기술 부채를 발생시키는 요소들에 대해 제시하고 있습니다.

2. Complex Models Erode Boundaries

전통적인 software engineering에서는 캡슐화(encapsulation)와 모듈 디자인(modular design)를 사용한 강한 추상화 경계(abstraction boundaries)를 통해 유지보수가 가능한 코드를 만들고, 독립된 작은 변화와 개선을 만들기 쉽게 합니다. 엄격한 추상화 경계는 인풋과 아웃풋에 대한 일관성을 가지게 합니다.

그러나 ML 시스템에서는 설계했던 동작이 외부의 데이터 의존성을 제외하고 소프트웨어 로직만으로는 효과적으로 표현 될 수 없기 때문에 엄격한 추상화 경계를 적용하기 어렵고, 이로 인해 심각한 기술 부채를 만들 수 있습니다.

Entanglement

ML 시스템에서는 입력값들이 서로 복잡하게 얽혀있기 때문에, 독립적으로 개선 하기 어렵습니다. 예를 들어 x_1, … , x_n 의 feature를 사용하는 모델에서 만약 x_1 의 입력 분포를 바꾸는 경우, 나머지 모든 n-1개의 feature 들의 가중치, 사용 여부 등이 모두 바뀌게 됩니다. 새로운 n+1 번째 feature 를 추가하거나, 특정 feature x_j 를 제거하는 경우도 마찬가지 입니다.

이 논문에서는 이러한 경우를 CACE (Changing Anything Changes Everything) 원칙으로 명명했습니다. CACE 원칙은 입력값에 대해서 뿐아니라, 하이퍼파라미터, 학습에 필요한 설정들, 샘플링 방법등 ML system 의 모든 가능한 작업에 적용될 수 있습니다.

이에 대한 해결책으로 한 가지 방법은 모델을 독립적으로 구성하고 ensemble한 결과를 제공하는 것입니다. Ensemble 은 문제가 sub task 들로 자연스럽게 잘 나뉘어질 수 있을 때 적용하기 좋고, 많은 케이스에서 ensemble은 잘 동작합니다. 그렇지만 ensemble 역시 모델이 얽혀있기 때문에, 개별 모델의 정확도 개선이 전체적인 시스템의 정확도를 낮추는 결과를 초래할 수 도 있습니다.

두 번째 가능한 전략으로는 발생할 수 있는 예측에 대한 변화를 감지하는 것에 주력하는 것입니다. 고차원의 정보를 시각화 할 수 있는 도구를 활용하여, 영향들을 다양한 차원에서 나누어서 분석해볼 수 있습니다.

Correction Cascades

문제 A 를 풀기 위한 모델 m_a 가 존재하고 문제 A와 약간만 다른 문제 A’에 대한 모델이 필요할 때, 빠른 해결책으로 모델 m_a의 결과를 입력으로 사용하고 일부분만 재학습하여 m_a'를 만들 수 있습니다.

그러나, 이렇게 만들어진 모델의 경우 모델 m_a에 대한 의존성을 가지고 있어 차후에 모델을 개선하기 위해 분석하는 비용이 많이 들게됩니다. 그리고 m_a’ 모델을 활용하여 다시 문제 A’‘에 적용하는 식으로 correction 모델들이 쌓여서 비용이 증가하게 됩니다. 또한 이런 correction cascade 들은 deadlock 을 발생시킬 수 있는데, 개별 모델의 정확도 개선이 전체 시스템 레벨의 정확도를 오히려 낮추게 될 수 있습니다. 이에 대해서는 feature 를 추가하여 직접적으로 correction 을 학습시키거나, 또 다른 모델 A'를 직접 개발하는 비용을 들여서 완화할 수 있습니다.

대략 문제 별로 그냥 모델을 따로 만들어라 하는 얘기 같음.

Undeclared Consumers

SE에서도 물론 동일하게 발생하는 문제이지만, batch prediction 같이 결과값이 파일로 저장되어 사용될 수 있는 ML system의 경우 특히 주의해야 할 이슈인 것 같다.

모델 m_a를 통한 예측 결과값이 다른 시스템에서 쉽게 접근가능한 경우, 접근 제한이 없으면 선언하지 않은 소비자들이 모델의 결과값을 사용하게 되는 경우가 발생할 수 있습니다. 전통적인 software engineering 에서는 이 문제를 visibility debt 라고 부릅니다.

선언하지 않은 consumer들은 모델 m_a와 특정 부분과의 숨겨진 밀접한 결합을 만들기 때문에, 작게는 (시스템 유지보수에 대한) 비용을 높이고, 최악의 경우는 위험할 수도 있습니다. 모델 m_a의 변화가 다른 어떤 부분에 의도하지 않은 영향을 끼칠 수 있습니다. 일반적으로 이러한 밀접한 결합은 비용을 증가시키고, 개선점을 m_a 에 적용하기 어렵게 만듭니다. 또한 이런 consumer 들은 4장에서 설명하는 숨겨진 피드백 루프를 만들 수도 있습니다.

Undeclared consumer 들은 엄격한 service-level agreements (SLAs) 같은 강력한 가드 가드 없이는 감지하기 어렵습니다.

3. Data Dependencies Cost More than Code Dependencies

전통적인 소프트웨어 엔지니어링에서 코드를 복잡하게 하고 기술 부채를 발생시키는 주요 요소로 dependency dept를 꼽고 있습니다. ML 시스템에서도 이와 비슷하게 기술 부채를 발생시키면서 감지하는 것도 더 어려운 data dependency 가 존재합니다. 코드 의존성의 경우 compiler, linker 등 static analysis 를 통해 확인할 수 있습니다. 데이터 의존성의 경우도 의존성을 파악할 수 있는 도구가 없다면, 대규모의 해결하기 어려운 데이터 의존성을 쌓기 쉽습니다.

Unstable Data Dependencies

모델에서 다른 시스템에서 만든 출력값을 모델의 입력값으로 사용하게 되는 경우가 많은데, 이런 경우 시간의 흐름에 따라 질적으로, 양적으로 변화하는 unstable 한 입력값이 될 수 있습니다. 예를 들어 다른 모델의 출력 값을 입력값으로 사용하는 경우 해당 모델이 변경됨에 따라 출력값이 암묵적으로 바뀔 수 있습니다. 이러한 입력값의 변화는 이를 사용하는 시스템에서 감지하기 어렵기 때문에 위험합니다.

이에 대한 한 가지 해결 전략으로는 데이터에 대해 versioned copy 를 만들어 데이터 의존성을 해결하는 방법이 있습니다.

예전에 데이터 버전 관리 툴 DVC 사용해보고 작성한 글 참고

Underutilized Data Dependencies

코드에서 underutilized dependencies는 사용되지 않는 package들을 의미합니다. 이와 유사하게, underutilized data dependencies 는 모델에서의 이득이 거의 없는 입력값들을 의미합니다. 이러한 입력값들은 ML system 에서 불필요하게 변화에 취약하게 만들고, 치명적으로 만들 수 있습니다.

Underutilized Data Dependencies는 다음의 경우에서 발생할 수 있습니다.

  • Legacy Features: 가장 흔한 경우로써, feature F가 모델의 개발단계에서 포함되었다가 시간이 지나면서 새로운 feature에 의해 불필요하게 된 경우입니다.
  • Bundled Features: 보통 마감 일정에 쫓겨서 모든 feature 를 bundle로 모델에 한꺼번에 추가하는 경우 발생하는데, 이때 특정 feauture 들은 적은 효과가 있거나 가치가 없는 feature 일 수 있습니다.
  • ϵ-Features: 모델의 정확도를 개선하기 위해 아주 작은 효과를 가지지만, 시스템의 복잡도를 크게 증가시킬 수 있는 feature를 포함시키려는 경우가 있습니다.
  • Correlated Featuress: 두 feature 가 강한 상관관계를 보이지만, 한 가지 feature가 직접적인 원인이 되는 경우가 많습니다. 많은 ML 방법들에서 이러한 특성을 감지하는 것은 어렵고, 두 feature를 동일하게 평가하거나 심지어는 직접적인 원인이 아닌 feature 를 택하는 경우가 있습니다.

Underutilized Data Dependencies 는 leave-one-feature-out 평가를 통해 감지할 수 있고, 이러한 평가는 정기적으로 수행하고 불필요한 feature 들을 제거해야 합니다.

Static Analysis of Data Dependencies

전통적인 코드에서는 컴파일러와 빌드 시스템에서 의존성 그래프에 대해 정적 검사를 수행할 수 있습니다. 데이터 의존성에 대한 정적 분석 도구는 자동화된 feature 관리 같은 도구를 예를 들 수 있고, 아직 그리 많이 있지는 않습니다.

4. Feedback Loops

Live ML 시스템에서 한 가지 중요한 특성은 시간이 흐름에 따라 시스템의 동작이 다시 모델에 영향을 끼치게 되는 것입니다. 이러한 특성은 모델이 배포되기 전에는 시스템이 어떻게 동작할 지 예측하기 어려운 analysys debt 를 발생시킬 수 있습니다.

모델이 데이터에 영향을 끼치고, 다시 그 데이터가 모델에 영향을 끼치는 건가 봉가

  • Direct Feedback Loops: 어떤 모델이 해당 모델에 사용할 미래의 학습 데이터를 선택하는데 직접적으로 영향을 끼질 수 있습니다. 이러한 문제는 supervised algorithm 에서 보통 bandit algorithm 을 통해 해결합니다.

뭔 말인지 모르겠음.

  • Hidden Feedback Loops: 직접적인 피드백 루프는 분석하기 어렵지만, 적어도 통계학적으로 해결책을 찾아볼 수는 있습니다. 더욱 더 어려운 케이스는 실세계에서 간접적으로 서로 영향을 주고 있는 시스템들에서 발생하는 숨어있는 피드백 루프입니다. 예를 들어 웹 페이지의 facet을 결정하는 두 시스템 (보여줄 제품을 선택하는 시스템과 제품에 관련된 리뷰를 선택하는 시스템) 에서 한 시스템에서의 개선 사항은 다른 시스템에 영향 (유저가 특정 제품을 더 많이/적게 클릭하는 등)을 끼칠 수 있습니다. 이러한 숨은 피드백 루프가 완전히 독립된 시스템 사이에서 발생한다는 사실을 유념해야 합니다.

5. ML-System Anti-Patterns

Real-world ML system 에서 실제 학습이나 예측에 사용되는 “ML 코드”가 차지하는 비중은 매우 적고, ML system 을 위해 요구되는 인프라는 매우 복잡합니다.

매우 진리이다. 한 백 번쯤 강조해야 한다.

아래에서는 이러한 ML 시스템에서 반드시 피하거나, 리팩토링 되어야하는 몇 가지 system-design anti-pattern 들에 대해 다룹니다.

Glue Code

ML 연구자들은 독립된 package 형식으로 솔루션을 개발하려는 경향이 있고, 오픈소스 형태로 다양하게 존재하고 있습니다. 이러한 package들을 사용하게 될 경우, 보통 이 package를 사용하기 위해 데이터 입출력을 위한 대량의 지원 코드를 작성하게 됨으로써 glue code system design pattern 으로 빠지게 합니다. Glue code 는 특정 package를 사용하도록 시스템을 종속시키게 되어 장기적으로는 비용이 큽니다.

Glue code 문제를 해결하기 위한 한 가지 전략은 이러한 black-box package 를 common API로 wrapping 하는 방법 이 있습니다. Common api 로 wrapping 하면 package의 변화에 따른 비용을 줄이고, 재사용성을 더 높일 수 있습니다.

Pipeline Jungles

Glue code의 특수한 케이스로, 데이터 전처리 단계에서 pipeline jungles 가 발생할 수 있습니다. Pipeline jungles 는 새로운 입력값이 식별되거나, 새로운 데이터 소스가 추가되면서 점차 발생하게 되는데, 여기에 주의를 기울이지 않으면 중간 파일 생성, join, 샘플링 등으로 점철된 데이터 전처리 정글을 만들게 됩니다.

데이터 파이프라인들을 관리하고, 에러를 감지하고, 장애로부터 복구하는 작업은 모두 어렵고 비용이 많이 듭니다. 파이프라인들을 테스트할 때에도 보통은 비용이 많이 드는 통합 테스트까지 필요로 하는 경우가 많습니다. 이런 모든 것들이 기술적인 부채를 쌓고, 혁신을 만드는 비용을 더 크게 만듭니다.

데이터를 수집하고 feature 를 추출하는 데 전체적으로 잘 생각해야만 Pipeline jungles 을 피할 수 있습니다. 파이프라인을 재설계하고 백지상태에서 출발하는 방식은 실제로 현재 발생중인 비용을 줄이고, 혁신을 가속화하는데 효과적인 방식입니다.

Glue code 와 pipeline jungle 현상은 research 와 engineering 의 역할이 분리되어 있다는 점이 근본 원인일 수 있습니다. Researcher 와 engineer 들이 같은 팀에서 하이브리드로 연구하는 방식은 이러한 문제를 해결하는데 도움이 될 수 있습니다.

Dead Experimental Codepaths

일반적으로 Glue code나 pipeline jungle 로 인해서 발생하는 결과는 단기적으로 main production code의 조건분 분기로 실험 코드를 구현하여 실험을 진행하게 되는 것입니다. 어떤 개별 변동사항에 대해 이러한 접근방식은 전체 인프라를 건드리지 않기 때문에 상대적으로 적은 비용이 듭니다. 그렇지만, 시간이 흐르면서 이러한 분기 코드들은 이전 버전과의 호환성을 유지하는데 어려움을 증가시키거나, cyclomatic complexity 를 기하급수적으로 증가시킵니다.

일반적인 소프트웨어 개발의 dead flag 와 같이 주기적으로 이러한 실험용 branch 들을 제거해야합니다. 실험들 중 매우 적은 실험 셋들만 실제로 사용되고, 대부분은 한번 테스트하고 버려지게 됩니다.

단기적으로 실험한 코드 branch들 째깍째깍 잘 정리해야함.

Abstraction Debt

위에서 언급한 이슈들은 ML system 을 지원하기 위한 강력한 abstraction 이 없다는 사실을 강조하고 있습니다. 데이터의 흐름이나 모델, 예측을 설명하기 위한 적합한 인터페이스는 무엇일까요?

논문에서 MR, Parameter-server 방식을 언급하긴 했지만, 특별한 해결책은 아직 없는 것 같음.

Common Smells

소프트웨어 엔지니어링에서 흔히 특정 component 나 시스템에 존재하는 문제를 smell 이라고 표현합니다. 본 논문에서는 몇 가지 ML system 에서의 smell 들을 정의했습니다.

  • Plain-Old-Data Type Smell: ML system에서 사용되고 생산되는 rich 정보들은 raw float 이나 interger와 같은 타입으로 인코딩되어 있습니다. 모델에서 이 정보가 의미하는 것이 무엇인지 (decision threshold 인지, log 승수인지) 명확하게 알아야하고, 데이터가 어떻게 생산되고 소비되어야하는지 알아야합니다.

  • Multiple-Language Smell: 특정 프로그래밍 언어가 특정 라이브러리나 구문을 사용하기 편리할 경우, 시스템의 한 부분만 다른 언어를 사용하려는 경우가 있습니다. 그러나 여러 개의 언어를 사용하는 것은 효과적으로 테스트 하는 비용을 증가시키고, service ownership을 넘기기 어렵게 만듭니다.

  • Prototype Smell: 새로운 아이디어를 테스트 하기위해 작은 스케일로 프로토타이핑하는 것은 편리합니다. 그렇지만 프로토타이핑 환경에 자주 의존하는 것은 full-scale 시스템이 변화에 대응하기 어렵고, 더 나은 추상화나 인터페이스가 필요하다는 것을 나타내는 지표일 수 있습니다. 프로토타이핑을 위한 환경을 유지하는 것은 비용이 들고, 시간의 압박에 쫓겨 프로토타이핑 시스템을 production 시스템에 적용하려는 시도를 부추길 수도 있습니다. 게다가 작은 스케일에서의 실험은 실제 세계의 현실을 제대로 반영하지 못하는 경우가 많습니다.

6. Configuration Debt

기술 부채가 축적될 수 있는 또 하나의 영역은 ML system 의 configuration 입니다. 대부분 대규모의 시스템에서 다양한 범위의 설정가능한 옵션 값들이 존재합니다. 이러한 설정값들은 특정 feature들에 대한 사용 여부, 데이터를 어떻게 선택할 지, 다양한 특정 알고리즘에 특화된 설정값들, 전처리 또는 후처리 등에 대한 값들을 포함합니다.

연구자들이나 엔지니어 모두 이런 설정값을 관리하는 것은 보통 나중일로 생각하는 경향이 있고, 설정값을 검증하거나 테스트하는 작업은 중요하지 않게 보일 수도 있습니다. 활발하게 개발되고 있는 성숙한 시스템에서는 설정을 위한 코드의 줄 수가, 실제 구동하는 코드의 줄 수보다 훨씬 많은 경우도 있습니다. 각각의 설정 코드들에는 실수들이 잠재적으로 포함되어 있을 수 있습니다.

아래는 논문에서 제시하는 좋은 configuration system 에 대한 원칙들 입니다.

  • 설정 값을 이전 설정값에서 부터 약간의 변경만으로 쉽게 지정할 수 있어야 합니다.
  • 수동적인 에러, 생략, 누락을 만들기 어려운 구조여야합니다.
  • 두 모델 간 설정값 차이를 쉽게 시각적으로 볼 수 있어야 합니다.
  • 설정값이 자동으로 assert 되고, 기본적인 값들 (사용하는 feature의 개수, transitive closure of data dependencies 등)이 자동으로 검증되어야 합니다.
  • 사용되지 않는 불필요한 설정들을 감지할 수 있어야 합니다.
  • 설정값은 전체 코드 리뷰 과정에서 리뷰되고, repository 에 merge되어야 합니다.

7. Dealing with Changes in the External World

ML system 이 매력적인 한 가지 이유는 외부 세계와 직접적으로 상호작용한다는 것입니다. 경험적으로, 외부 세계는 안정적인 경우가 매우 드물기 때문에, 외부 환경에 대한 변화로 인한 유지보수 비용이 지속적으로 발생합니다.

Fixed Thresholds in Dynamic Systems.

ML system에서 스팸 메일인지, 정상 메일인지 예측하여 표시하거나, 특정 광고를 유저에게 표시할 지 말지 등 특정 ML 모델이 어떤 동작을 수행하게 하기 위해 decision threshold 값 를 선택하는 경우가 많습니다. 한 가지 전통적인 방법은 가능한 임계값 범위 내에서 precision / recall 과 같은 tradeoffs 를 고려하여 하나를 택하는 방법이 있습니다.

그런데 이러한 임계값들을 대부분 수동적으로 설정하는 경우가 많습니다. 만약 모델이 새로운 데이터를 입력으로 받게되는 경우, 기존의 수동적으로 설정한 임계값은 유효하지 않을 수 있습니다. 또한, 많은 모델들의 임계값들을 수동으로 변경하는 일은 시간이 오래걸리고 다루기 힘든 작업입니다.

이러한 문제를 해결하기 위한 한 가지 전략은, 검증 데이터 셋을 통해 임계값을 학습하게 하는 방식 을 사용할 수 있습니다.

Monitoring and Testing.

동작하고 있는 시스템에서 개별 component 들에 대해 unit test 및 end-to-end 테스트를 수행하는 것은 중요한 작업이지만, 변화하는 실 세계 환경에서 이러한 테스트들은 해당 시스템이 의도한 대로 잘 동작하고 있다는 것을 증명하기에는 불충분할 수 있습니다.

장기적인 시스템 안정성에 대한 관점에서 자동화된 실시간 모니터링은 매우 중요합니다. 중요한 것은 무엇을 모니터링 할 것인가? 입니다. ML system 에서는 테스트 해야하는 값이 명확하게 주어지지 않는 경우가 많습니다. 본 논문에서는 모니터링 해야하는 시작점으로 아래의 요소들에 대해 제시합니다.

  • Prediction Bias: ML system 이 의도한 대로 잘 동작하고 있다면, 시스템에서 예측하는 label 의 분포가 관측한 label 의 분포와 동일해야합니다. Prediction bias 를 측정하는 방안은 갑작스럽게 변경되는 외부의 변화를 감지하는데 유용하게 쓸 수 있습니다. 다양한 차원에서 prediction bias를 나눔으로써, 이슈를 빠르게 분리하고, 자동화된 알림으로 사용할 수 있습니다.

  • Action Limits: ML system 에서 스팸 문자를 마킹하는 등의 특정 동작을 수행하는 경우, 동작에 대한 limit을 설정하는 방법은 sanity check 로 유용합니다. 만약 시스템에서 특정 동작에 대한 limit 을 넘어서는 경우, 자동 알림을 보내서 수동적으로 검사해볼 수 있습니다.

  • Up-Stream Producers: ML system에서 입력 데이터를 다양한 up-stream producer 들로 부터 수급받게 되는 경우가 많습니다. 이러한 up-stream process 들은 ML system의 요구사항을 포함한 서비스 레벨의 목적을 달성할 수 있도록 철저하게 모니터링되고 테스트되어야 합니다. 이러한 up-stream 들의 failure에 대한 alert 또한 반드시 ML system의 control 전략에 포함되어야하고, 비슷하게 ML system 에서의 failure 역시 down-stream consumer 들에게 전파되어 down-stream consumer 들의 control 전략에 포함되어야 합니다.

외부의 변화는 실시간으로 일어나기 때문에, 이에 대한 반응 또한 실시간으로 발생합니다. 사람이 직접 개입해서 대응하는 것이 보통이지만, 빠르게 해결되어야하는 문제에 대해서는 취약할 수 있습니다. 사람이 직접 개입하지 않고 자동으로 변화에 대해 대응할 수 있는 시스템을 만드는 것은 투자할만한 가치가 있습니다.

그런 시스템을 만드는 게 가능한가? 단순한 조치는 취할 수 있을 듯

그 밖에 ML 관련 기술 부채들에 대한 내용입니다.

Data Testing Debt.

데이터가 ML system 의 코드를 변경시키는 경우, 해당 코드에 대해서 반드시 테스트하고, 입력 데이터 또한 테스트하는 것은 매우 당연합니다. 기본적인 sanity 체크 및 입력 값의 분포의 변화에 대해 모니터링하기 위한 잘 설계된 테스트 가 유용하게 쓰일 수 있습니다.

Reproducibility Debt.

Scientist 로써, 실험을 다시 수행하고 비슷한 결과물을 얻는 것은 중요합니다. 그러나 real-world ML system 에서 Randomized 알고리즘의 사용, 초기 조건에 따른 의존성 등의 문제로 strict reproducibility 를 가지도록 설계하는 것은 더욱 더 어렵습니다.

Process Management Debt.

이 논문에 소개된 대부분의 사례들은 한 가지 모델을 유지보수 하기 위한 비용에 대해 말하고 있습니다. 그러나 성숙한 시스템의 경우는 수 십, 수 백개의 모델들이 동시에 실행됩니다. 이런 성숙한 시스템의 경우, 많은 비슷한 모델들의 congifuration 값들을 안전하게 자동적으로 변경하는 문제, 비즈니스 우선순위가 다른 모델등 사이에서 리소스를 할당하고 조율하는 문제, production 데이터 파이프라인의 흐름을 시각화하고 blocker 들을 감지하는 문제 등 넓은 범위에 걸쳐 해결해야 할 다양하고 중요한 문제들이 있습니다. 또한, 장애 시 복구를 위한 tool을 개발하는 것도 매우 중요합니다.

Cultural Debt.

ML research 와 engineering 사이에 큰 장벽이 있는 경우, 이러한 장벽은 장기적인 관점에서 생산성을 저하시킬 수 있습니다. 팀 차원에서 쓸모없는 feature 의 개수를 줄이고, 시스템의 복잡도를 낮추고, 재현성과 안정성을 개선하고, 시스템을 모니터링하는 것에 대해 보상하는 문화를 만드는 것이 중요 합니다.

9. Conclusions: Measuring Debt and Paying it Off

기술 부채는 유용한 표현이지만, 기술적 부채에 대해 측정할 수 있는 구체적인 metric 까지는 제시하고 있지 않습니다. 다음과 같은 질문들을 통해 기술적 부채가 얼마나 존재하는지 대략적으로 파악해 볼 수 있습니다.

  • 새로운 알고리즘적 접근이 얼마나 쉽게 full scale 에서 테스트 가능한가?
  • What is the transitive closure of all data dependencies? (무슨 말인지 모르겠음)
  • 시스템에 적용되는 새로운 변화에 대한 임팩트를 얼마나 정확하게 측정할 수 있는지?
  • 특정 모델이나 입력값에 대한 개선이 다른 것들의 성능을 저하시키는지?
  • 새로운 팀 멤버가 얼마나 빨리 업무에 적응할 수 있는가?

이 논문을 통해 유지보수 가능한 ML 시스템 개발을 위한 더 나은 추상화, 테스팅 기법, 디자인 패턴들이 나오길 바라고, 논문에서 제시하는 가장 중요한 시사점은 engineer 와 researcher 모두 기술 부채에 대해 경계하고 고민해야한다는 점 입니다.

정확도(accuracy)에서 작은 개선점을 얻을 수 있는 특정 research 솔루션이 전체 시스템의 복잡도을 매우 높이는 경우는 좋은 practice 가 될 수 없습니다.

ML 관련된 기술 부채를 줄이기 위해서는 팀 문화를 바꾸는 차원의 특별한 노력이 필요합니다. 장기적으로 성공적인 ML팀이 되기 위해 이러한 노력에 대해 인식하고 우선순위화해서 업무를 진행하고, 이에 대해 보상하는 것이 중요합니다.