从 51 问看机器学习面试:5 类必考编程题与 Python 实现要点
机器学习面试编程实战5类核心问题与Python解决方案1. 数据预处理缺失值与异常值处理数据预处理是机器学习项目中最耗时的环节也是面试官重点考察的实战能力。在真实业务场景中约60%的数据科学时间都花费在数据清洗和特征工程上。Pandas高效处理缺失值技巧import pandas as pd import numpy as np # 创建含缺失值的示例数据 data {年龄: [25, np.nan, 32, 28, 45, np.nan], 收入: [50000, 62000, np.nan, 58000, np.nan, 70000], 购买记录: [1, 0, 3, np.nan, 2, 1]} df pd.DataFrame(data) # 缺失值分析 print(缺失值统计:\n, df.isnull().sum()) # 填充策略 df_filled df.copy() df_filled[年龄] df[年龄].fillna(df[年龄].median()) # 数值型用中位数 df_filled[收入] df[收入].fillna(df[收入].mean()) # 连续变量用均值 df_filled[购买记录] df[购买记录].fillna(0) # 离散变量用0填充 # 删除缺失率过高的样本 df_dropped df.dropna(threshlen(df.columns)-1) # 保留至少n-1个特征的样本常见陷阱与解决方案数据泄露在填充缺失值时使用整个数据集统计量如全局均值会导致测试集信息污染正确做法仅在训练集计算统计量应用到验证/测试集异常值检测的三种方法IQR法Q1 - 1.5*IQR和Q3 1.5*IQR之外的值Z-score法绝对值大于3的标准分数隔离森林适用于高维数据提示在时间序列数据中应采用前向填充ffill而非均值填充以保持时间依赖性2. 特征工程编码与标准化特征工程的质量直接决定模型性能上限。面试中常要求手写特征转换代码考察对数据分布的理解。分类变量编码对比编码方式适用场景优缺点Python实现One-Hot类别数量10维度爆炸丢失类别关系pd.get_dummies()Target Encoding高基数类别(100个类别)可能引入目标泄露category_encoders库Embedding深度学习模型需要预训练或端到端学习tf.keras.layers.Embedding数值特征标准化代码from sklearn.preprocessing import StandardScaler, MinMaxScaler # 标准化(Z-score) scaler StandardScaler() X_train_std scaler.fit_transform(X_train) X_test_std scaler.transform(X_test) # 注意使用训练集的均值和方差 # 归一化(MinMax) minmax_scaler MinMaxScaler(feature_range(0, 1)) X_train_minmax minmax_scaler.fit_transform(X_train) # 鲁棒缩放(适用于有异常值) from sklearn.preprocessing import RobustScaler robust_scaler RobustScaler(quantile_range(25, 75)) X_train_robust robust_scaler.fit_transform(X_train)高频面试问题什么情况下需要做特征缩放距离度量算法KNN、SVM梯度下降算法如何处理数值特征的偏态分布对数变换、Box-Cox变换为什么测试集要使用训练集的缩放参数避免数据泄露3. 基础算法实现从零编写KNN手写经典算法是考察编程能力和数学基础的常见方式。以下是KNN的Python实现及优化技巧import numpy as np from collections import Counter from sklearn.metrics import accuracy_score class KNN: def __init__(self, k5, distance_metriceuclidean): self.k k self.metric distance_metric def _calculate_distance(self, x1, x2): if self.metric euclidean: return np.sqrt(np.sum((x1 - x2)**2)) elif self.metric manhattan: return np.sum(np.abs(x1 - x2)) else: raise ValueError(不支持的度量方式) def fit(self, X, y): self.X_train X self.y_train y def predict(self, X): predictions [] for x in X: # 计算距离并获取最近邻 distances [self._calculate_distance(x, x_train) for x_train in self.X_train] k_indices np.argsort(distances)[:self.k] k_nearest_labels [self.y_train[i] for i in k_indices] # 多数投票 most_common Counter(k_nearest_labels).most_common(1) predictions.append(most_common[0][0]) return np.array(predictions) # 测试示例 if __name__ __main__: from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split iris load_iris() X, y iris.data, iris.target X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2) knn KNN(k3) knn.fit(X_train, y_train) preds knn.predict(X_test) print(f准确率: {accuracy_score(y_test, preds):.2f})算法优化方向空间分割使用KD树或球树加速近邻搜索将复杂度从O(n)降到O(log n)距离加权给更近的邻居更高投票权重降维处理对高维数据先用PCA降维4. 模型评估从混淆矩阵到F1分数准确率在不平衡数据中会失真面试官常要求手写更全面的评估指标计算。多分类评估指标实现def multiclass_confusion_matrix(y_true, y_pred, classes): 生成多分类混淆矩阵 n_classes len(classes) matrix np.zeros((n_classes, n_classes), dtypeint) for true, pred in zip(y_true, y_pred): matrix[true][pred] 1 return matrix def precision_recall_f1(conf_matrix): 计算每个类别的精确率、召回率和F1 metrics {} for i in range(conf_matrix.shape[0]): tp conf_matrix[i,i] fp conf_matrix[:,i].sum() - tp fn conf_matrix[i,:].sum() - tp precision tp / (tp fp) if (tp fp) 0 else 0 recall tp / (tp fn) if (tp fn) 0 else 0 f1 2 * (precision * recall) / (precision recall) if (precision recall) 0 else 0 metrics[fClass_{i}] { Precision: precision, Recall: recall, F1: f1 } return metrics # 示例使用 y_true [0, 1, 2, 0, 1, 2] y_pred [0, 2, 1, 0, 0, 1] conf_mat multiclass_confusion_matrix(y_true, y_pred, classes[0,1,2]) print(混淆矩阵:\n, conf_mat) print(\n评估指标:\n, precision_recall_f1(conf_mat))关键概念解析宏平均 vs 微平均宏平均平等看待每个类别微平均平等看待每个样本ROC-AUC真正例率(TPR) vs 假正例率(FPR)的曲线下面积PR曲线精确率-召回率曲线特别适用于不平衡数据5. 交叉验证与超参数调优正确的验证策略能防止过拟合以下是带早停机制的交叉验证实现from sklearn.model_selection import KFold from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import f1_score def cross_val_with_early_stop(X, y, model, n_splits5, early_stop_rounds3): kf KFold(n_splitsn_splits) best_scores [] for train_idx, val_idx in kf.split(X): X_train, X_val X[train_idx], X[val_idx] y_train, y_val y[train_idx], y[val_idx] model.fit(X_train, y_train) val_preds model.predict(X_val) current_score f1_score(y_val, val_preds, averagemacro) # 早停机制 if len(best_scores) 0 and current_score max(best_scores): early_stop_rounds - 1 if early_stop_rounds 0: print(早停触发) break best_scores.append(current_score) return np.mean(best_scores) # 使用示例 X, y np.random.rand(100, 10), np.random.randint(0, 2, 100) model RandomForestClassifier(n_estimators100) mean_score cross_val_with_early_stop(X, y, model) print(f平均F1分数: {mean_score:.4f})超参数搜索技巧网格搜索适用于小参数空间from sklearn.model_selection import GridSearchCV param_grid {n_estimators: [50, 100, 200], max_depth: [None, 5, 10]} grid_search GridSearchCV(estimatormodel, param_gridparam_grid, cv5) grid_search.fit(X_train, y_train)贝叶斯优化适用于昂贵评估的大参数空间from skopt import BayesSearchCV opt BayesSearchCV( model, {n_estimators: (50, 200), max_depth: (1, 50)}, n_iter32, cv5 ) opt.fit(X_train, y_train)学习曲线分析判断增加数据还是调整模型复杂度6. 面试实战技巧与避坑指南代码白板书写规范先写函数签名和docstring处理边界条件空输入、极端值添加时间/空间复杂度分析准备测试用例正常、异常场景高频陷阱问题如何优化KNN在大数据集的性能当特征数量远大于样本数量时如何处理线上模型效果下降的可能原因有哪些典型错误案例# 错误在整体数据上做标准化后再划分训练测试集 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 数据泄露 X_train, X_test train_test_split(X_scaled, test_size0.2) # 正确做法 X_train, X_test train_test_split(X, test_size0.2) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)性能优化技巧使用numba加速数值计算对大数据集使用近似最近邻算法Annoy、Faiss利用多核并行化特征处理joblib库