【猫狗分类】Pytorch VGG16 实现猫狗分类2-模型构建

news2024/11/26 2:29:43

背景

数据处理做好了,现在搭建网络

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

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

VGG16,就是C这样,叫16是因为,卷积+全连接层,一共有6层,因为这俩有可以学习的w,池化层是没有的,所以叫做,VGG16

VGG 16的样子

VGG16网络pytorch搭建

pytorch官网有很多网络的源码:下面是VGG的源码

https://github.com/pytorch/vision/blob/main/torchvision/models/vgg.py

然后,针对于源码,进行改动就行了,因为我们要用VGG16

把源码复制到“return model”就行了,接下来,自己删删改改。

所以,学会用pytorch官网真的重要,多看看,多摸索。

VGG16网络代码拆解

现在,对于VGG16的pytorch官网的代码,每一块进行拆解,理解为什么是这样搭建的,以及,以后自己搭建,要怎么搭建,sop是什么。

1、导包

from functools import partial
from typing import Any, cast, Dict, List, Optional, Union

import torch
import torch.nn as nn

from ..transforms._presets import ImageClassification
from ..utils import _log_api_usage_once
from ._api import register_model, Weights, WeightsEnum
from ._meta import _IMAGENET_CATEGORIES
from ._utils import _ovewrite_named_param, handle_legacy_interface

  • 官网代码如上,但是很多都不需要,最后保留如下:

import torch

import torch.nn as nn

from torch.hub import load_state_dict_from_url

最后一行:导入PyTorch中的load_state_dict_from_url函数。这个函数是用来从指定的URL加载模型的状态字典(state dict)。状态字典是一个Python字典对象,它存储了模型中所有可学习参数(如权重和偏置)的值。这对于加载预训练模型的权重非常有用,因为你可以直接将这些权重载入到你定义的相同结构的模型中,从而复用预训练模型学到的特征。

使用这个函数的一般步骤包括:

  • 定义模型结构。
  • 通过URL指定预训练模型权重的位置。URL可以指向的不仅仅是网页,它还可以指向图片、视频、文档、API端点,甚至是托管在云存储服务上的模型文件等任何类型的网络资源。在这里,具体来说,它是指向预训练模型权重文件的直接链接。这个文件通常是以.pth或.pkl等格式存储的PyTorch模型状态字典文件。通过这个URL,PyTorch可以从远程服务器下载这些预训练权重,并将其加载到你的模型中。
  • 调用load_state_dict_from_url(url)下载并加载权重。
  • 将下载的权重状态字典赋给模型实例的.state_dict(),以恢复模型的参数。

这样就可以快速地利用已有的预训练模型进行迁移学习或者作为新任务的起点,而无需从头开始训练模型。

删除_all_列表:官网把所有VGG网络列在列表里,不需要,直接删除

  • 下载预训练权重

model_urls = { "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", }#权重下载网址

2、搭建VGG16网络

2.1 定义init函数,构建VGG16模型中处理分类决策的部分

搭架子,搭建网络架子,就是先把这个VGG16网络,里面的算子层,按照VGG16那个结构,搭建起来,从开始知道,它一共是16层,按照顺序,搭建起来。但是这一块和自己搭建不一样,他不是先一层一层搭建的,它先把分类器给搭建了。

把官方源代码复制过来,删除一些不必要的,用删除线表示,新增用斜线表示。

这里分为两块,网络搭建+权重初始化

先看看网络搭建部分,传特征,建池化层,按照顺序搭建

class VGG(nn.Module):
    def __init__(self, features: nn.Module, num_classes: int = 1000, init_weights: bool = True, dropout: float = 0.5) -> None:   
        super().__init__()
        _log_api_usage_once(self)
        self.features = features # 传特征
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),
            nn.Linear(4096, num_classes)
,
        )

注意点:

  •  init_weights: bool = True 这里意思是使用初始权重
  • self.classifier = nn.Sequential(....)这段代码实际上展示的是VGG网络后面的部分,即全连接层(FC)部分,通常紧随在卷积和池化层之后,用于图像分类任务的最终分类

2.2 模型创建时根据层的类型自动初始化权重和偏置

