自注意力机制全解析:从原理到计算细节,一文尽览!

本文涉及的产品
NLP 自学习平台,3个模型定制额度 1个月
NLP自然语言处理_高级版,每接口累计50万次
NLP自然语言处理_基础版,每接口每天50万次
简介: 自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。

引言

自注意力机制的历史背景与发展

自注意力机制(Self-Attention)的概念最早可以追溯到20世纪70年代的神经网络研究,但直到近年来才在深度学习领域得到广泛关注和发展。现代意义上的自注意力机制首次出现在2017年的论文《Attention is All You Need》中,该论文由Google Brain团队提出,并引入了Transformer架构。这一创新迅速改变了自然语言处理(NLP)领域的格局。

在此之前,循环神经网络(RNN)及其变体长短期记忆网络(LSTM)和门控循环单元(GRU)是处理序列数据的主要方法。然而,这些模型存在一些固有的局限性,比如难以并行化训练、捕捉长距离依赖关系的能力有限等。此外,随着序列长度增加,RNN类模型的表现往往会下降。

为了解决这些问题,研究人员开始探索基于注意力机制的方法,它最初是为了改善编码器-解码器框架下的机器翻译任务而设计的。传统的注意力机制允许模型在生成输出时集中于输入序列中的某些特定部分,从而提高了性能。但是,这种外部注意力机制仍然依赖于编码器提供的上下文信息。

相比之下,自注意力机制不依赖于任何外部信息源,而是直接关注输入序列内部元素之间的相互作用。这不仅使得模型能够更有效地捕捉序列内部复杂的依赖关系,还极大地促进了模型的并行化训练,因为每个位置上的计算都可以独立进行。因此,自注意力机制成为构建高效且强大的序列建模工具的关键组件之一。

自注意力机制的概念

自注意力机制(Self-Attention),也称为内部注意力机制,是一种将单个序列的不同位置关联起来以计算同一序列的表示的注意力机制。这种机制允许模型在处理序列数据时,动态地调整对每个元素的关注程度,从而捕捉序列内部的复杂依赖关系。

自注意力机制的核心在于,它不依赖于外部信息,而是在序列内部元素之间进行信息的交互和整合。这意味着,对于序列中的每个元素,自注意力机制会计算该元素与序列中所有其他元素的相关性,生成一个加权的表示,其中权重反映了元素间的相互关系。

image.png

自注意力机制的计算过程可以被分解为几个关键步骤。

  1. 输入序列被映射到查询(Query)、键(Key)和值(Value)三个向量。

  2. 通过计算查询向量与所有键向量之间的点积来获得注意力得分。这些得分随后被缩放并经过Softmax函数进行归一化,以获得每个元素的注意力权重。

  3. 这些权重被用来对值向量进行加权求和,生成最终的输出序列。

自注意力机制在现代深度学习模型中的重要性

自注意力机制的重要性在于其灵活性和强大表达能力,特别是在处理长文本或其他类型的序列数据方面表现尤为突出。以下是几个关键点:

  • Transformer架构的核心:自从Transformer被提出以来,它已经在多个NLP基准测试中取得了顶尖的成绩,并成为了当前最先进的预训练语言模型的基础,如BERT、GPT系列等。这些模型都依赖于多层堆叠的自注意力机制来实现卓越的效果。

  • 并行化优势:与传统RNN不同的是,自注意力机制允许对整个序列进行并行处理,而不是按顺序逐个处理时间步。这一特性大大加快了训练速度,尤其是在大规模语料库上训练时显得尤为重要。

  • 捕捉全局依赖性:通过让每个元素都能“看到”整个序列中的所有其他元素,自注意力机制能够在单一层内建立起非常广泛且深入的上下文联系。这对于理解复杂句子结构或文档级别的语义关系至关重要。

  • 跨领域应用:除了NLP之外,自注意力机制也被成功应用于计算机视觉、语音识别等多个领域。例如,在图像分类任务中,它可以用来捕捉图片内的空间依赖关系;而在视频分析中,则有助于理解时间维度上的动态变化。

Q、K、V的生成

在自注意力(Self-Attention)机制中,查询(Query,简称Q)、键(Key,简称K)和值(Value,简称V)是三个核心的概念,它们共同参与计算以生成序列的加权表示。

查询(Query,Q)

