
model.add(Dense(units=10, activation='sigmoid))
# 라이브러리 불러오기
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer # 사이킷런 내장 데이터
# 데이터 가져오기
data = load_breast_cancer()
# 사이킷런 데이터 타입(번치 객체) → 파이썬의 딕셔너리와 동일하게 구성: {key:value}
# 키값만 확인
data.keys()
# data: 입력특성, 문제 데이터
# target: 정답 데이터(인덱스 번호)
# target_names: 정답 데이터의 이름
# feature_names: 입력특성의 이름(컬럼명)
# DESCR: 데이터에 대한 설명
# 정답 데이터 확인
data["target"]
data["target_names"]
# target이 인덱스 번호
# 0: 'malignant', 악성
# 1: 'benign', 양성
# 클래스가 2개인 이진 분류 학습이 필요하다!
# 문제, 정답 데이터 분리
X = data["data"]
y = data["target"]
# 4개의 train, test 분리
# 랜덤→11, 분리→7.5:2.5, 클래스 비율 맞춰주도록 설정
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, stratify=y, random_state=11)
#크기 확인
print('훈련용 문제: ',X_train.shape)
print('훈련용 답: ',y_train.shape)
print('테스트용 문제: ',X_test.shape)
print('테스트용 답: ',y_test.shape)
훈련용 문제: (426, 30)
훈련용 답: (426,)
테스트용 문제: (143, 30)
테스트용 답: (143,)
# 모델 생성을 위한 도구 불러오기
from tensorflow.keras import Sequential # 뼈대
from tensorflow.keras.layers import Input, Dense
# Dense: '빽빽한','밀집된'이라는 의미를 가짐
# 층을 쌓을 때 사용 (중간층과 출력층에서 사용)
# 1. 신경망 모델 구조 설계
# 뼈대
model = Sequential()
# 입력층
# Input(shape = ()): 입력특성의 개수를 설정
model.add(Input(shape = (30,)))
# 중간층, 은닉층
# 학습능력을 설정
# 다층설계 (8,16,32) → Dense 층 3개 쌓기!
model.add(Dense(units = 8, activtion = 'sigmoid'))
model.add(Dense(units = 16, activtion = 'sigmoid'))
model.add(Dense(units = 32, activtion = 'sigmoid'))
# 출력층
# 출력하고자 하는 데이터의 형태를 지정~
# 이진분류 → 한 개의 확률값!
# 한 개 → units=1)
# 확률값 → activation='sigmoid'
model.add(Dense(units = 1, activation = 'sigmoid'))
# 이진분류: 1인지 아닌지에 대해서만 판별
# 1인지에 대한 확률값 1개만 필요
# 임계점(0.5) 이상 → class 1
# 임계점(0.5) 미만의 확률값 → class 0




# 2. 모델 학습 방법 및 평가 방법 설정
model.compile(
optimizer="SGD" # 최적화 알고리즘(경사하강법)
, loss="binary_crossentropy" # 이진분류
, metrics=["accuracy"] # 평가지표 (분류: 정확도)
)
# 3. 모델 학습 및 시각화
h1 = model.fit(X_train, y_train, validation_split=0.2, epochs=100)
# 시각화
plt.figure(figsize=(5,5))
plt.plot(h1.history["loss"], label="loss")
plt.plot(h1.history["val_loss"], label="val_loss")
plt.legend()
plt.show()

model = Sequential()
model.add(Input(shape=(30,)))
model.add(Dense(units=8, activation='sigmoid'))
model.add(Dense(units=16, activation='sigmoid'))
model.add(Dense(units=32, activation='sigmoid'))
model.add(Dense(units=1, activation='sigmoid'))
model.compile(
optimizer="SGD"
, loss="binary_crossentropy"
, metrics=["accuracy"]
)
h1 = model.fit(X_train, y_train, validation_split=0.2, epochs=50)
plt.figure(figsize=(5,5))
plt.plot(h1.history["loss"], label="loss")
plt.plot(h1.history["val_loss"], label="val_loss")
plt.legend()
plt.show()


model.summary()

















Batch_size
일반적으로 PC 메모리의 한계 및 속도 저하 때문에 대부분의 경우 한 번의 epoch에 모든 데이터를 한꺼번에 집어넣기가 힘듦










ReLU를 사용하는 이유
1. sigmoid 대신 ReLU를 사용하는 이유가 뭔가요?
- 가장 먼저 sigmoid, tanh, ReLU, Leaky ReLU 등의 정의와 그래프부터 파악해야 함
- 신경망(뉴럴넷; Neural Networks)에서는 입력된 값을 weight와 bias로 연산하고, 연산 결과를 활성화 함수(activation function)으로 처리
- sigmoid function 은 모든 실수를 0과 1사이의 값으로 변환하는 활성함수 → 각 노드의 결과값은 y=sigmoid(Wx+b)
- 신경망은 학습 과정에서 backpropagation이라는 방법을 이용해 결과값의 오차를 역전파해서 노드의 가중치 w를 수정 → chain rule에 의한 미분식
- 여기서 output은 2번째 은닉층의 결과에 활성함수를 적용한 결과, 즉 sigmoid(z1)
- 2번째 은닉층의 입력값인 hidden2는 1번째 은닉층의 결과에 활성함수를 적용한 결과, 즉 sigmoid(z2)
- 이처럼 sigmoid를 활성함수로 사용하는 신경망은 역전파 학습 시 sigmoid 미분값이 chain rule에 의해 곱해진다:
- 여기서 sigmoid function의 미분값은 (0, 0.25) 범위에 존재
- 따라서 레이어의 층수가 깊어질수록 레이어의 미분값인 d(error)/d(w1)는 매우 작아짐 → sigmoid 함수의 미분값인(0, 0.25) 값이 곱해지니까
- 이로 인해 최종 값의 오차가 앞에 있는 레이어까지 전달되지 않아서 신경망의 학습이 저하되는 vanishing gradient 문제가 발생
- ReLU는 max(0, x)로 음수는 모두 0으로 반환하고 양수는 값을 그대로 반환함
- 따라서 ReLU 함수의 미분은 음수는 모두 0, 양수는 모두 1 → sigmoid 대신 ReLU를 사용하면 chain rule이 적용될 때 레이어의 개수가 늘어나더라도 최종 결과의 오차를 레이어에 그대로 전달할 수 있음
- ReLU는 미분 불가능한데 BP가 어떻게 가능한가요?
- ReLU가 미분 불가능한 지점은 x=0일 때 뿐
- 실제 연산에서 x=0이 되는 경우가 극히 드물기 때문에 이를 무시하고 사용할 수 있다는 것이 일반적인 견해
- ※심화: ReLU의 미분불가능한 영역(x=0)이 backpropagation 연산에 미치는 영향에 대한 연구결과
- Bertoin et al., 2021. Numerical influence of ReLU’(0) on backpropagation @ NeurIPS 2021
- TensorFlow와 PyTorch 와 같은 상용 라이브러리에서는 ReLU'(0) = 0 으로 두고 전체 학습에 영향이 없을 것으로 가정하지만 저자인 Bertoin의 실험결과에 따르면 TF와 Torch의 기본셋팅인 32비트 연산에서는 x=0이 되는 지점(bifurcation zone)이 실제로 발생할 수 있고, 이 때 ReLU'(0)=0 또는 ReLU'(0)=1 으로 할 떄 학습결과에서 유의미한 차이가 발생할 수 있다고 주장함(동일한 학습을 16비트에서 할때는 결과가 더 안 좋아지고, 64비트에서는 문제가 없어진다고 함)
- 결론적으로, x=0인 minima가 거의 발생하지 않으므로 BP 자체는 문제가 없다고 가정할 수 있다. 그러나 이론적인 생각과 달리 컴퓨팅 과정에서 x=0 이 문제를 야기할 수도 있다.
- Leaky-ReLU는 무엇인가요?
- dying ReLU 문제(0보다 작은 입력값의 반환값과 미분값이 모두 0으로 반환되기 때문에 많은 뉴런들이 0으로 비활성화되는 단점) 보완을 위해 ReLU의 음수 구간에서 x보다 작은 값을 취할 수 있도록 정의한 함수
- Leaky-ReLU(x) = MAX(0.01x, x)
- 이외에도 ELU, PReLU 등 다양한 보완 방안이 존재
- 그러나 ReLU를 통해 뉴런이 0으로 변하는 것은 네트워크의 Sparsity를 높이는 장점이기도 함
- 따라서 어떤 게 더 좋냐는 질문보다는 해결해야 할 문제, 신경망의 구조, 그 외 학습 환경 등을 종합적으로 고려해서 가장 적합한 활성함수를 찾는 게 베스트
# ReLU 사용해보기
# ReLU 사용해보기
model = Sequential()
model.add(Input(shape=(30,)))
model.add(Dense(units=8, activation='relu'))
model.add(Dense(units=16, activation='relu'))
model.add(Dense(units=32, activation='relu'))
model.add(Dense(units=1, activation='sigmoid'))
model.compile(
optimizer="SGD" # 최적화 알고리즘(경사하강법)
, loss="binary_crossentropy" # 이진분류 → 분류: crossentropy
, metrics=["accuracy"] # 평가지표 (분류: 정확도)
)
h1 = model.fit(X_train, y_train, validation_split=0.2, epochs=20)
plt.figure(figsize=(5,5))
plt.plot(h1.history["loss"], label="loss")
plt.plot(h1.history["val_loss"], label="val_loss")
plt.legend()
plt.show()

→ 시그모이드를 사용했을 때보다 훨씬 더 적은 epochs로 좋은 결과를 낼 수 있음
y=x), 이진분류: sigmoid(선형모델이 출력하는 연속적인 값(-∞ ~ ∞)을 sigmoid를 통해 0~1 사이 값으로 변환(확률값)from tensorflow.keras.datasets import mnist
# 훈련용 데이터, 테스트용 데이터 구분해서 저장해 둠 → 변수에 담아주기
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 크기 확인
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
# 훈련용 데이터 6만장
# 평가용 데이터 1만장



(28, 28) → (28, 28, 1)에서 1을 생략한 것X_train[0].shape
# 28, 28 → 가로 28개, 세로 28개의 픽셀로 구성되어 있다!
# 픽셀: 사진의 정보를 가지는 최소 단위(이미지를 구성하는 작은 사각형 1개)
# 흑백 이미지 (28, 28, 1) → 얼마나 검은색인지, 흰색인지에 대한 정보를 담고 있음
# 0 (검은색), 255 (흰색)
X_train[0]
# 이미지 데이터는 넘파이 배열 형태로 출력이 된다
# 손글씨 데이터 그래프에 출력
plt.imshow(X_train[0])
# 왜 노란색으로 출력됨? cmap 파라미터 default 값이 'yellow'라서

plt.imshow(X_train[0], cmap='gray')

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Input, Dense, Flatten
# Flatten: 2차원의 입력 데이터를 1차원의 선형 모델이 학습할 수 있도록 해 주는 함수
# 2차원의 사진 데이터를 1차원으로 표현해 주기 위한 클래스
# 현재 우리 데이터 세트는 2차원: (28, 28) → 1차원의 선형 모델을 돌리기 위함
# 1. 모델 설계
# 뼈대
model = Sequential()
# 입력층
model.add(Input(shape=(28, 28))) # 28, 28의 2차원 이미지 데이터이다.
# 중간층
model.add(Flatten()) # 2차원의 사진 데이터를 1차원으로 변경(선형 모델 학습을 위함)
model.add(Dense(units=16, activation='relu'))
model.add(Dense(units=8, activation='relu'))
model.add(Dense(units=16, activation='relu'))
# 출력층 → 출력하고자 하는 데이터의 형태를 설계 → 다중분류
model.add(Dense(units=10, activation='softmax'))
# 분류는 확률값을 출력해야 함
# 다중분류 활성화함수: softmax
# 다중분류는 클래스 개수만큼의 units을 필요로 함
# 클래스 개수만큼의 결과 여러 개를 종합하여 총합이 1이 되게 만들어주는 함수: softmax

| 문제 유형 | 출력층 Unit 수 | 활성화 함수 | 설명 |
|---|---|---|---|
| 회귀 문제 | 1개 | 없음 (또는 linear) | - 항등함수: y = x- 선형 모델이 예측한 값을 그대로 출력 - activation을 지정하지 않으면 기본값- mean_squared_error 손실함수와 함께 사용 |
| 이진 분류 | 1개 | sigmoid | - 출력값은 0~1 사이 - 특정 클래스일 확률로 해석 가능 - 보통 binary_crossentropy 손실함수와 함께 사용 |
| 다중 분류 | 클래스 개수만큼 | softmax | - 각 클래스에 대한 확률값 출력 - 전체 합이 1이 되도록 정규화 - categorical_crossentropy 또는 sparse_categorical_crossentropy 손실함수와 함께 사용 |
# 2. 모델 학습 방법 및 평가 방법 설정
model.compile(
optimizer='SGD'
, loss='sparse_categorical_crossentropy' # y값에 대해 원핫 인코딩 자동 진행
, metrics=['accuracy']
)
# y는 하나의 숫자인데 output은 0~9 확률이 담긴 2차원 데이터
# compile에서 sparse_categorical_crossentropy 사용하면 원핫 인코딩을 해 줘서 오류 없이 학습 가능

# 3. 모델 학습 및 시각화
h1 = model.fit(X_train, y_train, validation_split=0.2, epochs=20)
plt.figure(figsize=(5,3))
plt.plot(h1.history["loss"], label="loss")
plt.plot(h1.history["val_loss"], label="val_loss")
plt.legend()
plt.show()






from tensorflow.keras.optimizers import SGD
model = Sequential()
model.add(Input(shape=(28, 28)))
model.add(Flatten())
model.add(Dense(units=16, activation='relu'))
model.add(Dense(units=8, activation='relu'))
model.add(Dense(units=16, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
model.compile(
optimizer= SGD(learning_rate=0.001)
, loss='sparse_categorical_crossentropy' # 다중분류: categorical_crossentropy
, metrics=['accuracy']
)

h1 = model.fit(X_train, y_train, validation_split=0.2, epochs=20)
plt.figure(figsize=(5,3))
plt.plot(h1.history["loss"], label="loss")
plt.plot(h1.history["val_loss"], label="val_loss")
plt.legend()
plt.show()
