人工智能与机器学习:Python从零实现逻辑回归模型

🧠 向所有学习者致敬!

“学习不是装满一桶水,而是点燃一把火。” —— 叶芝


我的博客主页: https://lizheng.blog.csdn.net

🌐 欢迎点击加入AI人工智能社区!

🚀 让我们一起努力,共创AI未来! 🚀


嘿,朋友们!今天咱们要来搞个大事情——自己动手实现逻辑回归!为啥要这么干呢?因为有时候,用现成的工具就像吃别人嚼过的糖,没劲!自己动手,那才叫真本事,不仅能搞懂背后的原理,还能在朋友面前炫耀一番:“看我这代码,多牛!”所以,咱们这就开始,一起踏上这个充满挑战和乐趣的旅程吧!🚀

逻辑回归

逻辑回归是一种算法,能帮咱们预测一个变量的两个类别。现实生活里头,这玩意儿能干不少事儿,比如:

  • 检测垃圾邮件
  • 预测客户会不会流失
  • 预测客户会不会拖欠贷款

这些用例有个共同点:它们的结果都只有两种。当然啦,逻辑回归也有能预测多个结果的类型,不过这篇文章就只讲二分类的逻辑。

逻辑回归的假设(二分类)

你会发现逻辑回归模型的假设和线性回归模型很像,因为逻辑回归也是线性的。

  • 二元因变量:目标变量必须只有两种可能的结果。
  • 没有多重共线性:自变量之间应该没啥相关性,不然模型的效果会大打折扣。
  • 自变量与对数几率的线性关系:对数几率是另一种表示概率的方式,形式上就是 log ⁡ ( p / ( 1 − p ) ) \log(p/(1-p)) log(p/(1p))

还有一些对其他模型也很关键的假设,比如没有异常值(如果有,记得处理一下),样本量要足够大。虽然没有明确的样本量阈值,但记住:样本越多越好。

搭建我们的逻辑回归对象

咱们先从创建初始化函数和对象里要用到的属性开始。你会看到初始化函数里有三个属性:学习率(learning_rate)、迭代次数(iterations)和测试集比例(test_size)
在用梯度下降找到模型最优权重的时候,fit 方法会用到学习率和迭代次数这两个属性。等咱们讲到这一步的时候,还会详细说说。咱们还加了一个训练集和测试集划分的方法,能将特征和目标变量的数据分成训练集和测试集。
自己动手搭建这些对象的时候,我喜欢把能用的功能都塞进去,这样后面写代码的时候就能更省事儿。

大部分属性一看就懂,除了 min_max_。咱们会在这些属性里存归一化缩放器的信息。对数据进行归一化处理,能让咱们在模型拟合的时候更快地找到解决方案。

import numpy as np
import pandas as pdclass DIYLogisticRegression:def __init__(self, learning_rate=0.01, iterations=1000, test_size=0.2):# 学习率,用于控制梯度下降的步长self.learning_rate = learning_rate# 迭代次数,决定了训练过程的循环次数self.iterations = iterations# 测试集占总数据集的比例self.test_size = test_size# 权重,初始值为 None,稍后会初始化self.W = None# 偏置项,初始值为 None,稍后会初始化self.b = None# 训练集特征数据self.X_train = None# 测试集特征数据self.X_test = None# 训练集目标数据self.y_train = None# 测试集目标数据self.y_test = None# 用于归一化处理的最小值self.min_ = None# 用于归一化处理的最大值self.max_ = None

训练集/测试集划分与归一化

要正确地训练机器学习模型,必须用一个数据集来训练,另一个数据集来验证。机器学习生命周期里的另一个关键步骤是数据清洗,这可能包括归一化处理。咱们会创建一个方法,把特征和目标数据分成训练集和测试集,同时填充相应的属性。这个方法还会用最大最小值归一化方法对训练数据进行缩放,并把同样的逻辑应用到测试数据上。这就是 min_max_ 属性的用武之地啦。
注意,这些最小值和最大值是从训练集中得到的,然后应用到测试集上。为啥要这么做呢?假设咱们训练好了一个模型,把它放到生产环境中,那咱们根本不知道新数据的上下限是多少,所以只能依赖从训练集中提取出来的逻辑。

