1.Logistic回归
人们将已知存在的数据点利用一条直线进行拟合(该直线被称为最佳拟合直线)的过程称为回归。
而今天介绍的Logistic回归分类的主要思想就是依据现有已分类的数据样本建立回归公式,以此进行分类。
“回归”源于最佳拟合,而Logistic回归中训练分类器的做法就是利用一些最优算法寻找符合最佳拟合的拟合参数。
1.1 Logistic分布
设X是连续的随机变量,且具有如下的分布函数和密度函数:
![](http://latex.codecogs.com/svg.latex? \Large $$ F(X) = P(X\leq{x}) = \frac{1}{1+\exp^{-\frac{(x-\mu)}{\gamma}}}$$)
![](http://latex.codecogs.com/svg.latex? \Large $$ f(X) = F'(X) = \frac{\exp{-\frac{x-\mu}{\gamma}}}{\gamma(1+\exp{-\frac{x-\mu}{\gamma}})^2}$$)
其中μ为位置参数,γ > 0为形状参数。
下图是F(X),f(X)的大致图像,人们又把F(X)函数成为sigmoid函数(以后会经常见到它)。
import matplotlib.pyplot as plt
import numpy as np
import scipy
x = np.linspace(-5, 5)
y1 = 1/(1 + np.exp(- x))
y2 = np.exp(- x)/(1 + np.exp(- x))**2
fig1 = plt.figure()
plt.xlabel('x')
plt.ylabel('F(x)')
plt.plot(x, y1, linewidth = 2.0)
fig2 = plt.figure()
plt.xlabel('x')
plt.ylabel('f(x)')
plt.plot(x, y2, linewidth = 2.0)
plt.show()
1.2 二项式Logistic回归
从以上的Logistic分布定义中引申出一种分类模型,就是下面要介绍的二项式Logistic回归模型。
在上述X中,将其分为0、1两类。即随机变量X的取值为实数,其分类变量定义为Y,每个x所属类别可以通过条件概率分布P(Y|X)进行表示。
条件概率分布如下:
![](http://latex.codecogs.com/svg.latex? \Large $$P(Y=1\mid x) = \frac{\exp(\omega \cdot x+b)}{1+\exp(\omega \cdot x+b)}$$)
![](http://latex.codecogs.com/svg.latex? \Large $$P(Y=0\mid x) = \frac{1}{1+\exp(\omega \cdot x+b)}$$)
x∈Rn是输入,Y∈ {0, 1}是输出,ω∈Rn为权值向量,b∈R为偏置,ω·x为ω和x的内积。
这里将ω和x进行扩充,即![](http://latex.codecogs.com/svg.latex? \Large $\omega = {\omega ^{(1)},\omega ^{(2)},...,\omega ^{(n)},b }$)和![](http://latex.codecogs.com/svg.latex? \Large $x = {x{(1)},x{(2)},...,x^{(n)},1}$)
此时的Logistic回归模型如下:
![](http://latex.codecogs.com/svg.latex? \Large $$P(Y=1\mid x) = \frac{\exp(\omega \cdot x)}{1+\exp(\omega \cdot x)}$$)
![](http://latex.codecogs.com/svg.latex? \Large $$P(Y=0\mid x) = \frac{1}{1+\exp(\omega \cdot x)}$$)
在考虑一个事件发生为p,则不发生的概率为1-p,那么这个事件发生的几率(odds)为p/(1-p)。对数几率(log odds)为
![](http://latex.codecogs.com/svg.latex? \Large $$logit(p) = \log{\frac{p}{1-p}}$$)
将上述Logistic回归模型代入其中可以得到:
![](http://latex.codecogs.com/svg.latex? \Large $$\log{\frac{P(Y = 1\mid x)}{1-P(Y = 1\mid x)}} = \omega \cdot x$$)
可以看出输出Y = 1的对数几率是输入x的线性函数,即Y可以用x的线性函数表示。自然地,在Logistic回归模型中,x的线性函数值ω·x越接近于+∞,其概率越接近于1,反之,ω·x越接近于-∞,其概率越接近于0。
1.3 参数估计
假设给定训练数据集![](http://latex.codecogs.com/svg.latex? \Large $T = {(x_1,y_1),(x_2,y_2),...,(x_N,y_N)}$)
其中,xi∈Rn,yi ∈ {0,1},利用极大似然估计法估计模型参数。
设![](http://latex.codecogs.com/svg.latex? \Large $P(Y=1\mid x) =\pi (x)$,则$P(Y=0\mid x) =1-\pi (x)$)
似然函数为:![](http://latex.codecogs.com/svg.latex? \Large $$\prod_{i=1}^{N} [\pi(x_i)]{y_i}[1-\pi(x_i)]{1-y_i}$$)
对数似然函数为:
此时对L(ω)求最大值,即得到ω的估计值。
为此,后续利用梯度下降法或者拟牛顿法进行该值的求解。
假设ω的极大似然估计值为ω',那么Logistic模型为:
![](http://latex.codecogs.com/svg.latex? \Large $$P(Y=1\mid x) = \frac{\exp(\omega' \cdot x)}{1+\exp(\omega' \cdot x)}$$)
![](http://latex.codecogs.com/svg.latex? \Large $$P(Y=0\mid x) = \frac{1}{1+\exp(\omega' \cdot x)}$$)
1.4 梯度法求解估计值
对上述L(ω)中的ω求导,得到:
令L(ω) = 0,可以求得其最大的L(ω)对应的ω',但由于无法直接求解,故采用梯度下降法进行求解。
由上求导过程可以知道,L(ω) 的梯度为
![](http://latex.codecogs.com/svg.latex? \Large $$\triangledown_{\omega}L(\omega) =\sum_{i=1}^{N}{y_i - \pi(x_i)}x_i$$)
故迭代公式(此处为梯度上升算法)为
![](http://latex.codecogs.com/svg.latex? \Large $$\omega = \omega + \alpha \sum_{i=1}^{N}{y_i - \pi(x_i)}x_i$$)
其中α为步长,当![](http://latex.codecogs.com/svg.latex? \Large $\mid\mid \triangledown_{\omega}L(\omega) \mid\mid < \varepsilon$) 即可求得最大的ω,ε为误差。
2、Logistic分类器实现
仍然使用这份数据进行算法实现
以下列数据集为例,进行Logistic分类器的设计。
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
1 | 青绿 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.697 | 0.460 | 是 |
2 | 乌黑 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 0.774 | 0.376 | 是 |
3 | 乌黑 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.634 | 0.264 | 是 |
4 | 青绿 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 0.608 | 0.318 | 是 |
5 | 浅白 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.556 | 0.215 | 是 |
6 | 青绿 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 软粘 | 0.403 | 0.237 | 是 |
7 | 乌黑 | 稍蜷 | 浊响 | 稍糊 | 稍凹 | 软粘 | 0.481 | 0.149 | 是 |
8 | 乌黑 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 硬滑 | 0.437 | 0.211 | 是 |
9 | 乌黑 | 稍蜷 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 0.666 | 0.091 | 否 |
10 | 青绿 | 硬挺 | 清脆 | 清晰 | 平坦 | 软粘 | 0.243 | 0.267 | 否 |
11 | 浅白 | 硬挺 | 清脆 | 模糊 | 平坦 | 硬滑 | 0.245 | 0.057 | 否 |
12 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 软粘 | 0.343 | 0.099 | 否 |
13 | 青绿 | 稍蜷 | 浊响 | 稍糊 | 凹陷 | 硬滑 | 0.639 | 0.161 | 否 |
14 | 浅白 | 稍蜷 | 沉闷 | 稍糊 | 凹陷 | 硬滑 | 0.657 | 0.198 | 否 |
15 | 乌黑 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 软粘 | 0.360 | 0.37 | 否 |
16 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 硬滑 | 0.593 | 0.042 | 否 |
17 | 青绿 | 蜷缩 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 0.719 | 0.103 | 否 |
2.1 解析数据文件
上一篇的《朴素贝叶斯法》已经利用了Pandas对读入的数据文件进行解析并进行哑变量处理,下面即封装好的解析代码:
#导入numpy和pandas库
import numpy as np
import pandas as pd
#解析数据文件
def loadDataSet(filename):
dataSet = pd.read_csv(filename, sep = '\t', index_col = '编号')
#哑变量处理
featureDict = []
new_dataSet = pd.DataFrame()
for i in range(len(dataSet.columns)):
featureList = dataSet[dataSet.columns[i]]
classSet = list(set(featureList))
count = 0
for feature in classSet:
d = dict()
if isinstance(feature, float):#判断是否为连续变量
continue
else:
featureList[featureList == feature] = count
d[feature] = count
count += 1
featureDict.append(d)
new_dataSet = pd.concat([new_dataSet, featureList], axis = 1)
dataMat = [list(new_dataSet.loc[i][:-1]) for i in range(1,len(new_dataSet) + 1)]
labelMat = list(new_dataSet[new_dataSet.columns[-1]])
return dataMat, labelMat
filename = 'data.txt'
dataMat, labelMat = loadDataSet(filename)
import pprint
pprint.pprint(dataMat)
pprint.pprint(labelMat)
[[1, 1, 2, 2, 0, 0, 0.69700000000000006, 0.46000000000000002],
[0, 1, 1, 2, 0, 0, 0.77400000000000002, 0.376],
[0, 1, 2, 2, 0, 0, 0.63400000000000001, 0.26400000000000001],
[1, 1, 1, 2, 0, 0, 0.60799999999999998, 0.318],
[2, 1, 2, 2, 0, 0, 0.55600000000000005, 0.215],
[1, 2, 2, 2, 1, 1, 0.40299999999999997, 0.23699999999999999],
[0, 2, 2, 0, 1, 1, 0.48100000000000004, 0.14899999999999999],
[0, 2, 2, 2, 1, 0, 0.43700000000000006, 0.21100000000000002],
[0, 2, 1, 0, 1, 0, 0.66599999999999993, 0.090999999999999998],
[1, 0, 0, 2, 2, 1, 0.24299999999999999, 0.26700000000000002],
[2, 0, 0, 1, 2, 0, 0.245, 0.057000000000000002],
[2, 1, 2, 1, 2, 1, 0.34299999999999997, 0.099000000000000005],
[1, 2, 2, 0, 0, 0, 0.63900000000000001, 0.161],
[2, 2, 1, 0, 0, 0, 0.65700000000000003, 0.19800000000000001],
[0, 2, 2, 2, 1, 1, 0.35999999999999999, 0.37],
[2, 1, 2, 1, 2, 0, 0.59299999999999997, 0.042000000000000003],
[1, 1, 1, 0, 1, 0, 0.71900000000000008, 0.10300000000000001]]
[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
可以看到,该操作将字符串型的分类变量解析成为了数值型的分类变量,从而方便了将要进行的数值运算。
2.2 利用梯度上升法求最佳回归系数
在前面的定义中,Logistic分布函数$F(X)$又被称为sigmoid函数,其对数几率为
![](http://latex.codecogs.com/svg.latex? \Large $$\log{\frac{P(Y = 1\mid x)}{1-P(Y = 1\mid x)}} = \omega \cdot x$$)
即将sigmoid函数的输入可以看作是:
![](http://latex.codecogs.com/svg.latex? \Large $$z = \omega {(1)}x{(1)}+\omega {(2)}x{(2)}+,...,+\omega {(n)}x{(n)} = \omega^{T} \cdot x$$)
由此,定义下面的代码进行梯度上升法的实现:
#定义Sigmoid函数
def sigmoid(inX):
return 1.0/(1 + np.exp(- inX))
#随机的梯度上升法
def gradAscent(dataMatIn, classLabels, numIter = 150):
#获得行数和列数,即样本数和特征数
m, n = np.shape(dataMatIn)
#权重初始化
weights = np.ones(n)
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0 + j + i) + 0.01
randIndex = int(np.random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMatIn[randIndex] * weights))
error = classLabels[randIndex] - h
weights = weights + np.dot(alpha * error, dataMatIn[randIndex])
return weights
weights = gradAscent(dataMat, labelMat)
print(weights)
[-1.91739131 -2.37320272 3.30085298 1.32020706 -2.30328752 0.58413608
0.84630395 -0.63702599]
2.3 分类器设计
由上,求出了相应的输入特征对应权重,利用对数几率公式,可以简单实现分类的效果,相关设计代码如下:
def classfy(testdir, weights):
dataMat, labelMat = loadDataSet(testdir)
dataMat = np.mat(dataMat)
weights = np.mat(weights)
h = sigmoid(dataMat * weights.transpose())
h = h.tolist()
m = len(h)
error = 0.0
for i in range(m):
if h[i][0] > 0.5:
print(int(labelMat[i]),'is classfied as: 1')
if int(labelMat[i])!=1:
error += 1
print('error')
else:
print(int(labelMat[i]),'is classfied as: 0')
if int(labelMat[i])!=0:
error += 1
print('error')
print('error rate is:','%.4f' %(error/m))
classfy(filename, weights)
1 is classfied as: 1
1 is classfied as: 1
1 is classfied as: 1
1 is classfied as: 1
1 is classfied as: 1
1 is classfied as: 1
1 is classfied as: 1
1 is classfied as: 1
0 is classfied as: 0
0 is classfied as: 0
0 is classfied as: 0
0 is classfied as: 0
0 is classfied as: 1
error
0 is classfied as: 0
0 is classfied as: 1
error
0 is classfied as: 0
0 is classfied as: 0
error rate is: 0.1176
将训练的数据样本进行测试,可以看出上述分类中,只有两个样本被分类错误了,准确度达到了88.24%,分类效果不错。
2.4 Scikit - Learn库简单实现Logistic分类
下面的代码是简单通过Scikit - Learn库实现Logistic的分类:
from sklearn.linear_model import LogisticRegression
X, Y = loadDataSet(filename)
clf = LogisticRegression()
clf.fit(X, Y)
y_pred = clf.predict(X)
accuracy = np.mean(Y == y_pred)
print('准确度为:', accuracy)
准确度为: 0.882352941176
可以看出,上述实现的准确度与设计的分类器的准确度基本一样,效果不错。
3、总结
Logist回归分类的优缺点:
- 优点:计算代价低,易于理解和实现;
- 缺点:易欠拟合,分类精度普遍不高。
本文实现过程中使用的数据样本量较少,结果也较为单一,后续可以通过训练和测试较多样本量的数据,来感受其作用。
4、 参考文献
[1] 李航. 统计学习方法.清华大学出版社,2012
[2] Peter Harrington. 机器学习实战. 人民邮电出版社,2013
[3] http://blog.csdn.net/lsldd/article/details/41551797
网友评论