Part3.第7章

第7章

一元逻辑回归

千万不要被逻辑回归里的“回归”误导,逻辑回归是一个分类问题的算法。
Sigmoid函数:
在这里插入图片描述
在这里插入图片描述
一元逻辑回归的假设函数
通过上边的学习,我们知道一元逻辑回归模型的假设函数为:
在这里插入图片描述
其中的w和b参数会对标准的Sigmoid函数曲线进行平移,翻转,缩放等。最终将输出映射到[0,1]。
一元逻辑回归就是在线性回归的基础上,增加了一个Sigmoid函数。这个Sigmoid函数叫做激活函数。这个非线性的函数我们就叫做激活函数。激活函数的作用就是在线性函数的基础上增加了非线性。

逻辑回归的损失函数

二分类交叉熵损失函数BCELoss(Binary Cross Entropy Loss)分为两种情况。
在这里插入图片描述
我们结合函数图像来对BCELoss进行理解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多元逻辑回归

在这里插入图片描述
通过构造高次项的特征就可以让线性回归拟合曲线。同样的,这里通过给逻辑回归里的线性回归部分增加高次项,同样可以解决曲线决策边界的问题;如果你增加更多的高次项,理论上逻辑回归的决策边界可以是任意曲线。
逻辑回归只能解决二分类问题吗
在这里插入图片描述

数据集的划分

实际上数据进行划分,会将数据划分为训练集,验证集测试集

训练集 训练集的作用就是训练模型,训练集上的数据你会使用很多次,跑很多迭代。用它来调整模型的参数。
验证集 验证集是帮助你在训练过程中评估模型的性能,它并没有被拿来训练模型。根据验证集的评估结果,你可以调整模型的超参数,比如调整学习率,或者增加特征的高次项等。 验证集的数据你使用的比训练集要少。只在模型训练到一定阶段,评估模型的性能表现。
测试集 测试集是你训练好了模型,最后只使用一次,评价模型的最终真实表现的。

欠拟合和过拟合

模型在训练中的状态分为三种,一种是欠拟合,一种是适度拟合,一种是过拟合。

梯度下降算法的改进

(1)批量梯度下降(Batch Gradient Descent)
梯度是利用所有训练数据求得每个参数的平均梯度,它准确的反应了模型在整个训练数据集上的改进方向。优点很明显,就是训练稳定,收敛方向准确。但是缺点是不利于模型探索不同的参数空间,可能会陷入局部最小值。 但是当数据量很大时,导致训练非常慢。
(2)随机梯度下降(Stochastic Gradient Descent)
它每次只从训练集里取一个样本,然后计算loss,反向传播,计算梯度,更新模型参数。 它的优点是计算快,而且因为单个数据随机性很强,每次都是不同的数据在更新参数,可以帮助模型训练快速逃出局部最小值。但是它的缺点也很明显,就是训练波动大,不稳定。
(3)小批量梯度下降(Mini-batch Gradient Descent)
每次从训练数据集里取一小部分样本,用来训练。这样一次训练叫做一个step。而通过每次取一小部分样本(叫做一个batch),把训练集所有数据都训练一遍叫做一个epoch(迭代)。

BatchSize怎么选择
一是因为内存/显存是按照2的幂分块的,比如8,16,32,64字节一块。如果batch size也是2的幂,则数据在存储中更容易对齐,减少读取次数,读取效率更好。二是CUDA架构中,一个执行单元是32个线程,执行效率在batch size是32或其倍数时最高。

泰坦尼克号生存预测

原文链接
独热编码(One-hot Encoding),今天介绍一种将离散特征转变成连续特征的编码方式,这就是独热编码。对于独热编码而言,每个离散列,假如有N个可能的取值,就新生成N列。而且对于每个样本,在这N个列里,只有一列取值为1,其他列取值都为0,这也是为什么它被叫做独热编码的原因。
示例代码:

import pandas as pd

