逻辑回归实战:从理论到MATLAB仿真完整指南

1次阅读
没有评论

共计 3852 个字符,预计需要花费 10 分钟才能阅读完成。

image.webp

1. 背景介绍

逻辑回归是一种广泛应用于二分类问题的统计方法,尽管名字中带有 ” 回归 ”,但它实际上是一种分类算法。它的核心思想是通过 sigmoid 函数将线性回归的输出映射到 0 和 1 之间,从而得到样本属于某一类的概率。

逻辑回归实战:从理论到 MATLAB 仿真完整指南

逻辑回归的主要优势包括:

  • 算法简单,计算效率高
  • 输出具有概率意义
  • 容易实现和解释
  • 对线性可分数据表现良好

在医疗诊断、信用评分、垃圾邮件过滤等领域都有广泛应用。

2. 数据准备

2.1 生成示例数据

我们先创建一个简单的二维数据集,方便可视化理解:

% 生成两类数据
rng(1); % 设置随机种子确保结果可重复
class1 = [1.5 + randn(50,1), 1.5 + randn(50,1)];
class2 = [-1.5 + randn(50,1), -1.5 + randn(50,1)];

% 合并数据并添加偏置项
X = [ones(100,1), [class1; class2]];
y = [ones(50,1); zeros(50,1)];

% 可视化数据
figure;
scatter(X(y==1,2), X(y==1,3), 'r', 'filled'); hold on;
scatter(X(y==0,2), X(y==0,3), 'b', 'filled');
xlabel('特征 1'); ylabel('特征 2');
legend('类别 1','类别 0'); title('原始数据分布');

2.2 数据归一化

虽然对于这个简单例子归一化不是必须的,但良好的习惯是对数据进行标准化处理:

% 标准化数据(不包括偏置列)mu = mean(X(:,2:end));
sigma = std(X(:,2:end));
X_norm = [ones(size(X,1),1), (X(:,2:end)-mu)./sigma];

3. 核心算法实现

3.1 Sigmoid 函数

逻辑回归的核心是 sigmoid 函数,它将任意实数映射到 (0,1) 区间:

$$ g(z) = \frac{1}{1 + e^{-z}} $$

MATLAB 实现:

function g = sigmoid(z)
% 计算 sigmoid 函数
% 输入:z - 可以是标量、向量或矩阵
% 输出:g - 与 z 同维度的 sigmoid 计算结果

g = 1./(1 + exp(-z));
end

3.2 代价函数和梯度

逻辑回归的代价函数为:

$$ J(\theta) = -\frac{1}{m}\sum_{i=1}^m [y^{(i)}\log(h_\theta(x^{(i)})) + (1-y^{(i)})\log(1-h_\theta(x^{(i)}))] $$

梯度计算:

$$ \frac{\partial J(\theta)}{\partial \theta_j} = \frac{1}{m}\sum_{i=1}^m (h_\theta(x^{(i)}) – y^{(i)})x_j^{(i)} $$

MATLAB 实现:

function [J, grad] = costFunction(theta, X, y)
% 计算逻辑回归的代价和梯度
% 输入:%   theta - 模型参数(n×1 向量)
%   X - 特征矩阵(m×n, 包含偏置项)
%   y - 真实标签(m×1 向量)
% 输出:%   J - 当前参数下的代价
%   grad - 梯度向量(n×1)

m = length(y); % 样本数
J = 0;
grad = zeros(size(theta));

h = sigmoid(X * theta); % 计算预测概率

% 计算代价
J = (-1/m) * sum(y.*log(h) + (1-y).*log(1-h));

