**逻辑回归**(Logistic Regression)
- 逻辑回归的定义
- 逻辑回归公式的理解
1. 逻辑回归是一种结局分类问题的算法,将样本的特征与样本发生的概率联系起来,通常,Logistic回归用于二分类问题,例如预测明天是否会下雨。当然它也可以用于多分类问题,下文只讨论二分类问题。通过概率来确定标签。就相当于一个复合函数(u(f(x))>>>P)的过程。如果P>=0.5标签为1,P<=0.5标签为0。
2. Sigmoid(S形函数)函数:
我们将他可视化:
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(t):
return 1/(1+np.exp(-t))
x=np.linspace(-10,10,100)
y=sigmoid(x)
plt.plot(x,y)
plt.show()
它的值域是在(0,1)之间,开区间。且t趋于无穷大,函数趋于1,反之t趋于无穷小,函数趋于0,当t>0,P>0.5,t<0,P<0.5.
3. 逻辑回归函数公式
g(f(x))其中f(x)即是线性回归中的θT*Xb
问题:怎末求得θ的值?>>>与线性回归一致,求得损失函数,并求其最小值.
cost=如果y(真实值)=1,P越小,cost越大。如果y=0,P越大,cost越大
cost=
{
-log(P) if y=1
-log(1-P) if y=0
}
所以cost=-ylog(P)-(1-y)log(1-P)
给定一个样本,我们就可以通过这个代价函数求出,样本所属类别的概率,而这个概率越大越好,所以也就是求解这个代价函数的最大值。既然概率出来了,那么最大似然估计也该出场了。假定样本与样本之间相互独立,那么整个样本集生成的概率即为所有样本生成概率的乘积,再将公式对数化,便可得到如下公式:
如果我们有m个样本,我们要让曲线最拟合(使函数极大的逼近原始数据(总误差最小),并反应原始数据的整体趋势),
但是该函数不能运用一般的数学方法,所以我们用梯度算法。
梯度算法:
2.梯度:
梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
当梯度与方向导数的方向向量同向时,斜率达到最大。反之相反时最小。
3.梯度上升法:
与梯度下降法类似,只是求得的是函数最大值。
梯度下降法:
- 优势:是一种基于搜索的优化方法
- 作用:最小化一个损失函数。
其中当导数为正时theta趋向于最小值点的theta,反之也相同。
对于,我们有这样的定义:
的取值决定了theta的变化快慢,变化慢了,收敛的就慢了,反之可能就不收敛。对应的图像为。
而且,我们不一定只存在一个极小值点,对于最小的极小值点,我们称之为全局最优解,反之称之为局部最优解。
对于该类问题解决的办法:所次运行,随机化初始点。
简单线性回归满足只有一个极值点,因为其损失函数为一个二次方程。
多元线性回归中的梯度下降法:
其中(theta1-theta0)/-a=gridientJ(theta)说明了,x该变量的方向与梯度方向相反。
从而满足了收敛到最小值的原理。
在多元线性回归中,我们要求
从而解得theta
加入我们要比较我们模型的好坏时,需要比较损失函数的总误差时,如果两样本数据不一致,则会导致样本多的总误差大,违背了比较的意义,所以我们取均值来比较(MSE)
即在上图中的梯度也/m.
实现梯度算法:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(666)
x=2*np.random.random(size=100)
y=x+3.+4.+np.random.normal(size=100)
plt.scatter(x,y)
plt.show()
def J(theta,X_b,y):
try:
return np.sum(y-X_b.dot(theta)**2)/len(X_b)
except:
return float("inf")
def dJ(theta,X_b,y):
tidu=np.empty(len(theta))
tidu[0]=np.sum(X_b.dot(theta)-y)
for i in range(1,len(theta)):
tidu[i]+=(X_b.dot(theta)-y).dot(X_b[:,i])
return 2*tidu/len(X_b)
theta_history=[]
def gradient(X_b,y,eta,chushitheta,cishu=1e4,cha=1e-18):
theta=chushitheta
chushicishu=0
theta_history.append(chushitheta)
while chushicishu<cishu:
gradient=dJ(theta,X_b,y)
last_theta=theta
theta=theta-eta*gradient
theta_history.append(theta)
if (abs (J(last_theta,X_b,y)-J(theta,X_b,y))<cha):
break
cishu+=1
X_b=np.hstack([np.ones((len(x),1)),x.reshape(-1,1)])
eta=0.01
theta=np.zeros(X_b.shape[1])
theta=gradient(X_b,y,eta,theta)
print(theta)
我们是否不用使用大量的for循环而简便算法,答案是可以,上式可以向量化为
这样原始的代码在def dJ()我们只需returnX_b.T.dot(X_b.dot(theta)-y)/len(X_b)
归一化:
为甚麽归一化?对于一个量度相差很大的数据,它们对应的theta0,theta1,等变量也相差很大,它们对应的图形偏扁,所以对应的梯度,某一维度的步长较快,到达最小值的速度过快,可能会形成“之”字,如果归一化,则图形变园,收敛度变高。
下面我们回到逻辑回归:
在逻辑回归的损失函数中,sigmoid函数的求导是关键,下面我们讨论对他的求导:
我么单独对thetaj求导
对(1-y)log(sigmoid)求导
在讨论对thetaj求导,两式相加,
我们发现他与线性回归的损失函数相似,显然它也拥有向量化方程,=1/m(X_b)T(sigmoid(X_b.dot(theta))-y)
代码复现:
import numpy as np
import matplotlib.pyplot as plt
def openfile():#打开文件提取信息
data = [] #创建数据列表
label = [] #创建标签列表
fr = open("C:\\Users\\24308\\Desktop\\data3.txt")
fe=fr.readlines()
while "\n" in fe:
fe.remove("\n") #打开文件
for line in fe: #逐行读取
linechuli = line.strip().split() #去空格\n等,放入列表
data.append([1.0, float(linechuli[0]), float(linechuli[1])]) #添加数据
label.append(int(linechuli[2])) #添加标签
fr.close()
return data, label
#sigmoid函数
def sigmoid(t):
return 1.0 / (1 + np.exp(-t))
#下面我们使用梯度上升算法
def graident(data,label):
data1 = np.mat(data) #转换成numpy的mat
label = np.mat(label).transpose() #转换成numpy的mat,并进行转置
m, n = np.shape(data1) #返回data1的大小。m为行数,n为列数。
alpha = 0.001 #移动步长,也就是学习速率,控制更新的幅度。
maxcishu = 500 #最大迭代次数
theta = np.ones((n,1))
for k in range(maxcishu):
h = sigmoid(data1 * theta) #梯度上升矢量化公式
error = label - h
theta= theta + alpha * data1.transpose() * error
return theta.getA()
data,label=openfile() #返回自己的数组
theta=graident(data,label)
#绘制数据集,决策边界
def huizhishuju(theta):
data,label=openfile()
data=np.array(data)
lenn=np.shape(data)[0]
zhengben_x=[];zhengben_y=[]#概率为1的样本
fuben_x=[];fuben_y=[]#概率为0的样本
for i in range(lenn):
if label[i]==1:
zhengben_x.append(data[i,1])
zhengben_y.append(data[i,2])
else:
fuben_x.append(data[i,1])
fuben_y.append(data[i,2])
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(zhengben_x, zhengben_y, c = 'red', marker = 's')#绘制正样本
ax.scatter(fuben_x, fuben_y, c = 'green')
x = np.arange(-3.0, 3.0, 0.1)
y = (-theta[0] - theta[1] * x) / theta[2]
ax.plot(x, y) #绘制title
plt.xlabel('x')
plt.ylabel('y') #绘制label
plt.show() #显示
#假设,我们使用的数据集一共有100个样本。那么,data就是一个100*3的矩阵。
#每次计算h的时候,都要计算data*theta这个矩阵乘法运算,要进行100*3次乘法运算和100*2次加法运算。同理,更新回归系数(最优参数)theta时,也需要用到整个数据集,要进行矩阵乘法运算。总而言之,该方法处理100个左右的数据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。因此,需要对算法进行改进,我们每次更新回归系数(最优参数)的时候,能不能不用所有样本呢?一次只用一个样本点去更新回归系数(最优参数)?这样就可以有效减少计算量了,这种方法就叫做随机梯度上升算法。
#该算法第一个改进之处在于,alpha在每次迭代的时候都会调整,并且,虽然alpha会随着迭代次数不断减小,但永远不会减小到0,因为这里还存在一个常数项。必须这样做的原因是为了保证在多次迭代之后新数据仍然具有一定的影响。如果需要处理的问题是动态变化的
def suijigraident(data1,label, diedaicishu=150):
m,n = np.shape(data1) #返回dataMatrix的大小。m为行数,n为列数。
theta= np.ones(n,dtype=np.float) #参数初始化
for j in range(diedaicishu):
dataxingzhuang = list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01 #降低alpha的大小,每次减小1/(j+i)。
randIndex = int(np.random.uniform(0,len(dataxingzhuang))) #随机选取样本
h = sigmoid(sum(data1[randIndex]*theta)) #选择随机选取的一个样本,计算h
error = label[randIndex] - h #计算误差
theta = theta + alpha * error * data1[randIndex] #更新回归系数)
del(dataxingzhuang[randIndex]) #删除已经使用的样本
return theta
theta=suijigraident(data,label)
huizhihshuju(theta)
优点:易于实现,易于理解。
缺点:容易欠拟合。
学习参考:https://blog.csdn.net/c406495762/article/details/77851973