文章目录
Logistic回归算法
主要思想
根据现有数据对分类边界线机建立回归公式,以此进行分类。训练分类器时的做法就是寻找最佳拟合参数,使用的是最优化算法。
一般过程
收集数据:采用任意方法收集数据
准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式最佳。
分析数据:采用任意方法对数据进行分析。
训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
测试算法:一旦训练步骤完成,分类将会很快。
使用算法:首先,需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练号的回归系数就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,就可以在输出的类别上做一些其他的分析工作。
优点与缺点
优点:计算代价不高,易于理解和实现
缺点:容易造成欠拟合,分类精度可能不高
适用数据类型:数值型和标称型数据
基于Logistic回归和Sigmoid函数的分类
Sigmoid函数:
σ
(
z
)
=
1
1
+
e
−
z
\sigma(z) = \frac{1}{1+e^{-z}}
σ(z)=1+e−z1
如上图所示,当x=0时,Sigmoid函数值为0.5。随着x的增大,对应的Sigmoid值逼近于1;而随着x的减小,Sigmoid的值逼近于0。
为了实现Logistic回归分类,在每个特征上都乘以一个回归系数,然后将所有值求和,代入Sigmoid函数中,进而得到一个范围在0~1的数值,大于0.5则为1类,小于0.5则为0类。
因此,Logistic回归也被看成一种概率估计。
优化方法
优化方法就是用来寻找最佳参数的,即每个特征的回归系数。
梯度下降法与梯度上升法
梯度下降法公式:
w
:
=
w
−
α
▽
w
f
(
x
)
w:= w - \alpha \triangledown_w f(x)
w:=w−α▽wf(x)
梯度上升法公式:
w
:
=
w
+
α
▽
w
f
(
x
)
w:= w + \alpha \triangledown_w f(x)
w:=w+α▽wf(x)
梯度上升算法是用来求函数的最大值,而梯度下降算法用来求函数的最小值。
Logistci推导以及似然函数梯度
注释:上面图片,有的写了ln,有的写了lg,应该全部统一为ln,全部是以e为底的对数。
根据上述的推导,如果令导数即最后的结果为0,看有没有解析解。如果有的话,就皆大欢喜,直接求出 w w w,如果没有就需要通过迭代的方法求出最佳参数 w w w。
优化算法–梯度下降法
α
\alpha
α为学习率,就是每一步走多远。
梯度下降算法的伪代码
- 初始化回归系数为1
- 重复下面步骤直到收敛{
2.1. 计算整个数据集的梯度
2.2 使用 α × ▽ w \alpha \times \triangledown_w α×▽w来更新回归系数
}- 返回回归系数值
代码:
import numpy as np
import pandas
import matplotlib.pyplot as plt
def loadDataSet():
"""
载入数据集
:return dataMat: 返回数据集的特征矩阵
:return labelMat: 返回标签矩阵
"""
with open('./testSet.txt') as f:
dataSet = [[float(line.strip().split()[0]), float(line.strip().split()[1]), 1.0,
int(line.strip().split()[2])] for line in f.readlines()] # 1.0为偏置项
dataMat = [item[:3] for item in dataSet]
labelMat = [item[3] for item in dataSet]
return dataMat, labelMat
def Sigmoid_fun(inX):
"""
Sigmoid函数
: param inX: 线性函数的求和值
: return: 返回一个0~1之间的值
"""
return 1.0 / (1+np.exp(-inX))
def draw_picture(dataMat, labelMat, weights):
"""
画散点图
: param x: 特征值1
: param y: 特征值2
"""
x1 = [items[0][0] for items in zip(dataMat, labelMat) if items[1] == 1]
y1 = [items[0][1] for items in zip(dataMat, labelMat) if items[1] == 1]
x2 = [items[0][0] for items in zip(dataMat, labelMat) if items[1] == 0]
y2 = [items[0][1] for items in zip(dataMat, labelMat) if items[1] == 0]
fig = plt.figure()
ax = fig.add_subplot()
ax.scatter(x1, y1, s=30, c='red', marker='s')
ax.scatter(x2, y2, s=30, c='green')
x = np.arange(-3, 3, 0.1)
y = (-weights[0]*x - weights[2]) / weights[1]
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
def GradDescent(dataMatIn, classLabels):
"""
逻辑回归的梯度下降算法实现
:param dataMatIn: 特征向量
:param classLabels: 分类向量`在这里插入代码片`
:retun weights: 权重向量
"""
dataMatrix = np.mat(dataMatIn) # m*3
labelMat = np.mat(classLabels).transpose() # m*1
_, c = np.shape(dataMatrix)
alpha = 0.01
maxCycles = 500
weights = np.ones((c, 1)) # 3*1
print('梯度下降start....')
for _ in range(maxCycles):
h = Sigmoid_fun(dataMatrix * weights)
error = labelMat - h # m*1
weights -= alpha * dataMatrix.transpose() * error * (-1)
return weights
if __name__ == '__main__':
# 初始权重
dataMat, labelMat = loadDataSet()
weights = GradDescent(dataMat, labelMat)
draw_picture(dataMat, labelMat, weights)
print('权重为:{}'.format(weights))
结果:
随机梯度下降
梯度下降算法在每次更新权重的时候都需要遍历整个数据集,对小数据集尚可。但是遇到有数亿岩本和成千上万的特征时,计算量非常大、复杂度高。改进的方法是一次仅用一个样本点来更新权重,这个方法叫随机梯度下降法。
由于对新的样本到来的时候对分类器进行增量的更新(假设我们已经在数据库A上训练好一个分类器h了,那新来一个样本x。对非增量学习算法来说,我们需要把x和数据库A混在一起,组成新的数据库B,再重新训练新的分类器。但对增量学习算法,我们只需要用新样本x来更新已有分类器h的参数即可),所以它属于在线学习算法。与在线学习相对应,一次处理整个数据集的叫“批处理”。
- 初始化回归系数为1
- 重复下面步骤直到收敛{
2.1. 计算该样本的梯度
2.2 使用 α × ▽ w \alpha \times \triangledown_w α×▽w来更新回归系数
}- 返回回归系数值
def stoGradDescent(dataMatIn, classLabels):
"""
逻辑回归的随机梯度下降算法实现
:param dataMatIn: 特征向量
:param classLabels: 分类向量
:retun weights: 权重向量
"""
dataMatrix = np.array(dataMatIn)
r, c = np.shape(dataMat)
alpha = 0.01
weights = np.ones(c)
for i in range(r):
# 对应元素相乘
h = Sigmoid_fun(sum(weights * dataMatrix[i]))
error = classLabels[i] - h
weights -= alpha * error * dataMatrix[i] * (-1)
return weights
结果:
改进的随机梯度下降
def stoGradDescent1(dataMatIn, classLabels, numIter=150):
"""
逻辑回归的随机梯度下降算法实现
:param dataMatIn: 特征向量
:param classLabels: 分类向量
:param numIter: 迭代的次数
:retun weights: 权重向量
"""
dataMatrix = np.array(dataMatIn)
r, c = np.shape(dataMatrix)
weights = np.ones(c)
for j in range(numIter):
for i in range(r):
# 随着迭代的次数,学习率随之减少
alpha = 4 / (1.0+i+j) + 0.01
randIndex = int(np.random.uniform(0, r))
h = Sigmoid_fun(sum(dataMatrix[randIndex] * weights))
error = classLabels[randIndex] - h
weights -= alpha * error * dataMatrix[randIndex] * (-1)
del(randIndex)
return weights
结果:
实例:从疝气病症预测病马的死亡率
使用Logistic回归估计马疝病的死亡率
- 收集数据
- 准备数据:用Python解析文本文件并填充缺失值
- 分析数据:可视化并观察数据
- 训练算法:使用优化算法,找到最佳的系数
- 测试算法:为了量化回归效果,需要观察错误率。根据错误率决定是否回退到训练阶段,通过改变迭代的次数和步长等参数来得到更好的回归系数。
- 使用算法:实现一个简单的命令行程序来收集马的症状并输出预测结果并非难事
处理缺失值
预处理阶段两件事:
第一件:
- 所有的缺失值必须用一个实数值来替换。
- Sigmoid(0)=0.5,即对结果的预测不具有任何倾向性。
第二件:
- 测试集中类别标签缺失的,就是丢弃该条数据。
代码如下:
import numpy as np
import matplotlib.pyplot as plt
def Sigmoid_fun(inX):
"""
Sigmoid函数
: param inX: 线性函数的求和值
: return: 返回一个0~1之间的值
"""
return 1.0 / (1+np.exp(-inX))
def stoGradDescent1(dataMatIn, classLabels, numIter=150):
"""
逻辑回归的随机梯度下降算法实现
:param dataMatIn: 特征向量
:param classLabels: 分类向量
:param numIter: 迭代的次数
:retun weights: 权重向量
"""
dataMatrix = np.array(dataMatIn)
r, c = np.shape(dataMatrix)
weights = np.ones(c)
for j in range(numIter):
for i in range(r):
# 随着迭代的次数,学习率随之减少
alpha = 4 / (1.0+i+j) + 0.01
randIndex = int(np.random.uniform(0, r))
h = Sigmoid_fun(sum(dataMatrix[randIndex] * weights))
error = classLabels[randIndex] - h
weights -= alpha * error * dataMatrix[randIndex] * (-1)
del(randIndex)
return weights
def load_data():
"""
提取数据
:return trainingSet: 训练数据的特征向量
:return trainingLabels: 训练数据的标签向量
:return testSet: 验证数据的特征向量
:return testLabels: 验证数据的标签向量
"""
with open('./horseColicTraining.txt') as f_train:
training = [line.strip().split('\t') for line in f_train.readlines()] # 一行有22个数据
trainingSet = [[ float(items) for items in item[:21]] for item in training]
# print(trainingSet)
trainingLabels = [ [float(item[21])] for item in training]
# print(trainingLabels)
with open('./horseColicTest.txt') as f_test:
test = [line.strip().split('\t') for line in f_test.readlines()]
# print(test)
testSet = [[ float(items) for items in item[:21]] for item in test]
testLabels = [float(item[21]) for item in test]
# print(testLabels)
return trainingSet, trainingLabels, testSet, testLabels
def classifyVector(inX, weights):
"""
分类:特征向量与优化后的权重相乘并求和,代入Sigmoid函数中,大于0.5则标签为1,小于0.5则标签为0。
:param inX: 训练集的特征向量
:param weights: 优化后的权重系数
:return label: 返回标签0或者1
"""
inX_arr = np.array(inX)
prob = Sigmoid_fun(sum(inX_arr * weights))
label = 1.0 if prob > 0.5 else 0.0
return label
def colicTest(param_dict):
"""
对模型进行训练,优化方法使用改进的随机梯度下降,对验证集进行验证
:param param_dict: 输入的参数的字典
:return errorRate: 错误率 = 错误的个数/总个数
"""
trainingSet = param_dict['trainingSet']
trainingLabels = param_dict['trainingLabels']
testSet = param_dict['testSet']
testLabels = param_dict['testLabels']
weights = stoGradDescent1(trainingSet, trainingLabels, numIter=500)
test_num = len(testLabels)
err_num = sum([1.0 if classifyVector(item, weights) != testLabels[index] else 0.0 for index, item in enumerate(testSet)])
# print(err_num, test_num)
errorRate = err_num / test_num
return errorRate
def multiTest():
"""
训练模型并测试10次,求错误率的平均值
"""
trainingSet, trainingLabels, testSet, testLabels = load_data()
param_dict = {'trainingSet': trainingSet,
'trainingLabels': trainingLabels,
'testSet': testSet,
'testLabels': testLabels
}
numTests = 10
errorSum = 0
for i in range(numTests):
errorSum += colicTest(param_dict)
print('第{}次迭代,错误率为{}'.format(i+1, colicTest(param_dict)))
print('经过{}次迭代,平均错误率为{}'.format(numTests, errorSum / numTests))
if __name__ == '__main__':
multiTest()
结果如下:
第1次迭代,错误率为0.31343283582089554
第2次迭代,错误率为0.44776119402985076
第3次迭代,错误率为0.5522388059701493
第4次迭代,错误率为0.3582089552238806
第5次迭代,错误率为0.34328358208955223
第6次迭代,错误率为0.2835820895522388
第7次迭代,错误率为0.2835820895522388
第8次迭代,错误率为0.2835820895522388
第9次迭代,错误率为0.2835820895522388
第10次迭代,错误率为0.34328358208955223
经过10次迭代,平均错误率为0.33880597014925373
总结
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法来完成。