目录
五、逻辑回归的参数估计
1、损失函数的构建
单特征的数据集
index | lables |
1 | 0 |
4 | 1 |
带入数据集
第一条数据真实情况为y=0,第二条数据真实情况为y=1。
由此还可以计算x=1时y为0的概率:
index | lables | 1-predict | 0-predict |
1 | 0 | ||
4 | 1 |
如果我们希望模型预测结果尽可能准确,就等价于希望和$两|个概率结果越大越好。
损失函数一般都是求最小值,因此可将上式求最大值转化为对应负数结果求最小值,同时累乘也可以转化为对数相加结果,因此上式求最大值可等价于下式求最小值:
我们即构建了一个由两条数据所构成的逻辑回归损失函数。
2、损失函数的求解
通过对LogitLoss(w,b)求偏导然后令偏导函数等于0、再联立方程组的方式来对参数进行求解。
上述构建损失函数和求解损失函数的过程也被称为极大似然估计。
六、极大似然估计
逻辑回归模型:
其中
step1:确定似然项
带入多少条数据进行建模,似然函数中就有多少个似然项。
对于逻辑回归来说,当和取得一组之后,既可以有一个概率预测输出结果,即:
对应y取0的概率有
可以简写为:
第i个数据所对应的似然项可以写成:
其中,表示第i条数据对应的类别标签。不难发现,当时,代表的是i第条数据标签为0,此时需要带入似然函数的似然项是(因为希望的概率更大)。反之,当时,代表的是i第条数据标签为1,此时需要带入似然函数的似然项是。上述似然项可以同时满足这两种不同的情况。
step2:构建似然函数
似然项的累乘计算极大似然函数:
step3:对数转化
step4:求解对数似然函数
采用导数为0联立方程组的方式进行求解,这也是极大似然估计对参数求解的一般方法。
在机器学习领域,我们通常会采用一些更加通用的优化方法对逻辑回归的损失函数进行求解,通常来说是牛顿法或者梯度下降算法。
七、KL离散度计算法
1、熵、相对熵、交叉熵
熵(entropy)是表示随机变量不确定性的度量,或者说系统混乱程度、信息混乱程度。熵的计算公式如下:
在分类模型中相当于每个类别对应的概率乘以这个概率log取值后求和取反。
表示多分类问题中第i个类别出现的概率,n表示类别总数,通常来说信息熵的计算都取底数为2,并且规定log0=0。
index | labels |
1 | 0 |
2 | 0 |
3 | 1 |
4 | 1 |
5 | 1 |
信息熵的计算过程中有两类,n=2,令代表类别0的概率代表类别1的概率。则有:
信息熵计算结果为:
可定义信息熵计算函数
def entropy(p):
'''
信息熵计算函数
:param p: 概率
:return: 信息熵
'''
if p==0 or p==1:
ent=0
else:
ent=-p*np.log2(p)-(1-p)*np.log2(1-p)
return ent
熵的基本性质
熵的计算结果在[0,1]之间,并且熵值越大,系统越混乱、信息越混乱。
相对熵也被称为Kullback-Leibler散度(KL散度)或者信息散度(information divergence)。通常用来衡量两个随机变量分布的差异性。
同一个随机变量X,有两个单独的概率分布P(x)和Q(x),当X是离散变量时,我们可以通过如下相对熵计算公式来衡量二者差异:
相对熵越小,代表Q(x)和P(x)越接近。
实际上可以理解为Q离P的差距有多少,在机器学习领域,我们一般令Q为模型输出结果,而P为数据集标签真实结果,以此来判断模型输出结果是否足够接近真实情况。
Q为拟合分布P为真实分布,也被称为前向KL散度(forward KL divergence)。
对于给定数据集,信息熵是确定的,因此相对熵的大小完全由决定。而该式计算结果也被称为交叉熵(cross entropy)计算。
相对熵=交叉熵-信息熵
相对熵的取值恒大于等于零,当预测分布和真实分布完全一致时相对熵取值为0,此时交叉熵等于数据信息熵,此外只要二者分布不一致,交叉熵的取值都将大于信息熵。
2、交叉熵损失函数
index | labels | predicts |
---|---|---|
1 | 1 | 0.8 |
2 | 0 | 0.3 |
3 | 0 | 0.4 |
4 | 1 | 0.7 |
将上述数据集改写为如下形式:(这种数据集标签由0-1转化为A、B,也被称为名义型变量的独热编码one-hot)
index | A类 | B类 |
---|---|---|
1 | 0 | 1 |
predicts | 0.2 | 0.8 |
2 | 1 | 0 |
predicts | 0.7 | 0.3 |
3 | 1 | 0 |
predicts | 0.6 | 0.4 |
4 | 0 | 1 |
predicts | 0.3 | 0.7 |
第一条数据的交叉熵计算过程如下:
多样本交叉熵计算公式:
其中m为数据量,n为类别数量
整体交叉熵是每条数据交叉熵的均值
3、二分类交叉熵损失函数
def BCE(y,yhat):
'''
二分类交叉熵损失函数
'''
return (-1/len(y)*np.sum(y*np.log2(yhat)+(1-y)*np.log2(1-yhat)))
y=np.array([1,0,0,1]).reshape(-1,1)
yhat=np.array([0.8,0.3,0.4,0.7]).reshape(-1,1)
print(BCE(y,yhat))
八、逻辑回归代码练习
1、创建分类数据集生成器
# 创建初始标记值
num_inputs = 2 # 数据集特征(2列)
num_examples = 500 # 每一类样本数(500行)
#np.random.normal()生成服从指定均值方差的数组
np.random.seed(24)
data0 = np.random.normal(3, 2, size=(num_examples, num_inputs))
data1 = np.random.normal(-3, 2, size=(num_examples, num_inputs))
a = np.random.normal([0, 1], [1, 2], size=(num_examples, num_inputs)) #第一列服从均值为0方差为1,第二列服从均值为1方差为2
#print(a)
#二分类标签
label0 = np.zeros(500)
label1 = np.ones(500) #一般重点学习对象类别为1
features = np.concatenate((data0, data1), 0)
labels = np.concatenate((label0, label1), 0)
print(features)
plt.scatter(features[:, 0], features[:, 1], c = labels)
plt.show()
方差(及标准差)其实表示数据的离散程度,方差越大、数据距离中心点的离散程度就越大。
def arrayGenCla(num_examples=500,num_input=2,num_class=3,deg_dispersion=[4,2],bias=False):
'''
分类数据集创建函数
:param num_examples: 每个类别的数据数量
:param num_input: 数据集特征数量
:param num_class: 数据集标签类别总数
:param deg_dispersion:数据分布离散程度参数,[a,b],第一个参数为每个类别数组均值的参考第二个参数表示随机数组标准差
:param bias:建立模型逻辑回归模型时是否带入截距,为True时将添加一列取值全为1的列
:return:生成的特征张量和标签张量,其中特征张量是浮点型二维数组,标签张量是长正型二维数组。
'''
cluster_l=np.empty([num_examples,1]) # 每一类标签数组的形状
mean_=deg_dispersion[0] # 每一类特征数组的均值的参考值
std_=deg_dispersion[1] # 每一类特征数组的方差
lf = [] # 用于存储每一类特征的列表容器
ll = [] # 用于存储每一类标签的列表容器
k=mean_*(num_class-1)/2 # 每一类特征均值的惩罚因子
for i in range(num_class):
data_temp=np.random.normal(i*mean_-k,std_,size=(num_examples,num_input)) # 生成每一类特征
lf.append(data_temp) # 将每一类特征添加到lf中
labels_temp = np.full_like(cluster_l, i) # 生成某一类的标签
ll.append(labels_temp)
features = np.concatenate(lf)
labels = np.concatenate(ll)
if bias == True:
features = np.concatenate((features, np.ones(labels.shape)), 1) # 在特征张量中添加一列全是1的列
return features, labels
2、逻辑回归的梯度计算
def sigmoid(x):
return (1 / (1 + np.exp(-x)))
def logit_gd(X, w, y):
"""
逻辑回归梯度计算公式
"""
m = X.shape[0]
grad = X.T.dot(sigmoid(X.dot(w)) - y) / m
return grad
3、逻辑回归的代码实现
数据准备:数据集切分(训练集,测试集),数据归一化
# 设置随机数种子
np.random.seed(24)
# 数据切分
Xtrain, Xtest, ytrain, ytest = array_split(f, l)
mean_=Xtrain[:,:-1].mean(axis=0) #计算训练集中两列特征的均值和方差
std_=Xtrain[:,:-1].std(axis=0)
#Z-Score标准化
Xtrain[:,:-1]=(Xtrain[:,:-1]-mean_)/std_
Xtest[:,:-1] = (Xtest[:,:-1]-mean_)/std_
初始化参数
# 设置随机数种子
np.random.seed(24)
# 参数初始值
n = f.shape[1]
w = np.random.randn(n, 1)
# 核心参数
batch_size = 50
num_epoch = 200
lr_init = 0.2
lr_lambda = lambda epoch: 0.95 ** epoch #定义匿名函数来执行学习率衰减
训练模型
for i in range(num_epoch):
w = sgd_cal(Xtrain, w, ytrain, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
print(w)
对于逻辑回归来说,在给出线性方程参数后,模型输出的概率结果为:
def logit_cla(y_hat,thr=0.5):
'''
逻辑回归类别输出函数
:param y_hat: 模型输出结果
:param thr: 阈值
:return: 类别判别结果
'''
ycla=np.zeros_like(y_hat)
ycla[y_hat>=thr]=1
return ycla
#模型输出的概率结果
yhat = sigmoid(Xtrain.dot(w))
#分类输出逻辑回归预测结果
y=logit_cla(yhat, thr=0.5)[:10]
print(y)
计算训练集和测试集上的准确率
def logit_acc(X, w, y, thr=0.5):
yhat = sigmoid(X.dot(w))
y_cal = logit_cla(yhat, thr=thr)
return (y_cal == y).mean()
# 训练集准确率
print(logit_acc(Xtrain, w, ytrain, thr=0.5))
# 测试集准确率
print(logit_acc(Xtest, w, ytest, thr=0.5))
4、模型准确率变化函数
# 设置随机数种子
np.random.seed(24)
# 参数初始值
n = f.shape[1]
w = np.random.randn(n, 1)
# 记录迭代过程模型准确率计算结果
train_acc = []
test_acc = []
for i in range(num_epoch):
w = sgd_cal(Xtrain, w, ytrain, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
train_acc.append(logit_acc(Xtrain, w, ytrain, thr=0.5))
test_acc.append(logit_acc(Xtest, w, ytest, thr=0.5))
# 观察计算结果
plt.plot(list(range(num_epoch)), np.array(train_acc).flatten(), label='train_acc')
plt.plot(list(range(num_epoch)), np.array(test_acc).flatten(), label='test_acc')
plt.xlabel('epochs')
plt.ylabel('Accuracy')
plt.legend(loc = 4)
plt.show()
逻辑回归在分类模型中通常并不作为第一选择,因为其对于复杂数据的判别能力并不是很强。但它有很好的可解释性,可用于特征选择。