逻辑回归与线性回归的区别
线性回归适用于不同类别的训练样例数目相当。如果不同类别的训练样例数目稍有差别,通常影响不大,但若差别很大,则会对学习过程造成困扰。
类别不平衡就是指分类任务中不同类别的训练样例数目差别很大的情况。
例如:
图显示了是否购买玩具和年龄之间的关系,可以用线性回归拟合成一条直线,将购买标注为1,不购买标注为0,拟合后取当0.5值为阈值来划分类别。
可以看到,在途中,年龄的区分点约为19岁。但当数据点不平衡时,很容易影响到阈值,见以下图:
可以看到,0值样本的年龄段往高年龄端偏移后,真实的阈值依然是19岁左右,但拟合出来的曲线的阈值往后边偏移了。可以想想,负样本越多,年龄大的人越多,偏移越严重。可是这符合实际情况吗?实际情况是60岁的老人和80岁的老人都不会购买玩具,增加几位80岁的老人,并不会影响可以看到,0值样本的年龄段往高年龄端偏移后,真实的阈值依然是19岁左右,但拟合出来的曲线的阈值往后边偏移了。可以想想,负样本越多,年龄大的人越多,偏移越严重。可是这符合实际情况吗?实际情况是60岁的老人和80岁的老人都不会购买玩具,增加几位80岁的老人,并不会影响20岁以下人群购买玩具的概率。但因为拟合曲线原本的值域为(−∞ ∞)(−∞ ∞)而转换后的值域为[0,1],阈值对变量偏移很敏感。20岁以下人群购买玩具的概率。但因为拟合曲线原本的值域为(−∞ ∞)(−∞ ∞)而转换后的值域为[0,1],阈值对变量偏移很敏感。
逻辑回归的原理
常用的替代函数是对数几率函数,它是一种“Sigmoid”函数,它将z值转化为一个接近0或1的y值,并且其输出值在z=0附近变化很陡。
我们可以看到,当z大于0时,函数大于0.5;当函数等于0时,函数等于0.5;函数小于0时,函数小于0.5。如果用函数表示目标分到某一类的概率,我们可以采用以下“单位阶跃函数”来判断数据的类别:
若Z大于0,则判断为正例;若小于0,判断为反例;若等于0,可任意判别。由于Sigmoid函数单调且可导,函数在(0,1)之间程Z字型,可以很好的模拟二分类情况。
正则化与模型评估指标
正则化
可以在损失函数后面,加上正则化函数,即𝜃θ的惩罚项,来抑制过拟合问题。
L1正则:
J
(
θ
)
=
1
m
∑
i
=
1
m
y
(
i
)
h
θ
(
x
(
i
)
)
+
(
1
−
y
(
i
)
)
(
1
−
h
θ
(
x
(
i
)
)
)
+
λ
m
∑
i
=
1
m
∣
θ
i
∣
J(\theta) =\frac{1}{m}\sum^{m}_{i=1} y^{(i)}h_\theta (x^{(i)}) + (1-y^{(i)})(1-h_\theta (x^{(i)})) + \frac{\lambda}{m }\sum^m_{i=1}|\theta_i|
J(θ)=m1i=1∑my(i)hθ(x(i))+(1−y(i))(1−hθ(x(i)))+mλi=1∑m∣θi∣
Δ
θ
i
l
(
θ
)
=
1
m
∑
i
=
1
m
(
y
(
i
)
−
h
θ
(
x
(
i
)
)
)
x
(
i
)
+
λ
m
s
g
n
(
θ
i
)
\Delta_{\theta_i} l(\theta) = \frac{1}{m}\sum^m_{i=1}(y^{(i)} - h_\theta (x^{(i)}))x^{(i)} + \frac{\lambda}{m}sgn(\theta_i)
Δθil(θ)=m1i=1∑m(y(i)−hθ(x(i)))x(i)+mλsgn(θi)
梯度下降法的迭代函数变为,
θ
:
=
θ
−
K
′
(
θ
)
−
λ
m
s
g
n
(
θ
)
\theta:=\theta-K'(\theta)-\frac{\lambda}{m}sgn(\theta)
θ:=θ−K′(θ)−mλsgn(θ)
K(θ)为原来的损失函数,由于最后一项的符号由𝜃决定,可以看到,当𝜃大于零时,更新后的𝜃变小;当𝜃θ小于零时,更新后的𝜃变大。因此,L1正则化调整后的结果会更加稀疏(结果向量中有更多的0值)。(见图示,相当于在等高线上找令菱形最小的点。)
L2正则
J
(
θ
)
=
1
m
∑
i
=
1
m
y
(
i
)
h
θ
(
x
(
i
)
)
+
(
1
−
y
(
i
)
)
(
1
−
h
θ
(
x
(
i
)
)
)
+
λ
2
m
∑
i
=
1
m
θ
i
2
J(\theta) =\frac{1}{m}\sum^{m}_{i=1} y^{(i)}h_\theta (x^{(i)}) + (1-y^{(i)})(1-h_\theta (x^{(i)})) + \frac{\lambda}{2m}\sum^m_{i=1}\theta_i^2
J(θ)=m1i=1∑my(i)hθ(x(i))+(1−y(i))(1−hθ(x(i)))+2mλi=1∑mθi2
Δ
θ
i
l
(
θ
)
=
1
m
∑
i
=
1
m
(
y
(
i
)
−
h
θ
(
x
(
i
)
)
)
x
(
i
)
+
λ
m
θ
i
\Delta_{\theta_i} l(\theta) = \frac{1}{m}\sum^m_{i=1}(y^{(i)} - h_\theta (x^{(i)}))x^{(i)} + \frac{\lambda}{m}\theta_i
Δθil(θ)=m1i=1∑m(y(i)−hθ(x(i)))x(i)+mλθi
梯度下降法的迭代函数变为,
θ
:
=
θ
−
K
′
(
θ
)
−
2
λ
m
θ
\theta:=\theta-K'(\theta)-\frac{2\lambda}{m}\theta
θ:=θ−K′(θ)−m2λθ
𝐾(𝜃)为原来的损失函数,最有一项的𝜆决定了对参数的惩罚力度,惩罚力度越大,最后的结果向量的参数普遍较小且分散,避免了个别参数对整个函数起较大的影响。(见图示,相当于在等高线上找令圆形最小的点)
python实现
use skearn
import pandas as pd
import matplotlib.pyplot as plt
df_X = pd.read_csv('./logistic_x.txt', sep='\ +', header=None, engine='python') # 读取X值
ys = pd.read_csv('./logistic_y.txt', sep='\ +', header=None, engine='python') # 读取y值
ys = ys.astype(int) # 转换ys的数据类型为整型
df_X['label'] = ys[0].values # 将X按照y值的结果一一打标签
ax = plt.axes()
# 在二维图中描绘X点所处位置,直观查看数据点的分布情况
df_X.query('label == 0').plot.scatter(x=0, y=1, ax=ax, color='blue')
df_X.query('label == 1').plot.scatter(x=0, y=1, ax=ax, color='red')
plt.show()
from __future__ import print_function
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LogisticRegression
df_X = pd.read_csv('./logistic_x.txt', sep='\ +',header=None, engine='python') # 读取X值
ys = pd.read_csv('./logistic_y.txt', sep='\ +',header=None, engine='python') # 读取y值
ys = ys.astype(int)
df_X['label'] = ys[0].values # 将X按照y值的结果一一打标签
# 提取用于学习的数据
Xs = df_X[[0, 1]].values
Xs = np.hstack([np.ones((Xs.shape[0], 1)), Xs])
"""
【拼接数组的方法】
np.hstack——Stack arrays in sequence horizontally (column wise).
np.vstack——Stack arrays in sequence vertically (row wise).
https://docs.scipy.org/doc/numpy/reference/generated/numpy.hstack.html
"""
ys = df_X['label'].values
lr = LogisticRegression(fit_intercept=False) # 因为前面已经将截距项的值合并到变量中,此处参数设置不需要截距项
lr.fit(Xs, ys) # 拟合
score = lr.score(Xs, ys) # 结果评价
print("Coefficient: %s" % lr.coef_)
print("Score: %s" % score)
ax = plt.axes()
df_X.query('label == 0').plot.scatter(x=0, y=1, ax=ax, color='blue')
df_X.query('label == 1').plot.scatter(x=0, y=1, ax=ax, color='red')
_xs = np.array([np.min(Xs[:,]), np.max(Xs[:, 1])])
# 将数据以二维图形式描点,并用学习得出的参数结果作为阈值,划分数据区域
_ys = (lr.coef_[0][0] + lr.coef_[0][1] * _xs) / (- lr.coef_[0][2])
plt.plot(_xs, _ys, lw=1)
逻辑回归
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df_X = pd.read_csv('./logistic_x.txt', sep='\ +', header=None, engine='python') # 读取X值
ys = pd.read_csv('./logistic_y.txt', sep='\ +', header=None, engine='python') # 读取y值
ys = ys.astype(int) # 转换ys的数据类型为整型
df_X['label'] = ys[0].values # 将X按照y值的结果一一打标签
# 提取用于学习的数据
Xs = df_X[[0, 1]].values
Xs = np.hstack([np.ones((Xs.shape[0], 1)), Xs])
"""
【拼接数组的方法】
np.hstack——Stack arrays in sequence horizontally (column wise).
np.vstack——Stack arrays in sequence vertically (row wise).
https://docs.scipy.org/doc/numpy/reference/generated/numpy.hstack.html
"""
ys = df_X['label'].values
class LGR_GD():
def __init__(self):
self.w = None
self.n_iters = None
def fit(self, X, y, alpha=0.03, loss=1e-10): # 设定步长为0.02,判断是否收敛的条件为1e-10
y = y.reshape(-1, 1) # 重塑y值的维度以便矩阵运算
[m, d] = np.shape(X) # 自变量的维度
self.w = np.zeros((1, d)) # 将参数的初始值定为0
tol = 1e5
self.n_iters = 0
# ============================= show me your code =======================
while tol > loss: # 设置收敛条件
# 计算Sigmoid函数结果
sigmoid = 1 / (1 + np.exp(-X.dot(self.w.T)))
theta = self.w + alpha * np.mean(X * (y - sigmoid), axis=0) # 迭代theta的值
tol = np.sum(np.abs(theta - self.w)) # 计算损失值
self.w = theta
self.n_iters += 1 # 更新迭代次数
# ============================= show me your code =======================
def predict(self, X):
# 用已经拟合的参数值预测新自变量
y_pred = X.dot(self.w)
return y_pred
if __name__ == "__main__":
lr_gd = LGR_GD()
lr_gd.fit(Xs, ys)
ax = plt.axes()
df_X.query('label == 0').plot.scatter(x=0, y=1, ax=ax, color='blue')
df_X.query('label == 1').plot.scatter(x=0, y=1, ax=ax, color='red')
_xs = np.array([np.min(Xs[:, 1]), np.max(Xs[:, 1])])
_ys = (lr_gd.w[0][0] + lr_gd.w[0][1] * _xs) / (- lr_gd.w[0][2])
plt.plot(_xs, _ys, lw=1)
plt.show()
牛顿法
class LGR_GD():
def __init__(self):
self.w = None
self.n_iters = None
def fit(self, X, y, alpha=0.03, loss=1e-10): # 设定步长为0.02,判断是否收敛的条件为1e-10
y = y.reshape(-1, 1) # 重塑y值的维度以便矩阵运算
[m, d] = np.shape(X) # 自变量的维度
self.w = np.zeros((1, d)) # 将参数的初始值定为0
tol = 1e5
self.n_iters = 0
# ============================= show me your code =======================
while tol > loss: # 设置收敛条件
# 计算Sigmoid函数结果
zs = X.dot(self.w.T)
h_f = 1 / (1 + np.exp(-zs))
theta = self.w + alpha * np.mean(X*(y - h_f), axis=0) # 计算迭代的参数值
# axis= 0 对**横轴操作**,在运算的过程中其运算的方向表现为**纵向运算**
tol = np.sum(np.abs(theta - self.w)) # tol本身是一个差值? 平均绝对误差
self.w = theta # 更新参数值
self.n_iters += 1 # 更新迭代次数
# ============================= show me your code =======================
def predict(self, X):
# 用已经拟合的参数值预测新自变量
y_pred = X.dot(self.w)
return y_pred
if __name__ == "__main__":
LGR_GD = LGR_GD()
LGR_GD.fit(Xs, ys)
ax = plt.axes()
df_X.query('label == 0').plot.scatter(x=0, y=1, ax=ax, color='blue')
df_X.query('label == 1').plot.scatter(x=0, y=1, ax=ax, color='red')
_xs = np.array([np.min(Xs[:, 1]), np.max(Xs[:, 1])])
_ys = (LGR_GD.w[0][0] + LGR_GD.w[0][1] * _xs) / (- LGR_GD.w[0][2])
plt.plot(_xs, _ys, lw=1)
plt.show()
print("梯度下降法结果参数:%s;梯度下降法迭代次数:%s" % (LGR_GD.w, LGR_GD.n_iters))