def train_test_split_scale(self, X, y):## 训练集和测试集划分n_samples = X.shape[0]test_size = int(n_samples * self.test_size)indices = np.arange(n_samples)np.random.shuffle(indices)test_indices = indices[:test_size]train_indices = indices[test_size:]self.X_train, self.X_test = X.iloc[train_indices], X.iloc[test_indices]self.y_train, self.y_test = y.iloc[train_indices], y.iloc[test_indices]# 最大最小值归一化self.min_ = self.X_train.min()self.max_ = self.X_train.max()self.X_train = (self.X_train - self.min_) / (self.max_ - self.min_)self.X_test = (self.X_test - self.min_) / (self.max_ - self.min_)

Sigmoid 函数

文章前面提到,咱们要搭建的是一个预测二元结果的逻辑回归模型,背后的原理其实是线性模型:

None

图由作者提供

和线性回归一样,咱们把线性模型设为等于 (z)。这个变量会被代入 Sigmoid 函数。Sigmoid 函数是逻辑回归模型做出预测之前的最后一步。这个函数的关键特性是,它的输出值会被限制在 0 和 1 之间。现在知道为啥逻辑回归要用它了吧?有了 Sigmoid 函数的输出,通常情况下,如果值大于或等于 0.5,咱们就预测二元变量的结果为 1,否则就是 0。当然啦,这个 0.5 的决策边界也可以根据具体需求调整。咱们这就把这个函数作为对象的一个方法加进去。

None

图由作者提供

def sigmoid(self, z):# Sigmoid 函数,将输入值映射到 0 和 1 之间return 1 / (1 + np.exp(-z))

最大似然估计和二元交叉熵损失

要理解逻辑回归,有一个最基本的概念必须得搞清楚,那就是怎么找到特征数据的权重 (W)。这个是通过最大似然估计(MLE)来实现的。MLE 的过程就是找到最能契合咱们数据的权重。一开始我琢磨这个概念的时候,总是在想,找到最优权重是不是就能得到最高的预测准确率呢?其实不是这么回事。最大似然估计函数(咱们要最大化这个函数)的目标是找到最能契合数据内在模式的权重。举个例子,假设咱们有一条观测数据,它的特征并没有明显显示出目标变量是 1 还是 0 的模式。那找到的最优权重和偏置就应该预测这个结果接近 0.5。再假设咱们有另一条观测数据,它的特征明显和预测结果为 1 的模式强相关,那咱们找到的权重和偏置就应该预测这个观测的结果接近 1。
所以,知道最大化 MLE 函数能找到最优权重,但它是怎么做到的呢?这就要通过梯度下降这个迭代过程来实现。梯度下降更适合最小化问题,所以咱们会用 MLE 函数的负值,也就是二元交叉熵损失
在梯度下降的每一步迭代中,咱们都要计算这个损失函数。接下来的部分,咱们会详细讲讲怎么做到这一点。
先看看计算这个损失函数的方法。你会注意到,咱们在这个损失函数里加了一个额外的步骤,用到了 np.clip() 函数。这个损失函数很容易得到极小值。
一旦出现这种情况,Python 可能会直接四舍五入到 0,那就会出现对数为 0 的情况,这是未定义的。np.clip() 函数会设置一个极小值下限,这样咱们就不会得到 0 了。

None

图由作者提供

def compute_loss(self, y, y_hat):# 计算二元交叉熵损失epsilon = 1e-10y_hat = np.clip(y_hat, epsilon, 1 - epsilon)return -np.mean(y * np.log(y_hat) + (1 - y) * np.log(1 - y_hat))

找到最优权重和偏置

这一步就是咱们要把模型拟合到数据上了。正如前面提到的,这是一个叫梯度下降的迭代过程。一开始,咱们会随机初始化权重和偏置的值,然后用它们通过线性函数和 Sigmoid 激活函数来预测每一个 (y) 值。这些预测值就叫 (y_{\text{hat}})。有了 (y_{\text{hat}}) 之后,咱们就能通过二元交叉熵损失函数对权重和偏置进行调整,朝着更优的解靠近。具体来说,咱们要对二元交叉熵损失函数分别对权重和偏置求偏导数。

None

