정규화 (Normalization)
머신러닝에서 출력값과 실제 정답(label) 간의 차이를 측정하기 위해 Cost함수를 사용한다. 이러한 Cost함수를 계산하기 위해서는 w(weight)와 x(입력 데이터)를 곱하는 경우가 있는데, 이때 w와 x사이의 Scale을 조절해야 한다.
그렇지 않고 w와 x중 하나의 크기가 너무 크거나 작으면 한 요소의 영향만 커져버리게 되어 모델 학습에 부정적인 영향을 미칠 수 있다고 한다.
이러한 문제를 방지하기 위해 정규화 과정을 통해 w와 x의 Scale을 적절히 조절한다. 정규화 방식에는 다음과 같이 있다.
1. X의 평균이 0, 분산이 1이 되도록 표준화한다.
2. X를 0과 1사이의 범위로 압축하는 Scailing을 수행한다.
Linear Regression 구현
#LinearRegression_TF2.ipynb
import tensorflow as tf
import numpy as np
#===== 실행문 =====
x = np.array([1, 2, 3])
y = np.array([1, 2, 3])
from tensorflow.keras import layers
#===== 선언문 =====
model = tf.keras.Sequential()
model.add(layers.Dense(1, activation = 'linear', input_dim = 1))
sgd = tf.keras.optimizers.SGD(0.01)
model.compile(optimizer = sgd, loss = 'mse')
#===== 실행문 =====
model.fit(x, y, epochs = 1000, verbose = 2)
predicted = model.predict(x)
Linear Regression문제를 TensorFlow2를 사용하여 최대경사법으로 구현한 코드다.
Cost(w, b)를 최소화하는 w와 b를 찾기 위해 train을 생성하고, 학습률(learning rate)을 0.01로 설정하고, 1000회 최대경사법을 반복하는 코드다.
이 코드에서 주목할 점은 다음과 같다.
- TF특성상, 선언문이 많고 실행문이 적다. 이는 본래 한 줄씩 컴파일되던 파이썬 언어의 한계를 해결하고, C와 같은 속도를 내기 위한 설계법이다.
- 이 코드는 Sequential하게 설계되었다. 이는 레이어를 차곡차곡 쌓겠다는 의미다. 이러한 Sequential의 반대 개념은 Functional 방식이라고 한다.
- Learning Rate를 설정하기 위해 sgd = tf.keras.optimizers.SGD(0.01)에서 0.01로 설정했다.
- MSE란, SSE를 1/n한 것이다.
- 한 epoch 당 32번 돈다. 즉, 본 코드에서 1000번 수행하게 지정했으니 실제로는 32 * 1,000 = 32,000번 돈다는 의미가 된다.
Logistic Regression 구현
import tensorflow as tf
import numpy as np
x = np.array([[1],
[2],
[3],
[4],
[5],
[6]])
y = np.array([[0], [0], [0], [1], [1], [1]])
from tensorflow.keras import layers
model = tf.keras.Sequential()
model.add(layers.Dense(1, activation = 'sigmoid', input_dim = 1))
model.compile(oprimizer = tf.keras.optimizers.SGD(0.1), loss = 'binary_crossentropy')
model.fit(x, y, epochs = 2000, verbose = 0)
predicted = model.predict(x)
print(predicted)
weights = model.layers[0].get_weights()[0]
biases = model.layers[0].get_weights()[1]
print(weights, biases)
위 코드는 로지스틱 회귀를 코드로 구현한 것이다.
선형 회귀와 로지스틱 회귀는 모델링 방법이 다르다. 선형 회귀는 연속형 종속 변수와 연속형 독립 변수 간의 관계를 모델링한다. 로지스틱 회귀는 이진 관계를 모델링한다.
쉽게 설명해 선형 회귀는 키에 따른 몸무게 관계처럼 대체로 입력 값과 결과 값이 연속적이고 선형 관계인 문제를 다루고, 로지스틱 회귀는 특정 사람의 이미지를 주고 남자인지, 여자인지를 판별하는 문제 같이 Yes, No처럼 이진 문제를 다룬다.
이 코드에서 주목할 점은 다음과 같다.
- activation이 sigmoid이다. 로지스틱 회귀에서 다루는 문제의 함수 모양은 이진 관계를 나타내기 때문에 Linear가 아닌 Sigmoid를 사용한다.
- loss가 binary_crossentropy이다. loss란 손실함수를 의미하는데 여기서 쓰이는 손실함수인 binary cross entropy는 주로 이진 문제를 해결할 때 쓰인다.
MNIST Data
Mnist Dataset은 우편번호를 자동으로 인식하고자 만든 학습 데이터셋이다.
자료는 TF에서 내장 제공하는 데이터다. 28 * 28 = 784개의 픽셀에 0~255 사이의 숫자로 표현된 형식이다.
MnistData 활용 코드
#MnistData.ipynb
import tenserflow as tf
import numpy as np
from tenserflow.keras import datasets
mnist = datasets.mnist
(train_x, train_y), (test_x, test_y) = mnist.load_data()
print(train_x.shape)
image = train_x[0]
print(image.shape)
import matplotlib.pyplot as plt
plt.imshow(image, 'gray')
plt.show()
print(train_y.shape)
print(train_y[0])
label = train_y[0]
print(label)
from tensorflow.keras.utils import to_categorical #one-hot encoding
print(to_categorical(label, 10))
위 코드는 TF에서 제공하는 6만장의 데이터 중 첫번째 이미지를 출력하는 프로그램이다.
첫번째 이미지는 5이다. 이를 one-hot encoding으로 나타내어 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]로 표현한다.
위 코드에서 주목할 점은 다음과 같다.
print(train_x.shape, test_x.shape)
print(train_y.shape, test_y.shape)
>>>
(60000, 28, 28) (10000, 28, 28)
(60000,) (10000,)
- train 데이터와 test 데이터의 출력값은 위와 같다.
- One Hot Encoding: 출력값을 표현하는 방식 중 하나다. 반드시 0, 1로 나타낼 필요는 없고 각 요소를 확률로 나타낼 수도 있다.
Softmax 함수
$$ P(Y=0)+P(Y=1)+...+P(Y=9)=1 $$
Softmax 함수는 위와 같이 각 확률을 전체 확률로 나눠주는 식이다. 쉽게 말해 확률의 합을 1로 맞추기 위해 사용한다.
Softmax를 활용한 코드를 구현하면 다음과 같다.
#Mnist_Softmax_TF2.ipynb
import tensorflow as tf
import numpy as np
from tensorflow.keras import datasets
from tensorflow.keras.utils import to_categorical
mnist = datasets.mnist
(train_x, train_y), (test_x, test_y) = mnist.load_data()
train_x = train_x.reshape(-1, 784)
test_x = test_x.reshape(-1, 784)
train_x = train_x / 255
test_x = test_x / 255
train_y_onehot = to_categorical(train_y)
test_y_onehot = to_categorical(test_y)
from tensorflow.keras import layers
model = tf.keras.Sequential()
model.add(layers.Dense(10, activation = 'softmax', input_dim = 784))
model.compile(optimizer = 'sgd', loss = 'categorical_crossentropy', metrics = ['accuracy'])
#테스트 데이터 다 써서 학습결과 확인하는 경우
model.fit(train_x, train_y_onehot, epochs = 5)
model.evaluate(test_x, test_y_onehot)
predicted[0], test_y_onehot[0]
#Batch size 조절하여 학습결과 확인하는 경우
model.fit(train_x, train_y_onehot, batch_size = 100, epochs = 5)
model.evaluate(test_x, test_y_onehot)
predicted[0], test_y_onehot[0]
$$ Cost(w)=\sum^n_{i=1}\sum^c_{j=1}(-Y_{ij}\cdot logH(X)_{ij}) $$
위 코드와 식에서 주목할 점은 다음과 같다.
- binary cross entropy 손실함수나 categorical cross entropy 손실함수나 수학적으로는 같은 수식이다.
- ‘테스트 데이터 다 써서 학습결과 확인하는 경우’라고 표시한 부분은 학습에 사용되지 않은 테스트 데이터 10,000개를 통해 학습결과를 확인하는 것이다. 이 과정을 통해서 Overfitting 검증을 수행할 수 있다고 짧게 언급하셨다.
- batch size란, 학습시킬 데이터의 양이 너무 많을 때 시간이 오래걸리므로, 정확도를 조금 손해보더라도 빠르게 학습시키기 원할 때 조절하는 값이다.
- Batch Size의 기본 값은 32이다.
SGD(Stochastic Gradient Descent)
SGD는 일반적인 Gradient Descent 방식에 Batch Size의 개념이 추가된 개념이다. 기존에 가중값을 갱신하기 위해서 전체 데이터셋에 대한 학습이 필요하던 방식과 달리, SGD는 Batch Size만큼의 데이터를 통해 가중값을 갱신한다.
이러한 방식으로 인해 같은 시간에 더 많은 가중값을 갱신할 수 있다는 장점이 있지만, 해가 진동하는 문제와 Cost Function의 Convexity가 보장되지 않는 문제가 발생할 수 있다.
이러한 문제는 Batch Size에 따라 다르게 나타난다. 배치 사이즈가 클 경우에는 진동이 작아져 정확한 경로를 탐색해 해로 수렴한다는 장점이 있지만, 계산 속도가 느리다는 단점이 있다. 반대로 배치 사이즈가 작을 경우에는 계산 속도가 빠르다는 장점이 있지만, 진동이 커져서 정확도가 떨어질 수 있다는 단점이 있다. 결국, 배치 크기를 적절하게 지정하는 것이 중요하다.
이 밖에도 보다 빠른 수렴을 위해 Learning Rate를 크게 설정했다가 점차 작게 설정하는 방식도 있다.
Batch Size에 따른 계산 횟수
model.fit(train_x, train_y_onehot, batch_size = 100, epochs=5)
위와 같이 주어지고, 가중값 계산은 다음과 같다.
$$n=\frac{60,000}{(100)\cdot (5)}=3,000$$