多层感知器模型与模型训练

news2024/10/6 16:25:12

文章目录

  • 1. 多层感知器模型与模型训练
    • 1.1 多层感知器模型
    • 1.2 损失函数
    • 1.3 优化器与初始化模型
    • 1.4 编写训练循环
    • 1.5 案例代码整合

最近看了日月光华老师的《PyTorch深度学习简明实战》,将里面的代码自己动手复现了一遍,还是受益良多,书里面的代码个别有点小问题,这里是我整理的一份学习笔记记录。

1. 多层感知器模型与模型训练

1.1 多层感知器模型

为了解决MNIST手写数字分类的问题,创建一个简单的多层感知器,这个模型仍然使用torch.nn.Linear层创建。

torch.nn.Linear层是全连接层,本质上就是对全部输入加权求和,它要求输入数据集的形状为一维的,如果使用批量运算,则增加一个batch维,也就是需要输入数据是二维的形状,第一维是batch维,第二维是数据特征,即(batch_size,feature_length)形式。通过打印train_dl中迭代的数据集的形状,train_dl返回每个批次的imgs的形状,即torch.Size([64, 1, 28, 28])。显然这个输入无法直接交给Linear层处理,这里可以对输入使用view()方法更改其形状为二维的,这样就可以直接交给Linear层计算了。本次创建的多层感知器使用两个Linear层作为中间隐藏层,每一层使用ReLU函数激活,最后输出分类数,模型代码如下:

import torch
from torch import nn


class Model(nn.Module):  # 创建模型,继承自nn.Module
    def __init__(self):
        super().__init__()
        #  第一层输入展平后的特征长度 28×28,创建120个神经元
        self.liner_1 = nn.Linear(28 * 28, 120)
        #  第二层输入的是前一层的输出,创建84个神经元
        self.liner_2 = nn.Linear(120, 84)
        #  输出层接收第二层的输入84,输出分类个数10
        self.liner_3 = nn.Linear(84, 10)

    def forward(self, input):
        x = input.view(-1, 28 * 28)
        # 将输入展平为二维,(1, 28,28)→(28*28)
        x = torch.relu(self.liner_1(x))  # 连接第一层liner_1并使用ReLU函数激活
        x = torch.relu(self.liner_2(x))  # 连接第二层liner 2并使用ReLU函数激活
        #  输出层,输出张量的长度,与类别数量一致
        x = self.liner_3(x)
        return x

上述代码中创建了3个Linear层,第一层接收的是view()方法修改形状为二维数据后的图片输入,也就是28×28,会发现我们在初始化层时,不用考虑batch这个维度,因为框架默认第一维是batch维,框架会根据批次大小自动处理,不需要在编写代码时体现,所以第一个Linear层的第一个参数是输入维度的长度,也就是28×28;第二个参数表示输出维度,这个参数是我们自己决定的,这里设置为120。那就是表示第一层会创建120个神经元,也就是输出的维度长度为120。为了方便理解,我们用简单的绘图来表示这一层,Linear层示意图如下图所示:

在这里插入图片描述

输入的维度为784,连接到120个神经元,得到120个输出,这正是第一层self.liner_1所完成的,这里每一个连接都是框架通过随机初始化一个权重和一个偏置进行加权求和实现的。理解了第一个Linear层之后,后面的第二层就很容易理解了,第二层的输入是前一层的输出,也就是输入维度为120,第二层创建84个神经元,所以2个参数是(120, 84),第三个Linear层是输出层,输出层的输出维度大小为分类个数。这里数据集是MNIST手写数字分类数据集,共有10类手写数字图片,所以最后一层参数为(84, 10)。

如何理解这个输出呢?我们在处理多分类问题时,常常使用softmax函数使模型输出C个可能不同的值上的概率(C代表类别数),softmax函数的公式如下:

在这里插入图片描述

