04단계 · 2유형 — 예측과 제출
RMSLE — 로그를 씌워 학습하기
RMSLE 가 RMSE 와 무엇이 다른지 알고, `log1p`/`expm1` 변환 전후의 검증 점수를 비교한다.
먼저 알아볼게요
코드로 이동 ↓RMSLE 는 예측과 정답에 각각 log(1+x) 를 씌운 뒤 RMSE 를 잰다. 로그 차이는 (1 + 예측값) / (1 + 정답값) 의 로그이므로, 원래 값의 차이보다 상대적인 차이를 살펴보는 데 쓰인다.
대여량처럼 0 부터 수백까지 퍼진 값에서, 10 을 20 으로 틀린 것과 200 을 210 으로 틀린 것은 다르게 취급되어야 한다. RMSE 는 둘 다 10 이지만 RMSLE 는 앞쪽을 훨씬 크게 벌한다.
np.log1p(x) 는 log(1 + x) 라 수학적으로는 x > -1 에서 정의된다. 다만 scikit-learn 의 RMSLE 는 음수 입력 자체를 거절한다 — 예측이 음수로 나오면 계산이 오류로 멈춘다. 그래서 np.clip(pred, 0, None) 으로 음수를 0 으로 올린다.
자주 쓰는 방법 하나는 정답에 로그를 씌워 학습하는 것이다. np.log1p(y) 로 학습하고, 예측한 뒤 np.expm1(...) 로 되돌린다(expm1 이 log1p 의 반대다). 늘 좋아진다는 보장은 없다 — 후보일 뿐이고, 좋아졌는지는 검증 데이터로 직접 재서 확인해야 한다. 아래 과제가 그 확인이다.
이번 데이터에서 로그 학습은 RMSLE 는 낮추고 RMSE 는 조금 높인다. 다른 데이터에서는 결과가 달라질 수 있다. 어떤 지표로 채점되는지 먼저 읽고 맞춰야 한다는 뜻이다.
마지막으로 평가 데이터까지 예측해 제출 파일을 만든다. get_dummies 는 학습과 평가에서 만들어지는 열이 다를 수 있으므로 reindex(columns=X.columns, fill_value=0) 으로 열 구성을 학습 쪽에 맞춘다.
import numpy as np
from sklearn.metrics import root_mean_squared_error, root_mean_squared_log_error
y = [10, 200]
p = [20, 210]
print(round(root_mean_squared_error(y, p), 4))
print(round(root_mean_squared_log_error(y, p), 4))
print(round(float(np.expm1(np.log1p(7))), 4))RMSLE 는 log1p 로 변환한 값 사이의 오차다. log1p 학습은 시도해 볼 후보이지 보장이 아니고, 재서 확인한다. 음수 예측은 반드시 막는다.
앞 수업과 같이 나눈 뒤 ① 그냥 학습한 모델의 RMSLE ② log1p 로 학습하고 expm1 로 되돌린 모델의 RMSLE ③ 두 모델의 RMSE 를 차례로(모두 소수 4) 출력하세요. 예측은 둘 다 np.clip(..., 0, None) 으로 음수를 막습니다. 마지막으로 ④ 학습 데이터 전체를 log1p 로 학습해 bike_test.csv 를 예측하고 result.csv 로 저장한 뒤 되읽은 shape 를 출력하세요.
제공 파일 2개 · 내용 보기
파일은 준비되어 있어요. 코드에서 이름으로 불러오세요.
기온,습도,풍속,요일,공휴일,시간대,대여량 22.8,47,1.9,월,0,3,3 20.6,73,3.5,목,0,3,2 20.0,88,1.7,월,0,13,17 4.5,91,3.0,목,0,22,2 16.0,32,2.9,일,0,8,13 0.3,59,0.6,금,1,22,3 19.5,70,2.6,일,0,17,23 10.4,41,2.5,일,0,9,22 18.6,92,2.1,금,0,21,8 18.0,44,2.9,목,0,22,15 19.2,41,3.4,금,0,9,31 26.7,41,4.0,화,0,0,6 19.0,89,2.0,목,0,14,10 11.3,36,3.6,금,1,16,6 12.7,42,2.4,일,0,18,19 11.0,84,2.8,월,1,14,2 16.2,70,2.5,수,0,4,1 22.2,86,1.1,수,0,4,3 12.1,77,4.6,목,0,10,3 11.4,85,2.1,토,0,3,2 29.8,44,-0.2,토,0,20,25 13.3,72,2.0,수,0,13,6 15.7,78,5.3,토,0,13,7 13.3,57,2.7,금,0,16,10 31.7,91,2.1,일,0,19,44 17.7,39,1.9,금,0,16,9 5.9,59,2.7,수,1,3,1 16.2,65,2.1,월,0,6,14 3.7,77,2.1,목,0,11,5 18.5,56,2.5,수,1,6,5 14.8,87,1.8,금,0,21,5 21.4,78,2.2,토,0,16,12 26.8,37,3.4,수,0,18,69 24.3,40,2.5,화,0,5,2 17.2,53,4.2,금,0,1,3 11.2,66,2.0,금,0,2,1 23.0,31,1.7,토,0,20,13 10.3,84,4.4,토,1,12,5 7.2,43,3.2,화,0,22,9 26.4,57,3.8,월,0,4,3 21.2,74,1.5,토,0,0,2 6.4,86,1.7,금,0,19,17 17.2,53,3.5,일,0,6,13 12.7,44,1.5,목,1,11,9 16.0,62,3.5,화,0,2,2 23.1,87,1.3,목,0,5,3 22.8,40,2.0,목,0,15,14 16.6,51,4.3,월,0,4,4 38.0,46,3.8,일,0,22,44 21.7,47,0.7,금,0,11,11 23.7,36,4.1,목,0,4,3 15.5,46,2.5,화,0,4,2 6.9,32,2.2,금,0,12,8 16.5,50,3.3,목,0,9,28 9.4,80,2.3,수,0,15,6 20.2,83,3.2,일,0,8,21 11.4,60,1.2,화,0,21,12 20.4,91,2.9,금,0,14,5 9.2,77,2.2,목,0,21,5 11.6,92,2.5,화,0,5,1 21.0,89,2.5,토,0,4,2 8.3,95,1.6,월,0,11,4 10.7,63,2.0,월,0,11,5 26.5,30,3.0,목,0,23,8 25.5,74,4.0,화,0,21,14 1.7,39,2.1,일,0,18,13 30.7,77,2.4,수,0,20,13 19.4,72,1.8,목,0,17,24 21.2,62,2.8,수,1,13,12 4.5,44,2.9,토,0,1,2 15.5,35,2.5,월,0,10,6 29.3,45,1.7,금,0,1,4 31.0,82,3.2,목,0,22,13 16.1,46,3.6,화,0,23,6 18.8,76,2.1,금,0,10,13 22.9,90,3.0,토,1,2,1 21.0,44,3.1,목,0,0,3 15.6,30,0.6,목,1,6,9 28.6,46,2.6,일,0,19,63 18.4,42,1.9,수,0,10,9 14.7,39,1.4,월,0,20,6 15.8,32,2.4,토,0,15,8 13.7,49,2.0,금,0,11,6 20.2,93,1.7,월,0,0,1 29.5,46,2.2,수,1,8,28 9.7,57,2.4,수,0,13,4 7.8,73,3.6,토,0,19,15 28.6,53,2.2,금,0,16,20 17.0,44,3.7,토,0,15,13 26.7,91,3.2,토,1,15,7 3.3,87,2.5,화,1,18,5 4.8,87,3.8,토,0,13,6 10.0,56,3.7,수,0,10,10 19.3,88,-0.6,일,0,23,5 21.0,70,3.4,화,1,19,9 26.8,48,3.1,일,0,8,21 11.8,67,3.8,화,0,3,2 18.5,59,3.9,토,0,8,18 -5.2,54,2.1,토,0,17,6 17.2,93,2.2,금,0,6,6 12.3,85,2.3,수,0,18,15 8.0,65,2.8,목,0,3,4 11.0,91,0.8,일,0,3,1 12.5,87,3.7,토,1,23,6 23.2,63,2.6,월,0,14,9 9.7,53,3.1,수,1,21,9 17.5,40,2.0,화,0,23,15 24.8,34,3.4,수,0,2,3 0.5,31,4.0,토,0,21,9 17.5,58,0.5,토,0,5,1 17.8,76,2.5,금,0,13,5 14.5,78,1.7,화,0,0,1 16.8,31,1.8,금,0,3,4 26.1,39,0.5,토,0,21,32 14.7,84,3.3,금,0,17,19 33.6,88,1.6,금,0,16,33 16.4,57,2.9,월,0,6,14 18.3,94,2.2,월,0,5,2 16.4,43,3.4,화,0,16,8 18.1,61,3.6,수,1,3,2 16.9,64,3.8,수,0,12,7 29.7,42,4.3,화,0,20,10 15.0,92,4.9,금,1,12,6 25.2,47,3.5,목,1,3,3 13.8,51,2.3,목,0,10,10 11.5,40,3.1,목,1,19,20 29.0,66,2.9,금,0,19,25 12.6,31,2.4,월,0,3,2 26.9,75,2.7,일,0,8,40 24.3,41,2.2,화,1,22,13 7.3,93,3.0,목,0,18,10 26.5,63,2.4,수,1,10,16 25.5,79,2.1,일,1,21,11 20.3,89,2.8,일,0,12,5 9.0,74,3.6,수,0,8,10 20.1,88,1.7,토,0,22,6 22.4,48,2.7,토,0,2,2 22.7,52,3.1,토,0,6,10 17.9,92,2.9,일,1,5,1 16.2,78,2.2,수,0,2,1 23.4,40,2.9,일,0,13,17 25.0,53,1.8,금,0,17,28 24.7,31,0.1,금,0,7,60 16.6,68,0.7,월,1,21,5 14.5,90,2.4,일,0,7,22 7.7,84,3.9,수,0,19,23 17.3,59,-0.2,화,0,13,10 11.9,38,3.6,금,0,14,9 7.8,49,4.0,목,0,7,9 4.5,46,2.5,금,0,19,16 23.0,58,1.9,금,1,1,3 26.8,85,2.4,토,0,12,14 21.9,91,4.3,화,0,5,2 16.0,95,3.8,목,0,14,9 13.6,49,1.9,토,0,3,3 16.7,48,2.2,목,0,5,1 19.0,31,3.3,금,0,12,13 27.3,37,2.1,일,0,4,4 37.6,90,3.6,목,0,14,19 18.0,91,1.9,일,0,21,10
기온,습도,풍속,요일,공휴일,시간대 3.2,40,3.4,월,0,14 24.7,75,2.6,토,1,15 17.4,54,3.1,목,0,4 13.4,64,0.9,토,0,5 21.3,37,3.0,화,0,17 3.5,76,2.0,화,0,5 6.0,61,2.6,화,0,9 8.5,33,1.7,토,0,11 16.8,81,3.2,수,0,20 7.1,69,4.8,목,0,1 20.8,63,2.2,토,1,17 6.4,44,1.9,금,0,19 10.5,34,2.6,일,0,1 7.1,46,2.7,토,0,14 17.9,90,4.3,일,0,22 12.1,66,0.2,일,0,5 19.3,33,2.9,화,0,23 20.0,67,3.0,일,0,3 17.5,43,1.9,금,0,23 19.4,33,1.8,화,0,7 22.6,91,2.8,목,0,21 7.1,79,2.7,금,0,0 5.9,74,2.8,금,0,23 19.3,50,1.5,월,0,19 29.4,46,3.0,수,1,21 14.9,88,3.3,토,0,11 2.4,62,3.3,일,0,19 14.1,54,4.9,일,0,3 14.6,36,0.4,목,0,11 10.2,53,3.0,화,0,14 15.3,69,3.0,월,0,12 23.2,95,3.1,화,0,10 14.9,53,2.5,일,0,4 29.1,69,0.5,목,0,20 13.4,80,4.7,토,0,13 13.5,64,1.0,목,0,20 0.6,36,2.4,토,0,5 15.6,69,1.6,일,0,15 2.0,81,3.0,목,0,1 19.1,55,4.2,토,0,2
들여쓰기: Tab · 편집기에서 나가기: Esc 다음 Tab
실행 결과
실행하면 코드가 출력한 내용이 여기에 나타나요.
막혔을 때
풀이와 비교하기
내 코드와 한 줄씩 비교해 보세요. 풀이를 보는 것만으로 완료되지는 않아요.
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import root_mean_squared_error, root_mean_squared_log_error
from sklearn.model_selection import train_test_split
train = pd.read_csv("bike_train.csv")
X = pd.get_dummies(train.drop(columns=["대여량"]), columns=["요일"])
y = train["대여량"]
x_tr, x_va, y_tr, y_va = train_test_split(X, y, test_size=0.2, random_state=42)
plain = RandomForestRegressor(n_estimators=100, random_state=42).fit(x_tr, y_tr)
p1 = np.clip(plain.predict(x_va), 0, None)
logged = RandomForestRegressor(n_estimators=100, random_state=42).fit(x_tr, np.log1p(y_tr))
p2 = np.clip(np.expm1(logged.predict(x_va)), 0, None)
print(round(root_mean_squared_log_error(y_va, p1), 4))
print(round(root_mean_squared_log_error(y_va, p2), 4))
print(round(root_mean_squared_error(y_va, p1), 4))
print(round(root_mean_squared_error(y_va, p2), 4))
final = RandomForestRegressor(n_estimators=100, random_state=42).fit(X, np.log1p(y))
X_test = pd.get_dummies(pd.read_csv("bike_test.csv"), columns=["요일"]).reindex(
columns=X.columns, fill_value=0
)
pred = np.clip(np.expm1(final.predict(X_test)), 0, None)
pd.DataFrame({"pred": pred}).to_csv("result.csv", index=False)
print(pd.read_csv("result.csv").shape)