跟我一起机器学习系列文章将首发于公众号:月来客栈,欢迎文末扫码关注!
0 前言
前面几篇文章笔者详细的介绍了什么是逻辑回归、如何进行多分类、以及分类任务对应的评价指标等,算是完成了前面第一个阶段的学习。但是到目前为止仍旧有一些问题没有解决,映射函数长什么样?逻辑回归的目标函数怎么来的?如何自己求解实现逻辑回归?下面我们就来一一回答这三个问题。在这三个问题解决后,整个逻辑回归算法的主要内容也就算是完成了。
1 映射函数
1.1 Sigmoid函数
前面我们只是介绍了通过一个函数将特征的线性组合映射
映射到区间
中去,那么这个
长什么样呢?
如图所示便是的函数图像,其同时也被称为sigmoid函数,其数学定义如下:
而之所以选择sigmoid的原因在于:①连续光滑处处可导;②sigmoid函数关于点中心对称;③sigmoid函数求导简单,其求导结果为:
。
1.2 实现
对于的实现也是非常简单,一句代码就完成了:
def g(z):
return 1 / (1 + np.exp(-z))
2 目标函数
2.1 设定概率表示
介绍完sigmoid函数下面就开始介绍逻辑回归的目标函数到底是怎么来的。
设:
其中均为一个列向量,
的含义为当给定参数
时,样本
属于
这个类别的概率为
。但是这样需要前面两个等式来衡量每一个样本所属类别的概率,为了更加方便的表示每个样本的概率,可以改写为如下形式:
这样一来,不管样本属于哪个类别,都可以通过等式
来进行概率计算。
我们知道,在机器学习中都是通过给定训练集,即来求得其中的未知参数
。换句话说,对于每个给定的
,我们已经知道了其所属的类别
,即**
的这样一个分布结果我们是知道的**。那么什么样的参数
能够使得已知的
这样一个结果最容易出现呢?即给定什么样的参数
,使得当输入
这
个样本时,最能够产生已知的类别结果。
2.2 引入最大似然
上面绕来绕去说了这么些目的就只有一个,即为什么要用似然函数进行下一步的计算。由上述分析可知,为了能够使得这样一个结果最容易出现,我们应该最大化如下似然函数:
对等式两边同时取自然对数得:
注:
易知,最大化函数等价于最小化函数
,且由于
为常数,故进一步等价于最小化函数
由此,我们便得到了逻辑回归算法的目标函数:
2.3 计算梯度
在求解线性回归中,我们首次引入并讲解了梯度下降算法,知道可以通过梯度下降算法来最小化某个目标函数。当目标函数取得(或接近)其函数最小值时,我们便得到了目标函数中对应的未知参数。由此可知,欲最小化函数 必须先计算得到其关于参数的梯度。故:
目标函数对
的梯度为:
目标函数对
的梯度为:
进一步,对公式矢量化可得:
3 手动实现
3.1 二分类
-
基本函数实现
def sigmoid(z): return 1 / (1 + np.exp(-z)) def sigmoid_prime(z): return sigmoid(z) * (1 - sigmoid(z)) def hypothesis(X, W, bias): z = np.matmul(X, W) + bias h_x = sigmoid(z) return h_x def prediction(X, W, bias, thre=0.5): h_x = hypothesis(X, W, bias) y_pre = (h_x > thre) * 1 return y_pre -
目标函数与梯度下降
def cost_function(X, y, W, bias): m, n = X.shape h_x = hypothesis(X, W, bias) cost = np.sum(y * np.log(h_x) + (1 - y) * np.log(1 - h_x)) return -cost / m def gradient_descent(X, y, W, bias, alpha): m, n = X.shape h_x = hypothesis(X, W, bias) grad_w = (1 / m) * np.matmul(X.T, (h_x - y)) # [n,m] @ [m,1] grad_b = (1 / m) * np.sum(h_x - y) W = W - alpha * grad_w # 梯度下降 bias = bias - alpha * grad_b return W, bias -
训练与结果
def train(X, y, ite=200): m, n = X.shape # 506,13 # W = np.random.randn(n, 1) W = np.random.uniform(-0.1, 0.1, n).reshape(n, 1) b = 0.1 alpha = 0.08 costs = [] for i in range(ite): J = cost_function(X, y, W, b) costs.append(J) W, b = gradient_descent(X, y, W, b, alpha) y_pre = prediction(X, W, b) print(classification_report(y, y_pre)) print('Accuracy: ', accuracy(y, y_pre)) return costs #结果 precision recall f1-score support 0 0.99 0.97 0.98 212 1 0.98 0.99 0.99 357 accuracy 0.98 569 -
损失值
3.2 多分类
由于篇幅所限这里就只展示核心部分,其中train_binary()这个函数就是上面的train(),详细代码参见引用。
def train(x, y, iter=300):
class_type = np.unique(y)
costs = []
W, b = [], []
for c in class_type:
label = (y == c) * 1
tmp = train_binary(x, label, iter=iter)
costs.append(tmp[0])
W.append(tmp[1])
b.append(tmp[2])
costs = np.vstack(costs)
costs = np.sum(costs, axis=0)
y_pre = prediction(x, W, b)
print(classification_report(y, y_pre))
print('Accuracy by impleme: ', accuracy(y, y_pre))
return costs
#结果
precision recall f1-score support
0 1.00 1.00 1.00 50
1 0.94 0.90 0.92 50
2 0.90 0.94 0.92 50
accuracy 0.96 150
4 总结
通过本篇文章的介绍,对于逻辑回归的主要内容也算到此结束了,但是还有一些提升模型性能的方法(例如数据集划分、正则化等)没有阐述,这在下一节的内容将进行介绍。
总结一下,如上图所示笔者首先通过一个小的示例引入了什么是分类模型,并通过在线性回归的基础上一步步的带出了什么是逻辑回归模型。然后笔者通过两篇文章介绍了逻辑回归从建模到利用开源库进行求解的过程,以及分类任务中常见的评价指标和多分类的方法等,完成了第一阶段的学习。最后,笔者通过本篇文章详细介绍了逻辑回归算法目标函的推导以及梯度的迭代公式等,还手动的实现了分类代码,进一步的完成了后面两个阶段的学习。本次内容就到此结束,感谢阅读!
若有任何疑问与见解,请发邮件至moon-hotel@hotmail.com并附上文章链接,青山不改,绿水长流,月来客栈见!
引用
- 示例代码:关注公众号回复“示例代码”即可直接获取!