8.1 基础知识

8.1.1 偏差-方差权衡

\(y=f(x)+\varepsilon, \; E(\varepsilon)=0\)\(f\)表示真实模型,\(\hat f\)是模型某次训练得到的结果,\(E(\hat f)\)表示训练模型的期望表现。

\[ \begin{aligned} E[(\hat f-y)^2] &= E[(\hat f - E(\hat f) + E(\hat f)-y)^2] \\ &= E[(\hat f - E(\hat f))^2] + E[(E(\hat f)-y)^2] + 2E[(\hat f - E(\hat f))(E(\hat f)-y)] \\ &= E[(\hat f - E(\hat f))^2] + E[(E(\hat f)-y)^2] \\ &= E[(\hat f - E(\hat f))^2] + E[(E(\hat f)-f-\varepsilon)^2] \\ &= E[(\hat f - E(\hat f))^2] + E[(E(\hat f) - f)^2] + \varepsilon^2 \end{aligned} \]

故模型的期望泛化错误率可拆解为方差+偏差+噪声

8.1.2 评价指标

  1. 分类问题
  • 准确率

\[ Accuracy = \frac{TP + TN}{TP + TN + FP +FN} \]

  • 精确率(查准率):有没有误报

\[ Precision = \frac{TP}{TP+FP} \]

  • 召回率(查全率):有没有漏报

\[ Recall = \frac{TP}{TP+FN} \]

  • F1与\(F_\beta\)

\[ F1 = \frac{2*Precision*Recall}{Precision + Recall} \\ F_\beta = \frac{(1+\beta^2)*Precision*Recall}{\beta^2*Precision + Recall} \]

\(0<\beta<1\)时精确率有更大影响,\(\beta>1\)时召回率有更大影响

  • ROC曲线与AUC:横轴假阳率FPR,纵轴真阳率TPR,全局性能评估

\[ TPR = \frac{TP}{TP+FN} \\ FPR = \frac{FP}{FP+TN} \]

  • PR曲线与AUC:横轴召回率,纵轴精确率,更关注正样本预测质量

当存在类别不平衡情况时,PR曲线相较ROC曲线更敏感,能捕捉到异常

  • 代价曲线:引入误判代价

  • 宏平均:对于多个混淆矩阵,先计算各个混淆矩阵的指标,再求平均

  • 微平均:对于多个混淆矩阵,先平均各个混淆矩阵,再求指标

  1. 回归问题
  • 均方误差:对异常值敏感

  • 均方根误差:量纲与目标变量一致

  • 平均绝对误差:对异常值不敏感

  • \(R^2\)\(R^2_{adj}\)

  1. 其他
  • AIC

\[ AIC = -2L(\hat \theta)_{max} + 2k \]

k是参数数量

  • BIC

\[ BIC = -2L(\hat \theta)_{max}+ k\ln(n) \]

8.1.3 特征工程

特征工程:从原始数据中创建、选择、变换或组合特征,以提高机器学习模型性能的过程。

8.1.3.1 探索性数据分析

了解数据在分布、类型、统计量、缺失值、异常值、实际含义等方面的基本信息。

方法:

  1. 数据可视化

    注意辛普森悖论,引入分层变量进行探索

  2. 描述性统计

  3. 专家的先验知识

  4. 相关性分析

    皮尔逊相关系数、斯皮尔曼秩相关系数(非参)、肯德尔秩相关系数(非参,有序变量)、列联表检验

8.1.3.2 数据清洗

  1. 缺失值

    • 删除:删除记录或者直接删除特征

    • 填充:用均值、中位数、众数、模型预测值、插值等方法进行填充

naniar包用于可视化缺失值,如vis_miss()miss_var_summary()miss_case_summary()

  1. 异常值

    先要检查指标的口径与定义是否不一致。

    • 识别:箱线图与四分位距IQR、Z-score

    • 处理:删除、用分位数替换、数据分箱

8.1.3.3 特征创建

基于已有特征创建新的特征,往往要结合专家建议

  • 取对数、取平方等数学变换

  • 融合特征,如根据总量指标与人数指标构建人均指标

  • 数据分箱

8.1.3.4 特征变换

