从零开始构建 Vision Transformer(ViT) 模型

news2024/9/18 10:20:45

Transformer 模型最早由 Vaswani 等人在 2017 年论文 Attention Is All You Need 中提出,并已广泛应用于自然语言处理。

2021年,Dosovitsky 等人在论文An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale中提出将 Transformer 用于计算机视觉任务,与卷积网络相比取得了优异的结果。

在本文中,我将从头开始构建一个视觉 Transformer 模型,并在 MNIST 数据集上进行测试。这对于算法本身的理解和算法面试大有好处

喜欢本文记得收藏、点赞、关注,希望大模型技术交流的加入我们。

技术交流

技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。

成立了算法面试和技术交流群,相关资料、技术交流&答疑,均可加我们的交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、微信搜索公众号:机器学习社区,后台回复:加群
方式②、添加微信号:mlc2040,备注:来自CSDN + 技术交流

通俗易懂讲解大模型系列

  • 重磅消息!《大模型面试宝典》(2024版) 正式发布!

  • 重磅消息!《大模型实战宝典》(2024版) 正式发布!

  • 做大模型也有1年多了,聊聊这段时间的感悟!

  • 用通俗易懂的方式讲解:大模型算法工程师最全面试题汇总

  • 用通俗易懂的方式讲解:不要再苦苦寻觅了!AI 大模型面试指南(含答案)的最全总结来了!

  • 用通俗易懂的方式讲解:我的大模型岗位面试总结:共24家,9个offer

  • 用通俗易懂的方式讲解:大模型 RAG 在 LangChain 中的应用实战

  • 用通俗易懂的方式讲解:ChatGPT 开放的多模态的DALL-E 3功能,好玩到停不下来!

  • 用通俗易懂的方式讲解:基于扩散模型(Diffusion),文生图 AnyText 的效果太棒了

  • 用通俗易懂的方式讲解:在 CPU 服务器上部署 ChatGLM3-6B 模型

  • 用通俗易懂的方式讲解:ChatGLM3-6B 部署指南

  • 用通俗易懂的方式讲解:使用 LangChain 封装自定义的 LLM,太棒了

  • 用通俗易懂的方式讲解:基于 Langchain 和 ChatChat 部署本地知识库问答系统

  • 用通俗易懂的方式讲解:Llama2 部署讲解及试用方式

  • 用通俗易懂的方式讲解:一份保姆级的 Stable Diffusion 部署教程,开启你的炼丹之路

  • 用通俗易懂的方式讲解:LlamaIndex 官方发布高清大图,纵览高级 RAG技术

  • 用通俗易懂的方式讲解:为什么大模型 Advanced RAG 方法对于AI的未来至关重要?

  • 用通俗易懂的方式讲解:基于 Langchain 框架,利用 MongoDB 矢量搜索实现大模型 RAG 高级检索方法

导入库和模块

import torch
import torch.nn as nn
import torchvision.transforms as T
from torch.optim import Adam
from torchvision.datasets.mnist import MNIST
from torch.utils.data import DataLoader
import numpy as np

我们将使用PyTorch来构建我们的视觉Transformer,因此我们需要导入PyTorch库及其他将在本教程中使用的库。

首先,我们导入PyTorch和其神经网络模块:

import torch
import torch.nn as nn

我们还需要导入torchvision.transforms以调整输入图像的大小并将其转换为张量。调整输入图像的大小是可选的,只需确保图像尺寸可以被patch的尺寸整除。

import torchvision.transforms as T

我们将使用Adam作为优化器,因此需要从torch.optim中导入它。

from torch.optim import Adam

我们将从torchvision中导入本教程使用的MNIST数据集。我们将使用PyTorch的DataLoader来帮助加载数据,因此也需要导入它。

from torchvision.datasets.mnist import MNIST
from torch.utils.data import DataLoader

最后,我们需要导入numpy,在创建位置编码时,我们将使用它来执行sin和cos计算。

import numpy as np

Patch Embeddings

class PatchEmbedding(nn.Module):
  def __init__(self, d_model, img_size, patch_size, n_channels):
    super().__init__()

    self.d_model = d_model # 模型维度
    self.img_size = img_size # 图像尺寸
    self.patch_size = patch_size # Patch尺寸
    self.n_channels = n_channels # 通道数

    self.linear_project = nn.Conv2d(self.n_channels, self.d_model, kernel_size=self.patch_size, stride=self.patch_size)

  # B: 批量大小
  # C: 图像通道
  # H: 图像高度
  # W: 图像宽度
  # P_col: Patch列
  # P_row: Patch行
  def forward(self, x):
    x = self.linear_project(x) # (B, C, H, W) -> (B, d_model, P_col, P_row)

    x = x.flatten(2) # (B, d_model, P_col, P_row) -> (B, d_model, P)

    x = x.transpose(1, 2) # (B, d_model, P) -> (B, P, d_model)
    
    return x