模型的返回值为含C个分量的概率向量,每个分量对应一个输出类别的概率。由于输出C个分量为概率,经过softmax函数计算的C个分量之和始终为1。这样,我们想知道预测结果的话,只需要计算一下哪一个类别的概率取值最大,就表示模型预测的是哪一个类别。可以看出,softmax函数相当于一个归一化函数,将输出长度为C的张量归一化为类别概率。细心的读者会发现,上面模型输出并没有使用softmax函数,而是直接输出了长度为C的张量,这是因为我们想要预测的是输出类别,也就是计算哪一个位置代表的这一类输出概率最大,因此,只需要查看输出的张量中哪一个位置的值最大即可,即没必要使用softmax函数归一化这些值也能直接得到预测结果。

1.2 损失函数

下面重点讲解分类模型的损失函数。在处理分类问题时,继续使用解决线性回归问题的均方误差作为损失函数当然也是可以的,但效果不是很好。如果我们将输出使用softmax函数计算,那么分类问题的输出是一个分布概率,也就是在C个可能不同类别上的分布概率,我们设计的损失函数应该体现模型输出的概率分布与实际的概率分布之间的损失。均方误差所惩罚的是与损失为同一数量级的情形,在分类问题上使用均方误差并不合适,例如一个三分类问题,模型的输出是一个类似(0.2,0.3, 0.5)这样的概率分布,而实际的标签是(0, 1, 0),显然两者之间的均方误差损失太小,根据损失反向传播优化参数会特别的慢,甚至不能训练。对于这种概率分布类型的问题,一般采取如下的交叉熵(cross entropy)损失函数会更为有效。单个样本的交叉熵损失计算公式如下:

在这里插入图片描述

公式中各参数的含义如下。

  • C——类别数。

  • log——对数运算。

  • yo,n——符号函数(0或者1),如果样本o的真实类别等于n,取1,否则取0。

  • po,n——观测样本o属于类别n的预测概率。

根据以上公式对所有样本计算损失后,求平均的结果就是交叉熵损失。交叉熵相比均方误差更适合度量概率分布损失,当概率分布差异较大时,它可以输出了一个更大的损失值(惩罚),甚至接近于无穷大,从而使模型参数更新更快、学习速度更快。交叉熵损失函数的放大作用使得训练完成后,模型不太可能做出错误预测。因此,交叉熵更适合作为分类模型的损失函数。总之,当分类问题结合交叉熵作为损失函数时,它可以放大分类损失,在模型效果差的时候学习速度比较快,在模型效果好的时候损失变小,学习速度变慢。PyTorch内置了计算交叉熵损失的函数,初始化交叉熵损失函数的代码如下:

loss_fn = nn.CrossEntropyLoss()    # 初始化交叉熵损失函数

该损失函数结合了nn.LogSoftmax()和nn.NLLLoss()两个函数,读者可以认为它融合了softmax计算和交叉熵损失计算,这也正好说明了为什么不在模型的输出那里做softmax计算的原因,就是因为选择nn.CrossEntropyLoss()时,损失函数会在内部对输出进行softmax计算,然后再将得到的概率分布与实际的分布做交叉熵损失计算。nn.CrossEntropyLoss()损失函数在做分类训练时是最常使用的损失函数。此损失函数有两个可能用到的参数:一是weight,使用此参数可给予不同类别以不同的权重,有利于解决类别不均衡的问题;二是ignore_index,此参数用于忽略某一类别带来的损失,常用在图像语义分割中,个别类别我们并不在乎是否可以正确分割,可用此参数忽略其带来的损失。还要特别注意,nn.CrossEntropyLoss()损失函数要求实际类别为数值编码形式,也就是0,1,2,…,C等这样的类别编码形式,而不是独热编码。可以注意到,第4章加载的MNIST数据集的标签正是这种形式,因此可以直接使用此损失函数计算损失。

1.3 优化器与初始化模型