if init_weights:
            for m in self.modules():
                if isinstance(m, nn.Conv2d):
                    nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
                    if m.bias is not None:
                        nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.BatchNorm2d):
                    nn.init.constant_(m.weight, 1)
                    nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.Linear):
                    nn.init.normal_(m.weight, 0, 0.01)
                    nn.init.constant_(m.bias, 0)

这段代码是用于神经网络模型中权重初始化的部分,它遍历模型的所有模块并根据模块的类型应用特定的初始化方法。逐条解释:

1. 条件判断:
   if init_weights: 这行代码检查是否需要初始化权重。如果`init_weights`为`True`,则执行下面的初始化逻辑。

2. 遍历所有模块:
   for m in self.modules():
   使用`self.modules()`方法遍历模型中的所有子模块,就是把所有的算子访问一遍。这包括所有直接或间接属于该模型的层(如卷积层、线性层、批归一化层等)。

3. 卷积层(nn.Conv2d)的初始化:
   if isinstance(m, nn.Conv2d):
       nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
       if m.bias is not None:
           nn.init.constant_(m.bias, 0)
   - 当检测到模块`m`是卷积层(`nn.Conv2d`)时,使用`kaiming_normal_`函数按照"he初始化"方法初始化权重。这种初始化方法特别适合ReLU激活函数,其中`mode="fan_out"`表示根据输出通道的数量调整权重的初始化范围,以保持输出的方差接近1,有利于训练的稳定性。
   - 如果卷积层包含偏置项(`bias`),则将其初始化为0。

4. 批量归一化层(nn.BatchNorm2d)的初始化:
   elif isinstance(m, nn.BatchNorm2d):
       nn.init.constant_(m.weight, 1)
       nn.init.constant_(m.bias, 0)
   - 当模块是批量归一化层时,其缩放因子(`weight`)被初始化为1,偏移量(`bias`)被初始化为0。这确保了在训练初期,批量归一化层不会改变输入数据的分布。

5. 线性层(nn.Linear)的初始化:
   elif isinstance(m, nn.Linear):
       nn.init.normal_(m.weight, 0, 0.01)
       nn.init.constant_(m.bias, 0)
   - 对于线性层,权重使用正态分布初始化,均值为0,标准差为0.01。这样的初始化策略有助于促进网络的学习过程。
   - 线性层的偏置同样被初始化为0。

总结起来,这段代码的作用是在模型创建时根据层的类型自动初始化权重和偏置,采用了针对不同层类型最优的初始化策略,有助于模型训练的快速收敛和良好的泛化能力

2.2 进行前向传播

 def forward(self, x) :
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

注意:

  • x = self.features(x)这一行调用了模型中定义的特征提取部分(self.features),它通常由多个卷积层、激活函数和池化层组成。输入x经过这些层后,转换为更高层次的特征表示。
  • x = torch.flatten(x, 1)将三维的特征图(形状可能是[N, C, H, W],其中N是批量大小,C是通道数,H和W是高度和宽度)展平为二维张量(形状变为[N, CHW]),以便能够输入到全连接层中。这里1表示沿第二个维度(通道维)开始展平

2.3 定义按照cfg配置文件来进行顺序排列的算子层

def make_layers(cfg: List[Union[str, int]], batch_norm: bool = False) -> nn.Sequential:
    layers: List[nn.Module] = []
    in_channels = 3
    for v in cfg: # make_layers对输入的cfg进行循环

        if v == "M":
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            v = cast(int, v)
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    return nn.Sequential(*layers)

解析:

