[0/18] 줄리아로 딥러닝 구현하기

해당 시리즈는 프로그래밍 언어 중 하나인 줄리아(Julia)로 딥러닝(Deep learning)을 구현하면서 원리를 설명합니다.


Introduction

이 시리즈는 줄리아를 사용하여 딥러닝의 알고리즘을 이해하고 구현하는 것을 목표로 한다. 특히 신경망에서 사용되는 수학적 지식이나 수식들에 대한 설명을 덧붙이면서 딥러닝을 잘 모르는 초보자들도 이해할 수 있도록 하였다.

목차는 아래와 같다.

  1. 행렬과 벡터의 곱

  2. 활성화 함수

  3. 손실 함수

  4. 경사하강법

  5. 인공신경망 구현 - 수학식 풀이

  6. 인공신경망 구현 - 모델 학습

  7. 인공신경망 구현 - 순전파 알고리즘

  8. 인공신경망 구현 - 역전파 설명

  9. 인공신경망 구현 - 역전파 알고리즘

  10. ReLU vs. Sigmoid 성능 비교

  11. 인공신경망 최적화 - Optimizer

  12. 인공신경망 최적화 - 가중치 초기값

  13. 인공신경망 최적화 - 드랍아웃(Dropout)

줄리아 사용법

위 시리즈는 프로그래밍 언어 중 하나인 줄리아(Julia)를 사용한다. 그렇다면 왜 파이썬이 아니라 줄리아로 구현하는가? 그 이유는 간단하다. 머신러닝을 구현하는 데 있어 줄리아는 오로지 내장 함수만을 사용하여 모든 구현이 가능하다. 만약 줄리아 언어 자체를 배우고 싶다면 블로그 내에 "Think Julia"을 정리한 시리즈가 있다.

배열 생성하기

신경망의 데이터들은 모두 배열로 구성되어 있기 때문에 배열을 필수적으로 생성해야 한다. 따라서 본격적으로 딥러닝을 구현하기 전에 줄리아에서는 어떻게 배열을 생성하는지에 대해 다뤄볼 것이다.

줄리아의 큰 장점 중 하나는 파이썬이나 R과는 다르게 1차원부터 n차원까지의 배열을 한 가지의 자료형으로 표현한다는 것이다. 그렇기에 우리는 차원의 구분없이 간단한 코드로 여러 차원의 배열을 생성할 수 있다. 줄리아에서 2차원 배열을 구현하는 방법은 다음과 같다.

1
2
3
4
5
6
7
8
Julia> [1 2 3 4 5 6]
1×6 Array{Int64,2}:
1 2 3 4 5 6

Julia> [1 2 3; 4 5 6]
2×3 Array{Int64,2}:
1 2 3
4 5 6

줄리아는 배열에 ;를 추가하면 2차원 배열을 반환한다. 위의 예시는 직접 데이터를 입력하는 경우이다. 만약 데이터가 랜덤 값인 배열을 생성하고 싶다면 어떻게 해야 하는가? 지금부터는 다양한 배열들을 구현하는 함수들을 살펴볼 것이다.

모든 요소가 1인 배열

아래의 코드는 모든 요소가 1인 2차원 배열을 반환한다.

1
2
3
4
5
Julia> ones(3,5)
3×5 Array{Float64,2}:
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0

결과에서 알 수 있듯이 요소는 모두 소수형태인 1로 반환되며, 2차원 배열을 생성하는 방법은 ones(row,column)순서이다. 3차원 배열을 만드는 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
Julia> ones(3,5,2)
3×5×2 Array{Float64,3}:
[:, :, 1] =
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0

[:, :, 2] =
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0

위의 배열은 3차원 배열이며, 이를 생성하는 방법은 ones(row,column,third dimension) 순서이다. 예시를 보면서 파악했을 수도 있지만 매개 변수의 개수가 차원의 크기와 동일하다. 즉, ones(3,5,2,3)은 4차원 배열을 반환한다.

모든 요소가 0인 배열

아래의 코드는 모든 요소가 0인 2차원 배열을 반환한다.

1
2
3
4
Julia> zeros(2,3)
2×3 Array{Float64,2}:
0.0 0.0 0.0
0.0 0.0 0.0

zeros()ones()는 요소의 값만 다를 뿐 문법은 같다. 즉, zeros(2,3,2)는 3차원 배열을 반환한다.

1
2
3
4
5
6
7
8
9
Julia> zeros(2,3,2)
2×3×2 Array{Float64,3}:
[:, :, 1] =
0.0 0.0 0.0
0.0 0.0 0.0

[:, :, 2] =
0.0 0.0 0.0
0.0 0.0 0.0

모든 요소가 정수 n인 배열

0과 1이 아닌 정수 n을 값으로 하는 배열을 만들고 싶다면 fill()을 사용하면 된다. 예시는 아래와 같다.

1
2
3
4
5
Julia> fill(2,3,4)
3×4 Array{Int64,2}:
2 2 2 2
2 2 2 2
2 2 2 2