优化是调整模型参数以减少每个训练步骤中的模型误差的过程。优化算法定义此过程的执行方式,所有优化逻辑都封装在优化器对象中。在此示例中,我们使用随机梯度下降优化器,也就是SGD优化器,此外,PyTorch中内置了许多不同的优化器,如Adam和RMSProp等,它们更适用于不同类型的模型和数据,尤其以Adam优化器最常用。PyTorch的优化器均在torch.optim模块下,代码如下:

optimizer = torch.optim.SGD(model.parameters(), lr=0.001)  # 初始化优化器

以上代码初始化了内置的SGD优化器,它有两个最重要的参数。

  • params,表示需要优化的模型参数。可调用模型的model.parameters()方法以生成器形式返回模型中需要优化的参数。

  • lr,表示学习速率(learning rate),类型为float,用来指定优化速率。

初始化模型

下面代码首先获取当前环境可用的训练设备(CPU或GPU),然后初始化前面编写的多层感知器模型,并在初始化模型后设置模型使用当前可用的device。

device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))  # 如果安装了GPu版本,显示Using cuda device
model = Model().to(device)  # 初始化模型,并设置模型使用device

1.4 编写训练循环

前面已经将训练和测试数据、优化器、损失函数和模型等全部准备好了,下面编写训练循环。为了方便以后代码复用,我们将编写一个训练函数train()和一个测试函数test(),在这两个函数中分别对全部训练数据和全部测试数据进行一次训练或测试。下面先来看train()函数的代码,注意阅读代码注释:

def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  #获取当前数据集样本总数量
    num_batches = len(dataloader)  #获取当前dataloader总批次数

    # train_loss用于累计所有批次的损失之和,correct用于累计预测正确的样本总数
    train_loss, correct = 0, 0
    for x, y in dataloader:
        #对dataloader进行迭代
        x, y = x.to(device), y.to(device)  #每一批次的数据设置为使用当前device
        #进行预测,并计算一个批次的损失
        pred = model(x)
        loss = loss_fn(pred, y)  #返回的是平均损失

        #使用反向传播算法,根据损失优化模型参数
        optimizer.zero_grad()  #将模型参数的梯度先全部归零
        loss.backward()  #损失反向传播,计算模型参数梯度
        optimizer.step()  #根据梯度优化参数
        with torch.no_grad():
            #correct用于累计预测正确的样本总数
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
            # train_loss用于累计所有批次的损失之和
            train_loss += loss.item()
    # train_loss是所有批次的损失之和,所以计算全部样本的平均损失时需要除以总批次数
    train_loss /= num_batches
    #correct是预测正确的样本总数,若计算整个epoch总体正确率,需除以样本总数量
    correct /= size
    return train_loss, correct

在上述代码中的train()函数中,首先使用len(dataloader.dataset)获取训练数据集样本总数量,使用len(dataloader)获取当前dataloader总批次数;然后对传进来的训练数据dataloader进行迭代,在迭代过程中,首先调用模型对当前批次的输入进行预测,并根据真实标签y计算一个批次中样本的平均损失;然后使用反向传播算法,根据损失优化模型参数;最后为了方便了解模型随着训练在数据集上的损失和正确率变化情况,初始化一个correct变量,并用它累计所有批次中预测正确的样本总数;初始化一个train_loss变量,用于累计所有批次的损失之和,这里的train_loss是所有批次的损失之和,所以计算全部样本的平均损失时需要除以总批次数,correct是预测正确的样本总数,计算整个epoch总体正确率,需要除以样本总数量。至此就得到了训练中平均正确率和平均损失值。

test()函数代码与train()函数类似,不过在test()函数代码中,仅仅测试模型在测试数据集的表现,也就是计算模型在测试数据集上的正确率和损失,并没有使用反向传播算法根据损失优化模型参数等部分的代码:

def test(dataloader, model):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0
    with torch.no_grad():
        for x, y in dataloader:
            x, y = x.to(device),
            y.to(device)
            pred = model(x)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    return test_loss, correct

