线性回归模型:
w=[0.1,0.2,0.4,0.2]
b=0.5
def linearRegression(x):
return sum([x[i]*w[i] for i in range(len(x))])+b
linearRegression([2,1,3,1])
通常将h(x)=0设为边界条件用于分类。h(x)>0的样本设为一类,反之设为另一类。
在此线性模型的基础上加上一个函数g,即。。这个函数就是sidmoid函数,也叫做logistic函数。它可以将一个线性回归中的结果转化为一个概率值。此时h(x)表示的就是某件事发生的概率。
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
sigmoid(linearRegression([2,1,3,1]))
下图为sigmoid函数图像:
import matplotlib.pyplot as plt
x = np.arange(-10, 10, 0.01)
y = sigmoid(x)
plt.plot(x, y)
plt.show()
逻辑回归的损失函数:
对于一般的二分类的逻辑回归,交叉熵函数为J(θ)=-[yln(y')+(1-y)ln(1-y')],其中y'是预测值。
训练中所有样本的损失:
注:θ代表的是所有的参数集合
为什么逻辑回归要采用交叉熵作为损失函数?这是假设逻辑回归中Y服从于伯努利分布,然后从最大似然估计中推导来的。
逻辑回归的优化方法:梯度下降法
函数梯度的方向就是函数增长最快的方向,反之梯度的反方向就是函数减少最快的方向。因此要计算函数的最小值,就朝着该函数梯度相反的方向前进。
假设需要优化的函数为。
首先初始化自变量,从开始。设置一个学习率η。
对于任何i>=0:如果是最小化f, ,
如果是最大化f, 。
注:随机梯度下降——每次计算一个样本的损失,然后更新参数θ;下降速度快,但收敛性差,容易陷入局部最优。批次梯度下降——每次根据一批次的样本来更新参数;梯度下降——全部样本,速度慢,但更能达到全局最优。
对于逻辑回归优化的目标函数
需要优化参数w,b,从而使其在已知的样本X,y上值最小。
首先要对J(w,b)求导。令
再令 ,即g=σ(a)
根据上述推导和链式法则,
根据以上公式,写一个根据随机梯度下降进行优化的函数。
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
X=datasets.load_iris()['data']
Y=datasets.load_iris()['target']
Y[Y>1]=1
X_train,X_test,y_train,y_test=train_test_split(X,Y,test_size=0.4,stratify=Y)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def cal_grad(y, t):
grad = np.sum(t - y) / t.shape[0]
return grad
def cal_cross_loss(y, t):
loss=np.sum(-y * np.log(t)- (1 - y) * np.log(1 - t))/t.shape[0]
return loss
class LR:
def __init__(self, in_num, lr, iters, train_x, train_y, test_x, test_y):
self.w = np.random.rand(in_num)
self.b = np.random.rand(1)
self.lr = lr
self.iters = iters
self.x = train_x
self.y = train_y
self.test_x=test_x
self.test_y=test_y
def forward(self, x):
self.a = np.dot(x, self.w) + self.b
self.g = sigmoid(self.a)
return self.g
def backward(self, x, grad):
w = grad * x
b = grad
self.w = self.w - self.lr * w
self.b = self.b - self.lr * b
def valid_loss(self):
pred = sigmoid(np.dot(self.test_x, self.w) + self.b)
return cal_cross_loss(self.test_y, pred)
def train_loss(self):
pred = sigmoid(np.dot(self.x, self.w) + self.b)
return cal_cross_loss(self.y, pred)
def train(self):
for iter in range(self.iters):
##这里采用随机梯度下降的方法
for i in range(self.x.shape[0]):
t = self.forward(self.x[i])
grad = cal_grad(self.y[i], t)
self.backward(self.x[i], grad)
train_loss = self.train_loss()
valid_loss = self.valid_loss()
if iter%5==0:
print("当前迭代次数为:", iter, "训练loss:", train_loss, "验证loss:", valid_loss)
model=LR(4,0.01,100,X_train,y_train,X_test,y_test)
model.train()
通常直接调用sklearn包:
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
X=datasets.load_iris()['data']
Y=datasets.load_iris()['target']
from sklearn.linear_model import LogisticRegression
X_train,X_test,y_train,y_test=train_test_split(X,Y,test_size=0.1,stratify=Y)
model=LogisticRegression(penalty='l2',class_weight=None,random_state=None,max_iter=100)
model.fit(X_train,y_train)
model.predict_proba(X_test)
penalty:惩罚系数,也就是正则化,默认为L2。
L1: 加上参数w绝对值的和。
L2: 加上参数w平方和。
当我们假设参数ω服从正态分布的时候,根据贝叶斯模型可以推导出L2正则化,当我们假设参数ω服从拉普拉斯分布发时候,根据贝叶斯模型可以推导出L1正则化。
class_weight:类别权重,一般在分类不均衡的时候使用。
max_iter:最大迭代次数。
特征离散化:一般我们不会将连续的值作为特征输入到逻辑回归的模型之中,而是将其离散成0,1变量。这样的好处有:
1、稀疏变量的内积乘法速度快,计算结果方便存储,并且容易扩展;
2、离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰。
3、逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合;
4、离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力;
5、特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问。