机器学习:逻辑回归——原理与代码实现
一 逻辑回归
- 线性回归虽然简单,却有丰富的变化。其形式如下(为书写方便,偏置写进权重向量):$$y=\mathbf{w^Tx}$$考虑单调可微函数\(g\),令\(g(y)=\mathbf{w^Tx}\),在形式上仍然是线性回归,但实质上是在求取输入空间到输出空间的非线性函数映射,其中\(y=g^{-1}(\mathbf{w^Tx})\),这样得到的模型称为“广义线性模型”
- 考虑二分类问题,输出标记为\(y\in\{0,1\}\),而线性回归模型产生的预测值\(z=\mathbf{w^Tx}\)是实值,于是我们需要将\(z\)转换成0/1。最理想的是利用“单位阶跃函数”,但是此函数不连续。因此我们需要寻找一个在一定程度上能够近似单位阶跃函数的函数,并希望他单调可微。sigmoid函数正是这样一个函数
\[y=\frac{1}{1+e^{-z}}$$将$z(x)$带入,得$$y=\frac{1}{1+e^{-\mathbf{w^Tx}}}$$进行变形$$ln\frac{y}{1-y}=\mathbf{w^Tx}$$若将$y$视为样本$x$作为正例的可能性,则$1-y$是其作为反例的可能性,两者的比值称为“几率”,反映了样本作为正例的相对可能性。
将$y$视为类后验概率估计$p(y=1|x)$,则上式重写为$$ln\frac{p(y=1|x)}{p(y=0|x)}=\mathbf{w^Tx}$$显然有$$p(y=1|x)=\frac{e^\mathbf{w^Tx}}{1+e^\mathbf{w^Tx}}=\frac{1}{1+e^{-\mathbf{w^Tx}}},\\p(y=0|x)=\frac{1}{1+e^\mathbf{w^Tx}}$$此即**逻辑回归模型**。**对于新的实例,比较两个概率大小,将实例分到较大的一类。**
####二 模型参数求解
- 利用极大似然函数法求解参数。对于样本数据,假设独立同分布,设$$p(y=1|x)=\pi (\mathbf{x}), p(y=0|x)=1-\pi (\mathbf{x})$$则类别概率可写为$$p(y|\mathbf{x})=[\pi (\mathbf{x})^{y_i}][1-\pi (\mathbf{x})]^{1-y_i}$$似然函数为$$\prod_{i=1}^{m}[\pi (\mathbf{x_i})^{y_i}][1-\pi (\mathbf{x_i})]^{1-y_i}$$对上式取对数得对数似然函数$$\mathit{L}(\mathbf{w})=\sum_{i=1}^{m}[y_ilog\pi (\mathbf{x_i})+(1-y_i)log(1-\pi (\mathbf{x_i}))]$$化简得$$\mathit{L}(\mathbf{w})=\sum_{i=1}^{m}[y_i(\mathbf{w^Tx_i})-log(1+exp(\mathbf{w^Tx_i}))]$$对上式取极大值,得到参数向量。等价于对下式(损失函数)取极小值$$\mathit{J}(\mathbf{w})=\frac{1}{m}\sum_{i=1}^{m}[-y_i(\mathbf{w^Tx_i})+log(1+exp(\mathbf{w^Tx_i}))]$$通常采用梯度下降法和牛顿法。采用梯度下降法求解,$$\frac{\partial J}{\partial w^{(j)}}=\frac{1}{m}\sum_{i=1}^{m}[-y_i\mathbf{x}_i^{(j)}+\frac{1}{1+e^\mathbf{w^Tx_i}}e^\mathbf{w^Tx_i}\mathbf{x}_i^{(j)}]=\frac{1}{m}\sum_{i=1}^{m}(\pi (\mathbf{x}_i)-y_i)x_i^{(j)}\\w^{(j)}=w^{(j)}-\alpha ~~\frac{\partial J}{\partial w^{(j)}}$$由此可以看出,形式上和线性回归一样,但是需要注意的是$\pi(x)$完全不同。
- 梯度向量化表示。$$\frac{\partial J}{\partial \mathbf{w}}=\begin{bmatrix}
\frac{\partial J}{\partial w^{(0)}}\\
\frac{\partial J}{\partial w^{(1)}}\\
\vdots \\
\frac{\partial J}{\partial w^{(n)}}
\end{bmatrix}=\frac{1}{m}\begin{bmatrix}
\sum_{i=1}^{m}(\pi (\mathbf{x}_i)-y_i)\\
\sum_{i=1}^{m}(\pi (\mathbf{x}_i)-y_i)x_i^{(1)}\\
\vdots \\
\sum_{i=1}^{m}(\pi (\mathbf{x}_i)-y_i)x_i^{(n)}
\end{bmatrix}=\frac{1}{m}\mathbf{X^T}\begin{bmatrix}
\pi (\mathbf{x}_1)-y_1\\
\pi (\mathbf{x}_2)-y_2\\
\vdots \\
\pi (\mathbf{x}_m)-y_m
\end{bmatrix}=\frac{1}{m}\mathbf{X^T}\begin{bmatrix}
sigmod(\mathbf{w^Tx}_1)-y_1\\
sigmod(\mathbf{w^Tx}_2)-y_2\\
\vdots \\
sigmod(\mathbf{w^Tx}_m)-y_m
\end{bmatrix}=\frac{1}{m}\mathbf{X^T}(sigmod(\mathbf{Xw})-y)\]
- 逻辑回归的损失函数实质是对数损失函数。在上面利用极大似然估计推导过程中$$p(y|\mathbf{x})=[\pi (\mathbf{x})^{y_i}][1-\pi (\mathbf{x})]{1-y_i}$$对数似然函数为$$\mathit{L}(\mathbf{w})=\sum_{i=1}[y_ilog\pi (\mathbf{x_i})+(1-y_i)log(1-\pi (\mathbf{x_i}))]$$极大化等价于添加负号后极小化$$\mathit{J}(\mathbf{w})=\sum_{i=1}^{m}[-y_ilog\pi (\mathbf{x_i})-(1-y_i)log(1-\pi (\mathbf{x_i}))]=\sum_{i=1}^{m}-log([\pi (\mathbf{x})^{y_i}][1-\pi (\mathbf{x})]^{1-y_i})$$对数损失函数的标准形式$$L(Y,P(Y|X))= -logP(Y|X)$$比较两式可以看出逻辑回归的损失就是对数损失函数。[https://www.zhihu.com/question/65350200]
- 关于损失函数(loss function),代价函数(cost function)与目标函数。术语问题,有的将对于样本的损失函数与整个数据集平均后的损失得到的函数都称为损失函数(比如李航蓝皮书),有的将对于样本的称为损失函数,对于数据集整体的平均损失称为代价函数(比如AndrewNG)。其实质都一样,个人喜欢哪种都可以。目标函数很好理解,最优化经验风险与结构风险的函数。
三 代码实现(未添加正则项)
class Logisticregression():
def __init__(self, learn_rate = 0.001, max_iteration=10000):
self.learn_rate = learn_rate
self.max_iteration = max_iteration
self._X_train = None
self._y_train = None
self._w = None
def fit(self, X_train, y_train):
m_samples, n_features = X_train.shape
self._X_train = np.insert(X_train, 0, 1, axis=1) # 二维数组,每行加一。相当于前面添加了一列。
self._y_train = np.reshape(y_train, (m_samples, 1)) # 二维数组,m 行一列。为了便于直接用向量计算
limit = np.sqrt(1 / n_features)
w = np.random.uniform(-limit, limit, (n_features, 1))
b = 0
self.w = np.insert(w, 0, b, axis=0)
iteration = 0
while iteration < self.max_iteration:
h_x = self._X_train.dot(self.w)
y_pred = 1/(1+np.exp(- h_x)) #第一遍编写完成后,随机生成了二分类数据用于测试正确率只有百分之一,检查了半天才发现漏了负号,一定要仔细!!
w_grad = self._X_train.T.dot(y_pred - self._y_train) # X.T(sigmod(Xw)-y)梯度
self.w = self.w - self.learn_rate * w_grad # 迭代
iteration = iteration + 1
def predict(self, X_test):
X_test = np.insert(X_test, 0, 1, axis=1)
h_x = X_test.dot(self.w)
y_pripr_1 = (1/(1+np.exp(-h_x)))
y_pripr_0 = 1 - y_pripr_1
y_cal = y_pripr_1 - y_pripr_0 #这里实质是与0.5比较
y_class = np.where(y_cal > 0, 1, 0)
return y_class
def score(self, X_test, y_test):
j = 0
y_test = np.reshape(y_test,(len(y_test),1))
for i in range(y_test.shape[0]):
if self.predict(X_test)[i,0] == y_test[i,0]:
j += 1
return ('accuracy: {:.10%}'.format(j / len(y_test)))
- 同样为避免过拟合,需要在损失函数后面加上正则项,计算梯度时同样略有不同。L2正则化用梯度下降时,直接求偏导即可。L1正则化在进行梯度下降时,通常有坐标轴下降法和最小角回归法
四 多分类逻辑回归
- 假设类别数共有K个,则根据对数几率之比组成的K-1个方程与概率和为1的方程共K个,可以得出多分类逻辑回归模型如下:$$P(y=k|\mathbf{x})=\frac{exp(\mathbf{wTx})}{1+\sum_{k=1}exp(\mathbf{wTx})},k=1,2,...,K-1\P(y=K|\mathbf{x})=\frac{1}{1+\sum_{k=1}exp(\mathbf{w^Tx})}$$
- [https://www.cnblogs.com/pinard/p/6047802.html]随机数据生成。
关于sklearn中的逻辑回归参数选择问题l
- 参考链接,写的很好。[https://www.cnblogs.com/pinard/p/6035872.html]
如有错误,欢迎批评指正。转载请注明出处。沟通交流liuyingxinwy@163.com