tensorflow - keras 라이브러리와 fashion_mnist 데이터 불러오기
from tensorflow import keras (train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data() # 애초에 4덩이로 구분되어있어서 train_input, train_target, test_input, test_target 변수로 받을 수 있음
train 데이터와 test 데이터 구조확인
print(train_input.shape, train_target.shape) #(60000, 28, 28) (60000,)print(test_input.shape, test_target.shape) #(10000, 28, 28) (10000,)
각 변수에 들어있는 데이터 구조를 확인.
train_input 은 (60000, 28, 28)인데 이건 28*28 이미지 데이터가 60000개 있는것.
(전체 이미지 개수(1차원), 이미지의 세로 픽셀 수(2차원), 이미지의 가로 픽셀 수(3차원))
train_target 은 (60000, )인데 타겟 데이터는 60000개의 라벨
fashion_mnist 데이터 시각화 확인하기
import matplotlib.pyplot as plt fig, axs = plt.subplots(1, 10, figsize=(10,10)) for i in range(10): axs[i].imshow(train_input[i], cmap='gray_r') axs[i].axis('off') plt.show()
fig, axs = plt.subplots(1, 10, figsize=(10,10))
1행 10열의 서브플롯을 생성
fig => 서브플롯(axes)을 포함하는 가장 바깥의 컨테이너. 캔버스(도화지)라고 보면 됨.
axes => 캔버스(도화지)안의 작은 네모칸 하나를 의미
axs => 서브플롯(axes)에 들어갈 데이터
subplot() => 도화지를 나누는 방식
1행 10열의 서브플롯을 생성
fig = Figure(1000 * 1000)
figsize=(10, 10) -> 10인치, 10인치
픽셀로 바꾸면 10인치 X 100dpi = 1000픽셀, 10인치 X 100dpi = 1000픽셀
import numpy as np
print(np.unique(train_target, return_counts=True))
#(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))
np.unique(train_target, return_counts=True)
return_counts = True 는
train_target의 unique한 값을 count해서 돌려줌.
-> 각 요소마다 몇 번 나오는지 알 수 있음
이 데이터에서는
10개의 카테고리가있고
각 카테고리별로 6000개씩 train데이터가 들어있음
train데이터 정규화
train_scaled = train_input / 255.0 train_scaled = train_scaled.reshape(-1, 28*28)
픽셀은 0~255까지 있는데
SGDClassifier에서는 특성마다 범위가 많이 다르면 적용시키기 애매함.
0~1 사이로 정해주는것이 좋음
이런 이유로 0~1사이로 만들어주기위해 255로 나눠줌
또한
SGDClassifier는 2차원 배열은 다룰 수 없으므로
2차원 배열인 각 샘플(28, 28)을 1차원 배열로 맞춰줘야함
reshape()를 이용
reshape(-1, 28*28)
-1 : 알아서 맞춤
28*28 : 샘플데이터의 2,3차원을 1차원으로 합침
-> 그러면 1차원의 샘플개수는 변화없이 60000개!!!
모델만들기
from sklearn.model_selection import cross_validate from sklearn.linear_model import SGDClassifier sc = SGDClassifier(loss = 'log_loss', max_iter = 5, random_state = 42) scores = cross_validate(sc, train_scaled, train_target, n_jobs = -1) print(np.mean(scores['test_score'])) #0.8194166666666666
확률적 경사 하강법(SGD)를 이용
(loss = 'log_loss', max_iter = 5, random_state = 42)
loss = 'log_loss' : 로지스틱 회귀를 의미
max_iter = 5 : 한번 학습할때 최대5번만 데이터 반복
교차 검증(Cross Validation)
-> 데이터를 여러 조각으로 나눠서 여러번 학습/검증하여 평균 성능을 계산
작동흐름(기본 5-fold)
데이터를 5등분
4개는 훈련, 1개는 검증
이걸 총 5번 바꿔가며 훈련/검증
각각의 test_score 저장
혹시 fold설정하고싶으면
from sklearn.model_selection import KFold
cv=KFold(n_splits=7, shuffle=True, random_state=42)
-> 7등분 + 섞기
scores = cross_validate(model, X, y, cv=cv) 으로 사용 가능
(sc, train_scaled, train_target, n_jobs = -1)
sc : 사용할 모델
train_scaled : 입력데이터 (X)
train_target : 정답데이터 (y)
n_jobs = -1 : CPU병렬처리(모든코어사용)
scores = cross_validate(...)
결과는 딕셔너리 형식으로 반환
ex)
scores.keys()
# dict_keys(['fit_time', 'score_time', 'test_score'])
scores['test_score']->5개의 검증 정확도 리스트가 들어있음
print(np.mean(scores['test_score']))
다섯 번 검증한 결과의 평균 정확도 출력
전체 흐름 정리
- train_scaled, train_target으로
- SGDClassifier 모델을 5번 훈련 + 검증하면서
- test_score에 성능 저장
- 평균 정확도를 print로 출력
모델 구조정의
# (케라스의 Dense클래스를 사용해 밀집층만들기) from tensorflow import keras dense = keras.layers.Dense(10, activation = 'softmax', input_shape = (784,))
# (케라스의 Dense클래스를 사용해 밀집층만들기)
Dense(10, activation = 'softmax', input_shape = (784,))
(출력층의 개수, 출력에 적용할 함수, 입력의 크기)
-> 입력의 크기는 784, 출력에 적용할 함수는 softmax, 출력층의 갯수는 10개
********Dense는 무조건 2차원으로 입력을 받아야함. 고로 3차원인 데이터는 평탄화 필요
# 신경망 모델을 Sequential클래스로 만들기
# Sequential 클래스는 밀집층인 danse를 받음.
model = keras.Sequential([dense])
# 신경망 모델을 Sequential클래스로 만들기
# Sequential 클래스는 밀집층인 danse를 받음.
Sequential 모델은 언제 사용???
입력 → 은닉층 → 출력층
레이어가 순서대로만 흘러가는 구조일 때
복잡한 연결(분기, 병합 등)이 없을 때 적합
model = keras.Sequential(dense) # 에러 발생 / 책이 잘못나옴
* 이유
여기서 dense는 레이어 하나인데,
Sequential()은 내부적으로 레이어들을 반복(iterate) 해서 쌓기 때문에,
리스트 형태로 넣어줘야 함.
최신 권장 방식
from tensorflow import keras inputs = keras.Input(shape=(784,)) # 입력 정의 (명시적인 Input) outputs = keras.layers.Dense(10, activation='softmax')(inputs) # Dense는 입력을 받아서 연결 model = keras.Model(inputs=inputs, outputs=outputs)또는,
model = keras.Sequential([ keras.Input(shape=(784,)), # 명시적 입력 keras.layers.Dense(10, activation='softmax') # Dense는 입력만 받음 ])
모델 학습설정
model.compile(loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
keras에서 모델 학습설정은
model.compile() 사용
model.compile(loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
loss = 손실함수
손실함수
이진 분류 -> binary_crossentropy
다중 분류 ->
원핫인코딩 후 사용
categorical_crossentropy
원핫인코딩 안된 데이터에 사용
sparse_categorical_crossentropy
metrics = ['accuracy']
Keras는 metrics에 여러 개의 평가 지표를 넣을 수 있게 설계!!
********항상 리스트 형태로 받음
나와있지않지만 강사님이 많이 쓰신 파라(07-2에서 나옴)
optimizer = 'adam'
optimizer :
- 모델이 오답을 줄이기 위해 가중치를 조금씩 조정하는 방법
- 딥러닝 모델이 **손실(loss)**을 줄이기 위해 가중치를 어떻게 바꿀지 결정하는 알고리즘,
즉 **최적화 함수(Optimizer)**
adam :
- Adaptive Moment Estimation의 줄임말
- 많이 쓰이는 최적화 알고리즘 중 하나
- 학습 속도도 빠르고 성능도 좋아서 대부분의 모델에서 기본값처럼 자주 사용
| 특징 | 설명 |
|---|---|
| 학습률 자동 조절 | 학습률을 각 가중치마다 자동으로 조정 |
| 빠르고 안정적 | 초반에 빠르게 수렴하면서 진동도 적음 |
| 모멘텀 + RMSProp 결합 | 과거 기울기 방향 + 변화량까지 고려 |
실제 모델 학습
model.fit(train_scaled, train_target, epochs = 5)
.fit() : 케라스에서 모델의 학습을 하는 메서드
sklearn과 비슷하나 epochs가 차이남.
model.fit(입력데이터, 정답데이터, epochs = 반복할 횟수)
모델 검증
model.evaluate(val_scaled, val_target)
evaluate() : 케라스에서 모델의 성능을 평가하는 메서드
model.evaluate(입력데이터, 정답데이터)
사이킷런 모델
모델 -> sc = SGDClassifier(loss='log_loss(손실함수)', max_iter = 5(반복횟수))
훈련 -> sc.fit(train_scaled, train_target)
평가 -> sc.score(val_scaled, val_target)
케라스 모델
층생성 -> dense = keras.layers.Dense(10(출력층 개수), activation='softmax'(함수설정), input_shape=(784,)(입력 개수))
모델 -> model = keras.Sequential(dense)
model.compile(loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
훈련 -> model.fit(train_scaled, train_target, epochs = 5(반복횟수))
평가 -> model.evaludte(val_sacled, val_target)
소프트맥스 함수
- 여러 클래스 중 하나를 선택할 수 있도록 출력을 확률처럼 정규화
- 출력값들이 모두 0~1 사이이며, 총합이 1이 되는 확률 벡터
(softmax([2.0, 1.0, 0.1]) -> [0.659, 0.242, 0.099] -> 전체 합이 1 )- 각 클래스에 대해 예측된 확률을 명확히 해석 가능
- 다중 분류 문제의 출력층
- 예측 결과를 확률로 해석할 수 있어 직관적이고 분석이 쉬움
시그모이드 함수
- 초창기 인공신경망에 많이 사용된 활성화 함수
- 입력 값을 0~1 사이의 확률처럼 변환
- 출력이 항상 부드럽고 연속적
- 입력이 매우 크거나 작으면 기울기 소실(Vanishing Gradient) 발생
올바른 출력을 만드는데 신속한 대응이 안됨.- 층이 많을수록 그래디언트가 누적되며 학습이 잘 안 됨
- 은닉층 또는 이진분류의 출력층에 사용
(과거에는 은닉층에서 사용, 현재는 이진 분류의 출력층에서만 주로 사용)
렐루함수
- 입력이 양수일 경우 마치 활성화함수가 없는 것처럼 입력을 통과시키고,
입력이 음수일 경우 0으로 만듦- 계산 빠름, 기울기 소실이 없음 (그래디언트 유지됨)
- 심층 신경망에서 빠른 학습, 효과적인 수렴
- 이미지 처리, CNN, DNN에 기본 활성화 함수처럼 사용됨
- 은닉층에서 매우 널리 사용
심층 신경망(DNN)
입력층(Input layer)과 출력층(Output layer) 사이에
은닉층(Hidden layers)이 2개 이상 존재하는 인공 신경망
[예시 구조]
[입력층]
↓
[은닉층1] ← Dense
↓
[은닉층2] ← Dense
↓
[출력층] ← Softmax
| 항목 | 얕은 신경망 (Shallow NN) | 심층 신경망 (Deep NN) |
|---|---|---|
| 정의 | 은닉층이 1개 이하 | 은닉층이 2개 이상 |
| 구조 예시 | 입력 → 은닉 1개 → 출력 | 입력 → 은닉 1 → 은닉 2 → ... → 출력 |
| 복잡도 | 구조가 단순 | 구조가 복잡 |
| 표현력 | 제한적 | 복잡한 패턴 표현 가능 |
| 학습 난이도 | 쉽고 빠름 | 느리고 튜닝 필요 |
| 적합 문제 | 간단한 문제 | 복잡한 문제 (이미지, 자연어 등) |
| 예시 | 퍼셉트론, 간단한 MLP | CNN, RNN, Transformer 등 |
- 이진분류 : 시그모이드 함수
- 다중분류 : 소프트맥스 함수
- 시그모이드 함수
- 렐루 함수
-> 회귀는 Dense층의 activation 매개변수에 아무런 값을 지정하지 않음.
방법 1
dense1 = keras.layers.Dense(100, activation = 'sigmoid', input_shape = (784, )) dense2 = keras.layers.Dense(10, activation = 'sigmoid')두 번째 층부터는 Keras가 자동으로 입력 shape를 추론 즉, dense1의 출력이 dense2의 입력이 되기 때문에 input_shape가 필요 없음.model = keras.Sequential([dense1, dense2]) model.summary()
케라스는 모델의 summary() 메서드를 호출하면 층에 대한 정보를 얻을 수 있음
파라미터 수 = (입력 노드 수 × 출력 노드 수) + 출력 노드 수
from tensorflow import keras
dense1 = keras.layers.Dense(100, activation='relu', input_shape=(784,), name='hidden_layer_1')
dense2 = keras.layers.Dense(10, activation='softmax', name='output_layer')
model = keras.Sequential([dense1, dense2])
- (None, 100) = (배치 크기, 뉴런 수)
None → 입력 데이터가 한 번에 몇 개 들어올지는 아직 모른다
(fit() 할 때 batch_size = .. 로 설정 가능)
- 100 → 이 레이어에서 나가는 출력 뉴런 수
케라스에서 기본 미니 batch_size = 32
모델이 다양한 크기의 입력을 받을 수 있도록(유연하게 사용하기 위해) None으로 남겨 둠.
fit()할때 사용
1개만 넣어도 되고 (배치=1), 64개 넣어도 되고 (배치=64), 전부 넣어도 됨
방법 2
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()
방법 3
model = keras.Sequential() model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784,), name = 'hidden')) model.add(keras.layers.Dense(10, activation='softmax', name = 'output')) model.summary()
방법 3은
- Dense 클래스의 객체를 따로 변수에 담지않고 바로 add()메서드로 전달할 수 있음.
- 추가되는 층을 한눈에 볼 수 있음
- 실행시 동적으로 층을 선택하여 추가할 수 있음.
Flatten 층
- Flatten 클래스는 배치차원을 제외하고 나머지 입력 차원을 모두 일렬로 펼치는 역할
- Flatten클래스를 층처럼 입력층과 은닉층 사이에 추가하기 때문에 층이라고 부름.
- 위 코드처럼 입력층 바로 뒤에 추가
- Flatten을 사용하면 입력값의 차원을 짐작하기 쉬움.
model = keras.Sequential([ keras.Input(shape=(28, 28)), # ← 입력층 명시 keras.layers.Flatten(), # ← Flatten은 평탄화만 함 # 앞에서 데이터 전처리시 reshape() 필요없음 keras.layers.Dense(100, activation='relu'), keras.layers.Dense(10, activation='softmax') ]) model.summary()
from tensorflow import keras
(train_input, train_target), (test_input, test_tergat) = 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)

옵티마이저 ( Adam 중요 )
- 손실 함수의 값을 최소화(minimize)하기 위해 가중치를 어떻게 업데이트할지 결정하는 알고리즘
| Optimizer | 핵심 아이디어 | 수식 요약 | 대표 특징 |
|---|---|---|---|
| SGD | 순수 경사하강법 | 단순하지만 진동 심하고 느림 | |
| Momentum | 이전 속도(모멘텀)를 이용한 가속 | 진동 줄이고 빠른 수렴 가능 | |
| Nesterov | 모멘텀 + 미리 한 발 앞으로 가서 기울기 확인 | 더 정교한 방향 제어 | |
| Adagrad | 각 가중치별로 학습률 자동 조절 | 희소 데이터에 유리 (텍스트 등) | |
| RMSprop | Adagrad의 단점 보완 (최근 기울기에 가중치) | RNN 계열에 잘 작동 | |
| Adam | Momentum + RMSprop 조합 | 로 평균과 제곱평균 반영 | 대부분의 문제에 기본값처럼 사용됨 |
| Nadam | Adam + Nesterov | 복잡한 결합식 | Adam보다 빠른 수렴 가능 |
SGD (Stochastic Gradient Descent)
- 가장 기본적인 옵티마이저, 간단하고 직관적
- 단순하지만,기울기 방향만 보고 이동하기 때문에
손실이 들쭉날쭉하게 줄어들고 진동이 심할 수 있음- 매 반복마다 일부 데이터(batch)로 손실을 계산해 가중치 갱신
optimizer = keras.optimizers.SGD(learning_rate=0.1) model.compile(optimizer = optimizer, loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
Momentum
- 관성 개념을 사용해 이전 방향으로 더 강하게 이동
- 진동 감소 + 수렴 속도 증가
optimizer = keras.optimizers.SGD(momentum=0.9, nesterov = True) model.compile(optimizer = optimizer, loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
Nesterov (Nesterov Accelerated Gradient, NAG)
- 모멘텀을 적용하기 전에 미리 한 걸음 앞서서 기울기를 계산하는 방식
- 더 정확한 방향 제어
- 수렴이 더 안정적이고 빠를 수 있음
optimizer = keras.optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True)
RMSprop
- Adagrad의 단점(학습률 소멸)을 해결
- 최근 기울기에 지수적으로 가중치를 줘서 더 안정적 (지수평균)
- RNN, LSTM에 매우 적합
- 빠른 수렴
optimizer = keras.optimizers.RMSprop(learning_rate=0.001) model.compile(optimizer = optimizer, loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
Adagrad
- 각 파라미터마다 학습률을 자동 조절
- 자주 바뀌는 가중치는 더 천천히 학습
→ 희소 데이터에 효과적(ex. 텍스트)- 단점: 학습률이 너무 작아져서 금방 멈춰버릴 수 있음
optimizer = keras.optimizers.Adagrad(learning_rate=0.01) model.compile(optimizer = optimizer, loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
Nadam (Nesterov + Adam)
- Adam에 Nesterov 모멘텀을 추가한 방식
- 최신 최적화 기법 중 하나
- 이론적으로 Adam보다 빠르게 수렴할 수도 있음 (실험적으로 사용됨)
optimizer = keras.optimizers.Nadam(learning_rate=0.001) model.compile(optimizer = optimizer, loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
Adam (Adaptive Moment Estimation) 중요
- Momentum + RMSprop 결합
- Momentum (기울기의 평균)
- RMSprop (기울기의 제곱 평균)
- 가장 널리 쓰임, 대부분의 문제에서 잘 동작
optimizer = keras.optimizers.Adam(learning_rate=0.001) model.compile(optimizer = optimizer, loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
| 옵티마이저 | 계산 방식 | 장점 | 대표 용도 |
|---|---|---|---|
| SGD | 순수 경사 하강 | 단순, 느림 | 실험, 비교용 |
| Momentum | + 속도 | 빠른 수렴 | CNN 등 |
| Nesterov | + 더 앞서 보기 | 정교한 이동 | 고급 CNN |
| Adagrad | 개별 학습률 | 희소 데이터에 강함 | 텍스트 |
| RMSprop | 최근 기울기 반영 | 빠름, RNN에 좋음 | 시계열 |
| Adam | Momentum + RMSprop | 👍가장 널리 쓰임 | 거의 모든 문제 |
| Nadam | Adam + Nesterov | 더 빠를 수 있음 | 실험적 최적화 |
model.fit(train_scaled, train_target, epochs = 5)
model.evaluate(val_scaled, val_target)00