【深度学习】多头注意力机制的实现|pytorch

在这里插入图片描述

  • 博主简介:努力学习的22级计算机科学与技术本科生一枚🌸
  • 博主主页: @Yaoyao2024
  • 往期回顾:【深度学习】注意力机制| 基于“上下文”进行编码,用更聪明的矩阵乘法替代笨重的全连接
  • 每日一言🌼: 路漫漫其修远兮,吾将上下而求索。—屈原🌺

在这里插入图片描述

0、前言

在上篇文章中,我们介绍了系统且详细的介绍了注意力机制及其数学原理进行系统且详细的讲解。在本篇博客中,我们围绕 多头注意力的代码实现进行展开。

这篇文章的代码实现还是youtube管博主所提供的worksheet:https://github.com/kilianmandon/alphafold-decoded.git

在本篇博客中,我们会根据worksheet中的内容,依次实现以下:

  • MultiHeadAttention:多头注意力机制
  • Gated MultiHeadAttention:带门控的注意力机制
  • Global Gated MultiHeadAttention:全局+门控注意力机制

最终将其整合到一个注意力模块中,利用传递参数的方法选择使用哪种注意力。不过本篇博客主要是从代码方面进行讲解,让对python和pytorch不是很熟悉的同学也能看懂代码。

1. 模型初始化和qkv准备

在这里插入图片描述

1.1 def init

class MultiHeadAttention(nn.Module):"""A MultiHeadAttention module with optional bias and optional gating."""def __init__(self, c_in, c, N_head, attn_dim, gated=False, is_global=False, use_bias_for_embeddings=False):"""Initializes the module. MultiHeadAttention theoretically consists of N_head separate linear layers for the query, key and value embeddings.However, the embeddings can be computed jointly and split afterwards,so we only need one query, key and value layer with larger c_out.Args:c_in (int): Input dimension for the embeddings.c (int): Embedding dimension for each individual head.N_head (int): Number of heads.attn_dim (int): The dimension in the input tensor along whichthe attention mechanism is performed.gated (bool, optional): If True, an additional sigmoid-activated linear layer will be multiplicated against the weighted value vectors before feeding them through the output layer. Defaults to False.is_global (bool, optional): If True, global calculation will be performed.For global calculation, key and value embeddings will only use one head,and the q query vectors will be averaged to one query vector.Defaults to False.use_bias_for_embeddings (bool, optional): If True, query, key, and value embeddings will use bias, otherwise not. Defaults to False."""super().__init__()self.c_in = c_inself.c = cself.N_head = N_headself.gated = gatedself.attn_dim = attn_dimself.is_global = is_global

首先在模型初始化中包含这样几个参数:

  • c_in:输入特征维度
  • c:每个注意力头的特征维度
  • N_head:注意力头的数量
  • attn_dim⭐:计算注意力的维度索引,注意力会沿着这个维度去计算不同元素之间的关联。比如对于上图的输入单词序列Input(N,ci),这里N代表token个数也是序列长度,注意力模型会沿着这个维度,去计算各个token之间的关联。
    🌸在计算点积亲和度(dot - product affinities )时,是在这个维度上不同位置的查询(queries)、键(keys)向量间进行点积运算,衡量不同位置之间的相关性,从而确定注意力权重 。 比如句子中某个词和其他词之间关联程度计算,就是沿着这个维度展开的。
  • gated:是否使用门控机制
  • is_global:是否使用全局注意力:如果是全局注意力key和value在线性层进行变换后只有一个头,query还是多头,但是会在后面q/k/v准备的时候沿着注意力头的方向被平均掉。
  • use_bias_for_embeddings:是否在Q/K/V线性变换中使用偏置(也就是Linear层要不要加偏置和上图中的在注意力得分后加偏置的意义不同)!

关键组件

  1. 线性变换层

    • linear_q:生成查询(Query)向量,输出维度为c*N_head
    • linear_k:生成键(Key)向量,全局模式下输出c,否则c*N_head
    • linear_v:生成值(Value)向量,维度同linear_k
    • linear_o:输出变换层,将多头结果合并回c_in维度
  2. 门控层(可选):

    • linear_g:生成门控信号,使用sigmoid激活
       ########################################################################### TODO: Initialize the query, key, value and output layers.              ##   Whether or not query, key, and value layers use bias is determined   ##   by `use_bias` (False for AlphaFold). The output layer should always  ##   use a bias. If gated is true, initialize another linear with bias.   ##   For compatibility use the names linear_q, linear_k, linear_v,        ##   linear_o and linear_g.                                               ###########################################################################

