0. Abstract
어려운 DNN의 훈련을 위해 Residual leraning 프레임워클르 제시했다. 참조되지 않은 함수를 학습하는 대신 레이어 입력을 참조하여 레이어를 Residual function으로 재구성한다.
이러한 Residual 네트워크는 최적화가 쉽고 깊이를 증가시켜도 정확도를 얻을 수 있다.
VGGNet보다 8개 깊지만 복잡성을 낮춘 152개의 Residual Layer로 이루어진 Residual nets로 ImageNet의 데이터셋을 평가했다. Residual Net의 앙상블은 3.57%의 오류를 테스트 셋에서 달성했다. 이 결과로 ILSVRC 2015 분류 과제에서 1위를 차지했고, 논문에서는 100과 1000개의 레이어를 가진 CIFAR-10에 대한 분석을 제시한다.
인식의 깊이는 시각적 인식 작업에서 가장 중요하다. 오로지 인식의 깊이를 늘리기 위해 COCO 객체 탐지 데이터셋으로 28%의 개선을 얻었다.
깊은 Residual nets는 ILSVRC & COCO 2015 대회에 제출한 것이 기초이며, ImageNet detection, ImageNet localization, COCO detection, COCO segmentation에서도 1위를 차지했다.
1. Introduction
깊은 CNN은 이미지 분류를 위한 돌파구이다. 심층 네트워크는 낮은, 중간, 높은 특징들과 분류기를 end-to-end 다층 방식으로 자연스럽게 통합한다. 그리고 특징의 레벨은 쌓인 층수(깊이)만큼 풍부해질 수 있다.
최근의 동향은 네트워크의 깊이가 매우 중요하다는 것을 보여준다. 그리고 ImageNet dataset 대회에서 16~30 층의 깊은 모델을 이용하는 결과를 낳았다. 다른 많은 시각적 인식 작업도 매우 깊은 모델의 이점을 크게 얻었다.
그러나 더 많은 계층을 쌓는 것만큼 네트워크가 더 학습할 수 있을까? 이 질문에 대한 대답으로는 악명 높은 문제인 경사도 소실, 폭주 문제가 있다. 처음으로부터의 수렴을 받해하는 문제이다.
하지만 이 문제는 대부분 정규화된 초기화 및 중간 정규화 계층에서 해결되었으며, 이 계층은 수십 개의 계층을 가진 네트워크가 역전파를 통해 SGD에 대해 수렴할 수 있게 한다.
더 깊은 네트워크가 수렴하기 시작할 때, 성능 저하 문제가 발생했다. 네트워크 깊이가 증가함에 따라 정확도가 포화 상태가 되고 그 후 급속히 저하하는 문제이다.
예기치 않은 이러한 열화는 과적합이 이유가 아니다. 그리고 그림 1과 저자의 실험 결과와 같이 더 많은 레이어를 추가하면 훈련 오류가 더 높아진다.
성능 저하 문제 (degradation)는 모든 시스템이 유사하게 최적화하기 쉬운 것이 아님을 나타낸다.
더 얕은 아키텍처와 더 많은 계층을 추가하는 방식을 생각해보자. 심층 모델의 구성에 대한 해결책이 존재한다.추가된 계층은 identity 매핑이고, 다른 계층은 학습된 하부 모델에서 복사된다.
이 구성된 솔루션의 존재는 더 깊은 모델이 더 낮은 모델보다 더 높은 훈련 오류를 생성해서는 안된다는 것을 나타낸다.
그러나 한 실험에 따르면 현재의 해결책들은 이 설계된 솔루션이나 그 이상의 성능을 보이는 솔루션을 찾을 수 없다.
본 논문에서는 Deep Reidual Learning Framework를 도입하여 성능 저하 문제를 해결하였다. 각 누적된 레이어가 직접 기본 매핑을 학습하지 않고, residual mapping에 맞춰 학습하도록 한다.
기본 매핑을 H(x)로 나타내면, 누적된 비선형 레이어는 F(x) := H(x)-x를 학습한다. 원래 매핑은 F(x) + x의 반복이다.
비참조 매핑인 기존의 매핑을 최적화하는 것보다 참조 매핑인 Residual mapping을 최적 하는 것이 더 쉽다는 가정을 한다. 극단적으로, 식별자 매핑이 최적이라면 비선형 레이어를 스택에 의한 식별자 매핑을 적합시키는 것보다 Residual 을 0으로 밀어 넣는 것이 더 쉬울 것이다.
F(x) + x는 shortcut connection을 가진 피드포워드 신경망으로 구현할 수 있다. Shortcut connection은 하나 이상의 레이어를 스킵한다. 저자의 경우, shortcut connections는 단순히 식별자 매핑을 수행하고 그 출력은 누적된 출력에 추가된다.
식별자 shortcut connection은 추가 매개 변수나 계산 복잡성을 추가하지 않는다. 전체 네트워크는 여전히 역전파를 통해 SGD에 의해 end-to-end로 훈련될 수 있으며, 해결책을 수정하지 않고도 Caffe와 같은 공통 라이브러리를 사용해 쉽게 구현할 수 있다.
저자는 ImageNet에 대한 포괄적인 실험을 제시하여 성능 저하 문제와 위의 방법을 평가한다.
저자는 아래의 두 가지 실험의 결과를 제공한다.
1) 극단적으로 깊은 Residual net은 최적화하기 쉽지만, 단순히 층을 쌓은 일반 네트워크는 깊이가 증가하면
더 높은 훈련 오류가 발생한다.
2) 심층 Residual 네트워크는 매우 향상된 깊이에서 쉽게 정확성을 얻을 수 있으며, 이전 네트워크보다 훨씬
나은 결과를 얻을 수 있다.
비슷한 현상이 나타나는 CIFAR-10 데이터셋을 통해 성능 저하 문제와 해결책의 효과가 특정 데이터셋에만 일어나는 것이 아님을 보일 것이다.
100개 이상의 레이어를 가진 이 데이터 셋에 대해 성공적으로 훈련된 모델을 제시하고 1000개 이상의 레이어를 가진 모델을 탐구할 것이다.
ImagewNet 분류 데이터셋에서 저자는 매우 깊은 Reidual 네트워크로 우수한 결과를 얻었다. 152 레이어 Residual net은 VGG 네트워크보다 복잡도가 낮으면서 ImageNet에 제시된 네트워크 중 가장 깊은 네트워크이다.
저자의 앙상블은 ImageNet 테스트 셋에서 3.57%의 top-5 에러율을 가지고 있으며, ILSVRC 2015 분류 대회에서 1위를 차지했다.
극도로 심층적인 표현은 다른 인식 작업에도 우수한 일반화 성능을 가지고 있으며, ILSVRC & COCO 2015 대회에서 ImageNet detection, ImageNet localization, COCO detection, and COCO segmentation in ILSVRC & COCO 2015에서도 1등을 차지했다.
이 증거는 Residual 학습 원칙이 일반적이고, 다른 비전 및 비전 문제에 적용 가능할 것을 예측한다.
요약
네트워크의 층이 깊어지면 깊어질 수록 학습 결과는 좋다. 하지만 층이 깊어질 수록 여러 문제들이 나타난다. 경사도 소실, 폭주가 대표적인데, 이는 정규화된 초기화 등 여러 방법으로 해결할 수 있다. 하지만 정확도 손실 문제는 그렇지 못하다.
정확도 손실 문제는 층이 깊은 네트워크가 학습을 잘 하다가 정점에 이르면 다시 내려가는 현상이다. 이 문제를 해결하기 위해 Identity mapping layer을 추가해 residual mapping을 통해 하나 또는 이상의 레이어를 skip하는 Shorcut Connection을 구현한다. 이는 추가적인 파라미터도, 복잡한 곱셈 연산도 필요하지 않다.
2. Related Work
Residual Representations
영상 인식에서 VLAD는 사전과 관련하여 Residual 벡터에 의해 인코딩되는 표현이다.
Fisher 벡터는 VLAD의 확률적 버전으로 공식화할 수 있다. 두 가지 모두 이미지 검색 및 분류를 위한 강력하고 얕은 표현이다.
벡터의 양자화의 경우, 원래 벡터를 인코딩하는 것보다 나머지 벡터를 인코딩하는 것이 더 효과적이다.
낮은 수준의 비전 및 컴퓨터 그래픽에서 부분 미분 방정식(PDE)를 해결하기 위해 널리 사용되는 멀티그리드 방법은 시스템을 여러 척도의 하위 문제로 재구성하며, 각 하위 문제는 더 좁은 척도와 미세한 척도 사이의 잔존 솔루션을 담당한다.
Multigrid 방법은 두 척도 사이의 잔차 벡터를 나타내는 변수에 의존하는 계층적 기초 전제 조건화이다. 이러한 해결책은 솔루션의 잔차 특성을 모르는 표준 해결책보다 훨씬 빠르게 수렴된다. 그리고 우수한 재구성 또는 전제 조건이 최적화를 단순화할 수 있음을 시사한다.
Shortcut Connection
Shortcut Connection을 활용한 실험과 이론은 예전부터 연구되었다. MLP 훈련의 초기 환행은 네트워크 입력에서 출력으로 연결된 선형 계층을 추가하는 것이었다.
저자의 작업은 동시에 'highway network'라는 게이트 기능과 shortcut connection을 제시한다. 이러한 게이트는 데이터에 의존적이며 매개 변수가 없는 식별자 shortcuts와는 대조적으로 매개 변수가 존재한다.
게이트 shortcut connection이 폐쇄(0에 근접)될 때 고속도로 네트워크의 레이어는 비잔류 기능을 나타낸다.
반대로 저자의 공식은 항상 잔차 함수를 학습한다. 저자의 식별자 shortcut connection은 결코 닫히지 않으며 모든 정보는 항상 학습해야 하는 추가 잔차 함수를 통해 전달된다.
또한 highwat network는 깊이가 극도로 증가됐을 때의 정확도 향상을 보여주지 못했다.
요약
Residual Representations는 이미지 인식에서 VLAD는 사전과 관련하여 잔차 벡터에 의해 인코딩되는 표현이며, Fisher 벡터는 VLAD의 확률 버전으로 제작할 수 있다. 둘 다 이미지 검색과 분류를 위해 강력하고 얕게 표현했으며, 잔차 벡터를 인코딩하는 것이 훨씬 빠르게 수렴한다.
Highway network는 게이트 기능과 shortcut connection이 있는데, 게이트는 shorcut이 0에 근접할 때 네트워크의 레이어는 비잔류 기능을 나타낸다. 하지만 잔차 함수는 결코 닫히지 않으며 항상 학습해야 하는 잔차 함수를 통해 전달된다.
3. Deep Residual Leraning
3.1 Residual Learning
H(x)를 몇 겹으로 쌓인 레이어에 의해 적합할 수 있는 기본 매핑으로 간주하고, x는 이러한 레이어 중 첫 번째에 대한 입력을 나타내도록 하자. 여러 비선형 레이어가 점근적으로 복잡한 함수에 근사할 수 있다고 가정하는 경우, H(x) - x와 같은 잔차 함수에 점근적으로 근사할 수 있다는 가설과 같다.
따라서 축적된 레이어가 H(x)에 근사할 것으로 예상하지 않고, 이러한 레이어가 잔류 함수 F(x) := H(x) - x에 근사하도록 명시적으로 허용한다. 원래 함수는 F(x) + x가 된다.
두 형태 모두 가설대로 원하는 함수를 점근적으로 근사할 수 있어야 하지만 학습의 용이성은 다를 수 있다.
이러한 재공식화는 성능 저하 문제에 대한 반직관적 현상에 의한 이유가 된다. 도입부에서 논의한 바와 같이 추가된 레이어가 ID 매핑으로 구성될 수 있다면 더 깊은 모델은 낮은 레이어보다 큰 훈련 오류를 가져야 한다.
성능 저하 문제는 여러 비선형 레이어에 의한 ID 매핑을 근사화하는 데 어려움을 겪을 수 있음을 시사한다. 잔류 학습 재구성을 통해 ID 매핑이 최적의 경우 해결책은 ID 매핑에 접근하기 위해 단순히 여러 비선형 레이어의 가중치를 0으로 향하게 할 수 있다.
실제로 ID 매핑이 최적일 가능성은 낮지만 저자의 재공식화는 문제를 전제하는 데 도움이 될 수 있다. 최적의 함수가 제로 매핑보다 식별자 매핑에 더 가까우면 해결책이 새로운 기능으로 학습하는 것보다 식별자 매핑을 참조하여 변화를 찾는 것이 더 쉬워야 한다.
저자는 실험을 통해 잔차 함수의 반응이 전반적으로 작다는 것을 보여주며, ID 매핑이 합리적인 전제 조건을 제공함을 시사한다.
요약
H(x)를 기본 매핑으로 간주하고 x가 입력될 때 다수의 비선형 레이어가 복잡한 함수를 점근적으로 근사할 수 있다고 가정하면 H(x) - x를 근사할 수 있다는 가설과 같다.
3.2 Identity Mapping by shortcuts
저자는 누적된 레이어마다 잔차 학습을 채택한다. 블록 설계는 그림 2와 같고, 공식적으로 본 논문에서는 1번과 같이 블록 설계를 정의한다.
x와 y는 레이어의 입출력 벡터이다. 함수 f(x, wi)는 학습할 잔차 매핑을 나타낸다. 그림 2의 두 개의 레이어가 있는 예에서 F=w2o(w1x)는 RELU를 나타내며, 표기를 단순화하기 위해 편향을 생략한다.
F + X는 바로 이 shortcut connection와 element-wise addition을 수행한다. 저자는 추가 후 두 번째 비선형성을 채택한다.
Eqn(1)의 바로 가기 연결은 추가 매개 변수나 계산 복잡성을 도입하지 않는다. 이것은 실제로 매력적일 뿐 아니라 일반 네트워크와 잔차 네트워크 간의 비교에서도 중요하다. 동일한 수의 매개 변수, 깊이, 폭 및 계산 비용을 동시에 갖는 일반, 잔차 네트워크를 공평하게 비교할 수 있다.
x와 F의 차원은 Eqn(1)과 같아야 한다. 그렇지 않을 경우 차원이 일치하도록 바로 가기 연결을 통해 선형 투영 W(s)를 수행할 수 있다.
또한 Eqn(1)에서 제곱 행렬 W(s)를 사용할 수도 있다. 그러나 실험을 통해 ID 매핑이 성능 저하 문제를 해결하는 데 충분하고 경제적이므로 차원을 일치시킬 때만 W(s)가 사용된다는 것을 보여줄 것이다.
잔차 함수 F의 형태는 유연하다. 본 논문에서 실험은 두 개 또는 세 개의 레이어를 갖는 함수 F를 포함한다 (그림5). 더 많은 레이어가 가능하지만 F에 단일 레이어가 있는 경우 Epn(1)은 선형 레이어인 y = W(1)x + x와 유사하며 여기에 대한 이점을 발견하지 못했다.
저자는 또한 위의 주석들이 단순성을 위해 완전연결레이어에관한 것이라고 설명했지만, 컨벌루션 레이어에도 적용이 가능하다. 함수 F(x, wi)는 다중 컨볼루션 레이어를 나타낼 수 있다. element-wise addition은 채널별 두 가지 피처맵에서 수행된다.
요약
식 1은 단순 덧셈으로 인해 복잡한 구조와 연산이 필요없다는 것이 이 방법의 핵심임을 직관적으로 보여준다.
x는 relu 함수를 o번 통과했고, bias는 생략한다. 이 때 x와 F의 차원은 동일하게 맞춰줘야 한다.
3.3 Network Architectures
저자는 다양한 일반, 잔류 네트워크를 테스트했고 일관된 현상을 발견했다. 이를 위한 예를 제공하기 위해 ImageNet에 대한 두 가지 모델을 다음과 같이 설정했다.
일반 네트워크
그림3의 가운데에 위치한 일반 모델의 기준선을 VGG 네트워크에서 영감을 받았다.
컨볼루션 레이어는 대부분 3*3 필터를 가지며, 두 가지 간단한 설계 규칙을 따른다. 첫째는 동일한 출력 기능 맵 크기에 대해 레이어는 동일한 수의 필터를 가지는 것이고, 둘째는 특징맵의 크기가 절반으로 줄면 필터 수가 두 배로 늘어나 레이어당 시간 복잡성을 보존하는 것이다.
저자는 stride가 2인 컨볼루션 레이어로 직접 다운 샘플링을 수행한다.
네트워크는 글로벌 평균 풀링 계층과 소프트맥스가 있는 1,000 개의 완전연결층으로 끝이 난다.
가중 레이어의 총 개수는 그림3에서 34개이다.
해당 모델은 VGG 모델보다 필터가 적고 복잡도가 낮다는 점을 주목해야 한다. 34-레이어는 36억개의 FLOPs(다중 추가)를 가지는데, 이는 VGG-19의 196억 개의 FLOP의 18%에 불과하다.
잔차 네트워크
일반 네트워크를 기반으로 네트워크를 상대 네트워크를 대응 관계의 잔차 버전으로 바꾸는 shorcut 연결을 추가했다.
식별자 shorcuts 연결은 입력과 출력의 차원이 동일할 때 직접 사용할 수 있다. 만약 차원이 증가할 때 두 가지 옵션을 사용할 수 있는데, 첫째는 0으로 패딩하는 것이다. 이 옵션은 추가 매개 변수를 도입하지 않는다. 두 번째는 Eqn(2)를 사용하는 것이다.
두 옵션 모두 바로 가기가 두 가지 크기의 피처 맵을 교차할 때 2의 보폭으로 수행된다.
요약
일반 네트워크
-3*3 필터 사용. stride는 2
-동일한 출력 기능 맵 크기에 대해 레이어는 동일한 수의 필터 보유
-특징맵의 크기가 절반으로 줄면 필터 수를 두 배로 늘려 시간 복잡성 보존
-레이어의 개수는 34개이며 마지막은 1,000개의 클래스로 softmax
잔차 네트워크
-일반 네트워크를 기반으로 shortcut 연결 추가.
-식별자 shortcut 연결은 입력과 출력의 차원이 동일할 때 사용이 가느아므로 차원이 바뀌면 맞춰줘야 함.
3.4 Implementation
ImageNet을 위한 구현에서 [21, 41] 참고를 따른다. 이미지의 크기는 256,480 에서 랜덤하게 샘플링된 더 짧은 면으로 조정한다. 224*224 크롭은 픽셀당 평균이 감산된 상태로 이미지 또는 그 수평 플립에서 랜덤하게 샘플링된다.
표준 색상 증강을 사용하는데, 각 컨볼루션 직후와 활성화 전에 배치 정규화를 사용했다.
[13]과 같이 가중치를 초기화하고 모든 일반, 잔차 네트워크를 처음부터 훈련시킨다. 미니 매치 크기가 256인 SGD를 사용한다.
학습률은 0.1에서 시작해 오류 고도가 있을 때마다 10으로 나누며, 모델은 최대 60*10^4번 반복되도록 훈련한다. 저자는 0.0001의 wight decay와 0.9의 모멘텀을 사용한다. 드롭아웃은 사용하지 않았다.
시험에서는 비교 연구를 위해 10-crop testing을 채택한다. 최상의 결과를 위해 완전 컨볼루션 형식을 채택하고 여러 척도로 점수를 평균한다. (이미지는 더 짧은 쪽이 {224, 256, 384, 480, 640} 중에 있도록 크기를 조정한다.)
요약
모델 설정
-이미지는 224*224로 리사이즈
-배치 정규화 사용
-가중치 초기화 사용
-SGD, 미니 배치는 256
-lr 0.01
-iteration 600,000
-weight decay 0.001
-momentum 0.9
-드롭아웃 사용 안함.
4. Experiments
4.1 ImageNet Classification
1,000개의 클래스로 구성된 ImageNet 2012 데이터로 해당 방법을 평가했다. 모델은 128만 개의 교육 이미지로 교육하고 500,000개의 검증 이미지로 평가했다. 또한 테스트 서버에서 보고한 100,000개의 테스트 셋에 대한 최종 결과를 얻는다. 그리고 상위 1과 5의 오류율을 모두 평가한다.
일반 네트워크
18, 34층의 일반 네트워크를 평가했다. 34층의 일반 네트워크는 그림3의 중간에 있고, 18층 네트워크도 비슷한 형태이다. 자세한 아키텍처는 표 1에 나와있다.
표 2의 결과는 보다 깊은 34 레이어 일반 네트워크보다 더 높은 검증 에러를 가지고 있음을 보여준다. 그 이유를 밝히기 위해 그림 4에서 훈련 과정에서 훈련, 검증 과정을 비교했다.
성능 저하 문제를 관찰했을 때, 18층 네트워크보다 34층 네트워크가 더 많은 훈련 오류를 가졌다. 저자는 이러한 최적화가 어려워지는 이유가 경사도 손실은 아닐 것이라고 주장한다.
또한 역전파가 배치 정규화로 좋은 정규화를 보이는 지도 검증한다. 그래서 forward와 backward 신호도 모두 사라지지 않는다. 실제로 34단 네트워크는 여전히 표 3과 같이 여전히 경쟁적으로 정확도를 달성할 수 있고, 해결책이 어느 정도는 작동함을 입증한다. 저자는 심층 네트워크가 지수적으로 낮은 수렴율을 가질 수 있으며, 이는 훈련 오류의 감소에 영향을 줄 수 있다고 추측한다.
이러한 최적화의 어려움이 있는 이유는 앞으로 계속 연구될 것이다.
요약
일반 네트워크는 18레이어보다 34 레이어의 에러율이 더 높다. 배치 정규화로 학습이 되었고 역전파와 순전파 모두 정상이다. 그렇기에 이 문제는 경사도 소실로 인한 문제가 아니다. 그저 깊은 네트워크는 낮은 수렴률을 가지고 있어 트레이닝 에러를 감소시키는 것이 어려울 것으로 예상한다.
잔차 네트워크
다음으로 18, 34층 잔류 네트워크를 평가한다. 기본 구조는 일반 네트워크와 동일하지만 그림3의 오른쪽과 같이 3*3 필터의 각 쌍에 바로가기 연결이 추가되었다.
첫 번째 비교은 표2와 그림4의 오른쪽은 모든 바로가기에 대해 식별자 매핑을 사용하고, 크기를 늘리기 위해 제로 패딩을 사용한다. 그래서 이들은 평범한 것에 비교해 별도의 매개 변수가 없다.
표2와 그림 4에 세 가지 주요 관측치가 있다. 첫째는 잔류 학습으로 상황이 역전된다. 34 레이어 ResNet이 18레이어 ResNet보다 2.8% 검증 에러가 줄어들었다. 더 중요한 것은 34층 ResNet이 상당히 낮은 훈련 오류를 나타내며 검증 데이터로 일반화할 수 있다는 점이다. 이 설정에서 성능 저하 문제가 잘 해결되었음을 나타내며, 증가된 깊이에서 정확도 이득을 얻을 수 있다.
둘째는 일반 네트워크와 비교해 34계층 ResNet은 상위 1개 오류를 3.5% 감소시켜 성공적으로 훈련 오류를 줄였다. 이 비교는 매우 심층적인 시스템에 대한 잔차 학습의 효과를 검증한다.
마지막으로 18계층 일반, ResNet이 비교적 정확하지만 18층 ResNet이 더 빨리 수렴한다는 점이다(그림 4 오른쪽과 왼쪽). 네트워트가 지나치게 깊지 않아도 현재의 SGD는 여전히 일반 네트워크에 대한 좋은 해결책을 찾을 수 있다. 이 경우 ResNet은 초기 단계에서 더 빠른 수렴을 제공함으로써 최적화를 용이하게 한다.
요약
잔차 네트워크에서는 모든 shortcuts를 위해 identity 매핑을 사용했고, 차원 증가를 위해서는 제로 패딩을 사용한다.
일반 네트워크와 달리 깊은 네트워크의 성능이 더 좋다. 또한 일반 네트워크 비해 학습 오류를 더 줄였다. 18레이어간 오류율은 비슷했지만 더 수렴이 빨랐다.
Identity vs Projection Shortcuts
매개 변수가 없는 식별자 지름길이 교육에 도움이 되는 것은 입증되었다. 다음은 Eqn 2의 Projection Shortcuts이다. 표 3에서 저자는 세 가지 옵션을 비교했다. 첫째는 크기를 증가시키는 대신 제로 패딩 shorcut을 사용했고, 모든 바로가기는 매개 변수가 없다. 둘째는 Projection 바로가기로 차원을 증가시키고 다른 바로가기는 식별자 바로가기이다. 마지막은 Projection 바로가기이다.
표 3은 세 가지 옵션 모두 일반 옵션보다 훨씬 낫다는 것을 보여준다. B가 A보다 약간 낫다. 저자는 A의 제로 패딩 차원이 실제로 잔차 학습이 없기 때문이라고 주장한다.
C는 B보다 약간 낫고, 이것을 많은 Projection 바로가기에 의해 도입된 추가 매개 변수의 탓으로 예측한다. 그러나 A, B, C 간의 작은 차이는 Projection 바로가기가 성능 저하 문제를 해결하는 데 필수적이지 않다는 것을 나타낸다. 그래서 저자는 메모리, 시간 복잡성과 모델의 크기를 줄이기 위해 논문의 나머지 부분에서는 옵션 C를 사용하지 않는다. 식별자 바로가기는 아래에 도입된 병목 아키텍처의 복잡성을 증가시키지 않는 데 특히 중요하다.
요약
1) 차원 증가를 위해 제로 패딩 사용
2) 차원 증가를 위해 Projection shorcuts 사용
3) 모든 shortcuts들이 Projection
세 가지 방법으로 진행했을 때, 3, 2, 1 순으로 효과가 좋았다.
2가 1보다 나은 것은 제로 패딩 과정에 잔차 학습 효과가 없기 때문이다.
3이 1보다 좋은 이유는 여분 파라미터가 많은 projection shortcuts에 사용되었기 때문이다.
하지만 성능 차이는 작기 때문에 projection shortcuts가 degradation 문제를 해결하는 데 필수적인 것은 아니다. 그래서 메모리가 증가하고 모델이 복잡해지는 것을 막기 위해 사용하지 않겠다.
Deeper Bottleneck Architectures
다음은 ImageNet용 심층 네트워크에 대한 설명이다. 감당 가능한 학습 시간에 대해 블록 설계를 병목 설계로 수정한다. 각 잔여 기능 F에 대해 2가 아닌 3개의 레이어로 이루어진 스택을 사용한다. (그림 5) 3개의 레이어는 1*1, 3*3, 1*1 컨볼루션으로, 1*1 레이어는 크기를 줄이고 복원하는 역할을 담당하며, 작은 bottleneck을 만든다. 그림 5는 두 설계가 비슷한 시간 복잡성을 갖는다는 것을 보여준다.
매개 변수가 없는 식별자 바로가기는 병목 구조 아키텍처에 특히 중요하다. 그림 5의 오른쪽의 식별자 바로가기를 Projection으로 대체하면 바로가기가 두 개의 고차원적 끝에 연결되어 있어 시간 복잡성과 모델 크기가 배가 되는 것을 볼 수 있다. 따라서 식별자 바로가기는 병목 설계 시 보다 효율적인 모델로 이어진다.
요약
기존의 3*3, 3*3 두 개의 레이어를 1*1, 3*3, 1*1로 재구성한다. 1*1은 차원을 감소하고 증가시켜 더 작은 입력, 출력 차원으로 보틀넥을 만든다.
수식상으로 두 개의 시간 복잡도는 비슷하다. 그런데 이걸 Projection으로 연결하면 시간 복잡도와 모델 크기가 배가 된다. (위에서 Projection shortcuts를 사용하지 않겠다고 선언한 이유)
50-layer ResNet:
34층 네트워크의 각 2계층 블록을 3계측 병목 블록으로 교체하여 50계층 ResNet을 생성했다. 차원 증가를 위해 옵션 B를 사용했고, 해당 모델은 38억 개의 FLOP을 가지고 있다.
101-layer and 152-layer ResNets:
더 많은 3계층 블록을 사용하여 101, 152 레이어 ResNet을 구현했다. 깊이가 크게 증가했지만 152 레이어 ResNet은 여전히 VGG-16, 19보다 복잡도가 낮다.
40, 101, 152 레이어 ResNet은 35 레이어보다 훨씬 정확하다. (표 3과 4 참고) 성능 저하 문제를 관측하지 않아 높은 깊이에서 정확도 향상을 누렸다. 모든 평가 지표 (표3, 4)에 대해 깊이의 이점을 확인할 수 있다.
Comparisons with State-of-the-art Methods.
표 4에서 이전의 최고의 단일 모델 결과와 비교했다. 34 레이어 ResNet은 매우 경쟁적인 정확도를 달성했다. 152 레이어 ResNet은 단일 모델로 검증 에러가 4.49%였다. 이 결과는 이전의 모든 앙상블 결과를 능가한다(표5). 저자는 서로 다른 깊이의 6개의 모델을 결합하여 앙상블을 형성했다. (제출때는 152개의 레이어만 포함했다) 이로 인해 테스트 셋에서 3.57%의 훈련 오류가 발생했다. 그리고 ILSVRC 2015에서 1등을 차지했다.
4.2 CIFAR-10 and Analysis
저자는 10개 클래스의 50,000개의 훈련 이미지와 10,000개의 테스트 이미지로 구성된 CIFAR-10 데이터 셋에 대해 연구를 수행했다. 매우 심층적인 네트워크의 동작에 초점을 맞추었고, 좋은 결과를 추진하는 것에 있지 않기에 의도적으로 다음과 같은 간단한 아키텍처를 사용한다.
일반, 잔류 네트워크는 그림3을 따른다. 네트워크 입력은 픽셀당 평균이 빼진 32*32 이미지이다. 첫 번째 층은 3*3 컨볼루션이다. 그런 다음 크기 {32, 16, 8}의 형상 맵에 각각 3×3 컨볼루션을 가진 6n 레이어의 스택을 사용하고, 각 형상 맵 크기에 대해 2n 레이어를 사용한다. 필터 수는 각각 {16, 32, 64}개 이다. 서브샘플링은 2의 보폭으로 컨볼루션에 의해 수행된다. 네트워크는 글로벌 평균 풀링, 10방향 완전 연결 계층 및 소프트맥스로 끝난다. 총 6n+2개의 쌓인 가중 레이어가 있고, 다음 표에서는 아키텍처를 요약합니다.
shortcut 연결이 사용되면 3×3 레이어 쌍(총 3n 바로 가기)에 연결된다. 이 데이터 세트에서 우리는 모든 경우(즉, 옵션 A)에서 ID 바로 가기를 사용하므로, 잔여 모델은 일반 모델과 깊이, 폭 및 매개 변수 수가 정확히 같다.
0.0001 weight decay, 0.9 모멘텀을 사용하고 배치 정규화와 가중치 초기화를 했지만 드롭아웃은 적용하지 않았다. 모델들은 두 개의 GPU로 128의 미니 배치 사이즈로 훈련되었다. 학습률은 0.1로 시작해서 32K, 48K 반복에서 10으로 나누고, 45K, 5K 훈련, 검증 셋으로 데이터를 나눈다. 훈련을 위해 간단한 데이터 증강을 했고, 각 면에 4픽셀이 패딩되어 32*32 크롭이 패딩 된 이미지 또는 수평 플립에서 랜덤하게 샘플링된다. 테스트의 경우 원본 32*32 이미지의 단일 view만 평가한다.
n이 {3, 5, 7, 9}일 때를 20, 32, 55, 56층 네트워크와 비교했다. 그림 6의 왼쪽은 일반 네트워크의 동작을 보여준다. 깊은 일반 네트워크는 깊이가 증가하여 더 깊이 들어갈 때 더 높은 테스트 에러율을 보인다. 이 현상은 ImageNet(그림 4의 왼쪽)및 MNIST와 유사하여 이러한 최적화 어려움이 근본적인 문제임을 시사한다.
그림 6의 가운데는 ResNet의 동작을 보여준다. 또한 ImageNet 사례 (그림 4의 오른쪽)과 유사하게 ResNet은 최적화 어려움을 극복하고 깊이가 증가할 때 정확도 향상을 보여주었다.
110 레이어의 n=18일 때 ResNet을 자세히 알아보자. 이 경우 저자는 0.1의 초기 학습 속도가 수렴하기에는 너무 크다는 것을 발견했다. 따라서 0.01을 사용하여 테스트 오류를 80% 미만으로 떨어질 때까지 훈련을 한 다음 0.1로 돌아가 훈련을 지속했다. 나머지 학습 일정은 전과 같다.
110 레이어 네트워크는 잘 수렴한다. (그림 6 중간) FitNet및 HighWay와 같은 다른 심층 및 얕은 네트워크에 비해 매개 변수가 적지만 좋은 결과에 속한다.
Analysis of Layer Responses.
그림 7은 layer response의 표준 편차를 보여준다. response는 배치 정규화 이후와 다른 비선형성 (ReLU, addition) 이전의 각 3*3 레이어의 출력이다. ResNet의 경우 이 분석을 통해 잔차 함수의 반응 강도를 확인할 수 있다.
그림 7은 ResNet이 일반 응답자보다 일반적으로 반응이 작다는 것을 보여준다ㅣ. 이러한 결과는 잔차 함수가 비잔차 함수보다 일반적으로 0에 가까울 수 있다는 기본 이론(3.1)을 뒷받침한다. 또한 그림 7의 ResNet-20, 56 및 110 간의 비교에서 입증되었듯 더 깊은 ResNet의 응답 크기가 더 작다는 것을 발견했다. 더 많은 레이어가 있을 때 ResNet의 개별 레이어는 신호를 덜 수정하는 경향이 있는 것이다.
요약
resnet이 일반 네트워크에 비해 더 적은 반응을 보인다. 앞서 주장한 가설 중 잔차 함수가 비잔차 함수보다 일반적으로 더 0에 가까울 수 있다는 가설을 뒷받침한다.
Exploring Over 1000 layers.
1,000개 이상의 레이어의 심층 모델을 탐구했다. 훈련된 1,202 레이어 네트워크로 이어지는 n=200을 설정했다. 최적화에 어려움을 보여주지 않았으며, 이 103 레이어 네트워크는 훈련 오류 0.1을 달성할 수 있었다. 검증 에러율 또한 상당히 양호하다.
그러나 깊은 모델에서는 여전히 미해결 문제가 있다. 이 1,202 레이어 네트워크의 테스트 결과는 둘 다 유사한 검증 에러율을 가지고 있지만 110 레이어 네트워크보다 더 나쁘다. 저자는 이것이 과적합 때문이라 주장한다.
1,202 네트워크는 불필요하게 크다. 작은 데이터셋에서 최상의 결과를 얻기 위해 amxout 또는 DROOP과 같은 강력한 정규화가 적용된다. 본 논문에서는 최적화의 어려움에 초점을 흐리지 않고 max out, drop out을 사용하지 않고 설계에 의한 심층 및 얕은 아키텍처를 통한 정규화만 부과한다. 하지만 더 강력한 정규화와 결합하면 결과를 개선할 수 있고 앞으로 연구할 것이다.
요약
1,202 개의 레이어로 구성된 모델은 110개로 구성된 모델보다 나쁘다. 이것은 과적합 때문이라고 생각하며, maxout 이나 드롭아웃 같은 규제를 사용한다면 향상될 것이다.
4.3 Object Detection on PASCAL and MS COCO
이 방법은 다른 인식 작업에서도 좋은 일반화 성능을 보인다. 표 7, 8은 PLASCAL VOL 2007, 2012 및 COCO에 대한 객체 감지 기준 결과를 보여준다. Faster-R-CNN을 탐지 방법으로 채택했다. 여기서 저자는 VGG-16을 ResNet-101로 교체하는 것의 개선에 관심이 있다. 두 모델 사용의 감지 구현은 동일하므로 이득은 더 나은 네트워크에만 귀속될 수 있다.
가장 주목할만한 것은 까다로운 COCO 데이터 셋에서 COCO의 표준 매트릭이 6% 증가하여 28%의 상대적 개선을 얻은 것이다. 이러한 이득은 오로지 학습된 표현에 기인한다.
심층 잔류를 기반으로 ILSVRC & COCO 2015 대회에서 여러 트랙에서 1위를 차지했다. ImageNet 탐지, ImageNet 로컬라이제이션, COCO 탐지 및 분할 등으로 자세한 내용은 부록에 있다.
구현
-데이터셋은 RPS 데이터셋 사용
#가위바위보 데이터 셋
import os, zipfile
import torchvision
import random
import numpy as np
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
%matplotlib inline
#압축풀기
local_zip = '/tmp/rps.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp/')
local_zip = '/tmp/rps-test-set.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp/')
zip_ref.close()
#Random Resize & Random Crop
transform = transforms.Compose([
transforms.Resize(random.randint(256, 480)),
transforms.RandomCrop(224),
transforms.ToTensor()
])
train_dataset = ImageFolder(root = '/tmp/rps/', transform = transform)
train_loader = DataLoader(train_dataset, batch_size = 128, shuffle = True, num_workers = 0) #원래 256인데 메모리가 못버팀
fig = plt.figure(figsize = (22,22))
i = 0
for batch_idx, (inputs, targets) in enumerate(train_loader) :
if batch_idx % 3 == 0 :
i += 1
subplot = fig.add_subplot(1, 7, i)
subplot.set_title(targets[0])
img = inputs[0].numpy() #[256, 3, 224, 224] => [3, 224, 224]
subplot.imshow(np.transpose(img, (1, 2, 0))) #[224, 224, 3]
plt.show()
valid_dataset = ImageFolder(root = '/tmp/rps-test-set/', transform = transform)
valid_loader = DataLoader(valid_dataset, batch_size = 128, shuffle = True, num_workers=0)
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#모델 설계
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
def seqCreate(inshape, kernel_size, stride, padding, outshape) :
seq = nn.Sequential(
nn.Conv2d(inshape, outshape, kernel_size = kernel_size, stride=stride, padding=padding),
nn.BatchNorm2d(outshape),
nn.ReLU(),
nn.Conv2d(outshape, outshape, kernel_size = kernel_size, stride=1, padding=padding),
nn.BatchNorm2d(outshape),
nn.ReLU()
)
return seq
class Resnet(nn.Module):
def __init__(self, output_shape):
super(Resnet, self).__init__()
#224
self.conv_1 = nn.Conv2d(3, 64, kernel_size = 7, stride=2, padding=3) #112
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding = 1) # 56
#conv2 - 3 56
self.block_1_1 = seqCreate(64, 3, 1, 1, 64)
self.block_1_2 = seqCreate(64, 3, 1, 1, 64)
self.block_1_3 = seqCreate(64, 3, 1, 1, 64)
#conv2 -> 3 28
self.block_2 = seqCreate(64, 3, 2, 1, 128)
#conv3 * 3 28
self.block_3_1 = seqCreate(128, 3, 1, 1, 128)
self.block_3_2 = seqCreate(128, 3, 1, 1, 128)
self.block_3_3 = seqCreate(128, 3, 1, 1, 128)
#conv 3 -> 4 14
self.block_4 = seqCreate(128, 3, 2, 1, 256)
#conv 4 * 5
self.block_5_1 = seqCreate(256, 3, 1, 1, 256)
self.block_5_2 = seqCreate(256, 3, 1, 1, 256)
self.block_5_3 = seqCreate(256, 3, 1, 1, 256)
self.block_5_4 = seqCreate(256, 3, 1, 1, 256)
self.block_5_5 = seqCreate(256, 3, 1, 1, 256)
#conv 4 -> 5 7
self.block_6 = seqCreate(256, 3, 2, 1, 512)
#conv 5*2
self.block_7_1 = seqCreate(512, 3, 1, 1, 512)
self.block_7_2 = seqCreate(512, 3, 1, 1, 512)
#Global Average Pooling은 일반 풀링과 다르게 Flatten()이 목적
self.avgpool = nn.AdaptiveAvgPool2d((1,1)) # 1
self.linear = nn.Linear(512, output_shape)
def zero_padding(self, small_dim, big_dim) :
#zero-padding이란 게 Conv의 패딩과 똑같은 거면
#패딩시켜서 더해주고 풀링해주면 되는 거 아닐까?
# 3*3과 5*5를 받았다고 치면
# 0 0 0 0 0 1 2 3 4 5 1 2 3 4 5
# 0 1 1 1 0 6 5 4 3 2 6 6 5 4 3
# 0 1 1 1 0 + 1 2 3 4 5 = 1 3 4 5 5
# 0 1 1 1 0 5 4 3 2 1 5 5 4 3 1
# 0 0 0 0 0 1 2 3 4 5 1 2 3 4 5
small_dim_dimension = small_dim.shape[1] #차원
small_dim_size = small_dim.shape[3] #크기
#작은 차원 패딩시키기
self.zero_pad = nn.ZeroPad2d(int(small_dim_size / 2))
if small_dim_size % 2 == 1 :
self.zero_pad = nn.ZeroPad2d((int(small_dim_size / 2),int(small_dim_size / 2)+1, #좌, 우
int(small_dim_size / 2),int(small_dim_size / 2)+1)) #상, 하
output = self.zero_pad(small_dim)
#큰 차원의 특성맵 수가 작은 차원의 특성맵 수의 1/2라서 두 배로 늘려줘야 함.
big_output = F.pad(big_dim, (0, 0, 0, 0, 0, int(small_dim_dimension/2)))
output = output + big_output
#그리고 다시 3*3으로.
#대충 커널 2짜리로 풀링하면 반으로 줄어듬
self.maxpool_padding = nn.MaxPool2d(2, 2)
output = self.maxpool_padding(output)
return output
def forward(self, x):
#conv1 : 224 => 112
output = self.conv_1(x)
#conv2 : 112 => 56 : 3회
output = self.maxpool(output)
save_output = output.clone()
output = self.block_1_1(output)
output = output + save_output
save_output = output.clone()
output = self.block_1_2(output)
output = output + save_output
save_output = output.clone()
output = self.block_1_3(output)
output = output + save_output
save_output = output.clone()
#conv3 : 56 => 28 : 4회
output = self.block_2(output)
output = self.zero_padding(output, save_output)
save_output = output.clone()
output = self.block_3_1(output)
output = output + save_output
save_output = output.clone()
output = self.block_3_2(output)
output = output + save_output
save_output = output.clone()
output = self.block_3_3(output)
output = output + save_output
save_output = output.clone()
#conv4 : 28 => 14 : 6회
output = self.block_4(output)
output = self.zero_padding(output, save_output)
save_output = output.clone()
output = self.block_5_1(output)
output = output + save_output
save_output = output.clone()
output = self.block_5_2(output)
output = output + save_output
save_output = output.clone()
output = self.block_5_3(output)
output = output + save_output
save_output = output.clone()
output = self.block_5_4(output)
output = output + save_output
save_output = output.clone()
output = self.block_5_5(output)
output = output + save_output
save_output = output.clone()
#conv5 : 14 => 7 : 3회
output = self.block_6(output)
output = self.zero_padding(output, save_output)
save_output = output.clone()
output = self.block_7_1(output)
output = output + save_output
save_output = output.clone()
output = self.block_7_2(output)
output = output + save_output
save_output = output.clone()
output = self.avgpool(output)
output = output.squeeze()
output = self.linear(output) #crossentropy에 softmax가 있으니 굳이 추가 x
return output
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
#device = 'cpu'
print(device)
resnet = Resnet(3).to(device) #rock, scissor, paper
#weight_decay = 0.0001, momentum = 0.9, lr = 0.01, SGD
#lr은 초반에는 0.01 사용하다 학습 에러가 80% 미만이 되면 0.1로
optimizer = torch.optim.SGD(resnet.parameters(), lr = 0.01, momentum = 0.9, weight_decay = 0.0001)
criterion = nn.CrossEntropyLoss().to(device)
from torchsummary import summary
if torch.cuda.is_available() : summary(resnet, (3, 224, 224), device = "cuda")
else : summary(resnet, (3, 224, 224), device = "cpu")
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#학습
from tqdm.notebook import tqdm
n_epoch = 40 #40까지 돌렸을 때 상승률이 정체되지 않는 걸 보니 EPOCH를 더 많이 늘리면 정확도 향상 가능
is_lr_changed = False
for epoch in range(n_epoch) :
epoch_loss = 0
acc = 0
for train_x, train_y in tqdm(train_loader) :
train_x = train_x.to(device)
train_y = train_y.to(device)
predict = resnet(train_x)
loss = criterion(predict, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss / len(train_loader)
correct_prediction = torch.argmax(predict, 1) == train_y # [True, Flase, True ...]
correct_prediction = correct_prediction.sum()
acc += correct_prediction
acc = acc.item() / (128 * len(train_loader))
print('Epoch : {}/{}, loss : {:.5f}, acc : {:.5f}'.format(epoch+1, n_epoch, epoch_loss, acc))
if(acc > 0.2) and epoch >= 10 and not is_lr_changed : #학습 에러 80% 미만시 0.1로 변경
for g in optimizer.param_groups :
print('lr 변경')
g['lr'] = 0.1
is_lr_changed = True
if acc > 0.98 and loss < 0.1:
#early stop
break
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#테스트 데이터로 검증
with torch.no_grad() :
val_loss = 0
acc = 0
for test_x, test_y in tqdm(valid_loader) :
test_x = test_x.to(device)
test_y = test_y.to(device)
predict = resnet(test_x)
loss = criterion(predict, test_y)
val_loss += loss / len(valid_loader)
correct_prediction = torch.argmax(predict, 1) == test_y # [True, Flase, True ...]
correct_prediction = correct_prediction.sum()
acc += correct_prediction
#5 개만 뽑아서 시각화
r = random.sample(range(0, len(test_x)), 5)
fig = plt.figure(figsize = (22, 22))
result_list = ['paper', 'rock', 'scissors']
for i in range(5) :
label = predict[r[i]]
label = torch.argmax(label).item()
img = test_x[r[i]].to('cpu').numpy()
img = np.transpose(img, (1,2,0))
subplot = fig.add_subplot(1, 5, i+1)
subplot.set_title(result_list[label])
subplot.imshow(img, cmap=plt.cm.gray_r)
plt.show()
acc = acc.item() / (128 * len(valid_loader))
print('loss : {:.5f}, acc : {:.5f}'.format(val_loss, acc))
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#----------------------------------------------#
#ten_crop
#원래는 {224 256 384 480 640} 인데 지금은 하나만
transform = transforms.Compose([
transforms.Resize(256),
transforms.ToTensor(),
transforms.TenCrop([224, 224]),
])
valid_dataset = ImageFolder(root = '/tmp/rps-test-set/', transform = transform)
valid_loader = DataLoader(valid_dataset, batch_size = 1, shuffle = True, num_workers=0)
for test_x, test_y in tqdm(valid_loader) :
result_list = torch.FloatTensor([0, 0, 0]).to(device)
for i, xx in enumerate(test_x) :
#print(i, '번째 : ', xx.shape)
with torch.no_grad() :
input_x = xx.to(device)
predict = resnet(input_x)
result_list = result_list + predict
print(result_list)
result = torch.argmax(result_list)
result_list2 = ['paper', 'rock', 'scissors']
fig = plt.figure(figsize = (5, 5))
img = xx.to('cpu').numpy()
img = img[0]
img = np.transpose(img, (1,2,0))
plt.title(result_list2[result])
plt.imshow(img)
break
'IT 지식 > 인공지능_딥러닝' 카테고리의 다른 글
[논문] DenseNet (0) | 2021.03.07 |
---|---|
[논문] AlexNet (0) | 2021.02.27 |
자연어 처리 : 트랜스포머 (0) | 2021.02.22 |
Convolutional Neural Network (합성곱 신경망, CNN) (0) | 2021.01.27 |