从零构建逻辑回归: sklearn 与自定义实现对比

news2025/3/11 10:31:48

文章目录


尽管名字中有“回归”二字,逻辑回归(Logistic Regression)实际上是一种分类方法,主要用于二分类问题。它通过一个线性模型与 Sigmoid 函数的结合,将问题转化为一个概率预测问题,并基于该概率来做分类决策。下面将详细讲解逻辑回归的工作原理、损失函数、梯度推导、以及如何通过梯度下降来优化模型。


这篇文章也写的不错:
Python实现逻辑回归(Logistic Regression)


理论基础

1. 逻辑回归模型

逻辑回归的核心是 Sigmoid 函数,也称为 逻辑函数。它可以将任何实数值映射到区间 [0, 1] 内,因此非常适合用于概率预测。Sigmoid 函数的形式为:

σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+ez1

其中 (z) 是线性回归的输出,可以表示为:

z = w T x + b z = w^T x + b z=wTx+b

这里:

  • w w w 是权重向量(模型参数之一),
  • x x x 是输入特征向量,
  • b b b 是偏置项。

因此,Sigmoid 函数的输出 y ^ = σ ( z ) \hat{y} = \sigma(z) y^=σ(z) 表示样本属于正类(即 y = 1 y = 1 y=1)的概率。对于二分类问题,模型的目标是预测输入样本属于类别1的概率:

P ( y = 1 ∣ x ) = σ ( w T x + b ) P(y = 1 | x) = \sigma(w^T x + b) P(y=1∣x)=σ(wTx+b)

如果 y ^ ≥ 0.5 \hat{y} \geq 0.5 y^0.5,则预测样本为正类 y = 1 y = 1 y=1,否则预测为负类 y = 0 y = 0 y=0


2. 损失函数

逻辑回归使用 交叉熵损失函数(Cross-Entropy Loss)来衡量模型的预测与真实标签之间的差异。交叉熵损失函数的形式如下:

L ( y , y ^ ) = − [ y log ⁡ ( y ^ ) + ( 1 − y ) log ⁡ ( 1 − y ^ ) ] L(y, \hat{y}) = - \left[ y \log(\hat{y}) + (1 - y) \log(1 - \hat{y}) \right] L(y,y^)=[ylog(y^)+(1y)log(1y^)]

其中:

  • y y y 是真实标签(0 或 1),
  • y ^ \hat{y} y^ 是模型预测的概率。

对于整个训练集,损失函数 J ( w , b ) J(w, b) J(w,b) 是所有样本损失的平均值,具体形式为:

J ( w , b ) = − 1 m ∑ i = 1 m [ y ( i ) log ⁡ ( y ^ ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − y ^ ( i ) ) ] J(w, b) = -\frac{1}{m} \sum_{i=1}^m \left[ y^{(i)} \log(\hat{y}^{(i)}) + (1 - y^{(i)}) \log(1 - \hat{y}^{(i)}) \right] J(w,b)=m1i=1m[y(i)log(y^(i))+(1y(i))log(1y^(i))]

其中 m m m 是训练集中的样本数量。


3. 梯度推导

为了优化逻辑回归模型,我们需要通过梯度下降算法最小化损失函数。首先,我们需要计算损失函数相对于模型参数 w w w b b b 的梯度。

(1) 计算 ∂ L ∂ y ^ \frac{\partial L}{\partial \hat{y}} y^L

损失函数对预测值的导数为:

∂ L ∂ y ^ = − ( y y ^ − 1 − y 1 − y ^ ) \frac{\partial L}{\partial \hat{y}} = -\left( \frac{y}{\hat{y}} - \frac{1 - y}{1 - \hat{y}} \right) y^L=(y^y1y^1y)

(2) 计算 ∂ y ^ ∂ z \frac{\partial \hat{y}}{\partial z} zy^

Sigmoid 函数的导数为:

∂ y ^ ∂ z = y ^ ( 1 − y ^ ) \frac{\partial \hat{y}}{\partial z} = \hat{y} (1 - \hat{y}) zy^=y^(1y^)

