
注意,本文里的y_pred指的是y预测值
逻辑回归是用来做分类算法的,大家都熟悉线性回归,一般形式是y_pred=aX+b,y的取值范围是[-∞, +∞],有这么多取值,怎么进行分类呢?不用担心,伟大的数学家已经为我们找到了一个方法。
也就是把Y的结果带入一个非线性变换的Sigmoid函数中,即可得到(0,1)之间取值范围的数S,S可以把它看成是一个概率值,如果我们设置概率阈值为0.5,那么S大于0.5可以看成是正样本,小于0.5看成是负样本,就可以进行分类了。
再简短说下:
公式:
g
(
z
)
=
1
1
+
e
−
z
gleft( z right) =frac{1}{1+e^{-z}}
g(z)=1+e−z1
其中 z 是 wx + b,(其实就是线性模型中的预测值y,只不过这里习惯写成 z ),扩展开来 z = w0+w1x1…+wnxn
图像:
性质:
任何一个模型都应该有个损失函数,就是描述预测值与实际值的差值的函数。
先上损失函数(这里假设样本服从伯努利分布(0-1分布)):
l
o
s
s
=
−
(
Σ
y
i
log
y
^
i
+
(
1
−
y
i
)
log
(
1
−
y
^
i
)
)
其中
y
^
=
s
i
g
m
o
i
d
(
W
T
X
)
loss=-left( varSigma y_ilog hat{y}_i+left( 1-y_i right) log left( 1-hat{y}_i right) right) \ text{其中}hat{y}=sigmoidleft( W^TX right)
loss=−(Σyilogy^i+(1−yi)log(1−y^i))其中y^=sigmoid(WTX)
逻辑回归的损失函数别名1:负对数似然损失
假设样本服从伯努利分布(0-1分布):
P
(
Y
∣
X
)
=
{
y
^
,
y
=
1
1
−
y
^
,
y
=
0
}
合并成一个形式:
y
^
y
(
1
−
y
^
)
1
−
y
那么所有样本:
Π
i
y
^
i
y
i
(
1
−
y
^
i
)
1
−
y
i
目标函数:
max
Π
i
y
^
i
y
i
(
1
−
y
^
i
)
1
−
y
i
取
log
并加上负号变成取最小值(方便优化):
l
o
s
s
=
−
(
Σ
y
i
log
y
^
i
+
(
1
−
y
i
)
log
(
1
−
y
^
i
)
)
求解参数
w
∗
,使得
l
o
s
s
最小化:
w
∗
=
a
r
g
min
−
(
Σ
y
i
log
y
^
i
+
(
1
−
y
i
)
log
(
1
−
y
^
i
)
)
解释下相关符号:
y
表示实际值;
y
^
表示预测值;
w
∗
和
a
r
g
min
表示当参数
w
为
w
∗
时使得
l
o
s
s
最小,这只是一种符号化形式
Pleft( Y|X right) =left. left{ begin{array}{c} hat{y}, y=1\ 1-hat{y}, y=0\ end{array} right. right} \ text{合并成一个形式:}hat{y}^yleft( 1-hat{y} right) ^{1-y} \ text{那么所有样本:}underset{i}{varPi}{hat{y}_i}^{y_i}left( 1-hat{y}_i right) ^{1-y_i} \ text{目标函数:}max underset{i}{varPi}{hat{y}_i}^{y_i}left( 1-hat{y}_i right) ^{1-y_i} \ text{取}logtext{并加上负号变成取最小值(方便优化):}loss=-left( varSigma y_ilog hat{y}_i+left( 1-y_i right) log left( 1-hat{y}_i right) right) \ text{求解参数}w^*text{,使得}losstext{最小化:}w^*=argmin -left( varSigma y_ilog hat{y}_i+left( 1-y_i right) log left( 1-hat{y}_i right) right) \ text{解释下相关符号:}ytext{表示实际值;}hat{y}text{表示预测值;} \ w^*text{和}argmintext{表示当参数}wtext{为}w^*text{时使得}losstext{最小,这只是一种符号化形式}
P(Y∣X)={y^,y=11−y^,y=0}合并成一个形式:y^y(1−y^)1−y那么所有样本:iΠy^iyi(1−y^i)1−yi目标函数:maxiΠy^iyi(1−y^i)1−yi取log并加上负号变成取最小值(方便优化):loss=−(Σyilogy^i+(1−yi)log(1−y^i))求解参数w∗,使得loss最小化:w∗=argmin−(Σyilogy^i+(1−yi)log(1−y^i))解释下相关符号:y表示实际值;y^表示预测值;w∗和argmin表示当参数w为w∗时使得loss最小,这只是一种符号化形式
小结一下
逻辑回归的损失函数别名2:交叉熵损失
参考网上文章叙述
先引入一个概念:KL散度:衡量两个概率分布的差异
逻辑回归模型最后的计算结果(通过sigmoid或softmax函数)是各个分类的概率(可以看做是各个分类的概率分布)。那么假设真实的概率分布是,估计得到的概率分布是, 这两个概率分布的距离如何去衡量?
在信息论中,「相对熵」,也就是KL散度,可以衡量两个概率分布的差异性。具体公式为:
D
K
L
(
p
∣
∣
q
)
=
Σ
x
p
(
x
)
log
p
(
x
)
q
(
x
)
=
Σ
x
p
(
x
)
(
log
p
(
x
)
−
log
q
(
x
)
)
=
−
Σ
x
p
(
x
)
log
q
(
x
)
−
(
−
Σ
x
p
(
x
)
log
p
(
x
)
)
=
H
(
p
,
q
)
−
H
(
p
)
其中
H
(
p
,
q
)
=
−
Σ
x
p
(
x
)
log
q
(
x
)
就是交叉熵,
H
(
p
)
是真实概率分布的信息熵
所以:
K
L
散度
=
交叉熵
−
真实概率分布的信息熵
\ D_{KL}left( p||q right) =underset{x}{varSigma}pleft( x right) log frac{pleft( x right)}{qleft( x right)}=underset{x}{varSigma}pleft( x right) left( log pleft( x right) -log qleft( x right) right) \ =-underset{x}{varSigma}pleft( x right) log qleft( x right) -left( -underset{x}{varSigma}pleft( x right) log pleft( x right) right) \ =Hleft( p,q right) -Hleft( p right) \ text{其中}Hleft( p,q right) =-underset{x}{varSigma}pleft( x right) log qleft( x right) text{就是交叉熵,}Hleft( p right) text{是真实概率分布的信息熵} \ text{所以:}KLtext{散度}=text{交叉熵}-text{真实概率分布的信息熵}
DKL(p∣∣q)=xΣp(x)logq(x)p(x)=xΣp(x)(logp(x)−logq(x))=−xΣp(x)logq(x)−(−xΣp(x)logp(x))=H(p,q)−H(p)其中H(p,q)=−xΣp(x)logq(x)就是交叉熵,H(p)是真实概率分布的信息熵所以:KL散度=交叉熵−真实概率分布的信息熵
因为交叉熵越大,KL散度越大,也可以用交叉熵来衡量两个概率分布之间的距离,所以逻辑回归使用交叉熵作为逻辑回归的损失函数。
一个模型在一个特定样本分布上只有一个损失函数
为啥逻辑回归要用梯度下降?因为参数项太多了,无法直接求导得出最优解
先明确几个概念:
这里就是使用梯度下降来求解逻辑回归的损失函数的参数
梯度的定义:
g
r
a
d
i
e
n
t
(
w
)
=
∂
l
o
s
s
(
w
)
∂
w
w
t
+
1
=
w
t
−
α
.
g
r
a
d
i
e
n
t
(
w
)
符号解释:
w
t
和
w
t
+
1
表示进行梯度下降的前后两个值(点);
α
表示学习率或者叫步长;
g
r
a
d
i
e
n
t
(
w
)
在公式中就是梯度,也是学习方向或者梯度方向,就是你要往哪个方向进行学习更新参数
w
;
在式子里含义就是
w
t
进行
α
.
g
r
a
d
i
e
n
t
(
w
)
梯度下降值后的新位置
w
t
+
1
gradientleft( w right) =frac{partial lossleft( w right)}{partial w} \ w_{t+1}=w_t-alpha .gradientleft( w right) \ text{符号解释:}w_ttext{和}w_{t+1}text{表示进行梯度下降的前后两个值(点);} \ alpha text{表示学习率或者叫步长;} \ gradientleft( w right) text{在公式中就是梯度,也是学习方向或者梯度方向,就是你要往哪个方向进行学习更新参数}w; \ text{在式子里含义就是}w_ttext{进行}alpha .gradientleft( w right) text{梯度下降值后的新位置}w_{t+1}
gradient(w)=∂w∂loss(w)wt+1=wt−α.gradient(w)符号解释:wt和wt+1表示进行梯度下降的前后两个值(点);α表示学习率或者叫步长;gradient(w)在公式中就是梯度,也是学习方向或者梯度方向,就是你要往哪个方向进行学习更新参数w;在式子里含义就是wt进行α.gradient(w)梯度下降值后的新位置wt+1
图示梯度下降样例:如图,当我要往山底走时(求最小值);我的方向四面八方,参考的东西(特征)很多,比如树的稀疏程度、草的茂盛程度、动物出没情况等等;你不清楚啥情况的时候,不知道往哪个方向,那么梯度公式就告诉你当前这一步你尝试往gradient(w)这个方向走a步(阿尔法符号)看看(梯度–>方向),每走完一步又继续确认方向再往前试探的走,直到走到谷底(求到最小值)。
LR中梯度法的推导:
推导流程来源于网络,但是意思能表示清楚
最后,带入梯度下降公式:这也就是LR的w参数关系式:
w
t
+
1
=
w
t
−
α
(
Σ
i
(
f
(
w
,
x
)
−
y
i
)
x
)
w_{t+1}=w_t-alpha left( underset{i}{varSigma}left( fleft( w,x right) -y_i right) x right)
wt+1=wt−α(iΣ(f(w,x)−yi)x)
解释下相关符号: J ( w , x ) 等价与 l o s s ( w ) ,就是损失函数的另一种写法; y _ p r e d i 等价于我上述损失函数的 y ^ ,表示的是预测值 y ; y i 就是实际值 y ; s i g m o i d ( w T x ) = s i g m o i d ( w 0 + w 1 x 1 + . . . w n x n ) , 其中 w T x 是向量的表示方式 ; 推导式子里有些有下角标有些没有 虽不严谨,但不影响推导理解,没有角标的都表示的是向量形式 text{解释下相关符号:}Jleft( w,x right) text{等价与}lossleft( w right) text{,就是损失函数的另一种写法;} \ y_pred_itext{等价于我上述损失函数的}hat{y}text{,表示的是预测值}y; \ y_itext{就是实际值}y; \ sigmoidleft( w^Tx right) =sigmoidleft( w_0+w_1x_1+...w_nx_n right) ,text{其中}w^Txtext{是向量的表示方式}; \ text{推导式子里有些有下角标有些没有} \ text{虽不严谨,但不影响推导理解,没有角标的都表示的是向量形式} 解释下相关符号:J(w,x)等价与loss(w),就是损失函数的另一种写法;y_predi等价于我上述损失函数的y^,表示的是预测值y;yi就是实际值y;sigmoid(wTx)=sigmoid(w0+w1x1+...wnxn),其中wTx是向量的表示方式;推导式子里有些有下角标有些没有虽不严谨,但不影响推导理解,没有角标的都表示的是向量形式
逻辑斯特回归为什么要对特征进行离散化手动实现逻辑回归本示例代码取用sklearn的鸢尾花数据集,并取用标签结果为0,1的数据集来测试分类
from math import exp
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# data
def create_data():
# 加载鸢尾花数据集(sklearn中的数据集)
iris = load_iris()
# 通过feature_names构造dataframe
df = pd.Dataframe(iris.data, columns=iris.feature_names)
# 把iris的结果放到dataframe的label属性中
df['label'] = iris.target
# 声明dataframe的新列项
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
# 切取dataframe前100行,index为0,1,-1的列
data = np.array(df.iloc[:100, [0,1,-1]])
# data[:,:2]切取所有行和前两列 ;data[:,-1]切取所有行和index=-1的那列
return data[:,:2], data[:,-1]
class LogisticReressionClassifier:
def __init__(self, max_iter=200, learning_rate=0.01):
self.max_iter = max_iter
self.learning_rate = learning_rate
# 定义sigmoid函数
def sigmoid(self, x):
return 1 / (1 + exp(-x))
def data_matrix(self, X):
data_mat = []
for d in X:
# 把d所有值放到数组里并append到data_mat里。这里为啥第一项是1.0?因为我们的公式w0+w1x1+....wnxn,公式中没有x0,实际上第一项x0等同于1.0
data_mat.append([1.0, *d])
return data_mat
def fit(self, X, y):
# 获得数据矩阵,也就是我们公式中的x集合
data_mat = self.data_matrix(X) # m*n
# 初始化参数w,即公式中的w0,w1,w2。为啥这里初始化全为0?因为训练过程中会逐渐根据损失函数调整参数w
self.weights = np.zeros((len(data_mat[0]), 1), dtype=np.float32)
# 迭代的根据损失函数优化参数w的取值
for iter_ in range(self.max_iter):
for i in range(len(X)):
# 计算逻辑回归的结果
result = self.sigmoid(np.dot(data_mat[i], self.weights))
# 计算损失值
error = y[i] - result
# 根据损失函数更新参数w,其中np.transpose表示矩阵转置
self.weights += self.learning_rate * error * np.transpose([data_mat[i]])
print('LogisticRegression Model(learning_rate={},max_iter={})'.format(
self.learning_rate, self.max_iter))
# 模型得分
def score(self, X_test, y_test):
right = 0
X_test = self.data_matrix(X_test)
# zip是方便for取到每个x,y
for x, y in zip(X_test, y_test):
# 根据训练出来的w参数对测试数据集进行校验真实结果
result = np.dot(x, self.weights)
# 由于数据集只取了前100行,故y的值只有0,1,用来分类
if (result > 0 and y == 1) or (result < 0 and y == 0):
right += 1
return right / len(X_test)
# ====================================开始训练============
# 调用函数获得样本集
X, y = create_data()
# 划分数据集为训练、测试数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
lr_clf = LogisticReressionClassifier()
# 训练
lr_clf.fit(X_train, y_train)
lr_clf.score(X_test, y_test)
# score结果打印:0.9666666666666667
画图展示
# 画图展示参数划分结果 x_ponits = np.arange(4, 8) y_ = -(lr_clf.weights[1]*x_ponits + lr_clf.weights[0])/lr_clf.weights[2] plt.plot(x_ponits, y_) #lr_clf.show_graph() plt.scatter(X[:50,0],X[:50,1], label='0') plt.scatter(X[50:,0],X[50:,1], label='1') plt.legend()使用sklearn逻辑回归模型
# 使用SKlearn里的模型 from sklearn.linear_model import LogisticRegression # 设置迭代次数为200得到模型 clf = LogisticRegression(max_iter=200) # 训练(沿用手动实现中的划分数据集) clf.fit(X_train, y_train) # 查看得分(沿用手动实现中的划分数据集) clf.score(X_test, y_test) # 打印参数w;coef_和intercept_都是模型参数,即为w;intercept_为w0,coef_为w1到wn print(clf.coef_, clf.intercept_)
score为:1.0
打印的参数w为:[[ 2.71974676 -2.60054221]] [-6.86475951]
画图展示:
# 图像化展示分类效果
x_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_ponits + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_ponits, y_)
plt.plot(X[:50, 0], X[:50, 1], 'bo', color='blue', label='0')
plt.plot(X[50:, 0], X[50:, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
以上,若有表述错误的地方,希望指出,谢谢!