百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

仅用逻辑回归来对非线性决策边界建模

wptr33 2025-02-27 16:56 16 浏览

数据科学家,会经常被复杂的机器学习技术所吸引。使用某些深度神经网络(DNN)压缩1%的额外精度并在此过程中必须启动一个GPU实例,这使人非常满意。但是,这些技术通常使您的想法落到计算机上,使我们对模型的工作方式了解甚少。所以,本文想回到基础。

在本文中,我希望教给您一些有关特征工程的知识,以及如何使用它来对非线性决策边界进行建模。我们将探讨两种技术的优缺点:逻辑回归(具有特征工程)和NN分类器。将给出用于拟合这些模型以及可视化其决策边界的Python代码。您还可以在上找到完整的项目。最后,我希望让您理解为什么特征工程可能是其他非线性建模技术的更好替代方案。

什么是特征工程

每当您从原始数据创建特征或将现有特征添加到数据集时,就在进行特征工程。这通常是使用特定领域的领域知识完成的。例如,假设我们要预测一个人从手术中恢复所花费的时间。从以前的手术中,我们已经捕获了患者的康复时间,身高和体重。根据这些数据,我们还可以计算每个患者的BMI =身高/体重2。通过计算BMI并将其包括在数据集中,我们正在进行特征工程。



我们为什么进行特征工程

特征工程功能强大,因为它允许我们将非线性问题重新表述为线性问题。为了说明这一点,假设恢复时间与身高和体重具有以下关系:

Y =β?+β?(height)+β2(weight)+β?(height/weight2)+noise

从第三项来看,我们可以看到Y与身高和体重没有线性关系。这意味着我们可能不会期望线性模型(例如线性回归)在估计β系数方面做得很好。您可以尝试使用非线性模型(例如DNN),也可以通过进行一些特征工程来帮助我们的模型。如果我们决定将BMI作为功能包括在内,则关系变为:

Y =β?+β?(height)+β2(weight)+β?(BMI)+noise

现在可以将Y建模为3个变量的线性关系。结果,我们期望线性回归在估计系数方面做得更好。稍后,我们将看到相同的想法适用于分类问题。

为什么不让计算机来做

特征工程从本质上讲就是内核技巧,因为我们正在将特征映射到更高的平面。尽管使用内核技巧,通常很少涉及到思想。内核函数被视为超参数,可以使用蛮力找到最佳函数-尝试各种函数变化的负载。使用正确的内核函数,您可以为非线性关系建模。给定适当数量的隐藏层/节点,DNN还将自动构造特征的非线性函数。那么,如果这些方法可以对非线性关系进行建模,那么为什么还要打扰特征工程呢?

上面我们解释了特征工程如何使我们即使使用线性模型也可以捕获数据中的非线性关系。这意味着,根据问题,我们可以获得与非线性模型相似的性能。在本文后面的部分中,我们将详细介绍一个示例。除此之外,使用特征工程还有其他好处,这使这项技术值得一试。

首先,您将对模型的工作原理有更好的了解。这是因为您确切地知道模型用于进行预测的信息。此外,通过直接查看特征系数,可以轻松地解释逻辑回归等模型。第一个原因,即该模型更易于解释。如果您在行业中工作,这尤其重要。您的同事更有可能会接触到更简单的模型。第三个原因是您的模型不太适合训练数据。通过对不同超参数的强行加载,很容易最终在数据中建模噪声。相比之下,具有经过深思熟虑的特征,您的模型将是直观的,并且可能会模型化真实的潜在趋势。



数据集

让我们深入一个实际的例子。为了使内容尽可能清晰,将使用人工生成的数据集。为避免该示例过于沉闷,我们将围绕它创建一个叙述。因此,假设您的人事部门要求您创建一个模型,该模型可以预测是否会升职。该模型应考虑员工的年龄和绩效得分。

我们在下面的代码中为2000名假设的员工创建特征。员工的年龄可以在18至60岁之间。绩效得分可以在-10至10之间(最高为10分)。两种特征都经过混洗,因此它们之间没有关联。然后,我们使用年龄(a)和绩效(p)的以下函数来生成目标变量:

γ(a,p)= 100(a)+ 200(p)+ 500(a / p)-10000 + 500(noise)

