요약


쿠버네티스를 이용하면,파드를 통해 클라이언트들이 어플리케이션을 호출할 수 있다. 그러나, 클라이언트 입장에서는 각각 파드의 주소가 존재하는하고, 클라이언트는 어느 파드로 갈지 명시하지 않았는데도, 쿠버네티스가 알아서 각각의 파드가 처리하게 만들어준다. 즉, 네트워킹이 쿠버네티스에 의해서, 알아서 처리되는데, 이 네트워크 서비스를 제공할 수 있는 자원이 "서비스(Service)"이다. 즉, 파드로 실행중인 앱을 네트워크서비스로 노출하게 해주는 자원이다.

 

 

서비스를 이용하기위해 각 파드, 노드가 만족해야할 사항[1]


  • 각 파드는 노드상의 모든 파드와 통신할 수 있어야함
  • 노드상의 에이전트(데몬, kubelet)은 해당 노드의 모든 파드와 통신할 수 있어야함.

 

 

어플리케이션을 네트워크 서비스로 외부에 노출하기


아래와 같이, 디플로이먼트(deployment)와 서비스(Service)를 명시한 yaml파일을 각각 만든다. 

// nginx-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
# run-my-nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

 

그리고, 아래와 같이 클러스터 내부에서는  서비스가 가능하고, 각 클러스터의 IP을 확인할 수 있다.

$ kubectl get services
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes        ClusterIP   10.96.0.1        <none>        443/TCP        28h
my-nginx          ClusterIP   10.108.171.235   <none>        80/TCP         26h

해당 클러스터의 내부

 

 

Trouble shooting: Cluster IP에 접속이 안될 때 -> Node Port로 외부 클라이언트가 접속할 수 있도록 변경 [2]


서비스하는 클러스터의 IP는 가상의 IP이기 때문에, 클러스터 내부에서 내부로만 서비스할 수 있다. 즉 위의 예시에서처럼 클러스터 IP가 10.108.171.235면, 이 클러스터로 들어가야만 "10.108.171.235:80"의 서비스를 요청하고 받을 수 있는 것이다. 이것을 해결하기, "Cluster IP"을 "Node Port"로 변경해주어야 한다.

Node Port은 외부 클라이언트가 노드로 접속하기 위한 포트를 의미한다. 이 노드 포트을 변경하려면 아래와 같이 kubectl expose을 이용한다.

$ kubectl expose deployment hello-world --type=LoadBalancer --name=example-service

 

expose을 하면 TYPE이 NodePort인 서비스가 하나 생성이된다.  example-serivce라는 이름을 가진 서비스의 TYPE이 NodePort인 것을 잘보자. 이 클러스터의 가상 IP은 10.101.151.252이다. 이것은 여전히 가상IP이기 때문에 서비스를 요청할 수 없다. 다만, 이 노드의 원래주소(localhost, 또는 privateIP, publicIP)로 들어갈때, 30384포트로 요청을하면 알아서 80번으로 바꿔준다. 즉 포트포워딩으로 30384을 80으로 바꿔서 10.101.151.252로 요청해볼 수 있다.

$ kubectl get services
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
example-service   NodePort    10.101.151.252   <none>        80:30384/TCP   26h
kubernetes        ClusterIP   10.96.0.1        <none>        443/TCP        28h

$ curl localhost:30384
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

 

 

[1] https://kubernetes.io/ko/docs/concepts/services-networking/

 

서비스, 로드밸런싱, 네트워킹

쿠버네티스의 네트워킹에 대한 개념과 리소스에 대해 설명한다.

kubernetes.io

[2] https://stackoverflow.com/questions/49297226/kubernetes-cluster-ip-service-not-working-as-expected

반응형

본 포스팅은 naver d2 이활석님의 발표자로를 글과 이해가 쉽도록 만든 Figure들로 재구성했음을 알립니다.

 

요약


  1. MSE로 최적화하는 것과 MLE로 최적화 하는 것은 동치이다. 
  2. sigmoid을 출력값으로 하는 경우 MSE로 최적화하는 것보다 CE로 하는 경우, 학습이 더 빨리 된다. 이는 activation function(sigmoid)의 도함수를 한 번 계산하지 않아도 되기 때문이다.

1. Backpropgation (역전파) 과정에 대한 가정(Assumption)


역전파알고리즘을 이용하기 위해서는 2가지 가정이 일단 필요하다 [1].

  1. Average $E = 1/n \sum_{x}E_{x}$: $n$개의 training 데이터에 해당하는 각각의 $x$에 대한 손실함수의 합의 평균은 손실함수 전체와 같다. 이는 각 훈련데이터를 각각 구한것의 그레디언트를 전체 오차함수에 대해서 일반화을 위하기 때문이다.
  2. 신경망의 출력값($\hat{y}$)에 대해서 손실함수를 표현할 수 있어야 한다는 것이다.

 

직관적으로 생각해보면 아래와 같다.