% 计算梯度
grad = (1/m) * (X' * (h - y));
end

3.3 梯度下降优化

使用 MATLAB 的优化函数 fminunc 进行参数优化:

% 初始化参数
initial_theta = zeros(size(X_norm,2),1);

% 设置优化选项
options = optimset('GradObj', 'on', 'MaxIter', 400);

% 运行优化
[theta, cost] = fminunc(@(t)(costFunction(t, X_norm, y)), initial_theta, options);

% 输出最优参数
fprintf('最优参数 theta:\n');
fprintf('%f \n', theta);

3.4 绘制决策边界

决策边界是 $\theta^T x = 0$ 的地方,对于二维特征:

$$ \theta_0 + \theta_1 x_1 + \theta_2 x_2 = 0 $$

可以解出:

$$ x_2 = -\frac{\theta_0 + \theta_1 x_1}{\theta_2} $$

绘制代码:

% 绘制决策边界
plot_x = [min(X_norm(:,2))-0.5, max(X_norm(:,2))+0.5];
plot_y = (-1./theta(3)) .* (theta(2).*plot_x + theta(1));

% 注意要将归一化的特征转换回原始尺度
plot_x_orig = plot_x * sigma(1) + mu(1);
plot_y_orig = plot_y * sigma(2) + mu(2);

figure;
scatter(X(y==1,2), X(y==1,3), 'r', 'filled'); hold on;
scatter(X(y==0,2), X(y==0,3), 'b', 'filled');
plot(plot_x_orig, plot_y_orig, 'LineWidth', 2);
xlabel('特征 1'); ylabel('特征 2');
legend('类别 1','类别 0','决策边界'); title('分类结果');

4. 结果分析

4.1 预测准确率

% 计算训练集上的预测准确率
p = predict(theta, X_norm);
fprintf('训练准确率: %f%%\n', mean(double(p == y)) * 100);

function p = predict(theta, X)
% 预测函数
% 输入:%   theta - 模型参数
%   X - 特征矩阵(包含偏置项)
% 输出:%   p - 预测类别(0 或 1)

p = round(sigmoid(X * theta));
end

4.2 混淆矩阵

% 计算混淆矩阵
predictions = predict(theta, X_norm);
TP = sum((predictions == 1) & (y == 1));
FP = sum((predictions == 1) & (y == 0));
TN = sum((predictions == 0) & (y == 0));
FN = sum((predictions == 0) & (y == 1));

confusion_matrix = [TP, FP; FN, TN];
fprintf('混淆矩阵:\n');
disp(confusion_matrix);

% 计算精确率、召回率和 F1 分数
precision = TP / (TP + FP);
recall = TP / (TP + FN);
F1 = 2 * precision * recall / (precision + recall);

fprintf('精确率: %.2f%%\n', precision*100);
fprintf('召回率: %.2f%%\n', recall*100);
fprintf('F1 分数: %.2f%%\n', F1*100);

5. 避坑指南

  1. 学习率选择不当
  2. 问题:学习率太大导致无法收敛,太小导致收敛过慢
  3. 解决:尝试不同的学习率(如 0.01, 0.03, 0.1, 0.3),观察代价函数下降曲线

  4. 未进行特征缩放

  5. 问题:特征尺度差异大会导致梯度下降效率低下
  6. 解决:对特征进行标准化 (z-score) 或归一化 (缩放到[0,1] 区间)

  7. 忘记添加偏置项

  8. 问题:模型缺少截距项,可能导致决策边界无法正确分类
  9. 解决:确保特征矩阵 X 的第一列全是 1

  10. 数据线性不可分

  11. 问题:逻辑回归只能学习线性决策边界
  12. 解决:尝试添加多项式特征或使用其他分类算法如 SVM、神经网络

  13. 类别不平衡

  14. 问题:一个类别的样本远多于另一个类别,导致模型偏向多数类
  15. 解决:使用过采样、欠采样或调整分类阈值

6. 进阶建议

  1. 正则化
  2. 添加 L1 或 L2 正则项防止过拟合
  3. 修改代价函数为:
    $$ J(\theta) = -\frac{1}{m}\sum_{i=1}^m [y^{(i)}\log(h_\theta(x^{(i)})) + (1-y^{(i)})\log(1-h_\theta(x^{(i)}))] + \frac{\lambda}{2m}\sum_{j=1}^n\theta_j^2 $$

  4. 特征工程

  5. 尝试添加多项式特征
  6. 使用领域知识构造新特征
  7. 进行特征选择减少冗余

  8. 多分类扩展

  9. 使用一对多 (one-vs-all) 策略处理多分类问题
  10. 对每个类别训练一个二分类器

  11. 其他优化算法

  12. 尝试共轭梯度法、BFGS 等更高级的优化算法
  13. 使用 MATLAB 的 fminunc 函数自动选择优化方法

7. 思考题

  1. 如果我们的数据不是线性可分的,应该如何修改我们的逻辑回归模型?
  2. 当特征维度很高 (如 1000 维) 时,逻辑回归可能会遇到什么问题?如何解决?
  3. 如何确定正则化参数 λ 的最佳值?

8. 总结

通过本文的完整实现,我们走过了逻辑回归从理论到实践的整个流程:从数据准备、模型实现到结果分析和可视化。MATLAB 提供了强大的矩阵运算和优化工具,使得实现机器学习算法变得相对简单。

逻辑回归作为分类问题的入门算法,虽然结构简单,但包含了机器学习中的许多核心概念,如梯度下降、概率建模、决策边界等。掌握好逻辑回归的实现,将为学习更复杂的机器学习算法打下坚实基础。

建议读者尝试修改数据集或调整参数,观察模型表现的变化,这样可以获得更直观的理解。在实际应用中,还需要考虑模型评估、特征工程等更多因素,这些内容我们将在后续文章中继续探讨。

正文完
 0
评论(没有评论)