본문 바로가기

데이터/머신러닝

[혼공] ch 7-2 심층 신경망

1. 2개의 층 

 

- 케라스 API를 사용해서 패션 MNIST 데이터셋 불러오기

from tensorflow import keras

(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()

 

 

- 이미지의 픽셀값을 0~255 범위에서 0~1 사이로 변환하고, 28*28 크기의 2차원 배열을 784 크기의 1차원 배열로 펼치기

- 사이킷런의 train_test_split() 함수로 훈련 세트와 검증 세트로 나눔 

from sklearn.model_selection import train_test_split

train_scaled = train_input / 255.0
train_scaled = train_scaled.reshape(-1, 28*28)

train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42)

 

- 인공 신경망 모델에 층을 2개 추가 

- 입력층과 출력층 사이에 있는 모든 층을 은닉층(hidden layer)이라고 부른다. 

- 활성화 함수는 신경망 층의 선형 방정식의 계산 값에 적용하는 함수이다. 

- 출력층에 적용하는 활성화 함수는 종류가 제한적(이진 분류 -> 시그모이드 함수, 다중 분류 -> 소프트맥스 함수)

- 반면 은닉층의 활성화 함수는 비교적 자유롭다. 

 

 

- 많이 사용하는 활성화 함수는  시그모이드 함수이고 뉴런의 출력 z값을 0과 1 사이로 압축한다 ,

- 시그모이드 활성화 함수를 사용한 은닉층소프트맥스 함수를 사용한 출력층케라스 Dense 클래스로 만들기 

 

dense1 = keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)) #은닉충
dense2 = keras.layers.Dense(10, activation='softmax') #출력층

 

- dense1이 은닉충이고 100개의 뉴런을 가진 밀집층이다. 활성화 함수를 'sigmoid'로 지정했고 input_shape 매개변수에서 입력의 크기를 (784,)로 지정했다, 

- 은닉층의 뉴런 개수를 정하는 데는 특별한 기준이 없으나 출력층의 뉴런보다는 많게 만들어야 한다. 

- dense2는 출력층으로 10개의 클래스를 분류하므로 10개의 뉴런을 두었고 활성화 함수는 소프트맥스 함수로 지정했다. 

 

 

2. 심층 신경망 만들기 

 

- dense1과 dense2 객체를 Sequential 클래스에 추가하여 심층 신경망(deep neural network, DNN)을 만들기 

 

model = keras.Sequential([dense1, dense2])

model.summary()

 

 

- Sequential 클래스의 객체를 만들 때 여러 개의 층을 추가하려면 dense1과 dense2를 리스트로 만들어 전달한다. 

- 주의할 것은 출력층을 가장 마지막에 두어야 한다. 

 

- 인공 신경망의 강력한 성능은 층을 추가하여 입력 데이터에 대해 연속적인 학습을 진행하는 능력에서 나온다. 

 

- summary() 메서드를 호출하면 층에 대한 유용한 정보를 얻을 수 있다. 

- 맨 첫 줄에 모델의 이름이 나온다. 그 다음 이 모델에 들어 있는 층이 순서대로 나열된다. 이 순서는 맨 처음 추가한 은닉층에서 출력층의 순서대로 나열된다, 

 

- 층마다 층 이름, 클래스, 출력 크기, 모델 파라미터 개수가 출력된다. 

 

- 출력 크기를 보면 (None, 100)이다. 첫 번째 차원은 샘플의 개수를 나타낸다. 샘플 개수가 아직 정의되어 있지 않기 때문에 None이다. 

 -> 케라스 모델의 fit() 메서드에 훈련 데이터를 주입하면 이 데이터를 한 번에 모두 사용하지 않고 잘게 나누어 여러 번에 걸쳐 경사 하강법 단계를 수행한다. 케라스의 기본 미니배치 크기는 32이다. 이 값은 fit() 메서드에서 batch_size 매개변수로 바꿀 수 있다. 따라서 샘플 개수를 고정하지 않고 어떤 배치 크기에도 유연하게 대응할 수 있도록 None으로 설정

 

 -> 두 번째 100은 은닉층의 뉴런 개수를 100개로 두었으니 100개의 출력이 나온다.

 -> 즉, 784개의 픽셀값이 은닉층을 통과하면서 100개의 특성으로 압축되었다. 

 