下面就可以开始编写训练循环了,对全部训练数据集训练50个epoch(一个epoch代表对全部数据训练一遍),并使用列表记录这50个epoch训练中训练数据集和测试数据集上平均损失值和正确率的变化,以便了解模型的训练情况,并思考优化的方向。

epochs = 50  #一个epoch代表对全部数据训练一遍
train_loss = []  #每个epoch训练中训练数据集的平均损失被添加到此列表
train_acc = []  #每个epoch训练中训练数据集的平均正确率被添加到此列表
test_loss = []  #每个epoch训练中测试数据集的平均损失被添加到此列表
test_acc = []  # 每个epoch 训练中测试数据集的平均正确率被添加到此列表
for epoch in range(epochs):
    #调用train()函数训练
    epoch_loss, epoch_acc = train(train_dl, model, loss_fn, optimizer)
    #调用test()函数测试
    epoch_test_loss, epoch_test_acc = test(test_dl, model)
    train_loss.append(epoch_loss)
    train_acc.append(epoch_acc)
    test_loss.append(epoch_test_loss)
    test_acc.append(epoch_test_acc)
    #定义一个打印模板
    template = ("epoch: {:2d}, train_loss: {:.5f}, train_acc: {:.1f}% ,test_loss: {:.5f}, test_acc: {:.1f}%")
    #输出当前epoch 的训练集损失、训练集正确率、测试集损失、测试集正确率
    print(template.format(epoch, epoch_loss, epoch_acc * 100, epoch_test_loss, epoch_test_acc * 100))

print("Done!")

在这里插入图片描述

输出可以观察到,随着模型训练,train_loss和test_loss在逐渐下降,而train_acc和test_acc在逐渐上升,这说明训练是有效的。还可以将训练中这些指标绘图,以便更加直观地了解模型训练情况,损失变化绘图代码如下:

# 调用windows中字体文件,使label标签中的中文正常显示,不然会乱码
font = font_manager.FontProperties(fname=r"C:\\Windows\\Fonts\\msyh.ttc",size=20)
# 绘制训练与测试损失比较图像
plt.plot(range(1, epochs + 1), train_loss, label='train_loss')
plt.plot(range(1, epochs + 1), test_loss, label='test_loss', ls="--")
plt.xlabel('训练与测试损失比较',fontproperties=font)
plt.legend()
plt.show()

在这里插入图片描述

正确率变化曲线绘图:

# 绘制训练与测试正确率变化比较图像
plt.plot(range(1, epochs+1), train_acc, label='train_acc')
plt.plot(range(1, epochs+1), test_acc, label='test_acc', ls="--")
plt.xlabel('训练与测试正确率比较',fontproperties=font)
plt.legend()
plt.show()

在这里插入图片描述

通过观察训练曲线可以看到,随着训练epoch数量的增加,刚开始时损失在快速下降,到后面时损失曲线越来越平,下降速度变慢;正确率也有类似的特点,随着epoch增加,正确率的曲线也接近水平,说明模型训练已经接近饱和,继续训练不能更好地优化模型,此时我们可以停止训练了。

1.5 案例代码整合

import torch
import torchvision
from torch import nn
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt
from matplotlib import font_manager
%matplotlib inline


class Model(nn.Module):  # 创建模型,继承自nn.Module
    def __init__(self):
        super().__init__()
        #  第一层输入展平后的特征长度 28×28,创建120个神经元
        self.liner_1 = nn.Linear(28 * 28, 120)
        #  第二层输入的是前一层的输出,创建84个神经元
        self.liner_2 = nn.Linear(120, 84)
        #  输出层接收第二层的输入84,输出分类个数10
        self.liner_3 = nn.Linear(84, 10)

    def forward(self, input):
        x = input.view(-1, 28 * 28)
        # 将输入展平为二维,(1, 28,28)→(28*28)
        x = torch.relu(self.liner_1(x))  # 连接第一层liner_1并使用ReLU函数激活
        x = torch.relu(self.liner_2(x))  # 连接第二层liner_2并使用ReLU函数激活
        #  输出层,输出张量的长度,与类别数量一致
        x = self.liner_3(x)
        return x


