在数据分析、机器学习和模型训练中,数据归一化(Normalization)与标准化(Standardization)是至关重要的预处理步骤。它们能够消除不同特征间的量纲差异,提升模型收敛速度与预测精度。MATLAB 提供了灵活的手动计算方式以及高效的内置函数,适用于矩阵、表格、时间序列等多种数据类型。本文将系统讲解两者的原理、实现方法、实战应用及常见注意事项。
核心原理: 将原始数据线性映射到指定区间,通常为 [0, 1] 或 [-1, 1],以统一数值范围。其数学表达式如下:
Xnorm = (X - Xmin) / (Xmax - Xmin)
若目标区间为 [-1, 1],则公式调整为:
Xnorm = 2 × (X - Xmin) / (Xmax - Xmin) - 1
[0,1]
[-1,1]
适用情况包括:
核心原理: 将数据转换为均值为 0、标准差为 1 的分布,突出偏离中心的程度。计算公式为:
Xstd = (X - μ) / σ
其中,μ 表示样本均值,σ 为样本标准差。
[-1,1]
典型应用场景:
| 方法 | 核心特性 | 抗异常值能力 | 适合的数据分布 |
|---|---|---|---|
| 归一化 | 结果范围固定,依赖最小值和最大值 | 弱(受极值影响大) | 近似均匀分布、无明显离群点 |
| 标准化 | 均值为0,标准差为1,基于统计参数 | 强(对异常值鲁棒性较好) | 接近正态分布,允许存在极端值 |
在进行归一化或标准化前,必须先清理数据中的缺失项和异常点,否则会导致缩放偏差。
% 示例:包含缺失值的温度传感器数据 data = [25.3, 26.1, NaN, 27.8, 30.5, 100.2, 28.9, 29.4]; % 1. 缺失值填充(使用忽略 NaN 的均值) data(isnan(data)) = mean(data, 'omitNaN'); % 2. 异常值检测与修正(采用3倍标准差准则) mu = mean(data); sigma = std(data); outlier_idx = abs(data - mu) > 3*sigma; data(outlier_idx) = median(data); % 使用中位数替代异常值
通过基础运算完成按列归一化,适用于自定义需求。
% 构造原始数据(每行代表一个样本,每列为一个特征)
X = [10, 200, 5;
15, 300, 8;
5, 100, 3;
20, 400, 10];
% 计算各列的最小值与最大值
X_min = min(X);
X_max = max(X);
% 执行 Min-Max 归一化至 [0,1]
X_norm = (X - X_min) ./ (X_max - X_min);
% 显示结果
disp('原始数据:'); disp(X);
disp('归一化后数据:'); disp(X_norm);
原始数据:
10 200 5
15 300 8
5 100 3
20 400 10
归一化后数据:
0.3333 0.3333 0.2857
0.6667 0.6667 0.7143
0 0 0
1.0000 1.0000 1.0000
MATLAB 提供了专用函数 mapminmax,可快速实现归一化,并支持自定义输出范围。
% 对数据进行归一化,默认映射到 [0,1] [X_norm, ps] = mapminmax(X'); % 注意:该函数要求特征在行方向,需转置 X_norm = X_norm'; % 转回原格式
mapminmax
利用均值和标准差自行计算,有助于掌握标准化过程。
% 原始数据同上
mu = mean(X);
sigma = std(X);
% 按列执行 Z-Score 标准化
X_std = (X - mu) ./ sigma;
% 输出查看
disp('标准化后数据:'); disp(X_std);
MATLAB 的 zscore 函数可直接完成标准化操作,简洁高效。
% 直接调用 zscore(默认按列标准化) X_std = zscore(X);
对于表格结构(table 类型),可通过变量提取后分别处理,再合并回表。
% 创建示例表格
T = table([1;2;3], [100;200;300], [5.1;6.3;7.8], 'VariableNames', {'Age','Income','Score'});
% 提取数值列并标准化
numeric_cols = varfun(@isnumeric, T, 'OutputFormat', 'uniform');
T_numeric = T{:, numeric_cols};
% 进行标准化
T_normalized = zscore(T_numeric);
% 写回新表格
T{: , numeric_cols} = num2cell(T_normalized);
在构建多元线性回归或岭回归模型时,若各特征尺度差异较大(如年龄 vs 收入),不进行归一化/标准化可能导致梯度下降缓慢或权重失衡。通过对训练集进行标准化处理,并将相同的参数应用于测试集,可显著提高模型稳定性与泛化能力。
应仅使用训练集的最小值/最大值或均值/标准差来处理测试集,防止信息泄露。例如,在使用 mapminmax 时保存参数 ps,并在测试集上复用。
当某一特征的所有值相同(方差为0),标准化会导致除零错误,此类特征应提前识别并剔除或单独处理。
MATLAB 多数函数默认按列操作。若数据组织为“每行为一样本”,则需确认函数是否需要转置输入,避免误操作。
模型输出常需恢复至原始尺度以便解释。可借助保存的极值或统计参数进行逆变换:
面对海量数据时,建议采用分块读取 + 参数缓存的方式进行归一化/标准化。先从训练数据中提取全局统计量(如最大值、最小值、均值、标准差),然后逐批次应用相同规则处理,确保一致性的同时降低内存压力。
方法1:手动实现(理解核心逻辑)
% 按列(特征)进行标准化
X_mu = mean(X); % 计算每列的均值
X_sigma = std(X); % 计算每列的标准差
X_std = (X - X_mu) ./ X_sigma;
disp('标准化后数据:'); disp(X_std);
执行结果:
标准化后数据:
-0.37796 -0.37796 -0.43301
0.37796 0.37796 0.51962
-1.13389 -1.13389 -1.08253
1.13389 1.13389 0.99592
方法2:使用内置函数
MATLAB 提供了 zscore 函数,可直接完成标准化处理,无需手动计算均值与标准差。
% 基础标准化(默认方式) X_std = zscore(X); % 处理包含缺失值的数据 X_with_nan = [10, 200, NaN; 15, 300, 8; 5, 100, 3]; X_std_nan = zscore(X_with_nan, 0, 'omitNaN'); % 使用n-1作为分母,并忽略NaN
核心参数说明:
0 表示使用无偏估计(除以 n-1),1 表示有偏估计(除以 n);'omitNaN':在存在缺失值时有效处理,防止输出全为 NaN。zscore
对原始数据进行归一化处理,并转回原始维度结构:
X_norm = X_norm'; % 转置恢复原始维度 % 将数据映射至 [-1, 1] 范围 [X_norm_neg, ps_neg] = mapminmax(X', -1, 1); X_norm_neg = X_norm_neg';
关键步骤:保存归一化过程中生成的参数,以便后续对新数据使用相同的变换规则。
% 保存归一化参数(用于测试阶段)
save('norm_params.mat', 'ps');
核心说明:
ps
保存的是训练数据中的最大最小值等归一化参数。新来的测试数据必须使用同一组参数进行转换,避免引入信息泄露问题。
% 加载之前保存的归一化参数
load('norm_params.mat');
% 示例测试样本
X_test = [12, 250, 6];
% 应用相同参数进行归一化
X_test_norm = mapminmax('apply', X_test', ps)';
实际项目中,数据常以表格形式存储(如 Excel 文件)。需先提取数值列,再进行标准化或归一化操作。
% 读取传感器数据表格
data_table = readtable('sensor_data.xlsx');
% 提取前三个数值型特征列
X_table = table2array(data_table(:, 1:3));
% 对特征进行标准化
X_table_std = zscore(X_table);
% 将标准化后的数据写回表格
data_table_std = data_table;
data_table_std(:, 1:3) = array2table(X_table_std);
% 保存处理后的结果
writetable(data_table_std, 'sensor_data_std.xlsx');
table
以房价预测任务为例,比较是否进行标准化对模型性能的影响。
% 1. 加载 MATLAB 内置房价数据集
load houseprices.mat;
% 提取输入特征(地块面积、卧室数量、浴室数量)和目标变量(售价)
X = [LotArea, BedroomAbvGr, FullBath];
y = SalePrice;
% 2. 数据预处理
% 对特征进行标准化
X_std = zscore(X);
% 对标签(房价)进行归一化,并保存参数用于后续反变换
[y_norm, ps_y] = mapminmax(y');
y_norm = y_norm';
% 3. 构建线性回归模型
% 原始数据训练的模型
model_raw = fitlm(X, y);
% 标准化后数据训练的模型
model_std = fitlm(X_std, y_norm);
% 4. 模型评估 —— 使用均方误差(MSE)
y_pred_raw = predict(model_raw, X);
mse_raw = mean((y - y_pred_raw).^2);
y_pred_std = predict(model_std, X_std);
% 将预测结果还原到原始价格尺度
y_pred_std_ori = mapminmax('reverse', y_pred_std', ps_y)';
mse_std = mean((y - y_pred_std_ori).^2);
% 输出评估结果
disp(['标准化前MSE:', num2str(mse_raw)]);
disp(['标准化后MSE:', num2str(mse_std)]);
结果分析:
标准化后的模型 MSE 显著低于未标准化的情况。原因在于原始特征之间量纲差异大(例如“地块面积”为数千单位,“卧室数”仅为个位整数),导致优化过程不稳定、权重分配失衡。通过标准化使所有特征处于相近数值范围,提升了模型收敛速度与拟合精度。
错误做法:将训练集和测试集合并后再统一进行归一化或标准化,这会导致测试集的信息“提前”被模型知晓,造成信息泄露,影响评估真实性。
正确做法:仅基于训练集计算均值、标准差或最大最小值,并将这些参数应用于测试集的变换中,确保测试过程完全独立。
1
'omitNaN'正确做法:使用训练集的最值、均值或标准差对测试集进行归一化或标准化处理,确保数据分布一致性。以下是具体实现步骤:
% 划分训练集(70%)和测试集(30%)
idx = randperm(size(X,1));
train_idx = idx(1:round(0.7*size(X,1)));
test_idx = idx(round(0.7*size(X,1))+1:end);
X_train = X(train_idx, :);
X_test = X(test_idx, :);
% 基于训练集计算归一化所需参数
X_train_min = min(X_train);
X_train_max = max(X_train);
% 对训练集执行归一化
X_train_norm = (X_train - X_train_min)./(X_train_max - X_train_min);
% 使用训练集的参数对测试集进行归一化
X_test_norm = (X_test - X_train_min)./(X_train_max - X_train_min);
对于所有值相同的常量列,其最大值与最小值之差为0,若直接进行归一化会导致除以零错误。因此,应在预处理阶段先行剔除此类特征。
% 检测是否存在常量列
constant_cols = std(X) == 0;
% 删除常量特征列
X_clean = X(:, ~constant_cols);
% 对清洗后的数据进行归一化处理
X_clean_norm = mapminmax(X_clean')';
常见误区是按行(即每个样本自身)进行归一化,这会破坏不同特征之间的量纲对比关系。正确的做法应始终针对列方向——也就是每一个特征维度独立进行标准化或归一化。
核心原则:归一化/标准化操作应在特征维度(列)上进行,而不是在样本维度(行)上执行。
模型输出通常处于归一化空间中,需将其转换回原始量纲以便解释。利用训练阶段保存的参数即可完成逆变换。
% Min-Max 归一化的逆操作
X_ori = X_norm .* (X_max - X_min) + X_min;
% Z-Score 标准化的逆操作
X_ori_std = X_std .* X_sigma + X_mu;
% 使用 mapminmax 函数进行反向转换
X_ori_map = mapminmax('reverse', X_norm', ps)';
当面对大规模数据(例如超过十万行)时,一次性加载全部数据可能引发内存溢出问题。推荐采用分块读取的方式逐步处理。
% 创建表格文本数据存储对象
ds = tabularTextDatastore('big_data.csv');
% 设置每次读取的数据块大小
ds.ReadSize = 1000; % 每次读取1000行
% 初始化用于存储全局最值的变量
all_min = [];
all_max = [];
first_chunk = true;
% 第一次遍历:统计所有数据块中的全局最小值和最大值
while hasdata(ds)
chunk = read(ds);
X_chunk = table2array(chunk);
if first_chunk
all_min = min(X_chunk);
all_max = max(X_chunk);
first_chunk = false;
else
all_min = min([all_min; min(X_chunk)]);
all_max = max([all_max; max(X_chunk)]);
end
end
% 完成后重置数据读取位置,准备第二次遍历
reset(ds);
% 创建空的目标文件用于存储结果
writetable(table(), 'normalized_big_data.csv');
% 第二次遍历:使用全局最值对每一块数据进行归一化并追加写入文件
while hasdata(ds)
chunk = read(ds);
X_chunk = table2array(chunk);
X_chunk_norm = (X_chunk - all_min)./(all_max - all_min);
chunk_norm = array2table(X_chunk_norm);
% 以追加模式写入归一化后的数据块
writetable(chunk_norm, 'normalized_big_data.csv', 'WriteMode', 'append');
end
扫码加好友,拉您进群



收藏