在初始化部分,我们主要是实现模型输入和输出的几个线性层:

        self.linear_q = nn.Linear(c_in, c*N_head, bias=use_bias_for_embeddings)c_kv = c if is_global else c*N_headself.linear_k = nn.Linear(c_in, c_kv, bias=use_bias_for_embeddings)self.linear_v = nn.Linear(c_in, c_kv, bias=use_bias_for_embeddings)self.linear_o = nn.Linear(c*N_head, c_in)if gated:self.linear_g = nn.Linear(c_in, c*N_head)

整个代码实现如上,用pytorch中的nn.Linear即可。对于当时学到这里的我来说,我并不是很理解在is_global下的处理逻辑:

If True, global calculation will be performed.
For global calculation, key and value embeddings will only use one head,
and the q query vectors will be averaged to one query vector.
Defaults to False.

大致意思是说,k,v使用单头,而q使用多头(然后在和k进行点积计算注意力得分计算之前沿着attn-dim维度进行平均


1.2 prepare_qkv

非全局注意力的q,k,v准备:

    def prepare_qkv(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor):"""Splits the embeddings into individual heads and transforms the inputshapes of form (*, q/k/v, *, N_head*c) into the shape (*, N_head, q/k/v, c). The position of the q/k/v dimension in the original tensors is given by attn_dim.Args:q (torch.Tensor): Query embedding of shape (*, q, *, N_head*c).k (torch.Tensor): Key embedding of shape (*, k, *, N_head*c).v (torch.Tensor): Value embedding of shape (*, v, *, N_head*c).Returns:tuple: The rearranged embeddings q, k, and v of shape (*, N_head, q/k/v, c) respectively."""########################################################################### TODO: Rearrange the tensors with the following changes:                ##   - (*, q/k/v, *, N_head*c) -> (*, q/k/v, N_head*c) with movedim       # #   - (*, q/k/v, N_head*c) -> (*, q/k/v, N_head, c)                      ##   - (*, q/k/v, N_head, c) -> (*, N_head, q/k/v, c)                     ############################################################################ Transposing to [*, q/k/v, N_head*c]q = q.movedim(self.attn_dim, -2)k = k.movedim(self.attn_dim, -2)v = v.movedim(self.attn_dim, -2)# Unwrapping to [*, q/k/v, N_head, c]q_shape = q.shape[:-1] + (self.N_head, -1)k_shape = k.shape[:-1] + (self.N_head, -1)v_shape = v.shape[:-1] + (self.N_head, -1)q = q.view(q_shape)k = k.view(k_shape)v = v.view(v_shape)# Transposing to [*, N_head, q/k/v, c]q = q.transpose(-2, -3)k = k.transpose(-2, -3)v = v.transpose(-2, -3)###########################################################################               END OF YOUR CODE                                         ###########################################################################return q, k, v

1. 移动 attn_dim 维度到倒数第二个位置

  • self.attn_dim 表示查询、键和值维度在原始张量中的位置。
  • movedim 方法用于将 attn_dim 维度移动到倒数第二个位置,这样做是为了方便后续的形状调整操作。经过这一步,张量的形状变为 (*, q/k/v, N_head*c)

在标准的多头注意力计算中,通常会将头的维度放在倒数第三个位置,这样可以更清晰地表示不同的头和每个头的嵌入维度。把 attn_dim 移动到倒数第二个位置,然后再进行后续的维度调整,最终可以得到符合这种习惯的形状,便于后续的注意力计算和代码实现。

2. 将 N_head*c 维度拆分为 N_headc

  • 首先,通过 q.shape[:-1] + (self.N_head, -1) 构建新的形状元组,将最后一个维度 N_head*c 拆分为 N_headc。这里的 -1 表示让 PyTorch 自动计算该维度的大小。
  • 然后,使用 view 方法将张量的形状调整为 (*, q/k/v, N_head, c)

3. 交换倒数第二个和倒数第三个维度

  • transpose 方法用于交换张量的两个维度。这里交换倒数第二个和倒数第三个维度,将 N_head 维度移动到倒数第三个位置,最终得到形状为 (*, N_head, q/k/v, c) 的张量。

1.3 prepare_qkv_global

  def prepare_qkv_global(self, q, k, v):"""Prepares the query, key and value embeddings with the following differences to the non-global version:- key and value embeddings use only one head.- the query vectors are contracted into one, average query vector.Args:q (torch.tensor): Query embeddings of shape (*, q, *, N_head*c).k (torch.tensor): Key embeddings of shape (*, k, *, c).v (torch.tensor): Value embeddings of shape (*, v, *, c).Returns:tuple: The rearranged embeddings q, k, and v ofshape (*, N_head, 1, c) for q and shape (*, 1, k, c) for k and v. """########################################################################### TODO: Rearrange the tensors to match the output dimensions. Use        ##   torch.mean for the contraction of q at the end of this function.     ###########################################################################q = q.movedim(self.attn_dim, -2)k = k.movedim(self.attn_dim, -2)v = v.movedim(self.attn_dim, -2)q_shape = q.shape[:-1] + (self.N_head, self.c)q = q.view(q_shape)q = q.transpose(-2, -3)k = k.unsqueeze(-3)v = v.unsqueeze(-3)q = torch.mean(q, dim=-2, keepdim=True)###########################################################################               END OF YOUR CODE                                         ###########################################################################return q, k, v

因为在上面初始化的时候已经讲到k,v都是单头的,所以在这里无需考虑n-head。但对于q来说,它需要考虑。

其次它的不同是,需要在最后沿着attn-dim的方向进行平均,这样让一个head下只有一个querykey进行矩阵乘法计算注意力得分。


1.4 解释:关于global选项下的qkv

到这里为止,我们把多头注意力的初始化、q/k/v的准备算是讲完了。其实到这里我还有一个疑问:

为什么在考虑Global-attention的时候,只对k/v使用单头?对q保留多头。后来我发现是自己对q/k/v的本身地位没有理解透彻。

如果和cnn类比的话,q相当于卷积核,k/v都是用来表示原始数据的信息。只有卷积核不同,模型才能提取出来各种各样的特征。这里也是类似,只有query不同,模型才能以各个角度去捕捉多样化的信息。k/v可以不用多头,因为它们本质主要为注意力计算提供可匹配信息实际要聚合的特征。单头足以提供关键信息,多头可能引入过多重复或相似信息,造成资源浪费,单头能更高效地提供必要信息 (主要是采用单头计算,能显著减少线性变换等操作次数)。

1. 核心目的:减少计算量
全局注意力的核心思想是将序列级别的全局信息压缩为一个"概要向量",从而避免计算庞大的 N × N N \times N N×N 注意力矩阵( N N N) 是序列长度)。

  • Key/Value单头:所有注意力头共享同一组Key/Value,相当于用单头生成一个"全局记忆池"。
    • 计算量从 O ( N 2 ⋅ H ) O(N^2 \cdot H) O(N2H) 降至 O ( N 2 + N ⋅ H ) O(N^2 + N \cdot H) O(N2+NH) H H H 是头数)。
  • Query多头:保留多头设计,让不同头从不同角度"查询"这个全局记忆池,维持特征多样性。

