墨西哥的员工流动率在全球排名第八,平均每年约17%的流失率 - 一些行业(如餐饮服务)的流失率高达50%。 根据Catalyst的一项研究,平均而言,替换员工的成本约为员工年薪的50%至75%。 考虑到月薪为2万比索的中级职位,替换这名员工的总费用约为14万比索。 平均而言,替换员工需要大约50天的时间,由于生产力损失而产生的成本将持续增加。 对于像everis这样的拥有超过2万名员工的大公司来说,考虑到15%的流失率和15,000比索的平均薪水,年营业额总成本将上升至至少2.7亿比索。
在本文中,我们提供了一个神经网络模型的详细信息,该模型能够识别具有高度人员流动风险的员工候选人,并以大约96%的准确率完成此任务。
方法论
我们使用了由IBM数据科学家创建的虚拟数据集“HR Employee Attrition and Performance”(https://community.watsonanalytics.com/wp-content/uploads/2015/03/WA_Fn-UseC_-HR-Employee-Attrition.xlsx)。 它包含1,470行员工历史数据。
经过探索性数据分析我们确定了一些与员工流动率相关性最高的特征。 这些是我们发现的最重要的特征:
年龄
与家的距离
加班情况
教育状况
婚姻状况
工作过的公司数量
总工作年限
月收入
这些特征用于训练模型以预测流失风险。 该数据集已经包含一个称为“attrition”的功能,该功能表明该员工是否会离开该职位并需要更换。 该特征是一种热点编码(将数据分解为训练和测试集后显示),并被用作神经网络预测的目标。 以下是用于单热编码的辅助函数:
由于数据集的不平衡性(员工流动率约占总人口的16%,或1,470人中的237人),因此采用上采样技术来重复更换营业案例 - 所以数据中有1,233个离职的个案和1,233个没有离职的个案。
对数据集进行上采样可以避免模型学习每次预测“没有离职”的情况; 在这种情况下,通过这样做可以达到大约84%的精度(这个精度可以作为我们的基准)。
接下来,使用StandardScaler将数据归一化到-1到1的范围,以避免异常值以不成比例的方式影响预测。
class standard_scaler:
def __init__(self, name):
self.name = name # candidato o empleado
self.scalers = {} # asignar cada scaler con el nombre de la columna (ej.'Age')
def add_scaler(self, scaler, name):
self.scalers[name] = scaler
# Initialize a standard_scaler class to hold all scalers for future reverse scaling
scalers_empleados = standard_scaler('empleados')
def scale_and_generate_scaler(data):
standard_scaler = StandardScaler()
scaled = standard_scaler.fit_transform(data.astype('int64').values.reshape(-1, 1))
return scaled, standard_scaler
def scale_array(scaler, array):
return scaler.transform([array])
def inverse_scale_array(scaler, array):
return scaler.inverse_transform([array])
def scale_append(data, scalers, name):
scaled, scaler = scale_and_generate_scaler(data[name])
scalers.add_scaler(scaler, name)
return scaled, scalers
# Select features to scale
var_empleados_num = [
'Age',
'BusinessTravel_Num',
'DistanceFromHome',
'EnvironmentSatisfaction',
'JobInvolvement',
'JobSatisfaction',
'MonthlyIncome',
'OverTime_Num',
'YearsAtCompany',
'WorkLifeBalance',
'Education',
'MaritalStatus_Num',
'NumCompaniesWorked',
'RelationshipSatisfaction',
'TotalWorkingYears'
]
# Scale each feature, save each feature's scaler
for var in var_empleados_num:
data_empleados_upsampled[var], scalers_empleados = scale_append(data_empleados_upsampled, scalers_empleados, var)
准备好数据后,将其随机分为训练数据(80%)和测试数据(20%),使用随机种子进行重现性。
X_train, X_test = train_test_split(data_empleados_upsampled, test_size=0.2, random_state=RANDOM_SEED)
X_train = data_empleados_upsampled
y_train = X_train['Attrition_Num']
X_train = X_train.drop(['Attrition_Num', 'Attrition', 'BusinessTravel', 'OverTime', 'MaritalStatus', 'YearsAtCompany'], axis=1)
y_test = X_test['Attrition_Num']
X_test = X_test.drop(['Attrition_Num', 'Attrition', 'BusinessTravel', 'OverTime', 'MaritalStatus','YearsAtCompany'], axis=1)
X_train = X_train.values
X_test = X_test.values
# One-Hot encoding
y_train_hot, label_encoder = one_hot_values(y_train)
y_test_hot, label_encoder_test = one_hot_values(y_test)
然后,使用Keras建立神经网络。 其架构如下:
它是一个简单的三层神经网络,在最后一层有两个神经元,根据用作模型目标的单热编码信息预测正确的类; 没有attrition = [0,1]和attrition = [0,1]。
使用随机梯度下降优化器,学习率为0.01,批量大小为64,分类错误的损失函数。 它经过200个周期的训练,实现了96.15%的验证准确率(与始终预测离职率的基线为84%相比)。
每个预测的输出是大小为2的数组; 该数组的元素总和为1.为了提取预测的类,从数组中取出最高元素。 如果第一个元素较大,则预测的类不会有attrition。 同样,如果第二个元素大于第一个元素,预测的类就会有attrition。 函数inverse_one_hot()用于从单热编码模型预测中获取预测类。 这是一个例子:
以下是如何对自定义配置文件执行预测的示例; 比方说,一个潜在的候选人的工作:
建立了一个类似的神经网络来预测YearsAtCompany,这个变量对应于该公司员工预期持续的总年数。 除了最后一层以外,架构是相同的,它有五个神经元而不是两个来预测0,1,2,4或10年。
它训练了350个时期,并且获得了82.52%的准确性(相比于随机猜测的基线20%,1/5的机会;或总是预测四年的22%)。
这里有一个功能可以深入了解模型的性能:
def evaluate_model(model, X_train):
preds = model.predict(X_train)
y_train_inv = [inverse_one_hot(label_encoder_train,np.array([list(y_train_hot[i])])) for i in range(len(y_train_hot))]
#y_train_inv = np.round(scalers_empleados_yac.scalers['YearsAtCompany'].inverse_transform([y_train_inv]))
y_train_inv = np.array(y_train_inv).flatten()
preds_inv = [inverse_one_hot(label_encoder_train,np.array([list(preds[i])])) for i in range(len(preds))]
#preds_inv = scalers_empleados_yac.scalers['YearsAtCompany'].inverse_transform([preds_inv])
preds_inv = np.array(preds_inv).flatten()
correct = 0
over = 0
under = 0
errors = []
for i in range(len(preds_inv)):
if preds_inv[i] == y_train_inv[i]:
correct += 1
elif (preds_inv[i]) < (y_train_inv[i]):
under += 1
elif (preds_inv[i]) > (y_train_inv[i]):
over += 1
errors.append(((preds_inv[i]) - (y_train_inv[i])))
print("correct: {}, over {}, under {}, accuracy {}, mse {}".format(correct, over, under, round(correct/len(preds_inv),3), np.round(np.array(np.power(errors,2)).mean(),3)))
print("errors:",pd.Series(np.array([abs(i) for i in errors if i != 0])).describe())
print("preds",pd.Series(preds_inv).describe())
print("y_train",pd.Series(y_train_inv).describe())
print(pd.Series(preds_inv).value_counts())
print(pd.Series(y_train_inv).value_counts())
print(sns.boxplot(np.array([abs(i) for i in errors if i != 0])))
return y_train_inv, preds_inv
实现和结果
前端设计为用户输入候选人的特征,两种模型都会产生一个结果:首先确定候选人是否有交易风险,然后预测候选人将被预测多少年保持潜在职位..
还会创建一个图表,以比较候选人特征的标准化值与数据集中所有员工的平均值。 这用于查看哪些特征与正常值的差异最大。
结论
还有工作要做。用来自墨西哥公司的真实数据测试这些模型会很好。此外,与离职率风险具有较高相关性的特征可能会有所不同。此外,还可以添加其他特征,例如每月食品杂货和住房费用,以及根据行业和候选人适用的职位进行更详细的分析。
公司预测的预期年份也可能更具体一些,尤其是前两年。目前,该模型只能预测一年或另一年,但也许预测数月而不是数年是值得使用更多信息来区分候选人的。
尽管如此,招聘人员可以从这些工具中受益匪浅。他们可以掌握客观的信息,做出更明智的决策,如果候选人的流失风险很高,至少可以直接与候选人讨论双方如何获益。有了这些工具和新的策略来打击流失率,世界各地的公司可以显着减少流失率,潜在增加数百万的收入。