目录
第1关:逻辑回归算法大体思想
第2关:逻辑回归的损失函数
第3关:梯度下降
第4关:逻辑回归算法流程
第5关:sklearn中的逻辑回归
什么是逻辑回归
当一看到“回归”这两个字,可能会认为逻辑回归是一种解决回归问题的算法,然而逻辑回归是通过回归的思想来解决二分类问题的算法。
那么问题来了,回归的算法怎样解决分类问题呢?其实很简单,逻辑回归是将样本特征和样本所属类别的概率联系在一起,假设现在已经训练好了一个逻辑回归的模型为f(x),模型的输出是样本
x
的标签是1
的概率,则该模型可以表示成p^=f(x)。若得到了样本x
属于标签1
的概率后,很自然的就能想到当p^>0.5时x
属于标签1
,否则属于标签0
。所以就有$$\hat y=\begin{cases} 0 & \hat p <0.5 \ 1 & \hat p >0.5 \end{cases}(其中\hat y$$为样本x根据模型预测出的标签结果,标签0
和标签1
所代表的含义是根据业务决定的,比如在癌细胞识别中可以使0
代表良性肿瘤,1
代表恶性肿瘤)。由于概率是0到1的实数,所以逻辑回归若只需要计算出样本所属标签的概率就是一种回归算法,若需要计算出样本所属标签,则就是一种二分类算法。
那么逻辑回归中样本所属标签的概率怎样计算呢?其实和线性回归有关系,学习了线性回归的同学肯定知道线性回归无非就是训练出一组参数WT和b来拟合样本数据,线性回归的输出为y^=WTx+b。不过y^的值域是(−∞,+∞),如果能够将值域为(−∞,+∞)的实数转换成(0,1)的概率值的话问题就解决了。**要解决这个问题很自然地就能想到将线性回归的输出作为输入,输入到另一个函数中,这个函数能够进行转换工作,假设函数为σ,转换后的概率为p^,则逻辑回归在预测时可以看成p^=σ(WTx+b)**。 σ其实就是接下来要介绍的
sigmoid
函数。sigmoid 函数
sigmoid
函数的公式为:σ(t)=1/1+e−t
函数图像如下图所示:
从sigmoid函数的图像可以看出当t趋近于−∞时函数值趋近于
0
,当t趋近于+∞时函数值趋近于1
。可见sigmoid
函数的值域是(0,1),满足我们要将(−∞,+∞)的实数转换成(0,1)的概率值的需求。因此逻辑回归在预测时可以看成p^=1/(1+e−WTx+b)
编程要求
根据提示,在右侧编辑器补充 python 代码,实现
sigmoid
函数。底层代码会调用您实现的sigmoid
函数来进行测试。(提示: numpy.exp() 函数可以实现e的幂运算)测试说明
测试用例:
输入:
1
预期输出:
0.73105857863
输入:
-2
预期输出:
0.119202922022
#encoding=utf8
import numpy as np
#sigmoid函数
def sigmoid(t):
#输入:负无穷到正无穷的实数
#输出:转换后的概率值
#********** Begin **********#
result = 1.0 / (1 + np.exp(-t))
#********** End **********#
return round(result,12)
if __name__ == '__main__':
pass
根据上一节实训中所学习到的知识,我们已经知道了逻辑回归计算出的样本所属类别的概率p^=σ(WTx+b),样本所属列表的判定条件为$$\hat y=\begin{cases} 0 & \hat p <0.5 \ 1 & \hat p >0.5 \end{cases}。很明显,在预测样本属于哪个类别时取决于算出来的\hat p。从另外一个角度来说,假设现在有一个样本的真实类别为\hat p$$有关。
当然逻辑回归的损失函数不仅仅与p^有关,它还与真实类别有关。假设现在有两种情况,情况A:现在有个样本的真实类别是
0
,但是模型预测出来该样本是类别1
的概率是0.7
(也就是说类别0的概率为0.3
);情况B:现在有个样本的真实类别是0
,但是模型预测出来该样本是类别1
的概率是0.6
(也就是说类别0
的概率为0.4
);请你思考2
秒钟,AB两种情况哪种情况的误差更大?很显然,情况A的误差更大!因为情况A中模型认为样本是类别0
的可能性只有30%
,而B有40%
。假设现在又有两种情况,情况A:现在有个样本的真实类别是
0
,但是模型预测出来该样本是类别1
的概率是0.7
(也就是说类别0
的概率为0.3
);情况B:现在有个样本的真实类别是1
,但是模型预测出来该样本是类别1
的概率是0.3
(也就是说类别0
的概率为0.7
);请你再思考2
秒钟,AB两种情况哪种情况的误差更大?很想然,一样大!所以逻辑回归的损失函数如下,其中
cost
表示损失函数的值,y
表示样本的真实类别:cost=−ylog(p^)−(1−y)log(1−p^)
这个式子其实很好理解,当样本的真实类别为
1
时,式子就变成了cost=−log(p^)。此时函数图像如下:
从图像能看出当样本的真实类别为1的前提下,p^越大,损失函数值就越小。因为p^越大就越说明模型越认为该样本的类别为
1
。当样本的真实类别为0时,式子就变成了cost=−log(1−p^)。此时函数图像如下:
从图像能看出当样本的真实类别为
0
的前提下,p^越大,损失函数值就越大。因为p^越大就越说明模型越认为该样本的类别为1
。cost=−ylog(p^)−(1−y)log(1−p^)是一个样本的损失计算公式,但是在一般情况下需要计算的是
m
条样本数据的平均损失值,所以损失函数的最终形态如下,其中m
表示数据集中样本的数量,i
表示数据集中第i
个样本:cost=−m1i=0∑my(i)log(p^(i))−(1−y(i))log(1−p^(i))
知道了逻辑回归的损失函数之后,逻辑回归的训练流程就很明显了,就是寻找一组合适的WT和b,使得损失值最小。找到这组参数后模型就确定下来了。
梯度:梯度的本意是一个向量,由函数对每个参数的偏导组成,表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向变化最快,变化率最大。
梯度下降算法原理
算法思想:梯度下降是一种非常通用的优化算法,能够为大范围的问题找到最优解。梯度下降的中心思想就是迭代地调整参数从而使损失函数最小化。假设你迷失在山上的迷雾中,你能感觉到的只有你脚下路面的坡度。快速到达山脚的一个策略就是沿着最陡的方向下坡。这就是梯度下降的做法:通过测量参数向量
θ
相关的损失函数的局部梯度,并不断沿着降低梯度的方向调整,直到梯度降为0
,达到最小值。
其中
η
为学习率,是0
到1
之间的值,是个超参数,需要我们自己来确定大小。算法原理: 在传统机器学习中,损失函数通常为凸函数,假设此时只有一个参数,则损失函数对参数的梯度即损失函数对参数的导数。如果刚开始参数初始在最优解的左边,
很明显,这个时候损失函数对参数的导数是小于
0
的,而学习率是一个0
到1
之间的数,此时按照公式更新参数,初始的参数减去一个小于0
的数是变大,也就是在坐标轴上往右走,即朝着最优解的方向走。同样的,如果参数初始在最优解的右边,此时按照公式更新,参数将会朝左走,即最优解的方向。所以,不管刚开始参数初始在何位置,按着梯度下降公式不断更新,参数都会朝着最优解的方向走。 #####梯度下降算法流程
- 随机初始参数
- 确定学习率
- 求出损失函数对参数梯度
- 按照公式更新参数
- 重复3、4直到满足终止条件(如:损失函数或参数更新变化值小于某个阈值,或者训练次数达到设定阈值)
编程要求
根据提示,使用 python 搭建梯度下降算法,并损失函数最小值时对应的参数
theta
,theta
会返回给外部代码,由外部代码来判断theta
是否正确。测试说明
损失函数为:loss=(theta−3)2 最优参数为:
3.0
你的答案跟最优参数的误差低于0.0001
才能通关。
# -*- coding: utf-8 -*-
import numpy as np
import warnings
warnings.filterwarnings("ignore")
#梯度下降,inital_theta为参数初始值,eta为学习率,n_iters为训练轮数,epslion为误差范围
def gradient_descent(initial_theta,eta=0.05,n_iters=1e3,epslion=1e-8):
# 请在此添加实现代码 #
#********** Begin *********#
theta = initial_theta
i_iter = 0
while i_iter < n_iters:
gradient = 2*(theta-3)
last_theta = theta
theta = theta - eta*gradient
if(abs(theta-last_theta)
乳腺癌数据集,其实例数量是
569
,实例中包括诊断类和属性,帮助预测的属性一共30
个,各属性包括为radius
半径(从中心到边缘上点的距离的平均值),texture
纹理(灰度值的标准偏差)等等,类包括:WDBC-Malignant
恶性和WDBC-Benign
良性。用数据集的80%
作为训练集,数据集的20%
作为测试集,训练集和测试集中都包括特征和诊断类。sklearn中已经提供了乳腺癌数据集的相关接口,想要使用该数据集可以使用如下代码:
from sklearn import datasets
#加载乳腺癌数据集
cancer = datasets.load_breast_cancer()
#X表示特征,y表示标签
X = cancer.data
y = cancer.target
数据集中部分数据与标签如下图所示:
构建逻辑回归模型
由数据集可以知道,每一个样本有
30
个特征和1
个标签,而我们要做的事就是通过这30
个特征来分析细胞是良性还是恶性,其中标签y=0
表示是良性,y=1
表示是恶性。逻辑回归算法正好是一个二分类模型,我们可以构建一个逻辑回归模型,来对癌细胞进行识别。模型如下:z=b+w1x1+w2x2+...+wnxn
y=1+e−z1
其中xi表示第
i
个特征,wi表示第i
个特征对应的权重,b
表示偏置。 为了方便,我们稍微将模型进行变换:z=w0x0+w1x1+w2x2+...+wnxn
其中x0等于1。
Z=θ.X
θ=(w0,w1,...,wn)
X=(1,x1,...,xn)
y=1+e−θ.X1
我们将一个样本输入模型,如果预测值大于等于
0.5
则判定为1
类别,如果小于0.5
则判定为0
类别。训练逻辑回归模型
我们已经知道如何构建一个逻辑回归模型,但是如何得到一个能正确对癌细胞进行识别的模型呢?通常,我们先将数据输入到模型,从而得到一个预测值,再将预测值与真实值结合,得到一个损失函数,最后用梯度下降的方法来优化损失函数,从而不断的更新模型的参数 θ,最后得到一个能够正确对良性细胞和癌细胞进行分类的模型。
在上一节中,我们知道要使用梯度下降算法首先要知道损失函数对参数的梯度,即损失函数对每个参数的偏导,求解步骤如下:
loss=−ylna−(1−y)ln(1−a)
∂w∂loss=∂a∂loss.∂z∂a.∂w∂z
∂a∂loss=−ay−1−a1−y(−1)=a(1−a)a−y
∂z∂a=(1+e−z)2e−z=a.(1−a)
∂w∂z=x
∂w∂loss=(a−y)x
其中
a
为预测值,y
为真实值。 于是,在逻辑回归中的梯度下降公式如下:wi=wi−η(a−y)xi
训练流程:
同梯度下降算法流程
编程要求
根据提示,在右侧编辑器补充 python 代码,构建一个逻辑回归模型,并对其进行训练,最后将得到的逻辑回归模型对癌细胞进行识别。
测试说明
只需返回预测结果即可,程序内部会检测您的代码,预测正确率高于
95%
视为过关。
# -*- coding: utf-8 -*-
import numpy as np
import warnings
warnings.filterwarnings("ignore")
#定义sigmoid函数
def sigmoid(x):
return 1/(1+np.exp(-x))
#梯度下降,x为输入数据,y为数据label,eta为学习率,n_iters为训练轮数
def fit(x,y,eta=1e-3,n_iters=1e4):
# 请在此添加实现代码 #
#********** Begin *********#
theta = np.zeros(x.shape[1])
i_iter = 0
while i_iter < n_iters:
gradient = (sigmoid(x.dot(theta))-y).dot(x)
theta = theta -eta*gradient
i_iter += 1
#********** End **********#
return theta
本关任务:你需要调用 sklearn 中的逻辑回归模型,并通过癌细胞数据集中癌细胞的
30
种属性与类别对逻辑回归模型进行训练。我们会调用你训练好的逻辑回归模型,来对癌细胞进行识别。相关知识
为了完成本关任务,你需要掌握:1.
LogisticRegression
。数据介绍
乳腺癌数据集,其实例数量是
569
,实例中包括诊断类和属性,帮助预测的属性一共30
个,各属性包括为radius
半径(从中心到边缘上点的距离的平均值),texture
纹理(灰度值的标准偏差)等等,类包括:WDBC-Malignant
恶性和WDBC-Benign
良性。用数据集的80%
作为训练集,数据集的20%
作为测试集,训练集和测试集中都包括特征和诊断类。sklearn中已经提供了乳腺癌数据集的相关接口,想要使用该数据集可以使用如下代码:
from sklearn import datasets
#加载乳腺癌数据集
cancer = datasets.load_breast_cancer()
#X表示特征,y表示标签
X = cancer.data
y = cancer.target
数据集中部分数据与标签如下图所示:
LogisticRegression
LogisticRegression
的构造函数中有三个常用的参数可以设置:
solver
:{'newton-cg' , 'lbfgs', 'liblinear', 'sag', 'saga'}
, 分别为几种优化算法。默认为liblinear
。C
:正则化系数的倒数,默认为1.0
,越小代表正则化越强。max_iter
:最大训练轮数,默认为100
。和 sklearn 中其他分类器一样,
LogisticRegression
类中的fit
函数用于训练模型,fit
函数有两个向量输入:
X
:大小为 [样本数量,特征数量] 的ndarray
,存放训练样本Y
:值为整型,大小为 [样本数量] 的ndarray
,存放训练样本的分类标签
LogisticRegression
类中的predict
函数用于预测,返回预测标签,predict
函数有一个向量输入:
X
:大小为[样本数量,特征数量]的ndarray
,存放预测样本
LogisticRegression
的使用代码如下:
logreg = LogisticRegression(solver='lbfgs',max_iter =10,C=10)
logreg.fit(X_train, Y_train)
result = logreg.predict(X_test)
编程要求
填写
cancer_predict(train_sample, train_label, test_sample)
函数完成癌细胞识别任务,其中:
train_sample
:训练样本train_label
:训练标签test_sample
:测试样本测试说明
只需返回预测结果即可,程序内部会检测您的代码,预测正确率高于
95%
视为过关。
#encoding=utf8
import warnings
warnings.filterwarnings("ignore")
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.model_selection import train_test_split
def cancer_predict(train_sample, train_label, test_sample):
'''
实现功能:1.训练模型 2.预测
:param train_sample: 包含多条训练样本的样本集,类型为ndarray
:param train_label: 包含多条训练样本标签的标签集,类型为ndarray
:param test_sample: 包含多条测试样本的测试集,类型为ndarry
:return: test_sample对应的预测标签
'''
#********* Begin *********#
cancer = datasets.load_breast_cancer()
#X表示特征,y表示标签
X = cancer.data
y = cancer.target
##划分训练集和测试集
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.20)
logreg = LogisticRegression(solver='lbfgs',max_iter =200,C=10)
logreg.fit(X_train, y_train)
result = logreg.predict(test_sample)
# print(result)
return result
#********* End *********#