图由作者提供

None

图由作者提供

接下来,咱们把这两个导数值乘以学习率 (\alpha),然后从当前的权重和偏置中减去。这个过程会不断重复,直到损失函数的值不再下降。

None

图由作者提供

在咱们的 fit 方法里,每迭代 100 次就输出一次损失函数的值,看看它是不是正确收敛了。如果学习率太高,损失值可能会一直越过最小值,那咱们就永远也“撞”不到最优权重和偏置了。反过来,如果学习率太小,那可能要经过很多次迭代才能“撞”到最小值。

def fit(self):n_samples, n_features = self.X_train.shape# 随机初始化权重self.W = np.random.randn(n_features) * 0.01# 初始化偏置为 0self.b = 0for i in range(self.iterations):linear_model = np.dot(self.X_train, self.W) + self.by_hat = self.sigmoid(linear_model)dW = (1 / n_samples) * np.dot(self.X_train.T, (y_hat - self.y_train))db = (1 / n_samples) * np.sum(y_hat - self.y_train)self.W -= self.learning_rate * dWself.b -= self.learning_rate * dbif i % 100 == 0:loss = self.compute_loss(self.y_train, y_hat)print(f"Iteration {i}, Loss: {loss:.4f}")

预测和评估

重头戏已经搞定了!接下来只要再加几个方法就行啦。一切都已经准备就绪,咱们可以开始添加预测和评估方法了。首先,咱们会创建一个方法,用拟合过程中找到的最优权重,代入线性函数,再通过 Sigmoid 方法,预测结果的概率。这个方法最终会输出一个介于 0 和 1 之间的值,也就是咱们对预测结果的概率估计。

接下来,我会再加一个预测方法,它会根据一个阈值,把概率转换成 1 或者 0。默认的阈值就设为 0.5 吧。

最后,我还会加一个综合评估方法,打印出混淆矩阵、精确率、召回率,还有咱们模型的整体准确率。注意,这些都是基于咱们的测试集来评估模型性能的。

def predict_proba(self, X):"""预测给定输入 X 的概率"""linear_model = np.dot(X, self.W) + self.breturn self.sigmoid(linear_model)def predict(self, X, threshold=0.5):"""根据阈值预测类别标签(0 或 1)"""probabilities = self.predict_proba(X)return (probabilities >= threshold).astype(int)def evaluate(self):"""使用精确率、召回率、准确率和混淆矩阵评估模型"""y_pred = self.predict(self.X_test)tp = np.sum((self.y_test == 1) & (y_pred == 1))fp = np.sum((self.y_test == 0) & (y_pred == 1))fn = np.sum((self.y_test == 1) & (y_pred == 0))tn = np.sum((self.y_test == 0) & (y_pred == 0))precision = tp / (tp + fp) if (tp + fp) > 0 else 0recall = tp / (tp + fn) if (tp + fn) > 0 else 0accuracy = (tp + tn) / (tp + tn + fp + fn)print("混淆矩阵:")print(f"TP: {tp}, FP: {fp}")print(f"FN: {fn}, TN: {tn}\n")print(f"精确率(预测为正的样本中实际为正的比例): {precision:.4f}")print(f"召回率(所有实际为正的样本中预测正确的比例): {recall:.4f}")print(f"准确率(所有样本预测正确的比例): {accuracy:.4f}")

测试我们的对象

咱们来看看这个逻辑回归对象在实际中的表现吧。为了测试它,咱们用的是 Kaggle 上的 心脏病发作预测数据集。这个数据集有 13 列,其中 12 列是特征,最后一列是目标变量 DEATH_EVENT。幸好,咱们的对象已经具备了处理数据、训练模型和评估模型的所有功能。

df = pd.read_csv("heart_failure_clinical_records_dataset.csv")  # 加载数据集
X = df.drop('DEATH_EVENT', axis=1)
y = df['DEATH_EVENT']model = DIYLogisticRegression(learning_rate=0.1, iterations=1000, test_size=0.25)
model.train_test_split_scale(X, y)
model.fit()

None

图由作者提供

model.evaluate()

