前言

​ 为什么又要写这个呢?😔弄个论文模板以防万一不让省心的论文队友啊

Logistic 介绍及应用领域

​ 逻辑回归是一个比较经典的分类算法,可以说也是机器学习的入门必需课。虽然现在有许多分类的方法比如决策树、svm、神经网路等,但是逻辑回归仍应用领域广泛。

贷款违约情况(是否违约

广告点击情况(是否会点击

商品推荐(是否会购买

情感分析(正面情感or负面情感

疾病诊断(阴性还是阳性

逻辑回归的性质

​ 逻辑回归本质是一个条件概率$P(Y|X)$即在X条件下情况为Y的概率是多少,X可以理解为特征。

既然是条件概率,那一定满足概率的定义:

  • $0\leq P(Y|X)\leq 1$
  • $\sum_y P(Y|X)=1$

由此显而易见,线性回归$P(Y|X)=w^Tx+b$ 不能是逻辑回归。但是通过任意函数将P(Y|X)映射到[0,1]都可称为逻辑回归,最常用的就是逻辑函数。

逻辑函数

这个简单又明了的就是大名鼎鼎的逻辑函数,在神经网络里也叫sigmod函数。他将$x\in (-\infty,\infty)$映射到(0,1)

这就满足了概率的条件。即$P(Y|X)=\frac{1}{1+e^{-{w^Tx+b}}}$

逻辑回归是线性的

​ 逻辑回归是否线性应该从其决策边界来判断:

可见其决策边界是线性的。

Pytorch 实现

loss函数|目标函数

由于:

可以统一写成:

从而我们只需要求解:

上式也叫目标函数

预测评估方法

当样本种类不均衡时,准确率可能就没有那么客观,所以需要其他评价标准

对于一个分类模型,假设二分类,则一定有如下表格(1):

Correct 纵列之和为真实label=1的总量。

Not Correct 纵列之和为真实label=0的总量

Correct Not Correct
Selected True Positive False Positive
Not Selected False Negative True Negative

精确率(precision)

预测正样本中 ground truth 的比例:

召回率 (recall)

ground truth中预测正确的比例:

F1-score

通过召回率和精确率综合评判模型的指标:

mAP(mean Average Precision)

数据参考:目标检测中的mAP是什么含义- 资质平平的AI的回答 - 知乎

​ 对于目标检测类问题,总是会在评估里面看到mAP。从字面上来看就是precision的平均,但是怎么样平均呢?

​ 首先还是介绍一下目标检测里的正样本:对于一个Bounding Box,IOU>0.5则认为是一个正样本GT=1,否则为负样本。

​ 按照confident 从大到小(数值相同序号小的在前没影响)然后就可以列出类似下表:

假设表为全部的Bounding Box,且GroundTruth总和为3

实际上GroundTruth总和是IOU>thresh的样本总和

Rank Image id BoundBox预测概率 (confident) GroundTruth Sum of TP
1 5 0.95 1 1
2 7 0.95 0 1
3 3 0.91 1 2
4 1 0.88 0 2
5 6 0.84 0 2
6 1 0.80 0 2
7 4 0.78 0 2
8 2 0.74 0 2
9 2 0.71 0 2
10 1 0.70 1 3

根据confident的离散排列,可绘制多个上表 (1),下举几个例子方便理解。

confident_rank3 第三行之前包括第三行 预测为selected,否则为not selected。

Correct Not Correct
Selected 2 1
Not Selected 1 6

此时 precision=0.66,recall=2/3=0.66

confident_rank5 第五行包括第五行 同上规律列出下表

Correct Not Correct
Selected 2 3
Not Selected 1 4

precision=0.4,recall=2/3=0.66

由前面两个例子和表,可得如下规律:

通过上式可得到各个rank的精确率和召回率

Rank Precision Recall
1 1/1=1 1/3=0.33
2 1/2=0.5 1/3=0.33
3 2/3=0.66 2/3=0.66
4 2/4=0.50 2/3=0.66
5 2/5=0.40 2/3=0.66
6 2/6=0.33 2/3=0.66
7 2/7=0.2857 2/3=0.66
8 2/8=0.25 2/3=0.66
9 2/9=0.222 2/3=0.66
10 3/10=0.33 3/3=1

分别以$recall \geq [0,0.1 \ldots ,0.9,1] $​​​ 为横坐标,满足recall范围的最大Precision为纵坐标画图。

Precision(recall[i])是通过插值法求出的近似值

分别以$recall\in[0.33,0.66.1] $​​​​​​​ 为横坐标,对于每个recall[i],纵坐标为满足大于recall[i]范围的最大Precision,mAP即为直线下的’面积’:

横坐标选取根据计算出的recall去重得到。

MAP(Maximum A Posteriori)

这个与前面完全不同可能也有点关系,注意m的大小写代表完全不同的含义!!

代码

就随便一写,数据自己看着造吧,precision好低

# -*- coding:utf-8 -*-
# @Author : Dummerfu
# @Contact : https://github.com/dummerchen
# @Time : 2021/7/26 15:31
import torch
from torchvision import transforms
from tqdm import tqdm
import numpy as np
import pandas as pd
from sklearn.metrics import precision_score,recall_score
import matplotlib as mpl
from matplotlib import pyplot as plt

mpl.rcParams['font.sans-serif'] = 'SimHei'
mpl.rcParams['axes.unicode_minus'] = False

class Logistic(nn.Module):
def __init__(self,inputs):
super(Logistic, self).__init__()
self.fc1=nn.Linear(inputs,1)
self.activation2=nn.Sigmoid()
def forward(self,input):

x=self.fc1(input)
x=self.activation2(x)
return x


class My_Dataset(Dataset):
def __init__(self,data,transforms=None):
self.data=data
self.transforms=transforms
def __len__(self):
return len(self.data)
def __getitem__(self, item):
x=self.data[item,:-1]
label=self.data[item,-1:]
x=torch.tensor(x,dtype=torch.float)
label=torch.tensor(label,dtype=torch.float)
# if self.transforms!=None:
# x=self.transforms(x)
return x,label



def main(Batchsize=1):
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('use ', device)

fea = pd.read_excel('./fea1.xlsx')
# with open('./data.txt','r') as f:
# data=f.readlines()
data = fea.to_numpy()
tot,dim=data.shape
train_size=int(tot*0.7)
val_size=tot-train_size
data = My_Dataset(data=data)
train_data, val_data = torch.utils.data.random_split(data, [train_size, val_size])

train_loader = torch.utils.data.DataLoader(train_data, batch_size=Batchsize, num_workers=0, shuffle=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=Batchsize, num_workers=0, shuffle=True)

model = Logistic(dim-1)
params = [i for i in model.parameters()]
# optimizer = torch.optim.SGD(params=params, lr=0.03,momentum=0.9, weight_decay=0.0005)
optimizer=torch.optim.Adam(params=params,lr=0.003)
# loss_func=Loss_func()
loss_func=torch.nn.BCELoss()
Epoch = int(1e5+2)

for epoch in range(Epoch):
model.train()
running_loss = 0

for data in train_loader:
x, label = data
pre_logits_list = model(x)
loss = loss_func(pre_logits_list,label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss = loss.item() + running_loss

if epoch%100==0:
model.eval()
with torch.no_grad():

acc=0
true_label=[]
pre_label=[]
for data in val_loader:
x,label=data
pre_logits_list=model(x)
predict_y =pre_logits_list.ge(0.5).float()
# precisious F1_score
acc += torch.eq(predict_y,label).sum().item()
pre_label+=predict_y
true_label+=label
# w0, w1 = model.fc1.weight[0]
# w0 = float(w0.item())
# w1 = float(w1.item())
# b = float(model.fc1.bias.item())
# plot_x = np.arange(-7, 7, 0.1)
# plot_y = (-w0 * plot_x - b) / w1
# num = fea.to_numpy()
# plt.scatter(num[:, 0], num[:, 1], c=num[:, 2])
# plt.plot(plot_x, plot_y)
# plt.show()
print('loss:', running_loss)
print('epoch:',epoch,'acc:',acc/val_size)
pre_score=precision_score(y_pred=pre_label,y_true=true_label,average='binary')
re_score=recall_score(y_true=true_label,y_pred=pre_label,average='binary')
try:
F1_score=2*pre_score*re_score/(pre_score+re_score)
print('精确率:', pre_score, '召回率:', re_score, 'F1score:', F1_score)
except:
pass


if __name__ == '__main__':
main(Batchsize=32)