#forust #RandomForust #앙상블모델
짜잔!
https://jinlow.github.io/forust/#python-api-reference
사용 예제 조금 튀들어서 다시 만들어 봤는데
사용하기 너무 쉽고 간편한 듯 해서 기록한다.
먼저,
>> !pip install forust seaborn
코드는 간단한데
import ssl
import polars as pl
import matplotlib.pyplot as plt
from forust import GradientBooster
from seaborn import lineplot
from seaborn import load_dataset
from sklearn.preprocessing import LabelEncoder
ssl._create_default_https_context = ssl._create_unverified_context
ssl 부분 체크가 필요하다.
이어서,
데이터가 조금 허접해서 파생 데이터 3개 정도 간단하게 만들고
df = pl.DataFrame(load_dataset("titanic"))
le = LabelEncoder()
df = df.with_columns([
pl.Series(le.fit_transform(np.array(df.select(pl.col('who'))).ravel())).alias('who_encode'),
pl.Series(le.fit_transform(np.array(df.select(pl.col('class'))).ravel())).alias('class_encode'),
pl.Series(le.fit_transform(np.array(df.select(pl.col('embarked'))).ravel())).alias('embarked_encode'),
])
잘 만든 데이터에서 머신러닝에 필요한 뉴메릭(?)... 숫자 데이터만 선택해서 가져온다.
X1 = df.select([
pl.col(
pl.Int8, pl.Int16, pl.Int32, pl.Int64, pl.UInt8, pl.UInt16, pl.UInt32, pl.UInt64,
pl.Float32, pl.Float64
).exclude("survived")
]).to_pandas()
y1 = df.select(pl.col("survived")).to_pandas()
*_encode 컬럼이 새로 만든 것이다.
우리는 보통 XGBoost 라이브러리를 주로 사용했을 것 같은데
Forust 도 못지않은 좋은 성능과 장점을 가지고 있는 것 같다.
첫번째 모델을 만들고 FIT 시켜보면
model = GradientBooster(
objective_type="LogLoss",
)
model.fit(X_train, y_train)
간단하게 테스트 데이터로 정확도를 확인해보면,
res = model.predict(X_test)
res = np.where(res > 0, 1, 0)
print(str(np.round(np.mean(res == y_test) * 100, decimals=2)) + '%')
>> 77.13%
라는 결과를 얻을 수 있었는데
재미있는 부분은 바로 monotone 을 사용해서 글로벌한 정확도를 끌어올릴 수 있다는 부분이다.
그에 앞서,
어떤 Feature 를 monotone 대상으로 볼 것인지 간단한(?) 엔지니어링 작업이 선행되어야 한다.
사실 간단하지 않지만 Forust 에서 제공하는 partial_dependence 를 사용하기 때문에 간단하다고 표현했다.
pd_values = model.partial_dependence(X=X1.to_pandas(), feature="age", samples=None)
fig = lineplot(x=pd_values[:,0], y=pd_values[:,1],)
plt.title("Partial Dependence Plot")
plt.xlabel("Age")
plt.ylabel("Log Odds")
참고로 코드는 홈페이지 예제에 똑같이! 나와있다.
특별히 age 컬럼에 대해서 model 에 어떤 영향도를 주는지 확인한다.
현업에서는 보통 이런 들쭉날쭉한 방정식에 대해서 1차 또는 2차 방적식으로 간단하게 표현해서 사용하는데
이게 바로 monotone 이다.
두번째 모델은 monotone 옵션을 추가해서 재작성 해보도록 한다.
model2 = GradientBooster(
objective_type="LogLoss",
monotone_constraints={"age": -1},
)
model2.fit(X_train, y_train)
pd_values = model2.partial_dependence(X=X_train, feature="age")
fig = lineplot(
x=pd_values[:, 0],
y=pd_values[:, 1],
)
plt.title("Partial Dependence Plot with Monotonicity")
plt.xlabel("Age")
plt.ylabel("Log Odds")
이러면 age 의 model 에 대한 영향도가 균일해질텐데 실제로 결과를 보면
뭔가 선이 스무스하게 y=-x 형태터럼 보인다.
두번째 모델로 정확도를 확인해보면
res = model2.predict(X_test)
res = np.where(res > 0, 1, 0)
print(str(np.round(np.mean(res == y_test) * 100, decimals=2)) + '%')
>> 81.61%
쬐~~끔 올랐다.
그래도 뭔가 변화가 있는 결과라는 부분에서 재미는 있다.
P.S.
데이터에 여러가지 파생데이터를 붙여서 해봤는데
monotone 사용이 몇 % 정도 이점을 주는 것으로 확인했다.