2. 为什么Query需要多头?
即使Key/Value是全局共享的,不同注意力头仍可关注不同的全局模式

  • 举例(蛋白质序列):
    • 头1可能关注"保守残基"的全局分布。
    • 头2可能关注"疏水残基"的全局密度。
    • 头3可能关注"二级结构"(如α螺旋)的周期性。
  • 数学上
    多组Query与同一组Key/Value计算注意力,仍会得到不同的加权结果(因Query向量不同)。

3. 为什么Key/Value可以单头?

  • 信息冗余假设
    对于超长序列,Key/Value的全局特征(如蛋白质的总体折叠模式)通常不需要多视角编码,一个统一的表示足够。
  • 计算效率
    Key/Value矩阵的维度从 N × ( H ⋅ d k ) N \times (H \cdot d_k) N×(Hdk) 降至 N × d k N \times d_k N×dk,显存占用大幅减少。

2. Forward

    def forward(self, x, bias=None, attention_mask=None):"""Forward pass through the MultiHeadAttention module.Args:x (torch.tensor): Input tensor of shape (*, q/k/v, *, c_in).bias (torch.tensor, optional): Optional bias tensor of shape(*, N_head, q, k) that will be added to the attention weights. Defaults to None.attention_mask (torch.tensor, optional): Optional attention maskof shape (*, k). If set, the keys with value 0 in the mask willnot be attended to.Returns:torch.tensor: Output tensor of shape (*, q/k/v, *, c_in)"""out = Noneq = self.linear_q(x)k = self.linear_k(x)v = self.linear_v(x)if self.is_global:q, k, v = self.prepare_qkv_global(q, k, v)else:q, k, v = self.prepare_qkv(q, k, v)q = q / math.sqrt(self.c)a = torch.einsum('...qc,...kc->...qk', q, k)if bias is not None:bias_batch_shape = bias.shape[:-3]bias_bc_shape = bias_batch_shape + (1,) * (a.ndim-len(bias_batch_shape)-3) + bias.shape[-3:]bias = bias.view(bias_bc_shape)a = a + biasif attention_mask is not None:attention_mask = attention_mask[..., None, None, :]offset = (attention_mask==0) * -1e8a = a + offseta = torch.softmax(a, dim=-1)# o has shape [*, N_head, q, c]o = torch.einsum('...qk,...kc->...qc', a, v)o = o.transpose(-3, -2)o = torch.flatten(o, start_dim=-2)o = o.moveaxis(-2, self.attn_dim)if self.gated:g = torch.sigmoid(self.linear_g(x))o = g * oout = self.linear_o(o)###########################################################################               END OF YOUR CODE                                         ###########################################################################return out
  1. 输入预处理: Create query, key and value embeddings,Rearrange the embeddings with prepare_qkv
