한국의 개발자들을 위한 Google for Developers 국문 블로그입니다.
TensorFlow 2.0에서 기호형 API와 명령형 API란?
2019년 5월 29일 수요일
게시자:
Josh Gordon
<블로그 원문은
이곳
에서 확인하실 수 있습니다>
필자가 TensorFlow 2.0에서 가장 좋아하는 것 중 하나는 TensorFlow 2.0이 여러 가지 수준의 추상화를 제공하므로 자신의 프로젝트에 적합한 추상화를 선택할 수 있다는 점입니다. 이 게시물에서는 신경망을 만드는 데 사용할 수 있는 두 가지 스타일 사이의 절충점에 대해 설명하겠습니다. 첫 번째는 계층의 그래프를 조작하여 모델을 빌드하는 기호형 스타일입니다. 두 번째는 클래스를 확장하여 모델을 빌드하는 명령형 스타일입니다. 이 두 가지 스타일을 소개하고 중요한 디자인 및 사용성 고려 사항에 관해 특기할 점을 공유하고 알맞은 스타일의 선택에 도움이 되는 간단한 권장 사항을 제시하는 것으로 마무리하겠습니다.
기호형 (또는 선언형) API
우리가 신경망을 생각할 때 흔히 사용하는 정신 모델은 아래 이미지에 묘사되어 있는 것과 같은 '계층의 그래프'입니다.
우리가 신경망을 생각할 때 흔히 사용하는 정신 모델은 계층의 그래프입니다(이미지는
Inception-ResNet
에 대한 스키마임).
이 그래프는 왼쪽에 표시된 DAG이거나 오른쪽에 표시된 스택일 수 있습니다. 모델을 기호로 빌드할 때는 이 그래프의 구조를 기술하는 방식으로 빌드합니다.
이 말이 기술적인 얘기로 들리세요? Keras를 사용해본 적이 있는 분이라면 이미 이런 경험이 있다는 사실을 알고서는 놀라실지 모르겠습니다. 다음은
Keras
Sequential API를 사용하여 모델을 기호 방식으로 빌드하는 간단한 예시입니다.
Keras Sequential API를 사용하여 기호 방식으로 빌드한 신경망. 이 예시는
여기
에서 실행할 수 있습니다.
위 예시에서, 우리는 계층 스택을 정의한 다음 내장된 훈련 루프인
model.fit
을 사용하여 계층 스택을 훈련했습니다.
Keras를 사용한 모델 빌드는 '레고 블록을 맞춰 끼우는 것'만큼이나 쉽게 느껴질 수 있습니다. 왜냐고요? 정신 모델과의 매칭 외에, 이후에 다룰 기술적 이유 때문에 이런 식으로 빌드한 모델은 프레임워크에서 제공되는 신중한 오류 메시지 덕분에 디버그하기 쉽습니다.
위의 코드로 만든 모델을 보여주는 그래프(
plot_model
로 빌드 시 이 게시물의 다음 예시에서 재사용할 수 있는 코드 스니펫이 있음).
TensorFlow 2.0은 또 다른 기호 모델을 빌드하는 API인 Keras Functional을 제공합니다. Sequential은 스택용이고, 아마 짐작하셨겠지만 Functional은 DAG용입니다.
Functional API를 사용하여 다중 입력/다중 출력 모델을 만드는 간단한
예시
.
Functional API는 더욱 유연한 모델을 만들기 위한 수단입니다. 이 API는 비선형 토폴로지, 공유 계층을 포함한 모델, 다중 입력 또는 출력을 포함한 모델을 처리할 수 있습니다. 기본적으로, Functional API는 이러한 계층 그래프를 빌드하기 위한 도구 세트입니다. 우리는 현재 개발자를 위해 이 스타일로 두어 가지 새로운 튜토리얼을 만들고 있습니다.
이미 사용 경험이 있으실지도 모르는 다른 기호형 API도 있습니다. 예를 들어 TensorFlow v1(그리고 Theano)은 훨씬 낮은 수준의 API를 제공했습니다. 컴파일하고 실행하는 연산의 그래프를 만들어 모델을 빌드했을 것입니다. 때때로 이 API를 사용하는 것은 컴파일러와 직접 상호 작용하는 듯한 느낌이 들 수도 있습니다. (저자를 포함한) 많은 사람들에게는 사용하기 어려운 API였습니다.
반대로, Keras에서는 추상화 수준이 정신 모델과 일치하므로 마치 레고 블록처럼 끼워 맞춘 계층의 그래프가 생성됩니다. 이는 사용하기에 자연스러운 느낌이 들고, 우리가 TensorFlow 2.0에서 표준화하고 있는 모델 빌드 접근 방식 중 하나입니다. 설명해드릴 다른 API가 있습니다. (이 API 역시 사용해봤거나 곧 사용해볼 가능성이 높은 API입니다.)
명령형 (또는 모델 하위 클래스) API
명령형 스타일에서는 NumPy를 작성하는 것처럼 모델을 작성합니다. 이 스타일로 모델을 빌드하는 것은 마치 객체 지향 Python 개발과 같은 느낌이 듭니다. 다음은 하위 클래스형 모델의 간단한 예시입니다.
명령형 스타일을 사용하여
주의를 포함한 이미지 캡션
을 위한 모델 빌드(참고: 예시는 현재 업데이트 중임).
개발자 관점에서 볼 때, 이 API는 프레임워크에 의해 정의된 Model 클래스를 확장하고 계층을 인스턴스화한 다음 모델의 순방향 전달을 명령형으로 작성하는 방식으로 작동합니다(역방향 전달은 자동으로 생성됨).
TensorFlow 2.0은 Keras
Subclassing
API를 사용하여 별도의 구성 없이 이를 지원합니다. Sequential API 및 Functional API와 함께, 이는 TensorFlow 2.0에서 권장되는 모델 개발 방법 중 하나입니다.
이는 TensorFlow에는 새로운 스타일이지만, 2015년에 이미
Chainer
에 도입되었던 스타일이라는 사실을 아시면 놀라실지 모르겠습니다. 세월은 참 빠르게도 흐릅니다. 그 이후로, 많은 프레임워크에서 Gluon, PyTorch, TensorFlow(Keras Subclassing 포함) 등을 비롯하여 유사한 접근 방식을 채택했습니다. 놀랍게도, 다양한 프레임워크에서 이 스타일로 작성된 코드가 서로 너무나 비슷해 보여
구분
하기 어려울 수 있습니다!
이 스타일을 사용하면 뛰어난 유연성을 누릴 수 있지만, 명확하지 않은 사용성 및 유지 관리 비용이 수반된다는 단점이 있습니다. 그에 대해 좀 더 자세히 알아봅시다.
훈련 루프
Sequential, Functional 또는 Subclassing 스타일 중 하나로 정의된 모델은 두 가지 방식으로 훈련할 수 있습니다. 내장된 훈련 루틴과 손실 함수를 사용하거나(model.fit과 model.compile을 사용하는 첫 번째 예시 참조), 맞춤 훈련 루프(예: 자체적인 그래디언트 클리핑 코드를 작성하고 싶은 경우) 또는 손실 함수의 복잡성을 추가할 필요가 있는 경우에는 다음과 같이 손쉽게 그렇게 할 수 있습니다.
위 코드는
Pix2Pix
에 대한 맞춤 훈련 루프와 손실 함수의 예입니다.
사용 가능한 이런 접근 방식을 둘 다 가지는 것이 중요하며, 그러면 간편하게 코드 복잡성과 유지 관리 비용을 줄일 수 있습니다. 기본적으로, 복잡성을 더하는 게 도움이 되면 이를 사용하고, 불필요하면 내장 메서드를 사용하고 남은 시간을 연구나 프로젝트에 활용할 수 있습니다.
이제 기호형 스타일과 명령형 스타일에 대한 감을 잡으셨을 것이니, 절충점을 살펴봅시다.
기호형 API의 이점과 제한 사항
이점
기호형 API를 사용한 모델은 그래프와 같은 데이터 구조입니다. 즉, 모델을 검사하거나 요약할 수 있다는 뜻입니다.
모델을 이미지로 플로팅하여 그래프를 표시하거나(
keras.utils.plot_model
사용) 단순히
model.summary()
를 사용하여 계층, 가중치 및 모양에 대한 설명을 볼 수 있습니다.
마찬가지로, 계층을 함께 끼워 맞출 때 라이브러리 디자이너는 (모델을 빌드하는 동안, 그리고 실행 이전에) 광범위한 계층 호환성 검사를 실행할 수 있습니다.
이는 컴파일러의 유형 검사와 비슷하며, 개발자 오류를 크게 줄여줄 수 있습니다.
모델 실행 단계가 아니라 모델 정의 단계 도중에 대부분의 디버깅이 이루어집니다. 컴파일하는 어떤 모델이든 실행을 보장합니다. 따라서 더 빠르게 반복하고 더 쉽게 디버깅할 수 있습니다.
기호 모델은 일관된 API를 제공합니다. 따라서 간단히 재사용하고 공유할 수 있습니다. 예를 들어 전이 학습에서는 중간 계층 활성화에 액세스하여 다음과 같이 기존 모델로부터 새 모델을 빌드할 수 있습니다.
from tensorflow.keras.applications.vgg19 import VGG19
base = VGG19(weights=’imagenet’)
model = Model(inputs=base.input,
outputs=base_model.get_layer(‘block4_pool’).output)
image = load(‘elephant.png’)
block4_pool_features = model.predict(image)
기호 모델은 모델을 자연스럽게 복사하거나 복제할 수 있게 해주는 데이터 구조로 정의됩니다.
예를 들어 Sequential API와 Functional API는 (모델 정의 및 훈련에 사용되는 원본 코드에 액세스하지 않고) 단지 데이터 구조로부터 같은 모델을 다시 생성하는 기능과 함께
model.get_config()
,
model.to_json()
,
model.save()
,
clone_model(model)
을 제공합니다.
제대로 설계된 API는 신경망을 위한 정신 모델과 일치해야겠지만, 프로그래머로서 우리가 가지고 있는 정신 모델과 일치하는 것도 똑같이 중요합니다. 우리 중 다수에게 그것은 명령형 프로그래밍 스타일입니다. 기호형 API에서는 '기호형 텐서'(아직 어떤 값도 가지고 있지 않은 텐서)를 조작하여 그래프를 빌드합니다. Keras Sequential API와 Functional API는 명령형 API와 같은 '느낌'입니다. 이들 API는 많은 개발자가 모델을 기호로 정의해왔음을 실감하지 못하도록 설계되었습니다.
제한 사항
현 세대의 기호형 AP는 계층의 비순환식 그래프로 방향이 설정된 모델 개발에 가장 적합합니다. 예컨대 트리-RNN과 같은 동적 네트워크와 재귀 네트워크 등, 이 깔끔한 추상화에 맞지 않는 몇몇 특수한 사용 사례도 있지만, 대부분의 사용 사례는 실제로 이에 해당합니다.
그것이 바로 TensorFlow가 명령형 모델 빌드 API 스타일(위에 표시된 Keras Subclassing)도 제공하는 이유입니다. Sequential API와 Functional API에서 친숙한 계층, 이니셜라이저 및 옵티마이저를 전부 사용하게 됩니다. 이 두 가지 스타일은 완전히 상호 운용 가능하기도 하므로 혼합하고 매칭할 수 있습니다(예: 한 모델 유형을 다른 모델 유형에 중첩할 수 있음). 기호형 모델을 택해 하위 클래스형 모델에서 계층으로 사용하거나 그 반대로 합니다.
명령형 API의 이점과 제한 사항
이점
순방향 전달은 명령형으로 작성되므로 자체 구현으로 라이브러리에 의해 구현된 부분(예: 계층, 활성화 또는 손실 함수)을 쉽게 바꿀 수 있습니다. 이는 프로그래밍하기에 자연스러운 느낌이며, 딥 러닝의 기본을 더 깊이 파고들 수 있는 훌륭한 방법입니다.
이는 새로운 아이디어를 빠르게 시험하기 용이하게 해주므로(DL 개발 워크플로가 객체 지향 Python과 같아짐), 연구자에게 특히 도움이 됩니다.
Python을 사용하여 모델의 순방향 전달 내에서 임의의 제어 흐름을 지정하기도 쉽습니다.
명령형 API는 최대의 유연성을 제공하지만 비용이 듭니다. 필자는 이 스타일로 코드를 작성하는 것도 좋아하지만, 잠시 제한 사항을 분명히 밝히고 싶습니다(절충점을 인식하는 것이 좋음).
제한 사항
명령형 API를 사용할 때 클래스 메서드의 본문에서 모델이 정의된다는 점이 중요합니다. 모델은 더 이상 투명한 데이터 구조가 아니라 불투명한 바이트코드 조각입니다. 이 스타일을 사용할 때는 유연성 확보를 위해 사용성 및 재사용성이 그만큼 저하됩니다.
모델을 정의할 때와는 반대로 실행 중에 디버깅이 이루어집니다.
입력 또는 계층 간 호환성에 대해 실행되는 검사는 거의 없으므로, 이 스타일을 사용할 때는 디버깅 부담이 대부분 프레임워크에서 개발자에게 넘어갑니다.
명령형 모델은 재사용하기 더 어려울 수 있습니다. 예를 들어 일치하는 API로 중간 계층이나 활성화에 액세스할 수 없습니다.
그 대신에 새로운 호출(또는 전달) 메서드로 새로운 클래스를 작성하여 활성화를 추출할 수 있습니다. 처음에는 이렇게 작성하는 것인 재미있을 수 있지만, 명확한 기준이 없으면 기술적 부채의 전형이 될 수 있습니다.
명령형 모드는 또한 검사, 복사 또는 복제가 더 어렵기도 합니다.
예를 들어,
model.save()
,
model.get_config()
,
clone_model
은 하위 클래스형 모델에는 효과가 없습니다. 마찬가지로,
model.summary()
는 계층의 목록만 제공합니다. 그리고 액세스할 수 없으므로 계층의 연결 방식에 관한 정보는 제공하지 않습니다.
ML 시스템의 기술적 부채
모델 빌드는 실제로는 머신러닝을 이용한 작업의 아주 작은 부분에 불과하다는 점을 기억하는 것이 중요합니다. 다음은 이 주제에 관해 필자가 즐겨 쓰는 그림입니다. 모델 자체(계층, 훈련 루프 등을 지정하는 코드 부분)는 중간에 있는 작은 상자입니다.
중간에 있는 작고 검은 상자로 표시된 것처럼, 실제 ML 시스템의 작은 부분만이 ML 코드로 구성됩니다. 출처:
머신러닝 시스템에 숨겨진 기술적 부채(Hidden Technical Debt in Machine Learning Systems)
.
기호로 정의되는 모델은 재사용성, 디버깅 및 테스트에 장점이 있습니다. 예를 들어 필자는 강의할 때 학생이 Sequential API를 사용 중인 경우 학생의 코드를 즉시 디버그할 수 있습니다. 하지만 (프레임워크와는 무관하게) 학생이 하위 클래스형 모델을 사용 중일 때는 시간이 더 오래 걸립니다(버그가 더 교묘하고 유형도 다양할 수 있음).
결론
TensorFlow 2.0은 별도의 구성 없이 이런 스타일을 둘 다 지원하므로 프로젝트에 적합한 추상화 및 복잡성 수준을 선택할 수 있습니다.
사용 편의성과 낮은 개념적 오버헤드를 목표로 하고 자신의 모델을 계층 그래프로 생각하고 싶다면 (마치 레고 블록을 끼워 맞추는 것과 같은) Keras Sequential API 또는 Functional API와 내장된 훈련 루프를 사용하세요. 이는 대부분의 문제에 들어맞는 적당한 방법입니다.
객체 지향 Python/Numpy 개발자로서 모델을 생각하고 싶고 유연성과 해킹 가능성을 우선한다면 Keras Subclassing이 적합한 API입니다.
이 글이 개요를 파악하는 데 도움이 되었기를 바라며 읽어주셔서 감사합니다! TensorFlow 2.0 스택과 이러한 모델 빌드 API 이상의 주제에 관한 자세한 내용을 보려면 이
게시물
을 참조하세요. TensorFlow와 Keras 사이의 관계에 대해 자세히 알아보려면
여기
를 참조하세요.
Contents
ML/Tensorflow
Android
Flutter
Web/Chrome
Cloud
Google Play
Community
Game
Firebase
검색
Tag
인디게임페스티벌
정책 세미나
창구프로그램
AdMob
AI
Android
Android 12
Android 12L
Android 13
Android 14
Android Assistant
Android Auto
Android Games
Android Jetpack
Android Machine Learning
Android Privacy
Android Studio
Android TV
Android Wear
App Bundle
bootcamp
Business
Chrome
Cloud
Community
compose
Firebase
Flutter
Foldables
Game
gdg
GDSC
google
Google Developer Student Clubs
Google Play
Google Play Games
Interview
Jetpack
Jetpack Compose
kotlin
Large Screens
Library
ma
Material Design
Material You
ML/Tensorflow
mobile games
Now in Android
PC
Play Console
Policy
priva
wa
wear
Wearables
Web
Web/Chrome
Weeklyupdates
WorkManager
Archive
2024
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2023
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2022
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2021
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2020
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2019
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2018
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2017
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2016
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2015
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2014
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2013
12월
11월
10월
9월
8월
7월
6월
5월
4월
3월
2월
1월
2012
12월
11월
10월
9월
8월
7월
6월
5월
3월
2월
1월
2011
12월
11월
Feed