# 《Machine Learning in Action》—— Taoye给你讲讲Logistic回归是咋回事

• 和Logistic回归交个朋友，互相认识一下吧（原理剖析，公式推导）
• 基于Logistic回归手撕解决二分类问题

## 一、和Logistic回归交个朋友，互相认识一下吧（原理剖析）

\begin{aligned} & x_i = (x_i^{(0)},x_i^{(1)},x_i^{(2)},...,x_i^{(N)}) \\ &y_i\in (0,1) \end{aligned}

\begin{aligned} & P(y_i=1|x_i;w)=h_w(x_i) \\ & P(y_i=0|x_i;w)=1-h_w(x_i) \end{aligned}

$P(y_i|x_i;w)=h_w(x_i)^{y_i}(1-h_w(x_i))^{1-y_i}$

$\prod_{i=1}^nh_w(x_i)^{y_i}(1-h_w(x_i))^{1-y_i}$

$L(w)=\sum_{i=1}^ny_ilog(h_w(x))+(1-y_i)log(1-h_w(x))$

$g(z)=\frac{1}{1+e^{-z}}$

$h_w(x)=g(w^Tx)=\frac{1}{1+e^{-w^Tx}}$

\begin{aligned} L(w) & =\sum_{i=1}^ny_ilog(h_w(x_i))+(1-y_i)log(1-h_w(x_i)) \\ & = \sum_{i=1}^n[y_i\log\frac{h_w(x_i)}{1-h_w(x_i)}+log(1-h_w(x_i))] \\ & = \sum_{i=1}^n[y_i(w^Tx_i)-w^Tx_i-log(1+e^{-w^Tx_i})] \end{aligned}

$\frac{\partial L(w)}{\partial w}=\sum_{i=1}^n(y_i-h_w(x_i))x_i$

\begin{aligned} & w_0^{new}=w_0^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^0 \\ & w_1^{new}=w_1^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^1 \\ & w_2^{new}=w_2^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^2 \\ & \vdots \\ & w_N^{new}=w_N^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_N^2 \\ \end{aligned}

## 二、基于Logistic回归手撕解决二分类问题

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: 通过NumPy准备数据集
Return:
x_data：样本的属性特征
y_label：样本属性特征所对应的标签
"""
def establish_data():
# np.random.seed(1) # 可根据需要是否选择随机种子
x_data = np.concatenate((np.add(np.random.randn(50, 2), [1.5, 1.5]),
np.subtract(np.random.randn(50, 2), [1.5, 1.5])),
axis = 0)      # random随机生成数据，+ -1.5达到不同类别数据分隔的目的
y_label = np.concatenate((np.zeros([50]), np.ones([50])), axis = 0)    # concatenate合并数据集
return x_data. y_label


$\frac{\partial L(w)}{\partial w}=\sum_{i=1}^n(y_i-h_w(x_i))x_i$

\begin{aligned} & w_0^{new}=w_0^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^0 \\ & w_1^{new}=w_1^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^1 \\ & w_2^{new}=w_2^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^2 \\ & \vdots \\ & w_N^{new}=w_N^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^N \\ \end{aligned}

$w^{new}=w^{old}+\alpha x^T(y-h_w(x))$

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: Sigmoid函数
Parameters:
in_data: sigmoid处理的输入数据
Return:
sigmoid_result：sigmoid函数处理之后的结果
"""
def sigmoid(in_data):
return 1 / (1 + np.exp(-in_data))

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: Logistic回归的核心方法，主要使用到了梯度上升算法
Parameters:
x_data：样本集的属性特征
y_label：样本集的标签
Return:
weights: 模型训练完成之后的w参数
"""
x_data, y_label = np.mat(x_data), np.mat(y_label).T    # 转换成矩阵形式，方便操作
data_number, attr_number = x_data.shape                # 获取样本数量以及属性特征的数量
learning_rate, max_iters, weights = 0.001, 500, np.ones([attr_number, 1])    # 一些超参数和参数的初始化
loss_list = list()
for each_iter in range(max_iters):         # 更新迭代max_iters次
sigmoid_result = sigmoid(np.matmul(x_data, weights))        # sigmoid处理 x*w
difference = y_label - sigmoid_result                             # 计算损失值
weights = weights + learning_rate * np.matmul(x_data.T, difference)    # 更新权重w向量
loss = np.matmul(y_label.T, np.log(sigmoid_result)) + np.matmul((1 - y_label).T, np.log(1 - sigmoid_result))
loss_list.append(loss.tolist()[0][0])
return weights.getA(), loss_list


"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: 可视化分类结果，也就是Logistic回归的可视化
Parameters:
x_data：样本集的属性特征
y_label：样本集的标签
weights：模型所需要的参数，也就是权重
"""
def show_result(x_data, y_label, weights):
from matplotlib import pyplot as plt
w_1, w_2, w_3 = weights[0][0], weights[1][0], weights[2][0]    # 获取权重参数
min_x_1, min_x_2 = np.min(x_data, axis = 0)[:-1]               # 获取属性特征的最小值
max_x_1, max_x_2 = np.max(x_data, axis = 0)[:-1]               # 获取属性特征的最大值
line_x_1 = np.linspace(min_x_1 - 0.2, max_x_1 + 0.2, 1000)     # 决策直线的横坐标
line_x_2 = (-w_3 - w_1 * line_x_1) / w_2                       # 决策直线的纵坐标
plt.scatter(x_data[:, 0], x_data[:, 1], c = y_label)           # 绘制数据的散点图
plt.plot(line_x_1, line_x_2)                                   # 绘制分类的决策直线


