【猫狗分类】Pytorch VGG16 实现猫狗分类4-开始训练

背景

现在,我们已经完成了,数据集的清洗,标签的制作,也把VGG16的模型建立好了。那接下来,我们应该把数据,放到我们搭建的vgg16的模型里面,让模型针对这些猫和狗的图片,去进行训练,经过几个epoch后,我们希望可以得到,一个经过若干轮训练后,学习的比较好的w,这样,当我们给这个vgg16的模型,一张它从没有见过的图片时候,它可以准确的判断出,这是猫还是狗。

声明:整个数据和代码来自于b站,链接:使用pytorch框架手把手教你利用VGG16网络编写猫狗分类程序_哔哩哔哩_bilibili

我做了复现,并且记录了自己在做这个项目分类时候,一些所思所得。

开始训练,我们新建一个main.py程序,作为我们训练的主程序,我们会在main.py里面,导入数据,对数据进行一些分批次的处理(data loader),然后调用我们新建的vgg16模型,进行训练,得到训练好的权重文件。

训练

1、导包

import torch

import torch.nn as nn

from net import vgg16

from torch.utils.data import DataLoader#工具取黑盒子,用函数来提取数据集中的数据(小批次) from data import *

from module import * 是一种导入模块的方式,这里的 module 是你要导入的模块名称。这条语句的意思是,从名为 data 的模块中,导入所有的公开(非以下划线开头的)对象到当前命名空间。这意味着,data.py 文件中定义的所有函数、类和变量(只要它们没有被定义为私有的,即没有前缀下划线 _)都将可以在当前脚本中直接使用,无需通过模块名作为前缀。data是做了数据增强。

2、数据集处理

数据集处理这里,要做两个事情:

读取标签(索引)文件,让程序正确的指向存放的图片

对于所有的图片,猫和狗的,做训练集和验证集的划分

2.1 读取标签文件并且打乱

实际上,标签文件就是一个索引文件,里面有每张图片的类别和每张图片的储存位置,

annotation_path='cls_train_1.txt'#读取数据集生成的文件

with open(annotation_path,'r',encoding='utf-8') as f:

lines=f.readlines() #按照行来读取,意思是把标签文件一行一行拿出来并存到名为lines的列表里

np.random.seed(10101)#函数用于生成指定随机数

np.random.shuffle(lines)#把lines列表里的数据打乱

np.random.seed(None)

  • np.random.seed(10101): 设定NumPy随机数生成器的种子为10101。这使得每次执行时产生的随机序列相同,对于实验的可重复性很重要。

  • np.random.shuffle(lines): 使用设定好的随机种子打乱lines列表中的元素顺序,实现数据集的随机化处理,有助于模型训练时的泛化能力。

  • np.random.seed(None): 重置随机数生成器的种子为默认值,这意味着接下来的随机操作将不再受之前设定种子的影响,产生真正的随机结果。

2.2 训练集和验证集的划分

把打乱的标签文件分成训练集和验证集

num_val = int(len(lines)*0.2)  #先计算有多少图片,然后,取20%作为验证集

num_train = len(lines)-num_val #剩下的做训练集

3、处理图片:进行数据增强

把训练集和验证集划分好了,之前已经设定了图像增强的data.py文件,现在就是,把每张图片进行数据增强