q = self.linear_q(x)
k = self.linear_k(x)
v = self.linear_v(x)
if self.is_global:q, k, v = self.prepare_qkv_global(q, k, v)
else:q, k, v = self.prepare_qkv(q, k, v)
  • 通过线性变换生成Query(Q)、Key(K)、Value(V)张量: (*, N_head, q/k/v, c)
  • 如果是全局注意力模式(is_global=True),会调用prepare_qkv_global对KV做特殊处理
  1. Query缩放:Scale the queries by 1/sqrt( c )
q = q / math.sqrt(self.c)
  • 将Query向量除以√d(d是每个头的维度),防止点积结果过大导致softmax梯度消失
  1. 注意力得分计算
a = torch.einsum('...qc,...kc->...qk', q, k)
  • 使用爱因斯坦求和约定计算Q和K的点积
  • 结果张量a的形状为[*, N_head, q, k],表示每个查询位置与每个键位置的相似度
  1. 偏置处理
if bias is not None:bias_batch_shape = bias.shape[:-3]bias_bc_shape = bias_batch_shape + (1,) * (a.ndim-len(bias_batch_shape)-3) + bias.shape[-3:]bias = bias.view(bias_bc_shape)a = a + bias
  • 调整偏置张量的形状使其可以广播到注意力得分矩阵
  • 将偏置加到原始得分上(如AlphaFold中用于注入残基对信息)
  1. 注意力掩码处理
if attention_mask is not None:attention_mask = attention_mask[..., None, None, :]offset = (attention_mask==0) * -1e8a = a + offset
  • 对需要屏蔽的位置(attention_mask==0)加上一个很大的负值(-1e8)
  • softmax后这些位置的权重会趋近于0
  1. Softmax归一化
a = torch.softmax(a, dim=-1)
  • 对最后一个维度(k)做softmax,得到归一化的注意力权重
  1. 加权求和
o = torch.einsum('...qk,...kc->...qc', a, v)
  • 使用注意力权重对Value向量加权求和
  • 输出形状为[*, N_head, q, c]
  1. 输出重组
o = o.transpose(-3, -2)
o = torch.flatten(o, start_dim=-2)
o = o.moveaxis(-2, self.attn_dim)
  • 转置头维和查询维
  • 展平多头输出
  • 将特征维度移动到指定位置(attn_dim)
  1. 门控机制
if self.gated:g = torch.sigmoid(self.linear_g(x))o = g * o
  • 如果启用门控,生成0-1之间的门控值
  • 按元素相乘控制信息流
  1. 最终输出变换
out = self.linear_o(o)
  • 通过最后一个线性层将维度映射回输入维度

这个forward方法是多头注意力机制的核心计算过程,我将逐步解释它的实现逻辑和关键步骤:

1. 输入预处理

q = self.linear_q(x)
k = self.linear_k(x)
v = self.linear_v(x)
  • 通过线性变换生成Query(Q)、Key(K)、Value(V)张量
  • 如果是全局注意力模式(is_global=True),会调用prepare_qkv_global对KV做特殊处理

2. Query缩放

q = q / math.sqrt(self.c)
  • 将Query向量除以√d(d是每个头的维度),防止点积结果过大导致softmax梯度消失

3. 注意力得分计算

