










hyperplane에 가장 가까운 값을 SV(서포트벡터)라고 함

직선(1차원)을 평면(2차원)으로 옮김
2차원으로 옮기고 나서 둘을 구분하려면 1차원(직선)으로 구분 가능
2차원에서는 1차원으로 분류 가능
3차원에서는 2차원으로 분류 가능

# 라이브러리 불러오기
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Warnings 제거
import warnings
warnings.filterwarnings('ignore')
# 예측 모델링을 위한 라이브러리 불러오기
from sklearn.model_selection import train_test_split # 훈련/평가 데이터 분리
from sklearn.linear_model import LogisticRegression # 로지스틱 모델
from sklearn.svm import LinearSVC # 분류용 SVM 모델
from sklearn import metrics # 평가용 모듈
from sklearn.model_selection import cross_val_score # 교차 검증 함수
# 모델 평가를 위한 라이브러리 불러오기
from sklearn.metrics import classification_report # 분류 평가지표
# 데이터 불러오기
df = pd.read_csv('./data/job_transfer.csv')
df.head()
df.info()
# 수치형데이터는 학습을 할 수가 없다
# Dtype 확인하여 수치형데이터로 변경해주어야 한다

# null값 확인
df.isnull().sum() # 모두 0개

# outlier 확인
df.describe()

# 이직
df['Attrition'].value_counts()


# 학습 및 집계를 위해 수치형 데이터로 변경
# 이직 = 1, 이직 안 함 = 0로 수정
# np.where(조건, 참이라면 바꿀 값, 거짓이라면 바꿀 값)
df['Attrition'] = np.where(df['Attrition']=='Yes', 1, 0)

# 학습 및 집계를 위해 수치형 데이터로 변경
# 이직 = 1, 이직 안 함 = 0로 수정
# np.where(조건, 참이라면 바꿀 값, 거짓이라면 바꿀 값)
df['Attrition'] = np.where(df['Attrition']=='Yes', 1, 0)
df['Attrition'].value_counts()

# 성별, 연령 분석
# 나이 컬럼을 구간화 (범주형데이터)
# where 기능이 2개의 구간으로 나눠주는 함수
df['Age_gp'] = np.where(df['Age'] <= 30, '30대 이하',
np.where(df['Age'] <= 40, '30-40대', '40대 이상'))
df[['Age','Age_gp']]

# 연령별 이직률 현황
# agg 함수 : aggregate의 약자로, 하나 이상의 함수를 적용하여, 그룹별로 집계 결과를 얻음
df_gp = df.groupby('Age_gp')['Attrition'].agg(['count','sum'])
df_gp['ratio'] = round(df_gp['sum']/df_gp['count']*100, 1) # 반올림
df_gp
# 젊을수록 이직률 높음

# 성별 이직률 현황
df_gp2 = df.groupby('Gender')['Attrition'].agg(['count','sum'])
df_gp2['ratio'] = round(df_gp2['sum']/df_gp2['count']*100, 1) # 반올림
df_gp2
# 남성의 이직률이 더 높음

# 부서별 이직률 현황
df_gp3 = df.groupby('Department')['Attrition'].agg(['count','sum'])
df_gp3['ratio'] = round(df_gp3['sum']/df_gp3['count']*100, 1) # 반올림
df_gp3
# Sales 이직률이 더 높음

# 인간관계 만족도, 업무 만족도와 이직 간의 상관관계 확인
df[['JobSatisfaction', 'RelationshipSatisfaction', 'Attrition']].head()

# 업무만족도별 이직률 현황
df_gp4 = df.groupby('JobSatisfaction')['Attrition'].agg(['count','sum'])
df_gp4['ratio'] = round(df_gp4['sum']/df_gp4['count']*100, 1) # 반올림
df_gp4
# 업무만족도가 낮을수록 이직률이 높음

# 인간관계만족도, 만족도 / 이직간의 상관관계 확인
df_gp5 = df.groupby(['JobSatisfaction', 'RelationshipSatisfaction'])['Attrition'].agg(['count','sum'])
df_gp5['ratio'] = round(df_gp5['sum']/df_gp5['count']*100, 1) # 반올림
df_gp5
# 업무만족도가 높은 직원은 인간관계에 따라 이직률에 영향을 덜 받는다
# 업무만족도가 낮은 직원은 인간관계에 따라 이직률이 증가하는 경향을 보인다


# 근속년수 대비 한가지 이상 역할을 한 비중
df['Role_Company'] = df['YearsInCurrentRole']/df['YearsAtCompany']
df['Role_Company'] # 0/0 Nan으로 출력되기 때문에 처리 필요

df['Role_Company'].fillna(0, inplace=True)

# Role_Company 분포 확인
# 데이터의 분포를 시각화 -> distplot(히스토그램과 커널밀도추정(KDE)를 함께 그려서 데이터 분포 나타내기)
sns.distplot(df['Role_Company'])

# 구간화
df['Role_Company_gp'] = np.where(df['Role_Company'] <= 0.3, '0.3 이하',
np.where(df['Role_Company'] <= 0.6, '0.3-0.6', '0.6 초과'))
df['Role_Company_gp']