当γ(a,p)≥0时,则晋升员工;当γ(a,p)<0时,则不晋升员工。我们可以看到术语a / p包含在上面的函数中。这意味着决策边界将不是年龄和绩效的线性函数。还包括随机噪声,因此数据无法完全分离。换句话说,模型不可能是100%准确的。

import numpy as np 
 import pandas as pd 
   n_points = 2000 
   age = np.round(np.linspace(18,60,n_points),2) #age of employee 
 np.random.shuffle(age)  
   performance = np.linspace(-10,10,n_points) #performance score of employee 
 np.random.shuffle(performance)  
   noise = np.random.randn(n_points)  
   g = (100*age) +200*(performance) + 500*age/performance -10000 + 500*noise 
 y = [1 if y>=0 else 0 for y in g]  
   data = pd.DataFrame(data={'age':age,'performance':performance,'y':y})

如果上面的步骤有点混乱,请不要担心。通过使用以下代码可视化数据集,我们可以使事情更清晰。在这里,我们创建了数据的散点图,结果如图1所示。只有两个功能,可以很容易地准确了解正在发生的事情。在y轴上,我们拥有员工的绩效得分,在x轴上,我们拥有员工的年龄。晋升员工的分数为红色,未晋升员工的分数为蓝色。最后,在2000名员工中,有459名(占23%)获得了晋升。对于不同的随机样本,此比率将略有变化。

import matplotlib.pyplot as plt 
 %matplotlib inline 
   plt.subplots(nrows=1, ncols=1,figsize=(15,10)) 
   plt.scatter('age','performance',c='#ff2121',data=data[data.y == 1]) 
 plt.scatter('age','performance',c='#2176ff',data=data[data.y == 0]) 
 plt.ylabel("Performance Score",size=20) 
 plt.xlabel('Age',size=20) 
 plt.yticks(size=12) 
 plt.xticks(size=12) 
 plt.legend(['Promoted','Not Promoted'],loc =2,prop={"size":20})

尽管生成了这些数据,但我们仍然可以对图进行实际说明。在图1中,我们可以看到3个不同的雇员组。第一个是绩效得分低于0的组。由于绩效不佳,这些员工中的大多数没有得到晋升,我们也可以期望其中的一些人被解雇。我们可以期望得分高于0的员工获得升职或接受其他职位。得分特别高的员工倾向于离职。这可能是因为它们的需求量很大,并且在其他地方收到了更好的报价。但是,随着雇主的年龄增长,他们需要更高的绩效分数才能离职。这可能是因为年长的员工更愿意担任目前的职位。

无论哪种叙述,很明显决策边界不是线性的。换句话说,不可能画出一条直线来很好地区分被提升和未被提升的群体。结果,我们不会期望线性模型做得很好。让我们通过仅使用年龄和绩效这两个特征来拟合逻辑回归模型来证明这一点。

逻辑回归

在下面的代码中,我们将2000名员工分为训练集(70%)和测试集(30%)。我们使用训练集来训练逻辑回归模型。然后,使用该模型,对测试集进行预测。测试仪的准确性为82%。这看起来似乎还不错,但是我们应该考虑不到23%的员工获得了晋升。因此,如果我们只是猜测没有一个员工会获得升职,那么我们应该期望其准确性约为77%。

from sklearn.model_selection import train_test_split 
 import sklearn.metrics as metric 
 import statsmodels.api as sm 
   x = data[['age','performance']] 
 x = sm.add_constant(x) 
 y = data['y'] 
 x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.3, random_state=101) 
   model = sm.Logit(y_train,x_train).fit() #fit logistic regression model 
   predictions = np.around(model.predict(x_test))  
 accuracy = metric.accuracy_score(y_test,predictions)

通过使用以下代码可视化模型的决策边界,我们可以更好地了解模型的特征。在这里,我们在样本空间内生成一百万个点。然后,我们使用逻辑回归模型对所有这些点进行预测。像图1中的散点图一样,我们可以绘制每个点。每个点的颜色由模型的预测确定-如果模型预测升级,则为粉红色,否则为浅蓝色。这使我们可以很好地近似决策边界,如图2所示。然后,我们可以在这些点上绘制实际数据集。