(3) 计算 ∂ L ∂ z \frac{\partial L}{\partial z} zL

根据链式法则,可以得到:

∂ L ∂ z = ∂ L ∂ y ^ ⋅ ∂ y ^ ∂ z = y ^ − y \frac{\partial L}{\partial z} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} = \hat{y} - y zL=y^Lzy^=y^y

(4) 计算 ∂ z ∂ w \frac{\partial z}{\partial w} wz ∂ z ∂ b \frac{\partial z}{\partial b} bz

线性回归的输出对权重和偏置的导数为:

∂ z ∂ w = x , ∂ z ∂ b = 1 \frac{\partial z}{\partial w} = x, \quad \frac{\partial z}{\partial b} = 1 wz=x,bz=1

(5) 计算 ∂ L ∂ w \frac{\partial L}{\partial w} wL ∂ L ∂ b \frac{\partial L}{\partial b} bL

根据链式法则:

∂ L ∂ w = ( y ^ − y ) ⋅ x \frac{\partial L}{\partial w} = (\hat{y} - y) \cdot x wL=(y^y)x

∂ L ∂ b = y ^ − y \frac{\partial L}{\partial b} = \hat{y} - y bL=y^y

对于整个训练集,梯度是所有样本梯度的平均值:

∂ J ∂ w = 1 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) ⋅ x ( i ) \frac{\partial J}{\partial w} = \frac{1}{m} \sum_{i=1}^m (\hat{y}^{(i)} - y^{(i)}) \cdot x^{(i)} wJ=m1i=1m(y^(i)y(i))x(i)

∂ J ∂ b = 1 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) \frac{\partial J}{\partial b} = \frac{1}{m} \sum_{i=1}^m (\hat{y}^{(i)} - y^{(i)}) bJ=m1i=1m(y^(i)y(i))


4. 梯度下降

梯度下降是一种优化算法,用于最小化损失函数并学习最佳的模型参数。梯度下降的更新规则如下:

w : = w − α ∂ J ∂ w w := w - \alpha \frac{\partial J}{\partial w} w:=wαwJ

b : = b − α ∂ J ∂ b b := b - \alpha \frac{\partial J}{\partial b} b:=bαbJ

其中, α \alpha α 是学习率,控制每次更新的步长。

梯度下降的执行步骤:
  1. 初始化参数 w w w b b b
  2. 计算损失函数 J ( w , b ) J(w, b) J(w,b)
  3. 计算梯度 ∂ J ∂ w \frac{\partial J}{\partial w} wJ ∂ J ∂ b \frac{\partial J}{\partial b} bJ
  4. 更新参数:
    w : = w − α ∂ J ∂ w w := w - \alpha \frac{\partial J}{\partial w} w:=wαwJ
    b : = b − α ∂ J ∂ b b := b - \alpha \frac{\partial J}{\partial b} b:=bαbJ
  5. 重复步骤 2-4,直到损失函数收敛或达到最大迭代次数。

5. 总结

逻辑回归通过将线性回归的结果通过 Sigmoid 函数转化为概率,并使用交叉熵损失函数来衡量模型的误差。通过反向传播计算梯度,并利用梯度下降优化参数,逻辑回归模型可以逐步学习到最优的分类边界,适用于二分类问题。


项目结构

project_root/
├── my_sklearn/
│   ├── linear_model/
│   │   ├── __init__.py
│   │   └── logistic_regression.py
│   ├── metrics/
│   │   ├── __init__.py
│   │   └── accuracy.py
│   └── __init__.py
└── main.py

代码实现

1. my_sklearn/linear_model/logistic_regression.py — 自定义逻辑回归实现
# my_sklearn/linear_model/logistic_regression.py
# 包含逻辑回归模型的实现

import numpy as np


