一、逻辑回归前期介绍
1.1 逻辑回归是什么算法?
逻辑回归是一个分类算法,并不是一个回归算法。可以说逻辑回归前期用的是和回归算法一样,后期加入了一个激活函数,成为了分类算法。
1.2 Sigmoid函数
σ
(
t
)
=
1
1
+
e
−
t
\sigma(t)=\frac{1}{1+e^{-t}}
σ(t)=1+e−t1
图像:
这个函数有啥用?
你看,他的取值空间是负无穷到正无穷,但是值域是0到1。所以说逻辑回归是先线性回归,然后再用一个激活函数分成两类(值域0.5作分界线,也就是取值0最为分界线)。。假设有两个标签。标签1和标签0。线性回归求出来的值如果大于0就让他分成1这个标签下,小于0就让他分到0这个类别。
1.3 小结
我们有m个数据,每个数据n个特征,每条数据的y都属于两个特征的其中一个。我们要训练出一个模型,这个模型可以让新来的数据分类到其中一个标签下。阶段目的是要求出n+1个
θ
i
\theta_i
θi。结合激活函数sigmoid实现正确分类。
p
^
=
σ
(
θ
T
⋅
x
b
)
=
1
1
+
e
−
θ
T
⋅
x
b
\hat{p}=\sigma\left(\theta^{T} \cdot x_{b}\right)=\frac{1}{1+e^{-\theta^{T} \cdot x_{b}}}
p^=σ(θT⋅xb)=1+e−θT⋅xb1
由于
θ
T
⋅
x
b
\theta^{T} \cdot x_{b}
θT⋅xb的取值范围是负无穷到正无穷,所以
p
^
\hat{p}
p^的取值范围是0到1.
二、代价函数怎么求?
依据上述推导,我们可以得到目前的准则。
y
^
=
{
1
,
p
^
≥
0.5
0
,
p
^
≤
0.5
\hat{y}= \begin{cases}1, & \hat{p} \geq 0.5 \\ 0, & \hat{p} \leq 0.5\end{cases}
y^={1,0,p^≥0.5p^≤0.5
所以我们的代价函数标准应该是:
cost
=
{
如果
y
=
1
,
p
越小, cost越大
如果
y
=
0
,
p越大, cost越大
\text { cost }=\left\{\begin{array}{l} \text { 如果 } y=1, p \text { 越小, cost越大 } \\ \text { 如果 } y=0, \text { p越大, cost越大 } \end{array}\right.
cost ={ 如果 y=1,p 越小, cost越大 如果 y=0, p越大, cost越大
我们可以想象一个函数满足上诉准则:
cost
=
{
−
log
(
p
^
)
if
y
=
1
−
log
(
1
−
p
^
)
if
y
=
0
\text { cost }=\left\{\begin{array}{ccc} -\log (\hat{p}) & \text { if } & y=1 \\ -\log (1-\hat{p}) & \text { if } & y=0 \end{array}\right.
cost ={−log(p^)−log(1−p^) if if y=1y=0
图像:
说明:
如果一条数据,y值是1,我们求的
p
^
\hat{p}
p^接近1,表明我们求对了,此刻应该让cost接近0,上式满足,如我们求得
p
^
\hat{p}
p^接近0,表示我们求错了,此刻应该加大惩罚。上式满足。
如果一条数据,y值是0,我们求的
p
^
\hat{p}
p^接近0,表明我们求对了,此刻应该让cost接近0,上式满足,如我们求得
p
^
\hat{p}
p^接近1,表明我们求错了,此刻应该加大惩罚。上式满足。
综上所述,这个代价函数很好,满足所需。但是有一点,他是两个式子,不方便啊,还要if判断之类的,所以我们合二为一。得到最终的代价函数。(带入y化简即可!)
cost
=
−
y
log
(
p
^
)
−
(
1
−
y
)
log
(
1
−
p
^
)
\text { cost }=-y \log (\hat{p})-(1-y) \log (1-\hat{p})
cost =−ylog(p^)−(1−y)log(1−p^)
更具体的说:
J
(
θ
)
=
−
1
m
∑
i
=
1
m
y
(
i
)
log
(
p
^
(
i
)
)
+
(
1
−
y
(
i
)
)
log
(
1
−
p
^
(
i
)
)
J(\theta)=-\frac{1}{m} \sum_{i=1}^{m} y^{(i)} \log \left(\hat{p}^{(i)}\right)+\left(1-y^{(i)}\right) \log \left(1-\hat{p}^{(i)}\right)
J(θ)=−m1i=1∑my(i)log(p^(i))+(1−y(i))log(1−p^(i))
其中:
p
^
(
i
)
=
σ
(
X
(
i
)
θ
)
=
1
1
+
e
−
X
(
i
)
θ
\hat{p}^{(i)}=\sigma\left(X^{(i)} \theta\right)=\frac{1}{1+e^{-X^{(i)} \theta}}
p^(i)=σ(X(i)θ)=1+e−X(i)θ1
所以啊:
J
(
θ
)
=
−
1
m
∑
i
=
1
m
y
(
i
)
log
(
σ
(
X
b
(
i
)
θ
)
)
+
(
1
−
y
(
i
)
)
log
(
1
−
σ
(
X
b
(
i
)
θ
)
)
J(\theta)=-\frac{1}{m} \sum_{i=1}^{m} y^{(i)} \log \left(\sigma\left(X_{b}^{(i)} \theta\right)\right)+\left(1-y^{(i)}\right) \log \left(1-\sigma\left(X_{b}^{(i)} \theta\right)\right)
J(θ)=−m1i=1∑my(i)log(σ(Xb(i)θ))+(1−y(i))log(1−σ(Xb(i)θ))
所以啊:
J
(
θ
)
=
−
1
m
∑
i
=
1
m
y
(
i
)
log
(
1
1
+
e
−
X
(
i
)
θ
)
+
(
1
−
y
(
i
)
)
log
(
1
−
1
1
+
e
−
X
(
i
)
θ
)
J(\theta)=-\frac{1}{m} \sum_{i=1}^{m} y^{(i)} \log \left(\frac{1}{1+e^{-X^{(i)} \theta}}\right)+\left(1-y^{(i)}\right) \log \left(1-\frac{1}{1+e^{-X^{(i)} \theta}}\right)
J(θ)=−m1i=1∑my(i)log(1+e−X(i)θ1)+(1−y(i))log(1−1+e−X(i)θ1)
2.1最小化代价函数!
梯度下降法优化代价函数。
代价函数求梯度
J
(
θ
)
θ
j
=
1
m
∑
i
=
1
m
(
σ
(
X
b
(
i
)
θ
)
−
y
(
i
)
)
X
j
(
i
)
\frac{J(\theta)}{\theta_{j}}=\frac{1}{m} \sum_{i=1}^{m}\left(\sigma\left(X_{b}^{(i)} \theta\right)-y^{(i)}\right) X_{j}^{(i)}
θjJ(θ)=m1i=1∑m(σ(Xb(i)θ)−y(i))Xj(i)
推导都是高中知识,就是麻烦点,并不难!,最后的结果是不是很眼熟,他简直和线性回归的代价函数一模一样!!
三、代码实现逻辑回归!!
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
class LogisticRegression:
def __init__(self):
"""初始化Logistic Regression模型"""
self.coef_ = None
self.intercept_ = None
self._theta = None
def _sigmoid(self, t):
return 1. / (1. + np.exp(-t))
def fit(self, X_train, y_train, eta=0.01, n_iters=1e4):
"""根据训练数据集X_train, y_train, 使用梯度下降法训练Logistic Regression模型"""
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
def J(theta, X_b, y):
y_hat = self._sigmoid(X_b.dot(theta))
try:
return - np.sum(y * np.log(y_hat) + (1 - y) * np.log(1 - y_hat)) / len(y)
except:
return float('inf')
def dJ(theta, X_b, y):
return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(y)
def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):
theta = initial_theta
cur_iter = 0
while cur_iter < n_iters:
gradient = dJ(theta, X_b, y)
last_theta = theta
theta = theta - eta * gradient
if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
break
cur_iter += 1
return theta
X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
initial_theta = np.zeros(X_b.shape[1])
self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)
self.intercept_ = self._theta[0]
self.coef_ = self._theta[1:]
return self
def predict_proba(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果概率向量"""
assert self.intercept_ is not None and self.coef_ is not None, \
"must fit before predict!"
assert X_predict.shape[1] == len(self.coef_), \
"the feature number of X_predict must be equal to X_train"
X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
return self._sigmoid(X_b.dot(self._theta))
def predict(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
assert self.intercept_ is not None and self.coef_ is not None, \
"must fit before predict!"
assert X_predict.shape[1] == len(self.coef_), \
"the feature number of X_predict must be equal to X_train"
proba = self.predict_proba(X_predict)
return np.array(proba >= 0.5, dtype='int')
def score(self, X_test, y_test):
"""根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""
y_predict = self.predict(X_test)
return accuracy_score(y_test, y_predict)
def __repr__(self):
return "LogisticRegression()"
iris = datasets.load_iris()
X = iris.data
y = iris.target
X = X[y < 2, :2]
y = y[y < 2]
plt.scatter(X[y == 0, 0], X[y == 0, 1], color="red")
plt.scatter(X[y == 1, 0], X[y == 1, 1], color="blue")
plt.show()
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)
print('分数', log_reg.score(X_test, y_test))
print(log_reg.predict(X_test))
print(y_test)