본 장에서는 딥러닝 네트워크의 합성곱 층과 완전 연결 층을 하드웨어로 설계하는 과정을 상세히 설명한다.
3.1 콘볼루션 층 (합성곱 층)의 아키텍쳐
그림 2는 딥러닝 네트워크의 콘볼루션 블록을 구성하는 $c$번째 합성곱 층의 아키텍처이며, 설명을 위한 변수를 다음과 같이 정의한다. 입력 특징 맵, $\mathbf{I}_{(\text
{Feat }, c)} \in \mathbf{R}^{\left(W_{\mathrm{I}} \times H_{\mathrm{I}} \times G_{\mathrm{I}}\right)}$
(혹은 이전 합성곱 층의 출력 특징 맵, $\mathbf{O}_{(\text {Feat }, c-1)}$)을 받아 가중치와 합성곱 연산을 수행한 후
해당 합성곱 층의 출력 특징 맵, $\mathbf{O}_{(\text {Feat }, c)} \in \mathbf{R}^{\left(W_{\mathrm{O}}
\times H_{\mathrm{O}} \times G_{\mathrm{O}}\right)}$을 생성한다. 이때, 입력 특징 맵의 너비는 $W_{{I}}$,
높이는 $H_{{I}}$, 채널 수는 $C_{{I}}$이다. 합성곱 연산에 사용된 커널의 너비, 높이, 그리고 채널 수는 $W_{{K}}$, $H_{{K}}$,
그리고 $C_{{K}}$로 정의된다. 마지막으로 출력 특징 맵, ${O}_{({Feat},\: c)}$의 너비, 높이, 그리고 채널 수는 각각 $W_{{O}}$,
$H_{{O}}$, 그리고 $C_{{O}}$이다.
그림 2의 합성곱 층은 스트라이드-패딩 모듈 (Stride-Padding, SP module), 콘볼루션 층 모듈 (Convolution Layer module),
그리고 가중치 및 입력/출력 특징 맵을 저장하거나 읽을 수 있는 메모리 (Memory)로 구성된다.
스트라이드-패딩 (SP) 모듈은 FW (Feature Write) 모듈과 FR(Feature Read) 모듈로 구성이 된다. $c$번째 합성곱 층의
FW 모듈은 입력 특징 맵을 메모리에 저장하기 위한 주소, $A_{({FW},\: c)}$를 계산하고, $c$번째 합성곱 층의 FR 모듈은 메모리에
저장된 입력 특징 맵을 읽어오기 위한 주소, $A_{({FR},\: c)}$를 계산한다.
그림 2의 $c$번째 합성곱 층의 SP 모듈에서는 메모리로의 쓰기와 메모리로부터 읽기 두 가지 동작을 수행한다. 먼저 쓰기 동작의 경우 이전 $(c-1)$번째
합성곱 층에서의 결과를 $c$번째 합성곱 층의 메모리, $M_{({I},\: c)}$에 저장한다. 읽기 동작의 경우 $M_{({I},\: c)}$로부터
값들을 읽어 $c$번째 합성곱 층의 합성 연산을 위한 수용영역 (receptive field)를 구성한다.
이전 $(c-1)$번째 합성곱 층에서 하나의 수용영역과 커널들의 합성곱 결과인 출력 특징 맵 벡터, $O_{({Feat},\: c-1)}\in{R}^{({C}_{{I}})}$를
입력으로 받아 다음 $c$번째 합성곱 층의 메모리, $M_{({I},\: c)}$에 저장한다. 벡터 $O_{({Feat},\: c-1)}$의 $C_{{I}}$개의
요솟값은 $M_{({I},\: c)}$의 $A_{({FW},\: c)}$번째 주소를 시작으로 순차적으로 저장이 된다.
모든 출력 특징 맵 벡터가 $M_{({I},\: c)}$에 저장되면 $c$번째 합성곱 층의 합성 연산을 위한 수용영역을 생성한다. 수용영역은 SP
모듈의 FR 모듈에서 결정된 $A_{({FR},\: c)}$주소를 이용하여 생성된다. 읽어온 값인 $M_{({I},\: c)}(A_{({FR},\:
c)})$을 레지스터 버퍼에 순차적으로 저장함으로써 하나의 수용영역, $D_{{rec}}\in R^{(W_{{K}}\times H_{{K}}\times
C_{{K}})}$을 생성한다.
메모리 $M_{({I},\: c)}$에 대해 값을 읽고 쓰는 동작이 하나의 포트 (port)를 통해서 실행되어야 한다. 이를 위해 하나의 MUX
(Multiplexer)를 사용하여 해당 MUX의 입력으로는 $A_{({FW},\: c)}$와 $A_{({FR},\: c)}$이 있으며 제어 신호를
통해 메모리에 접근할 최종 주소 $A_{({Feat},\: c)}$가 결정된다.
콘볼루션 블록의 $c$번째 합성곱 층의 가중치 및 바이어스 메모리는 $M_{({W},\: c)}$와 $M_{({B},\: c)}$이다. 각 메모리로부터
값을 읽어오기 위한 주소를 각각 $A_{({W},\: c)}\in[0,\: L_{{W}}-1]$, $A_{({B},\: c)}\in[0,\:
L_{{B}}-1]$로 정의한다. 변수 $L_{{W}}$와 $L_{{B}}$는 메모리 $M_{({W},\: c)}$와 $M_{({B},\: c)}$의
깊이 (depth)를 의미한다. 각 메모리로부터 가중치 값, $M_{({W},\: c)}(A_{({W},\: c)})$와 편향 값, $M_{({B},\:
c)}(A_{({B},\: c)})$를 순차적으로 읽어 레지스터 버퍼에 저장함으로써 가중치 값 벡터, $D_{{W}}\in R^{(L_{{W}})}$와
편향 값 벡터, $D_{{B}}\in R^{(L_{{B}})}$를 생성한다.
최종적으로, SP 모듈은 하나의 수용영역 ($D_{{rec}}$), 가중치 값 벡터 ($D_{{W}}$), 편향 값 벡터 ($D_{{B}}$)를 콘볼루션
층 모듈로 전달하여 합성곱 연산을 실행하며, 각 벡터는 아래와 같이 수식 (1), (2), (3)으로 정의할 수 있다.
수식 (1)은 메모리 $M_{({I},\: c)}$에서 하나의 수용영역을 만들기 위해 필요한 주소, $i\in[A_{({Feat},\: c)},\: ({A}_{({Feat},\:
c)}+{W}_{{K}}\times H_{{K}}\times C_{{K}}-1)]$에 대응하는 값들의 집합을 벡터로 나타낸 것이다. 수식 (2)와 (3)은 메모리 $M_{({W},\: c)}$와 $M_{({B},\: c)}$에서 주소 $A_{({W},\: c)}$와 $A_{({B},\: c)}$에
대응하는 값들의 집합을 벡터로 나타낸 것이다.
그림 2. 합성곱 층의 하드웨어 아키텍처.
Fig. 2. Hardware architecture of a convolution layer.
3.2 콘볼루션 층 모듈 (Convolutional Layer)
해당 모듈에서는 $c$번째 콘볼루션 층에서의 합성곱 연산을 수행한다. 합성곱 연산에서의 MAC 연산은 콘볼루션 코어 (CNN core) 모듈에서 수행되며
유한 상태 머신으로 콘볼루션 코어 모듈을 제어하여 최종 출력 특징 맵 벡터, $O_{({Feat},\: c)}\in{R}^{(C_{{O}})}$를
생성한다. 유한 상태 머신은 전달받은 $D_{{rec}}$, $D_{{W}}$, $D_{{B}}$ 벡터들을 콘볼루션 코어 모듈의 여러 MAC 연산
코어들로 전달한다. 전달받은 수용영역, $D_{{rec}}$에 대한 합성곱 연산이 완료되어 $O_{({Feat},\: c)}$ 벡터가 생성되면 다음
$(c+1)$번째 합성곱 층의 입력으로 전달한다.
3.2.1 콘볼루션 코어 모듈 (CNN core)
해당 모듈에서는 $D_{{rec}}$, $D_{{W}}$, $D_{{B}}$ 벡터들의 요솟값들을 MAC 연산코어들로 분산시켜 합성곱 연산을 병렬적으로
수행한다. 그림 3은 해당 모듈에서 수행되는 연산 과정을 단계별로 보여준다. 이 연산은 총 4단계로 구성되며, 단계 1에서는 $D_{{rec}}$, $D_{{W}}$가
입력으로 사용되고, 단계 4에서는 $D_{{B}}$가 입력으로 사용되어 최종적으로 출력 특징 맵 벡터 $O_{({Feat},\: c)}$를 생성한다.
합성곱 연산으로 계산되는 $O_{({Feat},\: c)}$ 벡터의 $i\in[0,\: C_{{O}}-1]$번째 요소인 $[O_{({Feat},\:
c)}]_{(i)}$의 수식은 다음과 같다. 수식 (4)는 그림 3에서 단계 1 (stage 1)에서 단계 4 (stage 4)까지의 과정을 의미하며 $[O_{({Feat},\: c)}]_{(i)}$는 단계 4에서
주황색 벡터의 $i$번째 요소에 바이어스를 더한 결과이다. $j\in[0,\: C_{{I}}-1]$는 입력 채널의 인덱스를 나타내며, $k\in[0,\:
W_{{K}}\times H_{{K}}-1]$는 커널 요소의 인덱스를 의미한다.
단계 1 : 수식 (4)를 수식 (5)와 같이 재작성할 수 있으며, 여기서 $[V_{S2}]_{(k \vert i,\: j)}$는 수식 (6)과 같다.
수식 (6)에서 $i$와 $j$ 값은 주어지는 값이며 $V_{{S}2}\in R^{(W_{{K}}\times H_{{K}})}$ 벡터는 그림 3의 단계 2에서의 벡터를 의미하고 해당 벡터의 각 요솟값을 수식 (6)으로 계산한다. 단계 1에서 생성되는 $V_{{S}2}$ 벡터의 총 수는 $C_{{O}}\times C_{{I}}$이다.
단계 2 : 수식 (5)를 수식 (7)과 같이 재작성할 수 있으며, 여기서 $[V_{{S}3}]_{(j \vert i)}$은 수식 (8)과 같이 정의된다.
수식 (8)에서 $i$는 주어지는 변수이며, $V_{S3}\in{R}^{(C_{{I}})}$ 벡터는 그림 3의 단계 3에서의 벡터를 의미하며 총 $C_{{I}}$개가 생성된다.
단계 3 : 수식 (7)은 수식 (9)와 같이 다시 정의할 수 있고, 여기서 $[V_{S4}]_{(i)}$은 수식 (10)과 같이 정의된다.
수식 (10)에서 $i$는 주어지는 변수이며, $V_{{S}4}\in{R}^{(C_{{O}})}$벡터는 그림 3의 단계 4에서의 벡터를 의미하며 총 1개가 생성된다.
단계 4 : 수식 (9)를 기반으로 단계 4에서는 최종 출력 벡터인 $O_{({Feat},\: c)}$의 $i$번째 요소인 $[O_{({Feat},\: c)}]_{(i)}$가
계산된다. 이는 단계 3에서 생성되는$[V_{S4}]_{(i)}$와 바이어스 값 $[D_{{B}}]_{(i)}$을 더한 결과이다. 위 연산을 $C_{{O}}$번
하여 최종 $O_{({Feat},\: c)}$ 벡터를 생성한다.
그림 3. 콘볼루션 코어 연산의 전체 과정.
Fig. 3. Entire process of convolution core operations.
3.2.2 파이프라인 (Pipeline)
콘볼루션 코어 모듈은 연산을 효율적으로 처리하기 위해 파이프라이닝 기법을 적용한 구조로 되어 있다. 특히 실시간으로 동작해야 하는 시스템에서는 파이프라인
기법이 매우 중요한 역할을 한다.
그림 4는 콘볼루션 코어 모듈 내에서 데이터가 연속적으로 입력될 때, 각 단계별 파이프라인이 어떻게 동작하는지를 시각적으로 보여준다. 콘볼루션 코어 모듈에서
수식 (4)의 연산을 독립적으로 실행할 수 있는 4단계의 구조로 나눔으로써 파이프라이닝이 가능하다. 즉, 수식 (4)에서 주어진 입력만으로 결과를 도출할 수 있는 분리 가능한 MAC 연산 묶음을 분리 설계함으로써 병렬 처리가 가능하다. 따라서, 파이프라이닝을 통해
수식 (4) 계산을 효율적으로 처리할 수 있게 한다.
그림 4. 파이프라인 연산 과정.
Fig. 4. Pipeline operation process.
3.3 스트라이드-패딩 (SP) 모듈
그림 2의 SP 모듈은 2개의 하위 모듈인 FW 모듈과 FR 모듈로 구성된다. FW 모듈은 콘볼루션 층의 패딩을 고려하여 특징 맵을 메모리에 저장한다. FR
모듈에서는 콘볼루션 층의 스트라이딩을 고려하여 합성곱 연산에 필요한 수용영역을 생성한다. 또한, 합성곱 연산에 필요한 가중치와 바이어스를 읽어 수용영역과
함께 콘볼루션 층 모듈로 송신한다.
임의의 $c$번째 콘볼루션 층의 FW 모듈은 $(c -1)$번째 콘볼루션 층의 출력 특징 맵, ${bold O}_{({Feat},\: c-1)}$을
패딩 동작을 고려하여 메모리 $M_{({I},\: c)}$에 저장하고, FR 모듈은 $M_{({I},\: c)}$에 저장된 ${O}_{({Feat},\:
c-1)}$의 일부를 순차적으로 읽어와 레지스터 버퍼에 저장하여 수용영역 $D_{{rec}}$를 생성한다.
메모리 $M_{({I},\: c)}$로의 읽기/쓰기 접근이 동시에 발생하는 것을 방지하기 위해서 유한 상태 머신을 이용하여 SP 모듈의 상태를 쓰기
상태 (Write state)와 읽기 상태 (Read state)로 구분하여 제어한다. 쓰기 상태일 때는 FW 모듈로 ${O}_{({Feat},\:
c-1)}$을 저장하고 저장이 완료되면 읽기 상태로 천이 한다. 읽기 상태에서는 FR 모듈을 통해 $D_{{rec}}$을 읽어 콘볼루션 층 모듈로
전송한다.
FW 모듈이 ${O}_{({Feat},\: c-1)}$를 $M_{({I},\: c)}$에 저장할 때 사용하는 메모리 주소 $A_{({FW},\:
c)}$와 FR 모듈이 $D_{{rec}}$를 생성할 때 사용하는 메모리 주소 $A_{({FR},\: c)}$는 서로 상이하기 때문에 SP 모듈의
상태별 사용되는 메모리 주소를 적절히 선택하기 위해 MUX (Multiplexer)를 사용하여 메모리 접근 주소를 제어한다.
3.3.1 FW (Feature Write) 모듈
FW 모듈은 그림 2에서와 같이 두 개의 하위 모듈로 구성되며 하나는 입력으로 들어오는 특징 맵 벡터의 채널 수를 카운트 하는 채널 카운터 (Channel Counter)
모듈이고 나머지는 해당 벡터를 메모리 저장할 때 필요한 쓰기 주소를 계산하는 주소 계산기 ($A_{({FW},\: c)}$ Calculator)
모듈이다.
채널 카운터 모듈은 입력 특징 맵 벡터를 채널 단위로 메모리에 저장할 수 있도록 0부터 $(C_{{I}}-1)$까지 순차적으로 증가하는 값 $c_{{I}}\in[0,\:
C_{{I}}-1]$ 을 생성한다. 이 값은 주소 계산기 모듈에서 각 채널에 맞는 메모리 주소를 생성하는데 사용된다.
이전 합성곱 층 모듈에서 전달받은 특징 맵 벡터, $O_{({Feat},\: c-1)}$의 크기는 $(1\times 1\times C_{{I}})$이며
이러한 벡터를 총 $(W_{{I}}\times H_{{I}})$개 수신한다. 이전 합성곱 층에서 $O_{({Feat},\: c-1)}$를 송신할
때 해당 벡터의 높이와 너비에 대한 좌표 정보 $(h_{{I}},\: w_{{I}})$도 함께 전달하며 $h_{{I}}\in[0,\: H_{{I}}-1]$
이고 $w_{{I}}\in[0,\: W_{{I}}-1]$ 이다.
$O_{({Feat},\: c-1)}$는 채널 단위로 $M_{({I},\: c)}$에 저장될 때 패딩을 고려할 수 있어야 하며 패딩의 크기가 $P$라고
했을 때 해당 콘볼루션 층에서 사용하는 입력 특징 맵, ${O}_{({Feat},\: c-1)}$의 높이 $H_{{P}}$ 및 너비 $W_{{P}}$는
$H_{{P}}= H_{{I}}+2\times P$와 $W_{{P}}= W_{{I}}+2\times P$로 결정된다. 이를 고려하여 $O_{({Feat},\:
c-1)}$를 $M_{({I},\: c)}$에 저장할 때 사용하는 메모리 주소 $A_{({FW},\: c)}$는 수식 (11)로 정의되며 여기서 사용되는 변수 $G$는 수식 (12)와 같이 정의된다.
여기서 $A_{({FW},\: c)}$의 범위는 $A_{({FW},\: c)}\in[0,\: W_{{P}}\times H_{{P}}\times
C_{{P}}-1]$이며 $C_{{I}}$개의 $O_{({Feat},\: c-1)}$가 수신되었을 때 FW 모듈의 동작은 완료되며 SP 모듈의 상태는
읽기 상태로 천이된다. 또한 완료 신호를 FR 모듈에 전달하면서 $D_{{rec}}$를 생성한다.
3.3.2 FR (Feature Read) 모듈
FR 모듈은 합성곱 층의 커널이 참조하는 영역인 수용영역 $D_{{rec}}$을 생성하기 위해 $M_{({I},\: c)}$에 저장된 ${O}_{({Feat},\:
c-1)}$의 값에 접근해야 한다. FW 모듈이 패딩을 고려하여 저장한 ${O}_{({Feat},\: c-1)}$의 크기는 $(W_{{P}}\times
H_{{P}}\times C_{{I}})$이고 FR 모듈이 생성하는 $D_{{rec}}$의 크기는 커널의 크기와 같은 $(W_{{K}}\times
H_{{K}}\times C_{{I}})$이다.
그림 2에서와 같이 해당 모듈은 2개의 하위 모듈로 구성되며, $C_{{addr}}$ calculator 모듈은 $M_{({I},\: c)}$로부터 생성하고자
하는 수용영역, $D_{{rec}}$의 중심 요소를 읽기 위해 필요한 메모리 접근 주소, $C_{{addr}}$을 계산하고 $A_{({FR},\:
c)}$ Calculator 모듈은 $C_{{addr}}$을 기준으로 $D_{{rec}}$의 나머지 요소들의 메모리 주소를 계산한다.
$A_{({FR},\: c)}$ Calculator 모듈에서 $D_{{rec}}$의 요솟값을 $M_{({I},\: c)}$에서 읽어오기 위한 메모리
접근 주소 $A_{({FR},\: c)}$는 수식 (13)과 같이 정의되며 여기서 사용되는 변수 $C_{{offset}}$은 수식 (14)로 정의된다.
수식 (13)에서 $h_{{r}}$, $w_{{r}}$, $c_{{r}}$는 $D_{{rec}}$를 기준으로 한 높이, 너비, 채널 방향으로의 좌표를 의미하고
각 변수의 범위는 $h_{{r}}\in[0,\: H_{{K}}-1]$, $w_{{r}}\in[0,\: W_{{K}}-1]$, $c_{{r}}\in[0,\:
C_{{I}}-1]$로 정의된다. 결과적으로 $h_{{r}}$, $w_{{r}}$, $c_{{r}}$좌표에서의 $D_{{rec}}$값은 수식 (15)와 같이 정의된다.
그림 5는 ${O}_{({Feat},\: c-1)}$가 저장된 $M_{({I},\: c)}$에서 $D_{{rec}}$를 생성하기 위해 접근하는 메모리
영역을 시각적으로 보여준다. 그림 5는 하나의 수용영역에 해당하는 요솟값들과 메모리에 저장된 값들 사이의 연관성을 시각화해서 보여준다. 수식 (15)는 이러한 연관성을 토대로 도출된 것이다. $M_{({I},\: c)}$의 요소들을 나열하여 정렬하면, ${O}_{({Feat},\: c-1)}$를
가로 $W_{{P}}\times C_{{I}}$, 세로 $H_{{P}}$ 크기의 직사각형 모양으로 볼 수 있다. 사각형 영역인 수용영역의 크기는 커널의
크기와 동일하며 가로 $W_{{K}}\times C_{{I}}$, 세로 $H_{{K}}$ 크기의 직사각형 모양으로 볼 수 있다. a 요소의 값은 $h_{{r}}$,
$w_{{r}}$, $c_{{r}}$좌표가 $(0,\: 0,\: 0)$인 시작 좌표에서 $D_{{rec}}$의 값을 의미하며 c 요소의 값은 좌표가
$(H_{{K}}-1,\: W_{{K}}-1,\: C_{{I}}-1)$인 마지막 좌표에서 $D_{{rec}}$의 값을 의미한다. b 요소의 값은
$D_{{rec}}$의 중심 요솟값이다.
$C_{{addr}}$ calculator 모듈은 수식 (13)에 필요한 $C_{{addr}}$을 생성한다. $C_{{addr}}$의 계산 수식은 출력 특징 맵, ${O}_{({Feat},\: c)}$의 하나의
요소와 연관 되는 ${O}_{({Feat},\: c-1)}$ 위에 놓이는 $D_{{rec}}$의 영역을 고려하여 도출되며 스트라이딩을 $S$라고
했을 때, 출력 특징 맵의 높이와 너비인 $H_{{O}}$와 $W_{{O}}$는 수식 (16)과 같이 정의된다.
${bold O}_{({Feat},\: c)}$의 요소별 좌표를 $(h_{{o}},\: w_{{o}},\: c_{{o}})$라 하면 각 좌표점의
범위는 $h_{{o}}\in[0,\: H_{{O}}-1]$, $w_{{o}}\in[0,\: W_{{O}}-1]$, $c_{{o}}\in[0,\:
C_{{O}}-1]$이다. 좌표 $(h_{{o}},\: w_{{o}})$에서의 $[{O}_{({Feat},\: c)}]_{(h_{{o}},\:
w_{{o}},\: :)}\in R^{(C_{{O}})}$는 콘볼루션 코어 모듈의 출력 벡터, $O_{({Feat},\: c)}$로 결정된다.
그리고 콘볼루션 코어 모듈은 수용영역 $D_{{rec}}$를 입력으로 받아 $O_{({Feat},\: c)}$를 구한다.
수용영역, $D_{{rec}}$ 기준의 좌표점 특히 중앙 요소의 좌표점을 그림 6에서와 같이 ${O}_{({Feat},\: c)}$의 $(h_{{o}},\: w_{{o}})$ 좌표점과 연관 지을 수 있다. 따라서, $[{O}_{({Feat},\:
c)}]_{(h_{{o}},\: w_{{o}},\: :)}$값 계산을 위한 $D_{{rec}}$를 $M_{({I},\: c)}$에서 읽어오기
위한 접근 주소 $C_{{addr}}$은 수식 (17)과 같이 정의되며 여기서 사용되는 변수 $C_{\star{t}}$는 수식 (18)과 같이 정의된다.
FR 모듈은 $(H_{{O}}\times W_{{O}})$번 반복하여 콘볼루션 코어 모듈을 통해 $\left[\mathbf{O}_{(\text {Feat
}, c)}\right]_{\left(h_0, w_0,:\right)}$를 계산하여 콘볼루션 층의 결과 ${O}_{({Feat},\: c)}$를
생성하고 동작이 완료된다. FR 모듈은 동작이 완료되면 연산 완료 신호를 송신하며 SP 모듈의 유한 상태 머신은 상태를 쓰기 상태로 천이한다.
그림 5. $M_{({I},\: c)}$에서 $D_{{rec}}$를 생성하기 위해 접근하는 메모리 영역.
Fig. 5. The memory region accessed to generate $D_{{rec}}$ from $M_{({I},\: c)}$.
그림 6. 출력 특징 맵 ${O}_{({Feat},\: c)}$을 만들기 위해 $M_{({I},\: c)}$에서 참조되는 영역과 그 중심값.
Fig. 6. The region referenced in $M_{({I},\: c)}$ and its center value to create
the output feature map, ${O}_{({Feat},\: c)}$.
3.4 완전 연결 층 (Fully-connected Layer) 모듈
해당 모듈은 완전 연결 층에서 $c$번째 층의 계산을 수행하는 역할을 한다. 그림 7 (a)는 딥러닝 네트워크의 완전 연결 블록 내에서 $c$번째 완전 연결 층을 시각적으로 표현한 것이고, 그림 7 (b)는 $c$번째 완전 연결 층의 모듈 아키텍처를 보여준다. 해당 아키텍처는 가중치와 노드 값의 연산을 담당하는 완전 연결 코어 (FC core) 모듈들로
구성되어 있다. 하나의 완전 연결 층 모듈에서는 입력 노드, $\mathbf{I}_{(\text {Node }, c)} \in \mathbf{R}^{\left(L_{\mathrm{I}}\right)}$
(혹은 이전 완전 연결 층의 출력 노드, ${O}_{({Node},\: c-1)}$)와 가중치 값을 메모리, $M_{({F},\: c-1)}$와
메모리, $M_{({FW},\: c)}$에서 불러와 MAC 연산 수행하는 완전 연결 코어 모듈로 전송하여 곱셈 및 누적 덧셈을 수행한다.
완전 연결 코어 모듈은 유한 상태 머신으로 제어되며, 최종 출력 노드, $\mathbf{O}_{(\text {Node }, c)} \in \mathbf{R}^{\left(L_{\mathrm{O}}\right)}$를
생성하여 메모리, $M_{({F},\: c)}$에 저장한다. 이때 $M_{({F},\: c)}$는 $(c+1)$번째 완전 연결 층 모듈의 입력
노드로 사용된다. 입력 노드가 저장된 메모리, $M_{({F},\: c-1)}$깊이는 $L_{{I}}$, 출력 노드가 저장된 메모리, $M_{({F},\:
c)}$의 깊이는 $L_{{O}}$, 가중치가 저장된 메모리, $M_{({FW},\: c)}$의 깊이는 $L_{{FW}}= L_{{O}}\times
L_{{I}}$로 정의된다.
완전 연결 코어 모듈에서는 입력 노드가 저장된 $M_{({F},\: c-1)}$의 요솟값들을 MAC 연산코어들로 분산시켜 덧셈, 곱셈 연산을 병렬적으로
수행한다. 연산을 마친 출력 노드의 $m\in[1,\: L_{{O}}]$번째 요소인 $[O_{({Node},\: c)}]_{(m)}$은 수식 (19)와 같이 정의된다.
수식 (19)에서 $[I_{({Node},\: c)}]_{(k)}$는 입력 노드의 $k\in[1,\: L_{{I}}]$번째 요소의 값이며, 이는 $M_{({F},\:
c-1)}(k)$와 동일하고, $[O_{({Node},\: c)}]_{(m)}$는 $M_{({F},\: c)}(m)$와 동일하다.
수식 (19)를 수식 (20)과 같이 재작성할 수 있으며,
여기서 $i_{k}$는 입력 노드의 $k$번째 요소의 값, $[I_{({Node},\: c)}]_{(k)}$이고, $w_{(k,\: m)}$은 가중치가
저장되어 있는 메모리, $M_{({FW},\: c)}$의 $(k\times m)$번째 요소인 $M_{({FW},\: c)}(k\times m)$이다.
완전 연결 코어 모듈은 출력 노드의 한 개의 요소에 대해서만 연산하는 모듈이다. 따라서 출력 노드의 모든 요소에 대한 연산을 수행하기 위해서는 그림 7 (b)와 같이 $L_{{O}}$개의 완전 연결 코어 모듈이 병렬로 동작하는 구조를 갖춰야 한다. 이를 통해 $L_{{O}}$개의 모듈이 병렬로 연산을 수행하여
최종적으로 $L_{{O}}$개의 출력 노드가 $M_{({F},\: c)}$에 저장된다.
그림 7. (a) 완전 연결 층의 연산 과정, (b) 완전 연결 층의 하드웨어 아키텍처.
Fig. 7. (a) the computation process of the Fully-connected Layer, (b) Hardware architecture
of a Fully-connected Layer.