查询向量Q代表了当前元素在序列中的作用,它用于“询问”序列中的其他元素以获取相关信息。在自注意力机制中,每个元素都会生成一个对应的查询向量,该向量用于与序列中的所有键向量进行比较,以确定每个元素的重要性或相关性。

键(Key,K)

键向量K包含了序列中每个元素的特征信息,这些信息将用于与查询向量进行匹配。键向量的主要作用是提供一种机制,使得模型能够识别和比较序列中不同元素之间的关系。在自注意力中,每个元素都会有一个对应的键向量,它与查询向量一起决定了注意力分数。

值(Value,V)

值向量V包含了序列中每个元素的实际信息或特征,这些信息将根据注意力分数被加权求和,以生成最终的输出。值向量代表了序列中每个元素的具体内容,它们是模型最终用于生成输出的原始数据。

image.png

在自注意力机制中,输入序列的每个元素首先被映射到三个向量:查询(Q)、键(K)和值(V)。这一过程通常通过与三个权重矩阵的线性变换实现。具体来说,输入序列X与权重矩阵W^Q、W^K和W^V相乘,得到Q、K和V:

image.png

其中,X是输入序列,W^Q、W^K和W^V是可学习的权重矩阵。这些矩阵的维度通常是(序列长度,特征维度)乘以(特征维度,Q/K/V维度)。Q、K和V的维度是(序列长度,Q/K/V维度)。

这三个变换可以看作是对原始输入的一种重新编码,目的是从不同角度提取信息,以便后续计算注意力分数时能够更有效地捕捉到元素间的相关性。

缩放点积计算注意力得分

自注意力机制中,查询向量Q与所有键向量K之间的点积被用来计算注意力得分。为了避免点积结果过大导致梯度问题,引入了一个缩放因子1/√dk,其中dk是键向量的维度。缩放后的注意力得分计算如下:

image.png
image.png

这个操作生成了一个注意力得分矩阵,其中每个元素代表对应元素对之间的相似度。

Softmax 归一化

为了将注意力得分转换为权重,应用Softmax函数进行归一化。Softmax确保所有输出权重的和为1,从而使得模型可以学习到每个元素对的重要性:

image.png

Softmax函数定义为:

image.png
image.png

其中,xi是注意力得分矩阵中的元素。这意味着对于每个位置 i,在计算完 softmax 后,我们会获得该位置与其他所有位置的相关性的“概率”。

权求和生成输出

最后,归一化的注意力权重被用来对值向量V进行加权求和,生成最终的输出序列。输出序列的每个元素是所有值向量的一个加权和,权重由对应的注意力权重决定:

image.png

这一步骤有效地整合了序列内部的信息,使得每个元素的输出表示包含了整个序列的上下文信息。

完整的计算流程

f3a43e21-ac19-4ce8-9085-9b03de505c51.gif

接下来,会一步步拆解,让你更清楚地理解这个过程。

首先,我们看输入部分:

image.png

然后,我们会用初始化的权重来计算key、value和query:

image.png

接下来,我们会为第一个输入计算attention score:

image.png

然后,对attention score执行softmax操作:

image.png

这一步之后,我们会将每个输入的softmaxed score和对应的value相乘,得到3个weighted value:

image.png

最后,把上一步的weighted value加起来(对应元素相加),就得到了输出。同样的步骤,我们也会对input#2和input#3执行,得到另外两个输出。

image.png

最后,这段代码展示了如何定义一个SelfAttention类,并在其中实现自注意力机制的核心步骤。

import torch
import torch.nn as nn
import torch.nn.functional as F
from math import sqrt