创建视觉Transformer的第一步是将输入图像拆分为patch,并创建这些patch的线性嵌入序列。我们可以使用PyTorch的Conv2d方法实现这一点。

Conv2d方法将输入图像拆分为patch,并提供与模型宽度相等的线性投影。通过将kernel_sizestride设置为patch的大小,我们确保patch的尺寸正确且没有重叠。

self.linear_project = nn.Conv2d(self.n_channels, self.d_model, kernel_size=self.patch_size, stride=self.patch_size)

forward方法中,我们通过linear_project/Conv2D方法传递形状为(B, C, H, W)的输入,并接收形状为(B, d_model, P_col, P_row)的输出。

def forward(self, x):
  x = self.linear_project(x) # (B, C, H, W) -> (B, d_model, P_col, P_row)


我们使用 flatten 方法将补丁列和补丁行维度组合成一个补丁维度,使其形状变为 (B, d_model, P)

x = x.flatten(2) # (B, d_model, P_col, P_row) -> (B, d_model, P)

最后,我们使用 transpose 方法将 d_model 和补丁维度交换,使其形状变为 (B, P, d_model)。

x = x.transpose(-2, -1) # (B, d_model, P) -> (B, P, d_model)


类 Token 和位置编码

class PositionalEncoding(nn.Module):
  def __init__(self, d_model, max_seq_length):
    super().__init__()

    self.cls_token = nn.Parameter(torch.randn(1, 1, d_model))  # 分类 Token

    # 创建位置编码
    pe = torch.zeros(max_seq_length, d_model)

    for pos in range(max_seq_length):
      for i in range(d_model):
        if i % 2 == 0:
          pe[pos][i] = np.sin(pos / (10000 ** (i / d_model)))
        else:
          pe[pos][i] = np.cos(pos / (10000 ** ((i - 1) / d_model)))

    self.register_buffer('pe', pe.unsqueeze(0))

  def forward(self, x):
    # 扩展以使每个图像在批处理中都有分类 Token
    tokens_batch = self.cls_token.expand(x.size()[0], -1, -1)

    # 将分类 Token 添加到每个嵌入的开头
    x = torch.cat((tokens_batch, x), dim=1)

    # 将位置编码添加到嵌入中
    x = x + self.pe

    return x

视觉 Transformer 模型使用标准方法将可学习的分类 Token 添加到补丁嵌入中以进行分类。

self.cls_token = nn.Parameter(torch.randn(1, 1, d_model))


与 LSTM 等模型顺序地接收嵌入不同,Transformer 并行地接收嵌入。虽然这提高了速度,但 Transformer 并不了解序列的顺序。这是一个问题,因为改变图像补丁的顺序很可能会改变图像的内容及其所表示的内容。一个例子是图 5,它显示了改变图像补丁顺序可以将图像从一个 O 改变为更像 X 的东西。

为了解决这个问题,需要将位置编码添加到补丁嵌入中。每个位置编码都是唯一的,表示它所代表的位置,这使得模型能够识别每个嵌入应该放在哪个位置。为了将位置编码添加到嵌入中,它们必须具有相同的维度,即 d_model。我们通过使用图 6 中的方程来获取位置编码。

pe = torch.zeros(max_seq_length, d_model)

for pos in range(max_seq_length):
  for i in range(d_model):
    if i % 2 == 0:
      pe[pos][i] = np.sin(pos / (10000 ** (i / d_model)))
    else:
      pe[pos][i] = np.cos(pos / (10000 ** ((i - 1) / d_model)))

self.register_buffer('pe', pe.unsqueeze(0))

在 forward 方法中,输入是多个图像的补丁嵌入的批量。因为这个原因,我们需要使用 expand 函数来使用 self.cls_token 为批量中的每个图像创建分类 token。

def forward(self, x):
    tokens_batch = self.cls_token.expand(x.size()[0], -1, -1)

这些分类 token 然后通过使用 torch.cat 方法添加到每个补丁嵌入的开头。

