一、背景
归一化(Normalization)和标准化(Standardization)是数据预处理中的两种常见技术,旨在调整数据的范围和分布,以提高机器学习模型或者深度学习模型的性能和训练速度。虽然它们的目标相似,但具体方法和应用场景有所不同。
1、归一化
归一化是将数据缩放到一个特定的范围(通常是0到1之间),以确保不同特征具有相同的尺度。归一化常用于需要保持数据相对比例的场景,如图像处理和神经网络训练。最常见的归一化方法是最小-最大归一化(Min-Max Normalization),其公式为:
其中:
- x是原始数据。
- x′是归一化后的数据。
- 和分别是数据集中的最小值和最大值。
2、标准化
标准化是将数据转换为均值为0、标准差为1的标准正态分布。标准化常用于需要假设数据服从正态分布的场景,如线性回归和支持向量机:
其中:
- x是原始数据。
- x′是标准化后的数据。
- μ是数据的均值。
- σ是数据的标准差。
二、python示例
下面的代码中,我们构建了一个torch板的全连接神经网络,训练数据集采用sklearn自带的经典的乳腺癌二分类数据集。我们将展示对数据进行归一化以及不进行归一化会给模型训练带来什么样的影响(也可以换成标准化的代码试一下)。模型的结构以及构建过程不再赘述,可以参考笔者往期的博文。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.metrics import precision_score, recall_score, f1_score# 加载乳腺癌数据集
data = load_breast_cancer()
X = data.data
y = data.target# 数据标准化
scaler = MinMaxScaler()
X = scaler.fit_transform(X)# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 将数据转换为PyTorch张量
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.float32)# 定义自定义数据集类
class BreastCancerDataset(Dataset):def __init__(self, X, y):self.X = Xself.y = ydef __len__(self):return len(self.X)def __getitem__(self, idx):return self.X[idx], self.y[idx]# 创建数据集和数据加载器
train_dataset = BreastCancerDataset(X_train, y_train)
test_dataset = BreastCancerDataset(X_test, y_test)train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=4, shuffle=False)# 定义MLP模型
class MLP(nn.Module):def __init__(self, input_size, hidden_size, num_classes):super(MLP, self).__init__()self.fc1 = nn.Linear(input_size, hidden_size)self.relu = nn.ReLU()self.fc2 = nn.Linear(hidden_size, num_classes)self.sigmoid = nn.Sigmoid()def forward(self, x):out = self.fc1(x)out = self.relu(out)out = self.fc2(out)out = self.sigmoid(out)return out# 定义模型参数
input_size = X_train.shape[1]
hidden_size = 16
num_classes = 1# 创建模型
model = MLP(input_size, hidden_size, num_classes)# 定义损失函数和优化器
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)# 训练模型
num_epochs = 5for epoch in range(num_epochs):model.train()running_loss = 0.0for inputs, labels in train_loader:# 前向传播outputs = model(inputs)loss = criterion(outputs.squeeze(), labels)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()running_loss += loss.item()print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}")print("Training complete.")# 评估模型
def evaluate(model, dataloader):model.eval()all_labels = []all_predictions = []with torch.no_grad():for inputs, labels in dataloader:outputs = model(inputs)predicted = (outputs.squeeze() > 0.5).float()all_labels.extend(labels.tolist())all_predictions.extend(predicted.tolist())precision = precision_score(all_labels, all_predictions)recall = recall_score(all_labels, all_predictions)f1 = f1_score(all_labels, all_predictions)return precision, recall, f1train_precision, train_recall, train_f1 = evaluate(model, train_loader)
test_precision, test_recall, test_f1 = evaluate(model, test_loader)print(f"Train Precision: {train_precision:.4f}")
print(f"Train Recall: {train_recall:.4f}")
print(f"Train F1 Score: {train_f1:.4f}")print(f"Test Precision: {test_precision:.4f}")
print(f"Test Recall: {test_recall:.4f}")
print(f"Test F1 Score: {test_f1:.4f}")
未进行数据归一化直接训练的模型表现:
归一化之后的模型表现:
可以看到,数据归一化之后训练的模型性能相比原始数据训练出来的模型要大大提升。训练和测试是precision都提升了10%左右,F1值也提升了5%左右。
三、总结
在机器学习建模过程中,许多模型在训练之前需要对输入数据进行归一化或标准化。这些模型通常对输入特征的尺度敏感,归一化或标准化可以提高模型的性能和训练速度。树类模型(如决策树、随机森林、梯度提升树等)不需要对特征进行归一化或标准化处理,因为它们的分裂标准与特征的尺度无关。然而,在神经网络中,输入数据的尺度过大可能导致激活函数的输出值过大或过小,影响梯度的计算,导致数值不稳定性。因此,我们在进行深度学习建模的过程中,如果特征取值之间的量纲差异较大,务必要对数据进行归一化或者标准化的处理。