class MyLogisticRegression:
    def __init__(self, learning_rate=0.01, epochs=1000):
        self.learning_rate = learning_rate  # 学习率
        self.epochs = epochs  # 迭代次数
        self.weights = None  # 权重
        self.bias = None  # 偏置

    # Sigmoid 激活函数
    def sigmoid(self, linear_output):
        result = 1 / (1 + np.exp(-linear_output))
        return result
    
    # 损失函数:交叉熵损失
    def compute_loss(self, X, y):
        m = len(y)
        predictions = self.sigmoid(np.dot(X, self.weights) + self.bias)
        loss = -1/m * np.sum(y * np.log(predictions) + (1 - y) * np.log(1 - predictions))
        return loss

    # 训练模型
    def fit(self, X, y):
        m, n = X.shape
        self.weights = np.zeros(n)
        self.bias = 0
        
        for epoch in range(self.epochs):
            # 前向传播
            z = np.dot(X, self.weights) + self.bias  # 计算线性组合
            predictions = self.sigmoid(z)  # 通过sigmoid函数得到概率
            
            # 反向传播
            dw = (1/m) * np.dot(X.T, (predictions - y))
            db = (1/m) * np.sum(predictions - y)
            
            # 更新参数
            self.weights -= self.learning_rate * dw
            self.bias -= self.learning_rate * db
            
            # 每100次迭代输出一次损失
            if epoch % 100 == 0:
                loss = self.compute_loss(X, y)
                print(f"Epoch {epoch}: Loss = {loss}")

    # 预测
    def predict(self, X):
        z = np.dot(X, self.weights) + self.bias
        predictions = self.sigmoid(z)
        class_predictions = np.where(predictions >= 0.5, 1, 0)
        return class_predictions

2. my_sklearn/metrics/accuracy.py — 自定义准确率计算
# my_sklearn/metrics/accuracy.py

def my_accuracy_score(y_true, y_pred):
    """
    计算准确率:正确预测的样本数 / 总样本数
    
    参数:
    y_true -- 真实标签
    y_pred -- 预测标签
    
    返回:
    accuracy -- 准确率
    """
    correct = sum(y_true == y_pred)
    total = len(y_true)
    accuracy = correct / total
    return accuracy

3. my_sklearn/linear_model/__init__.py — 导出 MyLogisticRegression
# my_sklearn/linear_model/__init__.py
# linear_model子包初始化文件

# 从logistic_regression模块导入类
from .logistic_regression import MyLogisticRegression

# 导出的类列表
__all__ = ['MyLogisticRegression'] 

4. my_sklearn/metrics/__init__.py — 导出准确率函数
# my_sklearn/metrics/__init__.py
# metrics子包初始化文件

# 从accuracy模块导入函数
from .accuracy import my_accuracy_score

# 导出的函数列表
__all__ = ['my_accuracy_score'] 

5. my_sklearn/__init__.py — 主包初始化
# my_sklearn/__init__.py
# 主包初始化文件

# 导入子模块,使它们可以通过my_sklearn.xxx直接访问
from . import metrics
from . import linear_model 

6. main.py — 主程序文件,包含数据处理和模型对比
# main.py

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression as SklearnLogisticRegression
from sklearn.metrics import accuracy_score  # 导入sklearn的准确率计算函数

# 导入自定义模块
from my_sklearn.linear_model import MyLogisticRegression  # 导入自定义逻辑回归模型
from my_sklearn.metrics import my_accuracy_score  # 导入自定义准确率计算函数

# 1. 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 只选择两类进行二分类
X = X[y != 2]
y = y[y != 2]

# 2. 数据预处理(标准化)
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 3. 切分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 4. 使用自定义实现的逻辑回归
my_lr = MyLogisticRegression(learning_rate=0.01, epochs=1000)
my_lr.fit(X_train, y_train)

# 5. 使用 sklearn 实现的逻辑回归
sklearn_lr = SklearnLogisticRegression(max_iter=1000)
sklearn_lr.fit(X_train, y_train)

# 6. 对比自定义模型和 sklearn 模型的预测准确度
my_pred = my_lr.predict(X_test)
sklearn_pred = sklearn_lr.predict(X_test)

# 使用自定义函数计算自定义模型的准确率
my_accuracy = my_accuracy_score(y_test, my_pred)
# 使用sklearn的函数计算sklearn模型的准确率
sklearn_accuracy = accuracy_score(y_test, sklearn_pred)