input_shape=[224,224] #导入图像大小 train_data=DataGenerator(lines[:num_train],input_shape,True) val_data=DataGenerator(lines[num_train:],input_shape,False) val_len=len(val_data) print(val_len)#返回测试集长度

  • 1. `input_shape=[224,224]`: 定义了图像预处理时所需的输入尺寸,即所有的输入图像都将被重塑为224x224像素的大小。这是常见的用于许多卷积神经网络(如VGG, ResNet等)的输入尺寸。实际上,input_shape=[224,224]这一行代码本身并不直接修改任何图像的尺寸。它只是定义了预期的输入尺寸,告诉后续处理流程(比如深度学习模型)你期望输入图像的尺寸是多少。为了将不同大小的照片统一修改为这个尺寸,需要在数据预处理阶段实现图像的缩放或重塑功能。图像尺寸调整主要发生在data.py的get_random_data方法内。
  • 2. `train_data=DataGenerator(lines[:num_train], input_shape, True)`: 
  •    - 这一行实例化了一个名为`DataGenerator`的类(假设它是自定义的或第三方库中的数据生成器类),用于生成训练数据。
  •    - `lines[:num_train]`表示使用之前划分的前80%数据行(训练集)。
  •    - `input_shape=[224,224]`作为参数传递,指示数据生成器如何重塑图像尺寸。
  •    - `True`参数可能意味着此数据生成器在提供数据时会应用数据增强技术,这对于提高模型的泛化能力非常有用,尤其是在训练集相对有限的情况下。
  • 3. `val_data=DataGenerator(lines[num_train:], input_shape, False)`: 
  •    - 同样实例化`DataGenerator`类,但这次是为验证集创建数据生成器。
  •    - `lines[num_train:]`使用剩余的20%数据行(验证集)。
  •    - `input_shape`同样设置为224x224。
  •    - `False`参数表明验证集的数据生成不会应用数据增强,因为验证阶段我们希望看到模型在未修改数据上的真实表现。
  • 4. `val_len=len(val_data)`: 计算验证集数据生成器中包含的样本数量,并将其赋值给变量`val_len`。
  • 5. `print(val_len)`: 打印出验证集的长度,即验证集包含的样本数量。这对于了解数据分布、监控模型训练进度等都是有用的信息。

4、加载数据:dataloader

gen_train=DataLoader(train_data,batch_size=4)#训练集batch_size读取小样本,规定每次取多少样本

gen_test=DataLoader(val_data,batch_size=4)#测试集读取小样本

5、加载模型:模型实例化

选择设备以及模型实例化,并把模型转移到指定设备

device=torch.device('cuda'if torch.cuda.is_available() else "cpu")#电脑主机的选择 net=vgg16(True, progress=True,num_classes=2)

net.to(device)

  • net=vgg16(True, progress=True,num_classes=2),因为之前是这么定义vgg16模的的:

def vgg16(pretrained=False, progress=True,num_classes=2):所以这三个参数要传进去

  • True: 表示加载预训练权重,这会从指定的URL下载预训练的VGG16模型权重并加载到模型中,这对于迁移学习非常关键。
  • progress=True: 当从网上下载模型权重时,显示下载进度条。
  • num_classes=2: 指定模型的输出类别数为2,猫狗分类

6、设置优化器

lr=0.0001#定义学习率

optim=torch.optim.Adam(net.parameters(),lr=lr)#导入网络和学习率 sculer=torch.optim.lr_scheduler.StepLR(optim,step_size=1)#步长为1的读取

  • 学习率定义 (lr):lr=0.0001

    这行代码定义了模型训练过程中的学习率(learning rate)为0.0001。学习率是一个超参数,决定了模型参数在梯度下降过程中更新的幅度,较小的学习率可以使训练过程更为稳定,但可能需要更多的迭代次数来收敛;较大的学习率可能加速收敛过程,但也可能导致训练不稳定或无法收敛。

  • 优化器定义 (optim):optim=torch.optim.Adam(net.parameters(),lr=lr)

    这里使用了Adam优化器来更新模型参数。Adam是Adaptive Moment Estimation的简称,是一种常用的优化算法,结合了RMSProp和动量的思想,能够自动调整学习率。net.parameters()用于获取模型的所有可学习参数,lr=lr则指定了之前定义的学习率。这行代码实例化了一个Adam优化器,它将用于模型训练过程中参数的更新。

学习率调度器定义 (sculer):sculer=torch.optim.lr_scheduler.StepLR(optim,step_size=1)#

这行代码创建了一个学习率调度器StepLR,它以固定步长(step_size)调整学习率。在这里,step_size=1意味着每经过一次(一个epoch或一个batch,取决于调度器如何被调用)学习率就会按照预定的规则进行调整。但是,仅凭这段代码,我们无法得知学习率具体是如何调整的(比如降低多少比例),因为这通常还需要设置gamma参数(默认为0.1,意味着每step_size次学习率乘以0.1)。如果希望在每个周期后调整学习率,可能需要额外指定gamma值或者检查默认设置是否符合需求。

