본문 바로가기
학부생의학부연구생/_deep_learning

im2col_convolution lowering : C로 구현해보자!

by 호상 🐧 2022. 6. 22.

>> 본 과제는 학부연구생을 진행하며 수행한 내용을 복습 및 기록 하기 위해 작성 하였습니다.

 

Im2col >

https://arxiv.org/abs/1410.0759 cuDNN: Efficient Primitives for Deep Learning

본 논문은 nvidia 에서 발표한 논문으로 nvidia에서 제공하는 cuDNN ( DNN primitives)를 설명한다. introduction 에서 CNN 은 기존 선형대수 라이브러리와는 달리 dense kernel 을 이용해 계산하는 점을 지적하며, 이를 위해 최적화된 primitives를 어떻게 만들었는지에 대해 서술한다. 

 

여기서 말하는 최적화된 promitives 는 3가지 방법으로 서술되고 그중  im2col 의 대한 접근이 있다. im2col 은 3d-tensor 인 input image 를 1d-tensor 로 만드는 방법이다. im2col 를 풀어서 말하자면 'image to column' 으로 쉽게 말해 image를 column 으로 만들겠다는 의도이다. 이를 수행하면 3d 이미지를 2d matrix 로 표현할 수 있다. 2d matrix를 통해 GEMM 연산을 진행하겠다는 것인데 GEMM 에 대해선 다음 포스팅에 자세히 다룰려고 하지만, 간단히 말하자면, convolution( 합성곱 / 이하 conv ) 대신 matrix multiplication (이하 matmul) 을 진행 하겠다는 것이다. conv 대신 matmul을 진행하면 연산시간이 떨어진것을 확인할 수 있다. matmul은 전송된 데이터 당 부동 소수점 연산 비율이 높기 때문에 연산이 빠른 성격을 가지고 있기 때문이다. 

 

따라서 matmul 을 위해 input image 를 matrix 로 만드는 것이라 할 수 있다. 

 

그럼 어떻게 진행되는지 그림을 통해 확인해 보자.

 

1 chanel - image , 1 chanel kernel -> im2col

 

위와 같이 input image 3x3 과 2x2 kernel 이 있을 때, output 으로 나오게 되는데 width 는 k * k , height 는 out_x * out_y 가 된다. 본 그림은 chanel 을 포함 하지 않았는데 chanel 을 포함한다면  width 는 c * k * k, height 는 그대로 out_x * out_y 가 된다. 

 

논문에 삽입된 상태도는 이렇다. 

 

cuDNN의 Convolution Lowering 개념도

위와 같이 image 와 filter 를 matrix 로 표현 한 다음 두 개의 matrix 를 연산하는것이 gemm 이다.  

 

본 포스팅은 im2col 을 중점으로 다루기 때문에 image를 어떻게 c 언어를 통해 matrix 로 코딩하는지 알아보자.

Im2col code>

void im2col(float *in, float *out, int h, int w, int c, int k, int stride, int pad){
   

    int rs = 0;
    int hw = 0;  
    int height, width, in_ch, r, s;
   
    for(height = 0; height < h; height += stride){

        for(width = 0; width < w; width += stride){     
            
            for(in_ch = 0; in_ch < c; in_ch++){          
            
                for(r = 0; r < k; r++){

                    for(s = 0; s < k ; s++){

                        int out_index =  hw * c * k * k + in_ch * k * k + rs;
                        int in_index =  in_ch * h * w + ( height + (r-pad) ) * w + ( width + (s-pad) );

                        
                        if( height + (r-pad) < 0 || width + (s-pad) < 0 || height + (r-pad) >= h || width + (s-pad) >= w ){
                            
                            out[out_index] = 0; 
                            rs += 1;                          
                            continue;
                        }  
                        out[out_index] = in[ in_index];
                        rs += 1;                       
                    }
                }
                rs=0;               
             }
             hw++;
         }
     }
}

 

input image 에 대하여 각각의 prameter 를 접근해야 하기 때문에 5중 for 문을 사용한다. h - w - c - r(k) - s(k) 로 이동하며 커널의 parameter 만큼 1patch 를 해주기 때문에 커널이 변경 될때 마다 hw 값을 이동해 준다. hw값은 ( out_x * out_y) 의 값으로 처음 그림을 통해 알 수 있듯이 height 값을 옮겨 주는 역할을 한다. 

 

pad 가 있는 경우가 있어 이 경우 exception 작업으로 if 문을 통해 0으로 삽입 후 걸러준다. 

 

코드를 보면 그렇게 어렵지 않다. 사각형 네모 박스를 채우는 과정을 진행 한 것 뿐이다. 

 

본 구현의 전체적인 코드

 

https://github.com/sangho0804/darknet-based-CNN/tree/master/Convolution%20Lowering

 

GitHub - sangho0804/darknet-based-CNN: for deep Learning CNN study

for deep Learning CNN study. Contribute to sangho0804/darknet-based-CNN development by creating an account on GitHub.

github.com

 

댓글