<jupyter_start><jupyter_text>3차원 데이터의 합성곱 연산, 풀링 계층 - 지금까지 2차원 형상을 다루는 합성곱 연산을 살펴봤다. 그러나 이미지만 해도 세로,가로,채널까지 총 3차원 데이터이다. - 채널 쪽으로 특징 맵이 여러 개 있다면 입력 데이터와 필터의 합성곱 연산을 채널마다 수행하고, 그 결과를 더해서 하나의 출력을 얻는다. - 3차원 합성곱 연산에서 주의할 점은 입력 데이터의 채널 수와 필터의 채널 수가 같아야 한다는 것이다.   - 한편 필터 자체의 크기는 원하는 값으로 설정할 수 있다. (단, 모든 채널의 필터가 같은 크기여야 한다.) - 블록으로 생각하기  - 3차원의 합성곱 연산은 데이터와 필터를 직육면체 블록이라고 생각하면 쉽다. 예를들어 채널수C, 높이H, 너비W인 데이터의 형상은(C,H,W)로 쓴다. 필터도 같은 순서로 쓴다.   - 합성곱 연산의 출력으로 다수의 채널을 내보내려면 어떻게 해야할까    - 다수의 필터(가중치)를 사용하는 것이다.     - 이런 방식으로 완성된 블록을 다음 계층으로 넘기겠다는 것이 CNN의 처리 흐름이다.  - 이상에서 보듯 합성곱 연산에서는 필터의 수도 고려해야 한다. 그런 이유로 필터의 가중치 데이터는 4차원 데이터이며(출력 채널 수, 입력 채널 수, 높이, 너비) 순으로 쓴다.  - 합성곱 연산에서도 완전연결 계층과 마찬가지로 편향이 쓰인다.   - 편향은 채널 하나에 값 하나씩으로 구성된다. 이 예에서는 편향의 현상은 (FN,1,1)이고 필터의 출력 결과의 형상은 (FN,OH,OW)이다. 이 두블록을 더하면 편향의 각 값이 필터의 출력인 (FN,OH,OW) 블록의 대응 채널의 원소 모두에 더해진다.     - 참고로 형상이 다른 블록의 덧셈은 넘파이의 브로드캐스트 기능으로 쉽게 구현 가능하다. - 배치 처리  - 합성곱 연산도 마찬가지로 배치 처리를 지원하고자 한다. 그래서 각 계층으로 흐르는 데이터의 차원을 하나 늘려 4차원 데이터로 저장한다. 구체적으로는 데이터를 (데이터 수, 채널 수, 높이, 너비) 순으로 저장한다.   - 여기에서 주의할 점 : 신경망에 4차원 데이터가 하나 흐를 따마다 데이터 N개에 대한 합성곱 연산이 이뤄진다는 것이다. 즉, N회 분의 처리를 한 번에 수행하는 것이다.  - 풀링 계층  - 풀링은 세로, 가로 방향의 공간을 줄이는 연산이다.   - 2x2 최대 풀링은 최댓값을 구하는 연산으로 2x2는 대상 영역의 크기를 뜻한다. 즉 2x2 최대 풀링은 2x2 크기의 영역에서 가장 큰 원소 하나를 꺼낸다. 또, 스트라이드는 이 예에서는 2로 설정했으므로 2x2 윈도우가 원소 2칸 간격으로 이동한다. 참고로 풀링의 윈도우 크기와 스트라이드는 같은 값으로 설정하는 것이 보통이다. - 풀링 계층의 특징  - 1) 풀링 계층은 합성곱 계층과 달리 학습해야 할 매개변수가 없다. 풀링은 대상 영역에서 최댓값이나 평균을 취하는 명확한 처리이므로 특별히 학습할 것이 없다.   - 2) 풀링 연산은 입력 데이터의 채널 수 그대로 출력 데이터로 내보낸다.   - 3) 입력 데이터가 조금 변해도 풀링의 결과는 잘 변하지 않는다. --- - 합성곱 풀링 계층 구현하기  - CNN에서 계층 사이를 흐르는 데이터는 4차원이다. 예를 들어 데이터의 형상이 (10,1,28,28)이라면, 이는 높이28, 너비28, 채널1인 데이터가 10개라는 이야기이다.<jupyter_code>import numpy as np
x = np.random.rand(10,1,28,28)
x.shape<jupyter_output><empty_output><jupyter_text>- 여기 10개 중 첫 번째 데이터에 접근하려면 단순히 x[0], 또, 첫 번째 데이터의 첫 채널의 공간 데이터에 접근하려면 x[0][0],x[0,0]을 통해 접근할 수 있다. - 이처럼 CNN은 4차원 데이터를 다룬다. 그래서 합성곱 연산의 구현은 복잡해질 것 같지만, 다음 절에서 설명하는 im2col(image to cloumn)이라는 트릭이 문제를 단순하게 만들 수 있다.  - im2col  - 입력 데이터를 필터링(가중치 계산)하기 좋게 전개하는(펼치는) 함수이다. 3차원 입력 데이터에 im2col을 적용하면 2차원 행렬로 바뀐다. 정확히는 배치 안의 데이터 수까지 포함한 4차원 데이터를 2차원으로 변환한다.   - im2col은 필터링하기 좋게 입력 데이터를 전개한다. 구체적으로는 입력 데이터에서 필터를 적용하는 영역을 한 줄로 늘어놓는다.   - 이 전개를 필터를 적용하는 모든 영역에서 수행하는 게 im2col이다.  - 필터 적용 영역이 겹치게 되면 im2col로 전개한 후의 원소 수가 원래 블록의 원소 수보다 많아진다. 그래서 im2col을 사용해 구현하면 메모리를 더 많이 소비하는 단점이 있다. 그렇지만 이 문제를 행렬 계산으로 만들면 선형 대수 라이브러리를 활용해 효율을 높일 수 있다.  - im2col 방식으로 출력한 결과는 2차원 행렬이다. CNN은 데이터를 4차원 배열로 저장하므로 2차원인 출력 데이터를 4차원으로 변형한다. 이상이 합성곱 계층의 구현 흐름이다. - im2col은 필터 크기, 스트라이드, 패딩을 고려하여 입력 데이터를 2차원 배열로 전개한다. 구현해보자<jupyter_code>import sys, os
sys.path.append(os.pardir)
from common.util import im2col