pd.set_option('display.max_columns', None)#打印时显示所有列。
# 从CSV文件读取数据(确保你有正确的路径)
df = pd.read_csv(r"<YOUR_DATA_PATH>\titanic\train.csv")
# 去除不需要的列
df = df.drop(columns=["PassengerId", "Name", "Ticket", "Cabin"])
# 去除 Age 缺失的样本
df = df.dropna(subset=["Age"])
# 对 Sex 和 Embarked 做独热编码
df = pd.get_dummies(df, columns=["Sex", "Embarked"],dtype=int)
print(df.head(10))

PyTorch里的Dataset和DataLoader

原文链接
(1)Dataset
Dataset类提供对数据集的抽象,任何自定义数据集都需要继承torch.utils.data.Dataset,并实现两个方法:len__和__getitem(idx)。其中__len__需要返回整个数据集样本的个数。getitem(idx)需要能根据样本的index返回具体的样本。
另外一般情况下,我们也把对数据的预处理工作放在自定义的Dataset类定义里。
示例代码如下:

from torch.utils.data import Dataset
import pandas as pd
import torch


class TitanicDataset(Dataset):
    def __init__(self, file_path):
        self.file_path = file_path
        self.mean = {
            "Pclass": 2.236695,
            "Age": 29.699118,
            "SibSp": 0.512605,
            "Parch": 0.431373,
            "Fare": 34.694514,
            "Sex_female": 0.365546,
            "Sex_male": 0.634454,
            "Embarked_C": 0.182073,
            "Embarked_Q": 0.039216,
            "Embarked_S": 0.775910
        }

        self.std = {
            "Pclass": 0.838250,
            "Age": 14.526497,
            "SibSp": 0.929783,
            "Parch": 0.853289,
            "Fare": 52.918930,
            "Sex_female": 0.481921,
            "Sex_male": 0.481921,
            "Embarked_C": 0.386175,
            "Embarked_Q": 0.194244,
            "Embarked_S": 0.417274
        }

        self.data = self._load_data()
        self.feature_size = len(self.data.columns) - 1

    def _load_data(self):
        df = pd.read_csv(self.file_path)
        df = df.drop(columns=["PassengerId", "Name", "Ticket", "Cabin"]) ##删除不用的列
        df = df.dropna(subset=["Age"])##删除Age有缺失的行
        df = pd.get_dummies(df, columns=["Sex", "Embarked"], dtype=int)##进行one-hot编码

        ##进行数据的标准化
        base_features = ["Pclass", "Age", "SibSp", "Parch", "Fare"]
        for i in range(len(base_features)):
            df[base_features[i]] = (df[base_features[i]] - self.mean[base_features[i]]) / self.std[base_features[i]]
        return df

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        features = self.data.drop(columns=["Survived"]).iloc[idx].values
        label = self.data["Survived"].iloc[idx]
        return torch.tensor(features, dtype=torch.float32), torch.tensor(label, dtype=torch.float32)

(2)DataLoader
PyTorch里默认实现的DataLoader就可以满足我们的使用,它定义了如何批量读取数据的功能。比如你可以通过batchsize设置每次读取数据的大小,通过shuffle参数设置是否对数据集进行打乱。另外如果你的Dataset的_getitem__比较费时,你可以通过num_workers参数指定多进程加载。

from torch.utils.data import DataLoader

dataset = TitanicDataset(r"E:\电子书\RethinkFun深度学习\data\titanic\train.csv")
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
for inputs, labels in dataloader:
    print(inputs.shape, labels.shape)
    break

利用PyTorch定义逻辑回归模型

nn.Module
在这里插入图片描述
实现逻辑回归模型
在这里插入图片描述
逻辑回归模型的实现代码如下:

class LogisticRegressionModel(nn.Module):
    def __init__(self, input_dim):
        super().__init__()
        self.linear = nn.Linear(input_dim, 1)  # nn.Linear也继承自nn.Module,输入为input_dim,输出一个值

    def forward(self, x):
        return torch.sigmoid(self.linear(x))  # Logistic Regression 输出概率

其中nn.Linear是定义一个线性回归模型。它传入两个参数,第一个是输入特征的个数。第二个是输出特征的个数,你可能奇怪线性回归输出的个数不就是1吗,为什么还可以是其他值?实际上这里的nn.Linear是一个线性层。它可以一次性定义多个线性回归,所以可以有多个输出,当我们设置输出值为1时,那么就是我们之前讲的线性回归了。