class SelfAttention(nn.Module):
    def __init__(self, embed_size, heads):
        super(SelfAttention, self).__init__()
        self.embed_size = embed_size
        self.heads = heads
        self.head_dim = embed_size // heads

        # Ensure the embedding size is divisible by the number of heads
        assert (self.head_dim * heads == embed_size), "Embedding size must be divisible by heads"

        # Define linear transformations for queries, keys, and values
        self.values = nn.Linear(embed_size, embed_size, bias=False)
        self.keys = nn.Linear(embed_size, embed_size, bias=False)
        self.queries = nn.Linear(embed_size, embed_size, bias=False)
        self.fc_out = nn.Linear(embed_size, embed_size)

    def forward(self, x):
        N = x.shape[0]  # Batch size
        length = x.shape[1]  # Sequence length

        # Pass inputs through linear layers for queries, keys, and values
        values = self.values(x)
        keys = self.keys(x)
        queries = self.queries(x)

        # Split into multiple heads
        values = values.view(N, length, self.heads, self.head_dim)
        keys = keys.view(N, length, self.heads, self.head_dim)
        queries = queries.view(N, length, self.heads, self.head_dim)

        # Permute dimensions to put heads second
        values = values.permute(0, 2, 1, 3)  # (N, heads, length, head_dim)
        keys = keys.permute(0, 2, 1, 3)
        queries = queries.permute(0, 2, 1, 3)

        # Compute scaled dot-product attention
        energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])  # (N, heads, query_len, key_len)
        attention = F.softmax(energy / (self.embed_size ** (1 / 2)), dim=3)  # Softmax on the last dimension

        # Weighted sum
        out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
            N, length, self.heads * self.head_dim
        )  # (N, length, embed_size)

        return self.fc_out(out)

# Example usage
embed_size = 256  # Embedding dimension
heads = 8  # Number of attention heads
x = torch.rand(64, 10, embed_size)  # Simulate input data

self_attention = SelfAttention(embed_size, heads)
output = self_attention(x)

print(output.shape)  # Should print: torch.Size([64, 10, 256])

自注意力机制的扩展应用场景

自然语言处理(NLP)中的机器翻译

案例描述:使用Transformer架构进行英德双语翻译。Transformer通过多层堆叠的自注意力机制,能够捕捉源语言句子中各个单词之间的复杂关系,从而生成更加准确的目标语言句子。

import torch
import torch.nn as nn
from transformers import BertTokenizer, BertModel

# 初始化BERT模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
model = BertModel.from_pretrained('bert-base-multilingual-cased')

def translate_sentence(sentence, model, tokenizer):
    # 对输入句子进行编码
    inputs = tokenizer(sentence, return_tensors="pt")

    # 获取BERT模型的输出
    outputs = model(**inputs)
    last_hidden_states = outputs.last_hidden_state

    # 这里可以加入额外的解码逻辑,例如使用一个预训练好的翻译模型
    # 简单起见,我们直接返回最后一层隐藏状态作为示例
    return last_hidden_states

# 示例翻译
sentence = "Hello, how are you?"
translated = translate_sentence(sentence, model, tokenizer)
print(translated.shape)  # 输出形状应为 (batch_size, sequence_length, hidden_dim)

推荐系统中的个性化推荐

在推荐系统中,个性化推荐的目标是为每个用户提供最符合其兴趣和需求的内容或商品。为了实现这一点,模型需要能够捕捉用户行为模式,并根据这些模式进行预测。自注意力机制(Self-Attention)因其强大的建模能力,在这一领域得到了广泛应用。下面我们将进一步细化基于自注意力机制的个性化推荐系统的构建过程,并提供更加详细的代码示例。

模型选择:SASRec (Self-Attentive Sequential Recommendation)

SASRec 是一种专门为序列化推荐设计的深度学习模型,它利用了自注意力机制来捕捉用户历史行为之间的复杂依赖关系。该模型可以有效地处理长序列数据,并且能够在不牺牲计算效率的情况下提高推荐质量。

数据准备

在构建个性化推荐系统之前,我们需要准备好用户的历史行为数据。通常情况下,这些数据包括用户ID、物品ID以及交互时间戳等信息。对于SASRec模型来说,还需要对物品进行编码,以便输入到模型中。

import pandas as pd
from sklearn.preprocessing import LabelEncoder

# 假设我们有一个包含用户行为的数据框 df
df = pd.read_csv('user_behavior.csv')

# 对用户和物品ID进行编码
user_encoder = LabelEncoder()
item_encoder = LabelEncoder()

df['user_id'] = user_encoder.fit_transform(df['original_user_id'])
df['item_id'] = item_encoder.fit_transform(df['original_item_id'])

# 将数据按用户分组,并按照时间排序
user_sequences = df.groupby('user_id')['item_id'].apply(list).reset_index(name='sequence')

构建SASRec模型

接下来,我们将定义SASRec模型结构。这里使用 TensorFlow 和 Keras API 来实现。需要注意的是,实际应用中可能需要安装额外的库,如 tfrs 或者其他特定于推荐系统的框架。