- 마지막으로 모델 파라미터 개수가 출력된다. 

 

- 두 번째 층의 출력 크기는 (None, 10)이다. 배치 차원은 동일하게 None이고 출력 뉴런 개수가 10개이기 때문이다. 

- 100개의 은닉층 뉴런과 10개의 출력층 뉴런이 모두 연결되고 출력층의 뉴런마다 하나의 절편이 있기 때문에 총 1,010개의 모델 파라미터가 있다. 

 

- summary() 메서드의 마지막에는 총 모델 파라미터 개수와 훈련되는 파라미터 개수가 동일하게 79,510개로 나온다. 

- 은닉층과 출력층의 파라미터 개수를 합친 값이다. 

- 그 아래 훈련되지 않은 파라미터(Non-trainable params)는 0으로 나온다. 

 -> 간혹 경사 하강법으로 훈련되지 않는 파라미터를 가진 층이 있는데, 이런 층의 파라미터 개수가 나타난다. 

 

 

3. 층을 추가하는 다른 방법 

 

1) Sequential 클래스의 생성자 안에서 바로 Dense 클래스의 객체를 만드는 방법 

 

model = keras.Sequential([
    keras.layers.Dense(100, activation='sigmoid', input_shape=(784,), name='hidden'),
    keras.layers.Dense(10, activation='softmax', name='output')
], name='패션 MNIST 모델')


model.summary()

 

 

 

2) add() 메서드 

 

- Sequential 클래스의 객체를 만들고 이 객체의 add() 메서드를 호출하여 층을 추가 

 

model = keras.Sequential()
model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)))
model.add(keras.layers.Dense(10, activation='softmax'))

model.summary()

 

 

 

 

- 모델 훈련 

model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

model.fit(train_scaled, train_target, epochs=5)

 

 

- 훈련 세트에 대한 성능을 보면 추가된 층이 성능을 향상시켰다는 것을 알 수 있다. 

 

 

4. 렐루 함수 

 

- 시그모이드 함수는 오른쪽과 왼쪽 끝으로 갈수록 그래프가 누워있기 때문에 올바른 출력을 만드는 데 신속하게 대응하지 못한다. 

- 이를 개선하기 위해서 다른 종류의 활성화 함수가 제안되었다. 

 

렐루(ReLU) 함수 

- 입력이 양수일 경우 마치 활성화 함수가 없는 것처럼 그냥 입력을 통과시키고 음수일 경우에는 0으로 만든다. 

 

 

- 렐루 함수는 max(0,z)와 같이 쓸 수 있다. 

- 이 함수는 z가 0보다 크면 z를 출력하고 z가 0보다 작으면 0을 출력한다. 

- 렐루 함수는 특히 이미지 처리에서 좋은 성능을 낸다고 알려져 있다, 

 

- Flatten 층은 추출된 주요 특징을 전결합층에 전달하기 위해 1차원 배열로 바꿔주는 층이다. 

- 이미지 형태의 데이터를 배열형태로 flatten하게 만들어 준다. 

- Flatten 클래스는 배치 차원을 제외하고 나머지 입력 차원을 모두 일렬로 펼치는 역할만 한다.  입력에 곱해지는 가중치나 절편이 없기 때문에 인공 신경망의 성능을 위해 기여하는 바는 없다. 하지만 Faltten 클래스를 층처럼 입력층과 은닉층 사이에 추가하기 때문에 이를 층이라고 부른다.

 

#Flatten 층은 입력층 바로 뒤에 추가
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28, 28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))


model.summary()

 

 

 

- Flatten 클래스에 포함된 모델 파라미터는 0이다. 

- 케라스의 Flatten 층을 신경망 모델에 추가하면 입력값의 차원을 짐작할 수 있는 것이 또 하나의 장점이다. 

 

- 훈련 데이터 준비 후 모델 훈련 

(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()

train_scaled = train_input / 255.0

train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42)

 

 

- 모델 컴파일하고 훈련 

model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

model.fit(train_scaled, train_target, epochs=5)

 

 

 

- 시그모이드 함수를 사용했을 떄와 비교하면 성능이 조금 향상되었다. 

 

