逻辑回归到底在干嘛?用 “判断今天要不要带伞” 举例
你每天出门前会想:
“今天会下雨吗?” → 这是一个二分类问题(带伞 / 不带伞)。
逻辑回归就是帮你做这个决定的 “小助手”,它的思考过程是:
- 收集线索:比如看天气预报的 “湿度”“云层厚度”(相当于输入特征 x)。
- 算一个 “下雨指数”:把线索加权相加(比如湿度 ×0.6 + 云层 ×0.4 + 0.1)。
- 把指数转成概率:比如 “下雨指数” 越高,越可能下雨(用 sigmoid 函数转成 0-1 的概率)。
- 做决定:如果概率≥50%,就判断 “会下雨”,建议带伞。
核心步骤拆解:像拼积木一样简单
1. 输入特征(x):
比如判断邮件是否为垃圾邮件,用 2 个线索:
- x₁:邮件里有没有 “免费” 这个词(出现次数)
- x₂:邮件里有没有 “优惠” 这个词(出现次数)
2. 算一个 “垃圾指数”(z):
z = w₁×x₁ + w₂×x₂ + b
- w₁和 w₂:是 “权重”,比如 “免费” 这个词更重要,w₁就大一点。
- b:是 “基础分”,比如默认有 10% 概率是垃圾邮件。
例子:
如果一封邮件里 “免费” 出现 3 次,“优惠” 出现 2 次,
假设 w₁=0.5,w₂=0.5,b=0.1,
则 z = 0.5×3 + 0.5×2 + 0.1 = 2.6
3. 把指数转成概率(p):
用 sigmoid 函数,公式是:p = 1/(1 + e^(-z))
- 不管 z 多大,p 都会变成 0 到 1 之间的数,代表 “是垃圾邮件的概率”。
- 上面的例子中 z=2.6,算出来 p≈0.93(93% 概率是垃圾邮件)。
直观理解 sigmoid:
- z=0 → p=0.5(50% 概率)
- z 越大 → p 越接近 1(比如 z=10 → p≈1)
- z 越小 → p 越接近 0(比如 z=-10 → p≈0)
4. 定一个 “门槛” 做分类:
一般用 0.5 作为门槛:
- p≥0.5 → 预测为 “垃圾邮件”(类别 1)
- p<0.5 → 预测为 “正常邮件”(类别 0)
⚖️ 损失函数:用 “考试打分” 理解 BCE Loss
假设老师给你出了 10 道判断题(答案只有对 / 错),你每道题猜一个概率(比如第 1 题你猜 70% 是对的)。
BCE Loss 就是老师给你打分的规则:
- 如果正确答案是 “对”(1):
- 你猜 70%(p=0.7)→ 得分高(损失小)
- 你猜 30%(p=0.3)→ 得分低(损失大)
- 如果正确答案是 “错”(0):
- 你猜 30%(p=0.3)→ 得分高(损失小)
- 你猜 70%(p=0.7)→ 得分低(损失大)
公式白话版:
损失 = - [真实答案 ×log (你猜的概率) + (1 - 真实答案)×log (1 - 你猜的概率)]
- 简单说:你猜的概率越接近真实答案,损失越小。
代码实战:用逻辑回归判断 “是不是垃圾邮件”
import torch
import matplotlib.pyplot as plt
import torch.nn as nn
import numpy as np
# 1. 生成数据(保持不变)
x = torch.randn(100, 2) * 3
y_true = ((x[:, 0] + x[:, 1] > 0) + 0.1 * torch.randn(100)).clamp(0, 1)
# 修正:调整y_true形状为[100, 1],与模型输出匹配
y_true = y_true.view(-1, 1) # 将一维张量转为二维张量
# 2. 定义模型(保持不变)
class Model(nn.Module):
def __init__(self):
super().__init__()
self.linear = torch.nn.Linear(2, 1)
def forward(self, x):
z = self.linear(x)
p = torch.sigmoid(z)
return p
# 3. 训练模型(其余代码保持不变)
model = Model()
criterion = torch.nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
losses = []
for i in range(500):
y_pred = model(x)
loss = criterion(y_pred, y_true)
losses.append(loss.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (i + 1) % 100 == 0:
print(f'第{i + 1}次猜,扣分:{loss.item():.4f}')
# 4. 评估模型(保持不变)
y_pred_guess = (y_pred >= 0.5).float()
accuracy = (y_pred_guess == y_true).float().mean()
print(f'\n模型猜的准确率:{accuracy.item() * 100:.2f}%')
# 5. 可视化(保持不变)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(x[y_true.view(-1) == 0, 0], x[y_true.view(-1) == 0, 1], c='blue', label='正常邮件')
plt.scatter(x[y_true.view(-1) == 1, 0], x[y_true.view(-1) == 1, 1], c='red', label='垃圾邮件')
x1_min, x1_max = x[:, 0].min() - 1, x[:, 0].max() + 1
x2_min, x2_max = x[:, 1].min() - 1, x[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max, 100), np.linspace(x2_min, x2_max, 100))
grid = torch.tensor(np.c_[xx1.ravel(), xx2.ravel()], dtype=torch.float32)
with torch.no_grad():
probs = model(grid).reshape(xx1.shape)
plt.contour(xx1, xx2, probs, levels=[0.5], linewidths=2, colors='black')
plt.xlabel('"免费"出现次数')
plt.ylabel('"优惠"出现次数')
plt.title('模型怎么判断垃圾邮件')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(range(500), losses)
plt.xlabel('猜的次数')
plt.ylabel('扣分多少')
plt.title('模型越学越准啦!')
plt.show()
代码拆解
一、准备工具(导入库)
import torch
import matplotlib.pyplot as plt
import torch.nn as nn
import numpy as np
- 翻译:
我们要开一家 “垃圾邮件识别公司”,先准备工具:torch:超级计算机,能自动帮我们算复杂的数学题matplotlib.pyplot:画画工具,用来画报表给老板看torch.nn:组装机器人的零件库(比如齿轮、电线)numpy:电子表格软件,能快速处理大量数据
二、生成假数据(模拟邮件特征)
x = torch.randn(100, 2) * 3
- 翻译:
我们要训练机器人识别垃圾邮件,先准备 100 封 “假邮件”:- 每封邮件有 2 个特征:
- 特征 1:“免费” 这个词出现的次数(比如 0 次、3 次)
- 特征 2:“优惠” 这个词出现的次数(比如 1 次、5 次)
torch.randn(100, 2):随机生成 100 行 2 列的数字(可能是负数,但先不管)* 3:把数字放大 3 倍,让它们更像真实的次数(比如 - 3 到 3 之间)
- 每封邮件有 2 个特征:
y_true = ((x[:, 0] + x[:, 1] > 0) + 0.1 * torch.randn(100)).clamp(0, 1)
- 翻译:
给每封邮件打 “真实标签”(是不是垃圾邮件):x[:, 0] + x[:, 1] > 0:如果 “免费” 和 “优惠” 的次数加起来 > 0,就标记为 1(垃圾邮件),否则标记为 0(正常邮件)+ 0.1 * torch.randn(100):加一点随机小错误(模拟现实中偶尔判断错)clamp(0, 1):确保标签在 0 到 1 之间(不能是负数或大于 1)
y_true = y_true.view(-1, 1) # 修正形状
- 翻译:
把标签的格式调整一下,从 “一行数字” 变成 “一列数字”,这样机器人更容易看懂。
(就像把表格从横排变成竖排,方便阅读)
三、搭建模型(造一个判断垃圾邮件的机器人)
class Model(nn.Module):
def __init__(self):
super().__init__()
self.linear = torch.nn.Linear(2, 1)
- 翻译:
我们要造一个机器人,它能根据 “免费” 和 “优惠” 的次数判断邮件是不是垃圾:class Model:定义机器人的设计图self.linear = torch.nn.Linear(2, 1):- 机器人有一个 “大脑”(线性层)
- 这个大脑接收 2 个输入(“免费” 和 “优惠” 的次数)
- 输出 1 个结果(0~1 之间的概率,表示是垃圾邮件的可能性)
def forward(self, x):
z = self.linear(x)
p = torch.sigmoid(z)
return p
- 翻译:
机器人的 “工作流程”:z = self.linear(x):大脑计算z = 权重1 * “免费”次数 + 权重2 * “优惠”次数 + 偏移量
(权重和偏移量是机器人自己学的,就像人通过经验总结规律)p = torch.sigmoid(z):把 z 的值压缩到 0~1 之间,得到概率 p- 如果 p 接近 1,说明很可能是垃圾邮件
- 如果 p 接近 0,说明很可能是正常邮件
model = Model() # 创建机器人实例
- 翻译:
根据设计图,造一个真正的机器人!现在它已经可以工作了,只是还没 “训练”(权重是随机的)。
四、训练模型(教机器人学会判断)
criterion = torch.nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
- 翻译:
训练机器人需要 2 个工具:criterion = torch.nn.BCELoss():
“打分器”,用来评价机器人判断得有多准。分数越低,说明判断越准确。optimizer = torch.optim.SGD(...):
“教练”,根据打分器的结果,教机器人调整自己的 “大脑”(权重和偏移量)。lr=0.01:学习速度,不能太快也不能太慢(就像教小孩,太快学不会,太慢浪费时间)
losses = [] # 记录每次训练的“扣分”
for i in range(500): # 训练500次
y_pred = model(x) # 让机器人判断所有100封邮件
loss = criterion(y_pred, y_true) # 用打分器评价机器人的判断
losses.append(loss.item()) # 记录这次的分数
- 翻译:
训练机器人的第一步:- 让机器人判断 100 封邮件,得到预测结果
y_pred - 用打分器对比预测结果和真实标签,得到一个分数(损失值)
- 把这个分数记录下来,看看机器人有没有进步
- 让机器人判断 100 封邮件,得到预测结果
optimizer.zero_grad() # 清空上次的“训练记录”
loss.backward() # 计算“怎么调整大脑”能让下次判断更准
optimizer.step() # 教练指导机器人调整大脑
- 翻译:
训练机器人的第二步:optimizer.zero_grad():清空上次的训练记录(就像擦黑板,准备重新计算)loss.backward():根据当前的损失值,计算应该怎么调整机器人的 “大脑”(权重和偏移量)optimizer.step():教练告诉机器人:“把权重 1 增加 0.01,把权重 2 减少 0.005……”
if (i+1) % 100 == 0:
print(f'第{i+1}次训练,扣分:{loss.item():.4f}')
- 翻译:
每训练 100 次,就打印一次当前的分数(损失值)。分数越低,说明机器人越厉害!
五、评估模型(看看机器人学的怎么样)
y_pred_guess = (y_pred >= 0.5).float() # 把概率转成0或1的判断
accuracy = (y_pred_guess == y_true).float().mean() # 计算准确率
print(f'模型猜的准确率:{accuracy.item() * 100:.2f}%')
- 翻译:
考试时间!让机器人判断 100 封邮件,看看它有多厉害: - 就像抛硬币猜正反:
- 模型说 “正面概率是 0.6” → 我们猜 “正面”(1)
- 模型说 “正面概率是 0.3” → 我们猜 “反面”(0)
(y_pred >= 0.5)就是这个 “猜” 的过程,结果是True/False,转成.float()后变成1.0/0.0。accuracy = (y_pred_guess == y_true).float().mean()(y_pred_guess == y_true):对比模型的猜测和真实标签,得到一堆True/False(猜对了 / 猜错了).float():把True转成 1,False转成 0(1 表示猜对,0 表示猜错).mean():计算这些 1 和 0 的平均值,就是 “猜对的比例”print(f'模型猜的准确率:{accuracy.item() * 100:.2f}%')accuracy.item():从张量中取出数值(比如 0.8)* 100:转成百分比(0.8 → 80%):.2f:保留两位小数(更美观)
六、可视化(画两张图,让结果更直观)
plt.figure(figsize=(12, 5)) # 创建一个画布(宽12厘米,高5厘米)
- 翻译:
准备一张大纸,用来画两张图给老板看。
# 左图:数据点和模型的判断边界
plt.subplot(1, 2, 1) # 把纸分成左右两半,画左边的图
plt.scatter(x[y_true.view(-1) == 0, 0], x[y_true.view(-1) == 0, 1], c='blue', label='正常邮件')
plt.scatter(x[y_true.view(-1) == 1, 0], x[y_true.view(-1) == 1, 1], c='red', label='垃圾邮件')
- 翻译:
画第一张图(散点图):- 蓝色点:正常邮件,横坐标是 “免费” 次数,纵坐标是 “优惠” 次数
- 红色点:垃圾邮件,同样用横纵坐标表示两个特征
# 画模型的判断边界(黑色线)
x1_min, x1_max = x[:, 0].min() - 1, x[:, 0].max() + 1
x2_min, x2_max = x[:, 1].min() - 1, x[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max, 100), np.linspace(x2_min, x2_max, 100))
grid = torch.tensor(np.c_[xx1.ravel(), xx2.ravel()], dtype=torch.float32)
with torch.no_grad():
probs = model(grid).reshape(xx1.shape)
plt.contour(xx1, xx2, probs, levels=[0.5], linewidths=2, colors='black')
- 翻译:
画一条 “分界线”,帮助我们理解机器人的判断逻辑:- 在整个区域内密密麻麻地撒 100×100 个点(就像在地图上标很多位置)
- 让机器人判断每个点是垃圾邮件的概率
- 画一条线,线上的点概率正好是 0.5(机器人 “不确定” 的位置)
- 线的左边:机器人更可能判断为正常邮件
- 线的右边:机器人更可能判断为垃圾邮件
plt.xlabel('"免费"出现次数')
plt.ylabel('"优惠"出现次数')
plt.title('模型怎么判断垃圾邮件')
plt.legend() # 显示图例(蓝色和红色点代表什么)
- 翻译:
给图加上标签:- x 轴:“免费” 出现的次数
- y 轴:“优惠” 出现的次数
- 标题:模型怎么判断垃圾邮件
- 图例:说明蓝色点和红色点分别代表什么
# 右图:扣分越来越少
plt.subplot(1, 2, 2) # 画右边的图
plt.plot(range(500), losses) # x轴是训练次数,y轴是每次的损失值
plt.xlabel('训练次数')
plt.ylabel('损失值(扣分)')
plt.title('模型越学越准啦!')
plt.show() # 显示这两张图
- 翻译:
画第二张图(折线图):- x 轴:训练次数(从 1 到 500)
- y 轴:每次训练的损失值(扣分)
- 理想情况下,随着训练次数增加,损失值会越来越低,说明机器人越来越厉害!
总结:整个流程像什么?
准备材料:
开一家 “垃圾邮件识别公司”,准备电脑、画图工具、零件库和电子表格。制造机器人:
设计一个机器人,它能根据 “免费” 和 “优惠” 的次数判断邮件是不是垃圾。训练机器人:
- 给机器人 100 封 “训练邮件”,每封邮件都标好是不是垃圾
- 让机器人判断,然后告诉它 “你错了多少”(损失值)
- 机器人根据错误调整自己的 “大脑”(权重和偏移量)
- 重复这个过程 500 次,直到机器人越来越准
考试:
让机器人判断 100 封新邮件,计算它的准确率(比如 85%)。画报表:
画两张图给老板看:- 左图:机器人的 “判断逻辑”(分界线)
- 右图:机器人的 “学习进度”(损失值越来越低)
终极总结(3 句话搞定)
逻辑回归是做什么的?
回答 “是不是” 的问题(比如是不是垃圾邮件),输出一个概率(0-1 之间)。核心步骤是什么?
- 算一个 “指数”(z = 特征 × 权重 + 偏置)
- 把指数转成概率(用 sigmoid 函数)
- 根据概率判断类别(比如≥0.5 就是 “是”)
怎么让模型学的准?
用 BCE 损失函数当 “老师”,告诉模型猜的有多错,再用梯度下降调整参数,让错误越来越小。
浙公网安备 33010602011771号