import tensorflow as tf
from tensorflow.keras.layers import Embedding, LayerNormalization, Dense
from tensorflow.keras.models import Model
import numpy as np

class SASRec(Model):
    def __init__(self, num_users, num_items, embedding_dim, max_len, num_heads=1, num_blocks=2):
        super(SASRec, self).__init__()
        self.user_embedding = Embedding(input_dim=num_users + 1, output_dim=embedding_dim)
        self.item_embedding = Embedding(input_dim=num_items + 1, output_dim=embedding_dim)
        self.positional_encoding = self.get_positional_encoding(max_len, embedding_dim)
        self.attention_layers = [tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embedding_dim) for _ in range(num_blocks)]
        self.layer_norms = [LayerNormalization() for _ in range(num_blocks)]
        self.dense = Dense(num_items, activation='softmax')

    def get_positional_encoding(self, seq_len, d_model):
        # 创建位置编码矩阵
        position = np.arange(seq_len)[:, np.newaxis]
        div_term = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
        pe = np.zeros((seq_len, d_model))
        pe[:, 0::2] = np.sin(position * div_term)
        pe[:, 1::2] = np.cos(position * div_term)
        return pe[np.newaxis, ...]

    def call(self, inputs):
        user_ids, sequences = inputs
        seq_emb = self.item_embedding(sequences) + self.positional_encoding[:, :tf.shape(sequences)[1], :]
        mask = tf.cast(tf.not_equal(sequences, 0), dtype=tf.float32)

        for i in range(len(self.attention_layers)):
            seq_emb = self.layer_norms[i](seq_emb + self.attention_layers[i](seq_emb, seq_emb, attention_mask=mask))

        logits = self.dense(seq_emb)
        return logits

# 初始化SASRec模型
num_users = len(user_encoder.classes_)
num_items = len(item_encoder.classes_)
embedding_dim = 128
max_len = 50  # 用户行为序列的最大长度

model = SASRec(num_users, num_items, embedding_dim, max_len)

训练模型

为了训练模型,我们需要定义损失函数和优化器,并编写一个适合推荐任务的数据生成器。这里简化了训练过程,实际应用中应该根据具体情况进行调整。

from tensorflow.keras.optimizers import Adam

# 编写简单的负采样函数以生成负样本
def negative_sampling(pos_seq, num_items, num_neg_samples=5):
    neg_samples = []
    for pos_item in pos_seq:
        neg_items = np.random.choice(num_items, size=num_neg_samples, replace=False)
        neg_samples.append(neg_items)
    return np.array(neg_samples)

# 定义损失函数(交叉熵)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# 选择优化器
optimizer = Adam(learning_rate=0.001)

# 训练模型
for epoch in range(epochs):
    for batch in data_generator():
        with tf.GradientTape() as tape:
            user_ids, sequences, labels = batch
            predictions = model([user_ids, sequences])
            loss = loss_fn(labels, predictions)

        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss.numpy()}')

推荐预测

一旦模型训练完成,就可以用来为新用户提供推荐服务。通过给定用户的最新行为序列,模型可以预测下一个最有可能点击的商品。

def recommend_items(model, user_id, user_history, top_k=10):
    # 对用户行为序列进行填充以匹配最大长度
    sequence = np.pad(user_history[-max_len:], (max_len - len(user_history[-max_len:]), 0), 'constant')

    # 获取预测结果
    predictions = model.predict([[user_id], [sequence]])
    sorted_indices = np.argsort(predictions[0])[-top_k:]

    # 返回解码后的商品ID
    recommended_items = item_encoder.inverse_transform(sorted_indices)
    return recommended_items.tolist()

# 示例推荐
user_id = 1234
user_history = [234, 567, 890]  # 用户的历史行为记录
recommendations = recommend_items(model, user_id, user_history)
print(recommendations)  # 输出推荐商品ID

这种类型的模型特别适用于那些需要理解用户长期偏好变化的应用场景,例如电子商务平台上的商品推荐、社交媒体中的内容推荐等。

总结

通过上述详细探讨,我们不仅深入了解了自注意力机制的基本原理及其在自然语言处理、计算机视觉、语音处理以及推荐系统等多个领域的广泛应用,还结合具体案例和代码示例展示了如何将这一强大工具付诸实践。自注意力机制凭借其捕捉复杂依赖关系的能力、并行化处理的优势以及对长序列数据的有效建模,已经成为现代深度学习不可或缺的一部分。