None
还不错嘛,咱们的模型准确率达到了大约 80%。而且损失函数也收敛得很好,每次迭代的损失值都在下降。咱们来好好聊聊数据和模型吧。这个模型能帮咱们告诉患者他们是否有心脏病发作的风险,所以可别小瞧了它的表现哦。我列出了三个评估指标:精确率、召回率和准确率。你觉得哪个最重要呢?要是咱们告诉某人他有心脏病发作的风险,但实际上他没有,这算不算坏事呢?我觉得不算,但如果咱们没告诉某人他有风险,而他其实有,那可就太糟糕了。所以,咱们应该尽量提高召回率,它能告诉我们所有实际为正的样本中有多少被正确预测了。

和 Sklearn 的逻辑回归对象对比

咱们成功地从零搭建了一个逻辑回归对象,现在来看看它和 sklearn 的逻辑回归类比起来怎么样吧。

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_scoreX = df.drop('DEATH_EVENT', axis=1)
y = df['DEATH_EVENT']# 训练集和测试集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)# 最大最小值归一化
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)# 初始化并训练逻辑回归模型
model = LogisticRegression()
model.fit(X_train, y_train)# 在测试数据上进行预测
y_pred = model.predict(X_test)# 计算评估指标
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
cm = confusion_matrix(y_test, y_pred)# 显示结果
print("混淆矩阵:")
print(f"TP: {cm[1,1]}, FP: {cm[0,1]}")
print(f"FN: {cm[1,0]}, TN: {cm[0,0]}")
print(f"精确率: {precision:.4f}")
print(f"召回率: {recall:.4f}")
print(f"准确率: {accuracy:.4f}")

None

总的来说,评估指标都很接近!感谢您阅读这篇文章!希望您通过这篇文章对逻辑回归模型有了更深入的理解。

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

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

相关文章

济南国网数字化培训班学习笔记-第二组-5节-输电线路设计