첫 번째로, MSE로 종류의 $E$(error)을 설정했다고 하자. 그리고, 이 오차함수는 예측값($y'$)과 실측값($y$)의 이를 구하게 된다. 그러면 다음과 같이 표기할 수 있다. 가령, $E(y, y')= (y-y')^{2}$가 라고 하자. 이는 일단 두번째 조건을 만족한다. 왜냐하면 $y'$은 신경망의 두번째 출력값으로 표현되었기 떄문이다. 그리고, 이러한 $y'$을 출력하기위한 입력값 $x$가 있을 것이다. 이 $x$가 $n$개가 있다고하면 각각의 $n$개의 오차의 합은 전체 $E$가 될 수 있다는 것이다. 즉, 다음과 같은 식으로 표현할 수 있다. $E=1/n \sum_{x} (y-y')^{2}$. 이렇게되면 1번 가정도 만족한다. 

 

위의 가정을 만족할 수 있으면, 이제 손실함수를 최소화시키는 최적의 파라미터($\theta$)을 찾으면된다. 보통 우리는 아래와 같이 표현한다.

$\theta^{*} = argmin_{\theta \in \Theta} L(f_{\theta}(x), y)$

위 식을 친절하게 해석해보면 다음과 같다.

  1. $\theta^{*}$: 우리가 원하는 최적의 파라미터를 의미한다. 
  2. $L(f_{\theta}(x), y)$: $\theta$가 어떤 것이 지정되냐에 따르는 손실함수($y$와 예측치 $f_{\theta}(x)$의 차이)를 의미한다.
  3. $argmin_{\theta \in \Theta} L$ : $\theta$가 될수 있는 모든 수 중에 $L$을 가장 작게만드는 $\theta$을 의미한다.

 

 

2. Gradient descent (경사하강법): 경사하강법으로 최적의 파라미터를 찾아간다.


위의 $\theta^{*} = argmin_{\theta \in \Theta} L(f_{\theta}(x), y)$식의 최적의 해를 찾는 방법으로 경사하강법(Gradient descent)가 이용된다. 이는 Iteration으로, 같은 로직을 여러번 반복하는 것을 의미한다. 식이 회귀방정식이라면 close form solution으로 직접 전개해서 풀면되지만, 활성화 함수랑 이것저것 엮여있는 DNN은 open form solution이어서 직접 추정해서 구해야한다.

 

경사하강법으로 최적의 파라미터($\theta$)을 구할 때, 고려사항과 각각의 방법은 아래의 표처럼 정리할 수 있다.

고려사항 방법
어느정도만큼 파라미터를 업데이트 할 것인가?
(= $\theta -> \theta + \Delta\theta$)
새로운 $\theta + \Delta\theta$로 손실을 측정할 때가 더 작을 때
(= $L(\theta + \Delta\theta) < L(\theta)$)
언제 멈출 것인가? 더 이상 업데이트가 변화하지 않을 때
$L(\theta + \Delta\theta) < L(\theta)$
어떻게 업데이트할 만큼($\Delta\theta$)을 찾을 것인가? $\Delta\theta = -\mu \nabla\theta$ where $\mu > 0 $

 

그림1. 이미지 출처: https://builtin.com/data-science/gradient-descent

 

위의 전략대로 아래와 같이, 일단 새로운 파라미터에 대해서 손실함수를 구하고자한다. 헷갈리지 말아야할 것은 $x$에 대해서 구하는게 아니라, 최적의 $\theta$을 찾는 것이다. 위의 그림과 같이 x1, x2축에 해당하는 w, b에따라서 손실함수의 크기 $J(w, b)$가 달라지는 것이다. 즉, $\theta$가 우리의 목표이다.

전체 데이터 포인트 X가 있으면, 아래와 같이 풀이가 가능하다.

  • $L(\theta_k, X) = \sum_{i}{L(\theta_{k}, x_{i}})$: 각 데이터포인트($x_{i}$)의 합은 전체와 같다.
  • $\nabla L(\theta_k, X) = \sum_{i}{\nabla L(\theta_{k}, x_{i}})$: 위의 식을 양변에 $\theta$에 대해서 미분한다. 
  • $\nabla L(\theta_k, X) \triangleq \sum_{i}{\nabla L(\theta_{k}, x_{i}})/ N$: 최적화만 구하면되니까, 이 미분값이 N으로 나눠도 된다. 
  • $\nabla L(\theta_k, X) \triangleq \sum_{j}{\nabla L(\theta_{k}, x_{j}})/M, ~ where ~M< N$: 전체 훈련데이터에 해당하는 데이터포인트를 다넣어서 N개의 연산을 해도되지만,너무 연산이 많이드니, 적당히 작은 M으로 나눠서 해를 찾을 수 있다. 이 때 랜덤으로 M개의 데이터 j개를 뽑는다.

즉, 결국에 X개 계산한다음에 업데이트하고싶지만, 계산이 많이 드니 M개만큼 쪼개서 업데이해볼 수 있다는 것이다.수식으로는 $\nabla L(\theta_k, X)$ 만큼 움직이는 것 대신에 $\sum_{j}{\nabla L(\theta_{k}, x_{j}})/M$ 만큼 움직여서 업데이트할 수 있다는 것이다.

