逻辑回归模型更多地用于如下例所示判断或分类场景。
例1 某银行的贷款用户数据如下表:
欠款(元) | 收入(元) | 是否逾期 | |
---|---|---|---|
1 | 7000 | 800 | Yes |
2 | 2000 | 2500 | No |
3 | 5000 | 3000 | Yes |
4 | 4000 | 4000 | No |
5 | 2000 | 3800 | No |
显然,客户是否逾期(记为
y
y
y)与其欠款额(记为
x
1
x_1
x1)和收入(记为
x
2
x_2
x2)相关。如果将客户逾期还款记为1,未逾期记为0,我们希望根据表中数据建立
R
2
→
{
0
,
1
}
\text{R}^2\rightarrow\{0,1\}
R2→{0,1}的拟合函数
y
=
F
(
x
)
y=F(\boldsymbol{x})
y=F(x)
使得
F
(
x
i
)
≈
y
i
,
i
=
1
,
2
,
⋯
,
5
F(\boldsymbol{x}_i)\approx y_i,i=1,2,\cdots,5
F(xi)≈yi,i=1,2,⋯,5,并用
F
(
x
)
F(\boldsymbol{x})
F(x)根据新客户的欠款额与收入数据
欠款(元) | 收入(元) | 是否逾期 |
---|---|---|
3500 | 3500 | ? |
3000 | 2100 | ? |
进行预测分类。
要用回归模型分类,关键在于如何将预测值
y
y
y离散化为0,1,或一般地离散化为连续的
n
n
n个整数
N
1
,
N
1
+
1
,
⋯
,
N
1
+
n
−
1
,
N
2
N_1, N_1+1,\cdots,N_1+n-1,N_2
N1,N1+1,⋯,N1+n−1,N2。解决之道是设置阈值,譬如,设
μ
1
=
N
1
+
0.5
,
μ
2
=
N
1
+
1.5
,
⋯
,
μ
n
−
1
=
N
1
+
n
−
0.5
\mu_1=N_1+0.5,\mu_2=N_1+1.5,\cdots,\mu_{n-1}=N_1+n-0.5
μ1=N1+0.5,μ2=N1+1.5,⋯,μn−1=N1+n−0.5,对
y
<
μ
1
y<\mu_1
y<μ1,
y
y
y转换为
N
1
N_1
N1;
μ
i
≤
y
<
μ
i
+
1
\mu_i\leq y<\mu_{i+1}
μi≤y<μi+1,将其转化为
N
1
+
i
N_1+i
N1+i;
y
≥
μ
n
−
1
y\geq\mu_{n-1}
y≥μn−1,转换为
N
2
N_2
N2。
其次,对分类模型的评价指标应该是分类正确率:设
(
x
i
,
y
i
)
(\boldsymbol{x}_i,y_i)
(xi,yi),
i
=
1
,
2
,
⋯
,
m
i=1,2,\cdots,m
i=1,2,⋯,m为测试数据。用训练所得的最优模式
w
0
\boldsymbol{w}_0
w0,得预测值
y
i
′
y'_i
yi′,
i
=
1
,
2
,
⋯
,
m
i=1,2,\cdots,m
i=1,2,⋯,m。记
y
=
(
y
1
y
2
⋮
y
m
)
\boldsymbol{y}=\begin{pmatrix}y_1\\y_2\\\vdots\\y_m\end{pmatrix}
y=
y1y2⋮ym
,
y
t
=
(
y
1
′
y
2
′
⋮
y
m
′
)
\boldsymbol{y}_t=\begin{pmatrix}y'_1\\y'_2\\\vdots\\y'_m\end{pmatrix}
yt=
y1′y2′⋮ym′
,计算
y
i
=
y
i
′
y_i=y'_i
yi=yi′成立的个数
m
1
m_1
m1,则正确率为
m
1
m
×
100
\frac{m_1}{m}\times100
mm1×100。
下列代码实现用于分类的Classification类。
import numpy as np #导入numpy
class Classification(): #分类模型
def threshold(self, x): #阈值函数
N1 = x.min().astype(int) - 1 #最小阈值整数部分
N2 = np.round(x.max()).astype(int) + 1 #最大阈值整数部分
y = np.array([N1] * x.size) #因变量数组
for n in range(N1, N2): #对每个可能的函数值
d = np.where((x >= n - 0.5)&(x < n + 0.5)) #取值区间
y[d] = n #函数值
if(y.size == 1): #单值情形
y = y[0]
return y
def predict(self, X): #重载预测函数
yp = RegressModel.predict(self, X) #计算预测值
return self.threshold(yp) #转换为离散值
def accuracy(self, y1, y2): #正确率
m = y1.size
acc=np.where(y1 == y2)[0].size #计算两者相等的元素个数
return acc / m * 100
def test(self, x, y): #测试函数
yp = self.predict(x)
return yp, self.accuracy(y, yp)
class LogicClassifier(Classification, LogicModel):
'''逻辑分类模型'''
程序中第2~22行定义了用于分类的Classification辅助类。其中第3~12行定义阈值函数threshold。第13~15行在预测函数predict外“套上”阈值函数threshold,筛选出分类值返回。第16~19行定义计算两个等长整数数组y1,y2中对应元素相等的比率函数accuracy。第20~22行定义测试函数test,对测试数据x和y,计算x的预测值yp,然后调用accuracy计算y和yt的相等比率返回。第23~24行联合Classification类和LogicModel类(详见博文《最优化方法Python计算:无约束优化应用——逻辑回归模型》)实现逻辑分类模型类LogicClassifier。下面我们来小试牛刀:用逻辑分类模型LogicClassifier计算例1中的问题。
import numpy as np #导入numpy
x = np.array([[7000, 800], #设置训练、测试数据
[2000, 2500],
[5000, 3000],
[4000, 4000],
[2000, 3800]])
y = np.array([1, 0, 1, 0, 0])
title = np.array(['No', 'Yes']) #预测值标签
np.random.seed(2024) #随机种子
credit = LogicClassifier() #创建逻辑分类模型
credit.fit(x,y) #训练
_, acc=credit.test(x,y) #测试
print('准确率:%.1f'%acc + '%')
x1 = np.array([[3500, 3500], #设置预测数据
[3000, 2100]])
print('对测试数据:')
print(x1)
Y = credit.predict(x1) #计算预测值
print('归类为:')
print([title[y] for y in Y])
看官可借助代码内注释信息理解程序,需要提请注意的是第9行设置随机种子是为了使看官的运行结果与下列的输出一致。运行程序,输出
训练中...,稍候
3次迭代后完成训练。
准确率:100.0%
对测试数据:
[[3500 3500]
[3000 2100]]
归类为:
['No', 'Yes']
开胃菜后,上正餐。
综合案例
文件iris.csv(来自UC Irvine Machine Learning Repository)是统计学家R. A. Fisher在1936年采集的一个小型经典数据集,这是用于评估分类方法的最早的已知数据集之一。该数据集含有150例3种鸢尾花:setosa、versicolour和virginica的数据
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | Species | |
---|---|---|---|---|---|
1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
2 | 4.9 | 3 | 1.4 | 0.2 | setosa |
3 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
51 | 7 | 3.2 | 4.7 | 1.4 | versicolor |
52 | 6.4 | 3.2 | 4.5 | 1.5 | versicolor |
53 | 6.9 | 3.1 | 4.9 | 1.5 | versicolor |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
148 | 6.5 | 3 | 5.2 | 2 | virginica |
149 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
150 | 5.9 | 3 | 5.1 | 1.8 | virginica |
五个数据属性的意义分别为
- Sepal.Length:花萼长度;
- Sepal.Width:花萼宽度;
- Petal.Length:花瓣长度;
- Petal.Width:花瓣宽度;
- Species:种类。
下列代码从文件中读取数据,将花的种类数字化。
import numpy as np #导入numpy
data = np.loadtxt('iris.csv', delimiter=',', dtype=str) #读取数据文件
X = np.array(data) #转换为数组
X = X[:, 1:] #去掉编号
title = X[0, :4] #读取特征名称
X = X[1:, :] #去掉表头
Y = X[:, 4] #读取标签数据
X = X[:, :4].astype(float)
m = X.shape[0] #读取样本个数
print('共有%d个数据样本'%m)
print('鸢尾花特征数据:')
print(X)
Y = np.array([0 if y == 'setosa' else #类别数值化
1 if y == 'versicolor' else
2 for y in Y])
print('鸢尾花种类数据:')
print(Y)
运行程序,输出
共有150个数据样本
鸢尾花特征数据:
[[5.1 3.5 1.4 0.2]
[4.9 3. 1.4 0.2]
[4.7 3.2 1.3 0.2]
...
[6.5 3. 5.2 2. ]
[6.2 3.4 5.4 2.3]
[5.9 3. 5.1 1.8]]
鸢尾花种类数据:
[0 0 0 ... 2 2 2]
花萼的长度、宽度,花瓣的长度、宽度对鸢尾花的种类有明显的相关性。接下来我们要从数据集中随机选取一部分作为训练数据,训练LogicClassifier分类模型,然后用剩下的数据进行测试,评价训练效果。在前面的程序后添加以下代码
……
a = np.arange(m) #数据项下标
np.random.seed(2024)
index = np.random.choice(a,m//3,replace=False) #50个随机下标
Xtrain = X[index] #训练数据
Ytrain = Y[index]
index1 = np.setdiff1d(a,index) #测试数据下标
Xtest = X[index1] #测试数据
Ytest = Y[index1]
iris = LogicClassifier() #创建模型
print('随机抽取%d个样本作为训练数据。'%(m//3))
iris.fit(Xtrain,Ytrain) #训练模型
_, accuracy = iris.test(Xtest,Ytest) #测试
print('对其余%d个数据测试,分类正确率为%.2f'%(m-m//3,accuracy)+'%')
运行程序输出
……
随机抽取50个样本作为训练数据。
训练中...,稍候
23次迭代后完成训练。
对其余100个数据测试,分类正确率为99.00%
第1行的省略号表示前一程序的输出。从测试结果可见训练效果效果不错!
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!