这段代码定义了一个名为`make_layers`的函数,用于根据给定的配置`cfg`创建一系列卷积层(以及可选的批量归一化层和激活函数),最终将这些层组合成一个`nn.Sequential`模块。

  • 函数签名:def make_layers(cfg: List[Union[str, int]], batch_norm: bool = False) -> nn.Sequential:

  - `cfg`: 一个列表,包含字符串"М"和整数。字符串"M"代表一个最大池化层,整数表示卷积层的输出通道数。
  - `batch_norm`: 布尔值,默认为`False`,决定是否在每个卷积层后添加批量归一化层。
  - 函数返回一个`nn.Sequential`对象,包含根据`cfg`构建的所有层。

  • 变量初始化:

  layers: List[nn.Module] = []
  in_channels = 3
  初始化一个空列表`layers`来存储创建的层,`in_channels`初始化为3,代表输入图像的通道数(通常为RGB图像)。

  • 循环构建层:for v in cfg:遍历配置列表`cfg`中的每个元素`v`。

  - 如果`v`等于字符串"M",则添加一个最大池化层
    layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
    这会减少特征图的空间尺寸,但保持通道数不变。

  - 否则,`v`被视为整数,表示下一个卷积层的输出通道数。创建一个卷积层
    conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
    这个卷积层使用3x3的卷积核,padding为1以保持输入输出尺寸相同,`in_channels`是输入通道数,`v`是输出通道数。

    - 根据`batch_norm`的值,决定是否在卷积层后添加批量归一化和ReLU激活函数
      if batch_norm:
          layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
      else:
          layers += [conv2d, nn.ReLU(inplace=True)]
      如果开启批量归一化,会在卷积后紧跟`nn.BatchNorm2d`和激活函数`nn.ReLU`。`inplace=True`意味着ReLU操作直接修改输入数据,节省内存。

  • 更新`in_channels`为当前卷积层的输出通道数,准备构建下一层。
  • 返回Sequential模型:return nn.Sequential(*layers):将构建好的层列表`layers`作为参数传递给`nn.Sequential`,创建一个有序的神经网络模块,其中每一层依次执行

这个函数非常实用,可以根据配置灵活生成具有或不具有批量归一化的卷积神经网络层序列,常用于构建经典的卷积神经网络架构。

2.4 创建cfg配置文件

