大数跨境

导师:“数据归一化搞懂了吗?”我:“Codex已写完了,KNN准确率65%!”导师:“特征尺度差80倍还不处理?MinMaxScaler后97%?”

导师:“数据归一化搞懂了吗?”我:“Codex已写完了,KNN准确率65%!”导师:“特征尺度差80倍还不处理?MinMaxScaler后97%?” 机器学习和人工智能AI
2026-10-21
2

哈喽,大家好~

最近有同学问到,为什么模型准确率不高,训练时还一直震荡?

一个非常重要的原因,可以先想想,数据归一化了吗?

我们在日常的实验中,不同特征的数值范围经常差得很大。比如年龄是 20~60,年收入却是几万到几十万。模型直接拿这些数据训练,很容易被“大数值特征”带偏~

今天我们就把数据归一化讲清楚,再用一个分类数据集,看它如何影响 KNN 的决策边界和模型训练过程~

01数据归一化是什么

数据归一化,就是把不同量纲、不同范围的特征,转换到一个相近的数值区间。

你可以这样理解:一个特征说话声音是 1,另一个特征的声音是 10000。模型听到以后,自然更关注后者。但数值大,不代表它真的更重要。

常见的 Min-Max 归一化公式为:

其中,  是原始数据,  和   分别是该特征的最小值与最大值,  是归一化结果。

例如某个特征范围是 0~100,原始值为 20,那么归一化后就是:

一句话概括:Min-Max 归一化会把数据压缩到   区间,同时保留原来的大小关系。

02哪些模型更需要归一化

归一化对依赖“距离”和“梯度”的模型非常重要。

KNN、K-Means 会直接计算样本距离。如果某个特征范围特别大,它就会主导距离结果。逻辑回归、神经网络等模型依赖梯度优化,特征尺度差异过大,也会让损失函数更难优化。

SVM、PCA 同样对特征尺度敏感。不过,决策树、随机森林这类基于特征切分的模型,通常不依赖距离,对归一化没有那么敏感。

需要注意的是,Min-Max 对异常值比较敏感。存在极端值时,可以考虑标准化:

这里的   是均值,  是标准差。它会把数据转换为均值接近 0、标准差接近 1 的形式。

03完整案例

下面生成一个二分类数据集,并故意把第二个特征放大 80 倍。我们分别观察 KNN 决策边界,以及逻辑回归的训练损失。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
import torch
import torch.nn as nn

np.random.seed(42)
torch.manual_seed(42)

# 1. 构造数据,并制造特征尺度差异
X, y = make_classification(
    n_samples=600, n_features=2, n_redundant=0,
    n_informative=2, class_sep=1.3, random_state=42
)
X[:, 1] *= 80

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=42
)

# 只能使用训练集拟合归一化器,避免数据泄漏
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 2. 训练KNN
knn_raw = KNeighborsClassifier(n_neighbors=9).fit(X_train, y_train)
knn_scaled = KNeighborsClassifier(n_neighbors=9).fit(
    X_train_scaled, y_train
)

# 3. 使用PyTorch训练逻辑回归
def train_logistic(data, label, epochs=150):
    torch.manual_seed(42)
    model = nn.Linear(2, 1)
    optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
    criterion = nn.BCEWithLogitsLoss()

    tx = torch.tensor(data, dtype=torch.float32)
    ty = torch.tensor(label[:, None], dtype=torch.float32)
    losses = []

    for _ in range(epochs):
        optimizer.zero_grad()
        loss = criterion(model(tx), ty)
        loss.backward()
        optimizer.step()
        losses.append(loss.item())
    return losses

raw_losses = train_logistic(X_train, y_train)
scaled_losses = train_logistic(X_train_scaled, y_train)

# 4. 绘制决策边界
def draw_boundary(ax, model, data, label, title):
    x1_min, x1_max = data[:, 0].min() - .2, data[:, 0].max() + .2
    x2_min, x2_max = data[:, 1].min() - .2, data[:, 1].max() + .2

    xx, yy = np.meshgrid(
        np.linspace(x1_min, x1_max, 300),
        np.linspace(x2_min, x2_max, 300)
    )
    grid = np.c_[xx.ravel(), yy.ravel()]
    zz = model.predict_proba(grid)[:, 1].reshape(xx.shape)

    ax.contourf(xx, yy, zz, levels=20, cmap="cool", alpha=.65)
    ax.contour(xx, yy, zz, levels=[.5], colors="#FFE600", linewidths=3)
    ax.scatter(data[:, 0], data[:, 1], c=label,
               cmap="spring", edgecolor="black", s=28)
    ax.set_title(title)

fig, axes = plt.subplots(1, 3, figsize=(18, 5))

raw_acc = accuracy_score(y_test, knn_raw.predict(X_test))
scaled_acc = accuracy_score(
    y_test, knn_scaled.predict(X_test_scaled)
)

draw_boundary(
    axes[0], knn_raw, X_train, y_train,
    f"Raw Data | KNN Accuracy={raw_acc:.3f}"
)
draw_boundary(
    axes[1], knn_scaled, X_train_scaled, y_train,
    f"Normalized | KNN Accuracy={scaled_acc:.3f}"
)

axes[2].plot(raw_losses, color="#FF1744", lw=2.5, label="Raw")
axes[2].plot(scaled_losses, color="#00E676", lw=2.5,
             label="Normalized")
axes[2].set_yscale("log")
axes[2].set_title("Logistic Regression Training Loss")
axes[2].set_xlabel("Epoch")
axes[2].set_ylabel("Loss")
axes[2].grid(alpha=.25)
axes[2].legend()

plt.tight_layout()
plt.show()

前两张图展示的是 KNN 决策边界。背景颜色代表模型对不同类别的预测区域,黄色曲线是分类分界线。

原始数据中,第二个特征被放大了 80 倍,KNN 计算距离时会过度关注它。归一化后,两个特征回到相近尺度,决策边界通常会更贴合真实的数据结构,测试集准确率也更合理。

第三张图对比逻辑回归的训练损失。纵轴使用对数刻度,可以更清楚地观察收敛速度。绿色曲线如果下降得更快、更平稳,说明归一化改善了优化过程。

还有一个细节必须注意:一定要先划分训练集和测试集,再用训练集执行 fit。如果拿全部数据计算最大值和最小值,就等于提前看到了测试集信息,这叫数据泄漏。

最后

数据归一化不是简单地“把数字变小”,而是让不同特征在更公平的尺度上参与模型计算。对于 KNN、SVM、K-Means、逻辑回归和神经网络,它往往会直接影响距离、收敛速度和最终效果。

实际使用时,你可以继续对比 MinMaxScaler 与 StandardScaler 的表现;如果数据中存在明显异常值,也可以尝试 RobustScaler。总之就是说,调模型之前,先把数据尺度检查清楚,很多问题会简单不少。

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 432
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读8.7k
粉丝0
内容432