■ ML의 문제 해결 과정은 학습과 추론 단계로 나뉜다. 학습 단계에서는 말 그대로 train set으로 모델에 대한 학습을 진행하고, 추론 단계에서는 최종 선택한 학습한 모델로 unseen data에 대해 추론, 즉 분류 문제이면 분류, 회귀 문제이면 회귀를 수행한다.
■ 신경망도 ML과 마찬가지로 이 두 단계를 거쳐 문제를 해결해 나간다. 차이점은 신경망에선 train set으로 매개변수를 학습하고, 추론 단계에서 학습한 매개변수를 사용해 입력 데이터를 예측한다.
■ 이번에는 케라스의 MNIST 데이터셋을 이용하여 추론 단계의 과정만 진행해 본다.
■ 먼저 다음과 같이 keras에서 MNIST 데이터셋을 불러온다.
import tensorflow as tf
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print(x_train.shape, x_test.shape)
print(y_train.shape, y_test.shape)
```#결과#```
(60000, 28, 28) (10000, 28, 28)
(60000,) (10000,)
````````````
- MNIST 데이터는 28 × 28 크기의 이미지가 저장되어 있다.
- 이 이미지 데이터를 입력으로 사용하기 위해 1차원 배열로 다음과 같이 평탄화하고 정규화를 적용한다.
- 정규화를 적용하는 이유는 이미지의 각 픽셀이 0에서 255 범위의 값을 가지기 때문에, 정규화하지 않고 진행하면 학습 속도가 느려진다. 따라서 정규화를 적용하여 각 이미지의 픽셀을 0.0 ~ 1.0 범위로 조정한다.
# 이미지 평탄화
x_train = x_train.reshape(60000, 28*28)
x_test = x_test.reshape(10000, 28*28)
print(x_train.shape, x_test.shape)
```#결과#```
(60000, 784) (10000, 784)
````````````
print(np.min(x_train), np.max(x_train))
print(np.min(x_test), np.max(x_test))
```#결과#```
0 255
0 255
````````````
# 데이터 정규화
x_train = x_train / x_train.max()
x_test = x_test / x_test.max()
print(np.min(x_train), np.max(x_train))
print(np.min(x_test), np.max(x_test))
```#결과#```
0.0 1.0
0.0 1.0
````````````
- 다음으로 신경망의 구조를 설정한다.
# 형태 정의
shapes = {
'W1': (784, 50), # 28 × 28 이미지를 평탄화하면 784, 즉 입력값이 784 & 첫 번째 은닉층에 뉴런 50개
'W2': (50, 100), # 두 번째 은닉층에 뉴런 100개
'W3': (100, 10), # 출력층에 뉴런 10개, mnist 데이터셋의 클래스가 0부터 9까지 10개이기 때문
'b1': (50,),
'b2': (100,),
'b3': (10,)
}
# 가중치와 편향을 저장할 딕셔너리
layer_params = {}
# 딕셔너리에 가중치 및 편향 값 채우기
for key, shape in shapes.items():
if key.startswith('W'):
layer_params[key] = np.random.rand(*shape) # 0과 1 사이의 랜덤 값으로 가중치 생성
elif key.startswith('b'):
layer_params[key] = np.random.rand(*shape) # 0과 1 사이의 랜덤 값으로 편향 생성
- 위와 같이 신경망의 구조를 설계하면 이미지 데이터 1장을 입력했을 때, 신경망의 처리 흐름은 다음과 같다.
- 은닉층과 출력층에 사용할 활성화 함수를 다음과 같이 정의한다.
def sigmoid_function(x): # 은닉층에 사용할 활성화 함수
return 1 / (1 + np.exp(-x))
def softmax_function(x): # 출력층에 사용할 활성화 함수
exp_a = np.exp(x - np.max(x))
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
- 다음으로 입력값과 가중치, 편향을 받아 이미지 데이터를 분류할 함수를 다음과 같이 정의한다.
def predict(parmas, x):
for key, value in parmas.items():
globals()[key] = value
a1 = np.dot(x, W1) + b1
z1 = sigmoid_function(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid_function(a2)
a3 = np.dot(z2, W3) + b3
y = softmax_function(a3)
return y
- 학습 과정을 생략하고 추론 단계만 진행하기 위해 train과 test set을 다음과 같이 합친 다음, 순서를 섞어 준다.
X = np.concatenate((x_train, x_test), axis=0)
y = np.concatenate((y_train, y_test), axis=0)
y
```#결과#```
array([5, 0, 4, ..., 4, 5, 6], dtype=uint8))
````````````
indices = np.arange(X.shape[0])
np.random.shuffle(indices)
# 섞기
X = X[indices]
y = y[indices]
y
```#결과#```
array([2, 2, 5, ..., 9, 6, 3], dtype=uint8))
````````````
- 신경망이 분류를 얼마나 올바르게 하였는지 확인하기 위하여 다음과 같이 정확도를 구한다.
acc_count = 0
for i in range(len(X)):
y_pred = predict(layer_params, X[i])
p = np.argmax(y_pred)
if p == y[i]:
acc_count += 1
print(acc_count / len(x_test)*100)
```#결과#```
72.92999999999999 # 정확도는 약 72.93%
````````````
- 위의 과정은 모든 데이터셋을 분류하기 위해 데이터셋의 길이만큼 분류를 진행한다.
- predict 사용자 정의 함수를 호출하여 분류할 이미지(입력값)을 순서대로 가중치와 편향을 결합하여 진행한다. 최종 결과는 출력층의 활성화 함수가 softmax이므로 각 클래스(0 ~ 9)에 대한 확률값을 반환한다.
- 그 확률 값을 y_pred에 저장하고, np.argmax( )함수를 이용해 확률값이 가장 큰 원소의 인덱스를 찾는다.
- 마지막으로 인덱스를 이용해 확률값이 가장 큰 원소와 y(정답 레이블)을 비교하여 분류를 올바르게 하면 acc_count의 값을 1씩 증가한다. 따라서 최종적으로 acc_count에는 올바르게 분류한 이미지의 수가 들어가게 되고, 이에 대한 정확도를 구하기 위해 acc_count의 값을 전체 이미지 데이터 수로 나눈다.
■ 위에서 이미지 데이터 1개가 들어왔을 때 신경망의 흐름은 다음과 같이 진행된다고 하였다.
■ 만약, 이미지 1개에서 이미지 100개씩 입력으로 사용한다면, 이미지에 대한 처리 시간을 크게 줄일 수 있을 것이다.
왜냐하면, 위의 X의 크기 1 × 784에서 크기를 100 × 784로 변경한다면, 출력 데이터의 크기도 1 × 10에서 100 × 10이 되므로 입력으로 100 × 784가 들어와서 출력으로 이미지 100장에 대한 결과가 한 번에 출력되기 때문이다.
이렇게 하나로 묶은 입력 데이터를 배치(batch)라고 한다.
■ 위의 코드에서 입력 이미지를 배치 처리하여 분류하려면 루프가 한 번 돌 때, 100장씩 들어가도록 하며, 결과를 비교할 때도 100장씩 비교하도록 다음과 같이 코드를 수정하면 된다.
acc_count = 0
batch_size = 100 # 배치 크기
for i in range(0, len(X), batch_size):
X_batch = X[i:i+batch_size] # 0에서 99, 100개씩
y_pred = predict(layer_params, X_batch)
p = np.argmax(y_pred, axis = 1) # y_pred는 100×10이며, 각각 가장 큰 값의 인덱스를 찾는다.
acc_count += np.sum(p == y[i:i+batch_size]) # == 연산자를 이용했기 때문에 결과는 True or False 이므로 sum을 이용해 True면 1, False는 0으로 계산