df_gp6 = df.groupby('Role_Company_gp')['Attrition'].agg(['count','sum'])
df_gp6['ratio'] = round(df_gp6['sum']/df_gp6['count']*100, 1) # 반올림
df_gp6

# 야근 여부에 따른 이직률 현황
df_gp7 = df.groupby('OverTime')['Attrition'].agg(['count','sum'])
df_gp7['ratio'] = round((df_gp7['sum']/df_gp7['count'])*100, 1)
df_gp7
# 야근을 하는 사람이 이직률이 높다

# PercentSalaryHike : 연봉 인상률
df_gp7 = df.groupby(['OverTime','PercentSalaryHike'])['Attrition'].agg(['count','sum'])
df_gp7['ratio'] = round((df_gp7['sum']/df_gp7['count'])*100, 1)
df_gp7

# 야근을 안하는 사람들의 연봉인상에 따른 이직률 평균계산
df_gp7.loc['No']['ratio'].mean()
# 야근을 안 하는 직원은 연봉 인상률과 관계 없이 평균(16%)보다 낮은 편이다

# 야근을 하는 사람들을 분석 (bar chart 시각화)
df_gp7.loc['Yes']['ratio'].plot(kind='bar', ylabel='ratio', rot=45, colormap='Pastel2')

# 문제와 답 분리
df_x = df.drop(['Attrition','Age_gp', 'Role_Company', 'Role_Company_gp'],axis=1)
y = df['Attrition']

# 숫자형태의 데이터와 글자형태의 데이터 분리
x_types = df_x.dtypes == 'O' # 대문자 O(Object)
x_types


categorical_list = list(x_types[x_types == True].index) # object 형태 데이터
numeric_list = list(x_types[x_types == False].index) # 숫자 형태 데이터

# 출장의 빈도에 따라 이직률에 영향을 주지 않을까?
df_x['BusinessTravel'].unique()

# 레이블인코딩
df_x['BusinessTravel'] = df_x['BusinessTravel'].map({'Non-Travel':0, 'Travel_Rarely':1, 'Travel_Frequently':2})

# 원핫인코딩 연습
x_one_hot = pd.get_dummies(df_x[categorical_list])
x_one_hot
# 숫자 타입 데이터는 알아서 거름

# T/F 숫자로 변경
x_one_hot = pd.get_dummies(df_x[categorical_list], dtype="int32")
x_one_hot

# 숫자 타입 컬럼과 인코딩된 컬럼 병합
X = pd.concat([df_x[numeric_list], x_one_hot], axis=1)
X.shape, y.shape

# 훈련용데이터와 평가용데이터 분리
# train_test_split(문제, 정답, 분리비율, 고정키)
X_train, X_test, y_train, y_test = train_test_split(X, y, # 문제와 정답
test_size=0.3, # 테스트데이터의 비율
random_state=819, # 같은 샘플 추출을 위한 난수 고정
stratify=y) # 훈련용과 평가용의 정답 클래스 비율유지
# stratify : 기존 데이터를 나눌 때 래스 분포 비율까지 맞춰 준다
# 선형분류 모델활용 학습
# 1. 모델객체 생성
attrition_logi = LogisticRegression()
attrition_svm = LinearSVC() # random_state로 고정하고 튜닝하여 원하는 최종모델로 만들어가는 것
# 2. 모델학습
attrition_logi.fit(X_train, y_train)
attrition_svm.fit(X_train, y_train)
# 3. 모델 예측
logi_pre = attrition_logi.predict(X_test)
svm_pre = attrition_svm.predict(X_test)
# 4. 모델 평가 (정확도)
print("Logi accuracy :", metrics.accuracy_score(y_test, logi_pre))
print("SVM accuracy :", metrics.accuracy_score(y_test, svm_pre))


cross_val_score(attrition_logi, X_train, y_train, cv=5) # cv : 검증횟수

# 로지스틱 모델의 교차검증 결과
cross_val_score(attrition_logi, X_train, y_train, cv=5).mean()

# SVM 모델의 교차검증 결과
cross_val_score(attrition_svm, X_train, y_train, cv=5)

-> SVM 모델은 민감하다
-> random_state를 난수 고정하여 시행하는 게 좋다


1) 훈련 데이터 성능 good, 평가 데이터 성능 bad : 과대적합
2) 훈련 데이터 성능 bad, 평가 데이터 성능 bad : 과소적합
3) 훈련 데이터 성능 good, 평가 데이터 성능 good : 일반화

파라미터를 조정하여 복잡도를 변경함.
위의 예시사진에서 왼쪽이 가장 단순하고, 오른쪽이 가장 복잡하고, 가운데가 적당한 복잡도를 가지고 있다.
적당한 복잡도를 가진 일반화된 모델을 유도해야 한다.
모델이 너무 단순하면 과소적합에 빠질 확률이 높다. 훈련이 적어 학습능력이 떨어짐.
모델이 너무 복잡하면 훈련데이터의 케이스에 잘 예측하나 테스트데이터에 대한 정확도가 떨어져 과대적합에 빠질 확률이 높다.