import numpy as np

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: 通过NumPy准备数据集
Return:
x_data：样本的属性特征
y_label：样本属性特征所对应的标签
"""
def establish_data():
#     np.random.seed(1)
x_data = np.concatenate((np.add(np.random.randn(50, 2), [1.5, 1.5]),
np.subtract(np.random.randn(50, 2), [1.5, 1.5])),
axis = 0)      # random随机生成数据，+ -1.5达到不同类别数据分隔的目的
x_data = np.concatenate((x_data, np.ones([100, 1])), axis = 1)
y_label = np.concatenate((np.zeros([50]), np.ones([50])), axis = 0)    # concatenate合并数据集
return x_data, y_label

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: Sigmoid函数
Parameters:
in_data: sigmoid处理的输入数据
Return:
sigmoid_result：sigmoid函数处理之后的结果
"""
def sigmoid(in_data):
return 1 / (1 + np.exp(-in_data))

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: Logistic回归的核心方法，主要使用到了梯度上升算法
Parameters:
x_data：样本集的属性特征
y_label：样本集的标签
Return:
weights: 模型训练完成之后的w参数
"""
x_data, y_label = np.mat(x_data), np.mat(y_label).T    # 转换成矩阵形式，方便操作
data_number, attr_number = x_data.shape                # 获取样本数量以及属性特征的数量
learning_rate, max_iters, weights = 0.001, 500, np.ones([attr_number, 1])    # 一些超参数和参数的初始化
loss_list = list()
for each_iter in range(max_iters):         # 更新迭代max_iters次
sigmoid_result = sigmoid(np.matmul(x_data, weights))        # sigmoid处理 x*w
difference = y_label - sigmoid_result                             # 计算损失值
weights = weights + learning_rate * np.matmul(x_data.T, difference)    # 更新权重w向量
loss = np.matmul(y_label.T, np.log(sigmoid_result)) + np.matmul((1 - y_label).T, np.log(1 - sigmoid_result))
loss_list.append(loss.tolist()[0][0])
return weights.getA(), loss_list

"""
Author: Taoye
微信公众号: 玩世不恭的Coder
Explain: 可视化分类结果，也就是Logistic回归的可视化
Parameters:
x_data：样本集的属性特征
y_label：样本集的标签
weights：模型所需要的参数，也就是权重
"""
def show_result(x_data, y_label, weights):
from matplotlib import pyplot as plt
w_1, w_2, w_3 = weights[0][0], weights[1][0], weights[2][0]    # 获取权重参数
min_x_1, min_x_2 = np.min(x_data, axis = 0)[:-1]               # 获取属性特征的最小值
max_x_1, max_x_2 = np.max(x_data, axis = 0)[:-1]               # 获取属性特征的最大值
line_x_1 = np.linspace(min_x_1 - 0.2, max_x_1 + 0.2, 1000)     # 决策直线的横坐标
line_x_2 = (-w_3 - w_1 * line_x_1) / w_2                       # 决策直线的纵坐标
plt.scatter(x_data[:, 0], x_data[:, 1], c = y_label)           # 绘制数据的散点图
plt.plot(line_x_1, line_x_2)                                   # 绘制分类的决策直线

if __name__ == "__main__":
x_data, y_label = establish_data()
show_result(x_data, y_label, weights)
#     from matplotlib import pyplot as plt
#     plt.plot(np.arange(len(loss_result)), loss_list)


$L(w)=\sum_{i=1}^ny_ilog(h_w(x))+(1-y_i)log(1-h_w(x))$

$h_w(x)=g(w^Tx)=\frac{1}{1+e^{-w^Tx}}$

\begin{aligned} L(w) & =\sum_{i=1}^ny_ilog(h_w(x_i))+(1-y_i)log(1-h_w(x_i)) \\ & = \sum_{i=1}^n[y_i\log\frac{h_w(x_i)}{1-h_w(x_i)}+log(1-h_w(x_i))] \\ & = \sum_{i=1}^n[y_i(w^Tx_i)-w^Tx_i-log(1+e^{-w^Tx_i})] \end{aligned}

$\frac{\partial L(w)}{\partial w}=\sum_{i=1}^n(y_i-h_w(x_i))x_i$

$w^{new}=w^{old}+\alpha x^T(y-h_w(x))$

\begin{aligned} & w_0^{new}=w_0^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^0 \\ & w_1^{new}=w_1^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^1 \\ & w_2^{new}=w_2^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^2 \\ & \vdots \\ & w_N^{new}=w_N^{old}+\alpha\sum_{i=1}^n(y_i-h_w(x_i))x_i^N \\ \end{aligned}

Logistic回归主要运用的是梯度上升算法，在上面案例实战的过程中，我们的随机生成的样本数据集不是特别的多，所以训练的速度还挺快。但是假定我们的训练样本数量比较多，这个时候的训练效率就比较的低下了。这个时候可能就需要对梯度上升算法进行一定的优化了，而该部分优化常用的方式是使用随机梯度上升算法，限于篇幅和时间原因，我们后面有机会再来肝。

[1] 《机器学习实战》：Peter Harrington 人民邮电出版社
[2] 《统计学习方法》：李航 第二版 清华大学出版社

posted @ 2020-12-07 17:05  玩世不恭的Coder  阅读(357)  评论(0编辑  收藏  举报