用逻辑回归对Titanic数据进行训练

PyTorch里的优化器
PyTorch里专门提取出了优化器(Optimizer)类,它主要有以下的功能:
(1)管理参数,对参数进行更新;
(2)封装对参数进行梯度更新的算法;
(3)提供在训练过程中管理学习率的接口;
使用SGD优化器的伪代码如下:

## 定义优化器,传入模型的参数,并且设置固定的学习率
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

for epoch in range(epochs):
    for features,labels in DataLoader(myDataset, batch_size=256):
        optimizer.zero_grad() ##优化器清理管理参数的梯度值。
        loss = forward_and_compute_loss(features, labels) ##前向传播并计算loss
        loss.backward() ##loss反向传播,每个参数计算梯度
        optimizer.step() ##优化器进行参数更新

上边的代码中optimizer.zero_grad()你可能会疑惑,这里做一下解释。每个可训练的参数都有一个关联的梯度值。每次loss的后向传播时,新计算的梯度值都会累加在原来每个参数的梯度上。直到调用optimizer.zero_grad(),才会将这个优化器管理的参数对应的梯度值设置为0。
在每一个训练的step里:
(1)optimizer把自己管理参数的梯度值都置为0;
(2)模型前向传播,计算loss;
(3)loss反向传播,计算出每个参数的梯度值;
(4)optimizer根据每个参数的梯度值,和优化器算法,学习率来更新参数;

你可能会奇怪为什么需要通过model.train()把模型设置为训练模式,这是因为有的模型在训练时和预测时表现是不一样的。模型需要知道自己目前是在训练状态还是预测状态。逻辑回归模型在训练和预测状态下表现是一致的,后边我们学习到的批量归一化Dropout模块它们在训练和预测是工作模式是不同的。

LR回归代码详解

part1.定义LR模型

源代码:

class LogisticRegressionModel(nn.Module):
    def __init__(self, input_dim):
        super().__init__()
        self.linear = nn.Linear(input_dim, 1)  # nn.Linear也继承自nn.Module,输入为input_dim,输出一个值

    def forward(self, x):
        return torch.sigmoid(self.linear(x))  # Logistic Regression 输出概率

解析:

类定义与继承

在这里插入图片描述

定义线性层

在这里插入图片描述

前向传播方法forward

在这里插入图片描述

part2.LR模型训练

源代码:

model = LogisticRegressionModel(train_dataset.feature_size)
model.to("cuda")
model.train()

optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

epochs = 100

for epoch in range(epochs):
    correct = 0      # 累计正确预测数
    step = 0         # 累计batch数(用于计算平均损失)
    total_loss = 0   # 累计总损失
    for features, labels in DataLoader(train_dataset, batch_size=256, shuffle=True):
        step += 1
        features = features.to("cuda")
        labels = labels.to("cuda")
        optimizer.zero_grad()  # 清除历史梯度(避免梯度累积)
        outputs = model(features).squeeze()  # 前向传播+压缩冗余维度
        # 计算准确率(需注意数据类型匹配)
        correct += torch.sum(((outputs >= 0.5) == labels).float())  
        loss = torch.nn.functional.binary_cross_entropy(outputs, labels)
        total_loss += loss.item()  # 记录标量损失值
        loss.backward()            # 反向传播计算梯度
        optimizer.step()           # 更新模型参数
    # 计算并打印当前epoch指标
    print(f'Epoch {epoch + 1}, Loss: {total_loss/step:.4f}')
    print(f'Training Accuracy: {correct / len(train_dataset):.4f}')

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

part3.LR模型评估

源代码

model.eval()
with torch.no_grad():
    correct = 0
    for features, labels in DataLoader(validation_dataset, batch_size=256):
        features = features.to("cuda")
        labels = labels.to("cuda")
        outputs = model(features).squeeze()
        correct += torch.sum(((outputs >= 0.5) == labels))
    print(f'Validation Accuracy: {correct / len(validation_dataset)}')

解析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值