cfgs: Dict[str, List[Union[str, int]]] = {
    "A": [64, "M", 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
    "B": [64, 64, "M", 128, 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
    "D": [64, 64, "M", 128, 128, "M", 256, 256, 256, "M", 512, 512, 512, "M", 512, 512, 512, "M"],
    "E": [64, 64, "M", 128, 128, "M", 256, 256, 256, 256, "M", 512, 512, 512, 512, "M", 512, 512, 512, 512, "M"],
}

在官方提供的配置字典cfgs中,VGG16对应的配置是"D",是一个字典。类似于yaml文件。

选择cfgs["D"]作为参数传递给make_layers函数。

2.5 定义 _vgg函数,用于根据给定的配置创建VGG模型,并可选地加载预训练权重

官方代码:

def _vgg(cfg: str, batch_norm: bool, weights: Optional[WeightsEnum], progress: bool, **kwargs: Any

pretrained=False, progress=True,num_classes=2

) -> VGG:
    if weights is not None:
        kwargs["init_weights"] = False
        if weights.meta["categories"] is not None:
            _ovewrite_named_param(kwargs, "num_classes", len(weights.meta["categories"]))

    model = VGG(make_layers(cfgs[cfg'D'], batch_norm=batch_norm), **kwargs)
    if weights is not None: # 就是说要预训练

    state_dict = load_state_dict_from_url(model_urls['vgg16'],model_dir='./model' ,progress=progress)#预训练模型地址

    model.load_state_dict(state_dict)

    model.load_state_dict(weights.get_state_dict(progress=progress, check_hash=True))
    return model

  • 在官方的代码中,有这样一个逻辑段:

if weights is not None:
    kwargs["init_weights"] = False
    if weights.meta["categories"] is not None:
        _ovewrite_named_param(kwargs, "num_classes", len(weights.meta["categories"]))

这段代码的作用是,当提供了预训练权重(`weights`不为`None`)时,执行以下操作:

1. **关闭自定义权重初始化**:`kwargs["init_weights"] = False`,因为预训练模型已经有了权重,不需要再执行自定义的权重初始化。
2. **调整输出类别数**:如果预训练权重的元数据中包含类别信息(`weights.meta["categories"]`),则使用该预训练权重对应的类别数(`len(weights.meta["categories"])`)来覆盖模型的输出类别数。这是因为预训练模型通常是为了特定任务训练的,比如ImageNet数据集有1000类,所以预训练权重的输出层大小是固定的。如果新任务的类别数不同,需要调整模型的输出层以匹配新任务的类别数。

因此,原本的逻辑考虑了如果使用预训练权重,模型的输出层(通常是最后的全连接层)的大小需要根据预训练权重的类别数来调整。但是猫狗识别只需识别两类,而预训练权重是为更多类设计的(比如1000类),直接使用不调整会导致模型结构不匹配,所以需要这个逻辑来自动调整模型的输出层以匹配预训练权重或特定任务的需求。但因为明确知道输出类只有2个,且可能不打算使用预训练权重,所以可以省略这部分调整逻辑。

  • 因为我们会使用预训练权重来初始化模型的一部分或全部权重,那么加载预训练权重的逻辑应当保留

if weights is not None: model.load_state_dict(weights.get_state_dict(progress=progress, check_hash=True))

这段代码确保了当提供了预训练权重(weights不为None)时,模型会加载这些预训练权重。weights.get_state_dict()会获取预训练权重的状态字典,而model.load_state_dict(...)则将这些权重加载到模型中。progress=True表示在下载预训练权重文件时显示进度条,check_hash=True则会检查下载的权重文件的哈希值,确保文件的完整性和一致性。如果你打算利用预训练权重进行迁移学习或微调,保留这行及其相关逻辑是必要的。

  • 新增:分类器修改问题,因为原来的网络是针对于1000个类别构建的现在我们只有猫和狗两个类别,所以,通常的做法就是,保留特征提取部分,什么卷积之类的,在最后一层,分类器构建的时候,直接复制前面,它根据1000个输出类别构建的分类器网络,然后把最后一层的输出改成2类,然后记得,把前面传进去的类别也改成两类。

if num_classes !=1000:

model.classifier = nn.Sequential(

nn.Linear(512 * 7 * 7, 4096),

nn.ReLU(True), nn.Dropout(p=0.5),#随机删除一部分不合格

nn.Linear(4096, 4096),

nn.ReLU(True),#防止过拟合

nn.Dropout(p=0.5),

nn.Linear(4096, num_classes), )

  • 为什么pretrain=false?不是要进行预训练吗

在这个函数定义中,`pretrained`是一个布尔参数,用于控制是否加载预训练权重。参数名设为`pretrained=False`是作为默认值,意味着如果不特别指定,函数将不加载预训练权重,而是创建一个未经训练的模型。这给予了使用者灵活性:他们可以选择使用预训练模型进行迁移学习(通过设置`pretrained=True`),或者从头开始训练一个模型(保留默认值`pretrained=False`)。

因此,`pretrained=False`并不代表不进行预训练,而是表示函数默认行为是不自动加载预训练权重。如果用户想要加载预训练权重,他们需要显式地将`pretrained`参数设置为`True`,如`vgg16(pretrained=True, ...)`。这样设计允许该函数既可用于初始化一个全新的模型用于从头训练,也可用于基于预训练模型的微调或特征提取任务。

2.6 return model

完整代码

import torch
import torch.nn as nn

from torch.hub import load_state_dict_from_url


model_urls = {
    "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth",
}#权重下载网址


class VGG(nn.Module):
    def __init__(self, features, num_classes = 1000, init_weights= True, dropout = 0.5):
        super(VGG,self).__init__()
        self.features = features
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))#AdaptiveAvgPool2d使处于不同大小的图片也能进行分类
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),#完成4096的全连接
            nn.Linear(4096, num_classes),#对num_classes的分类
        )
        if init_weights:
            for m in self.modules():
                if isinstance(m, nn.Conv2d):
                    nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
                    if m.bias is not None:
                        nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.BatchNorm2d):
                    nn.init.constant_(m.weight, 1)
                    nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.Linear):
                    nn.init.normal_(m.weight, 0, 0.01)
                    nn.init.constant_(m.bias, 0)

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)#对输入层进行平铺,转化为一维数据
        x = self.classifier(x)
        return x


def make_layers(cfg, batch_norm = False):#make_layers对输入的cfg进行循环
    layers = []
    in_channels = 3
    for v in cfg:#对cfg进行输入循环,取第一个v
        if v == "M":
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]#把输入图像进行缩小
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)#输入通道是3,输出通道64
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    return nn.Sequential(*layers)


cfgs = {
    "D": [64, 64, "M", 128, 128, "M", 256, 256, 256, "M", 512, 512, 512, "M", 512, 512, 512, "M"],

}

