이 글은 '밑바닥부터 배우는 딥러닝 / 사이토 고키 저 / 한빛미디어 출판'을 공부하며 정리한 글입니다.
3.2.3 계단 함수의 그래프
위 코드 Line5 : np.int만 작성하면 오류
명확하게 int32 or int64로 작성해줘야함
https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
NumPy 1.20.0 Release Notes — NumPy v1.25.dev0 Manual
NumPy 1.20.0 Release Notes This NumPy release is the largest so made to date, some 684 PRs contributed by 184 people have been merged. See the list of highlights below for more details. The Python versions supported for this release are 3.7-3.9, support fo
numpy.org
3.2.5 시그모이드 함수와 계단함수 비교
'매끄러움의 차이'
계단 함수
- 0을 경계로 출력값이 갑자기 바뀜
- 0과 1 둘중 하나의 값만 돌려줌
시그모이드 함수
- 부드러운 곡선, 입력에 따른 출력의 연속적인 변화
- 실수를 돌려줌
공통점
- 입력이 작아질 수록 출력은 0에 가깝거나 0, 입력이 커질 수록 1에 가깝거나 1이 되는 구조
- 입력이 중요하면 큰 값을 출력, 입력이 중요하지 않으면 작은 값을 출력
3.2.6 비선형 함수
선형 함수 : 함수에 어떤 값을 입력했을 때 출력이 입력의 상수배만큼 변하는 함수
y = ax+b와 같이 곧은 직선 1개인 함수
비선형 함수 : 선형이 아닌 함수 즉 직선 1개로 그릴 수 없는 함수
신경망에서는 선형 함수를 사용하면 안된다
선형 함수를 이용하면 신경망의 층을 깊게 하는 의미가 없어지기 때문
선형 함수의 한계 : 층을 아무리 깊게 해도 '은닉층이 없는 네트워크'로도 똑같은 기능을 할 수 있음
예를 들어
h(x) = cx
h(x)를 활성화 함수로 사용한 3층 네트워크 y(x) = h(h(h(x))) = c*c*c*x
그러나 이때 y(x) = c*c*c*x = a*x (a = c^3)
즉, 은닉층이 없는 네트워크 표현이 가능
선형 함수의 이용에서는 여러 층으로 구성하는 이점을 살릴 수 없음
고로 여러 층을 쌓는 혜택을 얻고 싶다면 활성화 함수로 비선형 함수를 사용해야 한다
3.2.7 ReLU 함수
새로운 신경망의 활성화 함수 : ReLU 함수
ReLU (Rectified Linear Unit, 렐루) 함수 : 입력이 0을 넘으면 입력값을 그대로 출력하고 0 이하이면 0을 출력하는 함수
3.3 다차원 배열의 계산
3.3.1 다차원 배열
다차원 배열
- 숫자가 한 줄로 늘어선 것
- 직사각형으로 늘어놓은 것
- 3차원으로 늘어놓은 것
- N차원으로 나열하는 것 ..
넘파이를 이용한 1(A), 2(B) 차원 배열
A : [1 2 3 4] --- A의 원소는 4개
ndim(A) : 1 --- A는 1차원 배열
A.shape : (4,) --- 튜플 반환
A.shape[0] : 4 --- [0]행의 형상 4
B : [[1 2]
[3 4]
[5 6]]
ndim(B) : 2
B.shape : (3,2)
- np.ndim( ) : 배열의 차원 수
- 인스턴스 변수 shape : 배열의 형상
튜플(tuple)이란? : 파이썬의 자료형
- 콤마(,)로 분리된 값을 의미하며 순서있는 임의의 객체의 모음
- 데이터를 열거하여 담아두는데 사용
- 튜플 내의 값은 수정할 수 없음으로 상수와 비슷한 속성을 가짐 (추가x, 삭제x)
- 수정하면 안 되는 자료를 쓸 때 사용
shape이 튜플을 반환하는 이유 : 1차원 배열이라도 다차원 배열일 때와 통일된 형태로 결과를 반환하기 위함
1차원 : (4), 2차원 : (4,3), 3차원(4,3,2)
위의 코드에서 B는 '3X2 배열'
0 번째 차원에 원소 3개, 1 번째 차원에 원소 2개 (파이썬의 인덱스는 0부터 시작)
→ 2차원 배열 : 행렬(matrix)
배열의 가로 방향 : 행(row) / 배열의 세로 방향 : 열(column)
3.3.2 행렬의 곱
np.dot( ) : 입력이 1차원 배열일 때 벡터, 2차원 배열일 때 행렬의 곱 계산
- 2x2 행렬의 곱
결과
[[19 22]
[43 50]]
[[23 34]
[31 46]]
이때 np.dot(A,B) ≠ np.dot(B,A)
- 2x3행렬 A 와 3x2행렬 B의 곱
결과
[[22 28]
[49 64]]
행렬의 곱에서 주의 할 점
: 행렬의 형상(shape) 주의 → 행렬 A의 1번째 차원의 원소 수(열의 수) = 행렬 B의 0번째 차원의 원소 수(행의 수)
>> 다차원 배열을 곱하려면 두 행렬의 대응하는 차원의 원소 수를 일치시켜야 함
3.3.3 신경망에서의 행렬 곱
넘파이 행렬을 이용한 신경망 구현
결과
[ 5 11 17]
X,W의 대응하는 차원의 원소 수가 같음
np.dot( ) : 행렬의 곱으로 한 번에 계산 가능
행렬의 곱으로 한번에 계산해주는 기능은 신경망을 구현할 때 매우 중요
3.3 3층 신경망 구현하기
3층 신경망 : 입력층(0층) - 첫 번째 은닉층(1층) - 두 번째 은닉층(2층) - 출력층(3층)
이번장의 핵심 : 신경망에서의 계산을 행렬 계산으로 정리할 수 있다는 것
3.4.1 표기법 설명
3.4.2 각 층의 신호 전달 구현하기
- 1층의 첫 번째 뉴런으로 가는 신호
편향은 아래 인덱스가 1개 : 앞 층의 편향 뉴런이 하나이기 때문
a1(1) : 가중치를 곱한 신호 두 개와 편향의 합
여기서 행렬의 곱을 이용하여 1층의 '가중치 부분' 간소화
각각의 식을 나타내면
넘파이 배열을 이용한 구현 (입력 신호, 가중치, 편향은 책의 임의의 값으로 설정)
결과
[0.3 0.7 1.1]
- 입력층에서 1층으로의 신호 전달 시 활성화 함수에서의 처리
a : 은닉층에서의 가중치 합 (가중 신호와 편향의 총합)
h( ) : 활성화 함수 → 시그모이드 함수 이용
z : 활성화 함수 h( )로 변환된 신호
- 1층에서 2층으로 가는 과정
- 2층에서 출력층으로의 신호 전달
항등 함수인 identity_function( ) 정의, 이를 출력층의 활성화 함수로 이용
항등함수 : 입력을 그대로 출력하는 함수 & σ( )로 표시
3.4.3 구현 정리
신경망의 순방향 최종 구현
- init_network( ) 함수 : 가중치와 편향 초기화하고 이들을 딕셔너리 변수인 network에 저장 - 딕셔너리 변수 network에는 각 층에 필요한 매개변수(가중치와 편향) 저장
- forward( ) 함수 : 입력 신호를 출력으로 변환하는 처리 과정 구현 - 신호가 순방향(입력~출력)으로 전달됨(순전파)을 알리기 위해 함수 이름을 forward라 함
3.5 출력층 설계하기
신경망은 활성화 함수를 어떤 것을 사용하느냐에 따라 이용 용도가 달라진다.
(일반적으로) 분류(classification)에는 소프트맥스 활성화 함수, 회귀(regression)에는 항등 함수를 사용한다.
분류 : 데이터가 어떤 클래스(class)에 속하느냐에 대한 문제 -ex) 사진 속 인물의 성별
회귀 : 입력 데이터에서 (연속적인) 수치 예측 -ex) 사진 속 인물의 몸무게
3.5.1 항등 함수와 소프트맥스 함수 구현하기
- 항등 함수(identity function) : 입력을 그대로 출력하는 함수 (입력과 출력이 같다)
- 회귀(regression)에 사용
- 소프트맥스 함수(softmax function)
- 분류(classification)에 사용
exp( ) = e^x : 지수함수(exponential function) (e는 자연상수)
n : 출력층의 뉴런 수
yk : 출력층의 뉴런 수 중 k번째 출력
소프트맥스 함수 분자 : 입력 신호 ak의 지수함수
소프트맥스 함수 분모 : 모든 입력 신호의 지수 함수의 합
소프트 맥스 함수의 분모에서 보듯, 출력층의 각 뉴런이 모든 입력 신호에서 영향을 받기 때문에
출력은 모든 입력신호로 부터 화살표를 받음
3.5.2 소프트맥스 함수 구현 시 주의점
위 코드 소프트맥스 함수 구현(1)는 식은 제대로 표현하고 있으나 컴퓨터로 계산 시 결함이 있음
소프트맥스 함수가 사용하는 지수함수는 쉽게 큰 값을 내뱉음
→ 오버플로우 (overflow) 문제
: 컴퓨터는 유한한 데이터를 다룸(4byte, 8byte ...) 따라서 표현할 수 있는 수의 범위보다 큰 값은 표현할 수 없는 문제 발생
소프트맥스 함수 구현 개선
전개 과정
1) C라는 임의의 정수를 분자와 분모에 곱함
2) C를 지수 함수 exp( )안으로 옮겨 logC로 만듦
3) logC → logC'
⇨ 소프트맥스의 지수 함수를 계산할 때 어떤 정수를 더해도(혹은 빼도) 결과는 바뀌지 않음
오버플로우를 막기 위해 C' = -(입력 신호 중 최댓값)
3.5.3 소프트맥스 함수의 특징
소프트맥스 함수의 성질 : 출력의 총합 = 1
→ 소프트맥스 함수의 출력을 '확률'로 해석 가능
[결과]
[0.01821127 0.24519181 0.73659691]
1.0
y[0] = 0.3 은 약 1.8%
y[1] =2.9 은 약 24.5%
y[2] = 4.0 은 약 73.7%
여기서 주의할 점은 소프트맥스 함수를 적용해도
소프트 맥스 함수의 지수 함수 exp(x)가 단조 증가 함수이기 때문에
각 원소의 대소 관계는 변하지 않음
단조 증가 함수의 예
단조 증가 : a<b일 때 f(a)<f(b)인 경우
신경망을 이용한 분류에서 일반적으로 가장 큰 출력을 내는 뉴런에 해당하는 클래스로만 인식
그리고 소프트맥스 함수를 적용해도 출력이 가장 큰 뉴런의 위치는 달라지지 않음
즉, 신경망으로 분류할 때는 출력층의 소프트맥스 함수 생략 가능
(현업에서도 지수 함수 계산에 드는 자원 낭비를 줄이고자 출력층의 소프트맥스 함수는 생략하는 것이 일반적)
3.5.4 출력층의 뉴런 수 정하기
출력층의 뉴런 수는 문제에 맞게 적절하게 정해야 함
분류에서는 분류하고 싶은 클래스 수로 정하는 것이 일반적
ex) 입력 이미지를 숫자 0~9까지로 분류하는 문제 → 출력층 뉴런을 10개로 설정
3.6 손글씨 숫자 인식
손글씨 숫자 분류를 신경망을 이용하여 분류
(3장에서는 추론 과정만 구현) → 이러한 추론 과정을 신경망의 순전파(forward propagation)
기계학습과 마찬가지로 신경망도 훈련 데이터를 이용하여 가중치 매개변수를 학습하고, 추론 단계에서 학습한 매개변수를 이용하여 입력 데이터를 분류한다
3.6.1 MNIST 데이터셋
- MNIST 데이터셋 : 손글씨 숫자 이미지 집합
- 0~9까지의 숫자 이미지로 구성
- 훈련 이미지 60,000장, 테스트 이미지 10,000장 준비
→ 훈련 이미지들을 사용하여 모델을 학습하고, 학습한 모델로 시험 이미지들을 얼마나 정확하게 분류하는지 평가
- MNIST 이미지 데이터
28x28 크기의 회색조 이미지
각 픽셀은 0~255
이미지가 실제 의미하는 숫자가 레이블로 붙어있음
ch01이라는 디렉토리를 생성해서 코드를 작성할 파일 저장
**
mnist.py를 임포트해 사용하려면 작업 디렉터리를 ch01, ch02, ... , ch08 중 하나로 옮겨야 함
mnist.py 파일은 dataset 디렉토리에 있고, 이 파일을 이용하는 다른 예제들은 각각 ch01, ch02, ... , ch08 디렉토리에서만 수행한다고 가정
그래서 각 예제에서 mnist.py 파일을 찾으려면 부모 디렉토리로부터 시작해야 해서 'sys.path.append(os.pardir)' 추가한 것 **
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/dataset/mnist.py
GitHub - oreilly-japan/deep-learning-from-scratch: 『ゼロから作る Deep Learning』(O'Reilly Japan, 2016)
『ゼロから作る Deep Learning』(O'Reilly Japan, 2016). Contribute to oreilly-japan/deep-learning-from-scratch development by creating an account on GitHub.
github.com
에서 mnist.py 파일 다운로드
- 가장 먼저 부모 디렉토리의 파일을 가져올 수 있도록 설정
- mnist.py 파일에 정의된 load_mnist( ) 함수를 import하여 데이터를 가져옴
- load_mnist( ) 함수는 읽은 MNIST 데이터를 "(훈련 이미지, 훈련 레이블), (테스트 이미지, 테스트 레이블)" 형식으로 반환
인수로는 normalize, flatten, one_hot_label 세 가지를 설정 할 수 있음 (세 인수는 모두 bool)
- normalize : 입력 이미지의 픽셀 값을 0.0 ~ 1.0 사이의 값으로 정규화 할지 결정 (False로 설정하면 입력 이미지의 픽셀은 원래 값 그대로 0~255 유지)
- flatten : 입력 이미지를 평탄하게 즉, 1차원 배열로 만들지 결정 (False로 설정하면 입력이미지를 1x28x28의 3차원 배열로, True로 설정하면 784개의 원소로 이뤄진 1차원 배열로 저장)
- one_hot_label : 레이블을 원-핫-인코딩(one-hot encoding)형태로 저장할지 결정 (원-핫 인코딩이란, 정답을 뜻하는 원소만 1(hot)이고, 나머지는 모두 0인 배열, one_hot_label이 False이면 '7', '2'와 같이 숫자 형태의 레이블 저장, True이면 레이블을 원-핫 인코딩하여 저장)
* 파이썬의 pickle 기능
프로그램 실행 중에 특정 객체를 파일로 저장하는 기능
저장해둔 pickle 파일을 로드하면 실행 당시의 객체를 즉시 복원할 수 있음
MNIST 데이터셋을 읽는 load_mnist( ) 함수에서도 (2번째 이후 읽기 시) pickle 이용
pickle 덕분에 데이터를 순식한게 준비할 수 있음
MNIST 이미지 화면에 불러오기
이미지 표시 : PIL(python Image Library) 모듈 사용
[결과]
(60000, 784)
(60000,)
(10000, 784)
(10000,)
5 # 출력할 이미지의 레이블
(784,) # 변형 전 이미지 모양
(28, 28) # 변형 후 이미지 모양
코드에서 flatten = True로 설정해 읽어드린 이미지는 1차원 넘파이 배열로 저장되어 있음
따라서 이미지를 표시할 때는 원래 형상인 28x28 크기로 다시 변형해야 함
→ reshape( ) 메서드에 원하는 형상을 인수로 지정 : 넘파이 배열의 형상으로 바꿀 수 있음
또한 넘파이로 저장된 이미지 데이터를 PIL용 데이터 객체로 변환해야 하는데 이 변환은 Image.fromarray( )가 수행
3.6.2 신경망 추론 처리
MNIST 데이터셋을 가지고 추론을 수행하는 신경망 구현
신경망의 구성
: 입력층 뉴런 784개 - 출력층 뉴런 10개 (0~9 구분),
은닉층 2개 (첫 번째 은닉층 50개의 뉴런, 두 번째 은닉층 100개의 뉴런 (임의의 설정값))
- get_data( )
- init_network( ) : pickle 파일인 sample_weight.pkl 에 저장된 '학습된 가중치 매개변수'를 읽음 (이 파일에 가중치와 편향 매개변수가 딕셔너리 변수로 저장되어 있음)
- predict( )
sample_weight.pkl 파일 (코드 실행 터미널 최종 주소 디렉토리에 저장)
GitHub - oreilly-japan/deep-learning-from-scratch: 『ゼロから作る Deep Learning』(O'Reilly Japan, 2016)
『ゼロから作る Deep Learning』(O'Reilly Japan, 2016). Contribute to oreilly-japan/deep-learning-from-scratch development by creating an account on GitHub.
github.com
- 정확도 평가
[결과]
Accuracy:0.098
- 가장 먼저 MNIST 데이터셋을 얻고 네트워크 생성
- for 문을 돌면서 x에 저장된 이미지 데이터를 1장씩 꺼내 predict( ) 함수로 분류
- (predict( ) 함수는 각 레이블의 확률을 넘파이 배열로 반환)
- np.argmax( ) 함수로 이 배열에서 값이 가장 큰(확률이 가장 높은) 원소의 인덱스를 구함 >> 예측 결과
- 신경망이 예측한 답변과 정답 레이블을 빅하여 맞힌 숫자(accuracy_cnt)를 세고, 전체 이미지 숫자로 나눠 정확도 구함
결과 Accuracy:0.098 = 올바르게 분류한 비율 98%
- load_mnist( ) 함수 인수인 normalize를 (getdata( )를 통해) True로 설정 : 픽셀 값의 범위를 0~255 에서 0.0~1.0 범위로 변환 (단순히 픽셀의 값을 255로 나눔)
⇨ 정규화(nomalization) : 데이터를 특정 범위로 변환하는 처리
전처리(pre-precessing) : 신경망의 입력 데이터에 특정 변환을 가하는 것
입력 이미지 데이터에 대한 전처리 작업으로 정규화를 수행
3.6.3 배치 처리
입력 데이터와 가중치 매개변수의 '형상'에 주의하여 구현
각 층의 가중치 형상 출력
[결과]
(10000, 784)
(784,)
(784, 50)
(50, 100)
(100, 10)
→ 다차원 배열의 대응하는 차원의 원소수가 일치함을 확인
이미지 데이터 1장만 입력했을 때의 처리 흐름
원소 784개로 구성된 1차원 배열(원래는 28x28인 2차원 배열)이 입력되어 최종 결과로 원소가 10개인 1차원 배열 y출력
이미지 데이터를 여러개 입력했을 때에는?
예를 들어 이미지 데이터 100개를 묶어 predict( ) 함수에 한번에 넘김
x의 형상을 100x784로 바꿔서 100장 분량의 데이터를 하나의 입력 데이터로 표현
입력 데이터 형상은 100x784, 출력 데이터 형상은 100x10이 됨
100장 분량 입력 데이터의 결과가 한 번에 출력됨을 나타냄
x[0]과 y[0]에는 0번째 이미지와 그 추론 결과가, x[1]과 y[1]에는 1번째의 이미지와 그 결과가 저장되는 식
배치(batch) : 하나로 묶은 입력 데이터
배치의 이점
- 이미지 1장당 처리 시간을 대폭 줄여줌
- 수치 계산 라이브러리 대부분이 큰 배열을 효율적으로 처리할 수 있도록 고도로 최적화되어 있기 때문
- 커다란 신경망에서는 데이터 전송이 병목으로 작용하는 경우가 자주 있는데, 배치 처리를 함으로써 버스에 주는 부하를 줄인다.(느린 I/O를 통해 데이터를 읽는 횟수가 줄어, 빠른 CPU나 GPU로 순수 계산을 수행하는 비율이 높아짐)
즉, 배치 처리를 수행함으로써 큰 배열로 이뤄진 계산을 하게 되는데, 컴퓨터에서는 큰 배열을 한꺼번에 계산 하는 것이 분할된 작은 배열을 여러 번 계산하는 것보다 빠름
* range( )
range(start, end) : 인수 2개 지정 -- start에서 end-1까지의 정수로 이뤄진 리스트 반환
range(start, end, step) : 인수 3개 지정 -- start에서 end-1까지 step 간격으로 증가하는 리스트 반환
- range( ) 함수가 반환하는 리스트를 바탕으로 x[i:i+batch_size] 에서 입력 데이터를 묶음
- ( x[i:i+batch_size] : 입력 데이터의 i번째 부터 i+batch_size번째까지의 데이터를 묶는다는 의미 )
- ( 코드에서 batch_size = 100 이므로 x[0:100], x[100:200], ... 와 같이 앞에서부터 100장씩 묶여서 꺼내게 됨
- argmax( ) : 최댓값의 인덱스를 가져옴
- ( argmax( )에서 axis=1 인수 추가 : 100x10의 배열 중 1번째 차원을 구성하는 각 원소에서(1번째 차원을 축으로) 최댓값의 인덱스를 찾도록 한 것(인덱스가 0부터 시작하니 0이 첫번째 차원) )
배치 단위로 분류한 결과를 실제 답과 비교하는 처리는 다음장에서..
데이터를 배치로 처리함으로써 효율적이고 빠르게 처리 가능
'AI' 카테고리의 다른 글
Deep Learning for Coders with fastai & PyTorch 1장 (0) | 2023.07.31 |
---|---|
[Deep Learning from Scratch] CHAPTER 7 합성곱 신경망(CNN) (0) | 2023.03.09 |
[Deep Learning from Scratch] CHAPTER 6 학습 관련 기술들 (0) | 2023.03.02 |
[Deep Learning from Scratch] CHAPTER 5 오차역전파법 (0) | 2023.02.21 |