2.【Python】分类算法—Logistic Regression
前言
逻辑回归算法一般用于二分类问题,属于典型的监督学习,此算法的复杂度低容易实现。
一、Logistic Regression模型
1.线性可分和线性不可分
在介绍Logistic Regression之前,先了解线性可分和线性不可分的概念。对于分类问题,如果样本可以用线性函数进行分类,则成为线性可分,如图1.1所示;否则成为线性不可分,如图1.2所示。
图1.1 线性可分(图侵删)
图1.2 线性不可分(图侵删)
2.Logistic Regression模型
Logistic Regression属于线性分类模型,如图1.1所示的线性函数可以表示为:
Wx+b=0
其中,W为权重,b为偏置。通过对样本的训练,可以得到两个类别的数据,利用阈值函数可以将样本映射到不同类别中,最常用的阈值函数为Sigmoid函数,公式为:
(图侵删)
如图所示,是将样本映射至值域(0,1)之间,以下为python的实现过程。
import numpy as np
def sig(x):
'''sigmoid函数
input:x(mat):feature*w
output:sigmoid(x)(mat):sigmoid值
'''
return 1.0/(1+np.exp(-x))
3.损失函数
为了求权重w和偏置b,需要先了解损失函数。
若输入值为x,输出值为y,则样本属于y的概率应该为P(y)P(1-y),表示为:
σ为sigmoid函数。可以使用极大似然法对W和b进行参数估计,假设有m个训练样本,则其似然函数为:
为了将乘法变为加法,将使用log函数进行对数求解。在Logistic Regression算法中,通常将负的log似然函数当做损失函数,因为损失函数求出的概率越小,损失越小。
此时需要求min l(W,b)。
二、梯度下降法
梯度下降法是寻找最优的权重W,用的最多的是基于梯度下降的优化函数。
1.梯度下降的过程
选取初始点X0
选取梯度下降的方向,选择步长,在某一时刻到达X3
若判断没有满足终止条件,则重复以上过程,直至找到最优解(最小值)。
(图侵删)
注意可能存在多个局部最优解得情况,全局最优解就是某一个局部最优解。
2.基于梯度下降法训练Logistic Regression模型
∇为梯度,根据梯度下降法得到权重的更新公式表示为:
具体过程如下:
def lr_train_bgd(feature,label,maxcycle,alpha):
'''利用梯度下降训练LR模型
input:feature(mat)特征
label(mat)标签
maxcycle(int)最大迭代次数
alpha(float)学习率
output:w(mat):权重
'''
n = np.shape(feature)[1] #特征个数
w = np.mat(np.ones((n, 1))) #初始化权重
i = 0
while i <=maxcycle: #在最大迭代次数的范围内
i += 1 #当前的迭代次数
h = sig(feature * w) #计算sigmoid的值
err = label - h
if i % 100 ==0:
print("\t---iter="+str(i)+",train error rate="+str(error_rate(h,label)))
w = w + alpha*feature.T * err #权重修正
return w
利用函数lr_train_bgd对损失函数进行了优化,函数输入为样本特征、标签、最大迭代次数、学习率,输出为权重,这些都需要自己定义。
在每次迭代过程中,需要计算当前的误差,定义误差函数为error_rate,以下部分应该放在定义函数lr_train_bgd之前。
def error_rate(h,label):
'''计算当前的损失函数值
input:h(mat):预测值
label(mat):实际值
output:err/m(float):错误率
'''
m = np.shape(h)[0]
sum_err = 0.0
for i in range(m):
if h[i,0] > 0 and (1-h[i,0]) > 0:
sum_err -= (label[i,0] * np.log(h[i,0])+(1-label[i,0]) * np.log(1 - h[i,0])
else:
sum_err -= 0
return sum_err/m
3.下降方向和步长的选择
若梯度下降<0,则最小值在minimum的右侧;若梯度下降<0,则最小值在minimum的左侧。
(图侵删)
左图步长过小,收敛速度较慢;右图步长过大,可能会跳过最优解,因此需要选择合适的步长。
(图侵删)
三、Logistic Regression实践
学习以上理论以后,接下来将进行实践,主要分为两个部分:对训练集训练模型和对测试集进行预测。
1.利用训练样本训练Logistic Regression模型
将训练文件命名为lr_train。
首先,导入训练样本数据,需要定义load_data函数。
# coding : UTF-8
import numpy as np
#filename文件名
def load_data(filename):
'''
input: filename(string)训练数据的文件位置
output: feature_data(mat)特征
label_data(mat)标签
'''
f = open(filename) #打开文件
feature_data = []
label_data = []
#逐行读取
for line in f.readlines():
feature_tmp = []
label_tmp = []
lines = line.strip().split("\t")
feature_tmp.append(1) #偏置项
for i in range(len(lines)-1): #读除最后一行的前几行
feature_tmp.append(float(lines[i]))
label_tmp.append(float(lines[-1])) #读最后一行
feature_data.append(feature_tmp)
label_data.append(label_tmp)
f.close() #关闭文件
return np.mat(feature_data),np.mat(label_data)
然后,利用梯度下降法对训练数据进行训练,结合第一二节的代码。
def sig(x):
'''sigmoid函数
input:x(mat):feature*w
output:sigmoid(x)(mat):sigmoid值
'''
return 1.0/(1+np.exp(-x))
def error_rate(h,label):
'''计算当前的损失函数值
input:h(mat):预测值
label(mat):实际值
output:err/m(float):错误率
'''
m = np.shape(h)[0]
sum_err = 0.0
for i in range(m):
if h[i,0] > 0 and (1-h[i,0]) > 0:
sum_err -= (label[i,0] * np.log(h[i,0])+(1-label[i,0]) * np.log(1 - h[i,0]))
else:
sum_err -= 0
return sum_err/m
def lr_train_bgd(feature_data,label_data,maxcycle=1000,alpha=0.01):
"""利用梯度下降训练LR模型
input:feature(mat)特征
label(mat)标签
maxcycle(int)最大迭代次数
alpha(float)学习率
output:w(mat):权重
"""
n = np.shape(feature_data)[1] #特征个数
w = np.mat(np.ones((n, 1))) #初始化权重
j = 0
while j <= maxcycle: #在最大迭代次数的范围内
j += 1 #当前的迭代次数
h = sig(feature_data * w) #计算sigmoid的值
err = label_data - h
if j % 100 ==0:
print("\t---iter="+str(j)+",train error rate="+str(error_rate(h,label)))
w = w + alpha *feature.T * err #权重修正
return w
将文件输入到文件weights中,保存模型需要定义save_model函数,可以将训练好的模型以文件的形式保存。
def save_model(filename,w):
'''保存最终模型
input: filename(string):模型保存的文件名
w(mat): LR模型的权重
'''
m = np.shape(w)[0] #读取矩阵第一维的长度
f_w = open(filename,'w')
w_array = []
for i in range(m):
w_array.append(str(w[i,0]))
f_w.write("\t".join(w_array))
f_w.close()
综上述定义的函数,训练模型的主函数代码如下。
if __name__ == "__main__":
#1.导入训练数据
print("------1.load data------")
feature,label = load_data("data.txt")
#2.训练logistics regression模型
print("------2.training-------")
w = lr_train_bgd(feature,label,1000,0.01) #最大迭代次数1000,学习率0.01
#3.保存最终的模型
print("------3.save model------")
save_model("weights",w)
2.对新的样本进行预测
接下来利用训练好的logistics regression模型对新的样本数据进行预测,lr模型保存在了weights文件中。
首先,在对新的数据集测试之前,需要先导入训练好的模型,将训练好的权重数值导入到权重矩阵中,需要定义load_weight函数。
# coding : UTF-8
import numpy as np
from numpy.doc.constants import line
def load_weights(w):
'''导入lr模型
input:w(string)权重所在文件的位置
output:np.mat(w)权重的矩阵
'''
f = open(w)
w = []
for lines in f.readlines():
lines = line.strip().split("\t")
w_tmp = []
for x in lines:
w_tmp.append(w_tmp)
f.close()
return np.mat(w)
其次,导入测试数据集,需要定义load_data函数。
n为特征的个数,当测试集的长度不等于特征数减一时,继续循环,否则丢弃。
def load_data(filename,n):
'''导入测试数据
input:filename(string)测试集的位置
n(int)特征的个数
output:np.mat(feature_data) (mat)测试集的特征
'''
f = open(filename)
feature_data = []
for line in f.readlines():
feature_tmp = []
lines = line.strip().split("\t")
#print lines[2]
if len(lines) != n-1:
continue
feature_tmp.append(1)
for x in lines:
feature_tmp.append(float(x))
feature_data.append(feature_tmp)
f.close()
return np.mat(feature_data)
在模型和测试数据都导入之后,可以对新的数据集进行预测,需要定义predict函数。其中需要使用sig函数,需要从lr_train文件中调用。
def predict(feature_data,w):
'''对测试数据进行预测
input:data(mat)测试数据的特征
w(mat)模型的权重参数
output:h(mat)最终的预测结果
'''
h = sig(feature_data * w.T) #求sigmoid对应的值
m = np.shape(h)[0]
for i in range(m):
if h[i,0] < 0.5:
h[i,0] = 0.0
else:
h[i, 0] = 1.0
return h
由于此次logistics regression算法探究的是二分类问题,于是将0.5作为分类边界,小于0.5的sigmoid概率值归为0,大于0.5的sigmoid概率值归为1。
最后保存预测结果,需要定义save_result函数。
def save_result(filename,h):
'''保存最终的预测结果
input:filename(string):预测结果保存的文件名
result(mat):预测的结果
'''
m = np.shape(h)[0]
#输出预测到文件
tmp = []
for i in range(m):
tmp.append(str(h[i,0]))
f_result = open(filename,"w")
f_result.write("\t".join(tmp))
f_result.close()
filename为保存预测结果的文件名。
总结
以上针对Logistic Regression(逻辑回归)算法的原理和python具体实现过程进行了介绍,python的具体实现过程主要分为两个部分—训练和预测。
参考文献:《Python机器学习算法》