: 생물학적 뉴런에서 영감을 받아 만든 머신러닝 알고리즘
: 구글이 만든 딥러닝 라이브러리로 매우 인기가 높음
: 가장 간단한 인공 신경망의 층
: 정수값을 배열에서 해당 정수 위치의 원소만 1이고 나머지는 모두 0으로 변환
.load_data()함수는 훈련/테스트 세트 나눠서 데이터 불러옴from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
print(train_input.shape, train_target.shape)
print(test_input.shape, test_target.shape)
->
(60000, 28, 28) (60000,)
(10000, 28, 28) (10000,)

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()

print([train_target[i] for i in range(10)])
->
[9, 0, 0, 3, 0, 2, 7, 2, 5, 5]
*참고, 패션MNIST에 포함된 10개의 레이블

np.unique()로 레이블마다 몇 개씩 들어있는지 확인# 0-9 정수로 이루어짐
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]))
SGDClassifier → 표준화 전처리 필요# 표준화 전처리 차원에서 0~ 255 사이의 픽셀값을 모두 255로 나눔
train_scaled = train_input / 255.0 # 정석은 아님, 간편해서 널리 사용
train_scaled = train_scaled.reshape(-1, 28*28) # SGD 쓰기 위해 각 샘플 1차원 배열로 변환
print(train_scaled.shape) # (28, 28) -> 784
->
(60000, 784)
cross_validate로 훈련 & 평가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.8196000000000001
: 2개 이상의 층을 포함한 신경망
: 이미지 분류 모델의 은닉층에 많이 사용하는 활성화 함수
: 신경망의 가중치와 절편을 학습하기 위한 알고리즘 또는 방법을 의미
from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
# .load_data() 로 데이터 준비, 알아서 훈련/테스트 세트 나눠서 반환
from sklearn.model_selection import train_test_split
train_scaled = train_input / 255.0 # 전처리
train_scaled = train_scaled.reshape(-1, 28*28) # SGD쓰기 위해 1차원으로 표현
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_scaled, train_target, test_size=0.2, random_state=42)
# 검증세트 수동으로 덜어내기, 딥러닝은 교차 검증 잘 안함
: 입력층과 출력층 사이에 있는 모든 층(=밀집층)

*활성화 함수
시그모이드 - 이진 분류 모델의 마지막 활성화 함수
소프트맥스 - 다중 분류 모델의 마지막 활성화 함수
ReLU - 기본적으로 은닉층에 사용하는 활성화 함수
+) 회귀를 위한 신경망의 출력층에서는 어떤 활성화 함수를 사용하나?
Seauential()로 전달해서 만듦activation(활성함수)은 시그모이드로 설정# 층 2개로!
dense1 = keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)) # 첫번째 층엔 input_shape 해줌, 유닛 100개인 은닉층 만듦
dense2 = keras.layers.Dense(10, activation='softmax') # 유닛 10개인 출력층
# 만든 층 2개를 쌓아서 '심층 신경망' 모델 만들기, 순서대로 넣으면 됨
model = keras.Sequential([dense1, dense2])
# 모델에 대한 정보 확인
model.summary()

.summary(): 모델에 대한 정보 확인 가능Dense()클래스를 Sequential()안에서 만듦# name으로 층 이름 설정 가능(영문만)
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()

-> 층이 많아지면 한 문장이 너무 길어진다는 단점 존재
.add()로 원하는 만큼 층 추가하는 방법add()속에서 if문 등을 활용해서 조건에 따라 층을 추가 가능model = keras.Sequential()
model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)))
model.add(keras.layers.Dense(10, activation='softmax'))
model.summary()

.compile()로 설정하고 .fit()으로 훈련되는 과정은 동일model.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy']) #설정해줌
model.fit(train_scaled, train_target, epochs=5) # 훈련, 층 추가하면서 성능 높아짐
*은닉층에 활성화 함수 적용하는 이유
: 2개의 선형 계산식이 연속된 형태로 있으면 중간 미지수가 사라지듯이, 신경망에서도 연속으로 쌓는 층이 선형적 계산으로만 구성되면 중간에 낀 은닉층들의 존재 의미가 사라짐

그래서 은닉층의 결과값을 비선형적으로 만들어주는 계산 필요

[시그모이드 함수의 단점]
: 출력값(z)이 너무 크거나 작을 땐 함수값의 차이가 너무 작음
→ 변화에 빠르게 대응하지 못함 → 층을 깊게 쌓기 힘듦