위 배열은 요소가 2인 2차원 배열이다. 즉, 첫 번째 매개 변수가 요소의 값이고 뒤의 매개 변수들이 행과 열인 것이다. 3차원 배열을 만드는 법은 다른 함수들과 같이 매개 변수를 추가하면 된다.

1
2
3
4
5
6
7
8
9
10
11
Julia> fill(2,3,4,2)
3×4×2 Array{Int64,3}:
[:, :, 1] =
2 2 2 2
2 2 2 2
2 2 2 2

[:, :, 2] =
2 2 2 2
2 2 2 2
2 2 2 2

랜덤 배열 만들기

앞에서는 하나의 값을 요소로 하는 배열들을 생성하였다. 지금부터는 하나의 값이 아니라 다양한 값을 랜덤으로 가지는 배열들을 만들어 볼 것이다.

부동소수점 랜덤 배열

부동소수점을 요소로 가지는 랜덤 배열은 아래와 같다.

1
2
3
4
Julia> Array{Float64,2}(undef,2,3)
2×3 Array{Float64,2}:
2.53205e-314 2.53205e-314 2.37106e-314
2.53203e-314 2.37106e-314 2.3099e-314

{Float64,2} 코드는 배열의 데이터 타입과 차원을 입력하고, (undef, 2, 3) 코드는 랜덤데이터와 행, 열을 입력한다. 그렇다면 3차원 배열을 만들기 위해서는 어떻게 해야 할까? 3차원 배열을 생성해보자.

1
2
3
4
5
6
7
8
9
Julia> Array{Float64,3}(undef,2,3,2)
2×3×2 Array{Float64,3}:
[:, :, 1] =
2.52381e-314 2.52381e-314 5.0e-324
2.55284e-314 2.52381e-314 2.52381e-314

[:, :, 2] =
2.52381e-314 4.0e-323 1.0e-323
2.52381e-314 5.0e-323 2.29607e-314

정수 랜덤 배열

정수로 구성된 랜덤 배열은 간단하다. 부동소수점 랜덤 배열을 생성하는 위의 코드에서 데이터 타입만 변경해주면 된다. 2차원 배열은 다음과 같다.

1
2
3
4
Julia> Array{Int64,2}(undef,2,3)
2×3 Array{Int64,2}:
5333990960 5124673872 4770
5333975168 4799098432 -1

3차원 배열 또한 차원 수를 변경하고, 매개 변수를 추가하면 된다.

1
2
3
4
5
6
7
8
9
Julia> Array{Int64,3}(undef,2,3,2)
2×3×2 Array{Int64,3}:
[:, :, 1] =
5108433792 5108433872 1
5343445008 5108433632 5108433952

[:, :, 2] =
5108434032 12 2
5108434112 12 4798283777

범위를 설정한 정수 랜덤 배열

위의 예시들을 보면 1부터 5343445008까지 정말 큰 숫자들이 랜덤으로 선정되는 걸 확인할 수 있다. 5343445008와 같은 큰 숫자가 필요없거나 최대값을 설정해야 한다면, 아래의 함수를 사용하자.

1
2
3
4
Julia> rand(1:10,2,4)
2×4 Array{Int64,2}:
8 7 10 10
8 9 1 10

rand()는 첫 번째 매개 변수로 범위를 받고, 이후 행과 열을 받는다. 범위 안에서 요소의 값들이 결정되며 3차원 배열 또한 생성 가능하다.

1
2
3
4
5
6
7
8
9
Julia> rand(1:10,2,4,2)
2×4×2 Array{Int64,3}:
[:, :, 1] =
9 7 9 8
5 7 7 7

[:, :, 2] =
9 8 9 5
9 3 3 1

배열 차원 변경하기

어떤 배열을 다른 형태의 배열로 변경하고 싶다면 reshape()를 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
Julia> a = collect(1:16)
16-element Array{Int64,1}:
1
2
3
4

13
14
15
16

위의 객체 a\(16\times1\) 배열이다. 이 배열을 \(4\times4\) 배열로 바꾸고 싶다면 아래와 같이 입력하면 된다.

1
2
3
4
5
6
Julia> reshape(a,4,4)
4×4 Array{Int64,2}:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16

reshape(a,4,4)은 앞에 첫 번째 매개 변수로 기존 배열을 받으며, 이후는 행과 열을 받는다. 데이터의 개수만 맞추면 모든 차원의 배열로 변경할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Julia> reshape(a,2,2,4)
2×2×4 Array{Int64,3}:
[:, :, 1] =
1 3
2 4

[:, :, 2] =
5 7
6 8

[:, :, 3] =
9 11
10 12

[:, :, 4] =
13 15
14 16

지금까지 신경망을 배우기 위한 기본적인 배열 처리 방법에 대해서 알아보았다. 이제는 본격적으로 신경망에 대해 알아보자.


[0/18] 줄리아로 딥러닝 구현하기
https://dev-bearabbit.github.io/ko/DeeplearningJulia/Deeplearning-0/
Author
Jess
Posted on
2020년 3월 25일
Licensed under