def vgg16(pretrained=False, progress=True,num_classes=2):
    model = VGG(make_layers(cfgs['D']))
    if pretrained:
        state_dict = load_state_dict_from_url(model_urls['vgg16'],model_dir='./model' ,progress=progress)#预训练模型地址
        model.load_state_dict(state_dict)
    if num_classes !=1000:
        model.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),#随机删除一部分不合格
            nn.Linear(4096, 4096),
            nn.ReLU(True),#防止过拟合
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes),
        )
    return model

总结

  • 构建网络步骤:
  1. 构建VGG网络,继承nn.module模块:构造init函数,搭建算子,主要是把分类器做好,权重初始化,然后设定做前向传播。
  2. 根据cfg做算子层的按照顺序排列,make_layers
  3. 列出来cfg配置文件
  4. 定义vgg16的model模型,调用make_layers和cfg配置文件匹配上:model = VGG(make_layers(cfgs['D']))
  • 注意:模型是一整个的概念,网络只是模型中的一个环节。

通过使用配置`cfgs['D']`来创建一个VGG模型实例,并将其赋值给变量`model`。这里的`VGG`是一个之前定义好的类,它用于构建具有特定结构的卷积神经网络,该结构遵循VGG16的设计。而`make_layers(cfgs['D'])`是一个函数调用,它接收一个配置参数(在这个例子中是`cfgs['D']`),并根据这个配置生成一系列的卷积层和池化层,这些层构成了VGG模型的主体部分——特征提取层。

具体来说:
- `cfgs['D']`通常是一个列表,其中包含了描述每一层类型(比如卷积层或最大池化层)和该层输出通道数的元组。对于VGG16,这个配置定义了多个卷积层后跟着最大池化层的重复模式,直到达到网络的高层部分。
  
- `make_layers`函数遍历这个配置列表,根据每个条目创建相应的层(使用`nn.Conv2d`和`nn.MaxPool2d`等PyTorch模块),并将它们添加到一个有序的层列表中,最终返回这个列表。这个过程实质上是把VGG的架构从配置信息转换为实际可执行的神经网络层结构。

因此,`model = VGG(make_layers(cfgs['D']))`实质上是构建了一个完整的VGG16模型,准备用于后续的训练或推理任务。

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

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

相关文章

【回文 马拉车】214. 最短回文串

本文涉及知识点 回文 马拉车 LeetCode214. 最短回文串 给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。 示例 1: 输入:s “aacecaaa” 输出:“aaacecaaa” 示…

string类的使用手册