- 검증 세트에서의 성능 확인 

model.evaluate(val_scaled, val_target)

 

 

- 은닉층을 추가하지 않은 경우보다 몇 퍼센트 성능이 향상되었다, 

 

 

5. 옵티마이저 

 

- 추가할 은닉층의 개수는 모델이 학습하는 것이 아니라 우리가 지정해 주어야 할 하이퍼 파라미터

- 추가할 은닉층의 개수, 뉴런 개수, 활성화 함수, 층의 종류, 배치 사이즈 매개변수, 에포크 매개변수 등은 하이퍼 파라미터이다. 

- compile() 메서드에서는 케라스의 기본 경사 하강법 알고리즘인 RMSprop을 사용했다, 

- 케라스는 다양한 종류의 경사 하강법 알고리즘을 제공하는 데, 이들을 옵티마이저(optimizer)라고 부른다. 

 

- 가장 기본적인 옵티마이저는 확률적 경사 하강법인 SGD이다. 

- SGD 옵티마이저를 사용하려면 compile() 메서드의 optimizer 매개변수를 'sgd'로 지정한다. 

- 이 옵티마이저는 tensorflow.keras.optimizers 패키지 아래 SGD 클래스로 구현되어 있다. 

 

model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics='accuracy')

 

- 만약 SGD 클래스의 학습률 기본값이 0.01일 때 이를 바꾸고 싶다면 다음과 같이 원하는 학습률을 learning_rate 매개변수에 지정하여 사용한다. 

 

sgd = keras.optimizers.SGD(learning_rate=0.1)

 

 

- 기본 경사 하강법 옵티마이저는 모두 SGD 클래스에서 제공한다. 

- SGD 클래스의 momentum 매개변수의 기본값은 0이다. 

- 0보다 큰 값으로 지정하면 마치 그레이디언트를 가속도처럼 사용하는 모멘텀 최적화(momentum optimization)를 사용한다. 보통 momentum 매개변수는 0.9 이상을 지정한다. 

 

- SGD 클래스의 nesterov 매개변수를 기본값 False에서 True로 바꾸면 네스테로프 모멘텀 최적화(nesterov momentum optimization)를 사용한다. 

 

sgd = keras.optimizers.SGD(momentum=0.9, nesterov=True)

 

- 네스테로프 모멘텀은 모멘텀 최적화를 2번 반복하여 구현한다. 대부분의 경우 네스테로프 모멘텀 최적화가 기본 확률적 경사 하강법보다 더 나은 성능을 제공한다. 

 

- 모델이 최적점에 가까이 갈수록 학습률을 낮출 수 있다. 이렇게 하면 안정적으로 최적점에 수렴할 가능성이 높다. 이러한 학습률을 적응적 학습률(adaptive learning rate)이라고 한다. 

 

- 적응적 학습률을 사용하는 대표적인 옵티마이저는 AdagradRMSprop이다. 

- 각각 compile() 메서드 optimizer 매개변수에 'adagrad'와 'rmsprop'으로 지정할 수 있다. 

 

- optimizer 매개변수의 기본값이 바로 'rmsprop'이다. 이 두 옵티마이저의 매개변수를 바꾸고 싶다면 SGD와 같이 Adagrad와 RMSprop 클래스 객체를 만들어 사용하면 된다. 

 

adagrad = keras.optimizers.Adagrad()
model.compile(optimizer=adagrad, loss='sparse_categorical_crossentropy', metrics='accuracy')

 

rmsprop = keras.optimizers.RMSprop()
model.compile(optimizer=rmsprop, loss='sparse_categorical_crossentropy', metrics='accuracy')

 

- 모멘텀 최적화와 RMSprop의 장점을 접목한 것이 Adam이다. 

 

- Adam 클래스의 매개변수 기본값을 사용해 패션 MNIST 모델을 훈련 

#모델 다시 생성 

model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28, 28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))

 

- compile() 메서드의 optimizer를 'adam'으로 설정하고 5번의 에포크 동안 훈련한다. 

 

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')

model.fit(train_scaled, train_target, epochs=5)

 

 

 

 

- 검증 세트에서의 성능 확인 

model.evaluate(val_scaled, val_target)