device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))  # 如果安装了GPu版本,显示Using cuda device
model = Model().to(device)  # 初始化模型,并设置模型使用device

loss_fn = nn.CrossEntropyLoss()  # 初始化交叉熵损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.001)  # 初始化优化器


def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  #获取当前数据集样本总数量
    num_batches = len(dataloader)  #获取当前dataloader总批次数

    # train_loss用于累计所有批次的损失之和,correct用于累计预测正确的样本总数
    train_loss, correct = 0, 0
    for x, y in dataloader:
        #对dataloader进行迭代
        x, y = x.to(device), y.to(device)  #每一批次的数据设置为使用当前device
        #进行预测,并计算一个批次的损失
        pred = model(x)
        loss = loss_fn(pred, y)  #返回的是平均损失

        #使用反向传播算法,根据损失优化模型参数
        optimizer.zero_grad()  #将模型参数的梯度先全部归零
        loss.backward()  #损失反向传播,计算模型参数梯度
        optimizer.step()  #根据梯度优化参数
        with torch.no_grad():
            #correct用于累计预测正确的样本总数
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
            # train_loss用于累计所有批次的损失之和
            train_loss += loss.item()
    # train_loss是所有批次的损失之和,所以计算全部样本的平均损失时需要除以总批次数
    train_loss /= num_batches
    #correct是预测正确的样本总数,若计算整个epoch总体正确率,需除以样本总数量
    correct /= size
    return train_loss, correct


def test(dataloader, model):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0
    with torch.no_grad():
        for x, y in dataloader:
            x, y = x.to(device),y.to(device)
            pred = model(x)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    return test_loss, correct


train_ds = torchvision.datasets.MNIST('data/', train=True, transform=ToTensor(), download=True)
test_ds = torchvision.datasets.MNIST('data/', train=False, transform=ToTensor(), download=True)

train_dl = torch.utils.data.DataLoader(train_ds, batch_size=64, shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds, batch_size=46)

epochs = 50  #一个epoch代表对全部数据训练一遍
train_loss = []  #每个epoch训练中训练数据集的平均损失被添加到此列表
train_acc = []  #每个epoch训练中训练数据集的平均正确率被添加到此列表
test_loss = []  #每个epoch训练中测试数据集的平均损失被添加到此列表
test_acc = []  # 每个epoch 训练中测试数据集的平均正确率被添加到此列表
for epoch in range(epochs):
    #调用train()函数训练
    epoch_loss, epoch_acc = train(train_dl, model, loss_fn, optimizer)
    #调用test()函数测试
    epoch_test_loss, epoch_test_acc = test(test_dl, model)
    train_loss.append(epoch_loss)
    train_acc.append(epoch_acc)
    test_loss.append(epoch_test_loss)
    test_acc.append(epoch_test_acc)
    #定义一个打印模板
    template = ("epoch: {:2d}, train_loss: {:.5f}, train_acc: {:.1f}% ,test_loss: {:.5f}, test_acc: {:.1f}%")
    #输出当前epoch 的训练集损失、训练集正确率、测试集损失、测试集正确率
    print(template.format(epoch, epoch_loss, epoch_acc * 100, epoch_test_loss, epoch_test_acc * 100))
    

print("Done!")


# 调用windows中字体文件,使label标签中的中文正常显示,不然会乱码
font = font_manager.FontProperties(fname=r"C:\\Windows\\Fonts\\msyh.ttc",size=20)

# 绘制训练与测试损失比较图像
plt.plot(range(1, epochs + 1), train_loss, label='train_loss')
plt.plot(range(1, epochs + 1), test_loss, label='test_loss', ls="--")
plt.xlabel('训练与测试损失比较',fontproperties=font)
plt.legend()
plt.show()


