如果我们根据身高、体重、年龄等来预测是否发生某种疾病,结果只有两种,1发生了,0没发生。这样就构成了一个二分类问题,而我们在处理二分类问题时常常会使用sign函数(取正负号),y为-1或者+1。但是这样处理对0.5左右的样本不友好,我们希望用概率来量化这件事情。
所以需要调用sigmoid这个函数,将f(x)范围控制在(0,1)
s i g m o i d ( x ) = 1 1 + e − x sigmoid(x) = \frac {1} {1+e^{-x}} sigmoid(x)=1+e−x1
这样得到的sigmoid转换过后的值可以在某种程度上认为是位于+1或者-1的概率。
那么解决了概率问题过后,还需要考虑我们的机器怎么评估错误率,即损失函数该怎么写。
其实还是从最大似然来的。对于我们手上拿到的数据即理想中的函数f(x),如果我们的理论函数h(x)与f(x)接近,那么生成的数据集D也是相似的。
如果是二分类问题:
P ( y ∣ x ) = { f ( x ) if y = + 1 1 − f ( x ) if y = − 1 P(y|x) = \begin{cases} f(x) &\text{if } y = +1 \\ 1-f(x) &\text{if } y = -1 \end{cases} P(y∣x)={f(x)1−f(x)if y=+1if y=−1
那么对于数据集 D = { ( x 1 , + 1 ) , ( x 2 , − 1 ) , . . . , ( x n , + 1 ) } D=\{(x_1, +1), (x_2, -1), ..., (x_n, +1)\} D={(x1,+1),(x2,−1),...,(xn,+1)}
f(x)产生该数据的概率就为
P
(
x
1
)
∗
f
(
x
1
)
∗
P
(
x
2
)
∗
(
1
−
f
(
x
2
)
)
∗
.
.
.
∗
P
(
x
n
)
∗
f
(
x
n
)
P(x_1)*f(x_1)*P(x_2)*(1-f(x_2))*...*P(x_n)*f(x_n)
P(x1)∗f(x1)∗P(x2)∗(1−f(x2))∗...∗P(xn)∗f(xn)
那么h(x)产生该数据的概率就为
P
(
x
1
)
∗
h
(
x
1
)
∗
P
(
x
2
)
∗
(
1
−
h
(
x
2
)
)
∗
.
.
.
∗
P
(
x
n
)
∗
h
(
x
n
)
P(x_1)*h(x_1)*P(x_2)*(1-h(x_2))*...*P(x_n)*h(x_n)
P(x1)∗h(x1)∗P(x2)∗(1−h(x2))∗...∗P(xn)∗h(xn)
实际上f(x)产生D的概率往往很大,所以我们就需要使
P
(
x
1
)
∗
h
(
x
1
)
∗
P
(
x
2
)
∗
(
1
−
h
(
x
2
)
)
∗
.
.
.
∗
P
(
x
n
)
∗
h
(
x
n
)
P(x_1)*h(x_1)*P(x_2)*(1-h(x_2))*...*P(x_n)*h(x_n)
P(x1)∗h(x1)∗P(x2)∗(1−h(x2))∗...∗P(xn)∗h(xn)最大的h(x)
也就是让 ∏ i = 1 n h ( y n ∗ x n ) \displaystyle\prod_{i=1}^n h(y_n*x_n) i=1∏nh(yn∗xn) 最大
接下来再把w放进去,就是让 ∏ i = 1 n s i g m o i d ( y n ∗ w ∗ x n ) \displaystyle\prod_{i=1}^n sigmoid(y_n*w*x_n) i=1∏nsigmoid(yn∗w∗xn) 最大
为了让连乘变连加,需要加上 ln \ln ln,然后让最大变最小,加个负号。
− ∑ i = 1 n ln ( s i g m o i d ( y n ∗ w ∗ x n ) ) -\sum_{i=1}^n \ln(sigmoid(y_n*w*x_n)) −i=1∑nln(sigmoid(yn∗w∗xn))
那么就是让上式变为最小。而我们把sigmoid函数代入过后公式变化成以下形式:
ln
(
1
+
e
−
y
w
x
)
\ln(1+e^{-ywx})
ln(1+e−ywx)
而这个函数我们称之为交叉熵。那么我们的损失函数就是:
ln ( 1 + e − y w x ) n \frac {\ln(1+e^{-ywx})} {n} nln(1+e−ywx)
解决了损失函数的问题,我们还要解决一个非常重要的问题,就是怎么学习,怎么去更新我们的权重。
为了让损失函数最小(函数是一个开口向上的函数),我们首先考虑将损失函数微分,希望能找到梯度为0的w。
这部分稍稍复杂了一些,后面再单独写一篇文章介绍吧,需要用梯度下降法更新参数,可以用随机梯度,可以用固定梯度。
固定梯度如下:
w t + 1 ← w t − η ∗ ∇ l o s s ( w t ) ∥ ∇ l o s s ( w t ) ∥ w_{t+1} \gets w_t−\frac{η* \nabla loss(w_t)}{\lVert \nabla loss(w_t)\rVert} wt+1←wt−∥∇loss(wt)∥η∗∇loss(wt)
∇ l o s s ( w t ) ∥ ∇ l o s s ( w t ) ∥ \frac{ \nabla loss(w_t)}{\lVert \nabla loss(w_t)\rVert} ∥∇loss(wt)∥∇loss(wt)是梯度方向的单位向量,每次更新都在梯度方向的反方向上走η。
但是为了让梯度在开始时快一些,快到谷底时慢一些,就在每一步时乘上梯度的模长:
w
t
+
1
←
w
t
−
η
∗
∇
l
o
s
s
(
w
t
)
∥
∇
l
o
s
s
(
w
t
)
∥
∗
∥
∇
l
o
s
s
(
w
t
)
∥
w
t
+
1
←
w
t
−
η
∗
∇
l
o
s
s
(
w
t
)
\begin{aligned} w_{t+1} &\gets w_t−\frac{η* \nabla loss(w_t)}{\lVert \nabla loss(w_t)\rVert}*\lVert \nabla loss(w_t)\rVert \\ w_{t+1} &\gets w_t−η* \nabla loss(w_t) \end{aligned}
wt+1wt+1←wt−∥∇loss(wt)∥η∗∇loss(wt)∗∥∇loss(wt)∥←wt−η∗∇loss(wt)
梯度 ∇ l o s s ( w t ) \nabla loss(w_t) ∇loss(wt)就是损失函数在 w t w_t wt处的微分。
#!/usr/local/bin/python3
# -*- coding: UTF-8 -*-
import numpy as np
class LogisticRegression:
def __init__(self, learn_rate, step):
self.learn_rate = learn_rate
self.step = step
@staticmethod
def sigmoid(s):
return 1/(1+np.exp(-s))
@staticmethod
def loss_func(self, w, X, y):
loss = np.sum(np.log(1+np.exp(-X.dot(w)*y)))/(X.shape[0])
return loss
def get_gradient(self, w, X, y):
y = y.reshape(-1, 1)
_gradient = np.sum(self.sigmoid(-X.dot(w)*y)*(-y*X), axis=0)/(X.shape[0])
return _gradient.reshape((X.shape[1], 1))
def fit(self, X, y):
w = np.zeros((X.shape[1], 1))
for i in range(0, self.step):
gradient = self.get_gradient(w, X, y)
w = w - self.learn_rate*gradient
return w
def predict(self, X, y):
w = self.fit(X, y)
new_y = self.sigmoid(X.dot(w))
return new_y
if __name__ == "__main__":
def boxmullersampling(mu=0, sigma=1, size=1):
u = np.random.uniform(size=size)
v = np.random.uniform(size=size)
z = np.sqrt(-2 * np.log(u)) * np.cos(2 * np.pi * v)
return mu + z * sigma
x1 = boxmullersampling(1.5, 0.1, 100)
x1 = [[x, x+1, x*2] for x in list(x1)]
y1 = [-1 for x in range(0, 100)]
x2 = boxmullersampling(2, 0.2, 100)
x2 = [[x, x+1.5, x*3] for x in list(x2)]
y2 = [1 for x in range(0, 100)]
train_x = np.array(x1+x2)
train_y = np.array(y1+y2)
cls = LogisticRegression(1, 1000)
_tmp1 = cls.fit(train_x, train_y)
y_pred = cls.predict(train_x, train_y)
print(_tmp1)
print(y_pred, train_y)