深度学习笔记03-逻辑回归:从理论到pytorch的实现
文章内容来源:《PyTorch编程技术与深度学习》 作者:袁梅宇
一、逻辑回归是什么?和线性回归有啥不一样?
很多刚接触机器学习的同学会疑惑:“逻辑回归带‘回归’二字,为啥用来做分类?”其实这正是它的特别之处——
- 线性回归:解决回归问题,目标是预测连续数值(比如房价、股票涨跌幅度),目标属性是连续的数值类型。
- 逻辑回归:解决分类问题,目标是把数据分到离散的类别里(比如判断邮件是不是垃圾邮件、用户会不会点击广告),目标属性是离散的标称类型。
简单说,线性回归是“预测数值多少”,逻辑回归是“判断属于哪一类”。
更妙的是,逻辑回归可以看作神经网络的“最小单元”——一个神经元。搞懂它,后面学复杂神经网络就轻松多了!
二、逻辑回归的核心:Sigmoid函数与概率思维
逻辑回归能做分类,全靠一个叫Sigmoid(也叫Logistic函数)的“神器”:
g(z)=11+e−z g(z) = \frac{1}{1 + e^{-z}} g(z)=1+e−z1
它长这样(图3.4的曲线):
- 输入很大的正数,输出接近1;
- 输入很大的负数,输出接近0;
- 输入为0时,输出刚好是0.5。
这个特性让Sigmoid完美适配“概率”的概念——我们可以把逻辑回归的输出 h(x;θ)h(x;\theta)h(x;θ) 理解为“样本属于正类的概率”:
- 如果 h(x;θ)≥0.5h(x;\theta) \geq 0.5h(x;θ)≥0.5,就预测为正类(比如标签1);
- 如果 h(x;θ)<0.5h(x;\theta) < 0.5h(x;θ)<0.5,就预测为负类(比如标签0)。
三、从零开始:用PyTorch手写逻辑回归
光懂原理不够,动手实现才是硬道理。我们分步骤搭建一个逻辑回归模型,解决二元分类问题。
步骤1:定义Sigmoid激活函数和模型结构
先写最基础的函数和模型框架:
import torch
def sigmoid(z):
"""S型激活函数,也就是Logistic函数"""
g = 1 / (1 + torch.exp(-z))
return g
def model(x, w, b):
"""逻辑回归模型:y_hat = g(w^T x + b)"""
return sigmoid(x.mv(w) + b)
这里的x是输入数据矩阵,w是权重向量,b是偏置标量。
步骤2:定义损失函数(负对数似然)
逻辑回归用负对数似然损失来衡量预测误差,公式对应代码:
def loss_fn(y_pred, y):
"""损失函数:负对数似然"""
loss = - y.mul(torch.log(y_pred)) - (1 - y).mul(torch.log(1 - y_pred))
return loss.mean()
def grad_loss_fn(y_pred, y):
"""损失函数对预测值的导数"""
return y_pred - y
步骤3:梯度计算与参数更新
要让模型学习,就得用梯度下降法更新参数(权重w和偏置b):
def grad_fn(x, y, y_pred):
"""计算权重和偏置的梯度"""
grad_w = grad_loss_fn(y_pred, y) * x
grad_b = grad_loss_fn(y_pred, y)
return torch.cat((grad_w.mean(dim=0), grad_b.mean().unsqueeze(0)), 0)
def model_training(x, y, n_epochs, learning_rate, params, print_params=True):
"""模型训练函数:用梯度下降更新参数"""
for epoch in range(1, n_epochs + 1):
w, b = params[:-1], params[-1]
# 前向传播
y_pred = model(x, w, b)
# 计算损失
loss = loss_fn(y_pred, y)
# 计算梯度
grad = grad_fn(x, y, y_pred)
# 更新参数
params -= learning_rate * grad
if epoch == 1 or epoch % 10 == 1:
print(f'轮次:{epoch}\t损失:{float(loss):.4f}')
if print_params:
print(f'参数:{params.detach().numpy()}')
print(f'梯度:{grad.detach().numpy()}\n')
return params
步骤4:生成数据并训练模型
最后生成模拟数据,初始化参数,启动训练:
# 随机生成二元分类数据
def generate_data():
# 这里省略数据生成逻辑,实际可根据需求构造
x = torch.randn(100, 2)
y = (x[:, 0] + x[:, 1] > 0).float().unsqueeze(1)
return x, y
x, y = generate_data()
# 初始化参数:权重w(2个特征)和偏置b
w = torch.zeros(2)
b = torch.zeros(1)
params = torch.tensor([0.0, 0.0, 0.0])
# 训练模型
params = model_training(
x=x,
y=y,
n_epochs=500,
learning_rate=0.1,
params=params
)
print(f'梯度下降找到的w和b:{params.numpy()}')
训练完成后,模型会学到一条“决策边界”(直线),把正例和负例分开,就像图3.5展示的那样。
四、偷懒版:用PyTorch官方API快速实现
如果觉得手写太麻烦,PyTorch的nn模块能让我们更简洁地实现逻辑回归:
import torch.nn as nn
import torch.optim as optim
# 定义模型:线性层 + Sigmoid激活
seg_model = nn.Sequential(
nn.Linear(2, 1), # 2个输入特征,1个输出(对应二分类的概率)
nn.Sigmoid()
)
# 定义优化器(SGD)和损失函数(二元交叉熵)
learning_rate = 0.1
optimizer = optim.SGD(seg_model.parameters(), lr=learning_rate)
loss_fn = nn.BCELoss() # 二元交叉熵损失,和我们手写的loss_fn等价
# 模型训练函数
def concise_model_training(x, y, n_epochs, optimizer, model, loss_fn):
for epoch in range(1, n_epochs + 1):
# 前向传播
y_pred = model(x)
# 计算损失
loss = loss_fn(y_pred, y)
# 梯度清零 + 反向传播 + 更新参数
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch == 1 or epoch % 10 == 1:
print(f'轮次:{epoch}\t损失:{float(loss):.4f}')
# 调用训练函数
concise_model_training(
x=x,
y=y,
n_epochs=500,
optimizer=optimizer,
model=seg_model,
loss_fn=loss_fn
)
# 查看训练后的参数
print('优化后的模型参数:')
for name, param in seg_model.named_parameters():
print(name, param)
对比手写版本,官方API把“梯度计算、参数更新”等细节封装好了,大大减少了代码量。
五、逻辑回归的延伸:从二分类到多分类?
逻辑回归天生是解决二元分类的,但我们可以通过“一对多”等策略扩展到多分类任务。不过更优雅的方法是后续要学的Softmax回归(下一篇博客预告!)。
另外,逻辑回归作为“神经网络的最小单元”,学好它能帮我们更顺畅地进入深度学习世界——毕竟复杂神经网络就是无数个“逻辑回归式神经元”的组合。
写在最后
逻辑回归看似简单,却是理解分类任务、神经网络的关键跳板。从原理到手写实现,再到用框架简化开发,每一步都值得细细琢磨。下一篇我们会聊聊多分类任务的解决方案——Softmax回归,不见不散~
(完整代码可参考书中的logistic_regression_from_scratch.py和logistic_regression_concise.py)
更多推荐



所有评论(0)