Flatten(): 입력데이터를 1차원으로 표현해주는 유틸리티 층(편의를 위한 층)model = keras.Sequential()
# 입력 데이터 1차원으로 펼치는 층
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()

→ 모델의 입력값 784개의 1차원 배열이라는 것을 알 수 있음
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
train_scaled = train_input / 255.0
# train_scaled = train_scaled.reshape(1-,28*28) 빠짐, flatten층 생겨서
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)
[옵티마이저란?]
딥러닝 학습시 최대한 틀리지 않는 방향으로 학습해야 한다,
얼마나 틀리는지(loss)를 알게 하는 함수가 loss function(손실함수)이다.
loss function 의 최솟값을 찾는 것을 학습 목표로 한다.
최소값을 찾아가는 것 최적화 = Optimization
이를 수행하는 알고리즘이 최적화 알고리즘 = Optimizer 이다.
*참고- 옵티마이저 종류

optimizer로 지정 가능, 문자열이나 객체로 만들어 놓고 호출 가능model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 기본적인 확률적 경사하강법
sgd = keras.optimizers.SGD()
# 위 처럼 따로 옵티마이저 객체 만들기(학습률 등 세부설정 가능해짐)
model.compile(optimizer=sgd, loss='sparse_categorical_crossentropy', metrics=['accuracy'])

→ 경사를 처음엔 많이 내려가다가 가까워질수록 점점 조금씩 내려가는게 더 효율적임, 이렇게 점차 학습률이 변화하는 것이 '적응적 학습률'
sgd = keras.optimizers.SGD(momentum=0.9, nesterov=True) # '네스테로프 모멘텀' 하강법
adagrad = keras.optimizers.Adagrad() #'아다그라드' 하강법(적응적 학습률)
model.compile(optimizer=adagrad, loss='sparse_categorical_crossentropy', metrics=['accuracy'])
rmsprop = keras.optimizers.RMSprop() # 'RMSprop' 하강법 (적응적 학습률)
model.compile(optimizer=rmsprop, loss='sparse_categorical_crossentropy', metrics=['accuracy'])
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.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_scaled, train_target, epochs=5)
*참고 - 앙상블 알고리즘 🆚 심층 신경망

: 은닉층에 있는 뉴런의 출력을 랜덤하게 꺼서 과대적합을 막는 기법
: 케라스 모델을 훈련하는 도중에 어떤 작업을 수행할 수 있도록 도와주는 도구
: 검증 점수가 더 이상 감소하지 않고 상승하여 과대적합이 일어나면 훈련을 계속 진행하지 않고 멈추는 기법
from tensorflow import keras
from sklearn.model_selection import train_test_split
(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)
# 모델 함수화
def model_fn(a_layer=None):
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28, 28)))
model.add(keras.layers.Dense(100, activation='relu'))
if a_layer: # 은닉층 뒤에 원하는 층 더 추가 가능하게 설정
model.add(a_layer)
model.add(keras.layers.Dense(10, activation='softmax'))
# 마지막은 전과 동일한 출력층
return model
model = model_fn()
model.summary()

.fit()메소드는 History 객체를 반환함# 똑같이 fit하고 그 결과를 history에 담아봄
model.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(train_scaled, train_target, epochs=5, verbose=0)
# 결과 담긴 history 객체에서 key 값 꺼내보면 => 손실 & 정확도
print(history.history.keys())
-> dict_keys(['accuracy', 'loss'])
import matplotlib.pyplot as plt
plt.plot(history.history['loss']) # 인덱스 0부터, loss값까지
plt.xlabel('epoch') # 에포크가 0부터 시작하니까 인덱스와 같음
plt.ylabel('loss')
plt.show()

plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

verbose: 훈련 과정 출력 조절, 기본값은 1 -> 에포크마다 진행 막대와 함께 손실 등의 지표 출력 / 2로 바꾸면 진행 막대 빼고 출력 / 0이면 훈련과정 안나타냄model = model_fn() #기본 모델
model.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(train_scaled, train_target, epochs=20, verbose=0)
# 에포크 20으로 늘려서
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

검증세트의 손실도 확인해야 함

validation_data: fit의 매개변수, 검증세트의 결과도 반환하도록 설정 가능
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target))
# 검증세트 결과도 반환해줘
print(history.history.keys()) # key 하나 더 추가됨
-> dict_keys(['accuracy', 'loss', 'val_accuracy', 'val_loss'])
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