그리고, 아래와 같이 다음(k+1)의 파라미터틀 업데이트한다. 우리는 이 k를 iteration, M개를 넣는 작업을 step이라고한다. tensorflow에는 iteration은 epoch, M개의 미니배치로 계산하는 것은 step이라는 표현으로 사용한다.

$\theta_{k+1} = \theta_{k} - \mu \nabla L(\theta_{k},X)$

 

 

 

3. Backpropgation에 따라서 각 레이어의 파라미터를 업데이트


위에서 구한 것과 같이 손실함수(loss function)은 $L(\theta_{k},X)$라고 했다. 헷갈리지 말아야할 것이, 파라미터($\theta$)에 해당하는 것은 각각의 레이어의 $w, b$을 의미한다. 2개의 표현식을 통틀어 크게는 $\theta$라고 표현한다. 

파라미터 업데이트는 $\theta_{k+1} = \theta_{k}-\mu\nabla L(\theta_{k}, X)$ 가 목표다. 이를 각각 w와 b에 대해서 풀어쓰면 다음과 같다.

  • $w_{k+1}^{l} = w_{k}^{l} - \mu\nabla L_{w_{k}^l}(\theta_{k}, X)$ : $k$번째 업데이트 할 때, $l$번째 레이어의 $w$에 대해서 미분이 필요하다
  • $b_{k+1}^{l} = w_{k}^{l} - \mu\nabla L_{b_{k}^l}(\theta_{k}, X)$: $k$번째 업데이트 할 때, $l$번째 레이어의 $b$에 대해서 미분이 필요하다

위의 두 식을 보면 각 레이어 $l$에 대해서 매 업데이트시(k)마다 미분이 필요하다. 이건 너무 계산이 많이들어 딥러닝이 못했던 허들이기도 하다.

 

그렇기에 이를 해결하고자 했던, 역전파로 해결한다 [2]. 이 계산을 알기위해서는 forward방향에서의 오차를 먼저 알아야한다. 그림4처럼 L번쨰 레이어의 i번째에 원래 뉴런이 하는 작업을 $\sigma$ (활성화함수아님)라 하자. 그러면, $z_{j}^{l}$이 입력이 오던걸, $\nabla z_{j}^{l}$만큼 변경하면, 출력값은 $\sigma(z_{j}^{l} + \nabla z_{j}^{l})$로 바뀐다. 그러면 최종적으로 전체 손실함수값은 $\frac{\partial C}{\partial z^l_j} \Delta z^l_j$만큼 바뀐다고 알려져있다.

그림 4.

이제 역전파로 바꿔생각해보자. 이를 구하기위한 각 notation은 아래 와 같다. 그리고, 레이어의 개수가 $l$개라고하자(=$l$ 레이어가 마지막레이어).

  • $\delta_{j}^{l}$: $j$번째 뉴런(유닛)의 $l$번째의 레이어의 변화량. 쉽게 표현하면, j번째 뉴런이 조금변경되면, 오차가 얼마나 변화하는지를 의미한다. 상세히는 w, b로 표현하면 $\partial C / \partial w^l_{jk}$, $\partial C / \partial b^l_j$도 된다. $\delta_{j}^{l}$을 활성화 함수가 포함된 합성함수라고 생각하면 다음과 같이 미분된다 [3]. $\delta^L_j = \frac{\partial C}{\partial a^L_j} \sigma'(z^L_j).$.
  • $a_{j}^{L}$: $l$레이어에 존재하는  $j$번재 아웃풋
  • $C$: cost function을 의미. 예를 들어 $C=1/2 \sum _{j}(y_{i}-a_{j}^{L})^{2}$
  • $z^{L}$: L번째 레이어에서 출력한 결과

 

다음과 같이 역전파를 진행한다.

순서 수식 의미
1 $\delta^L = \nabla_a C \odot \sigma'(z^L)$ l번째 레이어의 출력값이 변하면, 손실함수가 얼마나 변할까?
그리고 그 계수에 해당하는 activation()만큼 변하겠지?

$\nabla_a C$은 예측값과 출값의 차이니까 이를 대입한다. (단순히 MSE라고 생각)
가령, $\delta^L = (a^L-y) \odot \sigma'(z^L).$ 을 구할 수 있다.
2 $\delta^l = ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)$ $l$번째 레이어와 $l+1$번째 레이어에서의 관계식을 의미한다. 

일단 다음 레이어인 $l+1$번째의 오차를 먼저알기 때문에, 이때 계산에 쓰였던 파라미터 $w^{l+1}$을 전치한다음에 곱하고, 활성화함수만 곱해주면 이전 연산의 에러를 구할 수 있다는 것이다.

