对于分类问题的解决,我以前分享过一些算法,比如:SVM,决策树,KNN等(写的还算马虎,有兴趣的可以看看)。但是后来,开始学习到神经网络与深度学习的时候,才知道逻辑回归的重要性,所以今天我就想与大家来分享一下我学习逻辑回归的心得,有什么不足的地方,还望大家指出。
目录
一、逻辑回归函数模型
众所周知,逻辑回归虽然顶着回归的“名”,但干的确是分类的“事”。而它用到回归的“名”是因为它是在线性函数()的基础上套了一个sigmoid函数()。sigmoid函数图像如下所示:
那它是为什么可以在线性函数的基础上加一个sigmoid函数完成分类的任务呢?为什么不可以用别的函数?(接下来也是我个人认为,逻辑回归最重要的一个理解点。这里先只讨论二分类。)
其实两个问题答案的思考都指向了同一个方向——sigmoid函数的函数指的意义。
sigmoid函数它其实还有一个别称,叫做对数几率函数。(是几率,不是概率!!!!!)
而sigmoid有这个称呼,是因为:
对数我们都知道,那什么是几率呢? 几率是指,期望的结果比上不期望出现的结果。比如期望1的类别的概率是0.6,不期望0的类别是0.4,那么几率=0.6/0.4。而在上式中,y就是类别1的概率!
(Z是线性函数,这个式子怎么来的就不详细分享了,实在是太过于复杂,大家想了解的话可以在下方评论点击链接)
可想而知,当我们输入个体特征X的时候,当sigmoid函数值大于0.5时,我们是不是就可以判定这个个体它是属于类别1的,也就是说sigmoid函数值等于0.5是类别1和类别0的分界值,那它什么时候等于0.5呢? Z = 0的时候,sigmoid函数值为0.5,也就是线性函数的时候,而这个线性函数也就是这两个类别的分界面,如图(个体特征X是二维,所以分界面是一条线):
逻辑回归函数就是靠着这样的方式完成的分类。
那么问题又来了,我们如何得到最好的参数W,使得Z = 0?下面,就和大家分享一下逻辑回归函数的损失函数。
二、平均交叉熵误差
逻辑回归的目的是将样本分成非0即1的两类,但是我们也关心样本分类的准确性,比如一个肿瘤,我们预测它是恶性的,我们也会关心它是恶性的可能性究竟是多大。 或者可以反过来这么说,我们其实是通过概率将样本分成了0和1两类。在逻辑回归中,为了使得求解W过程更具普遍意义,我们用更常用的最大似然方法求解。
似然:
假设我们要生成一个模型T= 0,0,0,1,我们假设它生成类别1的概率是0.1,那么生成这个数据的似然值=0.9 * 0.9 * 0.9 * 0.1。这个概率值就叫做似然。然而,在实际问题中我们并不会知道要生成的模型是怎么样的,于是似然有一个更普遍的表达式:
T是类别中的1或0,N是生成模型T的个体数,X自然是个体的特征,tn是个体的类别,yn是类别1或类别0的概率。
为了求得最大似然,自然是要求导的,而为了求导,我们会将上式两边取对数,鉴于对数的单调递增特性,当上式取得最大的时候,对数似然也是最大的。
而对于求最大值我们又习惯于将它转为求最小值,所以最后我们的损失函数是:
这个式子我们也叫它平均交叉熵误差。
有了损失函数,接下来求导就🆗了,这里我就省掉步骤了(耐心一点,就可以了,建议大家自己动手试试。),直接给出来
注意,对最后一个w参数的的导数是:
这就🆗啦,有了逻辑回归函数和平均交叉熵,我们就可以轻松的解决二分类的问题了。
三、解决二分类源码
我们先来生成一个二维输入的数据X,和目标数据T2
np.random.seed(seed = 1)
N = 100
K = 2
T2 = np.zeros((N,2),dtype=np.uint8)#uint数据类型加快计算速度
X = np.zeros((N,2))
print(T2)
X_range0 = [-3,3]
X_range1 = [-3,3]
Mu = np.array([[-.5,.5],[.5,1.0]])
Sig = np.array([[.7,.7],[.8,.3]])
pi = np.array([0.4,0.8])
for n in range(N):
wk = np.random.rand()
for k in range(K):
if wk < pi[k]:
T2[n,k] = 1
break
for k in range(2):
X[n,k] = (np.random.randn() * Sig[T3[n,:] == 1,k] + Mu[T3[n,:] == 1,k])
数据可视化
def show_data2(x, t):
wk, K = t.shape
c = [[.5, .5, .5], [1, 1, 1], [0, 0, 0]]
for k in range(K):
plt.plot(x[t[:, k] == 1, 0], x[t[:, k] == 1, 1], marker='o', linestyle='none')
plt.grid(True)
plt.xlim(X_range0)
plt.ylim(X_range1)
plt.subplots_adjust(wspace=0.5)
逻辑回归模型
def logistic2(x0,x1,w):
y = 1 / (1 + np.exp(-(w[0] * x0 + w[1] * x1 +w[2])))
return y
平均交叉熵误差
def cee_logistic2(w,x,t):
X_n = x.shape[0]
y = logistic2(x[:,0],x[:,1],w)
cee = 0
for n in range(len(y)):
cee = cee - (t[n,0] * np.log(y[n]) + (1 - t[n,0]) * np.log(1 - y[n]))
cee = cee / X_n
return cee
平均交叉熵误差导数
def dcee_logistic2(w,x,t):
X_n = x.shape[0]
y = logistic2(x[:,0],x[:,1],w)
dcee = np.zeros(3)
for n in range(len(y)):
dcee[0] = dcee[0] + (y[n] - t[n,0]) * x[n,0]
dcee[1] = dcee[1] + (y[n] - t[n,0])* x[n,1]
dcee[2] = dcee[2] + (y[n] - t[n,0])
dcee = dcee / X_n
return dcee
寻找逻辑回归模型参数(用到的是我在回归里面分享到scipy.optimize库里的minimize函数)
def fit_logistic(w_inint,x,t):
res = minimize(cee_logistic2,w_inint,args=(x,t),jac=dcee_logistic2,method='CG')
return res.x
最后带入一个W的初始值就🆗了,就成功完成分类啦
W_init = [1,1,-1]
W = fit_logistic(W_init,X,T2)
xb = np.linspace(-3,3,100)
n = X.shape[0]
a = np.ones((n,1))
Y = - (W[0] * xb + W[2]) / W[1]
plt.plot(xb,Y)
plt.show()