什么是逻辑回归
线性回归预测的是一个连续值,逻辑回归给出的”是”和“否”的回答。逻辑回归用到了一个sigmoid函数,将一个连续的变量映射到概率空间(0~1)。
sigmoid函数是一个概率分布函数 ,给定某个输入,它将输出为一个概率值。
可以把输出值看做一个概率值,这个时候就能用sigmoid激活函数的输出来做分类问题的输出。
当我们输出一个概率值的时候,获得了一个分类输出,那我们如何来做出惩罚呢?或者说如何计算它的损失呢?
前面单变量线性回归的时候,我们使用的是均方误差。即预测值与真实值之间它们差距的平方再求均值。
现在,我们对这个输出添加了一个sigmoid激活函数,使得这种连续输出转变为概率输出,这个时候再使用均方误差输出虽然可以,但是计算量级很小,可能预测结果是0.9,真实值是1,它们之间差距只有0.1,这个值和原数据之间不在真正的一个数量级上。所以,对于分类损失,最好使用交叉熵损失,来增大它的损失。使用交叉熵会输出一个更大的损失,从而对原有的参数做出更大的惩罚(改变),使得它更快的优化。
交叉熵损失函数
交叉熵刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。假设概率分布p为期望输出,概率分布q为实际输出,H(p,q)为交叉熵 ,则:
L2就是代表均方误差损失,logistic损失就是交叉熵损失。当实际结果为0,输出为1;实际结果为1,输出为0的时候,经过log运算,会使得损失值非常大。 在计算分类问题的时候,使用交叉熵损失,因为他会放大分类损失的损失值。交叉熵损失也是度量两个概率分布之间距离的时候的一种方法。
在pytorch里,我们使用nn.BCELoss()来计算二元交叉熵
使用信用卡欺诈数据来做逻辑回归
#引入必要的库
import torch
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 读取数据集
# 可以看到,它把第一行当成了表头。实际上没有表头,所以要用header=None传参,# header=None表示没有表头。
data = pd.read_csv('../daatset/credit-a.csv',header=None)
data
结果:
可以看到,该数据集一共653行16列。前15列是特征值,第16列是目标(标签) 。
# 前15列是特征,最后一列是输出。data.iloc是pandas的方法,按位置取值
X = data.iloc[:,:-1]
Y = data.iloc[:,-1]
Y.unique() # 查看Y中不重复的元素,二分类,我们想把它分类为0和1
结果:
Y = data.iloc[:,-1].replace(-1,0) # 使用replace 方法将-1替换为0
Y.unique()
数据预处理:
现由于是pandas读取数据,所以取出其中的values转换为numpy的ndarray,再torch.from_numpy转换为tensor。
X = torch.from_numpy(X.values).type(torch.float32) # tensor转换数据类型用 .type 方法。要计算它必须使用float类型
X.shape
结果:
Y.values # 返回的是单个的array,所以需要reshape一下成 653个长度为1的向量,与653个特征一一对应
结果:
Y = torch.from_numpy(Y.values.reshape(-1,1)).type(torch.float32)
Y.shape
结果:
使用nn模块创建模型 ,初始化损失函数、优化方法。
我们可以使用 nn.sequential() 方法创建模型,它可以将多个层添加在一起形成一个模型。Sequential可以将多个层添加在一起形成一个模型;适用的场景就是顺序连接,也即第一个层的输出交给第二层处理.....
当模型是多个层顺序连接的时候,推荐使用sequentialz这个方法快速创建模型。
from torch import nn
model = nn.Sequential(
# X.shape: torch.Size([653, 15]),每个样本有15个特征,所以Linear的输入长度就是15,输出长度是1
nn.Linear(in_features=15,out_features=1), #这个层的输出交给下面的sigmoid层处理
nn.Sigmoid()
)
loss_fn = nn.BCELoss() # BCELoss是二元交叉熵损失,对应的标签就是0和1这样的标签
# 优化函数。SGD是随机梯度下降,它不会考虑前面下降了多少;Adam它会考虑前面下降了多少,对它做加权
opt = torch.optim.Adam(model.parameters(),lr=0.001) # torch.optim里面集成了很多优化方法,Adam是最常用的一种优化方法,
model
结果:
开始训练:
batchSize = 16 # 批次大小
num_batch = 653//batchSize # 将全部数据训练一遍需要几个批次
epoches = 500 # 将全部数据训练500次
for epoch in range(epoches):
for batch in range(num_batch): # 把全部数据训练一遍
start = batch*batchSize
end = start+batchSize
x = X[start:end]
y = Y[start:end]
y_pred=model(x) # 进行预测。y_pred是一个长度为16的向量,分别是输进去的16个样本的预测值
loss = loss_fn(y_pred,y) # 二元交叉熵损失,刻画的是实际输出(概率)与期望输出(概率)的距离
# 把模型中所有的变量的grad置为0
opt.zero_grad() # 这个方法会把所有需要优化的参数(即初始化时传进去的模型的参数)的梯度置为0
loss.backward() # 反向传播,计算每一个变量的梯度
opt.step() # 使用优化方法对参数进行优化
# 模型的权重和偏执。15个weight,还有一个偏执。
model.state_dict() # 这个模型所做的事情,sigmoid(w1*x1+w2*x2+....+w15*x15+b)
结果:
# model(x).data.numpy()返回的是一个概率值,我们认为>0.5就是输出为1,否则输出为0。
(model(X).data.numpy() > 0.5).astype('int') # astype是numpy转换数据类型的方法
(model(X).data.numpy() > 0.5).astype('int') == Y.numpy() # 实际预测结果与真实结果相比较,就能知道出哪个正确哪个错误了
((model(X).data.numpy() > 0.5).astype('int') == Y.numpy()).mean() # 模型正确率
正确率:
多层感知器