x1 = np.random.rand(1, 3, 7, 7)
col1 = im2col(x1, 5, 5, stride = 1, pad =0)
print(col1.shape)

x2 = np.random.rand(10, 3, 7, 7)
col2 = im2col(x2, 5, 5, stride = 1, pad =0)
print(col2.shape)<jupyter_output>(9, 75)
(90, 75)<jupyter_text>- im2col을 사용하여 합성곱 계층을 구현해보자<jupyter_code>class Convolution:
    def __init__(self, W, b, stride=1,pad=0):
        self.W=W
        self.b=b
        self.stride=stride
        self.pad=pad
    def forward(self, x):
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        out_h = int(1 + ( H + 2*self.pad - FH)/ self.stride)
        out_w = int(1 + ( W + 2*self.pad - FW)/ self.stride)
        
        col = im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T
        out = np.dot(col, col_W) + self.b
        
        out = im2col(x,FH,FW,self.stride, self.pad)
        
        return out<jupyter_output><empty_output><jupyter_text>- 이상이 합성곱 계층의 forward 구현이다. - 합성곱 계층의 역전파는 im2col을 역으로 처리한다.  - 풀링 계층 구현도 합성곱 계층과 마찬가지로 im2col을 사용해 입력 데이터를 전개한다.   - 다만, 풀링의 경우엔 채널 쪽이 독립적이라는 점이 합성곱 계층 때와 다르다.<jupyter_code>class Pooling:
    def __init__(self, pool_h, pool_w, stride = 1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stirde = pool_stirde
        self.pad = pad
        
    def forward(self, w):
        N, C, H, W = x.shape
        out_h = int(1 + ( H + 2*self.pad - FH)/ self.stride)
        out_w = int(1 + ( W + 2*self.pad - FW)/ self.stride)
        
        # 전개 
        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h *self.pool_w)
        
        # 최댓값
        out = np.max(col, axis = 1)
        
        # 성형
        out = out.reshape(N, out_h, out_w, C).transpose(0,3,1,2)
        
        return out<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09971001561454383, 'fraction_numerical': 0.029667633281284855, 'mean_word_length': 2.3663663663663663, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '486678', 'license': 'Apache-2.0', 'n_tokens_mistral': 3547, 'n_tokens_neox': 4403, 'n_words': 729, 'path': '_notebooks/2022-02-19-dl.ipynb', 'repo_name': 'rhkrehtjd/INTROdl', 'size': '4483'}