지난 글에서 선형 레이어를 구현했습니다. 이번 글에서는 신경망에서 자주 쓰는 활성화 함수를 알아보고 구현해 보겠습니다. 👍
🔎 활성화 함수#
ReLU(Rectified Linear Unit)라고도 불리는 활성화 함수는 입력이 0보다 크면 그대로 통과시키고, 0 이하이면 0으로 바꿉니다.
$$ReLU(x)=max(0,x)$$
신경망에 비선형성을 넣어 복잡한 관계를 학습하는 역할을 합니다. 계산이 간단하고 학습이 잘 되는 편이라 널리 쓰입니다.
활성화 함수 없이 선형층만 여러 개 쌓으면, 층이 아무리 많아도 결국 하나의 선형 변환과 같아요. 반면 ReLU를 중간중간 넣으면 입력과 출력의 관계를 더 복잡하게 표현할 수 있습니다.
⤴️ 미분은 아래와 같습니다.
$$\frac{dy}{dx}=\begin{cases}1&x\gt 0\\0&x\le 0\end{cases}$$
따라서 역전파 때 입력에 대한 기울기는 들어온 기울기에 ReLU의 미분값을 곱한 값입니다.
$$\frac{\partial L}{\partial x}=\frac{\partial L}{\partial y}\cdot ReLU'(x)$$
⚠️ 아래 코드는 오래된 코드일 수 있습니다. 이 곳을 참고해 주세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| // relu.hpp
Matrix<T> forward(const Matrix<T>& input) {
this->_input = input;
const std::size_t kRow = input.row();
const std::size_t kCol = input.col();
Matrix<T> result = input;
for(std::size_t row=0; row<kRow; row++) {
for(std::size_t col=0; col<kCol; col++) {
// ReLU 핵심
if(result(row, col) < 0) {
result(row, col) = 0;
}
}
}
return result;
}
Matrix<T> backward(const Matrix<T>& gradOutput) {
const std::size_t kRow = gradOutput.row();
const std::size_t kCol = gradOutput.col();
Matrix<T> result = gradOutput;
for(std::size_t row=0; row<kRow; row++) {
for(std::size_t col=0; col<kCol; col++) {
if(this->_input(row, col) <= 0) result(row, col) = 0;
}
}
return result;
}
|
테스트#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| TEST_CASE(AI_RELU_FORWARD_TEST) {
Yami::AI::Matrix<int> input(2,2);
for(std::size_t i=0; i<4; i++) {
input[i] = i+1;
}
input(0,0) *= -1;
input(1,1) *= -1;
Yami::AI::ReLU<int> relu;
Yami::AI::Matrix<int> ret = relu(input);
ASSERT_EQ(ret(0,0), 0);
ASSERT_EQ(ret(0,1), 2);
ASSERT_EQ(ret(1,0), 3);
ASSERT_EQ(ret(1,1), 0);
}
TEST_CASE(AI_RELU_BACKWARD_TEST) {
Yami::AI::Matrix<float> x(2, 2);
x.setElements(
1.5, -0.5,
-2.0, 3.0
);
Yami::AI::Matrix<float> dy(2, 2);
dy.setElements(
0.4, 0.8,
0.2, 0.6
);
Yami::AI::ReLU<float> relu;
relu(x);
Yami::AI::Matrix<float> dZ = relu.backward(dy);
ASSERT_FLOAT_EQ(dZ(0,0), 0.4);
ASSERT_FLOAT_EQ(dZ(0,1), 0);
ASSERT_FLOAT_EQ(dZ(1,0), 0);
ASSERT_FLOAT_EQ(dZ(1,1), 0.6);
}
|
1
2
3
4
| [RUN ] AI_RELU_FORWARD_TEST
[ OK ] AI_RELU_FORWARD_TEST
[RUN ] AI_RELU_BACKWARD_TEST
[ OK ] AI_RELU_BACKWARD_TEST
|

샘플 데이터로 결과값 정확히 적중했습니다. 👍