机器学习面试编程实战: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(thresh=len(df.columns)-1) # 保留至少n-1个特征的样本常见陷阱与解决方案:
数据泄露:在填充缺失值时使用整个数据集统计量(如全局均值),会导致测试集信息污染
- 正确做法:仅在训练集计算统计量,应用到验证/测试集
异常值检测的三种方法:
- IQR法:
Q1 - 1.5*IQR和Q3 + 1.5*IQR之外的值 - Z-score法:绝对值大于3的标准分数
- 隔离森林:适用于高维数据
- IQR法:
提示:在时间序列数据中,应采用前向填充(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, k=5, distance_metric='euclidean'): 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_size=0.2) knn = KNN(k=3) 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), dtype=int) 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[f'Class_{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_splits=5, early_stop_rounds=3): kf = KFold(n_splits=n_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, average='macro') # 早停机制 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_estimators=100) 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(estimator=model, param_grid=param_grid, cv=5) grid_search.fit(X_train, y_train)贝叶斯优化:适用于昂贵评估的大参数空间
from skopt import BayesSearchCV opt = BayesSearchCV( model, {'n_estimators': (50, 200), 'max_depth': (1, 50)}, n_iter=32, cv=5 ) 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_size=0.2) # 正确做法 X_train, X_test = train_test_split(X, test_size=0.2) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test)性能优化技巧:
- 使用
numba加速数值计算 - 对大数据集使用近似最近邻算法(Annoy、Faiss)
- 利用多核并行化特征处理(joblib库)