# 绘制训练与测试成功率比较图像
plt.plot(range(1, epochs+1), train_acc, label='train_acc')
plt.plot(range(1, epochs+1), test_acc, label='test_acc', ls="--")
plt.xlabel('训练与测试成功率比较',fontproperties=font)
plt.legend()
plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/479380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】狂肝两万字带你学会——类与对象

目录 01-初始对象 生活中的数据组织 程序中的数据组织​编辑 使用对象组织数据 总结01 02-类的成员方法 类的定义和使用 成员变量和成员方法 成员方法的定义语法 注意事项 成员方法——代码演示 总结02 03-类和对象 现实世界的事物和类 类和对象 使用类和对象…

Android 9.0 原生SystemUI下拉通知栏每条通知默认展开

1.前言 在9.0的系统rom原生开发中,在产品对SystemUI下拉通知栏做定制的时候,在下拉状态栏的时候,通知栏中 最后一条通知默认是收缩的 点击按钮 就会展开 原生系统systemui就是如此,为了更美观 所以要求最后一条通知也默认展开,显得更美观 最终效果图: 2.原生SystemUI下拉通…

网络通信原理

网络通信原理目录 1.网络通信基础1.1 IP地址1.2 端口号 2. 通信协议2.1 协议简介2.1 协议分层 3. 封装和分用 1.网络通信基础 😄网络通信的目的是网络数据传输,是主机的不同进程间,基于网络实现的数据传输。那么,首先我们应该先弄…

Java实现调用外部程序

Java实现调用外部程序 Java库Runtime类与ProcessBuilder类参数传递执行结果Runtime类的使用ProcessBuilder类的使用无参数调用简单参数调用复杂参数调用 Apache Commons Exec库使用步骤介绍使用实例 Java库 在Java中,可以通过Runtime类或ProcessBuilder类来实现调用…

看看人家的MyBatis批量插入数据优化,从120s到2.5s,那叫一个优雅!

粗略的实验 最后 最近在压测一批接口的时候,我发现接口处理速度比我们预期的要慢。这让我感到有点奇怪,因为我们之前已经对这些接口进行了优化。但是,当我们进行排查时,发现问题出在数据库批量保存这块。 我们的项目使用了 myb…

开源小项目ChatGPT-website已获得100+star,我都干了什么

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言&#x1f4d…

微信小程序学习实录1(wxml文档、引入weui、双向数据绑定、提交表单到后端)

微信小程序学习实录 一、wxml文档二、新建页面快捷方式三、微信小程序引入weui四、双向数据绑定1.wxml渲染层2.js逻辑层 提交表单到后端五、微信小程序跳转到H5 一、wxml文档 <!-- index.wxml --> <view><!-- 数据绑定 --><view><text>{{name}}…

蛋白质界的 ChatGPT:AlphaFold2 论文必备知识,不会有人还不知道吧

你知道 AlphaFold2 吗&#xff1f;它真正解决了蛋白质三维结构预测的算法困境&#xff0c;堪称蛋白质界的 chat-GPT4&#xff0c;甚至它的意义不是 chat-GPT4 所能够匹敌的。它为世界疾病治疗药物开发以及探究生物生命之谜提供了通向天神的一条道路&#xff0c;未来是生物的世纪…

Java 基础入门篇(二)—— Java 基础语法

文章目录 一、注释二、字面量三、变量3.1 变量概述3.2 变量在计算机中的底层原理 四、数据类型五、关键字、标志符六、类型转换6.1 自动类型转换6.2 表达式的自动类型转换6.3 强制类型转换 七、运算符7.1 基本算数运算符7.2 符号做连接符7.3 自增自减运算符7.4 赋值运算符7.5 …

Java 基础入门篇(五)—— 面向对象编程

