2023.06.03 - [IT/Data 분석] - [게 나이 예측] 선형회귀 베이스라인
앞서 단순한 선형회귀 베이스라인을 설정하고 돌렸을 때 1.44 정도의 MAE 점수가 나왔다.
EDA 를 성의없게 했기에 이번에는 EDA 를 통해 변수를 분석해보려 한다.
plt.figure(figsize=(12,10))
plt.subplot(2,1,1)
sns.boxplot(train_df,x='Age',y='Height')
plt.subplot(2,1,2)
sns.boxplot(train_df,x='Age',y='Length')
Age를 그룹화 하여 outlier 이상치가 매우 많은 것을 알 수 있다.
Height 가 1 을 넘어서기도 하고 Length도 이상치가 많아 보인다.
대체적으로 이상치가 많을 것으로 예상이 된다.
Age 에서는 특별히 28 나이의 게의 자료는 없는 것으로 보인다.
일단 이상치를 제거하는 것을 목표로 해본다.
Age 는 test_df 에 존재하지 않는 값이기 때문에 train_df 로만 이상치를 제거하고 이걸 토대로 fit & predict 를 해보도록 한다.
앞으로 사용할 함수의 사용을 위해 컬럼의 이름을 변경한다.
train_df.rename(columns={'Shucked Weight':'SW','Viscera Weight':'VW','Shell Weight':'SHW'},inplace=True)
결측치는 boxplot 에서 사용하는 IQR 를 계산하여 처리하도록 함수를 만들어보았다.
column 변수에 Shell Weight 와 같이 공백이 들어가면 처리하지 못하는 문제를 해결하기 위해 위에서 rename 으로 SHW 로 변경하였던 것이다.
def outlier_drop(df_drop,column):
Q1 = df_drop.groupby('Age')[column].quantile(.25)
Q3 = df_drop.groupby('Age')[column].quantile(.75)
IQR = Q3-Q1
index_count=0
for i in range(1,30):
if 28 == i:
pass
else:
lowout=0
highout=0
lowout = Q1[i] - 1.5 * IQR[i]
highout = Q3[i] + 1.5 * IQR[i]
indexs = df_drop.query(f'Age == {i} and ({column} < {lowout} or {column} > {highout})').index.to_list()
if len(indexs) != 0:
index_count += len(indexs)
df_drop.drop(index=indexs,inplace=True)
print(column,df_drop.shape,index_count)
return df_drop
train_df_drop = train_df.copy()
for i in train_df_drop.columns.difference(['id','Age','Sex']):
outlier_drop(train_df_drop,i)
Diameter (72473, 10) 1578
Height (71598, 10) 875
Length (71161, 10) 437
SHW (70177, 10) 984
SW (69472, 10) 705
VW (68969, 10) 503
Weight (68825, 10) 144
train_df_drop 에서 이상치를 제거하면서 행도 함께 줄고 있는 것을 알 수 있다.
plt.figure(figsize=(12,10))
plt.subplot(2,1,1)
sns.boxplot(train_df_drop,x='Age',y='Height')
plt.subplot(2,1,2)
sns.boxplot(train_df_drop,x='Age',y='Length')
깔끔하게 모두 제거 될 줄 알았는데 이상치가 더 남아 있다.
한번 더 이상치를 제거 해본다.
for i in train_df_drop.columns.difference(['id','Age','Sex']):
outlier_drop(train_df_drop,i)
Diameter (68667, 10) 157
Height (68417, 10) 250
Length (68299, 10) 118
SHW (68069, 10) 230
SW (67924, 10) 145
VW (67843, 10) 81
Weight (67779, 10) 64
plt.figure(figsize=(12,10))
plt.subplot(2,1,1)
sns.boxplot(train_df_drop,x='Age',y='Height')
plt.subplot(2,1,2)
sns.boxplot(train_df_drop,x='Age',y='Length')
이상치가 아주 조금 남아 있지만 많이 호전된 것 같다.
그런데 지우면서 느낀건데 이상치가 과연 무적권 지워야하는 대상일까?
너무 말도 안되는 값 예를 들어 height 가 0 이라거나 height 가 1 이상이라던가 하는 확실히 이상한 애들이 아니고서야
이상치는 무적권 지워야하는 대상이 아닐 것 같다는 생각이 느낌적으로 든다.
이제 까지 제거한 정도로만 해서 모델을 학습시켜본다.
Y=train_df_drop['Age']
X=train_df_drop.drop(columns=['id','Age'])
X=pd.get_dummies(X)
print(X.shape,Y.shape,X.info())
<class 'pandas.core.frame.DataFrame'>
Int64Index: 67779 entries, 0 to 74050
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Length 67779 non-null float64
1 Diameter 67779 non-null float64
2 Height 67779 non-null float64
3 Weight 67779 non-null float64
4 SW 67779 non-null float64
5 VW 67779 non-null float64
6 SHW 67779 non-null float64
7 Sex_F 67779 non-null uint8
8 Sex_I 67779 non-null uint8
9 Sex_M 67779 non-null uint8
dtypes: float64(7), uint8(3)
memory usage: 4.3 MB
(67779, 10) (67779,) None
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
x_train,x_test,y_train,y_test = train_test_split(X,Y,random_state=42,test_size=0.2,shuffle=True)
model_LR = LinearRegression()
model_LR.fit(x_train,y_train)
y_pred = model_LR.predict(x_test).astype(int)
print(mean_absolute_error(y_test,y_pred))
1.3360873413986427
MAE 점수가 무려 1.33 까지 줄일 수 있었다.
기쁜 마음을 갖고 test_df 데이터를 학습시킨 후 제출해보았다.
test_df_drop = test_df.copy()
test_df_drop.rename(columns={'Shucked Weight':'SW','Viscera Weight':'VW','Shell Weight':'SHW'},inplace=True)
test_df_drop.drop(columns='id',inplace=True)
print(test_df_drop.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 49368 entries, 0 to 49367
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Sex 49368 non-null object
1 Length 49368 non-null float64
2 Diameter 49368 non-null float64
3 Height 49368 non-null float64
4 Weight 49368 non-null float64
5 SW 49368 non-null float64
6 VW 49368 non-null float64
7 SHW 49368 non-null float64
dtypes: float64(7), object(1)
memory usage: 3.0+ MB
None
test_df_drop = pd.get_dummies(test_df_drop)
print(test_df_drop.info())
y_pred_test = model_LR.predict(test_df_drop).astype(int)
print(y_pred_test[:5])
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 49368 entries, 0 to 49367
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Length 49368 non-null float64
1 Diameter 49368 non-null float64
2 Height 49368 non-null float64
3 Weight 49368 non-null float64
4 SW 49368 non-null float64
5 VW 49368 non-null float64
6 SHW 49368 non-null float64
7 Sex_F 49368 non-null uint8
8 Sex_I 49368 non-null uint8
9 Sex_M 49368 non-null uint8
dtypes: float64(7), uint8(3)
memory usage: 2.8 MB
None
[ 7 7 10 9 7]
test_df['Age'] = y_pred_test
test_df_sub = test_df[['id','Age']]
test_df_sub.set_index('id',inplace=True)
print(test_df_sub.head())
test_df_sub.to_csv('submission.csv')
Age
id
74051 7
74052 7
74053 10
74054 9
74055 7
나는 놀라지 않을 수 없었다.
train_df 로 1.33 의 MAE 를 보였던 모델이 정작 test_df 에 적용하였을 때 지난번 베이스라인 모델만 사용했던 때보다 0.02 더 상승했다.
더 안좋은 모델을 만들어 냈다는 의미다.
여기에는 기록하지 않았는데,
처음에 all_data 형태로 train_df, test_df 를 concat 해서 글을 작성하기 전에 이상치를 제거했었다.
그런데 그래프에 여전히 이상치가 많이 남아있는것이 아닌가?
내가 이상치를 제거하는 방식은 Age x 축으로 해서 제거하는 방법이 였는데 생각해보니 test_df 에는 Age 컬럼이 존재하지 않는다.
다른 말로 train_df 는 이상치를 제거한 모델이 MAE 가 좋은 점수를 낼 수 있었다고 하더라도
test_df 에 있는 이상치 들에는 적합하지 않은 모델이 되는 셈이다. 이런걸 다른 말로 오버피팅 이라고도 할 수 있을 것 같다.
좋은 것을 배웠다.
그런데 문득 이런생각도 든다. all_data 를 통해서 이상치를 잘 제거 했다고 쳐도 결국에는 test_df 를 예측 할때에는 이상치가 있는 상태에서 예측을 해야 한다.
아무래도 이상치 제거는 정답이 아닐 수도 있겠다는 생각이 든다.
다음에는 feet , oz 의 단위가 다른 독립변수들을 스케일 하여 테스트를 해봐야겠다.
'IT > Data 분석' 카테고리의 다른 글
[게 나이 예측] 선형회귀 베이스라인 (0) | 2023.06.03 |
---|---|
[차원 이동] 베이스라인 모델 - 1 (0) | 2023.05.25 |
[차원 이동] 분석 12회차[독립변수 파악3] (0) | 2023.05.13 |
[차원 이동] 분석 11회차[독립변수 파악2] (0) | 2023.04.27 |
[차원 이동] 분석 10 회차[독립변수 파악] (0) | 2023.04.27 |