# 규제 파라미터 값을 모은 리스트
C_list = [10000, 1000, 100, 10, 1, 0.1, 0.01, 0.001, 0.0001] # 규제가 약한 것부터 강한 것까지
# 훈련용데이터 평가점수를 담을 리스트
train_list = []
# 평가용데이터 평가점수를 담을 리스트
test_list = []
for my_C in C_list :
logi2 = LogisticRegression(C=my_C) # 선형분류 모델객제 생성 # C=1.0 (Default)
logi2.fit(X_train, y_train) # 훈련용데이터 학습
pre_train = logi2.predict(X_train) # 훈련용데이터 예측
score_train = metrics.accuracy_score(y_train, pre_train) # 정확도 계산(실제값, 예측값)
train_list.append(score_train) # 리스트에 훈련용데이터 점수 추가
pre_test = logi2.predict(X_test) # 평가용데이터 예측
score_test = metrics.accuracy_score(y_test, pre_test) # 정확도 계산(실제값, 예측값)
test_list.append(score_test) # 리스트에 평가용데이터 점수 추가
# 튜닝결과 시각화
plt.figure(figsize=(10,5)) # 가로, 세로 비율
plt.plot(train_list) # 훈련용데이터 라인그래프
plt.plot(test_list) # 평가용데이터 라인그래프
plt.xticks(range(len(C_list)), C_list) # X축 눈금 지정
plt.show() # 그래프 보여주기

# 규제 파라미터 값을 모은 리스트
C_list = [10000, 1000, 100, 10, 1, 0.1, 0.01, 0.001, 0.0001] # 규제가 약한 것부터 강한 것까지
# 훈련용데이터 평가점수를 담을 리스트
train_list = []
# 평가용데이터 평가점수를 담을 리스트
test_list = []
for my_C in C_list :
svc = LinearSVC(C=my_C, random_state=1122) # 선형분류 모델객제 생성 # C=1.0 (Default)
svc.fit(X_train, y_train) # 훈련용데이터 학습
pre_train = svc.predict(X_train) # 훈련용데이터 예측
score_train = metrics.accuracy_score(y_train, pre_train) # 정확도 계산(실제값, 예측값)
train_list.append(score_train) # 리스트에 훈련용데이터 점수 추가
pre_test = svc.predict(X_test) # 평가용데이터 예측
score_test = metrics.accuracy_score(y_test, pre_test) # 정확도 계산(실제값, 예측값)
test_list.append(score_test) # 리스트에 평가용데이터 점수 추가
# 튜닝결과 시각화
plt.figure(figsize=(10,5)) # 가로, 세로 비율
plt.plot(train_list) # 훈련용데이터 라인그래프
plt.plot(test_list) # 평가용데이터 라인그래프
plt.xticks(range(len(C_list)), C_list) # X축 눈금 지정
plt.show() # 그래프 보여주기

# 규제 파라미터 값을 모은 리스트
C_list = [10000, 1000, 100, 10, 1, 0.1, 0.01, 0.001, 0.0001] # 규제가 약한 것부터 강한 것까지
# 교차검증 평가점수를 담을 리스트
cross_val_list = []
# 평가용데이터 평가점수를 담을 리스트
test_list = []
for my_C in C_list :
svc = LinearSVC(C=my_C, random_state=1122)
cv_score = cross_val_score(svc, X_train, y_train, cv=5).mean() # 교차검증 점수평균
cross_val_list.append(cv_score) # 교차검증 점수평균을 리스트에 추가
svc.fit(X_train, y_train) # 모델학습
pre_test = svc.predict(X_test) # 평가용데이터 예측
score_test = metrics.accuracy_score(y_test, pre_test) # 정확도 계산(실제값, 예측값)
test_list.append(score_test) # 리스트에 평가용데이터 점수 추가
# 튜닝결과 시각화
plt.figure(figsize=(10,5)) # 가로, 세로 비율
plt.plot(cross_val_list) # 교차검증 평가점수 라인그래프 그리기
plt.plot(test_list) # 평가용데이터 라인그래프
plt.xticks(range(len(C_list)), C_list) # X축 눈금 지정
plt.show() # 그래프 보여주기

정확도



정확도는 95%이나 재현율은 5%

재현율은 100% (실제 양성클래스는 맞추었으나) (양성클래스라고 추측한 것 중에 실제로 맞춘 것) 정밀도 5%
# 튜닝이 끝난 모델
logi3 = LogisticRegression(C=1)
svc2 = LinearSVC(random_state=1122, C=0.01)
# 모델학습
logi3.fit(X_train, y_train)
svc2.fit(X_train, y_train)
# 모델예측
logi_pre = logi3.predict(X_test)
svc_pre = svc2.predict(X_test)
# 다양한 분류평가지표로 검증
print(classification_report(y_test, logi_pre))
print(classification_report(y_test, svc_pre))
