深度学习之逻辑回归建模

373 阅读6分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

一 建模流程

逻辑回归相对于线性回归来说,就是输出层上进行了sigmoid的激活函数处理,从而使线性加权求和汇总进行了0,1放缩。其建模流程也分为四步:

  • 模型选择:在深度学习领域,模型选择过程就是确定神经网络的基本结构,确定神经网络的层数,每一层神经元的个数及选择激活函数
  • 确定目标函数:在确定模型基本机构之后,根据模型实际情况确定目标函数,构建一个包含模型参数的函数工程,且方程取值和建模目的一致,大多数情况下,我们是求解方程的极小值
  • 选择优化方法:围绕目标函数进行最小值求解,根据损失函数的函数特性,兼顾实际算力的消耗,选择最优的工具,
  • 模型训练:将模型训练到可用,利用优化方法,求解损失函数,并得到一组模型函数,对应在神经网络中,得到一组连接神经元的参数取值。
import random 
import matplotlib as mpl
import matplotlib .pyplot as plt
import numpy as np
import torch
from torch import nn,optim
import torch.nn.functional as F
from torch.utils .data import Dataset,TensorDataset,DataLoader
from torch.utils.tensorboard import SummaryWriter
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity ='all'

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
#分类数据集的创建函数
def tensorGenCla(num_examples=500,num_inputs=2,num_class=3,deg_dispersion=[4,2],bias=False):
    """
    param num_examples:每个类别的数据数量
    Parma num_inputs:数据集特征数量
    param num_class:数据集标签类别总数
    param deg_dispersion:数据分布离散程度参数,需要输入一个列表,其中第一个参数表示每个类别的均值
    第二个参数表示随机数标准差
    param bias:逻辑回归模型时是否有截距
    
    """
    cluster_l=torch.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=torch.normal(i*mean_-k,std_,size=(num_examples,num_inputs))
        lf.append(data_temp)
        labels_temp=torch.full_like(cluster_l,i)  #生产类的标签
        ll.append(labels_temp)
        
    features=torch.cat(lf).float()
    labels=torch.cat(ll).long()
    
    if bias==True:
        features=torch.cat((features,torch.ones(len(features),1)),1)  #添加一列全是1的列
    return features,labels
#创建随机数种子
torch.manual_seed(420)
features,labels=tensorGenCla(num_class=2,bias=True)  #tensorGenCla定义的分类函数
#可视化展示
plt.scatter(features[:,0],features[:,1],c=labels);
print(features[:5]);

image.png

二 建模过程

2.1 手动建模过程

2.1.1 模型选择

针对二分类问题(0-1问题),我们可以简单的输出一个结果,作为标签取值为1的概率,模型结构图如下:

image.png

2.1.2 定义激活函数

有封装好的sigmoid函数,和我们手动定义的一样

def sigmoid(z):
    return 1/(1+torch.exp(-z))

2.1.3 定义逻辑回归模型函数

逻辑回归再向前传播的时候,我们给出X,w,先进行矩阵的乘法,在进行sigmoid的处理,

def logistic(X,w):
    return sigmoid(torch.mm(X,w))

2.1.4 定义对应分类函数

由于sigmoid输出结果是连续值,而用于二分类判别时,我们需要将连续数值转化为所判定的类别,可定义对应分类函数

def cal(sigma,p=0.5):
    return((sigma>=p).float())

借助了布尔型和数值型数值之间的转化。

2.1.5 定义准确率函数

def accuracy(sigma,y):
    acc_bool=cal(sigma).flatter()==y.flatten()
    acc=torch.mean(acc_bool.float())
    return(acc)

2.1.6 定义损失函数

def accuracy(sigma,y):
    acc_bool=cal(sigma).flatter()==y.flatten()
    acc=torch.mean(acc_bool.float())
    return(acc)

2.1.7 定义优化方法

def sgd(params,lr):
    params.data-=lr*params.grad
    params.grad.zero_()

2.1.8 训练模型

#设置随机数种子
torch.manual_seed(300)
#初始化参数
batch_size=10
lr=0.03
num_epochs=3
w=torch.ones(3,1,requires_grad=True)

#训练的模型方程
net=logistic  #使用逻辑回归方程
loss=cross_entropy  #交叉熵损失函数
#训练过程
for epoch in range(num_epochs):
    for X,y in data_iter(batch_size,features,labels):
        l=loss(net(X,w),y)
        l.backward()
        sgd(w,lr)
    train_acc=accuracy(net(features,w),labels)
    print('epoch %d,  accurach %f'%(epoch+1,train_acc))

image.png

2.2 模型调试

迭代三轮返回的准确率,能够看出整体还在增加,让我们再多迭代几轮查看结果:

torch.manual_seed(300)

#迭代次数
num_epochs=20
#设置初始化权重
w=torch.ones(3,1,requires_grad=True)
#放置列表值
train_acc=[]

for i in range(num_epochs):
    for epoch in range(i):
        for X,y in data_iter(batch_size,features,labels):
            l=loss(net(X,w),y)
            l.backward()
            sgd(w,lr)
    train_acc.append(accuracy(net(features,w),labels))
        
plt.plot(list(range(num_epochs)),train_acc)
plt.grid(alpha=0.3)
print(train_acc)

image.png 增加迭代次数之后,损失函数逼近最小值点,每次迭代梯度取值较小,整体准确率趋于平稳