x = torch.cat((tokens_batch, x), dim=1)

位置编码在输出前添加。

x = x + self.pe

return x

注意力头

class AttentionHead(nn.Module):
  def __init__(self, d_model, head_size):
    super().__init__()
    self.head_size = head_size

    self.query = nn.Linear(d_model, head_size)
    self.key = nn.Linear(d_model, head_size)
    self.value = nn.Linear(d_model, head_size)

  def forward(self, x):
    # 获取 Queries, Keys 和 Values
    Q = self.query(x)
    K = self.key(x)
    V = self.value(x)

    # Queries 和 Keys 的点积
    attention = Q @ K.transpose(-2, -1)

    # 缩放
    attention = attention / (self.head_size ** 0.5)

    attention = torch.softmax(attention, dim=-1)

    attention = attention @ V

    return attention


视觉 Transformer 使用注意力机制,这是一种通信机制,允许模型关注图像的重要部分。注意力得分可以使用图 8 中的公式计算。

计算注意力的第一步是获取令牌的查询、键和值。令牌的查询表示该令牌所寻找的内容,键表示该令牌所包含的内容,而值表示令牌之间传递的信息。可以通过将令牌传递给线性模块来计算查询、键和值。

def forward(self, x):
    # 获取查询、键和值
    Q = self.query(x)
    K = self.key(x)
    V = self.value(x)

我们可以通过获取查询和键的点积来得到序列中令牌之间的关系。

# 查询和键的点积
attention = Q @ K.transpose(-2, -1)

我们需要对这些值进行缩放,以控制初始化时的方差,从而使令牌能够从多个其他令牌中聚合信息。缩放通过将点积除以注意力头大小的平方根来实现。

attention = attention / (self.head_size ** 0.5)

然后,我们需要对缩放后的点积应用软最大化。

attention = torch.softmax(attention, dim=-1)

最后,我们需要获取软最大化和值矩阵之间的点积。这本质上是传递相应令牌之间的信息。

attention = attention @ V
return attention

多头注意力

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        self.head_size = d_model // n_heads
        self.W_o = nn.Linear(d_model, d_model)
        self.heads = nn.ModuleList([AttentionHead(d_model, self.head_size) for _ in range(n_heads)])

    def forward(self, x):
        # 合并注意力头
        out = torch.cat([head(x) for head in self.heads], dim=-1)
        out = self.W_o(out)
        return out

多头注意力是在并行运行多个自注意力头并将它们组合在一起。我们可以通过将注意力头添加到模块列表中来实现这一点,

self.heads = nn.ModuleList([AttentionHead(d_model, self.head_size) for _ in range(n_heads)])

然后通过输入并连接结果。

def forward(self, x):
    # 合并注意力头
    out = torch.cat([head(x) for head in self.heads], dim=-1)
    out = self.W_o(out)
    return out

Transformer编码器

class TransformerEncoder(nn.Module):
    def __init__(self, d_model, n_heads, r_mlp=4):
        super().__init__()
        self.d_model = d_model
        self.n_heads = n_heads

        # 子层1归一化
        self.ln1 = nn.LayerNorm(d_model)

        # 多头注意力
        self.mha = MultiHeadAttention(d_model, n_heads)

        # 子层2归一化
        self.ln2 = nn.LayerNorm(d_model)

        # 多层感知器
        self.mlp = nn.Sequential(
            nn.Linear(d_model, d_model * r_mlp),
            nn.GELU(),
            nn.Linear(d_model * r_mlp, d_model)
        )

    def forward(self, x):
        # 子层1后的残差连接
        out = x + self.mha(self.ln1(x))

        # 子层2后的残差连接
        out = out + self.mlp(self.ln2(out))

        return out

Transformer编码器由两个子层组成:第一个子层执行多头注意力,第二个子层包含一个多层感知器。多头注意力子层在令牌之间进行通信,而多层感知器子层允许令牌单独“思考”传递给它们的信息。

层归一化是一种优化技术,它独立地对批次中的每个输入进行特征归一化。对于我们的模型,我们将在每个子层的开始通过一个层归一化模块。

# 子层1归一化
self.ln1 = nn.LayerNorm(d_model)

# 子层2归一化
self.ln2 = nn.LayerNorm(d_model)

MLP将由两个线性层和一个GELU层组成。使用GELU而不是RELU是因为它没有RELU在零点不可微的局限性。

