逻辑回归

为什么需要逻辑回归

  • 线性回归是不能解决分类问题的。线性回归实际上做了三个假设
  • 1.因变量Yi和自变量Xi之间呈线性相关
  • 2.自变量Xi与干扰项相互独立
  • 3.没被线性模型捕捉到的随机因素服务正态分布
  • 理论上来说,任何数据放在模型里面都会得到相应的参数估计,进而通过模型对数据进行预测。但是这并一定能保证模型效果,有时会得到”错且无用”的模型,因此建模的过程中需要不断提出假设和检验假设

什么是线性回归

  • 逻辑回归假设数据服从伯努利分布,通过极大似然函数的方法,运用梯度下降来求解参数,来达到将数据二分类目的。
  • 原理是将样本的特征和样本发生的概率联系起来,即,预测的是样本发生的概率是多少。由于概率是一个数,因此被叫作”逻辑回归”
  • 逻辑回归是一个非线性模型,但是其背后是以线性回归为理论支撑,其提出一个与线性模型长相类似但不同的新公式: 假设特征X所对应的y值是在质数上裱花,那么久可以将结果y值取对数,作为其线性模型逼近的目标。也就是所谓的”对数线性回归”
  • 对数线性回归公式中,可以改写如下形式。实际上是求输入空间X到输出空间y的非线性函数映射。对数函数的作用是将线性回归模型的预测值与真实标记联系起来
  • 一般意义上的单调可微的“联系函数”:g(a) = ln(a).其本质就是给原来线性变换加上一个非线性变换(或者说映射),使得模拟的函数有非线性的属性,但本质上调参还是线性的,主体是内部线性的调参。那么对于解决分类问题的逻辑回归来说,我们需要找到一个“联系函数”,将线性回归模型的预测值与真实标记联系起来。将“概率”转换为“分类”的工具是“阶段函数”:
  • 对于上述阶梯函数不连续,不能作为“联系函数”g,因此使用对数几率函数来在一定程度上近似阶梯函数,将线性回归模型的预测值转换为分类所对应的概率(如下)
  • 如果另y为整理,1-y为负例,所谓的“几率”就是二者的比值y/(1-y) ,几率反映了样本X为整理的相对可能性(如y值分别预测为0,1的概率为0.4,0.6,若阈值设置为0.5,那么y就被预测为1)。对数几率即对几率取对数ln(y/(1-y)),对数几率实际上就是sigmod函数,将线性模型转换为分类。如图,可以看出,sigmod实际上就是用线性回归模型的预测结果取逼近真实值的对数几率,因此逻辑回归也被成为”对数几率回归”
  • 之所以用sigmod做假设函数,因为线性回归模型预测值为实数,而样本标记为(0,1),我们需要将二分类任务的真实性标记y与线性回归模型的预测值联系起来,也就是找到了广义线性模型中的联系函数。如果选择单位阶跃函数的话,它是不连续的不可微,而选择sigmod函数,他是连续的,而且能够将z转换为一个接近0或1的值

逻辑回归代码

  • 原生实现
import  numpy as np
\#计算回归函数的输出指标,根据分类问题对输出指标进行修改
from  sklearn.metrics import  accuracy_score

class LogisticRegressionWithSigmod:
	def __init__(self):
		"""
		初始化类
		"""
		# 斜率
		self.coef_ = None
		self.intercept_ = None
		self._theta = None

	def _sigmoid(self, t):
		"""
		定义sigmod方法  1/(1+ e^(-t))
		:param t: 线性模型 t
		:return: sigmod表达式
		"""
		return 1./(1. + np.exp(-t))

	def fit(self,X_train, y_train, eta = 0.01, n_iters=1e4):
		"""
		fit方法,内部使用梯度下降训练法 来训练 Logitstic Regression
		参数:训练集特征集 X_train, 标签集y_train
		输出:训练好的模型
		:param X_train:
		:param y_train:
		:param eta:
		:param n_iters:
		:return:
		"""
		# 检查训练集数量和标签集数量相等
		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):
			"""
			定义逻辑回归的损失函数
			:param theta: 参数theta
			:param X_b: 构造好的矩阵X_b
			:param y: 标签y
			:return: 损失表达式
			"""

			# 定义逻辑回归的模型 y_hat
			y_hat = self._sigmoid(X_b.dot(theta))
			try:
				# 选择损失函数的表达式
				return - np.sum(y*np.log(y_hat)+(1-y)*np.log(y-y_hat))/len(y)
			except:
				return float('inf')

			def dj(theta,X_b,y):
				"""
				损失函数的导数计算
				:param theta: 参数theta
				:param X_b: 构造好的矩阵X_b
				:param y: 标签y
				:return: 计算好的表达式
				"""
				return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(y)

			def gradient_descent(X_b, y, initial_theta, n_iters=1e4, epsilon=1e-8):
				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])
			# 梯度下降的结果求出参数heta
			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

			"""
			逻辑回归是根据概率进行分类的,因此先预测概率
			参数:输入空间X_predict
			输出:结果概率向量
			"""

			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])
				# 将梯度下降得到的参数theta带入逻辑回归的表达式中
				return self._sigmoid(X_b.dot(self._theta))

			"""
			使用X_predict的结果概率向量,将其转换为分类
			参数:输入空间X_predict
			输出:分类结果
			"""

			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)
				# 判断概率是否大于0.5,然后将布尔表达式得到的向量,强转为int类型,即为0-1向量
				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()"