2.2.1 增加数据离散程度

增加数据内部离散程度,验证对模型是否造成影响

torch.manual_seed(300)

features,labels=tensorGenCla(num_class=2,bias=True,deg_dispersion=[4,6])
plt.scatter(features[:,0],features[:,1],c=labels,cmap='rainbow')

image.png

torch.manual_seed(300)
num_epochs=20
w=torch.zeros(3,1,requires_grad=True)
train_acc=[]
for i in range(num_epochs):
    for epochs in range(i):
        for X,y in data_iter(batch_size,features,labels):
            l=loss(net(X,w),y)
            l.backward()
            sgd(w,lr)
    train_acc.append(accuracy(net(features,w),labels))
        
plt.plot(list(range(num_epochs)),train_acc)
print(train_acc)

image.png 随着迭代次数的增加,准确率维持在66.7%附近震荡,随着数据情况变复杂,离散程度变大,相同模型的准确率发生了很大的变化,这种情况就需要其他的模型优化方法

2.3 调库建模过程

使用PyTorch中的函数和类,进行逻辑回归的快速构建。

2.3.1 定义核心参数

batch_size=10  #每一个小批的数量
lr=0.03 #学习率
num_epochs=3  #遍历的次数

2.3.2 数据准备

torch.manual_seed(300)
features,labels=tensorGenCla(num_class=2)
labels=labels.float()   #损失函数的标签必须是浮点型
data=TensorDataset(features,labels)
batchData=DataLoader(data,batch_size=batch_size,shuffle=True)

2.3.3 定义模型

class logisticR(nn.Module):
    def __init__(self,in_features=2,out_features=1): #定义模型的结构
        super(logisticR,self).__init__()
        self.linear=nn.Linear(in_features,out_features)
        
    def forward(self,x):   #定义损失函数
        out=self.linear(x)
        return out
    
#实例化模型
logic_model=logisticR()

2.3.4 定义损失函数

criterion=nn.BCEWithLogitsLoss()

2.3.5 定义优化方法

optimizer=optim.SGD(logic_model.parameters(),lr=lr)

2.3.6 模型训练

def fit(net,criterion ,optimizer,batchdata,epochs):
    for epoch in range(epochs):
        for X,y in batchdata:
            zhat=net.forward(X)
            loss=criterion(zhat,y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            
            
torch.manual_seed(300)
fit(net=logic_model
   ,criterion=criterion
    ,optimizer=optimizer
    ,batchdata=batchData
    ,epochs=num_epochs
   )

2.3.7 查看模型训练结果

logic_model

image.png

2.3.8 查看模型参数

list(logic_model.parameters())

image.png

2.3.9 计算交叉熵损失

criterion(logic_model(features),labels)

image.png 大多数情况下对交叉熵损失没有一个很清晰的判断,更多的以准确率为判断依据 定义一个没有经过sigmoid处理的准确率函数

def acc_zhat(zhat,y):
    """
    param zhat:线性方程的输出结果
    param y:数据集标签张量
    return :准确率
    """
    sigma=sigmoid(zhat)
    return accuracy(sigma,y)
#准确率
acc_zhat(logic_model(features),labels)

image.png

2.3.10 模型调试

增加迭代次数,查看准确率是否发生变化

torch.manual_seed(300)
features,labels=tensorGenCla(num_class=2)
labels=labels.float()
data=TensorDataset(features,labels)
batchData=DataLoader(data,batch_size=batch_size,shuffle=True)

num_epochs=20
LR1=logisticR()
cr1=nn.BCEWithLogitsLoss()
op1=optim.SGD(LR1.parameters(),lr=lr)

train_acc=[]
for epochs in range(num_epochs):
    fit(net=LR1
       ,criterion=cr1
        ,optimizer=op1
        ,batchdata=batchData
        ,epochs=epochs
       )
    epoch_acc=acc_zhat(LR1(features),labels)
    train_acc.append(epoch_acc)
plt.plot(list(range(num_epochs)),train_acc)
print(train_acc)

image.png 和之前相同,增加数据复杂度测试分类性能

torch.manual_seed(420)   

features, labels = tensorGenCla(num_class=2, deg_dispersion=[4, 6])                     
labels = labels.float()                           
data = TensorDataset(features, labels)            
batchData = DataLoader(data, batch_size = batch_size, shuffle = True)
plt.scatter(features[:, 0], features[:, 1], c = labels,cmap='rainbow')

image.png

#创建数据
torch.manual_seed(420)   

# 数据封装与加载
data = TensorDataset(features, labels)            
batchData = DataLoader(data, batch_size = batch_size, shuffle = True)

# 初始化核心参数
num_epochs = 20
LR1 = logisticR()
cr1 = nn.BCEWithLogitsLoss()
op1 = optim.SGD(LR1.parameters(), lr = lr)

# 创建列表容器
train_acc = []

# 执行建模
for epochs in range(num_epochs):
    fit(net = LR1, 
        criterion = cr1, 
        optimizer = op1, 
        batchdata = batchData, 
        epochs = epochs)
    epoch_acc = acc_zhat(LR1(features), labels)
    train_acc.append(epoch_acc)

# 绘制图像查看准确率变化情况
plt.plot(list(range(num_epochs)), train_acc)
print(train_acc)

image.png 和上面一样,准确率在0.64-0.65之间震荡。