为了明确学习率如何随着训练进行而调整,特别是使用StepLR调度器时,你应该显式地设置gamma参数。gamma值决定了每次调整时学习率的衰减比例。默认情况下,如果没有指定gammaStepLR会使用0.1,意味着每step_size个周期后学习率会乘以0.1,即减少到原来的10%。如果你想调整学习率衰减的策略,可以按照以下方式修改代码:

# 设置gamma值,例如衰减为原来的0.5 gamma_value = 0.5 sculer = torch.optim.lr_scheduler.StepLR(optim, step_size=1, gamma=gamma_value)

7、设置损失函数

criterion = nn.CrossEntropyLoss()

8、训练

epochs=20#读取数据次数,每次读取顺序方式不同
for epoch in range(epochs):total_train=0 #定义总损失for data in gen_train:img,label=datawith torch.no_grad():img =img.to(device)label=label.to(device)optim.zero_grad()output=net(img)train_loss=criterion(output,label).to(device)train_loss.backward()#反向传播optim.step()#优化器更新total_train+=train_loss #损失相加sculer.step()total_test=0#总损失total_accuracy=0#总精度for data in gen_test:img,label =data #图片转数据with torch.no_grad():img=img.to(device)label=label.to(device)optim.zero_grad()#梯度清零out=net(img)#投入网络test_loss=criterion(out,label).to(device)total_test+=test_loss#测试损失,无反向传播 accuracy=((out.argmax(1)==label).sum()).clone().detach().cpu().numpy()#正确预测的总和比测试集的长度,即预测正确的精度total_accuracy+=accuracy

【生成器的理解】

class DataGenerator(data.Dataset):def __init__(self,annotation_lines,inpt_shape,random=True):self.annotation_lines=annotation_linesself.input_shape=inpt_shapeself.random=randomdef __len__(self):return len(self.annotation_lines)
    def __getitem__(self, index):annotation_path=self.annotation_lines[index].split(';')[1].split()[0]image=Image.open(annotation_path)image=self.get_random_data(image,self.input_shape,random=self.random)image=np.transpose(preprocess_input(np.array(image).astype(np.float32)),[2,0,1])y=int(self.annotation_lines[index].split(';')[0])return image,y

在data.py中,`DataGenerator` 类被设计为生成器,它在每次迭代时不仅提供图像数据 (`img`) 还有对应的标签 (`label`)。当您看到 `data` 里面包含 `img` 和 `label`,这是因为在定义 `DataGenerator` 的 `__getitem__` 方法时,就已经指定了返回图像及其对应的标签。这里是关键部分的逻辑解释:

- 在 `DataGenerator` 类中,通过 `__getitem__` 方法定义了如何根据索引 `index` 获取单个样本。此方法内部,您首先使用索引从预先设定好的路径列表和标签列表中获取对应的图像路径和标签,然后对图像进行预处理(虽然具体的预处理函数 `preprocess_image` 没有展示,但假设它会读取图像文件并调整至所需尺寸等),最后返回这对数据(图像和标签)。

- 当使用 PyTorch 的 `DataLoader` 对象包装这个自定义的 `DataGenerator` 时,`DataLoader` 会在每个训练或验证周期内依次调用 `DataGenerator` 的 `__getitem__` 方法来获取数据。由于在 `DataGenerator` 中实现了返回 `(img, label)` 对,因此每次迭代时 `DataLoader` 返回的 `data` 实际上是一个包含图像张量和标签张量的元组

所以,在训练循环中,这一行代码 `img, label = data` 实际上是对从 `DataLoader` 中取出的一个批次数据的解包操作,其中 `img` 是一个形状为 `[batch_size, channels, height, width]` 的四维张量,包含了批量的图像数据;而 `label` 则是一个形状为 `[batch_size]` 的一维张量,包含了这些图像对应的类别标签。这样,就可以直接使用这些数据进行模型的前向传播、计算损失、反向传播等训练步骤了。

【训练集和测试集的理解】

