개발 환경의 주요 요소인 container와 Docker 개념을 정리해보자

 

이론적으로는 공부를 여러번 했지만 아직 클라우드 인프라와 MLOps 개발 경험이 없어서

오개념이나 설명이 부족할 수 있지만 그건 차차 보완하면 되겠지 ㅎㅎ

 

 

[1] 컨테이너가 필요한 이유

(https://azure.microsoft.com/ko-kr/overview/what-is-a-container/#why-containers)

소프트웨어 서비스는 다양한 조합의 개발환경에 종속적으로 실행되기때문에, 개발환경과 실행환경이 다를 경우 정상적으로 실행되기 어렵다. (“it works on my machine” 문제라고 부른다)

이 때문에 어플리케이션 개발코드를 구성 파일, 라이브러리, 종속성 등과 함께 패키징하여 제공하는 것을 컨테이너화(containerization)이라고 하며, 패키징된 번들을 컨테이너 이미지 라고 부른다.

컨테이너를 이용하면 어플리케이션이 컨테이너 이미지를 기본 단위로 테스트되고 배포되어 개발환경과 실행환경이 충돌하는 문제를 극복할 수 있다.

 

컨테이너를 이용하여 얻을 수 있는 장점을 3가지로 표현하면

- 민첩성: 개발자가 어플리케이션을 패키징하여, 배포와 운영에 필요한 추가 노력이 줄어들고 빠르게 서비스할 수 있음

- 이식성: 모든 환경에서 작동하도록 서비스를 제공하므로 머신/OS플랫폼/클라우드 간 이식(migration)이 가능함

- 신속한 확장성: 같은 인프라 자원에서 가상머신에 비해 많은 컨테이너를 지원할 수 있고, 경량화된 특성으로 빠르게 시작하거나 중지할 수 있어 신속하게 이용 가능함

 

 

 

[2] 가상머신(VM)와 컨테이너 비교

컨테이너와 유사한 형태인 가상머신(virtual machine)과 비교하여 두 개념의 차이는 다음과 같다

(https://docs.microsoft.com/ko-kr/virtualization/windowscontainers/about/containers-vs-vm)

컨테이너의 개념 그림
가상머신의 개념 그림

 

가상머신은 어플리케이션마다 커널과 운영체제(OS)를 포함한 구성을 실행하는 반면,

컨테이너는 호스트 운영체제와 커널 위에서 어플리케이션, 서비스 등만 포함하여 구분 실행한다.

(*커널이란 하드웨어의 주요 기능을 제어하는 인터페이스이다 [설명 LINK] )

 

그래서 가상머신을 이용할 때는 어플리케이션 이용을 위해 운영체제를 완전히 실행해야하지만,

컨테이너를 이용할때는 전체 리소스 중 일부만을 사용하도록 조정하여 리소스 사용을 줄일 수있다.

따라서 격리화된 컨테이너의 이용 공간은 VM이미지에 비해 매우 작다.

 

 

 

[3] Docker 란?

Docker란 컨테이너를 위한 운영체제를 제공하는 오픈소스 플랫폼이다. 초기엔 Linux 에서 호환되는 방식이었지만, 현재는 윈도우 서버나 Azure, AWS 같은 클라우드 서비스에서 이용할 수 있다.

https://www.docker.com/resources/what-container

가상머신이 하드웨어를 가상화하는 것과 유사하게, 컨테이너에서는 운영체제를 가상화한다. 이 때 각 서버에는 Docker Engine이 설치되어 컨테이너를 구축, 시작, 중단하기위한 명령 세트를 제공한다.

(*Hypervisor란 VMM이라고도 불리며 VM을 생성하고 구동하는 소프트웨어이다. 하이퍼바이저를 통해 하드웨어 리소스를 공유하고 여러 운영체계를 구동하는 것이 가상머신에서의 가상화 개념이다 [설명 LINK] )

 

 

Docker가 실제로 작동되는 workflow를 간단하게 설명하면 아래와 같다

(출처: https://www.slideshare.net/vincenzoferme/using-docker-containers-to-improve-reproducibility-in-software-and-web-engineering )

 

- Docker 파일은 컨테이너의 인프라와 종속성 등을 정의하는 문서

- Docker 이미지는 컨테이너를 정의하는 읽기 전용 템플릿으로, 라이브러리 및 종속성 코드를 포함하며 변경불가능(immutable)하다

- Docker 컨테이너는 인스턴스화된(실행되는) Docker 이미지이다

 

즉, 개발 단계에서 Docker파일을 Docker 이미지로 빌드하고, Docker이미지를 Pull & Push 할 수 있다.

 

 

 

 

 

(+)

실제 기업에서는 AWS 나 Azure 와 같은 클라우드 환경에서 개발을 관리하는 경우가 일반적이므로 클라우드에서 제공하는 Docker 서비스를 참고하여 익히면 효과적일 듯 하다

AWS Docker : https://aws.amazon.com/ko/docker/ 

남세동 대표의 딥러닝 질문리스트 중 마지막으로 다뤄볼 내용은 optimizer의 momentum과 ADAM 알고리즘이다

(그 외 내용은 다른방식으로 다루었거나 다룰 예정)

 

Adam은 입문단계에서 묻지도따지지도말고 국룰이라 momentum과 Adam은 많이들 익숙한 내용일 것이다

Adam 논문은 2022년 현재 피인용수 10만 무렵이지만,

너무 trivial 하여 인용하지않았거나 논문으로 발간되지 않은 사용사례가 그 몇배는 될 것으로 예상한다

 

Andrew Ng 님의 강의 Gradient Descent With Momentum (C2W2L06) 를 기초로

수식과 함께 정확하게 정리해보자

 

 

6. Optimizer에서 momentum은 무엇인가요? 

a. momentum을 수식으로 적어본다면 어떻게 되나요? 

 

기본적인 gradient descent 에서는 optimizer(최적화 알고리즘) 에 의해 모델의 오차가 줄어들도록 설정한다

이 때, learning rate가 작으면 학습이 너무 오래걸리거나 local minima에 빠질 수 있고,

learning rate가 크면 학습이 overshooting 또는 diverge 되는 문제를 야기할 수 있다.

이를 극복하기 위해서 momentum은 gradient가 일관적인 방향으로 조금 더 빠른 최적화를 수행하도록하는 term이다

 

momentum의 수식을 이해하기 위해 먼저 Exponentially Weighted Moving Average (지수가중이동평균)의 개념을 짚고 넘어가자

데이터의 이동평균을 구하기 위해 현재 평균값과 새로 추가되는 데이터를 β만큼 가중치를 준다면

V(t) =  β*V(t-1) + (1- β)*θ(t) 로 표현할 수 있다.

다시말하면 β 값이 클수록 직전의 평균값에 더 큰 가중치를 주는 것이다.

이 때 V(t)≈ avg. of 1/(1-β) days 라는 성질을 가진다. 예를 들어 β=0.9라면 V(t)는 10일치의 평균과 유사하다.

더 긴 기간의 이동평균을 계산하므로 더 smooth 한 V(t) 그래프를 얻을 수 있다.

 

momentum 의 정의는 gradient의 지수가중이동평균을 구하는 것이다

위에서 적은 수식과 마찬가지로 weight 또는 bias 의 gradient 를 

V(dw) =  β*V(dw) + (1-β)*dw 로 정의하고

learning rate α를 적용해서 W=W-αV(dw) 로  모델을 업데이트할 수 있다.

 

Gradient Descent with Momentum 알고리즘의 수식

 

 

예를 들어 cost function이 아래 그림과 같다면

수직방향으로의 이동폭은 줄이는 동시에, 수평방향(오른쪽)으로의 이동폭을 늘여야 빠르고 정확하게 모델이 최적화된다.

momentum은 gradient의 이동평균을 이용해서 "이전에 가던 방향"으로의 학습을 빠르게 만들어 준다.

 

 

learning rate 와 momentum의 설정에 의한 모델 최적화 결과를 시각화하면 보다 직관적인 이해가 가능하다

( https://distill.pub/2017/momentum/ )

momentum을 이용하면 모델이 global minima와 거리가 먼 초기 단계에서는 학습속도를 빠르게 진행시키고

학습이 진행될수록 momentum이 작아지면서 정밀한 fine tuning이 가능해진다.

 

 

 

b. Adam은 어떻게 동작하나요?

Adam은 momentum 방식과, RMSProp(Tieleman & Hinton, 2012)을 결합한 최적화 알고리즘이다.

 

참고로 RMSProp는 제곱항과 제곱근을 이용해서 아래와 같이 계산하는 방식이다.

기본적인 원리는 모멘텀과 크게 다르지않다.

RMSProp 알고리즘의 수식

 

Adam논문에서 쓰인 알고리즘을 바로 보자

여기서 β1로 계산한 m_t 가 momentum 파라미터이고,

β2 와 제곱항으로 계산한 v_t가 RMSProp 에서 차용한 파라미터이다.

bias correction 은 크게 신경쓰지않아도 되고..

모델 업데이트 과정에서 momentum과 RMSProp의 업데이트 방식을 결합한 것을 볼 수 있따.

 

 

Adam은 이처럼 기존의 아이디어들을 개선한 결과물이지만

굉장히 좋은 성능으로 딥러닝 입문단계나 모델 개발과정에서 고민을 줄여주는 고마운 알고리즘이다

 

파이썬 컴프리헨션(comprehension) 문법은 

좋은 코드인 파이썬스러움(Pythonic)의 대표적인 문법 중 하나로 코드의 간결성과 성능을 모두 높일 수 있다.

 

리스트 컴프리헨션(List Comprehension)을 만들어보자

(참고자료)

제대로 파이썬 : https://wikidocs.net/22805 ,

티베트 모래여우님 블로그 : https://tibetsandfox.tistory.com/25?category=885924)

 

 

TASK : 1부터 10까지 정수를 포함하는 리스트를 만든다

(1) 리스트 컴프리헨션

list1 = [x for x in range(1,11)]

 

(2) list() 함수를 이용 

list2 = list(range(1,11))

 

(3) for 반복문 이용

list3 = []
for i in range(1, 11):
    list3.append(i)

 

예시처럼 간단한 경우에는 list()함수를 써도 좋지만, list()는 어디까지나 리스트 형식으로 반환하는 기본함수이므로 복잡한 데이터를 정의하기에 적합한 방식은 아니다.

for문은 여러 조건을 쉽게 추가할 수 있지만, 계산도 오래걸리고 코드를 길고 복잡하게 만드는 주범이니 역시 데이터가 커질수록 좋지않다.

 

 

TASK : 1부터 10까지 정수 중, {5보다 작은 수,짝수} 쌍을 포함하는 리스트를 만든다

list4 = [(x, y) for x in range(1, 11) for y in range(1, 11) if x < 5 if y % 2 == 0 ]

컴프리헨션 문법으로 코드를 간결하게 만드는 장점을 보여주는 좋은 예제이다.

이처럼 컴프리헨션에서는 for 과 if 를 연속해서 적어주는 것으로 조건을 중첩하여 적용할 수 있다.

중첩된 for문의 실행순서는 왼쪽부터, if문은 for문 다음에 적어준다는 규칙을 알아두자.

 

 

그리고 마지막으로 프로그래머스 코딩테스트 연습문제 중 'k번째 수' 예제에 적용해보자

TASK: 배열 array의 i번째 숫자부터 j번째 숫자까지 자르고 정렬했을 때, k번째에 있는 수를 구하려 합니다.

#부끄럽지만 내가 예전에 썼던 코드
def solution(array, commands):
    answer = []
    for i,j,k in commands:
        sliced = sorted(array[i-1:j])
        answer.append(sliced[k-1])
    return answer
#컴프리헨션을 이용한 리스트
def solution(array, commands):
    answer = [ sorted(array[i-1:j])[k-1] for (i, j, k) in commands ]
    return answer

이렇게 for 문을 리스트 컴프리헨션으로 적어주면 깔끔하게 정리가 가능하다.

 

다시보니 처음 짰던 코드에서는 sliced 라는 중간 데이터를 만든 것도

실험용 데이터 분석을 하다보니 더블체크를 하려고 생긴 습관인데

대규모 연산이나 상용프로그램을 짤때는 메모리 낭비가 발생하지 않도록 유의해야겠다.

남세동 대표님의 질문 시리즈 5번째는 Loss Surface에 관한 질문이다

우리말로는 손실평면 으로 표현하는 문헌도 있지만 대체로 원어를 그대로 사용하는듯하다.

Loss Surface는 시각화 결과니까 크게 신경쓰지않고 직관적으로 받아들이는 경우가 많은데 좀더 자세히 알아보자

 

5. Loss surface에서 z축은 무엇인가요? 

a. x축, y축은 무엇일까요? 

 

먼저 loss surface 라는 용어의 정의를 찾아보자

[loss surface]
A graph of weight(s) vs. loss. Gradient descent aims to find the weight(s) for which the loss surface is at a local minimum.
@ https://developers.google.com/machine-learning/glossary

너무 간단하다...ㅎㅎ

 

일단 질문에서 x,y,z축을 질문했으니 loss surface는 3D 평면을 의미한다고 생각하자.

 

Loss Surface는 머신러닝 모델의 파라미터 값에 따라 loss (또는 cost)의 변화를 시각화하는 표현방법이다.

잘알고있듯이 gradient descent 방법은 오차의 gradient를 최소화하는 방향으로 모델을 업데이트하므로,

오차를 2차원 곡선 또는 3차원 평면(Loss Surface)에 시각화하여 모델이 업데이트되는 경로를 쉽게 시각화 할 수 있다.

 

source:&amp;nbsp;https://medium.com/@DBCerigo/on-why-gradient-descent-is-even-needed-25160197a635

 

여기서 z축은 loss function 값이다.

따라서 현재 지점에서 z값이 작아지는 gradient 방향이 모델이 업데이트 되는 방향이다

 

반면 x축과 y축을 정하는 것은 조금 복잡하다.

모델의 파라미터에 따른 loss를 표현한다고 했는데 딥러닝에서 파라미터(=가중치)의 갯수는 적어도 수만개이고, GPT-3 같은 대규모 모델은 천억개가 넘는 파라미터를 가지고 있다.

따라서 시각화를 위해서 1개(2D) 내지는 2개(3D)의 변수를 선정해야한다.

 

이를 위해

Li et al.,(2018) "Visualizing the Loss Landscape of Neural Nets" @ NeurIPS 2018

논문에서 제안하는 방법으로는

 

현재까지 학습된 모델을 좌표상 원점인 지점 θ* 으로 설정하고,

weight space에서 선택한 랜덤벡터 δ(델타)와 η(에타) 와 좌표의 두 점 α,β에 대해서

f(α,β) = L( θ* + αδ + βη ) 으로 손실함수 값을 구한다.

그 결과, x=α, y=β, z= f(α,β)  로 3차원 평면을 투영할 수 있다.

 

여기서 궁금한점이 생긴다. 그냥 랜덤으로 x축과 y축 파라미터를 골랐다고?논문에서 설명한 방법을 응용하면서 자세한 설명을 덧붙인 Javier의 블로그 를 참고해봤다.1. 왜 PCA를 사용하지 않는가? > 꼭 PCA를 통해 최적화(optimized)된 벡터를 이용할 필요는 없다2. 2개의 랜덤벡터가 직교하여 평면을 만들 수 있다고 가정할 수 있는가?> space의 차원이 커질 수록, 서로 다른 두 벡터가 직교할 확률은 1에 충분히 가까운 것이 수학적으로 설명된다

 

 다시 말하면 이 방식으로 구한 loss surface는 high dimension 모델의 오차함수를 2차원으로 슬라이스한 것과 같다.

soruce:&amp;nbsp;https://towardsdatascience.com/loss-landscapes-and-the-blessing-of-dimensionality-46685e28e6a4

 

이런 이유로 보통 loss surfcae는 개념적 설명을 돕기위한 시각화 방법이지 정확한 표현이 아니므로 굳이 고해상도 그림을 만들 필요는 없다.

하지만 위 블로그를 만든 Javier 의 losslandscape 프로젝트는 loss surface의 해상도를 극한으로 끌어올려서

마치 어느 극한지형을 보는 듯한 예술작품을 만들었으니 잠시 감상해보자

 

https://www.youtube.com/watch?v=aq3oA6jSGro  

 

 

b. Loss surface가 불연속일 수도 있을까요?

이 질문은 자료를 찾아도 나올법한 질문이 아니라서 나름대로의 답안을 생각해봤다.

실제로 우리가 보는 figure는 임의의 해상도로 정의된 좌표에서 x=α, y=β, z= f(α,β) 로 나타냈으므로 당연히 불연속하다.

그러나 loss surface의 정의에 따르면 뉴럴넷 모델이 가지는 고차원의 loss function 그래프 중 3-D subspace 이다.

 

예를 들어 본래의 모델에서 첫번째 2가지 파라미터를 선택하여 loss surface를 만든다고 하면original dataset : p1=[1,2,0,0,0] p2=[1,2,5,5,5] p3=[1,2,7,7,7]original loss : L1=[0.3] , L2=[0.4] , L3=[0,1]

on surface: f(1,2) = SUM(L1,L2,L3) 의 꼴로 나올 것이다. (이부분 굉장히 자신없음)

 

이 경우에 본래의 loss function 이 (일반적으로 미분가능해야하므로) 연속하므로,

loss surface 역시 연속할 것이라고 생각한다.

 

사실 이 글의 제일 첫 단계에서 loss surface는 시각화를 위해 3D로 차원축소한 그래프로 정의했는데이 경우 loss surface의 함수적 특성에 대해 디테일하게 생각할 필요가 있을지 잘 모르겠다.

 

 

 

===

 

이번 편은 질문이나 자료가 많이 없는 주제라서, 답에 대한 충분히 확신을 가지진못하면서 마무리한다..

 

추가로 여유가 된다면, Nguyen and Hein의 2017 ICML 논문 "The Loss Surface of Deep and Wide Neural Networks" 에 관련된 자료를 찾아보는 것도 좋겠다.

왜 딥러닝이 local minima 문제에 잘 빠지지않는 이유로 '실제로 대부분은 global minima 또는 그 가까이에서 수렴한다. 라는 이론을 제시해주었고 

 

남세동 대표님의 딥러닝 신입개발자에게 묻는 질문 시리즈의 4번째는 mini-batch (미니배치) 사용에 대한 질문이다.

다행히도(?) 얼마전에 코딩하면서 자료도 찾아보고 테스트해봤던 내용이지만

이번 기회에 깔끔하게 정리해본다

 

4. Mini-Batch를 사용하는 이유가 무엇인가요? 

a. 사양이 허락한다면 Mini-Batch보다 (Total) Batch가 좋을까요? 

 

기본적으로, 딥러닝 모델을 학습하기 위한 대용량의 데이터는 GPU 메모리보다 큰 경우가 일반적이다.

이 때문에 전체 데이터를 mini-batch(미니배치)로 나누어서 여러번 학습을 나누어서 수행한다. 이 나누어진 횟수를 iteration 이라고 부른다.

학습과정에서 1 epoch 은 이 미니배치로 나누어진 전체 데이터가 1번 학습되었을 때를 뜻한다.

통상적으로  미니배치의 사이즈는 2^n 으로 정의하는데 메모리 활용 측면에서 유리함이 있다고 한다 (더 구체적으로 알 필요는 없을듯하다)

 

그러면 toy example이나 실무에서 데이터가 크지않은 경우에는 굳이 미니배치를 쓰지 않아도 될까?

이론적으로나 본인의 경험으로나 그렇지 않았다.

메모리 가용범위 내에서 배치 사이즈를 늘릴 경우 epoch을 돌리는 속도는 당연히 증가한다.

하지만 전체 데이터를 사용하는 모델(BGD)의 성능은 미니배치를 사용할 때 보다 현저히 낮게 나왔다.

BGD 모델의 epoch을 단순히 늘리는 것으로는 성능이 개선되지도 않고 오버피팅의 문제도 발생할 수 있다.

 

같은 데이터를 학습시켰는데 왜 모델의 성능이 차이날까?

미니배치만큼 더 작은 local 데이터에 대해서 더 많이 (iteration 수 만큼) 학습하기때문이다.

 

배치크기가 크면 그만큼 큰 데이터셋에서 계산된 gradient 의 평균치를 모델이 학습한다. 이 때는 여러번의 epoch으로 학습하더라도 계속 같은 데이터를 사용하므로 데이터셋의 local minima에 빠질 수 있다.

이를 잘 나타낸 카카오블로그의 그림을 보자.

source:&nbsp;https://www.kakaobrain.com/blog/113

반면 극단적으로 배치 사이즈=1 인 경우, 즉 stochastic 알고리즘을 생각해본다면

매번 다른 데이터를 이용하므로 local minima 에 빠질 위험은 적지만 학습시간이 너무 길어지므로 현대의 딥러닝 개발환경에 적합하지 않다고 생각할 수 있다.

 

이같은 배치사이즈와 gradinet descnet 속도 관계는 다음과 같은 그림으로 정리할 수 있다.

source:&nbsp;https://www.kakaobrain.com/blog/113

 

 

b. Data Parallelism 시에 GD는 어떻게 동작하나요?

미니배치에서 그래디언트 계산 방식에 대한 질문인데 사실 특별한 것은 없다. 위에서 말한 것처럼 매 iteration 마다 gradient 계산과 레이어 학습이 이루어진다.

아마 GPU 메모리 로드에 관련한 질문의 의도가 있는 듯한데 이 글에서는 하드웨어에 대해서 너무 깊이 다루지 않으므로 패스하자.

+ Recent posts