3 $\frac{\partial C}{\partial b^l_j} = \delta^l_j.$ 1,2을 이용하여 각 b에 대해서 편미분을 구할 수 있다.
4 $\frac{\partial C}{\partial w^l_{jk}} = a^{l-1}_k \delta^l_j.$ 1,2을 이용하여 각 w에 대해서 편미분을 구할 수 있다.

 

 

 

 

MSE와 CrossEntorpy 비교: 왜 굳이 이진분류를 할 때 Xentopry을 사용할까?


 

TLTR: CrossEntropy을 이용할 수 있으면, gradient vanishing에서 조금이나마 MSE보다는 이득이기 떄문이다.

 

MSE와 CE을 이용하는 각각의 모델에서, 우리 모델의 마지막 레이어가 sigmoid로 활성화 함수를 썼다고 가정하자. MSE은 출력레이어에서의 오차를 $\delta^L = \nabla_a C \odot \sigma'(z^L)$처럼 계산한다고 했다. 자세히보면 오차를 계산할 때, 오차를 계산하자마자 $\sigma'(z^L)$을 한 번 한다. C은 간단히 y-a(마지막 아웃풋과의 차이)이라고하자. 그러면 MSE을 계산하려면 $\delta^L = (a-y) \sigma'(z^L)$.가 된다.

반대로 CE을 이용하자고하자 CE의 loss은 $C=-[ylna+(1-y)ln(1-a)]$이다. 이걸 a에 대해서 미분하면 다음과 같다.

  1. $\nabla_{a}C = \frac {1-y} {(1-a)a}$.
  2. $\sigma'(z^L)$도 미분하면, $\sigma'(x)=\frac{d}{dx}\sigma(x)=\sigma(x)(1-\sigma(x))$을 따르므로, $a(1-a)$가 된다.
  3. $\delta^L = \nabla_a C \odot \sigma'(z^L)$ 라고 했기 때문에,
  4. $\delta^L = \frac {1-y} {(1-a)a} a(1-a) = (a-y)$ 이므로,  $\sigma'(z^L)$가 상쇄된다. 

정리하면, MSE을 이용할경우 backpropagation에서 gradient decay가 한 번 발생하고 역전파하기 때문에, 학습이 늦게되는 반면, CE을 이용한 경우 gradient decay가 한번 없이 역전파해서 상대적으로 학습이 빠르게 된다

그림3. sigmoid function의 원함수와 미분된 함수의 값

 

 

 

 

MSE의 최적화와 Likelihood을 이용한 최적화의 비교


MLE(Maximum likelihood estimation)은 관측한 데이터(x)을 가장 잘 설명하도록 파라미터($\theta$)을 찾는 과정이라고 생각하면 쉽다. 조금 바꾸어 말하면, 모델(f)내에 파라미터가 출력(y)을 가장 잘 설하도록 모델의 파라미터($\theta$)을 찾는 과정이랑 같다. 우리가 모델에서부터 얻는 예측값($f_{\theta}(x))$( =$\hat(y)$ 이기에...)가 정규분포를 따른다면, 평균적으로 실측값(y)을 얻게끔 파라미터를 얻는 과정이다. 따라서 아래와 같이 수식으로 표현할 수 있다.

