0. 데이터셋 확인
- 사용 데이터: train 5000개, test 1000개
- 이미지 데이터의 크기 28x28
- 데이터 특징: 2차원의 0~255 픽셀 값으로 구성
- CNN의 input data format은 3차원(가로, 세로, 채널)
- => 전처리로 채널 차원을 추가해야한다
- 9개의 이미지 시각화
- 라벨 데이터
- 0~9까지의 10개의 값으로 라벨링 되어있다
- => 전처리 시, 원핫 인코딩 진행 필요
1. 데이터 전처리
1) x 데이터 채널차원 추가
numpy의 expand_dims 함수를 통해 채널 차원을 추가해준다
np.expand_dims (arr, axis)
arr: 차원을 확장할 배열
axis: 새로 추가될 차원의 위치(default: 0 -> 인덱스 0 앞에 새로운 차원을 추가하겠다)
실습에서는 axis에 -1을 넣어 맨 뒤에 새로운 차원을 추가하도록 했다
채널 차원이 추가되기 전에는 28x28의 2차원 행렬에서 채널 차원이 추가된 후에는 28x28x1의 3차원 행렬로 변환했다
2) 라벨 데이터 원핫인코딩
to_categorical 함수를 통해서 1차원의 배열로 원핫인코딩 작업을 진행했다
ex. 라벨 2: [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] 로 변경
- 전체 전처리 코드
def preprocess():
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images, train_labels = train_images[:5000], train_labels[:5000]
test_images, test_labels = test_images[:1000], test_labels[:1000]
# 255 값의 이미지를 0~1 사이의 값으로 정규화
train_images = train_images / 255
test_images = test_images / 255
# 채널차원 추가
train_images = np.expand_dims(train_images, -1)
test_images = np.expand_dims(test_images, -1)
# 원핫인코딩 진행
train_labels = to_categorical(train_labels, 10)
test_labels = to_categorical(test_labels, 10)
return train_images, test_images, train_labels, test_labels
2. 모델 생성
def CNN():
model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='SAME', input_shape= (28,28,1)))
model.add(tf.keras.layers.MaxPool2D(padding='SAME'))
model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=(3,3), activation='relu', padding='SAME'))
model.add(tf.keras.layers.MaxPool2D(padding='SAME'))
model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=(3,3), activation='relu', padding='SAME'))
model.add(tf.keras.layers.MaxPool2D(padding='SAME'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(32, activation='relu'))
model.add(tf.keras.layers.Dense(64, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
return model
- Conv2D layer
tf.keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='SAME', input_shape=(28,28,1))
- filter(=kernel)는 이미지의 특징을 찾아내기 위한 행렬
- 학교 수업에서는 필터를 '도장'으로 비유를 들어 학습했다. 나에겐 '도장'의 표현이 더 와닿았다.
- " Input 데이터를 도장의 크기 만큼 도장을 찍으며 특징을 확인한다 "
- kernel_size는 커널(필터)의 크기를 의미한다
- activation 활성화 함수는 relu 함수를 적용해서
- padding="SAME" 옵션을 통해서 Feature Map의 크기가 줄어들지 않고 동일하도록 설정한다
- input_shape 옵션은 입력층에서만 적용한다
- MaxPool2D layer
tf.keras.layers.MaxPool2D(padding='SAME')
- Max pooling는 Convolution 레이어의 출력 데이터를 입력 데이터로 받아서 크기를 줄이거나 특정 데이터의 특성을 강조한다
- Max pooling 크기는 (2, 2)이기에 일반적으로는 Feature Map의 크기가 줄어든
- padding="SAME" 옵션을 통해서 Feature Map의 크기가 줄어들지 않고 동일하도록 설정한다
- Flatten layer
tf.keras.layers.Flatten()
- CNN의 데이터 타입을 Fully Connected Neural Network의 형태로 변경하는 레이어이다
- Dense layer
tf.keras.layers.Dense(32, activation='relu')
- 32는 노드의 개수를 나타낸다
- 활성화함수로는 relu 함수를 사용했다
- Output layer
tf.keras.layers.Dense(10, activation='softmax')
- 라벨 개수가 10개 이기 때문에 출력층 노드의 개수도 10개로 지정한다
- 각 라벨에 대한 확률값을 나타내야하기 때문에 활성화함수로 softmax를 사용한다
※ 에러 발생
ValueError: A target array with shape (5000, 10) was passed for an output of shape (None, 14, 14, 10) while using as loss `categorical_crossentropy`. This loss expects targets to have the same shape as the output.
해결방법: Flatten layer을 추가해주자 에러가 해결
발생원인: 모델의 출력 형태와 레이블의 형태가 일치하지 않아서 발생한 것
Why?
Flatten layer 앞까지는 3차원의 형태였다. 그러나 결과는 10개의 라벨 중 하나의 값으로 출력되어야 한다. 따라서 1차원으로 평탄화해주는 작업이 필요하다.
3. 모델 학습
def main():
train_images, test_images, train_labels, test_labels = preprocess()
model = CNN()
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
history = model.fit(train_images, train_labels, epochs=15, batch_size=128,
validation_data=(test_images, test_labels), verbose=2)
print(model.summary)
1) 모델 컴파일하기
모델 구조를 선언하는 과정이다
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
- 옵티마이저는 adam 함수를 사용했다
- 분류 문제이기 때문에 손실함수는 3개 이상의 범주형 데이터에 사용하는 categorical crossentropy를 사용했다
- vs sparse_categorical_crossentropy는 원핫인코딩을 진행하지 않은 경우, 값이 정수 형태인 경우에 사용한다
- 모델 결과는 정확도를 보고 싶어서 accuracy를 적용했다
2) 모델 학습하기
model.fit(train_images, train_labels, epochs=15, batch_size=128,
validation_data=(test_images, test_labels), verbose=2)
- epochs는 전체 데이터의 학습 횟수를 의미한다
- batch_size는 데이터를 몇 개로 나누어서 학습할 것인지 의미한다
- validation_data는 검증 데이터로 사용할 x와 y 데이터를 묶음으로 적용한다
- verbose는 학습 과정을 출력하는 부분인데 기본값은 1이며 2는 진행바는 출력하지 않고 수치 정보들만 출력한다
4. 모델 평가
# 앞 함수에 이어서
loss, test_acc = model.evaluate(test_images, test_labels)
print('\nTest Loss : {:.4f} | Test Accuracy : {}'.format(loss, test_acc))
result = model.predict(test_images)
return history, result
1) 모델 평가하기
model.evaluate(test_images, test_labels)
- evaluate 함수에 test x 데이터와 y 데이터를 넣는다
2) 모델 예측하기
model.predict(test_images)
- 검증 데이터로 사용한 데이터를 넣는 것은 옳지 못하지만 간단한 실습이기에...!
- predict 함수에는 예측할 x 데이터를 넣어준다
- 답이 없는 예측 데이터이기 때문에 y 데이터는 당연히 넣지 않는 것!
반응형
'IT Study > ML & DL' 카테고리의 다른 글
[RNN] RNN, LSTM, GRU 모델 (1) | 2023.12.31 |
---|---|
[CNN] OpenCV 실습 | 이미지 조각 순서변경, 프리윗 필터, 회선처리 (1) | 2023.12.31 |
[DL/NLP] RNN 실습 (with. nsmc data) (1) | 2023.12.23 |
[DL/NLP] Word2Vec - CBOW & Skip-Gram 방식 (1) | 2023.12.23 |