print(f"My Logistic Regression Accuracy: {my_accuracy * 100:.2f}%")
print(f"Sklearn Logistic Regression Accuracy: {sklearn_accuracy * 100:.2f}%")


项目使用说明

  1. 安装依赖
    由于此项目只使用了 numpysklearn 库,你可以通过以下命令安装它们:

    pip install numpy scikit-learn
    
  2. 运行项目
    运行 main.py 文件来加载数据集、训练模型并对比自定义模型和 sklearn 版本的准确度:

    python main.py
    

总结

这个项目实现了自定义的逻辑回归模型,并与 sklearn 的实现进行了对比。结构上,模仿了 sklearn 的模块化方式,将功能分为 linear_modelmetrics 两个子模块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2313164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1256:献给阿尔吉侬的花束--BFS多组输入--memset

1256:献给阿尔吉侬的花束--BFS多组输入--memset 题目 解析代码【结构体】用book标记且计步数的代码[非结构体法] 题目 解析 标准的BFS题目,在多组输入中要做的就是先找到这一组的起点和终点,然后将其传给bfs,在多组输入中最易忘记…

【JavaEE】SpringBoot快速上手,探秘 Spring Boot,搭建 Java 项目的智慧脚手架

1.Spring Boot介绍 在学习SpringBoot之前, 我们先来认识⼀下Spring ,我们看下Spring官⽅的介绍 可以看到,Spring让Java程序更加快速, 简单和安全。 Spring对于速度、简单性和⽣产⼒的关注使其成为世界上最流⾏的Java框架。 Spring官⽅提供了很多开源的…

【C】初阶数据结构9 -- 直接插入排序