a = torch.einsum('...qc,...kc->...qk', q, k)
  • 使用爱因斯坦求和约定计算Q和K的点积
  • 结果张量a的形状为[*, N_head, q, k],表示每个查询位置与每个键位置的相似度

4. 偏置处理

if bias is not None:bias_batch_shape = bias.shape[:-3]bias_bc_shape = bias_batch_shape + (1,) * (a.ndim-len(bias_batch_shape)-3) + bias.shape[-3:]bias = bias.view(bias_bc_shape)a = a + bias
  • 调整偏置张量的形状使其可以广播到注意力得分矩阵
  • 将偏置加到原始得分上(如AlphaFold中用于注入残基对信息)

5. 注意力掩码处理

if attention_mask is not None:attention_mask = attention_mask[..., None, None, :]offset = (attention_mask==0) * -1e8a = a + offset
  • 对需要屏蔽的位置(attention_mask==0)加上一个很大的负值(-1e8)
  • softmax后这些位置的权重会趋近于0(代表不关注这些位置)

6. Softmax归一化

Use softmax to convert the attention scores into a probability distribution.

a = torch.softmax(a, dim=-1)
  • 对最后一个维度(k)做softmax,得到归一化的注意力权重

7. 加权求和

o = torch.einsum('...qk,...kc->...qc', a, v)
  • 使用注意力权重对Value向量加权求和
  • 输出形状为[*, N_head, q, c]

8. 输出重组

    #   - Rearrange the intermediate output in the following way:            ##       * (*, N_head, q, c) -> (*, q, N_head, c)                         ##       * (*, q, N_head, c) -> (*, q, N_head * c)                        ##       * (*, q, N_head * c) -> (*, q, *, N_head * c)                    ##       The order of these transformations is crucial, as moving q
o = o.transpose(-3, -2)
o = torch.flatten(o, start_dim=-2)
o = o.moveaxis(-2, self.attn_dim)
  • 转置头维和查询维
  • 展平多头输出
  • 将特征维度移动到指定位置(attn_dim)

9. 门控机制

if gated, calculate the gating with linear_g and sigmoid and multiply it against the output.

if self.gated:g = torch.sigmoid(self.linear_g(x))o = g * o
  • 如果启用门控,生成0-1之间的门控值
  • 按元素相乘控制信息流

10. 最终输出变换

apply linear_o to calculate the final output.

out = self.linear_o(o)
  • 通过最后一个线性层将维度映射回输入维度

关键设计特点:

  1. 高效张量操作:使用einsum进行批量矩阵运算
  2. 灵活的维度处理:支持任意批处理维度和自定义注意力维度
  3. 模块化设计:可插拔的偏置、掩码和门控机制
  4. 全局注意力支持:通过is_global标志切换模式

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

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

相关文章

java16

1.API续集 可以导入别人写好的clone的jar包 注意:方法要有调用者,如果调用者是null就会报错 2.如何导入别人写好的jar包 复制jar包然后粘贴在lib里面,然后右键点击jar包再点击下面的add 3.关于打印java中的引用数据类型

PostgreSQL的扩展 credcheck

PostgreSQL的扩展 credcheck credcheck 是 PostgreSQL 的一个安全扩展,专门用于强制实施密码策略和凭证检查,特别适合需要符合安全合规要求的数据库环境。 一、扩展概述 1. 主要功能 强制密码复杂度要求防止使用常见弱密码密码过期策略实施密码重复使…

MyBatis中的@Param注解-如何传入多个不同类型的参数

mybatis中参数识别规则 默认情况下,MyBatis 会按照参数位置自动分配名称:param1, param2, param3, ...或者 arg0, arg1。 // Mapper 接口方法 User getUserByIdAndName(Integer id, String name); 以上接口在XML中只能通过param1或者arg0这样的方式来引用,可读性差。 &l…

DIFY教程第一集:安装Dify配置环境

一、Dify的介绍 https://dify.ai/ Dify 是一款创新的智能生活助手应用,旨在为您提供便捷、高效的服务。通过人工智能技术, Dify 可以实现语音 助手、智能家居控制、日程管理等功能,助您轻松应对生活琐事,享受智慧生活。简约的…

5、Rag基础:RAG 专题

RAG 简介 什么是检索增强生成? 检索增强生成(RAG)是指对大型语言模型输出进行优化,使其能够在生成响应之前引用训练数据来源之外的权威知识库。大型语言模型(LLM)用海量数据进行训练,使用数十亿个参数为回答问题、翻译语言和完成句子等任务生成原始输出。在 LLM 本就强…

