미군 신체측정 데이터셋(Anthropometric Survey of US Army Personnel, ANSUR 2)은 2012년 내부에 공개되었고 2017년에 대중에 공개되었다. 총 6,000명 군인(남자 4,082, 여자 1,986)에 대한 측정정보를 담고 있다. 실제 데이터를 받아보면 6,068명 행으로 되어있고, 108 칼럼으로 구성되어 있다. R에서 불필요하다고 판단되는 변수를 일부 제고하고 남은 99개 변수를 대상으로 예측모형 구축작업을 수행해 나간다.
pd.read_csv()
함수로 데이터프레임으로 불러온 후에 .shape
, .dtypes
, .head
를 사용해서 데이터프레임과 친숙해진다.
import pandas as pd
ansur_pd = pd.read_csv("data/soldier_df.csv")
ansur_pd.shape
(6068, 99)
ansur_pd.dtypes
abdominalextensiondepthsitting int64
acromialheight int64
acromionradialelength int64
anklecircumference int64
axillaheight int64
balloffootcircumference int64
balloffootlength int64
biacromialbreadth int64
bicepscircumferenceflexed int64
bicristalbreadth int64
bideltoidbreadth int64
bimalleolarbreadth int64
bitragionchinarc int64
bitragionsubmandibulararc int64
bizygomaticbreadth int64
buttockcircumference int64
buttockdepth int64
buttockheight int64
buttockkneelength int64
buttockpopliteallength int64
calfcircumference int64
cervicaleheight int64
chestbreadth int64
chestcircumference int64
chestdepth int64
chestheight int64
crotchheight int64
crotchlengthomphalion int64
crotchlengthposterioromphalion int64
earbreadth int64
...
shoulderlength int64
sittingheight int64
sleevelengthspinewrist int64
sleeveoutseam int64
span int64
stature int64
suprasternaleheight int64
tenthribheight int64
thighcircumference int64
thighclearance int64
thumbtipreach int64
tibialheight int64
tragiontopofhead int64
trochanterionheight int64
verticaltrunkcircumferenceusa int64
waistbacklength int64
waistbreadth int64
waistcircumference int64
waistdepth int64
waistfrontlengthsitting int64
waistheightomphalion int64
weightkg int64
wristcircumference int64
wristheight int64
Gender object
Component object
Branch object
DODRace int64
Age int64
WritingPreference object
Length: 99, dtype: object
ansur_pd.head(3)
abdominalextensiondepthsitting ... WritingPreference
0 266 ... Right hand
1 233 ... Left hand
2 287 ... Left hand
[3 rows x 99 columns]
데이터에 대한 확인이 되었으면 다음으로 모형을 정의한다. 즉, 남성과 여성이 Label 이 되고, 남성과 여성을 신체특성 변수 feature로 예측하는 모형이다.
\[\text{남자 혹은 여자} = f(x_1 , x_2 , \cdots, x_n) + \epsilon\]
데이터프레임에서 범주형 변수로 정의한 칼럼명을 리스트로 정의한 후에 LabelEncoder
를 사용해서 예측모형에서 사용할 수 있는 형태로 변환시킨다.
from sklearn.preprocessing import LabelEncoder
category_columns = ["Gender", "Component", "Branch", "DODRace", "WritingPreference"]
for column in category_columns:
le = LabelEncoder()
ansur_pd[column] = le.fit_transform(ansur_pd[column])
print(ansur_pd.dtypes)
abdominalextensiondepthsitting int64
acromialheight int64
acromionradialelength int64
anklecircumference int64
axillaheight int64
balloffootcircumference int64
balloffootlength int64
biacromialbreadth int64
bicepscircumferenceflexed int64
bicristalbreadth int64
bideltoidbreadth int64
bimalleolarbreadth int64
bitragionchinarc int64
bitragionsubmandibulararc int64
bizygomaticbreadth int64
buttockcircumference int64
buttockdepth int64
buttockheight int64
buttockkneelength int64
buttockpopliteallength int64
calfcircumference int64
cervicaleheight int64
chestbreadth int64
chestcircumference int64
chestdepth int64
chestheight int64
crotchheight int64
crotchlengthomphalion int64
crotchlengthposterioromphalion int64
earbreadth int64
...
shoulderlength int64
sittingheight int64
sleevelengthspinewrist int64
sleeveoutseam int64
span int64
stature int64
suprasternaleheight int64
tenthribheight int64
thighcircumference int64
thighclearance int64
thumbtipreach int64
tibialheight int64
tragiontopofhead int64
trochanterionheight int64
verticaltrunkcircumferenceusa int64
waistbacklength int64
waistbreadth int64
waistcircumference int64
waistdepth int64
waistfrontlengthsitting int64
waistheightomphalion int64
weightkg int64
wristcircumference int64
wristheight int64
Gender int64
Component int64
Branch int64
DODRace int64
Age int64
WritingPreference int64
Length: 99, dtype: object
BernoulliNB
나이브 베이즈모형을 신체측정 feature를 바탕으로 시운전 개념으로 적합시켜본다.
from sklearn.naive_bayes import BernoulliNB
features, labels = ansur_pd.drop('Gender', 1), ansur_pd['Gender']
dry_run_nb = BernoulliNB()
dry_run_nb.fit(features, labels)
BernoulliNB(alpha=1.0, binarize=0.0, class_prior=None, fit_prior=True)
dry_run_nb.predict(features.head(5))
array([1, 1, 0, 1, 0])
train_test_split
으로 훈련/시험 데이터로 쪼개고 RandomForestClassifier
를 추가해서 두가지 예측모형의 성능을 비교할 수 있는 accuracy_score
함수로 측정한다.
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# 성능비교를 위한 딕셔너리
accuracies = {}
X_train, X_test, y_train, y_test = train_test_split(
features, labels, test_size = 0.3, random_state=7)
# Random Forest ------------------------------
rf_model = RandomForestClassifier(random_state = 77).fit(
X_train, y_train)
rf_predictions = rf_model.predict(X_test)
accuracies['rf'] = accuracy_score(y_test, rf_predictions)
# 나이브베이즈 ------------------------------
nb_model = BernoulliNB().fit(
X_train, y_train)
nb_predictions = nb_model.predict(X_test)
accuracies['Naive Bayes'] = accuracy_score(y_test, nb_predictions)
accuracies
{'rf': 0.9912136188907194, 'Naive Bayes': 0.6924766611751785}
Random Forest 예측모형은 초모수(hyper parameter)를 탐색하여 예측모형의 성능을 추가로 더 끌어올릴 수 있다. 이를 위해서 랜덤탐색(random search)도 있고, 격자탐색(grid search)도 더불어 많이 사용된다.
RandomForestClassifier
, Random Forest 모형의 경우 다음 초모수가 성능에 영향을 미치는 것으로 연구결과가 공개되어 있다.
from pprint import pprint
print('기본설정 디폴트 모형에 사용된 모수 목록:\n')
기본설정 디폴트 모형에 사용된 모수 목록:
pprint(rf_model.get_params())
{'bootstrap': True,
'class_weight': None,
'criterion': 'gini',
'max_depth': None,
'max_features': 'auto',
'max_leaf_nodes': None,
'min_impurity_decrease': 0.0,
'min_impurity_split': None,
'min_samples_leaf': 1,
'min_samples_split': 2,
'min_weight_fraction_leaf': 0.0,
'n_estimators': 10,
'n_jobs': 1,
'oob_score': False,
'random_state': 77,
'verbose': 0,
'warm_start': False}
Random Forest 예측모형에 가장 영향을 많이 미치는 초모수 3개에 대해서 아래와 같이 값을 부여하고, param_grid
를 통해 격자탐색을 작업을 수행하여 최적모수를 찾아낸다.
from sklearn.model_selection import GridSearchCV
param_grid = {'n_estimators' : list(range(10, 50, 10)),
'max_features' : ['auto', 'sqrt'],
'max_depth' : list(range(2, 10, 2))}
grid = GridSearchCV(RandomForestClassifier(), param_grid, cv=3)
grid.fit(X_train, y_train)
GridSearchCV(cv=3, error_score='raise',
estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
oob_score=False, random_state=None, verbose=0,
warm_start=False),
fit_params=None, iid=True, n_jobs=1,
param_grid={'n_estimators': [10, 20, 30, 40], 'max_features': ['auto', 'sqrt'], 'max_depth': [2, 4, 6, 8]},
pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
scoring=None, verbose=0)
grid.best_params_
{'max_depth': 8, 'max_features': 'sqrt', 'n_estimators': 40}
grid.best_params_
딕셔너리에는 최적화된 초모수가 포함되어 있다. 해당 값들을 꺼내 초모수에 넣어 Random Forest 모형에 넣어 다시 훈련데이터에 적합시키게 되면 예측모형의 성능이 소폭 향상된 것을 확인할 수 있다.
rf_hyper_model = RandomForestClassifier(n_estimators = grid.best_params_['n_estimators'],
max_features = grid.best_params_['max_features'],
max_depth = grid.best_params_['max_depth']).fit(X_train, y_train)
rf_hyper_predictions = rf_hyper_model.predict(X_test)
accuracies['rf hyper parameter'] = accuracy_score(y_test, rf_hyper_predictions)
accuracies
{'rf': 0.9912136188907194, 'Naive Bayes': 0.6924766611751785, 'rf hyper parameter': 0.9912136188907194}
SelectKBest
를 통해 예측에 효과가 큰 feature를 추출해낸다. 앞서 선택된 초모수를 함께 넣어 예측모형을 만들어낸다. 가장 영향력이 큰 feature 30개를 대상으로 예측모형 성능을 평가한다.
from sklearn.feature_selection import SelectKBest, f_classif
select_k_best_classifier = SelectKBest(f_classif, k='all').fit(X_train, y_train)
rf_selectK_model = RandomForestClassifier(n_estimators = grid.best_params_['n_estimators'],
max_features = grid.best_params_['max_features'],
max_depth = grid.best_params_['max_depth']).fit(select_k_best_classifier.transform(X_train), y_train)
rf_selectK_predictions = rf_selectK_model.predict(X_test)
accuracies['rf hyper selectK'] = accuracy_score(y_test, rf_selectK_predictions)
accuracies
{'rf': 0.9912136188907194, 'Naive Bayes': 0.6924766611751785, 'rf hyper parameter': 0.9912136188907194, 'rf hyper selectK': 0.9890170236133993}
SelectKBest
를 통해 어떤 feature가 추출되었는지 살펴본다. 먼저 매스크(mask)를 생성시키고 성성된 매스크를 넣어 변수명과 함께 확인할 수 있도록 코드를 작성한다.
mask = select_k_best_classifier.get_support() # 부울값 리스트
best_features = X_train.columns[mask]
print("Feature 총계:", len(best_features), "\n", "선택된 30개 feature:", best_features)
Feature 총계: 98
선택된 30개 feature: Index(['abdominalextensiondepthsitting', 'acromialheight',
'acromionradialelength', 'anklecircumference', 'axillaheight',
'balloffootcircumference', 'balloffootlength', 'biacromialbreadth',
'bicepscircumferenceflexed', 'bicristalbreadth', 'bideltoidbreadth',
'bimalleolarbreadth', 'bitragionchinarc', 'bitragionsubmandibulararc',
'bizygomaticbreadth', 'buttockcircumference', 'buttockdepth',
'buttockheight', 'buttockkneelength', 'buttockpopliteallength',
'calfcircumference', 'cervicaleheight', 'chestbreadth',
'chestcircumference', 'chestdepth', 'chestheight', 'crotchheight',
'crotchlengthomphalion', 'crotchlengthposterioromphalion', 'earbreadth',
'earlength', 'earprotrusion', 'elbowrestheight', 'eyeheightsitting',
'footbreadthhorizontal', 'footlength', 'forearmcenterofgriplength',
'forearmcircumferenceflexed', 'forearmforearmbreadth',
'forearmhandlength', 'functionalleglength', 'handbreadth',
'handcircumference', 'handlength', 'headbreadth', 'headcircumference',
'headlength', 'heelanklecircumference', 'heelbreadth', 'hipbreadth',
'hipbreadthsitting', 'iliocristaleheight', 'interpupillarybreadth',
'interscyei', 'interscyeii', 'kneeheightmidpatella',
'kneeheightsitting', 'lateralfemoralepicondyleheight',
'lateralmalleolusheight', 'lowerthighcircumference',
'mentonsellionlength', 'neckcircumference', 'neckcircumferencebase',
'overheadfingertipreachsitting', 'palmlength', 'poplitealheight',
'radialestylionlength', 'shouldercircumference', 'shoulderelbowlength',
'shoulderlength', 'sittingheight', 'sleevelengthspinewrist',
'sleeveoutseam', 'span', 'stature', 'suprasternaleheight',
'tenthribheight', 'thighcircumference', 'thighclearance',
'thumbtipreach', 'tibialheight', 'tragiontopofhead',
'trochanterionheight', 'verticaltrunkcircumferenceusa',
'waistbacklength', 'waistbreadth', 'waistcircumference', 'waistdepth',
'waistfrontlengthsitting', 'waistheightomphalion', 'weightkg',
'wristcircumference', 'wristheight', 'Component', 'Branch', 'DODRace',
'Age', 'WritingPreference'],
dtype='object')