前面我们学习了数据结构二叉树,接下来我们将开启一个新的章节,那就是在日常生活中经常会用到的排序算法。 所谓排序算法就是给你一堆数据,让你从小到大(或从大到小)的将这些数据排成一个有序的序列(这些数据…

Lottie与LottieFiles:快速为前端Web开发注入精美动画的利器

目录 Lottie与LottieFiles:快速为前端Web开发注入精美动画的利器 一、Lottie是什么?从GIF到JSON的动画技术演进 1、传统动画臃肿的Gif 2、Lottie的突破性创新 二、Lottie的核心组件解析(Lottie的技术架构) 1、Lottie核心三要…

音乐API

https://neteasecloudmusicapi.vercel.app/docs/#/https://neteasecloudmusicapi.vercel.app/docs/#/ 使用实例 所有榜单内容摘要 说明 : 调用此接口,可获取所有榜单内容摘要 接口地址 : /toplist/detail 调用例子 : /toplist/detail 获取歌单所有歌曲 说明 : 由于网易云…

从零搭建微服务项目Pro(第3-1章——本地/OSS图片文件存取)

前言: 在小型demo项目中,一般将图片音频等字节流文件存放本地数据库,但企业级项目中,由于数据量容量有限,需要借助OSS来管理大规模文件。 OSS(对象存储服务,Object Storage Service&#xff0…

游戏引擎学习第147天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾 具体来说,我们通过隐式计算来解决问题,而不是像数字微分分析器那样逐步增加数据。我们已经涵盖了这个部分,并计划继续处理音量问题。不过,实际上我们现在不需要继续处理…

Spring boot启动原理及相关组件

优质博文:IT-BLOG-CN 一、Spring Boot应用启动 一个Spring Boot应用的启动通常如下: SpringBootApplication Slf4j public class ApplicationMain {public static void main(String[] args) {ConfigurableApplicationContext ctx SpringApplication.…

【Linux】信号处理以及补充知识

目录 一、信号被处理的时机: 1、理解: 2、内核态与用户态: 1、概念: 2、重谈地址空间: 3、处理时机: 补充知识: 1、sigaction: 2、函数重入: 3、volatile&…

微服务——网关、网关登录校验、OpenFeign传递共享信息、Nacos共享配置以及热更新、动态路由

之前学习了Nacos,用于发现并注册、管理项目里所有的微服务,而OpenFeign简化微服务之间的通信,而为了使得前端可以使用微服务项目里的每一个微服务的接口,就应该将所有微服务的接口管理起来方便前端调用,所以有了网关。…

comctl32!ListView_OnSetItem函数分析LISTSUBITEM结构中的image表示图标位置

第一部分: BOOL ListView_SetSubItem(LV* plv, const LV_ITEM* plvi) { LISTSUBITEM lsi; BOOL fChanged FALSE; int i; int idpa; HDPA hdpa; if (plvi->mask & ~(LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) { …

数据结构——多项式问题(顺序存储结构or链式存储结构)

补充&#xff1a;malloc函数&#xff1a; malloc 函数是 C 语言标准库中的一个重要函数&#xff0c;位于 <stdlib.h> 头文件中&#xff0c;主要用于在程序运行时动态分配内存。以下将详细介绍其用法。 前面的返回值指针可以自己定义&#xff0c;如 &#xff08;int*&am…

记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)

文章目录 记录小白使用 Cursor 开发第一个微信小程序&#xff08;一&#xff09;&#xff1a;注册账号及下载工具&#xff08;250308&#xff09;一、微信小程序注册摘要1.1 注册流程要点 二、小程序发布流程三、下载工具 记录小白使用 Cursor 开发第一个微信小程序&#xff08…

vue2项目修改浏览器显示的网页图标

1.准备一个新的图标文件&#xff0c;通常是. ico格式&#xff0c;也可以是. Png、. Svg等格式 2.将新的图标文件(例如&#xff1a;faviconAt.png)放入项目的public文件夹中。如下图 public文件夹中的所有文件都会在构建时原样复制到最终的输出目录(通常是dist) 3. 修改vue项目…

【网络安全工程】任务10:三层交换机配置

CSDN 原创主页&#xff1a;不羁https://blog.csdn.net/2303_76492156?typeblog三层交换机是指在OSI&#xff08;开放系统互连&#xff09;模型中的第三层网络层提供路由功能的交换机。它不仅具备二层交换机的交换功能&#xff0c;还能实现路由功能&#xff0c;提供更为灵活的网…

侯捷 C++ 课程学习笔记:C++内存管理机制

内存管理从平地到万丈高楼 内存管理入门&#xff08;Memory Management 101&#xff09; 需要具有动态分配并使用memory&#xff08;存储&#xff08;器&#xff09;&#xff0c;&#xff08;计算机的&#xff09;内存&#xff09;&#xff0c;使用过C标准库的容器&#xff0…

JVM常用概念之本地内存跟踪

问题 Java应用启动或者运行过程中报“内存不足&#xff01;”&#xff0c;我们该怎么办? 基础知识 对于一个在本地机器运行的JVM应用而言&#xff0c;需要足够的内存来存储机器代码、堆元数据、类元数据、内存分析等数据结构&#xff0c;来保证JVM应用的成功启动以及未来平…

【鸿蒙开发】Hi3861学习笔记- 软件定时器示例

00. 目录 文章目录 00. 目录01. 定时器概述02. 定时器API03. 定时器常用API3.1 osTimerNew3.2 osTimerDelete3.3 osTimerStart3.4 osTimerStop 04. 程序示例05. 附录 01. 定时器概述 软件定时器&#xff0c;是基于系统Tick时钟中断且由软件来模拟的定时器&#xff0c;当经过设…

在Html5中仿Matlab自定义色带生成实践

目录 前言 一、RGB的相关知识 1、RGB的基本原理 2、RGB的数值表示 3、应用场景 二、ColorMap生成实战 1、外部库介绍 2、相关API 3、实例生成 三、总结 前言 在现代网页开发与数据可视化领域&#xff0c;色彩的表现力对于信息传达和视觉体验起着至关重要的作用。色带&…

贪心算法--

1.柠檬水找零 link:860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法&#xff0c; 优先花出大面额bill&#xff0c; 尽可能保护小面额billint five 0, ten 0;// 不…