n_points = 1000000 #use many point to visualise decision boundry 
   age_db = np.linspace(18,60,n_points) 
 np.random.shuffle(age_db) 
   performance_db= np.linspace(-10,10,n_points) 
 np.random.shuffle(performance_db) 
   data_db = pd.DataFrame({'age':age_db,'performance':performance_db}) 
 data_db = sm.add_constant(data_db) 
   #make predictions on the decision boundry points 
 predictions = model.predict(data_db) 
 y_db = [round(p) for p in predictions]  
 data_db['y'] = y_db 
   fig, ax = plt.subplots( nrows=1, ncols=1,figsize=(15,10)) 
   #Plot decision boundry 
 plt.scatter('age','performance',c='#ffbdbd',s=1,data=data_db[data_db.y == 1]) 
 plt.scatter('age','performance',c='#b0c4ff',s=1,data=data_db[data_db.y == 0]) 
   #Plot employee data points  
 plt.scatter('age','performance',c='#ff2121',data=data[data.y == 1]) 
 plt.scatter('age','performance',c='#2176ff',data=data[data.y == 0]) 
 plt.ylabel("Performance Score",size=20) 
 plt.xlabel('Age',size=20) 
 plt.yticks(size=12) 
 plt.xticks(size=12)

查看决策边界,我们可以看到模型做得很糟糕。它预测有晋升的员工中大约有一半没有晋升。然后,对于大多数获得晋升的员工,它预测他们没有获得晋升。注意,决策边界是一条直线。这强调逻辑回归是线性分类器。换句话说,模型只能构造一个决策边界,该边界是您提供的要素的线性函数。在这一点上,我们可能很想尝试其他模型,但让我们看看是否可以使用特征工程来提高性能。



用于特征工程的逻辑回归

首先,如下面的代码所示,我们添加了其他特征(即年龄与性能的比率)。从那时起,我们将遵循与先前模型相同的过程。火车测试拆分与我们为" random_state"使用相同的值相同。最终,该模型实现了98%的准确度,这是一个重大改进。

该模型仍然只需要员工的年龄和绩效即可做出预测。这是因为附加功能是年龄和性能的函数。这使我们能够像以前一样可视化决策边界。那就是通过在样本空间中每个年龄表现点使用模型的预测。我们可以看到,在图3中,通过添加附加特征,逻辑回归模型能够对非线性决策边界进行建模。从技术上讲,这是年龄和性能的非线性函数,但仍是所有3个特征的线性函数。

使用逻辑回归的另一个好处是该模型是可以解释的。这意味着该模型可以用人类的术语来解释。换句话说,我们可以直接查看模型系数以了解其工作原理。我们可以在表1中看到模型特征的系数以及它们的p值。我们不会详细介绍,但是系数允许您根据获得的几率的变化来解释特征的变化。促销。如果某个特征的系数为正,则该特征值的增加会导致获得晋升的几率增加。

从表1可以看出,随着年龄的增长,晋升的可能性也会增加。另一方面,对于性能而言,这种关系并不明确。绩效的提高也会降低年龄/绩效比率。这意味着绩效提高的效果取决于员工的年龄。这非常直观,因为它与我们在散点图中看到的一致。在这种情况下,可能没有必要使用系数来以此方式解释模型。仅可视化决策边界就足够了,但是随着功能数量的增加,执行此操作变得更加困难。在这种情况下,模型系数是了解模型工作原理的绝佳工具。

同样,p值可以帮助我们理解模型。由于系数是统计估计值,因此它们周围存在一些不确定性。低p值使我们可以确定我们的系数不同于0。换句话说,我们可以确定系数为正或负。这很重要,因为如果我们不确定系数的符号,就很难用几率的变化来解释特征的变化。从表1可以看出,所有系数在统计上都是显着的。这并不奇怪,因为我们使用功能的功能生成了数据。

总的来说,当我们生成数据时,上面的分析非常简单。因为我们知道使用了什么函数来生成数据,所以很明显,附加特征将提高模型的准确性。实际上,这不是那么简单。如果您对数据没有很好的了解,则可能需要与人事部门的人员交谈。他们也许能够将过去所看到的任何趋势通知您。否则,通过使用各种图表和汇总统计数据探索该数据,您可以了解哪些特征可能很重要。但是,假设我们不想做所有这些艰苦的工作。

神经网络

