iT邦幫忙

2021 iThome 鐵人賽

DAY 3
1
自我挑戰組

資料分析及AI深度學習-簡單基礎實作系列 第 3

DAY3:離職率預測(下)

  • 分享至 

  • xImage
  •  

人工智慧共創平台-離職率預測(下)

  • 資料清洗

ax = sns.countplot(x="PerStatus", data=train)

接續昨天的資料清洗,透過上圖可以發現離職(1)和尚未離職(0)的比例太過懸殊,離職的筆數只有796筆,所以我們也隨機將尚未離職的資料取出796筆,這樣總共筆數就變為1592筆,將離職和未離職的比例變成各半。

df = train.sample(frac=1)
quit_df = df.loc[df['PerStatus'] == 1]
non_quit_df = df.loc[df['PerStatus'] == 0][:796]
normal_distributed_df = pd.concat([quit_df, non_quit_df])
new_df = normal_distributed_df.sample(frac=1, random_state=42)

另外我們將'PerStatus'這個應變數欄位取出成為我們的label

label = new_df.loc[:,"PerStatus"]
label.value_counts()
new_df_no_label = new_df.drop(columns="PerStatus")

我們把資料整理成要丟入模型的樣子了,剩下就可以丟入建模啦!

這裡我們簡單總結一下,我們做了哪些資料清洗。

  1. 首先我們檢查到了資料有沒離職但卻沒有後續資料的員工
  2. 我們發現離職(0)和未離職員工(1)的資料比例太懸殊,所以我們將資料取成一樣的比例。
  3. 這邊我們沒有做到特徵工程的部分,有興趣的夥伴可以用視覺化的方式去做特徵的篩選。

  • 建模

講到python建模,一定會用到Scikit-Learn這個套件,非常好用,裡面包刮各種機器學習的模型,這裡我會用到多種模型來做預測,

下面程式碼的train_test_split是幫你把資料切成訓練集和測試集,test_size可以設定你測試集的比例,random_state是讓它固定,不會每次切的時候都是不一樣的訓練集和測試集。

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(new_df_no_label, label, test_size=0.2, random_state=2)

下面StratifiedKFold跟KFold交叉驗證差別在於,StratifiedKFold做交叉驗證,相較於KFold,StratifiedKFold會照比例在每個data set中抽取資料作驗證。

from sklearn.model_selection import StratifiedKFold

sk_fold = StratifiedKFold(10,shuffle=True, random_state=42)

而GridSearchCV函數會自動作Cross Validation,可以省去大量時間幫我們找出最佳參數組合。

from sklearn.model_selection import GridSearchCV

接下來就來建立模型啦~先給定模型,然後clf是我設定的參數,會透過GridSearchCV這個套件幫我篩選每個模型的最佳參數。

關於機器學習模型,小弟這邊有在github做一些整理,有需要也可以去看一下,不知道對你們有沒有幫助XDDDD
GitHub連結

先import SKlearn的模型套件。

# NAIBE BAYES
from sklearn.naive_bayes import GaussianNB
#KNN
from sklearn.neighbors import KNeighborsClassifier
#RANDOM FOREST
from sklearn.ensemble import RandomForestClassifier
#LOGISTIC REGRESSION
from sklearn.linear_model import LogisticRegression
#SVM
from sklearn.svm import SVC
#DECISON TREE
from sklearn.tree import DecisionTreeClassifier
#XGBOOST
from xgboost import XGBClassifier
#AdaBoosting Classifier
from sklearn.ensemble import AdaBoostClassifier
#GradientBoosting Classifier
from sklearn.ensemble import GradientBoostingClassifier
#HistGradientBoostingClassifier
from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier, StackingClassifier
from sklearn.metrics import confusion_matrix
g_nb = GaussianNB()
knn = KNeighborsClassifier()  # 參數:n_neighbors(鄰居數:預設為5)、weights(權重,預設為uniform)、leaf_size(葉的大小:預設為30)
ran_for  = RandomForestClassifier()
# n_estimators:樹的顆數、max_depth:最大深度,剪枝用,超過全部剪掉。
# min_samples_leaf:搭配max_depth使用,一個節點在分枝後每個子節點都必須包含至少min_samples_leaf個訓練樣本
# bootstrap:重新取樣原有Data產生新的Data,取樣的過程是均勻且可以重複取樣
log_reg = LogisticRegression() #penalty:懲罰函數(預設L2)、C:正則強度倒數,預設為1.0、solver:解決器(默認='lbfgs'),saga對所有懲罰都可以使用
tree= DecisionTreeClassifier()
xgb = XGBClassifier()#https://www.itread01.com/content/1536594984.html 參數詳解
ada_boost = AdaBoostClassifier() # https://ask.hellobi.com/blog/zhangjunhong0428/12405 參數詳解
grad_boost = GradientBoostingClassifier(n_estimators=100) # https://www.itread01.com/content/1514358146.html 參數詳解
hist_grad_boost = HistGradientBoostingClassifier() # https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.HistGradientBoostingClassifier.html