在深度学习中,训练集主要用于调整模型的权重参数。这一过程包括前向传播(forward pass)和反向传播(backward pass)两个主要步骤:

  • 前向传播:模型接收输入数据,通过一系列的计算(如线性变换、激活函数等),产生预测输出。
  • 反向传播:计算预测输出与实际标签之间的损失(loss),然后沿着网络的结构反向传播这个损失,以此来量化每个参数对损失的贡献程度。基于这些信息,算法(如梯度下降)会更新模型的参数,以期望在下一次预测时减小损失,即更准确地预测。

而对于测试集

  • 主要目的是评估模型在未见过的数据上的表现能力,即泛化能力。
  • 测试集不参与模型参数的学习过程,因此只进行前向传播来获取预测结果,计算准确率、查准率、查全率等评价指标。
  • 不执行反向传播和参数更新,因为测试集是用来模拟模型部署后遇到新数据时的表现,不应影响模型的训练过程。

总结来说,训练集用于教导模型如何预测,涉及参数调整;而测试集用来检验模型学得的知识,不改变模型参数。

【预测准确度的计算】

accuracy=((out.argmax(1)==label).sum()).clone().detach().cpu().numpy()

1. **`(out.argmax(1) == label)`**:
   - `out` 是模型对于一批输入数据的预测输出,通常是一个二维张量,其中每一行对应一个样本的预测概率分布。

比如:
   - `.argmax(1)` 操作沿着第1维度(行)找到概率最大的索引,即预测的类别。对于每个样本,这给出了模型认为最可能的类别。
   - `label` 是这批数据的真实标签,也是一个张量,其中每个元素代表一个样本的真实类别。
   - 这部分比较了模型的预测类别(`out.argmax(1)`)与真实类别(`label`),生成一个布尔张量,其中`True`表示预测正确,`False`表示预测错误。

2. **`.sum()`**:
   - 上述布尔张量中,`True`值被视为1,`False`值被视为0。因此,`.sum()`操作计算了预测正确的样本数

3. **`.clone().detach().cpu().numpy()`**:
   - `.clone()` 创建了一个张量的副本,这在某些情况下是必要的,以避免修改原始张量。
   - `.detach()` 用于从计算图中分离张量,这意味着对分离后的张量进行的操作不会记录到计算历史中,也不会占用额外的图形计算内存。这对于计算指标(如准确率)很有用,因为我们不需要对其进行反向传播。
   - `.cpu()` 将张量从GPU(如果之前在GPU上)移动到CPU内存中,因为`.numpy()`方法只能在CPU张量上调用。
   - `.numpy()` 最终将PyTorch张量转换为NumPy数组,便于进行进一步的数值处理或打印。

综上所述,这段代码计算了模型在某一批数据上的预测准确率,即预测正确的样本数占总样本数的比例。通过一系列操作,它确保了计算的正确性、高效性和兼容性。但请注意,为了得到整体的测试集精度,通常需要在整个测试集上累积这个准确率,并除以总的测试样本数。

out.argmax(1) 的理解】

执行.argmax(1)操作时,对于每个样本,会得到预测概率最高的类别索引。

out.argmax(1) 是一个在深度学习和机器学习领域常用的张量操作,尤其是在处理分类问题时。这里假设out是一个二维张量(tensor),它通常代表了一组数据样本通过神经网络模型得到的预测概率分布。每个样本对应一行,每列对应一个类别,元素值表示该样本属于对应类别的概率。

具体来说:

  • .argmax(dim) 是一个PyTorch中的函数,它作用于一个张量,返回沿着某一维度(dim)上的最大值的索引。索引是从0开始的整数。
  • 当你写out.argmax(1)时,你是在要求沿着张量的第二个维度(索引为1的维度,通常代表类别)找到每一行(每个样本)的最大值所在的索引。这个索引实际上就是模型对于每个样本预测的类别标签,因为它指向了概率最高的那个类别的列。
  • 在二分类问题中,场景会稍微简化一些,因为模型通常只需要预测两个类别,比如0(负类)和1(正类)。`out`张量对于每个样本仍然会给出两个类别的概率分布,但总和为1。例如:

    ```
    out = [[0.3, 0.7],  # 预测样本1为负类的概率为0.3,为正类的概率为0.7
           [0.1, 0.9],  # 预测样本2为负类的概率为0.1,为正类的概率为0.9
           [0.6, 0.4]]  # 预测样本3为负类的概率为0.6,为正类的概率为0.4
    ```

    这里的每一行第一个元素代表预测为类别0(通常视为负类)的概率,第二个元素代表预测为类别1(通常视为正类)的概率。

    应用`.argmax(1)`操作后,我们得到每个样本预测的类别:

    ```
    predicted_classes = out.argmax(1)
    predicted_classes = [1, 1, 0]  # 样本1和2被预测为类别1(正类),样本3被预测为类别0(负类)
    ```

    假设我们的`label`(真实类别)为:

    ```
    label = [1, 1, 1]  # 所有三个样本的真实类别均为正类(1)
    ```

    现在,我们比较预测类别与真实类别:

    ```
    correct_predictions = (predicted_classes == label)
    correct_predictions = [True, True, False]  # 样本1和2预测正确,样本3预测错误
    ```

    这说明,在这个二分类问题的例子中,前两个样本的预测是正确的(`True`),而第三个样本的预测是错误的(`False`)。

