逻辑回归
# 引入库文件方便后面用
import numpy as np
import matplotlib.pyplot as plt
1 线性回归
线性回归的模型是
h
(
w
)
=
w
0
+
w
1
x
1
+
w
2
x
2
+
⋯
+
w
n
x
n
=
w
T
x
h(w) = w^0 + w^1 x^1+w^2 x^2+\cdots+w^n x^n = \mathbf{w^Tx}
h(w)=w0+w1x1+w2x2+⋯+wnxn=wTx
线性回归任务是找到一个超平面,使得该平面尽量能够代表给出的数据整体。其通常求
θ
\theta
θ和
b
b
b的代价函数是
J
(
w
)
=
∑
i
1
2
(
h
w
(
x
i
)
−
y
i
)
2
J(w)=\sum_{i}\frac{1}{2}(h_w(x^i)-y^i)^2
J(w)=i∑21(hw(xi)−yi)2
运用梯度下降方法更新
w
:
=
w
−
α
∂
∂
w
J
(
w
)
w:=w - \alpha \frac{\partial}{\partial w}J(w)
w:=w−α∂w∂J(w)
只考虑第i个样本,对
w
w
w求导推得
w
:
=
w
−
α
(
y
i
−
h
w
(
x
i
)
)
x
i
w:=w-\alpha(y^i-h_w(x^i))x^i
w:=w−α(yi−hw(xi))xi
则批梯度下降(Batch Gradient Descent)可以描述成下式
Repeat until convergence{
w
:
=
α
∑
i
=
1
m
(
y
i
−
h
w
(
x
i
)
)
x
i
w:=\alpha\sum_{i=1}^m(y^i-h_w(x^i))x^i
w:=α∑i=1m(yi−hw(xi))xi (for every i)
}
通常批梯度下降当训练数据过大时,计算慢且耗内存,通常实际训练时用随机梯度下降 (Stochastic Gradient Descent)
Loop{
for i=1 to m,{
w
:
=
w
+
α
(
y
i
−
h
w
(
x
i
)
)
x
i
w:=w + \alpha(y^i-h_w(x^i))x^i
w:=w+α(yi−hw(xi))xi (for every i)
}
}
2 逻辑回归
2.1 基本推导
线性回归是拟合问题,感知机是二分类问题,逻辑回归是二分类问题。但是逻辑回归能够通过sigmoid函数映射成某一类概率值。
# 画sigmoid
x = np.linspace(-10,10,100)
y = 1/(1 + np.exp(-x))
plt.plot(x,y)
plt.show()
逻辑回归模型为
y
=
σ
(
w
T
x
+
b
)
,
w
h
e
r
e
σ
(
z
)
=
1
1
+
e
(
−
z
)
y=\sigma(w^Tx+b),\ where\ \sigma(z)=\frac {1} {1 + e^{(-z)}}
y=σ(wTx+b), where σ(z)=1+e(−z)1
假设sigmoid函数
σ
(
z
)
\sigma(z)
σ(z)表示属于1类的概率,定义
p
(
y
=
1
∣
x
;
w
)
=
σ
(
w
T
x
+
b
)
=
σ
(
z
)
p
(
y
=
0
∣
x
;
w
)
=
1
−
σ
(
z
)
p(y=1|x;w)=\sigma(w^Tx+b)=\sigma(z) \\ p(y=0|x;w)=1-\sigma(z)
p(y=1∣x;w)=σ(wTx+b)=σ(z)p(y=0∣x;w)=1−σ(z)
用
y
^
\hat{y}
y^表示我们通过模型计算的值,即
y
^
=
σ
(
x
)
=
1
1
+
e
−
(
w
x
+
b
)
\hat{y}=\sigma(x) = \frac{1}{1+e^{-(wx+b)}}
y^=σ(x)=1+e−(wx+b)1 ,上述二式可以统一写成
p
(
y
∣
x
)
=
y
^
y
(
1
−
y
^
)
(
1
−
y
)
p(y|x)=\hat{y}^y(1-\hat{y})^{(1-y)}
p(y∣x)=y^y(1−y^)(1−y)
上式将分类为0和分类和1的概率计算公式合二为一。假设分类器分类足够准确,此时对于一个样本,如果它是属于1类,分类器求出的属于1类的概率应该尽可能大,即
p
(
y
=
1
∣
x
)
p(y=1 | x)
p(y=1∣x)尽可能接近1;如果它是0类,分类器求出的属于0类的概率应该尽可能大,即
p
(
y
=
0
∣
x
)
p(y=0 | x)
p(y=0∣x)尽可能接近1。
通过上述公式对二类分类的情况分析,可知我们的目的是求取参数w和b,使得
p
(
y
∣
x
)
p(y|x)
p(y∣x)对0类和1类的分类结果尽可能取最大值,然而实际上我们定义的代价函数的是求最小值,于是,很自然的,我们想到对
p
(
y
∣
x
)
p(y|x)
p(y∣x)式子加一个负号,就变成了求最小值的问题,这就得到了逻辑回归中的代价函数。为了方便计算两边取对数得
L
(
y
^
−
y
)
=
−
l
o
g
p
(
y
∣
x
)
=
−
y
l
o
g
y
^
−
(
1
−
y
)
l
o
g
(
1
−
y
^
)
L(\hat{y}-y)=-log p(y|x) = -y log\hat{y} - (1-y)log(1-\hat{y})
L(y^−y)=−logp(y∣x)=−ylogy^−(1−y)log(1−y^)
对m个样本,则代价函数
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
y
^
i
−
y
i
)
J(w,b)=\frac {1}{m}\sum_{i=1}^m L(\hat{y}^i-y^i)
J(w,b)=m1i=1∑mL(y^i−yi)
上式对
w
w
w求偏导得
∂
J
∂
w
=
1
m
∑
i
=
1
m
(
y
^
i
−
y
i
)
x
i
\frac {\partial J}{\partial w} = \frac{1}{m}\sum_{i=1}^m(\hat{y}^i - y^i)x^i
∂w∂J=m1i=1∑m(y^i−yi)xi
上式对
b
b
b求偏导得
∂
J
∂
b
=
1
m
∑
i
=
1
m
(
y
^
i
−
y
i
)
\frac {\partial J}{\partial b} = \frac{1}{m}\sum_{i=1}^m(\hat{y}^i - y^i)
∂b∂J=m1i=1∑m(y^i−yi)
如果只考虑一个样本的情况,运用链式法则,得
∂
J
∂
w
=
(
σ
(
x
)
−
y
)
x
\frac {\partial J}{\partial w} = (\sigma(x) - y)x
∂w∂J=(σ(x)−y)x
∂
J
∂
b
=
σ
(
x
)
−
y
\frac {\partial J}{\partial b} = \sigma(x) - y
∂b∂J=σ(x)−y
所以迭代更新规则为
w
:
=
w
−
α
(
σ
(
x
)
−
y
)
x
w := w - \alpha(\sigma(x) - y)x
w:=w−α(σ(x)−y)x
b
:
=
b
−
α
(
σ
(
x
)
−
y
)
b := b - \alpha(\sigma(x) - y)
b:=b−α(σ(x)−y)
至此,我们的推导就都完成了。另一种求w,b的方法是运用概率论中的极大似然估计,这里就不介绍了。下面我们用Python代码来实现一个简单的二维逻辑回归。
2.2 逻辑回归python实现
'''手动创造点数据'''
c1 = np.random.normal(3, 2, (2,100))
c1 = np.r_[c1, np.zeros((1, 100))] # 标签
c2 = np.random.normal(-3,2, (2,100))
c2 = np.r_[c2, np.ones((1, 100))] # 标签
# 将两类数据拼在一起并打乱样本顺序作为训练数据集
trainset = np.c_[c1,c2]
index = np.arange(200)
np.random.shuffle(index)
trainset = trainset[:,index]
'''初始化工作'''
w = np.zeros((2, 1)) # 初始化权值
b = 0 # 初始化
X = trainset[0:2,:].reshape(2,200) # 训练数据
Y = trainset[2,:].reshape(1,200) # 训练标签
m=200 # 样本数
alpha = 0.1 # 学习率
'''训练'''
for iter in range(100):
# 前向计算
Z = np.dot(w.T,X) + b
A = 1/(1 + np.exp(-Z))
# 反向传播
dz = A - Y
dw = (1/m)*np.dot(X,dz.T)
db = (1/m)*np.sum(dz)
# 更新
w = w - alpha*dw
b = b - alpha*db
'''画图'''
# 画原始图
p1 = plt.scatter(c1[0,:], c1[1,:], c='r', marker='o', s=15)
p2 = plt.scatter(c2[0,:], c2[1,:], c='b', marker='x', s=15)
# 画出直线
x1 = np.linspace(-10,10,100)
x2 = -(w[0]*x1 + b)/w[1]
plt.plot(x1, x2, c='g')
plt.legend([p1, p2], ["class 0", "class 1"], loc='upper left')
plt.title("Logistic Regression", fontsize=24)
plt.xlabel("dimension 1", fontsize=14)
plt.ylabel("dimension 2", fontsize=14)
plt.show()