# 编码器多层感知器
self.mlp = nn.Sequential(
    nn.Linear(width, width * r_mlp),
    nn.GELU(),
    nn.Linear(width * r_mlp, width)
)

在编码器的forward方法中,输入首先通过第一个层归一化模块,然后执行多头注意力。原始输入加上多头注意力的输出,创建一个残差连接。

然后,这个结果通过另一个层归一化模块,再输入到MLP中。通过将MLP的输出加到第一个残差连接的输出中,创建另一个残差连接。

残差连接用于帮助防止梯度消失问题,通过创建一个梯度可以不受阻碍地反向传播到原始输入的路径。

def forward(self, x):
    # 子层1后的残差连接
    out = x + self.mha(self.ln1(x))

    # 子层2后的残差连接
    out = out + self.mlp(self.ln2(out))

    return out

视觉Transformer

class VisionTransformer(nn.Module):
    def __init__(self, d_model, n_classes, img_size, patch_size, n_channels, n_heads, n_layers):
        super().__init__()

        assert img_size[0] % patch_size[0] == 0 and img_size[1] % patch_size[1] == 0, "img_size的维度必须能被patch_size的维度整除"
        assert d_model % n_heads == 0, "d_model必须能被n_heads整除"

        self.d_model = d_model  # 模型维度
        self.n_classes = n_classes  # 类别数量
        self.img_size = img_size  # 图片大小
        self.patch_size = patch_size  # patch大小
        self.n_channels = n_channels  # 通道数量
        self.n_heads = n_heads  # 注意力头数量

        self.n_patches = (self.img_size[0] * self.img_size[1]) // (self.patch_size[0] * self.patch_size[1])
        self.max_seq_length = self.n_patches + 1

        self.patch_embedding = PatchEmbedding(self.d_model, self.img_size, self.patch_size, self.n_channels)
        self.positional_encoding = PositionalEncoding(self.d_model, self.max_seq_length)
        self.transformer_encoder = nn.Sequential(*[TransformerEncoder(self.d_model, self.n_heads) for _ in range(n_layers)])

        # 分类MLP
        self.classifier = nn.Sequential(
            nn.Linear(self.d_model, self.n_classes),
            nn.Softmax(dim=-1)
        )

    def forward(self, images):
        x = self.patch_embedding(images)
        x = self.positional_encoding(x)
        x = self.transformer_encoder(x)
        x = self.classifier(x[:, 0])
        return x

在创建视觉Transformer类时,我们首先需要确保输入图像可以均匀地分割成patch大小的块,并且模型的维度可以被注意力头的数量整除。

assert img_size[0] % patch_size[0] == 0 and img_size[1] % patch_size[1] == 0, "img_size的维度必须能被patch_size的维度整除"
assert d_model % n_heads == 0, "d_model必须能被n_heads整除"

我们还需要计算位置编码的最大序列长度,该长度等于patch的数量加一。可以通过将输入图像的高度和宽度的乘积除以patch大小的高度和宽度的乘积来找到patch的数量。

self.n_patches = (self.img_size[0] * self.img_size[1]) // (self.patch_size[0] * self.patch_size[1])
self.max_seq_length = self.n_patches + 1

视觉Transformer还需要能够包含多个编码器模块。这可以通过将一系列编码器层放入顺序包装器中实现。

self.encoder = nn.Sequential(*[TransformerEncoder(self.d_model, self.n_heads) for _ in range(n_layers)])

视觉Transformer模型的最后部分是MLP分类头。这由一个线性层和一个softmax层组成。

self.classifier = nn.Sequential(
    nn.Linear(self.d_model, self.n_classes),
    nn.Softmax(dim=-1)
)

在forward方法中,输入图像首先通过patch嵌入层,将图像分割成patch并获取这些patch的线性嵌入序列。然后,它们通过位置编码层添加分类令牌和位置编码,再通过编码器模块。分类令牌然后通过分类MLP确定图像的类别。

def forward(self, images):
    x = self.patch_embedding(images)
    x = self.positional_encoding(x)
    x = self.encoder(x)
    x = self

.classifier(x[:, 0])
    return x

我们完成了模型的构建。现在我们需要训练和测试它。

训练参数

d_model = 9
n_classes = 10
img_size = (32, 32)
patch_size = (16, 16)
n_channels = 1
n_heads = 3
n_layers = 3
batch_size = 128
epochs = 5
alpha = 0.005

加载MNIST数据集

transform = T.Compose([
    T.Resize(img_size),
    T.ToTensor()
])