GAMES202-高质量实时渲染(homework1)

目录 Homework1shadow MapPCF(Percentage Closer Filter)PCSS(Percentage Closer Soft Shadow) GitHub主页:https://github.com/sdpyy1 作业实现:https://github.com/sdpyy1/CppLearn/tree/main/games202 Homework1 shadow Map 首先需要完成MVP矩阵的构造&#xf…

JDK(Ubuntu 18.04.6 LTS)安装笔记

一、前言 本文与【MySQL 8(Ubuntu 18.04.6 LTS)安装笔记】同批次:先搭建数据库,再安装JDK,后面肯定就是部署Web应用:典型的单机部署。“麻雀虽小五脏俱全”,善始善终,还是记下来吧。…

软件测试之接口测试常见面试题

一、什么是(软件)接口测试? 接口测试:是测试系统组件间接口的一种测试方法 接口测试的重点:检查数据的交换,数据传递的正确性,以及接口间的逻辑依赖关系 接口测试的意义:在较早期开展,在软件开发的同时…

Lua 第11部分 小插曲:出现频率最高的单词

在本章中,我们要开发一个读取并输出一段文本中出现频率最高的单词的程序。像之前的小插曲一样,本章的程序也十分简单但是也使用了诸如迭代器和匿名函数这样的高级特性。 该程序的主要数据结构是一个记录文本中出现的每一个单词及其出现次数之间关系的表。…

软件项目进度管理活动详解

目录 1. 活动定义(Activity Definition) 2. 活动排序(Activity Sequencing) 3. 活动资源估算(Activity Resource Estimating) 4. 活动历时估算(Activity Duration Estimating) …

docker 国内源和常用命令

Ubuntu | Docker Docs 参考docker官方安装docker # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt…

身份与访问管理(IAM):零信任架构下的认证授权技术与实战

身份与访问管理(IAM):零信任架构下的认证授权技术与实战 在网络安全防御体系中,身份与访问管理(Identity and Access Management, IAM)是守护数字资产的“数字门禁系统”。随着远程办公和多云架构的普及&a…

Maven进阶知识

一、Maven 坐标 (一)概念 在 Maven 中坐标是构件的唯一标识,其元素包括 groupId、artifactId、version、packaging、classifier。其中 groupId、artifactId、version 是必定义项,packaging 默认为 jar。 (二&#x…

网络原理 ——TCP 协议

TCP 报文结构 TCP 头部 20字节(无选项),关键字段: 字段长度(bit)说明源端口16发送方端口目的端口16接收方端口序列号(seq)32数据字节的编号确认号(ack)32期…

C#使用sftp远程拷贝文件

需要下载 的包:Core.Renci.SshNet 下载依赖包的时候需要注意版本,高版本的.net环境不支持会用不了,我用的.net5,所以下载的2021.10.2 功能的核心式创建一个SftpClient,并传入所需要的参数:远程IP地址,端口…

文本预处理(NLTK)

1. 自然语言处理基础概念 1.1 什么是自然语言处理 自然语言处理( Natural Language Processing, NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门融语言学、计算机科学、数学于…

socket编程基础

上一篇 --- 网络基础概念(下)https://blog.csdn.net/Small_entreprene/article/details/147320155?fromshareblogdetail&sharetypeblogdetail&sharerId147320155&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link 理…

CSS 解决手机浏览器默认行为(点击出现蓝色背景)

最近写了一个 Web 应用,可以兼容手机端和PC端,在PC端调试的时候没有发现这个问题,但是在手机上或者PC浏览器改成手机模式进行调试的时候就会出现下面这个场景: 这是两个 div,点击的时候,会出现一个蓝色的背…

多模态大语言模型arxiv论文略读(三十八)

Tables as Texts or Images: Evaluating the Table Reasoning Ability of LLMs and MLLMs ➡️ 论文标题:Tables as Texts or Images: Evaluating the Table Reasoning Ability of LLMs and MLLMs ➡️ 论文作者:Naihao Deng, Zhenjie Sun, Ruiqi He, A…

聊聊Spring AI Alibaba的YuQueDocumentReader

序 本文主要研究一下Spring AI Alibaba的YuQueDocumentReader YuQueDocumentReader community/document-readers/spring-ai-alibaba-starter-document-reader-yuque/src/main/java/com/alibaba/cloud/ai/reader/yuque/YuQueDocumentReader.java public class YuQueDocument…