논문

하루에도 수만개의 글자를 읽고 있습니다. 하루에도 수백장의 종이를 들춰 읽습니다.
이것은 그 읽기에 대한 일기입니다.

Caffe Tutorial 2. Blobs, Layers, and Nets: anatomy of a Caffe model

Blobs, Layers, and Nets: anatomy of a Caffe model

딥러닝은 구성적인 모델(compositional model)로 데이터의 덩어리들에서 작동되는 층들의 연결로 표현됩니다. Caffe는 Net이라는 자체 모델 스키마로 층 대 층 네트워크를 정의합니다. 네트워크는 전체 모델을 입력 데이터로부터 로스까지 바닥부터 꼭대기까지 전체 모델로 정의합니다. 데이터와 미분값들은 네트워크를 앞으로, 그리고 뒤로(http://caffe.berkeleyvision.org/tutorial/forward_backward.html) 흐르면서, 이에 따라 Caffe는 Blob을 이용하여 정보를 저장하고 통신하며 조작합니다. Blob은 표준적인 배열이고 프레임웍크에서 사용되는 통합된 메모리 인터페이스입니다. Layer는 모델이나 계산의 foundation 다음에 오게 됩니다. Net는 층들간의 연결이나 집합 다음에 오게 됩니다. Blob은 어떻게 정보를 저장하고 layer나 net들 사이에서 통신하는지를 기술하게 됩니다.

Solving(http://caffe.berkeleyvision.org/tutorial/solver.html)은 서로 연결되지 않은 모델링과 최적화를 서로 별도로 구성합니다.

이제 이러한 컴포넌트들을 좀더 자세히 알아보겠습니다.

Blob storage and communication

Blob은 처리를 해야하고 Caffe에 의해서 서로 전송을 해야하는 실제 데이터를 한꺼풀 씌운 래퍼(wrapper)입니다. 또한 CPU와 GPU간의 동기화 능력을 가지고 있습니다. 수학적으로 blob은 N차원의 배열로써 C-contiguous fashion으로 배열 형태를 가집니다.

Caffe는 데이터를 blob을 이용해서 저장하고 통신합니다. Blob은 데이터에 대한 통홥된 메모리 인터페이스를 제공하는데, 이를테면 이미지들의 배치나, 모델 파라미터, 최적화를 위한 미분 값 등을 다룰 수 있습니다.

Blob은 GPU와 GPU를 함께 사용할 때, 필요할 때마다 GPU와 CPU간의 동기화를 해야하는 계산적, 정신적인 부담을 숨기도록 합니다. 호스트와 디바이스의 메모리는 효과적으로 사용되기 위해서 필요할 때만 할당됩니다.

기존의 경우, 이미지 데이터의 배치를 나타내는 blob의 차원은 N x 채널 K x 높이 H x 폭 W으로 계산됩니다. Blob 메모리는 row기준 메모리로, 마지막 / 가장 오른쪽 차원이 가장 빨리 변하게 됩니다. 예를 들면, 4D blob일 경우 인덱스 (n, k, h w)의 값은 물리적으로 ((n * K + k) * H + h) * W + w에 위치합니다.

  • Number / N은 데이터의 배치사이즈입니다. 배치 프로세싱은 통신과 디바이스의 처리에 더 나은 출력을 제공합니다. ImageNet의 트레이닝 작업에서는 256개 이미지로 이루어진 배치이므로 N=256입니다.
  • 채널 / K는 특징의 차원입니다. RGB 이미지의 경우 K=3입니다.

Caffe의 예제 속의 많은 blob들이 이미지 응용을 위해서 4D인 것에 주목해야 합니다. 이것은 blob을 이미지가 아닌 다른 응용에서 사용하려고 해도 완전히 올바른 것입니다. 만약 단순히 완전 연결 네트워크가 필요한 상황이라고 한다면, (N, D)로 이루어진 2D blobs을 사용하고 InnerProductLayer를 사용하면 됩니다.

파라미터 blob의 차원은 층의 종류와 구성에 따라 변하게 됩니다. 11 x 11 크기의 96개 필터로 이루어진 컨볼루션 층과 3개의 입력으로 이루어진 blob은 96 x 3 x 11 x 11크기를 갖습니다. 100개의 출력 채널과 1024개의 입력 채널을 갖는 내적 혹은 완전 연결 층은 1000 x 1024의 파라미터 blob이 됩니다.

기타 커스텀 데이터는 입력 준비작업 툴이나 데이터 층을 직접 만드는 작업이 필요합니다. 하지만 한번 데이터를 사용할 준비가 완료되면 층의 모듈성으로 인하여 나머지 부분에서도 잘 작동할 것입니다.

Implementation Details

Blob이 가지고 있는 그래디언트 뿐만 아니라 그 값에 대해서도 때론 관심이 있을 것입니다. Blob은 data와 diff, 두 덩어리의 메모리를 저장하고 잇습니다. Data는 우리가 망에서 전송시키는 보통의 데이터이고, diff는 망에서 계산된 그래디언트입니다.

실제 값들은 CPU와 GPU, 양쪽에 모두 저장될 수 있기에 그것에 접근하는 두가지 방법이 있습니다. 하나는 const way로 값을 변경하지 않는 방법이고, 나머지 하나는 mutable way로 값을 변경시키는 방법입니다.

(GPU나 diff의 경우도 비슷합니다.)

이렇게 디자인된 이유는 다음과 같습니다. Blob은 SyncedMem 클래스를 사용하여 CPU와 GPU사이에서 값들을 동기화시킵니다. 이는 실제 동기화하는 상세한 과정을 숨기고 데이터 전송을 최소화하기 위해서입니다. 첫번째 규칙은, 만약 값을 변경시키지 않기를 원한다면 항상 const call을 사용하고, 객체 내에 포인터를 저장하여 사용하지 않아야 합니다. Blob을 이용하는 동안에는 SyncedMem이 언제 데이터를 복사해야하는지 알아야 하기 때문에, 포인터를 얻고 싶을 때에는 항상 함수를 이용해서 포인터를 얻어야 한다.

GPU를 사용할 수 있을 때에는, CPU코드에서 디스크로부터 데이터를 blob으로 읽어온 다음, GPU 계산을 위해서 디바이스 커널을 호출합니다. 다음 blob을 다음 층으로 이동시킵니다. 상세한 세부 레벨의 동작을 무시하면서도 높은 수준의 성능을 유지할 수 있습니다. 모든 층이 GPU 구현이 되어있는 한, 모든 중간 단계 데이터와 그래디언트는 GPU 내에 저장될 것입니다.

Blob이 데이터를 복사하는할 때를 알고 싶은 사람들을 위해서 아래 예제를 보입니다.

Layer computation and connections

Layer는 모델의 핵심이며 계산의 기본 단위입니다. Layer는 필터를 컨볼루션 하고, pool하며 내적을 계산하고, rectified-linear 혹은 시그모이드 같은 비선형 연산을 적용합니다. 또한 다른 요소단위 변환과 노말라이즈, 데이터 로드, 소프트맥스(softmax)나 힌지(hinge)와 같은 손실 함수를 계산합니다. 이러한 모든 연산에 대해서는 Layer 카탈로그를 살펴보세요. 최신의 딥러닝을 적용하기 위한 대부분의 것들이 있습니다.

"blob"

Layer는 bottom의 연결로부터 들어오는 입력을 받아 top으로 연결로 나가는 출력을 만들어냅니다.

각 layer의 타입은 3가지 중요한 계산을 정의합니다. setup, forward, backward가 그것입니다.

  • Setup: 모델이 초기화되면 그 직후 layer와 그 연결을 초기화합니다.
  • Forward: bottom으로부터 입력이 주어지면 출력을 계산하고 top으로 보내는 것
  • Backward: top으로의 출력에 대한 그래디언트가 주어지면 입력에 대한 그래디언트를 계산하여 bottom으로 보내는 것. 파라미터를 가지는 layer는 그 파라미터들에 대한 그래디언트를 계산하여 내부적으로 저장해둡니다.

좀더 상세히 살펴보면, 2개의 Forward, Backward 함수가 구현되어야 합니다 하나는 CPU를 위한 것이고 하나는 GPU를 위한 것입니다. GPU 버전을 구현하지 않는다면, 백업 옵션으로 CPU 함수를 사용할 것입니다. 빠른 실험을 하고자 할 때에는 추가적인 데이터 코스트(입력이 GPU에서 CPU로, 출력이 CPU에서 GPU로 복사되는)가 들더라도 이것이 유용할 수도 있습니다.

Layer는 network를 위한 2개의 핵심 역할이 있습니다. 하나는 Forward pass는 입력을 받아서 출력을 내보내는 것입니다. 나머지 하나인 backward는 출력에 대한 그래디언트를 가지고 파라미터와 입력 값에 대한 그래디언트를 계산하는 것입니다. 이것은 이전의 층으로 역전파(back-propagated)되는 것입니다. 이러한 이동들은 각 layer의 forward와 backward를 조합한 것입니다.

사용자가 자신만의 커스텀 layer를 만들려면 망의 구성성과 코드의 모듈성을 가지도록 약간의 노력을 해야합니다. Layer의 setup과 forward, backward를 정의하면 net에 삽입할 준비가 된 것입니다.

Net definition and operation

Net은 그것의 구성하는 요소(composition)들과 자동 편미분에 의한 함수와 그 그래디언트를 정의합니다. 각 모든 층의 출력 요소들은 주어진 태스크를 수행하기 위한 함수를 계산합니다. 그리고 그 모든 층의 backward 요소들은 태스크를 학습하기 위해서 손실로부터 그래디언트를 계산합니다. Caffe의 모델은 기계학습 엔진의 끝과 끝입니다.

Net은 계산 그래프로 이어진 layer들의 집합입니다. 계산 그래프는 비순환 지향성 그래프(directed acyclic graph, DAG)입니다. Caffe는 DAG의 모든 층들을 기록해두는데 이는 forward와 backward 진행을 정확하게 수행하도록 하기 위함입니다. 일반적으로 net는 디스크로부터 데이터를 로드한 데이터 층으부터 시작하고, 분류나 복원 등과 같은 태크스를 위한 목적 함수를 계산하는 손실 층으로 끝납니다.

Net는 layer의 집합과 그 연결로 정의되는데 이것은 일반 텍스트 모델링 언어로 기술됩니다. 다음의 간단한 logistic regression 분류기는 다음과 같이 정의할 수 있습니다.

"LR"

모델 초기화는 Net::Init()으로 할 수 있습니다. 초기화는 크게 두가지 일을 하는데, 전체 blob과 layer들을 생성하여 전체 DAG의 뼈대는 만드는 일과, layer들의 SetUp() 함수를 실행하는 것입니다(C++에 익숙한 사람들을 위해서 첨언하자면 network는 그 생명주기 내에서 blob과 layer의 오너쉽을 가지고 있습니다). 또한 전체 네트워크 구조의 무결성을 점검하기 위한 기타 여러가지것들을 기록해둡니다. 또, 초기화 과정에서 net은 자신의 초기화 과정을 INFO로 로깅하고 있습니다. 그것은 아래와 같습니다.

네트워크를 생성하는 것은 장치에는 구애받지 않습니다. Blob과 layer는 그 상세한 구현이 모델 정의 과정에서 숨겨져 있다고 설명했었습니다. 네트워크의 생성 후에는, 네트워크가 Caffe::mode()에 정의된 대로 CPU 혹은 GPU에서 실행되게 됩니다. 이것은 Caffe::set_mode()로 전환할 수 있습니다. CPU와 GPU에서 돌아가는 각 layer는 서로 동일한 결과를 내놓습니다. 수치적 오류(numerical error)제외한다면 말입니다. 이 부분은 테스트 시에 보호되기는 합니다. CPU와 GPU의 전환은 모델 정의와는 독립적이게 부드럽게 이루어집니다. 연구와 배포를 위해서 모델과 그 구현을 분리하는 것이 좋습니다.

Model format

모델은 일반 텍스트 프로토콜 버퍼 스키마 (prototxt)로 정의되지만 반면에 학습이 끝난 모델은 바이너리 프로토콜 버퍼 (binaryproto)로 시리얼라이즈되어 .caffemodel 파일로 저장됩니다.

모델 포맷은 caffe.proto에서 protobuf로 정의되어있습니다. 소스 파일은 대부분 그 자체로 설명적이므로 한번 살펴보는 것이 좋습니다.

Caffe는 다음 성질에서 장점을 얻기 위해 Google Protocol Buffer를 사용합니다. 그것들에는 시리얼라이즈 후에 최소한의 바이너리 스트링 크기를 얻기 위함, 효과적인 시리얼라이즈 과정, 바이너리 버전과 호환되는 사람이 읽을 수 있는 텍스트 포맷, 여러 언어에서도 사용할 수 있는 효과적인 인터페이스(특히 C++과 Python) 등이 있습니다. 이것은 모두 Caffe 모델링의 확정성과 유연성에 기여하고 있습니다.


Tags:
Add a Comment Trackback