一、概念
逻辑回归(Logistic Regression),简单来说就是 —— 线性回归+sigmoid函数。旨在解决分类问题而非回归 [千万别被名字误导了!!]
1、回归问题
如下图,回归其实就是用我们高中统计学到的最小二乘法(这里我就不放最小二乘法的公式了感兴趣的可以自行查阅,就是求直线的公式其实),对数据点进行拟合的过程。如图所示,蓝色点是我们的样本数据点,通过最小二乘法得到红线,使数据点大致分布在红线,红线就是我们要拟合得到的回归曲线。
但有个问题是,线性回归的输出是某个函数值,但是函数值的大小是不确定的对我们区分样本类别有一定的阻碍。简单举个例子 ,比方我们算出来的值有几十,但也能达到好几万,数据范围很大,没有办法判断多大才是预测为正样本。因此,下面就通过sigmoid函数处理【深度学习中广泛应用的激活函数】,可以简单理解为将数据值做一定的约束处理在特定的范围内,好处多多请看下节。
2、sigmoid
原理:利用sigmoid函数特性,将线性回归输出的任意实数映射到 [0,1] 的范围,设置阈值及进行分类。[ 比方说阈值为0.5 ,那么大于0.5则为正类,小于则为负类 ]
(1)sigmoid函数:
(2)损失函数
在机器学习里成为代价函数,深度学习则为交叉熵。
在y = 1 和 y = 0情况下损失为
可以看到,
在y = 1下,损失要越小则越大,即 —>1
在y = 0下,损失要越小则越小,即 —>0
整个过程可以从下面这张表简单描述:
二、实例
这里附上数据集的下载地址(当然这里的数据集就是简单无意义的一些样本点):
1、加载数据集
import numpy as np
import matplotlib.pyplot as plt
def loadDataSet():
data = [] #创建数据列表
label = [] #创建标签列表
fr = open('testSet.txt') #打开文件
for line in fr.readlines(): #逐行读取
lineArr = line.strip().split() #去回车,放入列表
data.append([1.0, float(lineArr[0]), float(lineArr[1])]) #添加数据
label.append(int(lineArr[2])) #添加标签
fr.close() #关闭文件
return data, label #返回
def plotDataSet():
data, label = loadDataSet() #加载数据集
print(data,label)
dataArr = np.array(data) #转换成numpy的array数组
n = np.shape(data)[0] #数据个数
xcord1 = []; ycord1 = [] #正样本
xcord2 = []; ycord2 = [] #负样本
for i in range(n): #根据数据集标签进行分类
if int(label[i]) == 1:
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2]) #1为正样本
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) #0为负样本
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = 's',alpha=.5)#绘制正样本
ax.scatter(xcord2, ycord2, s = 20, c = 'green',alpha=.5) #绘制负样本
plt.title('DataSet') #绘制title
plt.xlabel('x'); plt.ylabel('y') #绘制label
plt.show() #显示
if __name__ == '__main__':
plotDataSet()
【可视化】print效果 + 散点图
2、sigmoid函数和梯度上升
#实现sigmoid函数
def sigmoid(in_x):
return 1.0/(1 + np.exp(-in_x))
#实现梯度上升
def gradAscent(data,label):
data = np.mat(data)
label = np.mat(label)
r,l = np.shape(data)
alpha = 0.001 #步长/学习率
epoch = 500 #迭代次数
weights = np.ones((l,1)) #
for k in range(epoch):
pre_lable = sigmoid(data*weights) #预测值
error = (label - pre_lable) #预测值和真实值误差
weights = weights + alpha * data.transpose() * error #梯度上升——权重更新
return weights
#输出看看效果
if __name__ == '__main__':
data,label = loadDataSet()
weights = gradAscent(data,label)
print(weights)
【可视化】
3、画出回归后的拟合曲线
但是,在上述例子中,样本数量为100,需要进行3*100次计算。类似的,如果在较大数据集下的计算复杂度会高很多,导致模型收敛较慢,因此我们考虑对算法进行改进,只需要每轮迭代一次运算即可——随机梯度下降。
4、随机梯度下降
# 随机梯度上升
def sgraDscent(data,label):
data = np.array(data)
r,l = np.shape(data)
epoch = 500
weights = np.ones(l)
for k in range(epoch):
dataIndex = list(range(r))
for i in range(r):
alpha = 4/(1.0+k+i)+0.01 #学习率不断减小
randIndex = int(np.random.uniform(0,len(dataIndex)))
y_pred = sigmoid(sum(data[randIndex]*weights)) #随机选一个
error = label[randIndex] - y_pred
weights = weights + alpha * error * data[randIndex]
del(dataIndex[randIndex])
return weights
if __name__ == '__main__':
data,label = loadDataSet()
# weights = gradAscent(data,label)
# print(weights)
# plotFit(weights)
weights2 = sgraDscent(data,label)
print(weights2)
plotFit(weights2)
【可视化】
不过从可视化结果也可以看到,在这个例子下,梯度下降比随机梯度下降分类准确率高一些,这也引出了另一个需要注意的:当数据量少时使用梯度下降,数据量大时用随机梯度下降。
用随机梯度下降的算法可以明显地加快模型的收敛,如下图