1.构造函数 补充:npos:size_t类型数据的最大值 default (1) string(); 构造空的string类对象 copy (2) string (const string& str); 拷贝构造函数(深拷贝) substring (3) string (const string& str, size_t pos, size_…

python 实现各种数据分析方法

1、相关性分析 1.1、https://zhuanlan.zhihu.com/p/669355778https://zhuanlan.zhihu.com/p/669355778

【AI学习】Together AI的新研究:Together MoA(Mixture of Agents)

第一次听说Mixture of Agents,原来Agent也能混合,有意思! 介绍 Together AI的最新研究“Together MoA”(Mixture of Agents,代理混合)是一种创新的方法,旨在通过结合多个开源大型语言模型&…

Postman使用教程(Postman详细图文教程)

本文讲解的是postman工具安装、postman安装教程、postman工具下载、postman使用教程。Postman使得得开发人员和测试人员能够更高效地与Web服务进行交互和调试。 Postman不仅支持常见的HTTP方法,如GET、POST、PUT、DELETE等,还提供了丰富的请求编辑功能&…

Linux---防火墙

文章目录 目录 文章目录 前言 一.静态防火墙:iptables iptables五链 iptables 四表 iptables控制类型 iptables命令配置 前言 这儿主要介绍Linux系统本身提供的软件防火墙的功能,即数据包过滤机制。 数据包过滤,也就是分析进入主机的网络数…

Ubuntu20.04配置ORBSLAM2并在kitti数据集序列进行实验

一、ORB-SLAM2 安装和编译 1.ORB-SLAM2下载 用以下命令在终端上下载 git clone https://github.com/raulmur/ORB_SLAM2 2.安装Pangolin 在下载了ZIP压缩包后解压缩放在ubantu的/home下(此处只要是英文路径都可以),但别急着安装Pangolin我…

STM32项目分享:智慧农业(机智云)系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片: 哔哩哔哩视频链接: https://www.bilibili.c…

PCIe总线-RK3588 PCIe子系统简介(八)

1.PCIe子系统 RK3588 PCIe子系统如下图所示。总共拥有5个PCIe控制器。PCIe30X4(4L)支持RC和EP模式,其他4个仅支持RC模式。ITS port 1连接PCIe30X4(4L)和PCIe30X2(2L)控制器,PCIe30X4(4L)和PCIe30X2(2L)控制器使用PCIe3.0 PIPE PHY。ITS port 0连接PCIe3…

mysql:简单理解mysql mvcc的可重复读

# 原理 假设有这样的sql begin select(或update、insert、delete) ... commit当执行【begin】的时候,标记有一个新事务要开始,但是事务还没有真正开始,事务id还没有产生当执行事务里面的第一个sql语句时(…

VCG显示——汉字,数字,图像

详细的介绍资料: 【从零开始走进FPGA】 玩转VGA http://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html 【FPGA实验】基于DE2-115平台的VGA显示_vga接口实验 de2-115-CSDN博客 【FPGA】VGA显示文字、彩条、图片——基于DE2-115-CSDN博客 一.VCG原理 1.1…

区块链中的gas与转账收款相关概念

区块链是一个经济系统 计算与存储系统都是稀缺的,区块链的工作需要消耗资源共识、trustless需要矿工的工作,而矿工需要激励Transaction的执行有成本(gas),gas费成为矿工的奖励ether是这个经济生态系统的通行货币 关心的问题 合…

Stable Diffusion vs DALL·E3

大模型技术论文不断,每个月总会新增上千篇。本专栏精选论文重点解读,主题还是围绕着行业实践和工程量产。若在某个环节出现卡点,可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技(Mamba,xLSTM,KAN)则提…

父亲节:我要做爸爸的健康监督员

父亲节将至,总想着能为爸爸做些什么,来表达我们的感激与关爱。在这个特殊的日子里,成为爸爸的健康监督员,用华为 Watch 4 的智慧健康功能,任何时刻都可以关注爸爸的健康状况,放心又安心了。 用一键微体检…

C++ 50 之 继承中的对象模型

继承中的对象模型 在C编译器的内部可以理解为结构体&#xff0c;子类是由父类成员叠加子类新成员而成&#xff1a; #include <iostream> #include <string> using namespace std;class Base03{ public:int m_a; protected:int m_b; private:int m_c; // 哪怕是…

DNS域名解析----分离解析、多域名解析、父域与子域

1 理论部分 1.1 分离解析 DNS的分离解析&#xff0c;是指根据不同的客户端提供不同的域名解析记录。来自不同地址的客户机请求解析同一域名时&#xff0c;为其提供不同的解析结果。也就是内外网客户请求访问相同的域名时&#xff0c;能解析出不同的IP地址&#xff0c;实现负载…

汇编:Linux汇编基本框架与系统调用

在Linux操作系统下进行汇编编程时&#xff0c;基本的汇编程序框架通常包括以下几个部分&#xff1a; ①全局段声明&#xff08;section declarations&#xff09;&#xff1a;定义数据段、代码段等。 ②入口点&#xff08;entry point&#xff09;&#xff1a;程序的执行起点…

Python 显示笔记本电脑的电池状态和百分比

方法一&#xff1a; import psutil import psutil battery psutil.sensors_battery() if battery is None:print("No battery is found.")exit() print (battery) percentagebattery.percent print(f"Battery Percentage: {percentage}%")Battery的信息…

一个在C#中集成Python的例子

一个在C#中集成Python的例子。在C#中可以执行Python脚本&#xff0c;在Python中也可以调用C#宿主中的功能&#xff08;clr.AddReference(Business)&#xff09;。 文件说明 Debug为执行目录 Mgr.exe为执行文件 Py\init.py为python初始化脚本 Py\Lib.zip为python需要的模块&…

数据库 | 期末复习专题(HBUT 韩洪木)

总结&#xff1a; 考研数据库系统概论题目整理_若视图的属性来自聚集函数、表达式,则该视图是可以更新的。-CSDN博客 数据库系统概论 ---知识点大全&#xff08;期末复习版&#xff09;_数据库系统概论期末复习-CSDN博客 1.数据库系统&#xff08;DBS&#xff09;的组成&#…