参考文献:

Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., ... & Polosukhin, I. (2017). Attention is all you need. In Advances in neural information processing systems (pp. 5998-6008).

Devlin, J., Chang, M.-W., Lee, K., & Toutanova, K. (2018). BERT: Pre-training of deep bidirectional transformers for language understanding. arXiv preprint arXiv:1810.04805.

Dosovitskiy, A., Beyer, L., Kolesnikov, A., Weissenborn, D., Zhai, X., Unterthiner, T., ... & Houlsby, N. (2020). An image is worth 16x16 words: Transformers for image recognition at scale. arXiv preprint arXiv:2010.11929.

Wu, Y., Al-Shedivat, M., Ghasemipour, S., Wang, Y., Abbeel, P., Schwing, A. G., & Finn, C. (2020). Pay less attention with lightweight and dynamic convolutions. International Conference on Learning Representations.

相关文章
|
5月前
|
安全 算法 网络协议
解析:HTTPS通过SSL/TLS证书加密的原理与逻辑
HTTPS通过SSL/TLS证书加密,结合对称与非对称加密及数字证书验证实现安全通信。首先,服务器发送含公钥的数字证书,客户端验证其合法性后生成随机数并用公钥加密发送给服务器,双方据此生成相同的对称密钥。后续通信使用对称加密确保高效性和安全性。同时,数字证书验证服务器身份,防止中间人攻击;哈希算法和数字签名确保数据完整性,防止篡改。整个流程保障了身份认证、数据加密和完整性保护。
|
4月前
|
机器学习/深度学习 数据可视化 PyTorch
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
379 7
深入解析图神经网络注意力机制:数学原理与可视化实现
|
4月前
|
机器学习/深度学习 缓存 自然语言处理
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
Tiktokenizer 是一款现代分词工具,旨在高效、智能地将文本转换为机器可处理的离散单元(token)。它不仅超越了传统的空格分割和正则表达式匹配方法,还结合了上下文感知能力,适应复杂语言结构。Tiktokenizer 的核心特性包括自适应 token 分割、高效编码能力和出色的可扩展性,使其适用于从聊天机器人到大规模文本分析等多种应用场景。通过模块化设计,Tiktokenizer 确保了代码的可重用性和维护性,并在分词精度、处理效率和灵活性方面表现出色。此外,它支持多语言处理、表情符号识别和领域特定文本处理,能够应对各种复杂的文本输入需求。
505 6
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
|
5月前
|
机器学习/深度学习 存储 缓存
LLM高效推理:KV缓存与分页注意力机制深度解析
随着大型语言模型(LLM)规模和复杂性的增长,高效推理变得至关重要。KV缓存和分页注意力是优化LLM推理的两项关键技术。KV缓存通过存储键值对减少重复计算,而分页注意力则通过将序列分割成小块来降低内存消耗,从而有效处理长序列。本文深入剖析这些技术的工作原理及其在仅解码器模型中的应用,探讨其优势与挑战,并展示其实现示例。
219 16
LLM高效推理:KV缓存与分页注意力机制深度解析
|
5月前
|
机器学习/深度学习 算法 数据挖掘
解析静态代理IP改善游戏体验的原理
静态代理IP通过提高网络稳定性和降低延迟,优化游戏体验。具体表现在加快游戏网络速度、实时玩家数据分析、优化游戏设计、简化更新流程、维护网络稳定性、提高连接可靠性、支持地区特性及提升访问速度等方面,确保更流畅、高效的游戏体验。
147 22
解析静态代理IP改善游戏体验的原理
|
5月前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
330 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
4月前
|
传感器 人工智能 监控
反向寻车系统怎么做?基本原理与系统组成解析
本文通过反向寻车系统的核心组成部分与技术分析,阐述反向寻车系统的工作原理,适用于适用于商场停车场、医院停车场及火车站停车场等。如需获取智慧停车场反向寻车技术方案前往文章最下方获取,如有项目合作及技术交流欢迎私信作者。
269 2
|
5月前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
217 12
|
5月前
|
开发框架 监控 JavaScript
解锁鸿蒙装饰器:应用、原理与优势全解析
ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。
125 2
|
4月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

推荐镜像

更多
  • DNS