改变特征的尺度、编码方式。

  • 中心标准化

  • 极差标准化

  • 哑变量编码

  • 独热编码

  • 序数编码:适用于顺序变量

8.1.3.5 特征选择

剔除无关或冗余的特征。

  • 惩罚函数法,如单变量选择、群组变量选择

  • 基于统计指标(AIC、BIC、R方)等的模型选择

  • 树模型的重要性得分

  • SHAP值

  • 降维方法:PCA、t-SNE、UMAP、自编码器

8.1.4 pandas与sklearn

8.1.4.1 导入数据集

import pandas as pd

# 读取CSV文件
df = pd.read_csv(path)

# 读取Excel文件
df = pd.read_excel(path)
# 读取Excel文件中的多个sheet,存储为字典
df = pd.read_excel(path, sheet_name=['Sheet1', 'Sheet2'])
# 读取Excel文件中所有sheet,存储为字典
df = pd.read_excel(path, sheet_name=None)

8.1.4.2 数据预处理

  1. 缺失值处理
# 各列缺失值数量
df.count()
df.isnull().sum()

# 各列缺失值比例
df.isnull().mean() * 100

# 缺失值可视化
import missingno as msno
import matplotlib.pyplot as plt
msno.matrix(df) # 矩阵图:直观展示缺失值在数据中的分布
plt.show()

msno.bar(df)    # 条形图:按列展示完整数据比例
plt.show()

# 删除缺失值
df = df.dropna()                   # 删除包含任何缺失值的行
df = df.dropna(axis=1, how='all')  # 删除全是缺失值的列

# 填充缺失值
df_filled = df.fillna(0)                    # 用固定值填充
df['A'] = df['A'].fillna(df['A'].mean())    # 均值
df['B'] = df['B'].fillna(df['B'].median())  # 中位数
df = df.ffill()                             # 前向填充
df = df.bfill()                             # 后向填充
df = df.interpolate()                       # 用插值法填充(更平滑的填充方式)
  1. 异常值处理
# Z-score
from scipy import stats
z_scores = np.abs(stats.zscore(df['A']))
outliers = df[z_scores >= 3]

# 四分位距
Q1 = df['A'].quantile(0.25)
Q3 = df['A'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['A'] < lower_bound) | (df['A'] > upper_bound)]

# 异常值处理
df = df[z_scores < 3]   # 三倍标准差内
df['A'] = df['A'].clip(lower=lower_bound, upper=upper_bound)
  1. 标准化
from sklearn.preprocessing import StandardScaler
# from sklearn.preprocessing import MinMaxScaler

# 选取数值列
numeric_cols = df.select_dtypes(include=['number']).columns
# 标准化并替换
scaler = StandardScaler()    # scaler = MinMaxScaler()
df[numeric_cols] = scaler.fit_transform(df[numeric_cols])
  1. 编码
# 独热编码
df = pd.get_dummies(df)
# 也可考虑from sklearn.preprocessing import OneHotEncoder

# 哑变量编码
df = pd.get_dummies(df, drop_first=True)

# 频率编码
frequency = df['A'].value_counts(normalize=True)
df['A'] = df['A'].map(frequency)

# 有序变量编码
df['评分'] = pd.Categorical(
    df['评分'], 
    categories=['低', '中', '高', '极高'], 
    ordered=True
)
df['评分编码'] = df['评分'].cat.codes

# 自定义编码
edu_map = {'小学': 1, '中学': 2, '大学': 3, '硕士': 4, '博士': 5}
df['edu'] = df['edu'].map(edu_map)

8.1.4.3 分割数据集

from sklearn.model_selection import train_test_split

# 训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=42)

# 分层分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify= categorical_var)

# 交叉验证
from sklearn.model_selection import KFold
kf = KFold(n_splits=10, shuffle=True, random_state=42)
for train_index, test_index in kf.split(X, y):
    # 获取分割后的训练集和测试集
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    ...
# 也可使用cross_val_score及cross_validate来简化交叉验证流程
cross_val_score(model, X, y, cv=5, scoring='accuracy')   # 单个评估指标
cross_validate(model, X, y, cv=5, scoring=['accuracy', 'f1_macro'], return_train_score=True) # 多个评估指标