-> 훈련세트에만 너무 잘맞는 과대적합이였음을 확인 가능함
신경망 모델에만 있는 규제 방법으로 딥러닝의 아버지 Geoffrey Hinton이 소개하심
층에 있는 유닛을 다 훈련X, 일부를 랜덤하게 off해서 훈련 성능 낮춤
ex) 1st 샘플에선 두번째 유닛 계산 안 하고, 2nd 샘플에선 첫번째 유닛 계산 안 하고,•••

그림 출처
Dropout(): 드롭아웃 기능을 제공하는 클래스. 얼마나 drop할지 비율을 정해야 함
(*마치 케라스 층처럼 사용되지만, 학습되는 모델 파라미터는 없음)
# 드롭아웃으로 랜덤하게 유닛들 off하면서 훈련 = 훈련세트 성능 규제
model = model_fn(keras.layers.Dropout(0.3)) # 층처럼 쌓음
model.summary()

# 드롬아웃 처리한 걸로 다시 훈련
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

-> 최적의 에포크 10정도 되어 보임
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
history = model.fit(train_scaled, train_target, epochs=10, verbose=0,
validation_data=(val_scaled, val_target))
.save_weights(): 훈련된 모델의 가중치를 저장하는 메소드.save(): 훈련된 모델의 구조와 가중치를 통째로 저장하는 메소드model.save('model-whole.keras') # 가중치랑 모델 구조까지 다 저장
model.save_weights('model.weights.h5') # 모델의 가중치만 저장
!ls -al model* # 잘 만들어졌나 셀 명령으로 확인

.load_weights(): 이전에 저장했던 모델의 가중치를 적재하는(불러오는) 메소드.load_model(): 이전에 저장했던 모델 전체를 불러오는 메소드model = model_fn(keras.layers.Dropout(0.3)) # 새 모델 만들고
model.load_weights('model.weights.h5') # 만든 가중치 반영
evaluate() 써도 되지만, 그렇게 계산하려면 복원한 모델에 또 다시 compile()을 실행해야 함. 여기에선 그냥 새로운 데이터에 대해 정확도만 계산하면 되는 상황이라고 가정
[계산 방법]
: 예측 클래스 직접 구하고 타겟이랑 비교해서 맞춘 비율로 계산
.predict(): 샘플마다 각 클래스일 확률을 반환해줌(사이킷런 predict_proba처럼).argmax(): predict가 출력한 확률들 중에 가장 큰 값을 뽑기 위해 넘파이 활용axis=-1: 배열의 마지막 차원(여기서는 axis=1)을 따라 argmax를 수행val_labels와 val_target을 비교해서, 일치하는 비율이 곧 검증세트의 정확도가 됨import numpy as np # 넘파이의 argmax 활용
val_labels = np.argmax(model.predict(val_scaled), axis=-1)
# 모델이 구한 예측 클래스와 실제 타겟값 비교해서 맞춘 비율 알아봄
print(np.mean(val_labels == val_target))
이 경우는 모델의 구조와 옵티마이저까지 모두 그대로 복원했기 때문에 바로 evaluate()를 사용할 수 있음
.load_model(): 이전에 저장했던 모델 전체를 불러오는 메소드# 모델 전체 저장했던 걸로 해봄
model = keras.models.load_model('model-whole.keras')
model.evaluate(val_scaled, val_target)
keras.callbacks 패키지 아래에 다양한 클래스 존재ModelCheckpoint(): 가장 자주 사용되는 콜백, 에포크마다 모델을 저장save_best_only=True: 손실이 가장 낮은 모델만 저장하도록 하는 설정model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-model.keras',
save_best_only=True)
model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target),
callbacks=[checkpoint_cb])


최적의 모델 찾을 때 에포크를 무작정 높게 설정해놓고 찾으면, 불필요하게 오랫동안 훈련을 계속함
EarlyStopping(): 과대적합이 시작되면 훈련을 알아서 조기종료 해주는 콜백
patience: 검증세트 성능이 좋아지지 않더라도 참고 기다릴 에포크 횟수 설정
restore_best_weights=True: 훈련동안 가장 손실 낮았던 최적 가중치로 돌리는 설정

fit()에서 epochs를 크게 설정해도 ㄱㅊ
.stopped_epoch: 몇 번째 에포크에서 조기종료 했는지 저장되어 있는 속성
→ patience 설정했던 것과 같이 생각해보면 최상의 에포크가 언제인지 나옴