clf = [("Naive Bayes", g_nb, {}), \
       ("K Nearest", knn, {"n_neighbors": [3, 5, 6, 7, 8, 9, 10], "leaf_size": [25, 30, 35]}), \
       ("Random Forest", ran_for, {"n_estimators": [10, 50, 100, 200, 400], "max_depth": [3, 10, 20, 40], "random_state": [99],"min_samples_leaf": [5, 10, 20, 40, 50], "bootstrap":[False]}), \
       ("Logistic Regression", log_reg, {"penalty": ['l2'], "C": [100, 10, 1.0, 0.1, 0.01],      "solver": ['saga']}), \
       ("Decision Tree", tree, {}), \
       ("XGBoost", xgb,{"n_estimators": [200], "max_depth": [3, 4, 5], "learning_rate": [.01,    .1, .2], "subsample": [.8],"colsample_bytree": [1], "gamma": [0, 1, 5], "lambda": [.01,.1, 1]}), \("Adapative Boost", ada_boost, {"n_estimators": [100], "learning_rate": [.6, .8, 1]}), \("Gradient Boost", grad_boost, {}), \
("Histogram GB", hist_grad_boost,{"loss": ["binary_crossentropy"], "min_samples_leaf":           [5, 10, 20, 40, 50], "l2_regularization": [0, .1, 1]})]

前置作業都準備好之後,我們可以來訓練模型了,我透過GridSearchCV這個套件一次訓練多個模型讓他找出最佳參數組合。

stack_list = []
train_scores = pd.DataFrame(columns=["Name", "Train Score", "Test Score"])

i = 0
for name, clf1, param_grid in clf:
    clf = GridSearchCV(clf1, param_grid=param_grid, scoring="accuracy", cv=sk_fold,                       return_train_score=True)
    clf.fit(X_train, y_train)  # .reshape(-1,1)
    y_pred = clf.best_estimator_.predict(X_test)

    cm = confusion_matrix(y_test, y_pred)
    print(cm)
    print("=====================================")

    train_scores.loc[i] = [name, clf.best_score_, (cm[0, 0] + cm[1, 1,]) / (cm[0, 0] + cm[0, 1]+ cm[1, 0] + cm[1, 1])]
    stack_list.append(clf.best_estimator_)
    i = i + 1

est = [("g_nb", stack_list[0]), \
       ("knn", stack_list[1]), \
       ("ran_for", stack_list[2]), \
       ("log_reg", stack_list[3]), \
       ("dec_tree", stack_list[4]), \
       ("XGBoost", stack_list[5]), \
       ("ada_boost", stack_list[6]), \
       ("grad_boost", stack_list[7]), \
       ("hist_grad_boost", stack_list[8])]

這幾個模型練完之後,我想再加上集成學習stacking堆疊法去做訓練。

sc = StackingClassifier(estimators=est,final_estimator = None,cv=sk_fold,passthrough=False)
sc.fit(X_train,y_train)
y_pred = sc.predict(X_test)
cm1 = confusion_matrix(y_test,y_pred)
y_pred_train = sc.predict(X_train)
cm2 = confusion_matrix(y_train,y_pred_train)
train_scores.append(pd.Series(["Stacking",(cm2[0,0]+cm2[1,1,])/(cm2[0,0]+cm2[0,1]+cm2[1,0]+cm2[1,1]),(cm1[0,0]+cm1[1,1,])/(cm1[0,0]+cm1[0,1]+cm1[1,0]+cm1[1,1])],index=train_scores.columns),ignore_index=True)

因為這邊我沒有多做特徵的篩選,只有做一些簡單的資料清洗,所以跑出來的準確度不是很好,如下圖:

觀察一下上述的準確度,Random Forest是我們Test Score最高,我選擇用Random Forest來做為我最後要上傳所使用的模型。

ran_for.fit(X_train,y_train)
test['PerStatus'] = ran_for.predict(test)
submission = test[['PerNo','PerStatus']]
submission.to_csv("./submission1.csv", index=False)

最後產出的CSV檔我們就上傳到AIdea的平台啦。

恩...29名...這排名和分數就先不要去在意他啦XDDDD至少我們自己從頭到尾做了一次,也上傳得到了成績和名次,整個過程在資料清洗和特徵篩選的部分,有很多可以優化的,這次主要是想讓剛接觸的夥伴可以知道怎麼去完成一個平台的題目,如果有大大有得到更好的分數和名次也歡迎來一起分享給大家討論喔!!


  • 資料視覺化