$\theta^{*} = argmin_{\theta}[-log(p(y|f_{\theta}(x))]$

위의 식이 (-)Negative와 log가 붙은 negative log likelihood 이다. 의미는 모델의 반환값이 어떤 분포를 따른다면, 따른다면, 이 분포의 파라미터를 잘조절에서 y가 잘 나오게해주는 $\theta$을 찾아주세요. 라는 의미이다. 

 

위를 풀기위해 함수를 최적화하기위해 IID 을 가정한다

  • Independent : $p(y|f_{\theta}(x) = \Pi_{i}P_{D_{i}}(y|f_{\theta}(x_{i}))$: 트레이닝 데이터 DB ($D_{i}$)을 최적화하는 것에 대한 모든 곱은 전체와 같다. (독립사건이기에 서로 곱할 수 있다)
  • Identical distribution: $p(y|f_{\theta}(x) = \Pi_{i}p(y|f_{\theta}(x_{i}))$: training데이터는 매번 다른 분포를 같는 것이 아니라, $x1$이 정규분포라면 $x_{2},...x_{i}$가 모두 정규분포이다.

이를 이용하여 다음과 같이 negative loglikelhood을 정리할 수 있다

=> $-log(p(y|f_{\theta}(x))) = -\sum_{i}log(p(y_{i}|f_{\theta}(x_{i})))$: (IID을 이용). 각각의 확률값의 negative log likelhood을 다 더하면 전체의 training DB의 negative loglikelihood와 같다

위의 분포가 정규분포를 따른다면, 정규분포의 Likelihood을 이용해서 파라미터를 찾을 수 있다. 정규분포의 log-likelihood값은 위의 그림2와 같다. x은 관찰값 데이터포인트, $\mu$은 우리가 구하고자하는 파라미터 중에 평균에 해당한다. 이 식을 우리의 문제로 바꿔보자. x가 관찰값이었다면, 우리문제에서는 모델의 매번반환하는 $f(x)$의 평균값이 $\mu$가 되고자하는 것이다. 즉, $f_{\theta}(x_{i}) =\mu_{i}, ~\sigma_{i}=1$ 인 것을 원한다.

그림 2. log-likelihood function .&nbsp;https://www.statlect.com/fundamentals-of-statistics/normal-distribution-maximum-likelihood

$= -\frac{n}{2}ln(2\pi)-\frac{n}{2}(\sigma^{2}) - \frac{1}{2\sigma^{2}}\sum_{j=1}(x_{j}-\mu)^{2}$ 을 

$= -\frac{n}{2}ln(2\pi)-\frac{n}{2}(\sigma^{2}) - \frac{1}{2\sigma^{2}}\sum_{j=1}(f_{\theta}(x) - y_{i})^{2}$: 이렇게 바꿀 수 있다.

위의 식을보면 $y_{i}$와 $f_{\theta}(x_{i})$의 차이의 제곱. 즉 MSE와 같다.

 

반면의 베르누이 분포(=확률(p)에 따라, 결과가 0,1로 나오는 분포)를 따른다면 아래와 같다.

$p(y_{i}|p_{i})=p_{i}^{y_{i}}(1-p_{i})^{1-y_{i}}$

<=> $log(p(y_{i}|p_{i})) = y_{i}logp_{i} + (1-y_{i})log(1-p_{i})$
<=> $-log(p(y_{i}|p_{i})) = -[y_{i}logp_{i} + (1-y_{i})log(1-p_{i})]$

 

 

Reference


[1]  Nielsen (2015), "[W]hat assumptions do we need to make about our cost function ... in order that backpropagation can be applied? The first assumption we need is that the cost function can be written as an average ... over cost functions ... for individual training examples ... The second assumption we make about the cost is that it can be written as a function of the outputs from the neural network ..."

[2] http://neuralnetworksanddeeplearning.com/chap2.html

 

Neural networks and deep learning

In the last chapter we saw how neural networks can learn their weights and biases using the gradient descent algorithm. There was, however, a gap in our explanation: we didn't discuss how to compute the gradient of the cost function. That's quite a gap! In

neuralnetworksanddeeplearning.com

[3] https://j1w2k3.tistory.com/1100

반응형

1. 구글드라이브에 올라간 파일의 고유 ID을 다음과 같이 얻습니다. 아래 그림과 같이 "1q4d8FmF452uJ9SdlW1zQ2SMJYAphKwts"가 파일의 고유ID입니다.

 

2. wget사용하기위해서, gdown이라는 패키지를 다운받습니다.

python3 -m pip install gdown

 

3. gdown을 이용하여 다운받습니다.

// 파일 고유ID을 이용해서 다운로드
$ gdown --id [파일고유ID] 

// 파일 고유ID을 이용해서 다운로드 + 다운로드 대역폭을 3MB 제한
$ gdown --id [파일고유ID] --speed "3MB"

 

반응형

 

요약


요약: Supervised learning의 일반적인 개념으로, Supervsed learning에서는 분류하고자할 instance가 정확히 True/False 등의 라벨이 필요한데,Multiple instance learning에서는 Instance까지는 True/False 까지는 안되어있어도, Instance의 묶음(Bag) 내에 True가 있을까? False일까를 맞추는문제. 주로 탐색해야할 영역들이 넓은 Pathological image, Document classification에 사용된다. 

 

Fig 1. MIL과 Single instance learning 과의 차이(이미지: Multiple instance learning for histopathological breast cancer image classification)

 

 

 

 

입/출력에 대한 상세: 입력값의 형태, 출력값의 형태, 문제는 어떤형식인가?


구체적으로는 각각의 instance까지의 라벨은 안되어도, instance을 포함하는 bag들의 라벨만 있으면 되는 형식이다 [1]. 보통 이걸 Weakly labling되었다고 한다

수식으로 보면 아래와 같다 [2].

  • $B_{n}$: Whole Slide Image(WSI)을 하나의 Bag이라고 가정한다 
  • $B_{n} = (x_{1}, x_{2}, x_{3}, x_{4},...,x_{n_{i}}), \omega_{n}$ : n번째 WSI을 하나의 Bag이라고 생각하면, 이 WSI내에는 각각의 패치를 $x_{n}$이라고 할 수 있다. 이 패치는 WSI이미지 마다 개수가 다를 수 있으므로 $x_{n_{i}}$ 라고 서브스크립트를 달아 놓았다.
  • $\omega_{n}$: 이러한 n번째 WSI는 각각의 라벨이 존재한다. 예) 악성종양/양성종양 
  • $p(\omega|B_{n})$: 전체 WSI내에 여러 이미지를 하나의 bag으로 주었을 때, 라벨을 맞추는 문제.
  • $p(\omega|B_{n})= E[p(\omega|x_{i})] \forall x_{i} \in B_{n}$: 어떤 인스턴스(x)가 들어와도 라벨의 잘 맞출 확률
  • $\omega = max_{k}\left \{ y_{n_{i}} \right \}$: bag내에 $n_{i}$개 중, 모든라벨이 0이어야 bag의 라벨이 0, 하나라도 1일경우에는 1을 의미한다.

VGG을 이용한 MIL모델의 예시. 이미지 소스: MULTIPLE INSTANCE LEARNING OF DEEP CONVOLUTIONAL NEURAL NETWORKS FOR BREAST HISTOPATHOLOGY WHOLE SLIDE CLASSIFICATION

 

실제 구현 예시


tensorflow로 구현

 

 

 

언제쓰면 적절한가?


  1. 각 인스턴스의 라벨을 직접 다는 것이 너무 어렵지만, bag의 라벨은 달 수 있을 때(Weakly labled)
  2. 큰 이미지를 다룰 때,
  3. 다운샘플링(Donwsampling)이 불가능할 때

 

 

MIL 사례 연구들 


- 사례 1: Document에서의 사례 [1]


- 문제: 문서가 이미지로 들어올 때, 관련 문서인지 파악하는 문제
- Supervised로 한다면?: 문서에는 로고, 시그니처, 그림, 테이블 등이 포함되어서 global descriptor로 풀기가어려움.
따라서, 이미지를 각각의 여러 zone으로나눈후, 각각의 zone에 대한 분류로 나누고자함. 분류기가 Document = [zone1, zone,2 zone3, ... zone N]라고 생각해서 각각의 zone 중에 한 영역만 True여도 관련분야다라고
학습시키려고 한다는 것이다. 그럴려면 사실 각 zone에 대해서 많은 label이 필요한데 비용이 많이든다.

 

 

- 사례 2: Histopahtological Image (조직병리학적 이미지) 슬라이드에서의 암예측

Camelyon7 Challenge (breast ca): 병리이미지를보고 환자가 전이성암이 될 것인가 말것인가를 푸는문제, PANDA challenge (prostate ca): prostate ca의 점수를 계량할 수 있는 점수(Gleason score)을 예측하는 것

 

 

 

- 사례3: Classifying and segmenting microscopy images with deep multiple-instance learning [3, 4]

instance-based MIL형태에 속하는 연구이다. 각 instance에 대해서 분류기를 통과시키고, 이를 다시 aggregation하는 형태로 동작함. aggregation하는 함수를 global pooling layer라고하는데, 이 연구에서는 Noisy-and pooling function이라는 것을 만들었다. 가정은 Bag이 Positive이면, Bag내의 인스턴스가 Positive인 것들이 일정 threshold을 넘길것이라는 가정을 둔다. 

  1. $p(t_{i}=1 | x_{j})$: Bag내에 인스턴스 $x_{j}$을 인자로 받았을 때, $t_{i}$클레스일 확률을 구하는 분류기
  2. $g(\cdot)$ (global pooling function): noisty-OR 이라고 함. 집계한 확률을 내어주는 함수. $p(t_{i}=1 | x_{1},...x_{N})$. 좀 다르게 표현하면, 각 인스턴스의 확률을 집계하여, Bag이 i라벨에 속할 확률을 구해주는것. $P_{i} = g(p_{i1}, p_{i2}, ...P_{iN}):
  3. Noisy-AND: $P_{i}=g_{i}(p_{ij}) = \frac{\sigma(a_{p_{ij}-b_{i}}) - \sigma(-ab_{i})}{\sigma(a(1-b_{i})) - \sigma(-ab_{i})}$. 이 식의 $a$은 sigmoid function전에 입력값을 multiple할 때 쓰고, $b_{i}$은 적어도 몇개가 있어야하는지에 대한 threshold로 역할을 한다.

 

$g(\cdot)$(global pooling function)을 어떻게 하냐에 따라 종류가 많은데, 대체적으로 아래와 같이 알려져있음.

https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4908336/pdf/btw252.pdf

 

실험에서는 MNIST dataset은 concat시켜서 만들어, MIL의 분류기를 만드는 시험을 했다. 라벨이 2라면 2가 적어도 b개만큼 있어야한다는 Noisy-AND을 이용하여 학습한다.

 

 

 

Reference



[1] : https://www.academia.edu/download/78345586/Kumar_ICDAR_2011.pdf

[2]: MULTIPLE INSTANCE LEARNING OF DEEP CONVOLUTIONAL NEURAL NETWORKS FOR BREAST HISTOPATHOLOGY WHOLE SLIDE CLASSIFICATION

[3] https://github.com/dancsalo/TensorFlow-MIL

[4] https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4908336/pdf/btw252.pdf

반응형

표지: Engineering MLOps: Rapidly build, test, and manage production-ready machine learning life cycles at scale

 

해당 책은 아래와 같은 구성으로 이뤄져 있다. 해당 포스팅은 Section 1을 다룬다.

3
27
51
79
103
Deploying Machine Learning Models at Scale
133
Key Principles for Deploying Your ML System
135
Building Robust CICD Pipelines
165
APIs and Microservice Management 191
Testing and Securing Your ML Solution
213
Essentials of Production Release
229
Monitoring Machine Learning Models in Production
253
Key Principles for Monitoring Your ML System
255
Model Serving and Monitoring
275
Governing the ML System for Continual Learning
311
About Packt
339
Other Books You May Enjoy
340
Index
343
 
 

 

 

Fundamentals of an MLOps workflow


전통적인 소프트웨어 개발방식MLOps와의 차이: 데이터와 코드를 둘다 다뤄야하는 ML문제

ML을 이용한 서비스가 점차적으로 확대될 것이라는 것에 이 챕터의 내용이 강조되고 있다. 여태까지 소프트웨어의 발전은 보통 "개발업무(development)"라는 것으로 취급되어서, Waterfall 방식, Agile 방식, DevOps방식으로 쭉 진행되어왔다. 소프트웨어의 개발이 더 큰 개념이기 때문에, 열거한 3가지 방식중에 최근 방식인 Agile/DevOps로 해결하면 되는것이 아닌가? 생각을 하게된다. 하지만, 현업에서는 많은 실패사례들이 존재한다. 왜 그럴까? 이 책에서는 ML을 이용한 서비스가 실패하는 이유를 말하는데, 전통적인 소프트웨어 개발과의 차이점을 강조한다.

Machine Learning = data  + code

 

DevOps라고하면, 지속적으로 코드를 개발하고, 업데이트하고, 사용자가 일부 개발과정을 포함해서 지속적으로 개발/도입/운영할 수 있는 개발방식으로 여겨지는데, MLOps도 이와 같이 했을 경우는 코드는 업데이트되지만, 데이터는 업데이트 안된다는 것에 단점이 있다. 따라서, 데이터와 코드가 지속적으로 함께 관리되어야함을 강조한다. 예) 모델은 최신모델인데 과거데이터를 쓰거나, 현재 데이터만을 이용해서 향후에 서비스 방식이 바뀌면 재도입이 어려운 것들에 대한 문제가 이해 해당된다. 이러한 단점을 줄이기 위해서 MLOps은 체계적인 방식으로 "data"와 "code(model)"을 같이 운영하는 것을 목표로한다. 이를 위해서, 모델의 개발, 도입(depolyment), 모니터링등알 함께 지속적인 방식으로 도입하는 것을 의미한다.

 

 

MLOps의 워크플로우: 어떤방식으로 진행이되나?


Figure 2. https://static.packt-cdn.com/products/9781800562882/graphics/image/B16572_01_010.jpg

 

Figure 2은 MLOps의 워크플로우의 개념도이다. 두 형태로 나뉘는데, 상위(Build, Delopy, Monitor)은 주로 MLOps pipeline이라고 불린다.한편 하위는 Driver(드라이버)라고 불린다.

  • MLOps pipeline: 모델 개발, 적용, 적용후 모니터링에 대한 업무의 개요에 해당된다.
  • Driver: MLOps pipeline을 하기위해서 실제로 행해야하는 하위 테스크, 구성되어야하는 인프라 등을 일컫는다.

MLOps업무를 해본사람이면 대부분 이해할 수 있는 개념이다. 참고로, model packaging, model registering, application testing 에 관한 내용만 추가로 아래와 같이 기술해 놓았다.

  • Model packaging: 모델을 직렬화하고, 독립적인 소프트웨어로 작동할 수 있게끔 컨테이너화하는 단계를 의미한다. 주로 ONNX file로 모델을 직렬화한다. ML모델을 Training/Testing으로 나누고 실제 잘 개발되었다고하자. 더 이상 이 모델은 현시점에서 바뀔일이 없다. 따라서, Pickling을 하든, ONNX로 변경하든 직렬화해놓고, 메모리에 로드하여 바로 쓸 수 있는 단계로 만드는 일에 해당된다. 그리고, 이 직렬화한 모델을 독립적인 소프트웨어로 쓸 수 있도록, Docker등으로 필요한 데이터, 종속코드 등을 하나로 묶어 만들어놓는 것을 의미한다.
  • Model registering: 모델레지스터링은 직렬화+패키징된 모델을 DB등과 같은 레지스트리에 등록/저장해놓는 것을 의미한다. 이렇게 해놓으면, 언제든지 해당 모델이 필요할 때, 다운로드 받아서 쓸 수 있다.
  • Application testing: 동의어로 Pre-production testing이라고 생각하면된다. 모델을 개발해놓고, 실제 운영에 쓰기전에 진짜 쓸만한것인지를 Extra validation하는 것과 같다. API나 쿠버네티스, 컨테이너로 띄워놓고 훈련용이 아닌, 추가적인 테스트용도로 데이터를 보내보고, 모델이 기대하는 것과 잘 동작하는지를 의미한다. Application testing의 결과는 도메인 전문가가 직접 결과를 분석하기도 한다(실제로 잘되었는지 확인해야하니까..), 도메인 전문가가 쓸만하다라고 판단해준 이후에나 실제 개발환경에 넣고, 추론할 수 있는 대기상태로 만들어두는 것이다.

 

MLOps 파이프라인의 예시: 희귀질환 환자의 유전변이추천시스템을 개발했다고 하자.

  1. 이 개발할 때 썼던 데이터는 2021년 이전의 데이터였다. 그렇게 만들어낸 Top 5 recall이 96.3%였다. 여기까지가 Model build이다.
  2. 이 모델을 바로 쓸 수 있도록, ONNX으로 직렬화하고 docker image로 만들어 내었다(model packaging).
  3. 이렇게 모델패키지한 것을 도커 이미지 리포지토리에 올려놓았다(model registering, 완벽한의미의 regstering은 아님).
  4. 그리고 나서, 임상유전학자들이 판독에 쓰기직전에 추가적으로 2022년 이후의 데이터를 모아놓고, 추천시스템 API을 띄워놓은 상태에서 모아놓은 데이터를 추론해보았다. 2022년 이후의 데이터를 추론했을 때의 결과가 20개중에, top 3가 95%이상이었다. 이 결과를 임상유전학자에게 다시 보냈다(Application testing).
  5. 그 후에 model production에 쓸 수 있도록 운영 서버로부터 데이터수 있도록 Restful API을 오픈했다. 

 

반응형

 

요약


도커 컴포즈는 다수의 컨테이너 어플리케이션을 정의하고(생성), 실행하는 툴을 의미한다. 이 도커 컴포즈는 간단히 하나의 텍스트파일(YAML 형식)을 작성해서, 여러개의 시스템을 실행하고, 종료, 폐기하여 컨테이너를 운용할 수 있는 툴이다[1]

이미지 소스: https://ostechnix.com/introduction-to-docker-compose/

 

Docker-compose vs Dockerfile vs 쿠버네티스


1. 도커 컴포즈(docker-compose)다수의 컨테이너의 실행(run), 정지(stop), 삭제(rm)을 한번해 실행 시킬 수 있는 명령어(툴)이다. 또한, 컨테이너와 주변환경, 네트웤, 볼륨까지 한번에 생성할 수 있다. 즉 

2. Dockerfile은 이미지를 만들기 위한 것이다. 도커 이미지 내에 여러 서비스를 넣어둘 수 있긴하지만, 동작상태의 컨테이너가 아닌 붕어빵 틀에 해당하는 이미지를 만들기 위한 것이다.

3. 쿠버네티스도 여러개의 컨테이너를 오케스트레이션(=다수의 컨테이너를 운용)한다는 점에서 동일하다. 단, 관리기능이 더 자세하다. 

즉, 도커 컨테이너-> docker compose, 도커 이미지->Dockerfile에 해당하다.  도커 컴포즈와 쿠버네티스의 차이는 컨테이너는 더 자세히 관리할 수 있는 툴인 것에 비해서, 도커 컴포즈는 단순히 여러 컨테이너를 정지/삭제 정도밖에할 수 없다.

 

 

 

도커 컴포즈 작성하는 법: 도커 컴포즈에서 쓰는 YAML파일 작성방법


도커 컴포즈는 한 파일 내에 docker-compose.yml 파일 있으면된다. 여러 도커 컴포즈를 생성할 경우, 각 폴더별로 docker-compose.yml파일이 있어야 한다. 꼭 "docker-compose.yaml"으로 YAML파일이 있을 필요는 없으나, 하나씩은 있어야한다. 도커 컴포즈의 파일은 아래와 같이 작성한다. "version"을 제외하고 "services", "networks", "volumes"은 모두 s가 붙는다

1) version 을 적는다