文章目录 一、面向对象的思想二、类的定义与对象的创建三、对象内存分配情况 ★ 3.1 两个对象的内存图3.2 两个变量指向同一个对象内存图 四、构造器4.1 构造器的格式与分类4.2 构造器的调用 五、 this 关键字六、封装七、标准JavaBean补充&#xff1a;局部变量和成员变量的区别…

Java 基础入门篇(六)—— String 类详解

文章目录 一、String 类概述二、String 创建对象的方式2.1 创建对象的两种方式2.2 面试&#xff1a;两种方式的区别 ★2.3 常见面试题 ★ 三、String 类常用方法3.1 字符串内容比较3.2 常用 API&#xff1a;遍历、截取、替换、分割 一、String 类概述 java.lang.String 类代表…

Java 基础入门篇(四)—— 方法的重载与参数传递机制

文章目录 一、方法的定义二、方法的参数传递机制 ★2.1 基本类型的参数传递2.2 引用类型的参数传递 三、方法重载 一、方法的定义 方法的作用&#xff1a;封装一段代码的语法结构&#xff0c;可以被重复调用&#xff0c;以此提高代码的复用性&#xff0c;提高开发效率&#xf…

【VsCode远程开发】Windows SSH远程连接Linux服务器 - 无公网IP内网穿透

文章目录 前言视频教程1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 转…

Java 基础进阶篇(一)—— static 静态关键字与单例模式

文章目录 一、static 静态关键字1.1 静态成员变量与实例成员变量1.2 静态成员方法与实例成员方法1.3 static 访问注意事项1.4 内存使用情况 二、工具类三、代码块四、单例模式4.1 饿汉单例4.2 懒汉单例 一、static 静态关键字 static&#xff1a;代表静态的意思&#xff0c;可…

KaliLinux安装burpsuite(超详细)

注意事项 1.注意linux位数 安装jdk之前先输出uname -a&#xff0c;看看kali linux是32位的还是64位&#xff0c;例如此处我的kali是32位的&#xff0c;因此需下载的是32位的jdk 2.jdk版本 jdk版本最好是oracle的&#xff0c;若使用的是openjdk很可能会出现burpsuite闪退现象…

远程访问本地jupyter notebook服务 - 无公网IP端口映射

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自远控源码文章&#xff1a;公网远程访问jupyter notebook【cpolar内网穿透】 前言 Jupyter Notebook&am…

【Linux】信号的保存信号的捕捉信号集零碎知识点总结

【Linux】信号的保存&信号的捕捉&信号集&零碎知识点总结 一、信号的保存1.1 信号几种概念1.2 信号在内核中的表示 二、信号的捕捉了解用户态和内核态2.1 捕捉过程2.2 信号的捕捉方法2.3 信号捕捉规则2.4 多信号屏蔽问题 三、信号集3.1 概念3.2 信号集&#xff08;s…

【视频教程解读】Window上安装和使用autogluon V0.7

1.使用conda安装的python环境 教程使用的是极简版miniconda,由于我们的电脑中安装了anaconda&#xff0c;所以不需要进行进一步安装。python版本为3.9&#xff0c;博客里面有anaconda和python版本的对应关系。注意查看版本autogluon V0.4需要3.8或者3.9和3.10&#xff0c;pip版…

2023年第二十届五一数学建模B题:快递需求分析问题-思路详解

一、题目简析 今年的B题是一道较为综合的题目&#xff0c;包括了数据分析、综合评价、时间序列预测、最优化问题以及概率估计问题。考察范围广&#xff0c;但是整体看来题目背景简单&#xff0c;切入点多&#xff0c;难度适中。 二、逐问思路 1.问题1&#xff1a;附件1为该快…

私有GitLab仓库 - 本地搭建GitLab私有代码仓库并随时远程访问「内网穿透」

文章目录 前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar内网穿透5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名 7. 测试访问二级子域名 转载自远控源码文章&#xff1a;Linux搭建GitLab私有仓库&#xff0c;并内网穿透实现公…