train_set = MNIST(
    root="./../datasets", train=True, download=True, transform=transform
)
test_set = MNIST(
    root="./../datasets", train=False, download=True, transform=transform
)

train_loader = DataLoader(train_set, shuffle=True, batch_size=batch_size)
test_loader = DataLoader(test_set, shuffle=False, batch_size=batch_size)

训练

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("Using device: ", device, f"({torch.cuda.get_device_name(device)})" if torch.cuda.is_available() else "")

transformer = VisionTransformer(d_model, n_classes, img_size, patch_size, n_channels, n_heads, n_layers).to(device)

optimizer = Adam(transformer.parameters(), lr=alpha)
criterion = nn.CrossEntropyLoss()

for epoch in range(epochs):
    training_loss = 0.0
    for i, data in enumerate(train_loader, 0):
        inputs, labels = data
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()

        outputs = transformer(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        training_loss += loss.item()

    print(f'Epoch {epoch + 1}/{epochs} loss: {training_loss / len(train_loader) :.3f}')

测试

correct = 0
total = 0

with torch.no_grad():
    for data in test_loader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)

        outputs = transformer(images)

        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    print(f'\nModel Accuracy: {100 * correct // total} %')

结果

使用此模型,我们在仅训练5个epoch后,在MNIST数据集上实现了约92%的准确率。这个示例展示了自注意力可以作为深度卷积网络的替代方案。

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

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

相关文章

信息系统项目管理师0133:工具与技术(8项目整合管理—8.8实施整体变更控制—8.8.2工具与技术)

点击查看专栏目录 文章目录 8.8.2 工具与技术8.8.2 工具与技术 专家判断实施整体变更控制过程中,应征求具备如下领域相关专业知识或接受过相关培训的个人或小组的意见,涉及领域包括:关于项目所在的行业以及项目关注的领域的技术知识;法律法规;法规与采购;配置管理;风险管…

万物循环:在游戏开发与服务器监听中的极致应用

新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一、循环的极致场景 代码示例 二、无限循环的机智运用 代码示例 三、总结 一、循环的极致…

字典的创建和删除

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中,字典与列表类似,也是可变序列,不过与列表不同,它是无序的可变序列,保存的内容…

mysql 、oss 结合使用

以下是一个使用 Express、MySQL、OSS 和 axios 的 Node.js 示例。这个示例创建了一个 Express 服务器,该服务器有一个路由用于处理视频上传的请求。视频文件首先被上传到 OSS,然后视频的 OSS URL 被存储到 MySQL 数据库。 首先,我们需要安装必…

2024.5.27 作业 xyt

定义自己的命名空间my_sapce&#xff0c;在my_sapce中定义string类型的变量s1&#xff0c;再定义一个函数完成对字符串的逆置 #include <iostream>using namespace std; namespace my_space {string s1"hello world";void my_strreverse(string str); } using…

四川汇聚荣科技有限公司好不好?

在当今科技飞速发展的时代&#xff0c;企业要想在激烈的市场竞争中脱颖而出&#xff0c;不仅需要先进的技术支持&#xff0c;还需要优质的服务和良好的口碑。那么&#xff0c;四川汇聚荣科技有限公司是否具备这些条件呢?接下来&#xff0c;我们将从公司实力、服务质量、客户反…

声学特征在膝关节健康诊断中的应用分析

关键词&#xff1a;膝关节声发射、膝关节生物标志物、因果关系、机器学习 声学膝关节健康评估长期以来一直被看作是一种替代临床可用医学成像工具的替代方法&#xff0c;如声发射技术是通过检测膝关节在运动过程中产生的微小裂纹或损伤引起的声波信号&#xff0c;从而评估关节的…

人工智能超万卡集群的核心设计原则和架构

超万卡集群的核心设计原则和架构 超万卡集群建设方兴未艾,当前主要依托英伟达GPU及其设备。英伟达GPU在大模型训练中表现卓越,但国产AI芯片虽进步显著,性能与生态构建仍存差距。面对诸多挑战,构建技术领先、基于国产生态的超万卡集群,仍需不断突破与创新。 大模型升级至万…

leetcode 1225 报告系统状态的连续日期(postgresql)

需求 系统 每天 运行一个任务。每个任务都独立于先前的任务。任务的状态可以是失败或是成功。 编写一个 SQL 查询 2019-01-01 到 2019-12-31 期间任务连续同状态 period_state 的起止日期&#xff08;start_date 和 end_date&#xff09;。即如果任务失败了&#xff0c;就是失…

