#导入必要的包
import numpy as np import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt %matplotlib inline
BGD求解逻辑回归
#⾸先定义联系函数sigmoid函数 def sigmoid(inX): return 1.0/(1+np.exp(-inX))
In [7]:
#自定义一个归一化函数(此函数的前提是我的数据是一个矩阵) def regularize(xMat): inMat = xMat.copy()#创建一个副本,这样对inmat进行操作不会影响到xmat inMeans = np.mean(inMat,axis = 0) #求均值 inVar = np.std(inMat,axis = 0) #求标准差 inMat = (inMat - inMeans)/inVar #归一化 return inMat
#编写批量梯度下降的自定义函数 def logisticReg_0(dataSet,eps=0.01,numIt=50000): xMat = np.mat(dataSet.iloc[:, :-1].values) yMat = np.mat(dataSet.iloc[:, -1].values).T xMat = regularize(xMat) m,n = xMat.shape weights = np.zeros((n,1)) for k in range(numIt): grad = xMat.T * (sigmoid(xMat * weights) - yMat) / m#加入了sigmoid函数 weights = weights - eps * grad return weights
testSet = pd.read_table('testSet.txt', header=None) testSet.head()#一个二分类数据集
ws = logisticReg_0(testSet, eps=0.01,numIt=500)
ws
计算准确率
#提取Xmat与Ymat xMat = np.mat(testSet.iloc[:, :-1].values) yMat = np.mat(testSet.iloc[:, -1].values).T xMat = regularize(xMat)#对xmat归一化,ymat本身就是o和1 所以不用归一化了
(xMat * ws).A.flatten()#求得方程本身并转换成array再转换成行向量
#进⼀步乘以sigmoid函数之后得到的是y取得1的概率⼤⼩ p = sigmoid(xMat * ws).A.flatten() p
enumerate(p) 函数:返回一个索引和值组成二元可迭代对象
#我们可令p>0.5时预测输出值为1,反之为0,得到最终y值预测结果 for i, j in enumerate(p): if j < 0.5: p[i] = 0 else: p[i] = 1
p
testSet[3]=p
testSet
. .
#首先封装一个准确率判断模型 def accuracyCalculation(dataSet): m = dataSet.shape[0] #如果后一列数据=前一列数据,对返回为True的行计数 res = (dataSet.iloc[:, -1] == dataSet.iloc[:, -2]).value_counts() acc = res.loc[True] / m print("Model accuracy is :{}".format(acc) ) return acc
accuracyCalculation(testSet) #准确率%92
train_error = (np.fabs(yMat.A.flatten() - p)).sum() train_error #判断错误个数又8个
train_error_rate = train_error / yMat.shape[0] train_error_rate #相对的 错误率为8
#参数分别为 数据集 模型 学习率 迭代次数 def logisticAcc(dataSet, method, eps=0.01, numIt=50000): weights = method(dataSet,eps=eps,numIt=numIt) p = sigmoid(xMat * ws).A.flatten() for i, j in enumerate(p): if j < 0.5: p[i] = 0 else:p[i] = 1 train_error = (np.fabs(yMat.A.flatten() - p)).sum() trainAcc = 1 - train_error / yMat.shape[0] return trainAcc
import time %time logisticAcc(testSet,logisticReg_0)
SGD求解逻辑回归
def logisticReg_1(dataSet,eps=0.01,numIt=50000): dataSet = dataSet.sample(numIt, replace=True) dataSet.index = range(dataSet.shape[0]) xMat = np.mat(dataSet.iloc[:, :-1].values) yMat = np.mat(dataSet.iloc[:, -1].values).T xMat = regularize(xMat) m,n = xMat.shape weights = np.zeros((n,1)) for i in range(m): grad = xMat[i].T * (sigmoid(xMat[i] * weights) - yMat[i]) weights = weights - eps * grad return weights
logisticReg_1(testSet)
Out[38]:
%time logisticAcc(testSet,logisticReg_1)
from sklearn.linear_model import LogisticRegression clf = LogisticRegression() clf.fit(testSet.iloc[:, :-1], testSet.iloc[:, -1])
Out[40]:
clf.coef_
clf.intercept_
clf.predict(testSet.iloc[:, :-1])
from sklearn.metrics import accuracy_score accuracy_score(testSet.iloc[:, -1], clf.predict(testSet.iloc[:, :-1]))
注:梯度下降不适合求解回归方程,因为方程系数对线性回归是很重要的,我们可以根据系数直观的连接线性回归
1.参数讲解
1.1 概述
在scikit-learn中,与逻辑回归有关的常用的有2类。
LogisticRegression, LogisticRegressionCV,其中LogisticRegression和LogisticRegressionCV的主要区别是 LogisticRegressionCV使⽤了交叉验证来选择正则化系数C。⽽LogisticRegression需要⾃⼰每次指定 ⼀个正则化系数。除了交叉验证,以及选择正则化系数C以外, LogisticRegression和LogisticRegressionCV的使⽤⽅法基本相同。
1.2 参数详解
正则化参数penalty
LogisticRegression和LogisticRegressionCV默认就带了正则化项。penalty参数可选择的值
为"I1"和"I2".分别对应L1的正则化和L2的正则化,默认是L2的正则化。 在调参时如果我们主要的⽬的只是为了解决过拟合,⼀般penalty选择L2正则化就够了。但是如 果选择L2正则化发现还是过拟合,即预测效果差的时候,就可以考虑L1正则化。另外,如果模型 的特征⾮常多,我们希望⼀些不重要的特征系数归零,从⽽让模型系数稀疏化的话,也可以使⽤ L1正则化。 penalty参数的选择会影响我们损失函数优化算法的选择。即参数solver的选择,如果是L2正则 化,那么4种可选的算法{‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’}都可以选择。但是如果penalty是L1 正则化的话,就只能选择‘liblinear’了。这是因为L1正则化的损失函数不是连续可导的,⽽ {‘newton-cg’, ‘lbfgs’,‘sag’}这三种优化算法时都需要损失函数的⼀阶或者⼆阶连续导数。 ⽽‘liblinear’并没有这个依赖。
算法优化参数solver
solver参数决定了我们对逻辑回归损失函数的优化⽅法,有4种算法可以选择,
分别是: liblinear:使⽤了开源的liblinear库实现,内部使⽤了坐标轴下降法来迭代优化损失函数。 lbfgs:拟⽜顿法的⼀种,利⽤损失函数⼆阶导数矩阵即海森矩阵来迭代优化损失函数。 newton-cg:也是⽜顿法家族的⼀种,利⽤损失函数⼆阶导数矩阵即海森矩阵来迭代优化损失函 数。 sag:即随机平均梯度下降,是梯度下降法的变种,和普通梯度下降法的区别是每次迭代仅仅⽤ ⼀部分的样本来计算梯度,适合于样本数据多的时候。 从上⾯的描述可以看出,newton-cg, lbfgs和sag这三种优化算法时都需要损失函数的⼀阶或者 ⼆阶连续导数,因此不能⽤于没有连续导数的L1正则化,只能⽤于L2正则化。⽽liblinear通吃L1 正则化和L2正则化。 同时,sag每次仅仅使⽤了部分样本进⾏梯度迭代,所以当样本量少的时候不要选择它,⽽如果 样本量⾮常⼤,⽐如⼤于10万,sag是第⼀选择。但是sag不能⽤于L1正则化,所以当你有⼤量 的样本,⼜需要L1正则化的话就要⾃⼰做取舍了。要么通过对样本采样来降低样本量,要么回到 L2正则化。 从上⾯的描述,⼤家可能觉得,既然newton-cg, lbfgs和sag这么多限制,如果不是⼤样本,我们 选择liblinear不就⾏了嘛!错,因为liblinear也有⾃⼰的弱点!我们知道,逻辑回归有⼆元逻辑 回归和多元逻辑回归。对于多元逻辑回归常⻅的有one-vs-rest(OvR)和many-vs-many(MvM)两 种。⽽MvM⼀般⽐OvR分类相对准确⼀些。郁闷的是liblinear只⽀持OvR,不⽀持MvM,这样如 果我们需要相对精确的多元逻辑回归时,就不能选择liblinear了。也意味着如果我们需要相对精 确的多元逻辑回归不能使⽤L1正则化了。
分类⽅式选择参数
multi_class参数决定了我们分类⽅式的选择,有 ovr和multinomial两个值可以选择,默认是 ovr。
ovr即前⾯提到的one-vs-rest(OvR),⽽multinomial即前⾯提到的many-vs-many(MvM)。如果 是⼆元逻辑回归,ovr和multinomial并没有任何区别,区别主要在多元逻辑回归上。 OvR的思想很简单,⽆论你是多少元逻辑回归,我们都可以看做⼆元逻辑回归。具体做法是,对 于第K类的分类决策,我们把所有第K类的样本作为正例,除了第K类样本以外的所有样本都作为 负例,然后在上⾯做⼆元逻辑回归,得到第K类的分类模型。其他类的分类模型获得以此类推。 ⽽MvM则相对复杂,这⾥举MvM的特例one-vs-one(OvO)作讲解。如果模型有T类,我们每次在 所有的T类样本⾥⾯选择两类样本出来,不妨记为T1类和T2类,把所有的输出为T1和T2的样本放 在⼀起,把T1作为正例,T2作为负例,进⾏⼆元逻辑回归,得到模型参数。我们⼀共需要T(T- 1)/2次分类。 从上⾯的描述可以看出OvR相对简单,但分类效果相对略差(这⾥指⼤多数样本分布情况,某些 样本分布下OvR可能更好)。⽽MvM分类相对精确,但是分类速度没有OvR快。 如果选择了ovr,则4种损失函数的优化⽅法liblinear,newton-cg, lbfgs和sag都可以选择。但是 如果选择了multinomial,则只能选择newton-cg, lbfgs和sag了。
分类权重参数
class_weight参数⽤于标示分类模型中各种类型的权重,
可以不输⼊,即不考虑权重,或者说所 有类型的权重⼀样。如果选择输⼊的话,可以选择balanced让类库⾃⼰计算类型权重,或者我 们⾃⼰输⼊各个类型的权重,⽐如对于0,1的⼆元模型,我们可以定义class_weight={0:0.9, 1:0.1},这样类型0的权重为90%,⽽类型1的权重为10%。 如果class_weight选择balanced,那么类库会根据训练样本量来计算权重。某种类型样本量越 多,则权重越低,样本量越少,则权重越⾼。 那么class_weight有什么作⽤呢?在分类模型中,我们经常会遇到两类问题: 第⼀种是误分类的代价很⾼。⽐如对合法⽤户和⾮法⽤户进⾏分类,将⾮法⽤户分类为合法⽤户 的代价很⾼,我们宁愿将合法⽤户分类为⾮法⽤户,这时可以⼈⼯再甄别,但是却不愿将⾮法⽤ 户分类为合法⽤户。这时,我们可以适当提⾼⾮法⽤户的权重。 第⼆种是样本是⾼度失衡的,⽐如我们有合法⽤户和⾮法⽤户的⼆元样本数据10000条,⾥⾯合 法⽤户有9995条,⾮法⽤户只有5条,如果我们不考虑权重,则我们可以将所有的测试集都预测 为合法⽤户,这样预测准确率理论上有99.95%,但是却没有任何意义。这时,我们可以选择 balanced,让类库⾃动提⾼⾮法⽤户样本的权重。 提⾼了某种分类的权重,相⽐不考虑权重,会有更多的样本分类划分到⾼权重的类别,从⽽可以 解决上⾯两类问题。
样本权重参数
之前我们提到了样本不失衡的问题,由于样本不平衡,导致样本不是总体样本的⽆偏估计.
从⽽ 可能导致我们的模型预测能⼒下降。遇到这种情况,我们可以通过调节样本权重来尝试解决这个 问题。调节样本权重的⽅法有两种,第⼀种是在class_weight使⽤balanced。第⼆种是在调⽤fit 函数时,通过sample_weight来⾃⼰调节每个样本权重。 在scikit-learn做逻辑回归时,如果上 ⾯两种⽅法都⽤到了,那么样本的真正权重是class_weight*sample_weight.