从 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(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库)
更多推荐


所有评论(0)