逻辑回归(Logistic Regression)
首先要明确一点,逻辑回归不仅仅可以处理回归问题,也可以处理分类问题。
线性回归的问题
当我们遇到如下数据的时候,可以用线性回归很好的进行拟合。
但是线性回归对离群点(outliers)很敏感,如我们加入一个离群点,就会做出如下拟合。这样的拟合会让正常的数据点的预测变得不准确。
这样我们就需要一个非线性的函数来拟合。于是伟大的科学家就选择了sigmoid函数。为什么要选择sigmoid函数?
Sigmoid函数
Sigmoid函数形式如下:
\[g(z) = \frac{1}{1+e^{-z}}\]
函数图如下:
可见sigmoid函数将 $(-\infty,+\infty) \(的值映射到\)(0,1)$区间内,在二分类问题中,我们可以认为若预测值小于0.5,则其分类为0,若预测值大于0.5,则其分类为1。
当然具体问题具体分析,当我们预测病人癌症患病概率时,若预测值为0.49,表明该病人有0.49的可能性患癌,我们可以适当调整阈值,比如\(x=0.3\)等,这些都是后话。
Sigmoid的导数
在这里不得不提一下它的导数,因为在逻辑回归中,计算梯度下降必然要计算其导数。sigmoid函数优秀的性质可以让我们很方便的计算其导数。
\[\begin{align}\frac{\mathrm{d}g(z)}{\mathrm{d}z} &= \frac{-1}{(1+e^{-z})^2}·(-e^{-z})\\&= \frac{1}{1+e^{-z}}·(1-\frac{1}{1+e^{-z}})\\&= g(z)(1-g(z))\end{align}\]
\(h_\theta (x)\)
自然而然地,我们可以假设:
\[h_\theta (x) = \frac{1}{1+e^{-\theta^T x}}\]
其中:
\[\theta = \left<\theta_1,\theta_2,...,\theta_n\right>\\x = {x^1,x^2,...,x^m}\]
这里\(x^1\)代表的是第一个数据,包括n个特征值\({x_1,x_2,...,x_n}\)和1个label。
逻辑回归
下面我们开始进行逻辑回归的一次运算。
数据我们使用的时sklearn库中的癌症预测数据,通过cancer.DESCR
可以看到该数据集有569个实例,每个数据包括10个特征和1个标签以表示是否患病。
from sklearn import datasets
cancer = datasets.load_breast_cancer()
data = cancer.data
target = cancer.target
print(cancer.DESCR)
根据线性回归的计算过程,我们不难总结出逻辑回归的几个步骤:
- 随机初始化\(\theta\)
- 找到合适的损失函数
- 通过梯度下降法迭代找出使损失函数最小的\(\theta\)值。
1.随机初始化\(\theta\)
通过python函数来实现,没什么好说的。
2.找到合适的损失函数
不同于线性回归,在处理二分类问题中,我们有两个目标值0和1,而我们预测出的却是在$(-\infty,+\infty) $之间的离散值,我们需要达到两点:
1.在预测为正例时,我们希望预测值越靠近1,损失函数越小。
\[J(\theta) =-log(h_\theta (x))\]
2.在预测为反时,我们希望预测值越靠近0,损失函数越小。
\[J(\theta) = -log(1-h_\theta (x))\]
将二者结合,我们得到:
\[J(\theta) = \frac{1}{m}\sum_{i=1}^m\left(-y·log(h_\theta (x))-(1-y)·log(1-h_\theta (x))\right)\]
3.通过梯度下降法迭代找出使损失函数最小的\(\theta\)值
又到了我们最喜欢的公式推导环节,令:
\[z = \theta x = \theta_0x_0+\theta_1x_1+...\\h_\theta(x)=g(z)\]
注意这里\(x_1\)代表一个向量,由所有实例的第1个特征值组成\(x_1^0,x_1^1,x_1^2...\)
先来计算\(h_\theta(x^i)\)对第\(j\)个参数的偏导(根据上面的sigmoid导数和链式法则可以推出):
\[\begin{align}\frac{\partial h_\theta(x^i)}{\partial \theta_j}&= \frac{\partial g(z^i)}{\partial z^i}·\frac{\partial z^i}{\partial \theta_j}\\&= h_\theta(x^i)·(1-h_\theta(x^i))·\frac{\partial z^i}{\partial \theta_j}\\&= h_\theta(x^i)·(1-h_\theta(x^i))·x_j^i\end{align}\]
然后:
$$
\begin{align}
J(\theta) &= \frac{1}{m}\sum_{i=1}^m\left(-y^i·log(h_\theta (x^i))-(1-y^i)·log(1-h_\theta (x^i))\right)\
\frac{\partial J(\theta)}{\partial \theta_i} &= \frac{1}{m}\sum_{i=1}^m\left(\frac{-y^i}{h_\theta (x^i)}·\frac{\partial h_\theta(x^i)}{\partial \theta_j}-\frac{(1-y^i)}{1-h_\theta (x^i)}·\frac{\partial(1- h_\theta(x^i))}{\partial \theta_j}\right)\
&= \frac{1}{m}\sum_{i=1}^m(x_j^i·(h_\theta (x^i)-y^i))(利用之前推导的式子)
\end{align}
$$
用矩阵描述
因为在计算机处理时,经常用Numpy等进行矩阵上的计算,所以我想用矩阵来描述一下。
定义矩阵:
\[\begin{align}X &= \{X^0,X^1,...X^m\}(共m个数据,shape:m*n)\\X^i&=\{X^i_0,X^i_1,...X^i_n\}(每个数据n个特征值,shape:1*n)\\Y &= \{Y^0,Y^1,...Y^m\}(每个数据的label,shape:1*m)\\\theta&= \{ \theta_0,\theta_1...\theta_n \}(需要优化的参数,shape:1*m)\end{align}\]
上述定义中, \(Y\)以及\(\theta\)有些特殊,在sklearn中读出的标签值(data.target)的维度实际上为(m,),如下图,含义是他是一个有569个元素的list,而不是一个矩阵。
通常我习惯用reshape
函数处理成m*1维的矩阵。
target.reshape(-1, 1)
而\(\theta\)为了与target保持一致,我也如此初始化(仅为个人习惯):
theta = np.ones(data.shape[1])
下面开始推导: