写在前面
本博客是作者在学习机器学习基础时写下的总结,学习的资源为网易云课堂上吴恩达的机器学习课程,参考资料主要为stanford cs229课程的英文讲义,有兴趣的读者可以去网上下载原版的英文讲义来看。此外,由于本人为初学者,对知识点理解有限,因此文中有任何错误非常欢迎大家指出。最后,本文涉及的所有代码的完整版均会上传到github,欢迎大家交流。
1. 逻辑回归模型
逻辑回归实际上对应监督学习中的分类问题,它与线性回归不同的是,逻辑回归的输出值为有限个离散的值。在二元逻辑回归中,输出
y
y
y的取值为0和1,其中称作0为负类,而1为正类。对于给定的输入
x
(
i
)
x^{(i)}
x(i),输出响应
y
(
i
)
y^{(i)}
y(i)也被称为训练样本的标记。下面给出假设函数:
h
θ
(
x
)
=
g
(
θ
T
x
)
=
1
1
+
e
−
θ
T
x
\displaystyle h_\theta(x) = g(\theta^Tx) = \displaystyle \frac{1}{1+e^{-\displaystyle \theta^Tx}}
hθ(x)=g(θTx)=1+e−θTx1
其中,
g
(
z
)
=
1
1
+
e
−
z
g(z) = \frac{1}{1+e^{-z}}
g(z)=1+e−z1
称为逻辑函数或者sigmoid函数。
它的图像如下:
可以看出,sigmoid函数将
(
−
∞
,
+
∞
)
(-\infty, +\infty)
(−∞,+∞)变换到
(
0
,
1
)
(0, 1)
(0,1)范围内,并且当
z
<
0
z\lt0
z<0时,
g
(
z
)
<
0.5
g(z)\lt0.5
g(z)<0.5;当
z
>
0
z>0
z>0时,
g
(
z
)
>
0.5
g(z)>0.5
g(z)>0.5。另外,从单调性来看,sigmoid函数不改变z的单调性。
Sigmoid函数还有一个重要的性质如下:
g
′
(
z
)
=
d
d
z
1
1
+
e
−
z
=
1
(
1
+
e
−
z
)
2
(
e
−
z
)
=
g
(
z
)
(
1
−
g
(
z
)
)
\begin{aligned} g'(z) &= \frac{d}{dz}\frac{1}{1+e^{-z}} \\ &=\frac{1}{(1+e^{-z})^2}(e^{-z}) \\ & = g(z)(1-g(z)) \end{aligned}
g′(z)=dzd1+e−z1=(1+e−z)21(e−z)=g(z)(1−g(z))
2. 成本函数
考虑线性回归时所用的成本函数:
J
(
θ
)
=
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
2
J(\theta) = \frac{1}{2m}\sum_{i = 1}^m(h_\theta(x^{(i)})-y^{(i)})^2
J(θ)=2m1i=1∑m(hθ(x(i))−y(i))2
由于sigmoid函数的非线性,这里的
J
(
θ
)
J(\theta)
J(θ)实际上是一个非凸的函数。也就是说,
J
(
θ
)
J(\theta)
J(θ)存在不唯一的局部最优解,当我们利用梯度下降来优化
J
(
θ
)
‘
J(\theta)‘
J(θ)‘时,往往求解到的不是全局最优解,而是局部最优解。
下面直接给出逻辑回归的成本函数,该函数可通过极大似然估计求得:
C
o
s
t
(
h
θ
(
x
)
,
y
)
=
{
−
ln
(
h
θ
(
x
)
)
,
y
=
1
−
ln
(
1
−
h
θ
(
x
)
)
,
y
=
0
Cost(h_\theta(x), y) = \begin{cases} -\ln(h_\theta(x))& ,y = 1 \\-\ln(1-h_\theta(x))&, y = 0 \end{cases}
Cost(hθ(x),y)={−ln(hθ(x))−ln(1−hθ(x)),y=1,y=0
该成本函数的解释如下,当
y
=
1
y = 1
y=1时,
C
o
s
t
(
h
θ
(
x
)
,
y
)
Cost(h_\theta(x), y)
Cost(hθ(x),y)随
h
θ
(
x
)
)
h_\theta(x))
hθ(x))变化的图像如图所示:
由于
y
=
1
y=1
y=1,因此当
h
θ
(
x
)
)
→
1
h_\theta(x))\rightarrow1
hθ(x))→1时,
C
o
s
t
→
0
Cost \rightarrow0
Cost→0;而当
h
θ
(
x
)
)
→
0
h_\theta(x))\rightarrow0
hθ(x))→0时,
C
o
s
t
→
+
∞
Cost \rightarrow+\infty
Cost→+∞。
同样的,当
y
=
0
y = 0
y=0时,有
由于
y
=
0
y=0
y=0,因此当
h
θ
(
x
)
)
→
0
h_\theta(x))\rightarrow0
hθ(x))→0时,
C
o
s
t
→
0
Cost \rightarrow0
Cost→0;而当
h
θ
(
x
)
)
→
1
h_\theta(x))\rightarrow1
hθ(x))→1时,
C
o
s
t
→
+
∞
Cost \rightarrow+\infty
Cost→+∞。
将上述成本函数写成更加紧凑的方式:
C
o
s
t
(
h
θ
(
x
)
,
y
)
=
−
y
ln
(
h
θ
(
x
)
)
−
(
1
−
y
)
ln
(
1
−
h
θ
(
x
)
)
Cost(h_\theta(x), y) = -y\ln(h\theta(x))-(1-y)\ln(1-h\theta(x))
Cost(hθ(x),y)=−yln(hθ(x))−(1−y)ln(1−hθ(x))
因此对于m个样本,成本函数
J
(
θ
)
J(\theta)
J(θ)为:
J
(
θ
)
=
1
m
∑
i
=
1
m
C
o
s
t
(
h
θ
(
x
(
i
)
,
y
(
i
)
)
=
−
1
m
[
∑
i
=
1
m
y
(
i
)
ln
h
θ
(
x
(
i
)
)
+
(
1
−
y
(
i
)
)
ln
(
1
−
h
θ
(
x
(
i
)
)
)
]
\begin{aligned} J(\theta) & = \frac{1}{m}\sum_{i=1}^mCost(h_\theta(x^{(i)}, y^{(i)}) \\& = -\frac{1}{m}[\sum_{i=1}^my^{(i)}\ln h_\theta(x^{(i)})+(1-y^{(i)})\ln(1-h_\theta(x{(i)}))] \end{aligned}
J(θ)=m1i=1∑mCost(hθ(x(i),y(i))=−m1[i=1∑my(i)lnhθ(x(i))+(1−y(i))ln(1−hθ(x(i)))]
可以证明,该成本函数和对数极大似然函数的结果一致。
3. 梯度下降
为了最小化成本函数
J
(
θ
)
J(\theta)
J(θ),下面对参数
θ
\theta
θ求偏导可得:
∂
∂
θ
j
J
(
θ
)
=
1
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
j
(
i
)
\frac{\partial}{\partial \theta_j}J(\theta) = \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_j^{(i)}
∂θj∂J(θ)=m1i=1∑m(hθ(x(i))−y(i))xj(i)
因此,梯度下降的过程可以表示为:
\qquad
Repeat until convergence{
θ
j
:
=
θ
j
−
α
1
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
j
(
i
)
\displaystyle \qquad\qquad \theta_j := \theta_j - \alpha \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x^{(i)}_j
θj:=θj−αm1i=1∑m(hθ(x(i))−y(i))xj(i)
\qquad
}
j
=
1
,
2
,
.
.
.
j = 1, 2, ...
j=1,2,...
从表面来看,逻辑回归的梯度下降过程与线性回归一模一样,但实际上这里的 h θ ( x ) = 1 1 + e − θ T x h_\theta(x)=\displaystyle \frac{1}{1+e^{-\theta^Tx}} hθ(x)=1+e−θTx1,而线性回归中的假设函数为 h θ ( x ) = θ T x h_\theta(x)=\theta^Tx hθ(x)=θTx。
4. 逻辑回归实例分析
考虑下面一个二元分类问题,数据集可见GitHub:
平面上有若干个点,它们分别属于两个类别,先用逻辑回归进行分类,于是
x
T
=
[
1
x
1
(
0
)
x
2
(
0
)
1
x
1
(
1
)
x
2
(
0
)
.
.
.
.
.
.
.
.
.
1
x
1
(
m
−
1
)
x
2
(
m
−
1
)
]
,
θ
=
[
θ
0
θ
1
θ
2
]
x^T = \left[ \begin{matrix} 1&x_1^{(0)}&x_2^{(0)} \\1&x_1^{(1)}&x_2^{(0)} \\ ...& ...&... \\1&x_{1}^{(m-1)}&x_{2}^{(m-1)} \end{matrix} \right], \theta = \left[ \begin{matrix} \theta_0 \\\theta_1 \\\theta_2 \end{matrix} \right]
xT=⎣⎢⎢⎢⎡11...1x1(0)x1(1)...x1(m−1)x2(0)x2(0)...x2(m−1)⎦⎥⎥⎥⎤,θ=⎣⎡θ0θ1θ2⎦⎤
成本函数偏导数
{
∂
∂
θ
0
J
(
θ
)
=
1
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
0
(
i
)
∂
∂
θ
1
J
(
θ
)
=
1
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
1
(
i
)
∂
∂
θ
2
J
(
θ
)
=
1
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
2
(
i
)
\begin{cases} \displaystyle \frac{\partial}{\partial \theta_0}J(\theta) = \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_0^{(i)} \\\displaystyle \frac{\partial}{\partial \theta_1}J(\theta) = \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_1^{(i)} \\\displaystyle \frac{\partial}{\partial \theta_2}J(\theta) = \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_2^{(i)} \end{cases}
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧∂θ0∂J(θ)=m1i=1∑m(hθ(x(i))−y(i))x0(i)∂θ1∂J(θ)=m1i=1∑m(hθ(x(i))−y(i))x1(i)∂θ2∂J(θ)=m1i=1∑m(hθ(x(i))−y(i))x2(i)
值得提及的是,上述所有公式均可表示为矩阵运算,这样可以有更高的效率,代码如下:
import matplotlib.pyplot as plt
import numpy as np
class logisRegression(object):
def __init__(self, theta, alpha):
self.theta = theta
self.alpha = alpha
self.cos = 0
def sigmoid(self, x):
z = 1/(1+np.exp(-x))
return z
def cost(self, trainX, trainY):
self.cos = (1/m)*(self.sigmoid(trainX.dot(self.theta))-trainY).T.dot(trainX).T
def update(self, trainX, trainY):
self.cost(trainX, trainY)
self.theta = self.theta - self.alpha * self.cos
def plotFig(lr):
plt.plot(data[:15, 0], data[:15, 1], 'x')
plt.plot(data[15:, 0], data[15:, 1], 'x')
plt.xlim([-3, 5])
plt.ylim([-1, 5])
a = np.linspace(-3, 5, 9)
b = np.linspace(-1, 5, 7)
x = np.meshgrid(a, b)
z = 1/(1+np.exp(-(x[0]*lr.theta[1]+x[1]*lr.theta[2]+lr.theta[0])))
plt.contour(x[0], x[1], z, [0.5], width=0.5, alpha=0.4)
plt.show()
if __name__ == '__main__':
data = np.loadtxt(open("1.txt", "rb"))
m = data.shape[0]
n = data.shape[1] + 1 - 1
trainX = np.ones(data.shape)
trainX[:, 1:] = data[:, :-1]
trainY = data[:, -1].reshape(data.shape[0], 1)
theta = np.zeros((n, 1))
LR = logisRegression(theta, 0.03)
for i in range(30000):
LR.update(trainX, trainY)
print(LR.theta)
plotFig(LR)
程序运行的效果如下:
从上图可以看出,逻辑回归将两类点分离开来,因此可以看出效果还是不错的。