为了进行比较,让我们使用非线性模型。在下面的代码中,我们使用Keras拟合神经网络。我们仅使用年龄和性能作为特征,因此NN的输入层的尺寸为2。有2个隐藏层,分别具有20和15个节点。两个隐藏层均具有relu激活功能,输出层具有S型激活功能。要训??练模型,我们使用10和100个纪元的批量。训练集大小为1400,这使我们有14000步。最后,该模型在测试集上达到了98%的精度。这与逻辑回归模型的准确性相同,但是我们不必进行任何特征工程。

查看图4中的NN决策边界,我们可以看到为什么将其视为非线性分类算法。即使我们只给出了模型的年龄和性能,它仍然能够构造非线性决策边界。因此,该模型在一定程度上为我们做了艰苦的工作。您可以说模型的隐藏层已自动完成要素工程。然后,考虑到该模型具有很高的准确性,并且我们需要较少的工作,为什么还要考虑逻辑回归呢?

NN的缺点是只能解释。这意味着,与逻辑回归不同,我们无法直接查看模型的参数以查看其工作原理。我们可以使用其他方法,但是最终,要理解NN的工作原理更加困难。向非技术人员解释这一点更加困难。这使得逻辑回归模型在行业环境中的价值更大。

工业界和学术界都存在许多问题,而且大多数问题都比本文中给出的示例更为复杂。所提出的方法显然不是所有这些问题的最佳解决方案。例如,如果您要进行图像识别,那么Logistic回归将无济于事。对于更简单的问题,通常只需要逻辑回归和对数据的充分理解。


相关推荐

十年之重修Redis原理(redis重试机制)

弱小和无知并不是生存的障碍,傲慢才是。--------面试者...

Redis 中ZSET数据类型命令使用及对应场景总结

1.zadd添加元素zaddkeyscoremember...

redis总结(redis常用)

RedisTemplate封装的工具类packagehk.com.easyview.common.helper;importcom.alibaba.fastjson.JSONObject;...

配置热更新系统(如何实现热更新)

整体设计概览┌────────────┐┌────────────────┐┌────────────┐│配置后台服务│--写入-->│Red...

java高级用法之:调用本地方法的利器JNA

简介JAVA是可以调用本地方法的,官方提供的调用方式叫做JNI,全称叫做javanativeinterface。要想使用JNI,我们需要在JAVA代码中定义native方法,然后通过javah命令...

SpringBoot:如何优雅地进行响应数据封装、异常处理

背景越来越多的项目开始基于前后端分离的模式进行开发,这对后端接口的报文格式便有了一定的要求。通常,我们会采用JSON格式作为前后端交换数据格式,从而减少沟通成本等。...

Java中有了基本类型为什么还要有包装类型(封装类型)

Java中基本数据类型与包装类型有:...

java面向对象三大特性:封装、继承、多态——举例说明(转载)

概念封装:封装就是将客观的事物抽象成类,类中存在属于这个类的属性和方法。...

java 面向对象编程:封装、继承、多态

Java中的封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)是面向对象编程的三大基本概念。它们有助于提高代码的可重用性、可扩展性和可维护性。...

怎样解析java中的封装(怎样解析java中的封装文件)

1.解析java中的封装1.1以生活中的例子为例,打开电视机的时候你只需要按下开关键,电视机就会打开,我们通过这个操作我们可以去间接的对电视机里面的元器件进行亮屏和显示界面操作,具体怎么实现我们并不...

python 示例代码(python代码详解)

以下是35个python代码示例,涵盖了从基础到高级的各种应用场景。这些示例旨在帮助你学习和理解python编程的各个方面。1.Hello,World!#python...

python 进阶突破——内置模块(Standard Library)

Python提供了丰富的内置模块(StandardLibrary),无需安装即可直接使用。以下是一些常用的内置模块及其主要功能:1.文件与系统操作...

Python程序员如何调试和分析Python脚本程序?附代码实现

调试和分析Python脚本程序调试技术和分析技术在Python开发中发挥着重要作用。调试器可以设置条件断点,帮助程序员分析所有代码。而分析器可以运行程序,并提供运行时的详细信息,同时也能找出程序中的性...

python中,函数和方法异同点(python方法和函数的区别)

在Python中,函数(Function)...

Python入门基础命令详解(python基础入门教程)

以下是Python基本命令的详解指南,专为初学者设计,涵盖基础语法、常用操作和实用示例:Python基本命令详解:入门必备指南1.Python简介特点:简洁易读、跨平台、丰富的库支持...