9、保存模型

print("训练集上的损失:{}".format(total_train))
print("测试集上的损失:{}".format(total_test))
print("测试集上的精度:{:.1%}".format(total_accuracy/val_len))#百分数精度,正确预测的总和比测试集的长度torch.save(net.state_dict(),"DogandCat{}.pth".format(epoch+1))
print("模型已保存")

完整代码

import numpy as np
import os
import torch
import torch.nn as nn
from net import vgg16
from torch.utils.data import DataLoader#工具取黑盒子,用函数来提取数据集中的数据(小批次)
from data import *
'''数据集'''
annotation_path='cls_train_1.txt'#读取数据集生成的文件
with open(annotation_path,'r',encoding='utf-8') as f:lines=f.readlines()
np.random.seed(10101)#函数用于生成指定随机数
np.random.shuffle(lines)#数据打乱
np.random.seed(None)
num_val=int(len(lines)*0.2)#十分之一数据用来测试
num_train=len(lines)-num_val
#输入图像大小
input_shape=[224,224]   #导入图像大小
train_data=DataGenerator(lines[:num_train],input_shape,True)
val_data=DataGenerator(lines[num_train:],input_shape,False)
val_len=len(val_data)
print(val_len)#返回测试集长度
# 取黑盒子工具
"""加载数据"""
gen_train=DataLoader(train_data,batch_size=4)#训练集batch_size读取小样本,规定每次取多少样本
gen_test=DataLoader(val_data,batch_size=4)#测试集读取小样本
'''构建网络'''
device=torch.device('cuda'if torch.cuda.is_available() else "cpu")#电脑主机的选择
net=vgg16(True, progress=True,num_classes=2)#定于分类的类别
net.to(device)
'''选择优化器和学习率的调整方法'''
lr=0.0001#定义学习率
optim=torch.optim.Adam(net.parameters(),lr=lr)#导入网络和学习率
sculer=torch.optim.lr_scheduler.StepLR(optim,step_size=1)#步长为1的读取
# 定义损失函数
criterion = nn.CrossEntropyLoss()'''训练'''
epochs=20#读取数据次数,每次读取顺序方式不同
for epoch in range(epochs):total_train=0 #定义总损失for data in gen_train:img,label=datawith torch.no_grad():img =img.to(device)label=label.to(device)optim.zero_grad()output=net(img)train_loss=criterion(output,label).to(device)train_loss.backward()#反向传播optim.step()#优化器更新total_train+=train_loss #损失相加sculer.step()total_test=0#总损失total_accuracy=0#总精度for data in gen_test:img,label = data #图片转数据with torch.no_grad():img=img.to(device)label=label.to(device)optim.zero_grad()#梯度清零out=net(img)#投入网络test_loss=criterion(out,label).to(device)total_test+=test_loss #测试损失,无反向传播accuracy=((out.argmax(1)==label).sum()).clone().detach().cpu().numpy()#正确预测的总和比测试集的长度,即预测正确的精度total_accuracy+=accuracyprint("训练集上的损失:{}".format(total_train))print("测试集上的损失:{}".format(total_test))print("测试集上的精度:{:.1%}".format(total_accuracy/val_len))#百分数精度,正确预测的总和比测试集的长度torch.save(net.state_dict(),"DogandCat{}.pth".format(epoch+1))print("模型已保存")

