逻辑回归
- 逻辑回归的模型是一个非线性模型
- sigmoid函数,又称逻辑回归函数。但是它本质上又是一个线性回归模型
- 因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归
- 可以说,逻辑回归,都是以线性回归为理论支持的
- 只不过,线性模型,无法做到sigmoid的非线性形式,sigmoid可以轻松处理0/1分类问题
逻辑回归
- 找一个合适的预测函数,一般表示为h函数,该函数就是需要找的分类函数,它用来预测输入数据的判断结果
- 构造一个损失函数,该函数表示预测输出(h)与训练数据类别(y)之间的偏差,可以是二者之间的差(h-y)或者其他的形式。综合考虑所有训练数据的“损失”,将损失求和或者求平均,记为函数,表示所有训练数据预测值与实际类别的偏差。
- 显然,函数的值越小表示预测函数越精确,所以这一步需要做的就是找到函数的最小值,这里利用梯度下降法
Logistic函数
如果忽略二分类问题中的y的取值是一个离散的取值(0或1),我们继续使用线性回归来预测y的取值。这样做会导致y的取值并不为0或1。逻辑回归使用一个函数来归一化y值,使y的取值在区间(0,1)内,这个函数称为Logistic函数,也称Sigmoid函数,函数公式如下:
函数图形如下:
Z是线性回归函数,所以也可以表示成:
对于线性边界的情况,边界形式如下:
构造预测函数为:
函数的值有特殊的含义,它表示结果取1的概率,因此对于输入x分类结果为类别1和类别0的概率分别为:
损失函数
对于任何机器学习问题,都需要明确损失函数:
但是在LR模型解决的二分类问题中,这个函数形式为:
这个损失函数通常称作是对数损失(logloss),由于y的取值为0或者1,因此可以表示为:
优化求解
现在我们已经确定了模型的损失函数,那么接下来就是根据这个损失函数,不断优化模型参数从而获得拟合数据的最佳模型。重新看一下损失函数,其本质上是;关于模型中线性方程部分两个参数w和b的函数:
其中:
现在的学习任务转化为数学优化的形式即为:
由于损失函数连续可微,我们可以借助梯度下降法进行优化求解,对于这个核心参数的更新方式如下:
代码及代码解释如下:
import numpy as np
class LogisticRegression(object):
#初始化参数(学习率,最大迭代次数,seed为种子数,seed相同时,numpy.random.seed()生成的随机数相同
def __init__(self,learning_rate=0.1,max_iter=100,seed=None):
self.seed = seed
self.lr = learning_rate
self.max_iter = max_iter
#进行训练,np.random.normal()的意思是一个正态分布,normal这里是正态的意思。
# 参数loc(float):正态分布的均值,对应着这个分布的中心。loc=0说明这一个以Y轴为对称轴的正态分布,
# 参数scale(float):正态分布的标准差,对应分布的宽度,scale越大,正态分布的曲线越矮胖,scale越小,曲线越高瘦。
# 参数size(int 或者整数元组):输出的值赋在shape里,默认为None。
def fit(self,x,y):
np.random.seed(self.seed)
self.w = np.random.normal(loc=0.0,scale=1.0,size=x.shape[1])
self.b = np.random.normal(loc=0.0,scale=1.0)
self.x = x
self.y = y
for i in range(self.max_iter):
self._update_step()
def _sigmoid(self,z):
return 1.0/(1.0+np.exp(-z))
# 返回一个逻辑函数
def _f(self,x,w,b):
z = x.dot(w) + b
return self._sigmoid(z)
def predict_prob(self,x=None):
if x is None:
x = self.x
y_preb = self._f(x,self.w,self.b)
# 预测值,y_pred_proba表示逻辑函数Y,是一个概率,y_pred为分类后的预测值
def predict(self,x=None):
if x is None:
x = self.x
y_pred_proba = self._f(x,self.w,self.b)
y_pred = np.array([0 if y_pred_proba[i]<0.5 else 1 for i in range(len(y_pred_proba))])
return y_pred
def score(self,y_true=None,y_pred=None):
if y_true is None or y_pred is None:
y_true = self.y
y_pred = self.predict()
acc = np.mean([1 if y_true[i]==y_pred[i] else 0 for i in range(len(y_true))])
return acc
#损失函数
def loss(self,y_true=None,y_pred_proba=None):
if y_true is None or y_pred is None:
y_true = self.y
y_pred_proba = self.predict_proba()
return np.mean(-1.0*(y_true*np.log(y_pred_prob)+(1.0-y_true)*np.log(1.0-y_pred_proba)))
# 机算梯度
def _cal_gradient(self):
y_pred = self.predict()
d_w = (y_pred - self.y).dot(self.x)/len(self.y)
d_b = np.mean(y_pred - self.y)
return d_w,d_b
# 进行迭代,利用梯度下降算法求解w,b,d_w,d_b为损失函数L对w,b求偏导
def _update_step(self):
d_w,d_b = self._cal_gradient()
self.w = self.w - self.lr*d_w
self.b = self.b - self.lr*d_b
return self.w, self.b
#生成数据
def generate_data(seed):
np.random.seed(seed)
data_size_1 = 300
x1_1 = np.random.normal(loc=5.0,scale=1.0,size=data_size_1)
x2_1 = np.random.normal(loc=5.0,scale=1.0,size=data_size_1)
y_1 = [0 for _ in range(data_size_1)]
data_size_2 = 400
x1_2 = np.random.normal(loc=10.0,scale=2.0,size=data_size_2)
x2_2 = np.random.normal(loc=8.0,scale=2.0,size=data_size_2)
y_2 = [1 for _ in range(data_size_2)]
# numpy.concatenate((a1, a2, ...), axis),a1,a2 .. 相同类型的数组序列,axis 必须连接数组的轴。默认值为0
x1 = np.concatenate((x1_1,x1_2),axis=0)
x2 = np.concatenate((x2_1,x2_2),axis=0)
# hstack(tup) ,参数tup可以是元组,列表,或者numpy数组,返回结果为numpy的数组,它其实就是水平(按列顺序)把数组给堆叠起来
# reshape(-1,1)表示(任意行,1列),第一列为横座标,第二列为纵座标,y为分类的值,为0或者1
x = np.hstack((x1.reshape(-1,1),x2.reshape(-1,1)))
y = np.concatenate((y_1,y_2),axis=0)
# np.random.permutation():随机排列序列
data_size_all = data_size_1 + data_size_2
shuffled_index = np.random.permutation(data_size_all)
x = x[shuffled_index]
y = y[shuffled_index]
return x,y
# 训练集和测试集分割
def train_tset_split(x,y):
splite_index = int(len(y)*0.7)
x_train = x[:splite_index]
y_train = y[:splite_index]
x_test = x[splite_index:]
y_test = y[splite_index:]
return x_train,y_train,x_test,y_test
import matplotlib.pyplot as plt
x,y = generate_data(seed=272)
x_train,y_train,x_test,y_test = train_tset_split(x,y)
# x_train第一列为横座标,第二列为纵座标,根据y_train的值(0或者1)来标记颜色
plt.scatter(x_train[:,0],x_train[:,1],c=y_train,marker=".")
plt.show()
plt.scatter(x_test[:,0], x_test[:,1], c=y_test, marker='.')
plt.show()
# s数据归一化
x_train=(x_train-np.min(x_train,axis=0))/(np.max(x_train,axis=0)-np.min(x_train,axis=0))
x_test=(x_test-np.min(x_test,axis=0))/(np.max(x_test,axis=0)-np.min(x_test,axis=0))
# 逻辑回归
clf = LogisticRegression(learning_rate=0.1,max_iter=500,seed=272)
clf.fit(x_train,y_train)
# xx表示0.1-0.6 步长为0.1
# 由于是斜向下的直线,因此y=-wx-b
split_boundary_fuc = lambda x:(-clf.b-clf.w[0]*x)/clf.w[1]
xx = np.arange(0.1,0.6,0.1)
cValue=['g','b']
plt.scatter(x_train[:,0],x_train[:,1],c=[cValue[i] for i in y_train],marker="o")
plt.plot(xx,split_boundary_fuc(xx),c='red')
运行结果为: