Reference · 인쇄용
keras/keras01.py ~ keras13_ddarung.py 전체에서 뽑은 핵심 사실.
객관식 시험 직전 5분에 읽을 문서.
#1. 데이터 → x, y 준비 · (필요하면) train/test 분리 · 스케일링
#2. 모델구성 → Sequential() + model.add(Dense(...))
#3. 컴파일·훈련 → model.compile(loss, optimizer) · model.fit(x, y, epochs, batch_size)
#4. 평가·예측 → model.evaluate(x_test, y_test) · model.predict(x_new)
이 4단계 주석은 keras01.py부터 keras13_ddarung.py까지 한 번도 바뀌지 않는다.
문제에서 "다음 코드의 빈칸"이 나오면 거의 이 순서 안의 한 칸이다.
| 표현 | 뜻 |
|---|---|
Sequential() | 레이어를 일렬로 쌓는 모델. add() 순서 = 데이터가 흐르는 순서 |
Dense(5) | 출력 노드(=유닛) 5개인 완전연결 레이어. units는 출력 크기 |
input_dim=3 | 입력 특성(열) 3개. 첫 레이어에만 지정 |
input_shape=(3,) | input_dim=3과 같은 뜻. 튜플이라 콤마 필수 |
마지막 Dense | 노드 수 = 샘플 하나당 예측할 값의 개수 = y의 열 개수. y가 1차원 (5,)이면 열이 없으니 Dense(1) (회귀에서 활성화 함수 없음 = linear) |
Dense가 실제로 계산하는 식: output = activation(dot(input, kernel) + bias).
activation을 안 주면 선형(a(x)=x) — 이 폴더의 모든 모델이 그렇다.[1]
파라미터 수 = (입력 수 × 노드 수) + 노드 수(bias).
예: Dense(5, input_dim=1) → 1×5+5 = 10개.
| 코드 | shape | 읽는 법 |
|---|---|---|
np.array(5) | () | 0차원(스칼라). 숫자 하나, 축이 없음 |
np.array([1,2,3]) | (3,) | 1차원(벡터). 원소 3개 |
np.array([[1,2,3]]) | (1,3) | 2차원(행렬). 1행 3열 |
np.array([[1,2],[3,4],[5,6]]) | (3,2) | 2차원(행렬). 3행 2열 |
np.array([[[1,2],[3,4]],[[5,6],[7,8]]]) | (2,2,2) | 3차원 텐서. 대괄호 깊이 = 차원 수 |
input_dim은 마지막 숫자와 같아야 한다.n_samples … being the number of rows in a data array X"[6].
같은 것을 instance(인스턴스) · data point(데이터 포인트) · observation(관측치) · 행(row)이라고도 부른다.
통계학의 "표본"과 혼동하지 말 것: 통계에서 표본은 관측값 집합 전체(데이터 100줄 = 표본 1개, 크기 100)를 뜻하지만,
머신러닝에서는 한 줄이 1 샘플이다(데이터 100줄 = 샘플 100개). 케라스·사이킷런 문서와 에러 메시지가 이 뜻으로 쓴다.차원 수는 arr.ndim, shape는 arr.shape로 확인한다.
이름은 0차원 스칼라 / 1차원 벡터 / 2차원 행렬 / 3차원 이상 텐서지만, 엄밀히는 전부 텐서다(TensorFlow의 rank-N tensor).
이 폴더는 Dense만 쓰므로 2차원까지만 등장한다. 3차원은 RNN (샘플, 타임스텝, 특성), 4차원은 컬러 이미지 (샘플, 높이, 너비, 채널)에서 나온다.
x = np.array([[1,2,3,4,5], [6,7,8,9,10]]) # (2, 5) ← 샘플 2개, 특성 5개로 잘못 해석됨
x = x.T # (5, 2) ← 샘플 5개, 특성 2개 ✔
y = np.array([1,2,3,4,5]) # (5,) ← 타깃(정답), 샘플 5개
# x.T == x.transpose()
y가 [1,2,3,4,5](샘플 5개)이므로 x의 행도 5여야 한다. x.T를 빼먹으면 shape 에러.
| 파일 | x.shape | y.shape | 첫 레이어 | 마지막 레이어 |
|---|---|---|---|---|
| mlp1 | (5, 2) | (5,) | input_shape=(2,) | Dense(1) |
| mlp2 | (10, 3) | (10,) | input_shape=(3,) | Dense(1) |
| mlp3 | (10, 3) | (10, 2) | input_shape=(3,) | Dense(2) |
| mlp4 | (10,) | (10, 3) | input_shape=(1,) | Dense(3) |
predict에 넣는 값도 2차원이어야 한다. 특성 3개 모델(input_shape=(3,))에 샘플 하나를 넣는다면:
model.predict(np.array([[10, 31, 211]])) # (1, 3) ← 특성 3개인 샘플 1개 ✔
model.predict(np.array([10, 31, 211])) # (3,) ← 특성 1개인 샘플 3개로 읽힘 ✗
# ValueError: Expected shape (None, 3), but input has incompatible shape (3,)
shape의 첫 숫자가 항상 샘플 수이기 때문이다. 1차원 (3,)을 넣으면 Keras는 마지막에 축을 붙여 (3, 1)로 읽으므로
"샘플 3개, 특성 1개"가 되어 특성 수가 3과 맞지 않는다.
keras01~keras03의 predict(np.array([1,2,3,4,5]))가 통과하는 건 그 모델이 input_dim=1이라
(5,) → (5, 1) = "샘플 5개, 특성 1개"가 우연히 맞아떨어지기 때문이다. 특성이 2개 이상이면 곧바로 에러다.| 인자 | 이 폴더에서의 값 | 의미 |
|---|---|---|
loss='mse' | 전 파일 동일 | Mean Squared Error = 평균제곱오차. 회귀의 기본 손실 |
optimizer='adam' | 전 파일 동일 | Adaptive Moment Estimation. 가중치 갱신 알고리즘 (기본으로 가장 무난) |
epochs | 100~5000 | 전체 데이터를 몇 바퀴 학습할지 |
batch_size | 1, 2, 4 / 생략 | 가중치를 한 번 갱신할 때 쓰는 샘플 수. 생략하면 32[2] |
validation_split=0.2 | keras11~13 | train의 20%를 검증용으로 떼어 학습에 쓰지 않는다 |
batch_size=4 → 1 에폭 = ceil(1062/4) = 266 스텝.하이퍼파라미터(keras06 주석): 노드 개수, 에폭 수, 레이어 깊이, 배치 사이즈. = 사람이 정하는 값. 가중치·bias는 학습으로 정해지므로 하이퍼파라미터가 아니다.
Error(오차) → Squared(제곱) → Mean(평균) 순으로 계산한다.
mse = mean(square(y_true - y_pred)) # Keras 공식 문서의 정의식
# 정답 y = [1, 2, 3], 예측 ŷ = [1.5, 2, 2.0] 일 때
# 1) 오차: -0.5, 0, +1.0
# 2) 제곱: 0.25, 0, 1.0
# 3) 평균: (0.25 + 0 + 1.0) / 3 = 0.4167 ← mse
-0.5와 +1.0이 서로 깎아먹어 오차가 작아 보인다.
(2) 큰 오차에 큰 벌점: 오차가 2배면 벌점은 4배. 크게 틀린 샘플을 먼저 고치게 밀어붙이지만,
반대로 이상치(outlier) 하나가 손실 전체를 지배할 수 있다.| 지표 | 약자 풀이 | 식 | 성격 |
|---|---|---|---|
mse | Mean Squared Error | mean((y-ŷ)²) | 큰 오차에 민감. 회귀 기본 손실 |
mae | Mean Absolute Error | mean(|y-ŷ|) | 이상치에 둔감. 단위가 원본과 같아 해석 쉬움 |
| RMSE | Root Mean Squared Error | sqrt(mse) | mse의 제곱된 단위를 되돌림 |
mse의 약점은 단위가 제곱된다는 것. 집값을 만원 단위로 예측하면 mse의 단위는 "만원²"이라 값이 직관적으로 안 읽힌다.
그래서 학습은 loss='mse', 사람이 볼 지표는 RMSE·MAE로 따로 두는 조합이 흔하다.
RMSE는 문자열 이름이 없어 클래스로 넣는다: model.compile(loss='mse', optimizer='adam', metrics=[keras.metrics.RootMeanSquaredError()]).
# ① 수동 ② 슬라이싱 ③ 사이킷런 (실전)
x_train = np.array( x_train = x[:7] x_train, x_test, y_train, y_test = \
range(1,8)) x_test = x[7:] train_test_split(x, y, train_size=0.8,
shuffle=True, random_state=42)
| 인자 | 기본값 | 효과 |
|---|---|---|
train_size=0.8 | — | train 80% / test 20% |
shuffle | True | 나누기 전에 섞는다[3] |
random_state=42 | None | 섞는 순서 고정 → 재현 가능. 없으면 실행마다 결과가 달라진다[3] |
| 반환 순서 | — | x_train, x_test, y_train, y_test — 순서를 바꿔 받으면 조용히 망가진다 |
왜 나누는가: 훈련에 쓴 데이터로 평가하면 점수가 부풀려진다. evaluate는 반드시 본 적 없는 x_test로.
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train) # train에서 평균·표준편차를 배우고 변환
x_test = scaler.transform(x_test) # test는 변환만 (fit 금지!)
각 열을 (x - 평균) / 표준편차로 바꿔 평균 0, 표준편차 1로 만든다.[4]
시험 포인트 x_test에 fit_transform을 쓰면 테스트 정보가 새어 들어간다(데이터 누수). y는 스케일링하지 않는다.
| 지표 | 코드 | 좋은 값 | 성질 |
|---|---|---|---|
| MSE | mean_squared_error(y_test, y_pred) | 작을수록 | 오차를 제곱 → 큰 오차에 민감. 단위가 y의 제곱 |
| RMSE | np.sqrt(mean_squared_error(...)) | 작을수록 | MSE의 제곱근 → y와 같은 단위 |
| MAE | mean_absolute_error(y_test, y_pred) | 작을수록 | 절댓값 평균 → 이상치에 덜 민감 |
| R² | r2_score(y_test, y_pred) | 1에 가까울수록 | 최대 1, 음수도 가능. 평균만 예측하는 모델 = 0[5] |
R² = 1 - SSE / SST
# SSE = Σ(y_test - y_pred)² 내 모델의 오차합
# SST = Σ(y_test - y_test.mean())² 평균만 예측하는 모델의 오차합
# y_test = [100, 200, 300, 400, 500] (평균 300, SST = 100000) 일 때
# 예측 [110,190,310,390,505] → SSE= 425 → R² = 0.9958 오차 99.6% 줄임
# 예측 [300,300,300,300,300] → SSE= 100000 → R² = 0.0000 기준선과 동급
# 예측 [500,400,300,200,100] → SSE= 400000 → R² = -3.0000 기준선보다 나쁨
MSE와 함께 쓰는 이유: MSE는 단위에 딸려 변해서 값만 보고는 좋은지 알 수 없다.
위 "좋은 모델"을 만원 단위로 재면 mse=85, 원 단위로 재면 mse=85억이지만 R²는 둘 다 0.9958이다.
R²는 무단위라 다른 데이터셋끼리도 비교할 수 있다. 그래서 학습·튜닝은 MSE, 성능 판정은 R²로 나눠 쓴다.RMSE는 사이킷런에 없어서 직접 만든다 — keras12의 세 파일 모두 같은 함수:
def RMSE(y_test, y_pred):
return np.sqrt(mean_squared_error(y_test, y_pred))
사이킷런 지표의 인자 순서는 항상 (y_true, y_pred) = (y_test, y_pred), 즉 실제값이 먼저다.
MSE·MAE는 차이를 제곱·절댓값 처리하므로 뒤집어도 값이 같지만, R²는 첫 인자의 분산을 기준으로 삼아 뒤집으면 다른 값이 나온다 — 에러 없이 조용히 틀린다.
evaluate의 loss = 직접 계산한 MSE.
loss='mse'로 컴파일했다면 아래 두 경로는 같은 값을 준다 — MSE를 따로 구할 필요가 없다.
loss = model.evaluate(x_test, y_test) # ① Keras가 계산
y_pred = model.predict(x_test)
mse = mean_squared_error(y_test, y_pred) # ② 사이킷런으로 계산 (위 표)
# loss == mse (같은 데이터 x_test/y_test + 같은 수식)
조건 두 가지: (1) loss='mae' 등으로 컴파일하면 당연히 MSE가 아니다.
(2) metrics=[...]를 주면 반환값이 스칼라가 아니라 [loss, metric1, ...] 리스트다.fit 로그의 loss는 train MSE, val_loss는 validation MSE로 위 값과 전부 다르다.
본 적 없는 데이터로 잰 evaluate 값만이 신뢰할 수 있는 성능이다.
| 파일 | 불러오기 | x.shape | input_dim |
|---|---|---|---|
| boston | boston_housing.load_data() → (x_train,y_train),(x_test,y_test) | (404, 13) / (102, 13) | 13 |
| california | fetch_california_housing() → .data / .target | (20640, 8) | 8 |
| diabetes | load_diabetes() → .data / .target | (442, 10) | 10 |
| 따릉이 | pd.read_csv(..., index_col=0) | (1328, 9) ※ count 제외 | 9 |
boston_housing은 이미 train/test로 나뉘어 오므로 train_test_split이 필요 없다.
사이킷런 load_*/fetch_*는 x = datasets.data, y = datasets.target으로 꺼내 직접 나눈다.train_csv = pd.read_csv(path + "train.csv", index_col=0) # 0번째 열을 인덱스로 → 특성에서 제외
train_csv = train_csv.dropna() # 결측치 행 삭제 (1459 → 1328)
x = train_csv.drop(["count"], axis=1) # axis=1 = 열 삭제 → (1328, 9)
y = train_csv["count"] # 타깃 열
| 표현 | 의미 |
|---|---|
index_col=0 | 첫 열(id/날짜)을 인덱스로. 안 쓰면 특성이 10개가 되어 input_dim이 틀어진다 |
dropna() | NaN이 있는 행 제거. train은 10열, test는 9열(count 없음) |
drop([...], axis=1) | axis=0=행, axis=1=열 |
train_csv.shape (1328, 10) | x 9열 + y(count) 1열 |
| 함정 | 정답 |
|---|---|
model.evaluate(x_train, y_train) | model.evaluate(x_test, y_test)로. 훈련에 쓴 데이터 점수는 부풀려져 신뢰할 수 없다 |
scaler.fit_transform(x_test) | scaler.transform(x_test)만. fit은 x_train에서 한 번뿐(여기서의 fit은 모델이 아니라 스케일러의 fit). x_test로 fit하면 데이터 누출 |
y가 (10,2)인데 마지막을 Dense(1) | Dense(2). keras01~mlp2가 전부 Dense(1)이라 손이 기억해 버린다. 마지막 노드 수 = 샘플 하나당 예측할 값의 개수(= y의 열 수). 중간 레이어 숫자는 y와 무관 — mlp3_1은 Dense(3) 다음이 Dense(2) |
특성 3개 모델에 predict(np.array([10, 31, 211])) | [[10, 31, 211]]로 2차원. 1차원 (3,)은 "특성 3개인 샘플 1개"가 아니라 (3,1)="샘플 3개, 특성 1개"로 읽혀 ValueError. keras01~03은 input_dim=1이라 1차원이 통과했을 뿐 |
r2_score(y_pred, y_test) | r2_score(y_test, y_pred). 사이킷런은 (y_true, y_pred) = (실제값, 예측값) 순서이고 y_true가 y_test다. MSE·MAE는 뒤집어도 값이 같지만 R²는 조용히 다른 값이 나온다(에러 없음) |
output = activation(dot(input, kernel) + bias), 기본 activation은 linear.fit): "If unspecified, batch_size will default to 32"; validation 데이터는 "selected from the last samples … before shuffling".train_test_split: shuffle 기본 True, random_state는 재현성 제어, test_size/train_size 모두 None이면 test 0.25.StandardScaler: z = (x - u) / s.r2_score: "Best possible score is 1.0 and it can be negative"; 평균만 예측하는 모델은 0.0.sample: "a noun to indicate a single feature vector. Elsewhere a sample is called an instance, data point, or observation. n_samples … being the number of rows in a data array X".