
简介这份资源面向希望上手机器学习实战的初学者与数据分析从业者围绕员工绩效与薪资数据集提供从数据预处理、特征工程到建模预测的完整分析链路帮助读者理解回归任务在真实业务场景中的落地方式。压缩包共23个文件以20个Python源代码为主另含1个CSV数据集、1份PDF任务说明与1份readme文本整体约266KB代码与数据分离存放便于按步骤复现。代码覆盖numpy、pandas、seaborn、matplotlib等数据处理与可视化模块并调用sklearn的MinMaxScaler、train_test_split、RandomForestRegressor与mean_squared_error完成归一化、切分、随机森林回归及误差评估同时涉及warnings、os、datetime等辅助模块。已有67人学习读者可借此掌握数据合并、探索性分析、模型训练与效果评估的完整流程并参考PDF中的任务清单逐项练习适合作为课程作业或自学项目的实操素材。1. 员工绩效数据集分析预测从 273.98 KB 的 Excel 到可解释的离职风险评分很多团队第一次做员工绩效预测都是从一个几十万字节的 Excel 开始的——273.98 KB20 个源代码文件字段无非是工号、部门、绩效评分、加班时长、满意度、离职标签。数据量不大但坑一点不少类别不平衡、特征泄漏、评分口径不统一随便踩一个都能让模型 AUC 虚高到 0.99上线后直接翻车。这个标题讲的不是「用 AI 预测员工绩效」这种空话而是一套能跑通、能解释、能复现的最小闭环把这份数据集读进来做特征工程训练一个可解释的模型输出每个员工的绩效等级或离职风险分并且知道哪些特征在真正起作用。适合两类人一是手里有类似 HR 数据集、想快速验证可行性的数据工程师二是需要向业务方解释「模型为什么这么判」的算法从业者。下面按数据探查、特征处理、模型训练、避坑、进阶验证五步走代码可以直接抄。2. 数据探查与字段口径对齐先搞清楚 273.98 KB 里到底有什么拿到数据集的第一件事不是read_csv而是先确认字段含义和分布。员工绩效类数据集通常包含三类字段身份类工号、部门、职位、行为类加班时长、项目数、考勤、结果类绩效评分、离职标签。273.98 KB 的体量大概对应几百到一千行、十几到二十列属于典型的小样本表格数据。小样本最大的风险是「看起来能跑其实在背答案」所以探查阶段必须把每列的取值分布、缺失率、类别数全部打出来。2.1 用 pandas 做一次完整的数据体检import pandas as pd import numpy as np # 读取数据集注意编码HR 系统导出的 Excel 常见 gbk 或 utf-8-sig df pd.read_csv(employee_performance.csv, encodingutf-8-sig) # 基础信息行数、列数、每列类型、非空数量 print(shape:, df.shape) print(df.info()) # 缺失率排序优先处理缺失超过 30% 的列 missing df.isnull().mean().sort_values(ascendingFalse) print(missing[missing 0]) # 类别型字段的取值分布重点看部门、职位、绩效等级 for col in df.select_dtypes(includeobject).columns: print(f\n{col} 取值数: {df[col].nunique()}) print(df[col].value_counts().head(10)) # 数值型字段的描述统计关注加班时长、满意度这类可能有异常值的列 print(df.describe().T[[mean, std, min, 50%, max]])这段代码的逻辑是先看整体形状和类型再分别处理缺失、类别分布、数值分布。参数上encoding一定要试HR 系统导出的文件经常是gbk用默认utf-8会直接报UnicodeDecodeError。value_counts().head(10)是为了防止某个字段有上百个取值把输出刷屏。重点看两个信号一是绩效等级或离职标签是否严重不平衡比如离职只占 5%二是加班时长、满意度这类字段有没有明显超出合理范围的异常值比如满意度出现 0 到 1 之外的数。2.2 字段口径对齐绩效评分到底是谁打的这一步最容易被跳过但它是后面所有工作的地基。员工绩效数据集里「绩效评分」可能来自三种口径自评、上级评、系统综合分。如果数据集里同时有多个评分列必须先确认用哪个作为标签。常见做法是看列名后缀比如performance_score_self、performance_score_manager然后和业务方确认预测目标。如果目标是预测「最终绩效等级」那就用综合分或上级评分如果目标是预测「离职风险」那绩效评分应该作为特征而不是标签。这个区分决定了后面是分类任务还是回归任务也决定了特征里能不能放绩效评分——放了就是特征泄漏模型准确率会虚高到没有意义。提示如果数据集里没有明确的标签列只有绩效评分可以把评分离散化成「高/中/低」三档做分类阈值用分位数而不是固定值避免业务口径变化导致标签漂移。3. 特征工程与类别不平衡处理让模型学到真信号而不是背答案探查完之后真正决定模型上限的是特征工程。员工绩效数据集的原始字段通常很粗糙部门是字符串、职位是字符串、加班时长是连续值、满意度是 0 到 1 的小数。直接丢给模型不是不行但树模型对类别编码敏感线性模型对量纲敏感所以这一步要做三件事类别编码、数值分箱、不平衡处理。3.1 类别特征编码独热还是目标编码部门、职位这类低基数类别取值小于 15 个用独热编码就够了高基数类别比如岗位名称有几十上百种用目标编码或频率编码更合适。员工绩效数据集里部门通常不超过 10 个职位可能多一点但也不会太夸张。from sklearn.preprocessing import OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 低基数类别列独热编码 low_card_cols [department, gender, marital_status] # 高基数类别列用频率编码替代避免维度爆炸 high_card_cols [job_role, education_field] # 频率编码用取值出现的频率替换原始类别 for col in high_card_cols: freq df[col].value_counts(normalizeTrue) df[col _freq] df[col].map(freq) # 构建预处理管道 preprocessor ColumnTransformer( transformers[ (onehot, OneHotEncoder(handle_unknownignore), low_card_cols), (num, passthrough, [age, tenure_years, overtime_hours, satisfaction_score, job_role_freq, education_field_freq]) ], remainderdrop )这段代码的关键点是handle_unknownignore它保证线上出现训练集没见过的部门时不会报错而是编码成全零向量。频率编码用value_counts(normalizeTrue)得到的是比例而不是计数这样不同规模的数据集之间可以复用。remainderdrop是显式丢弃没列出的列防止不小心把工号这种 ID 列带进模型——ID 列进模型是典型的噪声来源树模型可能会用它做无意义的切分。3.2 类别不平衡不要只会 SMOTE员工绩效数据集里高绩效和离职标签通常是不平衡的。比如离职率 10%高绩效占比 15%。很多人第一反应是上 SMOTE 过采样但小样本数据上 SMOTE 容易生成不真实的合成样本导致模型在真实数据上表现下降。更稳的做法是先用class_weightbalanced如果效果不够再考虑欠采样或组合采样。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, roc_auc_score # 假设标签列是 attrition1 表示离职 X df.drop(columns[employee_id, attrition]) y df[attrition] # 分层切分保证训练集和测试集里正负样本比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) # 用 class_weight 处理不平衡比 SMOTE 更保守 clf Pipeline([ (prep, preprocessor), (model, RandomForestClassifier( n_estimators300, max_depth8, class_weightbalanced, random_state42 )) ]) clf.fit(X_train, y_train) y_prob clf.predict_proba(X_test)[:, 1] print(AUC:, roc_auc_score(y_test, y_prob)) print(classification_report(y_test, clf.predict(X_test)))参数上stratifyy是必须的否则小样本下测试集可能一个正样本都没有。max_depth8是防止树长得太深记住训练集小样本数据上树深超过 10 基本就开始过拟合了。class_weightbalanced会自动按类别频率反比加权效果通常比 SMOTE 稳定而且没有合成样本带来的分布偏移问题。如果 AUC 在 0.75 到 0.85 之间说明模型学到了真信号如果超过 0.95先检查有没有特征泄漏尤其是绩效评分、离职日期这类字段。3.3 特征重要性业务方只信能解释的模型训练完模型业务方第一个问题一定是「哪些因素在影响绩效」。树模型自带feature_importances_但更推荐用 SHAP 值因为它能给出每个样本每个特征的贡献方向。import shap # 取预处理后的特征名 feature_names (clf.named_steps[prep] .get_feature_names_out()) # 用树模型解释器 explainer shap.TreeExplainer(clf.named_steps[model]) # 对测试集做解释注意要先 transform X_test_transformed clf.named_steps[prep].transform(X_test) shap_values explainer.shap_values(X_test_transformed) # 输出全局重要性前 10 shap.summary_plot(shap_values[1], X_test_transformed, feature_namesfeature_names, plot_typebar)这段代码里shap_values[1]取的是正类离职的贡献值。summary_plot的plot_typebar输出全局重要性排序去掉这个参数会输出蜂群图能看到每个特征对每个样本的正负影响。实际用的时候如果发现overtime_hours排第一且方向为正说明加班越多离职风险越高这个结论业务方容易接受如果发现某个编码后的类别特征排第一要回去检查是不是编码方式引入了虚假相关。4. 模型训练与验证小样本下怎么判断模型是真的能用小样本表格数据的验证不能只看一次train_test_split的结果因为随机切分带来的方差可能比模型之间的差异还大。正确做法是交叉验证加多指标评估同时留一个时间维度的切分做最终验证如果数据里有时间字段。4.1 交叉验证用 StratifiedKFold 而不是 KFoldfrom sklearn.model_selection import StratifiedKFold, cross_val_score cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) # 用 AUC 做评估指标因为不平衡数据下准确率没有意义 scores cross_val_score(clf, X, y, cvcv, scoringroc_auc) print(AUC 均值: %.4f, 标准差: %.4f % (scores.mean(), scores.std()))StratifiedKFold保证每一折里正负样本比例和整体一致shuffleTrue打乱顺序避免数据按某个隐藏维度排序。scoringroc_auc是因为不平衡数据下准确率会被多数类主导一个全预测「不离职」的模型准确率也有 90%但 AUC 只有 0.5。标准差很重要如果标准差超过 0.05说明模型在不同折之间表现不稳定需要检查特征或增加数据。4.2 时间维度验证如果有入职日期或评估日期如果数据集里有时间字段比如hire_date或review_date一定要做一次时间切分验证用早期数据训练用后期数据测试。这是最接近线上真实场景的验证方式。# 假设有 review_year 字段 train_mask df[review_year] 2023 test_mask df[review_year] 2023 X_train_t, y_train_t X[train_mask], y[train_mask] X_test_t, y_test_t X[test_mask], y[test_mask] clf.fit(X_train_t, y_train_t) y_prob_t clf.predict_proba(X_test_t)[:, 1] print(时间切分 AUC:, roc_auc_score(y_test_t, y_prob_t))时间切分的结果通常会比随机切分低 0.05 到 0.15这是正常的因为线上数据分布会漂移。如果时间切分 AUC 掉到 0.6 以下说明模型学到的规律不稳定需要重新审视特征工程尤其是那些和时间强相关的特征比如工龄、司龄。4.3 模型对比不要只试一个算法小样本数据上逻辑回归、随机森林、梯度提升树的表现差异可能不大但可解释性和训练速度差异明显。建议至少对比三个模型用同一套交叉验证。模型优点缺点小样本建议逻辑回归可解释性强训练快对非线性关系拟合弱先跑 baseline随机森林抗过拟合特征重要性直观小样本下方差大限制树深XGBoost/LightGBM精度通常最高参数多容易过拟合强正则化实际项目中我一般先用逻辑回归跑一个 baseline如果 AUC 已经到 0.75 以上说明特征工程做到位了再用随机森林或 LightGBM 提升 3 到 5 个点。如果逻辑回归只有 0.6换复杂模型也救不回来问题在特征不在模型。5. 避坑与排查员工绩效预测里最容易翻车的 5 个地方这一章是血泪经验合集。员工绩效数据集看起来简单但每个字段背后都有业务含义不理解业务直接跑模型结果就是「离线指标漂亮上线没人用」。5.1 现象AUC 高到 0.98业务方不信原因特征泄漏。最常见的是把绩效评分、离职日期、离职面谈记录放进了特征。绩效评分如果是标签就不能同时做特征离职日期如果已知那模型只是在背答案。解决训练前做一次特征审计把所有和标签在时间上重叠的字段列出来逐个确认是否在预测时点可得。预测离职风险时只能用员工当前和历史的行为数据不能用未来的结果数据。5.2 现象模型在测试集上很好换一批数据就崩原因过拟合加分布漂移。小样本数据上模型很容易记住训练集的噪声同时不同部门的绩效评分口径可能不同训练集里某个部门的样本多模型就偏向那个部门的规律。解决做部门维度的分组交叉验证用GroupKFold按部门分组确保每个部门都在测试集里出现过。如果某个部门的 AUC 明显低于其他部门说明模型对该部门不公平需要单独建模或增加该部门的样本权重。5.3 现象特征重要性里工号排第一原因工号被当成了数值特征。工号是随机分配的和绩效没有因果关系但如果它恰好和某个隐藏变量相关比如入职顺序树模型就会用它做切分。解决训练前显式丢弃所有 ID 类字段包括工号、员工编号、记录 ID。在ColumnTransformer里用remainderdrop或者显式指定drop列。5.4 现象加班时长和绩效的关系时正时负原因多重共线性或交互效应。加班时长单独看可能和绩效正相关加班多的人产出多但和满意度一起看又负相关加班多的人满意度低绩效反而下降。模型在不同折里学到不同的关系。解决检查特征之间的相关性矩阵相关系数超过 0.7 的成对特征只保留一个或者用 PCA 降维。更简单的做法是构造交互特征比如overtime_hours * satisfaction_score让模型显式学到这种关系。5.5 现象模型预测全是「不离职」原因类别极度不平衡加阈值未调整。如果离职率只有 5%模型默认阈值 0.5 会导致所有样本都被判为负类。解决不要用默认阈值用验证集上的 F1 或召回率来选阈值。或者直接用predict_proba输出概率让业务方按概率排序处理而不是二分类。注意员工绩效数据涉及个人隐私做分析和建模时务必脱敏工号、姓名、身份证号等字段在进入模型前就要替换成匿名 ID输出结果只保留聚合结论不要落到个人。6. 进阶技巧用 SHAP 单样本解释做绩效面谈辅助模型跑通之后真正有价值的不是那个 AUC 数字而是能不能对每个员工给出「为什么他绩效高/低」的解释。SHAP 的单样本解释可以做到这一点对某个员工输出每个特征对他预测结果的贡献值正贡献表示拉高绩效负贡献表示拉低绩效。这个能力可以直接用在绩效面谈里让管理者有数据支撑地沟通而不是凭感觉。# 对测试集里第 5 个员工做单样本解释 idx 5 single_shap explainer.shap_values(X_test_transformed[idx:idx1]) # 输出每个特征的贡献值按绝对值排序 contributions pd.DataFrame({ feature: feature_names, shap_value: single_shap[1][0] }) contributions[abs_shap] contributions[shap_value].abs() contributions contributions.sort_values(abs_shap, ascendingFalse) print(contributions.head(10))这段代码输出的是该员工每个特征对离职风险的贡献。shap_value为正表示增加离职风险为负表示降低。实际用的时候我会把feature_names映射回业务可读的名称比如overtime_hours显示成「月均加班时长」然后取绝对值前 5 个特征生成一段话「该员工离职风险主要受加班时长0.12和满意度-0.08影响建议关注工作负荷。」这种解释比一个冷冰冰的概率值有用得多。验证解释是否靠谱的方法很简单找几个已知结果的员工看 SHAP 解释是否符合业务直觉。如果模型说「加班多导致离职风险高」但该员工实际是主动离职去创业那解释就是错的需要检查特征或模型。我一般会抽 10 个样本人工核对准确率超过 7 个才敢把解释交给业务方。还有一个实用技巧是「反事实解释」对某个高风险员工找到需要改变哪些特征才能把风险降到阈值以下。比如「如果满意度从 0.4 提升到 0.7离职风险从 0.65 降到 0.35」。这个用 SHAP 的force_plot或者简单的特征扰动都能做但要注意反事实解释只能作为参考不能承诺「改了就一定不走」。最后说一个我自己的习惯每次做完员工绩效预测我都会把模型的特征重要性排序和业务方对齐一次问他们「这个排序符合你们的直觉吗」。如果业务方说「加班时长不可能排第一」那要么是数据有问题要么是业务理解有偏差两种都值得深挖。模型不是用来替代判断的是用来暴露那些被忽略的信号的。希望帮到你。本文还有配套的精品资源点击获取