逻辑回归,原理及代码实现
2021/4/23 18:28:30
本文主要是介绍逻辑回归,原理及代码实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Ⅰ.逻辑回归概述: 逻辑回归(LR,Logistic Regression)是传统机器学习中的一种分类模型,它属于一种在线学习算法,可以利用新的数据对各个特征的权重进行更新,而不需要重新利用历史数据训练。因此在实际开发中,一般针对该类任务首先都会构建一个基于LR的模型作为Baseline Model,实现快速上线,然后在此基础上结合后续业务与数据的演进,不断的优化改进。 由于LR算法具有简单、高效、易于并行且在线学习(动态扩展)的特点,在工业界具有非常广泛的应用。例如:评论信息正负情感分析(二分类)、用户点击率(二分类)、用户违约信息预测(二分类)、用户等级分类(多分类 )等场景。 Ⅱ.本次作业主要使用二元逻辑回归进行线性分类: 1.封装一个逻辑回归的类,用的损失函数是交叉熵。 2.以iris数据集为数据来源(需要处理),利用定义的train_test_split函数,将train和test进行分割,其中测试集占20%,训练集占80%。 3.实现逻辑回归,首先实例化一个类,然后通过训练集训练得到线性函数参数θ,画出决策边界,最后对测试集进行预测。 4.总结,根据本次实验和资料查找分析逻辑回归的优缺点。 注:由于逻辑回归在自然语言处理中有极大的运用,因此本次实验使用传统的逻辑回归(即二元逻辑回归)进行线性分类。
1.封装Logistics模型
注:构建的是二元逻辑回归类,适用于二维数据的线性分类 In [1]: import numpy as np from math import sqrt class LogisticRegression: def __init__(self): """初始化logisticRegression模型""" self.coef_=None#系数 self.intercept=None#系数 self._theta=None# θ[θ0,θ1,θ2]参数 def _sigmoid(self,t): return 1./(1.+np.exp(-t))#Sigmoid函数的输出表征了当前样本标签为1的概率; def fit(self ,X_train,y_train,eta=0.01,n_iters=1e4):#eta学习率,1e4是指1*10的4次方; assert X_train.shape[0]==y_train.shape[0]#assert函数可以在条件不满足程序运行的情况下直接返回错误,而 不必等待程序运行后出现崩溃的情况。 def J(theta,X_b,y):#定义交叉熵损失函数,theta是θ参数,也是最后要优化的参数,X_b由多行[1,X1,X2]组成,y是实际值; y_hat=self._sigmoid(X_b.dot(theta))#首先X_b.dot(θ),然后sigmoid,得到线性判断边界即: hθ(x)=sigmoid(θ0+θ1X1+θ2X2),其中hθ(x)就是预测值y_hat try : return -np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat))/len(y)#这里交叉熵没有添加正则化项 except: return float("inf")#返回无穷 def dJ(theta,X_b,y):#根据交叉熵损失函数,对θ求导 return X_b.T.dot(self._sigmoid(X_b.dot(theta))-y)/len(y) def gradient_descent(X_b,y,initial_theta,eta,n_iters=1e4,epsilon=1e-8): #梯度下降 """四个超参数:eta学习率,theta_initial是θ初始值,epsilon差距判断值是为了判断梯度是否为0(梯度不可能为0或者说很难找到,所以用epsilon=1e-8表示交叉熵值变化幅度替代梯度为0),n_iters迭代下降次数""" theta=initial_theta cur_iter = 0#cur_iter用来计量迭代次数 while cur_iter < n_iters: #开始迭代 gradient = dJ(theta, X_b, y) #梯度等于交叉熵求导值 last_theta = theta #上一个参数θ值 theta = theta - eta * gradient#更换参数 if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):#如果交叉熵变化幅度足够小于epsilon,就得到最优的参数θ,停止循环。 break cur_iter += 1#迭代次数加1 return theta#返回的θ,就是需要求的最优参数 X_b = np.hstack([np.ones((len(X_train), 1)), X_train])# np.hstack()进行的是列操作,将行数与X相同的一维全是1的数组与X矩阵进行列拼接,即:[1i,X1i,X2i]。 initial_theta = np.zeros(X_b.shape[1])#初始化θ,构造出一组长度与x_b行数一样,全是零的数组 self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters) #调用gradient_descent梯度下降函数 self.intercept_ = self._theta[0]#得到θ0 self.coef_ = self._theta[1:]#得到θ1和θ2 return self#返回的是它自己,可以连续调用它的方法 def predict_proba(self, X_predict): """给定待预测数据集X_predict,返回表示X_predict的结果概率向量""" assert self.intercept_ is not None and self.coef_ is not None#θ0、θ1、θ2不为空; assert X_predict.shape[1] == len(self.coef_)#输入的X矩阵列数要与参数长度相同 X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict]) return self._sigmoid(X_b.dot(self._theta)) #经过sigmoid函数,输出的就是(0,1)之间的概率 def predict(self, X_predict): """给定待预测数据集X_predict,返回表示X_predict的结果向量""" assert self.intercept_ is not None and self.coef_ is not None#θ0、θ1、θ2不为空; assert X_predict.shape[1] == len(self.coef_)#输入的X矩阵列数要与参数长度相同 proba = self.predict_proba(X_predict) #调用predict_proba()函数 return np.array(proba >= 0.5, dtype='int')#大于0.5就是1,反之是0,相当于分成0,1两类 def accuracy_score(self, X,y): """根据待测数据集 X 、y和y_predict确定当前模型的准确度""" y_predict = self.predict(X) #预测出y_predict assert len(y) == len(y_predict) #判断正确标签y与预测标签y_predict是否相同 score=np.sum(y_test == y_predict) / len(y) #计算准确率 return score#返回准确率 def __repr__(self): #返回显示类的自定义属性信息 return "LogisticRegression()"
2.实现逻辑回归
2.1处理数据集
In [2]: import matplotlib.pyplot as plt from sklearn import datasets iris = datasets.load_iris()#iris数据集是用来给花做分类的数据集,iris包含150个样本,对应着数据集的每行数据; #每行数据包含每个样本的四个特征和样本的类别信息。 X = iris.data#X是(150,4)矩阵 y = iris.target#y是(150,)矩阵,其中值只有0,1,2;iris数据集是对三种花分类,所以有三种标签就可以。 """******************这里用iris数据集,主要是利用数据集的数据,拼凑出符合实验的数据,个人认为这并不具有具体意义,但强求的说(通过两种特征进行预测标签是0或1的花品种)****************""" X = X[y<2,:2] #计算出X中符合y<2条件的数量为100,则将X矩阵中满足条件的100行提取,然后切前两列的数据。 y = y[y<2]#计算出在y中符合y<2的数量,为100;使y与X数据保持同步。 plt.scatter(X[y==0,0],X[y==0,1],color="r")#横坐标:X[y==0,0]的行满足y=0条件,列是X第一列的所有数据Xi, #纵坐标:X[y==0,1]的行满足y=0条件,列是X第二列的所有数据y,将其得到的(Xi,y)点归为一类 plt.scatter(X[y==1,0],X[y==1,1],color="g")#横坐标:X[y==1,0]的行满足y=1条件,列是X的第一列的所有数据Xi, #纵坐标:X[y==1,1]的行满足y=1条件,列是X第二列的所有数据y,将其得到的(Xi,y)点归为另一类
2.2使用逻辑回归
In [3]: def train_test_split(X,y,test_radio=0.2,seed=None): “”“定义分割数据集函数,测试集占0.2,训练集占0.8;需要把数据要打乱,因为训练集可能是整理好的有序的;有可能在多次打乱情况下,还要指定的部分训练集, 所以需要numpy中的随机seed, 默认是不指定随机seed的”“” assert X.shape[0]==y.shape[0] assert 0.0<=test_radio<=1.0 if seed: # 是否使用随机种子,使随机结果相同,方便debug np.random.seed(seed)#seed( ) 用于指定随机数生成时所用算法开始的整数值。 shuffled_indexes=np.random.permutation(len(X))#permutation()用来随机排列一个数组的。 test_size=int(len(X)*test_radio)#测试集的大小 test_indexes=shuffled_indexes[:test_size]#测试集的随机索引 train_indexes=shuffled_indexes[test_size:]#训练集的随机索引,注意:切片的位置 X_train=X[train_indexes] #train_indexes是个矩阵, """例子:若a=numpy.array[2,3,4,5],在python中不能出现a[[1,2,3]],会错误。而可以b=[1,2,3],a=[b],这返回是[3,4,5],是指b对应的是a的位置 """ y_train=y[train_indexes] #训练集的正确标签 X_test=X[test_indexes] #得到的是测试集和训练集的具体矩阵 y_test=y[test_indexes] #测试集的正确标签 return X_test,X_train,y_train,y_test X_test,X_train,y_train,y_test=train_test_split(X,y,seed=666)#读入数据集 log_reg=LogisticRegression()#实例化 log_reg.fit(X_train,y_train)#训练集训练参数 Out[3]: LogisticRegression()
2.3.绘图
In [4]: def x2(x1):#已知log_reg.coef_和log_reg.intercept_参数(θ参数),可以定义逻辑回归的决策边界函数。 return (-log_reg.coef_[0] * x1 - log_reg.intercept_) / log_reg.coef_[1] x1_plot = np.linspace(4, 8, 1000)#将4到8分成1000个数据,赋值给x1_plot。 x2_plot = x2(x1_plot)#将x1_plot的数据,带入x2函数中,得出1000个x2_plot数据。 """数据集被决策边界分成两类的情况""" plt.scatter(X[y==0,0], X[y==0,1], color="r")#读入数据集y=0标签的离散点 plt.scatter(X[y==1,0], X[y==1,1], color="g")#读入数据集y=1标签的离散点 plt.xlabel("X1",color="b",size=12)#x轴上的名字 plt.ylabel("X2",color="b",size=12)#y轴上的名字 plt.title("train_test",size=18) #标题 plt.annotate("θ0+θ1X1+θ2X2=0",xy=(7,4),xytext=(+30,-30),textcoords='offset points',fontsize=16, arrowprops=dict(arrowstyle='->',connectionstyle='arc3,rad=.2')) #添加标记 plt.legend(['label:0','label:1'],loc="best") #标签 plt.plot(x1_plot, x2_plot) #绘画线性方程 plt.show() """测试集的数据被决策边界分成两类的情况""" plt.scatter(X_test[y_test==0,0], X_test[y_test==0,1], color="r")#读入测试集y=0标签的离散点 plt.scatter(X_test[y_test==1,0], X_test[y_test==1,1], color="g")#读入测试集y=0标签的离散点 plt.xlabel("X1",color="b",size=12) #x轴上的名字 plt.ylabel("X2",color="b",size=12) #y轴上的名字 plt.title("test",size=18) #标题 plt.annotate("θ0+θ1X1+θ2X2=0",xy=(7,4),xytext=(+30,-30),textcoords='offset points',fontsize=16, arrowprops=dict(arrowstyle='->',connectionstyle='arc3,rad=.2')) #添加标记 plt.legend(['label:0','label:1'],loc="best") #标签;红色是标签0,绿色是标签1。 plt.plot(x1_plot, x2_plot) #绘画线性方程 plt.show() Out[4]:
3.实验结果
In [5]: a=log_reg.accuracy_score(X_test,y_test) #测试集准确率 b=log_reg.accuracy_score(X_train,y_train) #训练集准确率 c=log_reg.predict_proba(X_test)#预测测试集每个数据的y值的概率 d=log_reg.predict(X_test)#预测测试集每个数据的y值 e=y_test#测试集正确标签y值 print(" 测试集准确率:",a,"\n","训练集准确率:",b,"\n","预测值y概率:",c,"\n 预测值y:",d,"\n","真实值y:",e) Out[5]: 测试集准确率: 1.0 训练集准确率: 0.9875 预测值y概率: [0.92972035 0.98664939 0.14852024 0.01685947 0.0369836 0.0186637 0.04936918 0.99669244 0.97993941 0.74524655 0.04473194 0.00339285 0.26131273 0.0369836 0.84192923 0.79892262 0.82890209 0.32358166 0.06535323 0.20735334] 预测值y: [1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0] 真实值y: [1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0]
4.总结:
以下结合本次实验以及资料查找: 逻辑回归优点: 模型:模型清晰,背后的概率推导经得住推敲。 输出:输出值自然地落在0到1之间,并且有概率意义 参数:参数代表每个特征对输出的影响,可解释性强。 可扩展:可以使用online learning的方式更新轻松更新参数,不需要重新训练整个模型。 过拟合:解决过拟合的方法很多,如L1、L2正则化。 多重共线性:L2正则化就可以解决多重共线性问题。 逻辑回归缺点: 特征相关情况:因为它本质上是一个线性的分类器,所以处理不好特征之间相关的情况。 特征空间:特征空间很大时,性能不好。 精度:容易欠拟合,与其它分类算法(例如:SVM等等)相比,精度不高。
这篇关于逻辑回归,原理及代码实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-22项目:远程温湿度检测系统
- 2024-12-21《鸿蒙HarmonyOS应用开发从入门到精通(第2版)》简介
- 2024-12-21后台管理系统开发教程:新手入门全指南
- 2024-12-21后台开发教程:新手入门及实战指南
- 2024-12-21后台综合解决方案教程:新手入门指南
- 2024-12-21接口模块封装教程:新手必备指南
- 2024-12-21请求动作封装教程:新手必看指南
- 2024-12-21RBAC的权限教程:从入门到实践
- 2024-12-21登录鉴权实战:新手入门教程
- 2024-12-21动态权限实战入门指南