零基础入门逻辑回归:从原理到实战
逻辑回归是机器学习里超基础、超常用的分类方法(划重点!它不是回归方法),比如判断邮件是不是垃圾邮件、用户会不会点击广告,都能用它解决。下面用大白话拆解它的核心逻辑和实战步骤,零基础也能看懂。
一、先搞懂:为什么需要逻辑回归?(从线性回归的 “缺点” 说起)
我们先从熟悉的 “线性回归” 入手,看看它处理分类问题时的不足 —— 这也是逻辑回归存在的意义。
1. 一个简单的分类问题
比如用 “学习时长” 判断 “是否通过考试”:
- 特征(输入):学习时长(1 小时、2 小时…10 小时)
- 标签(输出):是否通过(0 = 不通过,1 = 通过)
这是典型的 “二分类问题”(结果只有 2 种)。
2. 试试用线性回归解决?
线性回归的核心是 “拟合一条直线”,比如用直线 y = 系数×时长 + 截距
预测结果。我们可以约定:
- 若预测值
y > 0.5
,就判断 “通过”(1) - 若
y ≤ 0.5
,就判断 “不通过”(0)
但这样做有个大问题:线性回归的预测值可能超出 0-1 范围。比如学习时长特别短(比如 - 1 小时,虽然实际不存在),预测值可能是负数;时长特别长,预测值可能大于 1。但 “是否通过” 的概率只能在 0-1 之间,显然线性回归不适合直接做分类。
二、逻辑回归的 “核心 trick”:用 Sigmoid 函数 “压缩” 范围
逻辑回归的关键,是给线性回归加了一个 “转换工具”——Sigmoid 函数,把线性回归输出的 “任意数”,压缩到 0-1 之间(刚好对应 “概率”)。
1. Sigmoid 函数长什么样?
它的公式不用记,只需记住形状:像一条 “S 型曲线”,特点是:
- 输入(比如线性回归的结果)不管是多大的正数,输出都无限接近 1;
- 输入不管是多小的负数,输出都无限接近 0;
- 输入 = 0 时,输出正好是 0.5。
这简直是为二分类量身定做的!我们可以直接用它的输出当 “概率”:
- 若 Sigmoid 输出 > 0.5,判断为 “是”(标签 1,比如通过考试);
- 若 ≤ 0.5,判断为 “否”(标签 0,比如不通过)。
2. 逻辑回归的完整公式(大白话版)
逻辑回归其实是 “线性回归 + Sigmoid 转换” 的组合,分两步:
第一步:算线性结果
跟线性回归一样,把特征(比如学习时长 x)和系数(w)相乘再加截距(b),得到z = w×x + b
(如果有多个特征,就是z = w1×x1 + w2×x2 + ... + b
)。
这里的z
可以理解为 “分类边界的基础”。第二步:用 Sigmoid 压缩
把z
代入 Sigmoid 函数,得到最终的 “概率”:
概率 = 1 / (1 + e^(-z))
(e 是数学里的常数,约等于 2.718)。
比如:
- 当
z
很大(比如 10),e^(-10)
几乎是 0,概率≈1/1=1(几乎肯定是 1 类); - 当
z
很小(比如 - 10),e^(-(-10))=e^10
很大,概率≈1/(1 + 大数字)≈0(几乎肯定是 0 类)。
三、逻辑回归怎么 “学” 到最优参数?(损失函数 + 梯度下降)
模型要 “学习” 的,就是线性部分的系数 w
和截距 b
。怎么判断参数好不好?需要两个关键工具:损失函数(判断误差)和梯度下降(调整参数减小误差)。
1. 损失函数:给 “误差” 打分
损失函数的作用是:计算模型预测结果和真实标签的差距有多大。差距越小,损失值越小,模型越好。
逻辑回归不用线性回归的 “平方损失”(会导致参数难优化),而是用对数损失函数。不用记公式,只需理解核心:
- 如果真实标签是 1,模型预测的概率越接近 1,损失越小;越接近 0,损失越大(比如预测错了,罚得狠);
- 如果真实标签是 0,模型预测的概率越接近 0,损失越小;越接近 1,损失越大。
2. 梯度下降:“一步步找最优参数”
有了损失函数,怎么找到让损失最小的 w
和 b
?靠 “梯度下降”,原理像 “下山找最低点”:
- 梯度:可以理解为 “当前位置下山最快的方向”;
- 学习率:每一步走多大(步太大容易跨过最低点,步太小走得慢);
- 迭代:重复 “算梯度→往梯度反方向走一步→更新参数”,直到损失不再减小(找到山脚)。
简单说,梯度下降就是让模型 “一点点调整参数”,最终找到让损失最小的最优参数。
四、实战:用代码实现逻辑回归(两种方式)
下面用真实数据集(特征 X0、X1,标签 Y=0 或 1),分别手动实现逻辑回归,再用 sklearn (机器学习库)快速实现,直观感受过程。
方式 1:手动实现(理解核心步骤)
我们把前面的逻辑(Sigmoid、损失函数、梯度下降)写成代码,步骤如下:
1. 导入工具库
import numpy as np # 做数学计算
import pandas as pd # 处理数据
import matplotlib.pyplot as plt # 画图
2. 加载数据并可视化
先看看数据长什么样:
# 加载数据集(网上可直接获取)
df = pd.read_csv("https://labfile.oss.aliyuncs.com/courses/1081/course-8-data.csv")
# 画散点图:X0是横轴,X1是纵轴,颜色区分标签Y(0=深蓝,1=黄色)
plt.figure(figsize=(10,6))
plt.scatter(df['X0'], df['X1'], c=df['Y'])
plt.show()
会看到两类点分布在图上,我们要找一条线把它们分开。
3. 定义核心函数(Sigmoid、损失、梯度)
# 1. Sigmoid函数:压缩到0-1
def sigmoid(z):
return 1 / (1 + np.exp(-z))
# 2. 对数损失函数:计算误差
def calculate_loss(h, y):
# h是预测概率,y是真实标签(0或1)
return (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean()
# 3. 计算梯度:找下山方向
def calculate_gradient(X, h, y):
# X是特征,h是预测概率,y是真实标签
return np.dot(X.T, (h - y)) / y.shape[0]
4. 实现逻辑回归的训练过程
def logistic_regression(X, y, learning_rate=0.01, num_iterations=30000):
"""
逻辑回归模型训练函数,通过梯度下降法寻找最优参数
参数:
X: 特征数据,形状为 (样本数, 特征数) 的numpy数组
y: 标签数据,形状为 (样本数,) 的numpy数组,值为0或1
learning_rate: 学习率,控制梯度下降的步长,默认0.01
num_iterations: 迭代次数,控制梯度下降的迭代轮数,默认30000
返回:
w: 训练好的参数,包含截距项,形状为 (特征数+1,) 的numpy数组
"""
# 步骤1:给特征矩阵添加截距列(全为1)
# 这是为了将截距项b整合到参数w中,相当于给每个样本增加一个恒为1的特征x0
# 此时模型变为 z = w0*x0 + w1*x1 + ... + wn*xn,其中x0=1,w0即截距b
intercept = np.ones((X.shape[0], 1)) # 创建形状为(样本数, 1)的全1数组
X = np.concatenate((intercept, X), axis=1) # 按列拼接,新特征矩阵形状为(样本数, 特征数+1)
# 步骤2:初始化参数w
# 初始化为全0数组,长度等于新特征矩阵的列数(特征数+1)
# w[0]对应截距项b,w[1:]对应各个特征的系数
w = np.zeros(X.shape[1]) # 形状为(特征数+1,)
# 步骤3:通过梯度下降法迭代优化参数
for i in range(num_iterations):
# 计算线性组合z = X·w(矩阵乘法)
# 形状为(样本数,),每个元素对应一个样本的线性计算结果
z = np.dot(X, w)
# 计算Sigmoid函数值h,得到每个样本的预测概率(0-1之间)
# 形状为(样本数,),h[i]表示第i个样本预测为1的概率
h = sigmoid(z)
# 计算梯度(损失函数对参数w的偏导数)
# 形状为(特征数+1,),每个元素表示对应参数的梯度方向
grad = calculate_gradient(X, h, y)
# 更新参数:沿梯度反方向移动,步长为learning_rate
# 这一步是梯度下降的核心,通过不断调整参数减小损失
w -= learning_rate * grad
# 每1000次迭代打印一次损失值,监控训练过程
# 损失值逐渐减小说明模型在优化,趋于稳定说明接近最优解
if i % 1000 == 0:
loss = calculate_loss(h, y) # 计算当前的平均损失
print(f"迭代第{i}次,损失:{loss:.4f}") # 保留4位小数打印
return w # 返回训练好的参数,包含截距项和各特征系数
5. 训练模型并画分类边界
# 准备数据:特征X(X0、X1),标签y(Y)
X = df[['X0', 'X1']].values
y = df['Y'].values
# 训练模型
trained_w = logistic_regression(X, y)
# 画分类边界(红线就是分开两类点的线)
plt.figure(figsize=(10,6))
plt.scatter(df['X0'], df['X1'], c=df['Y'])
# 生成网格点(覆盖整个图的范围)
x1_min, x1_max = df['X0'].min(), df['X0'].max()
x2_min, x2_max = df['X1'].min(), df['X1'].max()
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max))
# 计算每个网格点的预测结果(判断在边界哪一侧)
grid = np.c_[xx1.ravel(), xx2.ravel()] # 把网格点转成特征格式
# 加截距列(1),算线性结果z
z = np.dot(np.c_[np.ones(grid.shape[0]), grid], trained_w)
# 画边界:z=0的地方就是分类线(Sigmoid(z)=0.5的位置)
plt.contour(xx1, xx2, z.reshape(xx1.shape), levels=[0], colors='red', linewidths=2)
plt.show()
运行后会看到一条红线,能较好地把两类点分开 —— 这就是逻辑回归找到的 “分类边界”。
方式 2:用 sklearn 快速实现(实际工作常用)
手动实现是为了理解原理,实际工作中直接用 sklearn 库,几行代码就能搞定:
from sklearn.linear_model import LogisticRegression
# 1. 初始化模型(设置参数:迭代次数足够多,确保收敛)
model = LogisticRegression(max_iter=10000, solver='liblinear')
# 2. 训练模型(直接喂特征X和标签y)
model.fit(X, y)
# 3. 查看模型参数(系数w和截距b)
print("系数w:", model.coef_) # 对应X0、X1的系数
print("截距b:", model.intercept_) # 对应手动实现中的trained_w[0]
# 4. 计算准确率(模型在训练集上的正确率)
accuracy = model.score(X, y)
print(f"模型准确率:{accuracy:.4f}") # 通常能达到0.9以上
# 5. 画分类边界(和手动实现类似,红线分开两类点)
plt.figure(figsize=(10,6))
plt.scatter(df['X0'], df['X1'], c=df['Y'])
z = np.dot(grid, model.coef_.T) + model.intercept_ # 算线性结果
plt.contour(xx1, xx2, z.reshape(xx1.shape), levels=[0], colors='red', linewidths=2)
plt.show()
sklearn 已经封装好了所有复杂逻辑,我们只需调用接口,效率极高。
五、回到最初的问题:逻辑回归为什么带 “回归” 二字?
现在能回答开头的疑问了:
- “逻辑”:来自 “逻辑分布”(Sigmoid 函数是逻辑分布的核心),代表 “是 / 否” 的二分类逻辑;
- “回归”:因为它的核心是用线性模型(和线性回归一样的 w×X + b)构建分类边界,本质是 “线性模型的分类应用”,所以保留了 “回归” 的名字。
六、总结:逻辑回归的核心要点
- 本质:二分类模型,用 “线性模型 + Sigmoid” 实现概率预测;
- 关键工具:
- Sigmoid 函数:压缩输出到 0-1(概率);
- 对数损失函数:衡量预测误差;
- 梯度下降:找到最优参数(w 和 b);
- 优势:简单、高效、易解释,适合处理二分类问题(如垃圾邮件识别、疾病初筛);
- 实战:手动实现理解原理,sklearn 快速落地。
只要记住 “线性回归做基础,Sigmoid 来转换,梯度下降找最优”,逻辑回归的核心就掌握了!