2) services

3) networks (옵션)

4) volumes (옵션)

# example) docker-compose.yaml
version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"

 

 

Docker compose 명령어: up, down, stop


// 도커 컴포즈내에 정의된 컨테이너 실행
$ docker-compose -f [docker-compose_file_path] up 옵션

// 도커 컴포즈내에 정의된 컨테이너만 정지
$ docker-compose -f [docker-compose_file_path] stop 옵션

// 도커 컴포즈내에 정의된 컨테이너만 종료/삭제, 네트워크 종료
$ docker-compose -f [docker-compose_file_path] down 옵션

 

아래와 같이 도커 컴포즈에 정의된 컨테이너 2개와 네트워크1개를 한번에 폐기할 수 있다. 네트워크도 삭제되는 것에 주의하고, stop이 아닌 remove된다는 것에 주의를 기울이자.

// docker-compose에 정의된 컨테이너를 한번에 종료 후 삭제

$ docker-compose -f docker-compose.yml down
[+] Running 3/3
 - Container 03-wordpress000ex12-1  Removed                                                                                                                                                                                             1.5s
 - Container 03-mysql000ex11-1      Removed                                                                                                                                                                                             2.2s
 - Network 03_wordpress000net1      Removed                                                                                                                                                                                             0.5s

 

 

Reference

[1] https://docs.docker.com/compose/

[2] https://docs.docker.com/compose/gettingstarted/

 

 

반응형

+ Recent posts