AppInventor2 代码调试方法,99%的人可能不知道这种方式!

AppInventor2 基本上所有调试App的前提是安装好AI伴侣&#xff0c;并与AI伴侣连接后进行&#xff0c;我们在设计界面的每次改动&#xff0c;都会实时的反映在AI伴侣中。 AI伴侣可以运行在手机上&#xff0c;也可以是安卓模拟器。 AI伴侣具体的安装及连接步骤请参考&#xff1…

【C++】set与map

目录 一、键值对 二、set 1. set的模板参数列表 2. set的构造 3. set的迭代器 4. set的容量 5. set的修改 6. set的查找 三、map 1. map的模板参数列表 2. map的构造 3. map的迭代器 4. map的容量 5. map的修改 6. map的查找 一、键值对 用来表示具有一一对应关…

微服务八股-分布式事务-注册中心-服务保护

一、分布式事务 1.CAP和BASE 三者不能同时存在。 CP&#xff1a;由于网络分片的存在&#xff0c;如果要保证强一致性就不能写&#xff0c;此时不满足可用性 AP&#xff1a;由于网络分片的存在&#xff0c;如果要保证可用性&#xff0c;能读也能写&#xff0c;就不能保证强一致…

蜂窝物联水雨情监测:实时监测水雨情,提前预警守护平安家园!

蜂窝物联水雨情监测系统是一个集成了物联网技术的综合系统&#xff0c;主要用于实时监测和管理水文、气象信息&#xff0c;特别是关于水体&#xff08;如河流、湖泊等&#xff09;的水位和降雨量等关键参数。 水位、雨量等数据监测 实时采集水位计、雨量等数据&#xff1b;通过…

Google Extension 【Google 最佳扩展插件】

pockettube: youtube manager 订阅号分组沉浸式翻译&#xff1a;全网口碑炸裂的双语对照网页翻译插件Google 翻译腾讯翻译篡改猴MetaMaskGlarity: Summarize & Translate Any Page

执行普罗米修斯插件mysqld_exporter出现闪退问题如何解决?

运行 mysqld_exporter.exe 文件闪退的问题可能是由于配置文件或环境变量设置不正确导致的。 检查配置文件 my.cnf&#xff1a; 打开 my.cnf 文件&#xff0c;确保其中的配置项正确无误&#xff0c;尤其是 MySQL 数据库的连接信息。配置示例&#xff1a;[client] useryour_mysql…

Android studio版本和Android gradle plugin版本对应表

1.Android studio 版本的升级&#xff0c;一个方面上看主要是升级对AGP最高版本的支持 2.那为什么AGP要出高版本呢&#xff0c;主要支持高版本的API&#xff0c;真是一环扣一环

3.5 四个子空间的维度

一、概述 这一节的主要定理是将秩与维度联系在一起。矩阵的秩就是主元的个数&#xff0c;子空间的维度是基向量的个数&#xff0c;我们计算出这两个数就可以得到秩与维度。 A A A 的秩揭露了四个基本子空间的维度。 四个子空间中&#xff0c;两个子空间来自 A A A&#xff0c…

01Python相关基础学习

Python基础 模块相关导入模块sys模块 模块相关 导入模块 1. import 模块名 2. import 模块名 as 别名 3. from 模块名 import 成员名 as 别名sys模块 1. sys.argv 介绍: 实现从程序的外部想程序传递参数返回的是一个列表,第一个元素是程序文件名,第二个元素是程序外部传入的…

抖音小店怎么对接达人合作?达人带货的细节分享,附邀约达人话术

大家好&#xff0c;我是电商花花 人有多大胆&#xff0c;地就有多大产&#xff0c;做抖店想要出单&#xff0c;爆单&#xff0c;那必须要对接大量的达人来帮我们带货&#xff0c;抖音小店就是直播电商&#xff0c;帮我们对接的达人越多&#xff0c;出单就越多。 所以做抖店如…

龙蜥开发者说:夏日编码奇遇,中国科学院大学学子的开源世界探索记 | 第 27 期

「龙蜥开发者说」第 27 期来了&#xff01;开发者与开源社区相辅相成&#xff0c;相互成就&#xff0c;这些个人在龙蜥社区的使用心得、实践总结和技术成长经历都是宝贵的&#xff0c;我们希望在这里让更多人看见技术的力量。本期故事&#xff0c;我们邀请了中国科学院大学学生…