学习笔记(二):逻辑斯蒂回归算法(Logistic Regression)
一、算法简介
代码下载:https://download.csdn.net/download/xdg2008/16744956?spm=1001.2014.3001.5503
1.1 定义
逻辑斯蒂回归(Logistic Regression) 虽然名字中有回归,但模型最初是为了解决二分类问题,后来可以解决多分类问题。
线性回归模型帮助我们用最简单的线性方程实现了对数据的拟合,但只实现了回归而无法进行分类。因此LR就是在线性回归的基础上,构造的一种分类模型。
对线性模型进行分类如二分类任务,简单的是通过阶跃函数(unit-step function),即将线性模型的输出值套上一个函数进行分割,大于z的判定为0,小于z的判定为1。


但这样的分段函数数学性质不好,既不连续也不可微。因此有人提出了对数几率函数,见上图右,简称Sigmoid函数。

该函数具有很好的数学性质,既可以用于预测类别,并且任意阶可微,因此可用于求解最优解。将函数带进去,可得LR模型为
![]()
其实,LR 模型就是在拟合 z = w^T x +b 这条直线,使得这条直线尽可能地将原始数据中的两个类别正确的划分开
1.2 损失函数表达式
回归问题的损失函数一般为平均误差平方损失 MSE,LR解决二分类问题中,损失函数为如下形式
L=∑ylogy+(1-y)log(1-y)

1.3梯度下降
得到了逻辑回归的表达式,下一步跟线性回归类似,构建似然函数,然后最大似然估计,最终推导出θ的迭代更新表达式。这个思路不清楚的请参考文章《线性回归、梯度下降》,只不过这里用的不是梯度下降,而是梯度上升,因为这里是最大化似然函数不是最小化似然函数
L=∑ylogy+(1-y)log(1-y)
转换后的似然函数对w求偏导,在这里我们以只有一个训练样本的情况为例:

这个求偏导过程第一步是对θ偏导的转化,依据偏导公式:y=lnx y'=1/x。
第二步是根据g(z)求导的特性g'(z) = g(z)(1 - g(z)) 。
第三步就是普通的变换。
这样我们就得到了梯度上升每次迭代的更新方向,那么θ的迭代表达式为:
逻辑回归误差对w和b的偏导数∂1,∂2 有以下公式:
∂1_w=X*(H(X)-Y)
其中:H(X)=sigmoid(Feature*Weight),Y=Label,X=Feature.T
二、案列实现
2.1 案列背景
杜紫藤女士, 一直在某婚恋网站上寻找自己中意的他, 网站给她推荐了很多男士, 但是她并不是每个都喜欢, 网站统计了这些男士的情况,
这些男士可以划分为如下几类:
(1)不喜欢的人(didntLike)
(2)魅力一般的人(smallDoses)
(3)极具魅力的人(largeDoses)
2.2 特征数字
杜紫藤女士收集了一些样本数据,她把样本数据存放在datingTestSet.txt文本文件中,每个样本数据占据一行,总共有1000行。
这些样本数据主要有以下3个特征:
(1)每年获得的飞行常客里程数
(2)玩视频游戏所消耗的时间百分比
(3)每周消费的冰淇淋公升数
3.3算法及代码:
import numpy as np
def filetomatrix(filename):
fr = open(filename)
arrayOLines = fr.readlines() #读取文件的所有行
numberOfLines = len(arrayOLines) #获取文件行数
returnMat =np.zeros((numberOfLines,3)) #返回Numpy矩阵,解析完成的数据为numberOfLines行,3列零数据矩阵classLabelVector = [] #返回的分类标签向量
classLabelVector = [] #返回的分类标签向量
index = 0
for line in arrayOLines:
line = line.strip() #使用line.strip()函数截取掉所有的空白符,s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
listFromLine = line.split('\t') #将line根据'\t'分隔符进行切片
returnMat[index,:] = listFromLine[0:3] #选取前三列元素,将它们存储到特征矩阵中
classLabelVector.append(listFromLine[-1]) #将最后一列数据加入classLabelVector中,作为情感分类标签
index += 1
return returnMat,classLabelVector
rmat1,label1=filetomatrix('D:\Train_test\datingTestSet.txt')
lab=[]
l=[]
for i,item in enumerate(label1):
if item =='largeDoses':
lab.append(0)
elif item =='smallDoses':
lab.append(1)
elif item =='didntLike':
lab.append(2)
one-hot编码
label11=np.array(lab).astype(int)
label12=np.eye(3)[label11]
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def gradentdecent(train,target):
#feature = train[:,0:1]
feature = train
ones = np.ones((len(feature),1))
Feature = np.hstack((feature ,ones))
Label = target
weight = np.ones((Feature.shape[1],target.shape[1]))
changeweight = np.zeros((Feature.shape[1],target.shape[1]))
msehistory = []
learningrate = 1
for i in range(10000):
y = np.dot(Feature,weight)
error=sigmoid(y)-Label
mse = np.sum(np.power(error,2))
msehistory.append(mse)
if len(msehistory)>=2:
if(msehistory[-1]>msehistory[-2]):
learningrate = learningrate/2
else:
learningrate = learningrate * 1.1
change = np.dot(Feature.T,error)
changeweight = changeweight + change**2
weight = weight - learningrate* change/np.sqrt(np.float32(changeweight))
if(np.sum(np.square(change))<0.01):
break
return weight
def datingClassTest(train,target,lab):
Ratio = 0.10 #使用Ratio比例来分割预测数据和测试数据
m = train.shape[0]
numTestVecs = int(m*Ratio) #计算测试向
weight=gradentdecent(train[numTestVecs:m,:],target[numTestVecs:m])
errorCount = 0.0
for i in range(numTestVecs): #0到numTestVecs为测试数据集,numTestVecs到为训练数据集
#classifierResult = knn(feature[numTestVecs:m,:],datingLabels[numTestVecs:m],feature[i,:],8)
value = train[i,:].reshape(1,-1)
ones = np.ones((len(value),1))
vas = np.hstack((value ,ones))
np.set_printoptions(suppress=True)
result=np.exp(np.dot(vas,weight))/np.sum(np.exp(np.dot(vas,weight)))
expect = np.argmax(result)
if expect==0:
classifierResult="largeDose"
elif expect==1:
classifierResult="smallDose"
elif expect==2:
classifierResult="didntLike"
if lab[i]==0:
datingLabels="largeDose"
elif lab[i]==1:
datingLabels="smallDose"
elif lab[i]==2:
datingLabels="didntLike"
print("预测分类类别:%s,真实列表类别:%s" % (classifierResult,datingLabels))
if (classifierResult != datingLabels) : errorCount +=1.0
print ("错误率:%f " % (errorCount/float(numTestVecs)))
datingClassTest(rmat1,label12,lab)