结果:

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

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

相关文章

面试题 17.17. 多次搜索

链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a; class Solution { private:struct Trie {Trie() {end false;index -1;next.resize(26);}bool end;int index;std::vector<std::unique_ptr<Trie>> next;};void insert_trie(int in…

【CT】LeetCode手撕—88. 合并两个有序数组

目录 题目1- 思路2- 实现⭐88. 合并两个有序数组——题解思路 2- ACM实现 题目 原题连接&#xff1a;88. 合并两个有序数组 1- 思路 模式识别 模式1&#xff1a;两个有序数组合并 ——> 双指针模式2&#xff1a;返回结果填充到 nums1[mn] ——> 需要开辟新的数组空间 …

lxml库在爬虫领域的贡献及应用

重头戏lxml库里面的xpath 一段代码给各位开开胃 这段代码首先导入了lxml库中的etree模块&#xff0c;然后定义了一个包含HTML内容的字符串html。接着&#xff0c;我们使用etree.HTML()函数解析这个HTML字符串&#xff0c;得到一个表示整个HTML文档的树形结构。最后&#xff0c;…

qt 实现模拟实际物体带速度的移动(水平、垂直、斜角度)——————附带完整代码

文章目录 0 效果1 原理1.1 图片旋转1.2 物体带速度移动 2 完整实现2.1 将车辆按钮封装为一个类&#xff1a;2.2 调用方法 3 完整代码参考 0 效果 实现后的效果如下 可以显示属性&#xff08;继承自QToolButton&#xff09;: 鼠标悬浮显示文字 按钮显示文字 1 原理 类继承…

Folx软件安装教程及最新版下载

简介&#xff1a; Folx Pro是一款适合Mac的专业下载工具也是一款BT下载器&#xff0c;Folx中文版有一个支持Retina显示的现代界面&#xff0c;提供独特的系统排序、存储下载内容与预览下载文件。Folx中文官网提供Folx教程、激活码、下载。 安 装 包 获 取 地 址&#xff1a; …

远程连接管理服务SHH

1.了解服务端和客户端 服务端&#xff1a;提供服务 客户端&#xff1a;享受服务 思考: 我们通过网络是如何找到我们想要访问的服务的? IP(提供服务的服务器)Port(找到相应的服务) 2.了解端口号的设定 说明:端口号只有整数&#xff0c;范围是从0 到65535 1&#xff5e;2…

warning LNK4017: DESCRIPTION 语句不支持目标平台;已忽略

文章目录 warning LNK4017: DESCRIPTION 语句不支持目标平台&#xff1b;已忽略概述笔记备注END warning LNK4017: DESCRIPTION 语句不支持目标平台&#xff1b;已忽略 概述 基于ATL的COM DLL导出函数&#xff0c;无法用__declspec(dllexport)直接在函数上标记为导出函数。 只…

Scala网络编程:代理设置与Curl库应用实例

在网络编程的世界里&#xff0c;Scala以其强大的并发模型和函数式编程特性&#xff0c;成为了开发者的得力助手。然而&#xff0c;网络请求往往需要通过代理服务器进行&#xff0c;以满足企业安全策略或访问控制的需求。本文将深入探讨如何在Scala中使用Curl库进行网络编程&…

MySQL第三方图形化工具:DBeaver

操纵数据库的语言&#xff0c;基于功能划分为4类&#xff1a; 数据定义:DDL(Data Definition Language)库的创建删除、表的创建删除等 数据操纵:DML(Data ManipulationLanguage)新增数据、删除数据、修改数据等 数据控制:DCL(Data ControlLanguage)新增用户、删除用户、密码…

DAY24 回溯算法part01 77. 组合 216.组合总和III 17.电话号码的字母组合

理论基础 #什么是回溯法 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 在二叉树系列中&#xff0c;我们已经不止一次&#xff0c;提到了回溯&#xff0c;例如二叉树&#xff1a;以为使用了递归&#xff0c;其实还隐藏着回溯 (opens new window)。 回溯是递…

【靶场搭建】-02- 搭建OWASP靶机

1.OWASP靶机介绍 相比较其他靶机&#xff0c;OWASP提供的环境更多&#xff0c;且包含了许多其他靶机的环境&#xff0c;属于性价比比较高的靶机了。 2.下载OWASP 访问以下地址进行下载&#xff1a; https://sourceforge.net/projects/owaspbwa/ 因为OWASP是虚拟机文件&…

函数(上)(C语言)

函数(上&#xff09; 一. 函数的概念二. 函数的使用1. 库函数和自定义函数(1) 库函数(2) 自定义函数的形式 2. 形参和实参3. return语句4. 数组做函数参数 一. 函数的概念 数学中我们其实就见过函数的概念&#xff0c;比如&#xff1a;一次函数ykxb&#xff0c;k和b都是常数&a…

跻身中国市场前三,联想服务器的“智变”与“质变”

IDC发布的《2024年第一季度中国x86服务器市场报告》显示&#xff0c;联想服务销售额同比增长200.2%&#xff0c;在前十厂商中同比增速第一&#xff0c;并跻身中国市场前三&#xff0c;迈入算力基础设施“第一阵营”。 十年砺剑联想梦&#xff0c;三甲登榜领风骚。探究联想服务器…

【机器学习】机器学习重要分支——强化学习:从理论到实践

文章目录 强化学习&#xff1a;从理论到实践引言第一章 强化学习的基本概念1.1 什么是强化学习1.2 强化学习的基本组成部分1.3 马尔可夫决策过程 第二章 强化学习的核心算法2.1 Q学习2.2 深度Q网络&#xff08;DQN&#xff09;2.3 策略梯度方法 第三章 强化学习的应用实例3.1 游…

【C语言】解决C语言报错:Format String Vulnerability

文章目录 简介什么是Format String VulnerabilityFormat String Vulnerability的常见原因如何检测和调试Format String Vulnerability解决Format String Vulnerability的最佳实践详细实例解析示例1&#xff1a;直接使用不受信任的输入作为格式化字符串示例2&#xff1a;未验证格…

1949年到2021年中国历年稻谷产量统计报告

数据介绍 数据来源于国家统计局&#xff0c;为1949年到2021年我国每年的稻谷产量数据。 2021年&#xff0c;我国稻谷产量为21284.24万吨&#xff0c;比上年增长0.5%。 数据统计单位为&#xff1a;万吨 我国稻谷产量有多少&#xff1f; 2021年&#xff0c;我国稻谷产量为2128…

springboot与flowable(12):网关服务(包容网关)

一、绘制流程图 包容网关可以看作是排他网关和并行网关的结合体。和排他网关一样&#xff0c;可以在外出顺序流上定义条件&#xff0c;包容网关会解析它们。但是主要的区别是包容网关可以选择多余一条顺序流&#xff0c;这和并行网关一样。包容网关的功能是基于进入和外出顺序流…

3d渲染的类型,渲染100邀请码1a12

3D渲染有不同的类型和方法&#xff0c;它们各有各的优缺点和适用场景&#xff0c;这里我们简单介绍下。 1、离线渲染 离线渲染也被称作预渲染&#xff0c;是指在不考虑时间限制的情况下&#xff0c;生成高质量二维图像或视频的方法。离线渲染通常用于电影、广告、设计等非交互…

回归预测 | Matlab实现NGO-HKELM北方苍鹰算法优化混合核极限学习机多变量回归预测

回归预测 | Matlab实现NGO-HKELM北方苍鹰算法优化混合核极限学习机多变量回归预测 目录 回归预测 | Matlab实现NGO-HKELM北方苍鹰算法优化混合核极限学习机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现NGO-HKELM北方苍鹰算法优化混合核极限…

ubuntu第三方库离线安装包(.deb离线安装方法;apt离线安装;离线安装deb)(docker离线安装、安装docker安装)

文章目录 方法1&#xff1a;Ubuntu Packages 网站下载离线包&#xff08;失败了&#xff0c;找不到包的可下载源&#xff0c;有的包有&#xff0c;有的包没有&#xff0c;不知道怎么回事&#xff09;操作步骤1. 在有网络的环境中&#xff0c;打开浏览器并访问 Ubuntu Packages …