再來教大家做簡單的資料視覺化,正常來說拿到資料一定要用資料視覺化去觀察各個特徵喔!我們會用到matplotlib這個套件,另外還有seaborn這個套件,它是建立於matplotlib之上,因此seaborn可以直接與之產生的圖互動。

import matplotlib.pyplot as plt
import seaborn as sns

首先import套件之後呢,來看看統計圖表,我們可以透過圖表來篩選哪些是重要特徵,每個特徵我們都可以用合理的方法去推斷他為何重要或影響力較低,例如性別的離職人數比較:

sex_list = list(train['sex'].unique())
sex_sum_leave = []
for i in sex_list:
    x = train[train['sex']==i]
    sex_leave = sum(x.PerStatus)
    sex_sum_leave.append(sex_leave)

data = pd.DataFrame({'sex_list': sex_list,'sex_leave':sex_sum_leave})
new_index = (data['sex_leave'].sort_values(ascending=True)).index.values
sorted_data2 = data.reindex(new_index)

plt.figure(figsize=(15,10))
sns.barplot(x=sorted_data2['sex_list'], y=sorted_data2['sex_leave'])
plt.xticks(rotation= 90)
plt.xlabel('Sex')
plt.ylabel('sum of Leave')
plt.title("Sum of Leave of Sex")

可以觀察到性別1比性別0要多出三倍的離職人數,可以說明性別這個自變數對於離職這個應變數是有影響力的,因此性別可以視為重要特徵

再來看看每年度的離職率~

year_list = list(train['yyyy'].unique())
year_leaverate = []
for i in year_list:
    x = train[train['yyyy']==i]
    year_leave_rate = sum(x.PerStatus)/len(x)
    year_leaverate.append(year_leave_rate)

data = pd.DataFrame({'year_list': year_list,'year_leave_ratio':year_leaverate})
new_index = (data['year_leave_ratio'].sort_values(ascending=True)).index.values
sorted_data2 = data.reindex(new_index)

plt.figure(figsize=(15,10))
sns.barplot(x=sorted_data2['year_list'], y=sorted_data2['year_leave_ratio'])
plt.xticks(rotation= 90)
plt.xlabel('Year')
plt.ylabel('Leave Rate')
plt.title("Leave rate of Year")
plt.show()

這看起來差異就並不是那麼大,那從圖中可以了解到2014年的離職比率是最高的,也說明年份相較於性別,對於應變數離職的影響力就沒那麼大。

各部門離職人數

department_list = list(train['歸屬部門'].unique())
department_leave_list = []
for i in department_list:
    x = train[train['歸屬部門']==i]
    department_leave = sum(x.PerStatus)
    department_leave_list.append(department_leave)

data = pd.DataFrame({'dep_list': department_list,'dep_leave':department_leave_list})
new_index = (data['dep_leave'].sort_values(ascending=True)).index.values
sorted_data2 = data.reindex(new_index)

plt.figure(figsize=(15,10))
sns.barplot(x=sorted_data2['dep_list'], y=sorted_data2['dep_leave'])
plt.xticks(rotation= 90)
plt.xlabel('dep')
plt.ylabel('sum of Leave')
plt.title("Sum of Leave of dep")

(抱歉座標的字有點小><)從圖中可以發現部門代號20208為離職人數最多的,部門代號14040則為次之。另外也可以知道部門對於離職是有差異性的,我也會把部門視為重要特徵

可以一一的對每個特徵做統計圖表的檢視來篩選變數,這裡就簡單示範,後面就不一一贅述啦,讀者可以去把每個變數都看一遍,把不重要的變數排除看看,最後建模看是否準確度會比較高,應該是多少有幫助的喔!!

今天就先到這邊囉,如果過程有誤也歡迎留言糾正,教學相長嘛哈哈哈,我相信這邊有更多大神有更完善的處理方法,也歡迎分享給小弟,讓小弟多多學習。


  • 今日小結

做資料分析,一定是資料清洗以及特徵篩選最為重要,我這邊是有偷懶,但大家切記在幫客戶做資料分析的時候,前面兩個動作一定要做的完善且謹慎,這樣得到得結果會越是理想。

另外要做機器學習建議先去了解機器學習的模型,甚麼情況適合用甚麼模型,二分類以及多分類等等,使用的模型都會影響到結果的好壞,但沒有哪種模型是最好的,每個模型都有優缺點,建議多方嘗試,找出最適合的模型。

參考資料:

https://www.itread01.com/content/1536594984.html
https://ask.hellobi.com/blog/zhangjunhong0428/12405
https://www.itread01.com/content/1514358146.html
https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.HistGradientBoostingClassifier.html


上一篇
DAY2:離職率預測(上)
下一篇
DAY4:Kaggle-Data Science London + Scikit-learn(一)
系列文
資料分析及AI深度學習-簡單基礎實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言