输电线路设计 工程设计阶段划分 35kv及以上输变电工程勘测设计全过程 可行性研究(包括规划、工程选站)(包括电力系统一次二次,站址选择及工程设想,线路工程选择及工程设想,节能降耗分析,环境…

【Linux网络】TCP服务中IOService应用与实现

📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…

Linux 怎么找Java程序的监听的端口

Linux 怎么找Java程序的监听的端口 1、假设你知道启动该Java应用的进程ID (PID),可以通过以下命令查找其监听的端口: 首先找到该Java应用的PID: ps -ef | grep xxxx-1.0-RELEASE.jar或者,如果你知道启动命令的一部分&#xff0…

解读《数据资产质量评估实施规则》:企业数据资产认证落地的关键指南

随着“数据要素市场”建设加速,数据资产逐步成为企业核心资产之一。2024年4月,由中国质量认证中心(CQC)发布的《数据资产质量评估实施规则》(编号:CQC96-831160-2024)正式实施,为企业…

[吾爱出品] 【键鼠自动化工具】支持识别窗口、识图、发送文本、按键组合等

键鼠自动化工具 链接:https://pan.xunlei.com/s/VOOhDZkj-E0mdDZCvo3jp6s4A1?pwdfufb# 1、增加的找图点击功能(不算增加,只能算缝补),各种的不完善,但是能运行。 2、因为受限于原程序的界面,…

【计算机视觉】CV实战项目 - 基于YOLOv5的人脸检测与关键点定位系统深度解析

基于YOLOv5的人脸检测与关键点定位系统深度解析 1. 技术背景与项目意义传统方案的局限性YOLOv5多任务方案的优势 2. 核心算法原理网络架构改进关键点回归分支损失函数设计 3. 实战指南:从环境搭建到模型应用环境配置数据准备数据格式要求数据目录结构 模型训练配置文…

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤

以下是在 IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤: 步骤 1:创建 Maven Web 项目 新建项目 File -> New -> Project → 选择 Maven → 勾选 Create from archetype → 选择 maven-archetype-webapp。输入 GroupId(如 com.examp…

基于javaweb的SpringBoot+MyBatis通讯录管理系统设计与实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

EFISH-SBC-RK3588无人机地面基准站项目

一、核心技术组件解析 1. EFISH-SBC-RK3588 性能优势:作为瑞芯微推出的高性能处理器,采用8nm 工艺,搭载ARM Cortex-A76/A55 八核架构,集成强大的 NPU(神经网络处理单元)和 GPU(图形处理器&…

蓝桥杯 4. 卡片换位

卡片换位 原题目链接 题目描述 你玩过华容道的游戏吗? 这是一个类似的,但更简单的游戏。 看下面的 3 2 格子: --------- | A | * | * | --------- | B | | * | ---------在其中放置了 5 张牌,其中: A 表示关…

7.Excel:单元格格式

一 案例 1.案例1 2.案例2 3.案例3 二 三种基本数据类型 补充:在没有任何格式的情况下是这样对齐的。 1.文本 默认左对齐,文本不可参与计算。 2.数值 默认右对齐,数值计算精度是15位。 若超出15位,超出部分显示为0。 3.逻辑值 …

高效DCDC电源芯片在运动控制器中的应用:设计考量、性能评估与可靠性分析

摘要 随着工业自动化的深入推进,运动控制器作为自动化系统的核心组件,对电源芯片的性能提出了极高要求。高效率DCDC电源芯片凭借其卓越的电能转换效率、优异的负载响应特性和高功率密度,在运动控制器领域得以广泛应用。本文以国科安芯的ASP3…

Action:Update your application‘s configuration

在使用Maven项目时,有一个报错信息是:Update your applications configuration 这类问题,就是我们的application.yml文件 或者 application.properties文件 内容哪里写错了 最有可能就是对齐方式有问题

Pycharm(六):可变与不可变类型

一、引用 在java中既有值传递,也有引用传递,我们思考一下在python中值的传递方式是哪种类型呢? 答案是引用传递。 概述: Python中存储变量是需要占用内存空间的,为了更好地管理这些空间,每块空间都是有自己的地址值的。 格式: id(变量名/值) 可以查看变量/…

动态ip与静态ip的概念、区别、应用场景

动态ip与静态ip的区别 前言 一、IP地址的概念和作用 1.1、IP地址的定义 1.2、IP地址的作用 二、动态IP和静态IP的区别 2.1、动态IP和静态IP的定义 2.2、动态IP和静态IP的特点 2.3、动态IP和静态IP的优缺点比较 三、动态IP和静态IP的应用场景 3.1. 动态IP的应用场景 3.2. 静态IP…

六个能够白嫖学习资料的网站

一、咖喱君的资源库 地址:https://flowus.cn/galijun/share/de0f6d2f-df17-4075-86ed-ebead0394a77 这是一个学习资料/学习网站分享平台,包含了英语、法语、德语、韩语、日语、泰语等几十种外国语言的学习资料及平台,这个网站的优势就是外语…

[贪心_7] 最优除法 | 跳跃游戏 II | 加油站

目录 1.最优除法 题解 2.跳跃游戏 II 题解 3.加油站 题解 利用 单调性,可以实现 区间跳跃 1.最优除法 链接: 553. 最优除法 给定一正整数数组 nums,nums 中的相邻整数将进行浮点除法。 例如,nums [2,3,4],我…

“Daz to Unreal”将 G8 角色(包括表情)从 daz3d 导入到 UE5。在 UE5 中,我发现使用某个表情并与闭眼混合后,上眼睑出现了问题

1) Bake & Export Corrective Morphs from Daz before you go into UE5 1) 在进入 UE5 之前,从 Daz 烘焙并导出修正型变形 In Daz Studio 在 Daz Studio 中 Load your G8 head, dial in the exact mix (e.g. Smile 1.0 Eyes Closed 1.0). 加载你的 G8 头部&am…

【机器学习速记】面试重点/期末考试

自用,有错误欢迎评论区指出 目录 一、机器学习基础概念 二、机器学习类型分类 三、经典算法与原理 1. 线性模型 2. 决策树 3. SVM(支持向量机) 4. K近邻(KNN) 5. 贝叶斯分类 6. 集成学习 四、模型评价指标 五、模型泛化能力与调参 六、特征工程与数据预处理 七、维…

Redis 原子操作

文章目录 前言✅ 一、什么是「原子操作」?🔍 二、怎么判断一个操作是否原子?🧪 三、项目中的原子 vs 非原子案例(秒杀系统)✅ 原子性(OK)❌ 非原子性(高风险)…