当前位置: 首页 > news >正文

生成式聊天机器人 -- 基于Transformer实现的SeqToSeq模型 -- 上

生成式聊天机器人 -- 基于Transformer实现的SeqToSeq模型 -- 上

  • 引言
  • 数据预处理
    • 下载并处理数据
    • 数据加载
  • Transformer模型
    • 嵌入层&位置编码层
    • 多头注意力机制
    • EncoderLayer
    • DecoderLayer
    • Point-wise Feed Forward Network
    • Transformer


引言

在此之前,我们已经了解了如何基于RNN加Attention架构去实现一个聊天机器人,本文将基于Transformer架构替换RNN重新实现一个生成式聊天机器人。

生成式聊天机器人 – 基于Pytorch + Global Attention + 双向 GRU 实现的SeqToSeq模型 – 上

生成式聊天机器人 – 基于Pytorch + Global Attention + 双向 GRU 实现的SeqToSeq模型 – 下

中文测试集效果(基于小型数据集训练出来的效果,仅能作为学习使用):

User:以第三人称叙述的方式写一个关于必须做出重要职业决定的主人公的短篇故事。
ChatBot:从前,有一位名叫杰克的人,他的父母住在一个小镇上。他的父母亲是一位勤劳的工作,并且他的职业生涯,并且在公司工作的职业生涯中取得了成功。User:讨论大萧条的原因
ChatBot:大萧条的经济因素是由于经济衰退,由于经济衰退化,经济衰退化,导致经济衰退。这些原因包括经济衰退化User:描述电脑主板的功能
ChatBot:计算机的主板电路板是电脑,通常由两个组成,每个组成,每个组件都包含一个组成,每个组件都包含一个组成,每个组件都包含一个组件。这些组件包括:User:提出数据隐私问题的道德解决方案
ChatBot:数据隐私问题的一个潜在解决方案是实施数据安全和隐私。这些方法可能包括:User:向非技术人员解释冒泡排序算法的概念。
ChatBot:这是一个简单的算法,它将其分成两个球员的球员名字列表,其中每个球员的球员排列表,并且每个球员都会根据其他元素的球员。这意味着它的球员,并且可以在其中找到一个球员的球员。User:给定以下输入,构建一个有创意的故事。一把神奇的弓箭
ChatBot:从前,有一个名叫莉莉莉莉的年轻女孩,她住在一个小村庄里。她住在一个小村庄里,一个名叫莉莉莉莉莉莉莉莉莉莉莉莉莉莉的年轻女孩,在一个小村庄里。她的小村庄里,她住在一个小村庄里,在森林里,在森林里游泳,在森林里游泳。她的一个小村庄里,在森林里,在森林里游泳,在森林里游泳,在森林里游泳,在森林里游泳。她的一个小村庄里,在森林里游泳,在森林里游泳,在森林里游泳,在森林里游泳,在森林里游泳。她的一个小村庄里游泳,在森林里游泳,在森林里游泳,在森林里游泳,在森林里游泳,在森林里游泳,在森林里游泳。User:提出一个关于自动驾驶汽车的想法。
ChatBot:自动驾驶汽车的一个潜在想法是使用自动驾驶汽车的自动驾驶汽车。自动驾驶汽车可以通过自动驾驶汽车User:写下您最近观看的电影的评论。寄生虫 (2019)
ChatBot:我最近看了电影《了!我最近看电影《了!我最近看了!我最近看了!我最近看了!我最近看了!我最近看了!我最近看了!我最近的电影《了!我最近的电影《了!我最近的电影《了!我最近的电影《了!我最近的电影《了!我最近的电影《了!我最近的电影星期的电影《了!我最近的电影星期的电影《了!我最近的电影星期的电影《了!我最近的电影星期的电影《了!我最近的电影星期的电影》是什么让我最近的电影《了!我最近的电影《了!我最近的电影《了!我最近的电影星期的电影《了!我最近的电影星期的电影》。我最近的电影《了!我最近的电影《了!我最User:创建 Twitter 帖子来宣传您的新产品。艾丽莎的全天然果酱
ChatBot:我们的新鲜花一切都是享用我们的美味佳选择!

数据预处理

下载并处理数据

本文使用的是github上找到的一个小型的开源中文数据集,下载链接如下:

  • 数据集 + 中文分词

需要下载dataset文件夹到项目根目录下:

在这里插入图片描述

data_zh.json 是一个只有3万条对话的小型数据集,也是本文训练所采用的数据集,仅能作为学习使用 ,而dataset_train.json来自一个开源的中文对话语料库,大约1.4G的对话。

不需要基于大语料库进行训练的同学,可以不下载dataset_train.json文件。

char_base.json 是一个含有常见中文字符的字典文件,我们会利用该文件完成字典的初始化任务。

1. 初始化字典文件 1.初始化字典文件 1.初始化字典文件

import json# 常见的中文字符和英文标点符号
common_characters = ",。!?;:“”‘’()[]&#;``【】``;《》1234567890"
common_english_punctuation = ".,!?;:\"'()[]{}<>qwertyuiopasdfghjklzxcvbnm"# 打开JSON文件以读取汉字映射数据
with open('../dataset/dataset/char_base.json', 'r', encoding='utf-8') as json_file:char_data = json.load(json_file)# 创建汉字映射字典
word_map = {}
# 遍历char_data列表并将汉字及其对应的索引添加到字典中
for item in char_data:char = item.get("char", "")if char not in word_map:word_map[char] = len(word_map) + 1# 遍历常见字符并将它们添加到字典中
for char in common_characters:if char not in word_map:word_map[char] = len(word_map) + 1# 遍历常见英文标点符号并将它们添加到字典中
for char in common_english_punctuation:if char not in word_map:word_map[char] = len(word_map) + 1# 添加特殊标记
word_map['<unk>'] = len(word_map) + 1
word_map['<start>'] = len(word_map) + 1
word_map['<end>'] = len(word_map) + 1
word_map['<pad>'] = 0if __name__ == '__main__':# 保存汉字映射字典到文件 --- word_map保存每个字符对应的IDwith open('../dataset/dataset/WORDMAP_corpus.json', 'w', encoding='utf-8') as map_file:json.dump(word_map, map_file, ensure_ascii=False)

执行上述代码后,处理效果如下:

在这里插入图片描述

2. 初始化句对 2. 初始化句对 2.初始化句对

import jsonmax_len = 256# 打开WORDMAP_corpus_.json文件以读取汉字映射数据
with open('../dataset/dataset/WORDMAP_corpus.json', 'r', encoding='utf-8') as word_map_file:word_map = json.load(word_map_file)def encode_question(words, word_map):enc_c = [word_map.get(word, word_map['<unk>']) for word in words] + [word_map['<pad>']] * (max_len - len(words))return enc_cdef encode_reply(words, word_map):enc_c = [word_map['<start>']] + [word_map.get(word, word_map['<unk>']) for word in words] + [word_map['<end>']] + [word_map['<pad>']] * (max_len - len(words))return enc_cpairs_encoded = []def generate_from_small_dataset():# 只有3万条对话的小型数据集,数据量连大模型的微调都不够。全参训练就图看个乐,能回就成功了,误当真。input_file = "../dataset/dataset/data_zh.json"with open(input_file, "r", encoding="utf-8") as file:  # 指定编码格式为utf-8data_zh = json.load(file)new_data = []for idx, item in enumerate(data_zh):query_history = []instruction = item["instruction"]output = item["output"]instruction = instruction.replace('\n\n', '\n')output = output[0].replace('\n\n', '\n')output = output[:max_len - 1].ljust(max_len - 1)qus = encode_question(instruction[:max_len-1], word_map)ans = encode_reply(output, word_map)pairs_encoded.append([qus, ans])generate_from_small_dataset()
if __name__ == '__main__':print('开始写pairs_encoded')with open('../pairs_encoded.json', 'w') as p:json.dump(pairs_encoded, p)

执行上述代码后,处理效果如下:
在这里插入图片描述


数据加载

下面我们使用DataSet类来封装对数据的读取和加载逻辑:

# 定义数据集类
class Dataset(Dataset):def __init__(self):# 加载已编码的问答对self.pairs = json.load(open('pairs_encoded.json'))self.dataset_size = len(self.pairs)def __getitem__(self, i):# 获取一组问答对question = torch.LongTensor(self.pairs[i][0])reply = torch.LongTensor(self.pairs[i][1])return question, replydef __len__(self):# 返回数据集大小return self.dataset_size

使用pytorch提供的DataLoader类来封装DataSet,完成数据的批量加载:

train_loader = data.DataLoader(Dataset(),batch_size=512,shuffle=True,pin_memory=True)

Transformer模型

关于Transformer和SeqToSeq模型理论部分大家可以阅读下文学习,本文就不再进行理论知识的讲解了,重点看代码逻辑的实现:

  • 演进历史: Seq2Seq 到 Transformer

嵌入层&位置编码层

首先我们来看Transformer中的嵌入层与位置编码层的代码实现:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")class Embeddings(nn.Module):"""实现词的嵌入并添加位置编码。"""def __init__(self, vocab_size, d_model, max_len):super(Embeddings, self).__init__()# 嵌入向量的维度self.d_model = d_model# 定义一个 Dropout 层,丢弃率为 0.1,用于防止过拟合self.dropout = nn.Dropout(0.1)# 定义一个嵌入层,将词的索引映射到 d_model 维的稠密向量空间# vocab_size 是词汇表的大小,d_model 是嵌入向量的维度self.embed = nn.Embedding(vocab_size, d_model)# 调用 create_positinal_encoding 方法创建位置编码self.pe = self.create_positinal_encoding(max_len, self.d_model)def create_positinal_encoding(self, max_len, d_model):pe = torch.zeros(max_len, d_model).to(device)# 遍历每个词的位置for pos in range(max_len):# 遍历每个位置的每个偶数维度for i in range(0, d_model, 2):# 根据正弦公式计算偶数维度的位置编码值pe[pos, i] = math.sin(pos / (10000 ** ((2 * i) / d_model)))# 根据余弦公式计算奇数维度的位置编码值pe[pos, i + 1] = math.cos(pos / (10000 ** ((2 * (i + 1)) / d_model)))# 在第 0 维添加一个维度,将 pe 的形状变为 (1, max_len, d_model),以便后续扩展批次大小pe = pe.unsqueeze(0)return pedef forward(self, encoded_words):# 通过嵌入层将编码后的词转换为嵌入向量# 乘以 math.sqrt(self.d_model) 是为了缩放嵌入向量,防止梯度消失或爆炸embedding = self.embed(encoded_words) * math.sqrt(self.d_model)# 将位置编码添加到嵌入向量上# self.pe[:, :embedding.size(1)] 截取位置编码的前 embedding.size(1) 个位置# 这里位置编码会自动扩展为与 encoded_words 相同的批次大小 -- 广播embedding += self.pe[:, :embedding.size(1)]# 对添加了位置编码的嵌入向量应用 Dropout 操作embedding = self.dropout(embedding)return embedding

下面通过一个实例来演示该层的运行流程:

在这里插入图片描述
关于Transformer采用的正弦位置编码理论部分这里就不过多讲解了,大家可自行查询相关资料进行学习。


多头注意力机制

本节我们来重点看一下Transformer中的多头注意力机制模块代码是如何实现的,以及通过图解的方式完整的看一遍它的处理流程。

import torch.nn.functional as Fclass MultiHeadAttention(nn.Module):"""多头注意力机制。多头注意力机制允许模型在不同的表示子空间中并行地关注输入序列的不同部分,从而捕捉到更丰富的语义信息。"""def __init__(self, heads, d_model):"""初始化多头注意力机制。:param heads: 注意力头的数量。:param d_model: 模型的维度,即输入和输出向量的维度。"""super(MultiHeadAttention, self).__init__()# 确保 d_model 能被 heads 整除,因为要将 d_model 均分到每个头中assert d_model % heads == 0# 每个头的维度self.d_k = d_model // heads# 注意力头的数量self.heads = heads# 定义 Dropout 层,丢弃率为 0.1,用于防止过拟合self.dropout = nn.Dropout(0.1)# 定义WQ,WK,WV矩阵,用于将输入的q,k,v向量投影到对应的query空间,key空间和value空间中self.query = nn.Linear(d_model, d_model)self.key = nn.Linear(d_model, d_model)self.value = nn.Linear(d_model, d_model)# 经过多头注意力计算后,每个头的输出是相互独立的。# 将这些输出拼接起来只是简单地将它们组合在一起,并没有充分融合各个头所提取的信息。# 通过一个线性变换层(全连接层),可以对拼接后的向量进行线性组合和变换,使得不同头的信息能够相互作用和融合,从而得到一个更加综合和丰富的表示。self.concat = nn.Linear(d_model, d_model)def forward(self, query, key, value, mask):"""前向传播方法,定义了多头注意力机制的具体计算流程。:param query: 查询向量,形状通常为 (batch_size, seq_len, d_model):param key: 键向量,形状通常为 (batch_size, seq_len, d_model):param value: 值向量,形状通常为 (batch_size, seq_len, d_model):param mask: 掩码矩阵,用于在注意力计算中屏蔽某些位置的信息,形状通常为 (batch_size, 1, seq_len, seq_len):return: 经过多头注意力机制计算后的交互向量,形状为 (batch_size, seq_len, d_model)"""# 通过线性变换将输入的 query、key 和 value 向量分别投影到对应的query,key,value空间中去query = self.query(query)key = self.key(key)value = self.value(value)# 将 query、key 和 value 向量的维度进行重塑和调整,以适应多头注意力的计算# 先将最后一个维度 d_model 拆分为 (heads, d_k),表示每个头分别关注输入维度的不同部分# 然后调整维度顺序为 (batch_size, heads, seq_len, d_k)query = query.view(query.shape[0], -1, self.heads, self.d_k).permute(0, 2, 1, 3)key = key.view(key.shape[0], -1, self.heads, self.d_k).permute(0, 2, 1, 3)value = value.view(value.shape[0], -1, self.heads, self.d_k).permute(0, 2, 1, 3)# 计算注意力分数# 通过矩阵乘法计算 query 和 key 的转置的乘积# 除以 math.sqrt(query.size(-1)) 是为了缩放注意力分数,防止梯度消失或爆炸scores = torch.matmul(query, key.permute(0, 1, 3, 2)) / math.sqrt(query.size(-1))# 应用掩码# 将掩码矩阵中值为 0 的位置对应的注意力分数设置为负无穷大# 这样在后续的 softmax 计算中,这些位置的权重将趋近于 0scores = scores.masked_fill(mask == 0, -1e9)# 计算注意力权重# 对注意力分数应用 softmax 函数,将其转换为概率分布weights = F.softmax(scores, dim=-1)# 应用 Dropout 进行正则化weights = self.dropout(weights)# 计算上下文向量# 通过矩阵乘法将注意力权重和 value 向量相乘,得到每个头的上下文向量context = torch.matmul(weights, value)# 将多头注意力的输出进行拼接# 先调整维度顺序为 (batch_size, seq_len, heads, d_k)# 然后将最后两个维度拼接成一个维度,即 (batch_size, seq_len, d_model)context = context.permute(0, 2, 1, 3).contiguous().view(context.shape[0], -1, self.heads * self.d_k)# 通过一个线性变换层(全连接层),可以对拼接后的向量进行线性组合和变换,使得不同头的信息能够相互作用和融合,从而得到一个更加综合和丰富的表示interacted = self.concat(context)return interacted

下面通过一个自注意力机制的运行实例来演示该层的运算流程:

# 参数信息
heads = 2
d_model = 8
batch_size = 1
seq_len = 6

在这里插入图片描述

上图流程中只演示了因果掩码的应用,使用padding掩码的过程也是一样的,将padding mask矩阵与还未进行SoftMax归一化的Attention Score矩阵相加即可,后面进行SoftMax运算时,会将Attention Score中值为负无穷的位置处的权重值设置为0。


EncoderLayer

本节我们来看一下Transformer中Encoder块是如何实现的:

class EncoderLayer(nn.Module):"""编码器层。编码器层是 Transformer 模型编码器部分的核心组件,主要由多头自注意力机制和前馈神经网络构成,并且在每个子层后都使用了残差连接和层归一化操作,有助于缓解梯度消失问题,提升模型的训练稳定性和性能。"""def __init__(self, d_model, heads):"""初始化编码器层。:param d_model: 模型的维度,即输入和输出向量的维度大小。:param heads: 多头注意力机制中的头数,决定了模型可以并行关注不同特征子空间的能力。"""super(EncoderLayer, self).__init__()# 定义层归一化层,对输入张量的最后一个维度进行归一化操作,# 使输入数据的均值为 0,方差为 1,有助于加速模型收敛和提高稳定性self.layernorm = nn.LayerNorm(d_model)# 实例化多头自注意力机制模块,用于对输入序列进行自注意力计算,# 可以捕捉序列中不同位置之间的依赖关系self.self_multihead = MultiHeadAttention(heads, d_model)# 实例化前馈神经网络模块,用于对多头自注意力机制的输出进行非线性变换,# 增加模型的表达能力self.feed_forward = FeedForward(d_model)# 定义 Dropout 层,丢弃率为 0.1,用于在训练过程中随机将部分输入元素置为 0,# 防止模型过拟合self.dropout = nn.Dropout(0.1)def forward(self, embeddings, mask):"""前向传播方法,定义了编码器层的具体计算流程。:param embeddings: 输入的嵌入向量,形状通常为 (batch_size, seq_len, d_model),其中 batch_size 是批次大小,seq_len 是序列长度,d_model 是模型维度。:param mask: 掩码矩阵,用于在多头注意力计算中屏蔽某些位置的信息,形状通常为 (batch_size, 1, seq_len, seq_len)。:return: 编码后的向量,形状与输入的 embeddings 相同,为 (batch_size, seq_len, d_model)。"""# 第一步:多头自注意力机制# 将输入的 embeddings 同时作为 query、key 和 value 输入到多头自注意力模块中,# 计算得到交互后的表示# 对多头自注意力模块的输出应用 Dropout 操作,防止过拟合interacted = self.dropout(self.self_multihead(embeddings, embeddings, embeddings, mask))# 第二步:第一个残差连接和层归一化# 将多头自注意力机制的输出与输入的 embeddings 相加,形成残差连接,# 有助于缓解梯度消失问题,使模型更容易训练# 对残差连接的结果应用层归一化操作,稳定训练过程interacted = self.layernorm(interacted + embeddings)# 第三步:前馈神经网络# 将经过多头自注意力机制和层归一化处理后的结果输入到前馈神经网络中,# 进行非线性变换# 对前馈神经网络的输出应用 Dropout 操作,防止过拟合feed_forward_out = self.dropout(self.feed_forward(interacted))# 第四步:第二个残差连接和层归一化# 将前馈神经网络的输出与经过多头自注意力机制和层归一化处理后的结果相加,# 形成残差连接# 对残差连接的结果应用层归一化操作,得到最终的编码结果encoded = self.layernorm(feed_forward_out + interacted)return encoded

再理解了多头注意力机制的代码实现后,EncoderLayer的代码就比较好理解了,但是这里需要注意一点,EncoderLayer类的前向传播方法传入的mask为padding_mask,用于将传入序列中为pad的词,在计算注意力分数时给排除掉,避免将注意力过多分配给这些无意义的词。


DecoderLayer

本节我们来看一下Transformer中Decoder块是如何实现的:

class DecoderLayer(nn.Module):"""解码器层。解码器层是 Transformer 模型解码器部分的核心组件,主要包含两个多头注意力机制(自注意力和编码器 - 解码器注意力)以及一个前馈神经网络,并且在每个子层后都使用了残差连接和层归一化操作,有助于提升模型性能和训练稳定性。"""def __init__(self, d_model, heads):"""初始化解码器层。:param d_model: 模型的维度,即输入和输出向量的维度大小。:param heads: 多头注意力机制中的头数,决定了模型可以并行关注不同特征子空间的能力。"""super(DecoderLayer, self).__init__()# 定义层归一化层,对输入张量的最后一个维度进行归一化操作,# 使输入数据的均值为 0,方差为 1,有助于加速模型收敛和提高稳定性self.layernorm = nn.LayerNorm(d_model)# 实例化第一个多头自注意力机制模块,用于对目标序列进行自注意力计算,# 可以捕捉目标序列中不同位置之间的依赖关系self.self_multihead = MultiHeadAttention(heads, d_model)# 实例化第二个多头注意力机制模块,用于进行编码器 - 解码器注意力计算,# 可以让解码器关注编码器的输出信息self.src_multihead = MultiHeadAttention(heads, d_model)# 实例化前馈神经网络模块,用于对多头注意力机制的输出进行非线性变换,# 增加模型的表达能力self.feed_forward = FeedForward(d_model)# 定义 Dropout 层,丢弃率为 0.1,用于在训练过程中随机将部分输入元素置为 0,# 防止模型过拟合self.dropout = nn.Dropout(0.1)def forward(self, embeddings, encoded, src_mask, target_mask):"""前向传播方法,定义了解码器层的具体计算流程。:param embeddings: 目标序列的嵌入向量,形状通常为 (batch_size, target_seq_len, d_model),其中 batch_size 是批次大小,target_seq_len 是目标序列长度,d_model 是模型维度。:param encoded: 编码器的输出,形状通常为 (batch_size, src_seq_len, d_model),其中 src_seq_len 是源序列长度。:param src_mask: 源序列的掩码矩阵,用于在编码器 - 解码器注意力计算中屏蔽某些位置的信息,形状通常为 (batch_size, 1, src_seq_len, src_seq_len)。:param target_mask: 目标序列的掩码矩阵,用于在自注意力计算中屏蔽某些位置的信息,通常是一个上三角矩阵,用于防止解码器看到未来的信息,形状为 (batch_size, 1, target_seq_len, target_seq_len)。:return: 解码后的向量,形状与输入的 embeddings 相同,为 (batch_size, target_seq_len, d_model)。"""# 第一步:目标序列的自注意力机制# 将输入的目标序列嵌入向量同时作为 query、key 和 value 输入到自注意力模块中,# 使用目标序列掩码 target_mask 进行计算,得到交互后的表示# 对自注意力模块的输出应用 Dropout 操作,防止过拟合query = self.dropout(self.self_multihead(embeddings, embeddings, embeddings, target_mask))# 第一个残差连接和层归一化# 将自注意力机制的输出与输入的目标序列嵌入向量相加,形成残差连接,# 有助于缓解梯度消失问题,使模型更容易训练# 对残差连接的结果应用层归一化操作,稳定训练过程query = self.layernorm(query + embeddings)# 第二步:编码器 - 解码器注意力机制# 将经过自注意力和层归一化处理后的结果作为 query,# 编码器的输出 encoded 同时作为 key 和 value,# 使用源序列掩码 src_mask 输入到多头注意力模块中,进行编码器 - 解码器注意力计算,# 让解码器关注编码器的输出信息# 对多头注意力模块的输出应用 Dropout 操作,防止过拟合interacted = self.dropout(self.src_multihead(query, encoded, encoded, src_mask))# 第二个残差连接和层归一化# 将编码器 - 解码器注意力机制的输出与经过自注意力和层归一化处理后的结果相加,# 形成残差连接# 对残差连接的结果应用层归一化操作interacted = self.layernorm(interacted + query)# 第三步:前馈神经网络# 将经过编码器 - 解码器注意力机制和层归一化处理后的结果输入到前馈神经网络中,# 进行非线性变换# 对前馈神经网络的输出应用 Dropout 操作,防止过拟合feed_forward_out = self.dropout(self.feed_forward(interacted))# 第三个残差连接和层归一化# 将前馈神经网络的输出与经过编码器 - 解码器注意力机制和层归一化处理后的结果相加,# 形成残差连接# 对残差连接的结果应用层归一化操作,得到最终的解码结果decoded = self.layernorm(feed_forward_out + interacted)return decoded

Decoder 在其所流经的第一个多头注意力机制模块中传入的掩码为因果掩码,用于确保在生成某个位置的输出时,解码器只能关注到该位置及其之前的输入信息,而不能看到未来的信息。

Decoder 所流经的第二个多头注意力机制模块的q来源于解码器自身第一个注意力层的输出,k和v来自编码器的输出,由于q,k,v来源不同,该层也被成为交叉自注意力层。同时该层计算时,会传入原序列对应的padding掩码矩阵,用于屏蔽源序列中的填充位置,防止模型在自注意力计算时关注到这些无意义的位置。


Point-wise Feed Forward Network

在 Transformer 架构里,FeedForward 层也被称作逐点前馈网络(Point-wise Feed Forward Network),主要是因为以下两点原因:

  1. 独立处理每个位置:逐点前馈网络对输入序列中的每个位置(时间步)进行独立的计算,各个位置之间不存在交互。也就是说,在该网络的计算过程中,对于输入序列里的每一个元素,都是按照相同的方式进行处理的,彼此之间没有依赖关系。例如,在一个句子的嵌入表示中,每个词的嵌入向量都会独立地经过前馈网络的计算,不会受到其他词的影响。
  2. 共享参数:逐点前馈网络在整个序列上共享相同的参数。具体而言,FeedForward 层中的两个线性层 self.fc1 和 self.fc2 的权重参数在处理输入序列的所有位置时都是一样的。这种参数共享的方式大大减少了模型的参数数量,提升了计算效率。
class FeedForward(nn.Module):"""前馈神经网络。"""def __init__(self, d_model, middle_dim=2048):super(FeedForward, self).__init__()self.fc1 = nn.Linear(d_model, middle_dim)self.fc2 = nn.Linear(middle_dim, d_model)self.dropout = nn.Dropout(0.1)def forward(self, x):out = F.relu(self.fc1(x))out = self.fc2(self.dropout(out))return out

下面通过一个实例来演示该过程:

# 初始化参数值
batch_size = 1
embedd_size = 8
seq_len  = 6
middle_dim = 16

在这里插入图片描述

自注意力机制负责将同一个序列中所有词向量按照注意力权重进行信息融合,经过自注意力机制的处理后,Transformer再将数据通过前馈层独立的完成对单个词向量中信息的融合。


Transformer

我们上面已经讲解完了Transformer中涉及到的所有组件,下面看看如何把这些组件组装成一个完整的transformer模型。

import torch.nn.functional as Fclass Transformer(nn.Module):"""Transformer模型。Transformer是一种基于注意力机制的深度学习模型,广泛应用于自然语言处理任务,由编码器(Encoder)和解码器(Decoder)组成,能够处理序列到序列的任务。"""def __init__(self, d_model, heads, num_layers, word_map, max_len=260):"""初始化Transformer模型。:param d_model: 模型的维度,即嵌入向量和隐藏层的维度大小。:param heads: 多头注意力机制中的头数。:param num_layers: 编码器和解码器的层数。:param word_map: 词汇表,用于将单词映射为索引。:param max_len: 输入序列的最大长度,默认为260。"""super(Transformer, self).__init__()# 保存模型的维度self.d_model = d_model# 计算词汇表的大小self.vocab_size = len(word_map)# 初始化嵌入层,用于将输入的单词索引转换为嵌入向量self.embed = Embeddings(self.vocab_size, d_model, max_len=max_len)# 初始化编码器层列表,包含num_layers个EncoderLayer实例self.encoder = nn.ModuleList([EncoderLayer(d_model, heads) for _ in range(num_layers)])# 初始化解码器层列表,包含num_layers个DecoderLayer实例self.decoder = nn.ModuleList([DecoderLayer(d_model, heads) for _ in range(num_layers)])# 初始化线性层,用于将解码器的输出映射到词汇表大小的维度,以便进行单词预测self.logit = nn.Linear(d_model, self.vocab_size)def encode(self, src_words, src_mask):"""编码器前向传播,对源序列进行编码。:param src_words: 源序列的单词索引,形状通常为 (batch_size, src_seq_len):param src_mask: 源序列的掩码,用于屏蔽填充位置,形状通常为 (batch_size, 1, src_seq_len, src_seq_len):return: 编码后的源序列嵌入向量,形状为 (batch_size, src_seq_len, d_model)"""# 将源序列的单词索引通过嵌入层转换为嵌入向量src_embeddings = self.embed(src_words)# 依次通过编码器的每一层进行处理for layer in self.encoder:src_embeddings = layer(src_embeddings, src_mask)return src_embeddingsdef decode(self, target_words, target_mask, src_embeddings, src_mask):"""解码器前向传播,根据编码后的源序列和目标序列进行解码。:param target_words: 目标序列的单词索引,形状通常为 (batch_size, tgt_seq_len):param target_mask: 目标序列的掩码,包括填充掩码和因果掩码,形状通常为 (batch_size, 1, tgt_seq_len, tgt_seq_len):param src_embeddings: 编码后的源序列嵌入向量,形状为 (batch_size, src_seq_len, d_model):param src_mask: 源序列的掩码,用于屏蔽填充位置,形状通常为 (batch_size, 1, src_seq_len, src_seq_len):return: 解码后的目标序列嵌入向量,形状为 (batch_size, tgt_seq_len, d_model)"""# 将目标序列的单词索引通过嵌入层转换为嵌入向量tgt_embeddings = self.embed(target_words)# 依次通过解码器的每一层进行处理for layer in self.decoder:tgt_embeddings = layer(tgt_embeddings, src_embeddings, src_mask, target_mask)return tgt_embeddingsdef forward(self, src_words, src_mask, target_words, target_mask):"""Transformer模型的前向传播。:param src_words: 源序列的单词索引,形状通常为 (batch_size, src_seq_len):param src_mask: 源序列的掩码,用于屏蔽填充位置,形状通常为 (batch_size, 1, src_seq_len, src_seq_len):param target_words: 目标序列的单词索引,形状通常为 (batch_size, tgt_seq_len):param target_mask: 目标序列的掩码,包括填充掩码和因果掩码,形状通常为 (batch_size, 1, tgt_seq_len, tgt_seq_len):return: 经过Transformer模型处理后的输出,形状为 (batch_size, tgt_seq_len, vocab_size),表示每个位置预测每个单词的概率对数"""# 对源序列进行编码encoded = self.encode(src_words, src_mask)# 根据编码后的源序列和目标序列进行解码decoded = self.decode(target_words, target_mask, encoded, src_mask)# 将解码后的结果通过线性层映射到词汇表大小的维度# 并使用log_softmax函数将其转换为概率对数out = F.log_softmax(self.logit(decoded), dim=2)return out

Transformer 类实现了一个完整的 Transformer 模型,包含嵌入层、编码器、解码器和输出层。通过 encode 方法对源序列进行编码,decode 方法根据编码后的源序列和目标序列进行解码,最后在 forward 方法中组合编码和解码过程,并输出预测结果的概率对数。

在这里插入图片描述


相关文章:

生成式聊天机器人 -- 基于Transformer实现的SeqToSeq模型 -- 上

生成式聊天机器人 -- 基于Transformer实现的SeqToSeq模型 -- 上 引言数据预处理下载并处理数据数据加载 Transformer模型嵌入层&位置编码层多头注意力机制EncoderLayerDecoderLayerPoint-wise Feed Forward NetworkTransformer 引言 在此之前&#xff0c;我们已经了解了如…...

【Java 面试 八股文】Spring Cloud 篇

Spring Cloud 篇 1. Spring Cloud 5大组件有哪些&#xff1f;2. 服务注册和发现是什么意思&#xff1f;Spring Cloud 如何实现服务注册发现&#xff1f;3. 我看你之前也用过nacos&#xff0c;你能说下nacos与eureka的区别&#xff1f;4. 你们项目负载均衡如何实现的&#xff1f…...

CAS单点登录(第7版)10.多因素身份验证

如有疑问&#xff0c;请看视频&#xff1a;CAS单点登录&#xff08;第7版&#xff09; 多因素身份验证 概述 多因素身份验证 &#xff08;MFA&#xff09; 多因素身份验证&#xff08;Multifactor Authentication MFA&#xff09;是一种安全机制&#xff0c;要求用户提供两种…...

【16】思科AireOS:创建使用 LWA 认证的 WLAN

1. 概述 LWA(Local Web Authentication)是一种基于 Web 认证的方式,允许无线客户端在连接 WLAN 后,使用 Web 认证页面进行身份验证。该方法适用于访客网络或需要身份认证的场景。 本指南详细介绍如何在 Cisco AireOS 无线控制器(WLC)上配置 LWA 认证的 WLAN,并确保认证…...

webassembly009 transformers.js 网页端侧推理 whisper-web

whisper-web https://github.com/xenova/whisper-web 页面结构 AudioManager: 该组件负责音频的录制和处理。它会使用 Web API 来访问麦克风&#xff0c;录制音频数据&#xff0c;并将其传递给 transcriber 进行转录。它通过 transcriber 管理转录状态&#xff0c;音频数据将…...

vscode使用常见问题处理合集

目录 一、使用vite创建的vue3项目&#xff0c;script和style首行代码不会缩进,且格式化属性字段等会换行问题 首行缩进情况如下&#xff1a; 属性、参数格式化换行情况如下&#xff1a; 解决方式&#xff1a; 一、使用vite创建的vue3项目&#xff0c;script和style首行代码不…...

EasyExcel提取excel文档

目录 一、前言二、提取excel文档2.1、所有sheet----获取得到headerList和总行数2.2、所有sheet----获取合并单元格信息2.3、读取某个sheet的每行数据一、前言 EasyExcel 是阿里巴巴开源的一个高性能 Excel 读写库,相比于 Apache POI 和 JXL,它有明显的优势,特别是在处理大数…...

DeepSeek v3 技术报告阅读笔记

注 本文参考 DeepSeek-v3 / v2 / v1 Technical Report 及相关参考模型论文本文不包括基础的知识点讲解&#xff0c;为笔记/大纲性质而非教程&#xff0c;建议阅读技术报告原文交流可发送至邮箱 henryhua0721foxmail.com 架构核心 核心&#xff1a; MLA 高效推理DeepSeekMOE 更…...

Python爬虫-猫眼电影的影院数据

前言 本文是该专栏的第46篇,后面会持续分享python爬虫干货知识,记得关注。 本文笔者以猫眼电影为例子,获取猫眼的影院相关数据。 废话不多说,具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详细内容。(附带完整代码) …...

每天五分钟深度学习框架pytorch:搭建谷歌的Inception网络模块

本文重点 前面我们学习了VGG,从现在开始我们将学习谷歌公司推出的GoogLeNet。当年ImageNet竞赛的第二名是VGG,而第一名就是GoogLeNet,它的模型设计拥有很多的技巧,这个model证明了一件事:用更多的卷积,更深的层次可以得到更好的结构 GoogLeNet的网络结构 如图所示就是Go…...

export default与export区别

1.定义&#xff1a; export default‌&#xff1a;用于导出模块中的默认成员。一个模块中只能有一个export default&#xff0c;通常用于导出模块的主要功能或对象。导入时可以使用任意名称&#xff0c;因为它没有具体的名称‌ ‌export‌&#xff1a;用于导出模块中的多个成…...

当Ollama遇上划词翻译:我的Windows本地AI服务搭建日记

&#x1f680; 实现Windows本地大模型翻译服务 - 基于OllamaFlask的划词翻译实践 &#x1f6e0;️ 步骤概要1️⃣ python 环境准备2️⃣ Ollama 安装3️⃣ 一个 Flask 服务4️⃣ Windows 服务化封装5️⃣ 测试本地接口6️⃣ 配置划词翻译自定义翻译源7️⃣ 效果展示8️⃣ debug…...

5G与物联网的协同发展:打造智能城市的未来

引言 随着科技的不断进步&#xff0c;智能城市的概念已经不再是科幻小说中的幻想&#xff0c;它正在逐步走进我们的生活。而这背后的两大驱动力无疑是 5G和 物联网&#xff08;IoT&#xff09;。5G网络以其高速率、低延迟、大容量的优势&#xff0c;与物联网的强大连接能力相结…...

并发编程---synchronized关键字,以及synchronized同步锁

文章目录 Synchronized 的使用synchronized 在普通方法上的使用&#xff08;对象锁&#xff09;synchronized 在静态方法上的使用&#xff08;类锁&#xff09;synchronized 在代码块上的使用 JVM 中锁的优化锁的类型自旋锁与自适应自旋锁自旋锁&#xff08;Spin Lock&#xff…...

Vue学习笔记5(Vue3)

Vue3学习笔记 一、create-vue搭建vue3项目 create-vue是vue官方新的脚手架工具&#xff0c;底层切换到了vite 步骤&#xff1a; 查看环境条件 node -v版本需要在16.0及以上创建一个vue应用 npm init vuelatest 这一指令会安装并执行create-vue 二、项目目录和关键文件 in…...

VoIP之音视频会议中的混音技术

在VoIP音视频会议中&#xff0c;需要将多路参会方音频流混合成一路音频流再发送给各参会方&#xff0c;以达到参会方可以听到每个与会人声音的目的&#xff0c;这种技术叫混音。 一、混音基础原理 在实际生活中&#xff0c;我们所处的生活和工作环境就是一个自然的混音场&…...

Baklib一站式云平台:全场景赋能企业知识资产激活

内容概要 在数字化浪潮推动下&#xff0c;企业知识资产的高效管理与价值释放成为核心议题。Baklib作为一站式云平台&#xff0c;以全场景赋能为核心定位&#xff0c;通过构建知识中台架构&#xff0c;为企业提供从资源整合到应用落地的闭环解决方案。该平台不仅支持文本、图像…...

基于nuScenes数据集和DeepSeek模型的端到端自动驾驶解决方案

结合DeepSeek模型进行知识蒸馏&#xff0c;以提高模型性能。这需要将nuScenes中的多模态数据&#xff08;如摄像头图像、雷达点云、车辆状态等&#xff09;整合到模型中&#xff0c;同时使用DeepSeek的生成能力进行蒸馏。 接下来&#xff0c;我需要考虑用户可能的背景。用户可能…...

《AI大模型开发笔记》deepseek提示词技巧

为什么你的 AI 助手总是答非所问&#xff1f; 「写篇产品分析」 → 收到一堆不知所云的文字 「做个竞品对比」 → 得到几页没有重点的废话 揭秘&#xff1a;不是 AI 不够聪明&#xff0c;而是你的指令太“高冷”&#xff01; 一、新手进阶&#xff1a; 5 大法则&#xff0c;让…...

学习笔记-人脸识别相关编程基础

通过编程实现人脸识别功能&#xff0c;需要掌握一定的技术基础&#xff0c;包括编程语言、图像处理、机器学习以及相关的库和框架&#xff1a; 1. 编程语言 Python&#xff1a;Python 是实现人脸识别最常用的语言之一&#xff0c;因为它有大量的库和框架支持&#xff0c;如 Op…...

Java发展史

JavaEE的由来 语言的诞生 Java的前身是Oak语言&#xff0c;其目的是搞嵌入式开发开发智能面包机 叮~~~&#x1f35e;&#x1f35e;&#x1f35e; 产品以失败告终 巅峰 网景公司需要网景浏览器打开网页&#xff0c;Oak->Java&#xff0c;进行前端开发&#xff08;相关技…...

SAP-ABAP:SAP中REPORT程序和online程序的区别对比

在SAP中&#xff0c;REPORT程序和Online程序&#xff08;通常指Dialog程序&#xff09;是两种常见的ABAP程序类型&#xff0c;它们在用途、结构和用户交互方式上有显著区别。以下是它们的详细对比&#xff1a; 1. 用途 REPORT程序Online程序主要用于数据查询、报表生成和批量数…...

【第2章:神经网络基础与实现——2.1 前馈神经网络的结构与工作原理】

老铁们好!今天我们要来一场长达两万字的超详细技术探险,我会像拆解乐高积木一样把前馈神经网络(Feedforward Neural Network)的每个零件摆在台面上,用最接地气的方式让你彻底搞懂这个深度学习基石的工作原理。准备好了吗?我们开始吧! 第一章:神经网络的 “乐高积木” 1…...

Pythong 解决Pycharm 运行太慢

Pythong 解决Pycharm 运行太慢 官方给Pycharm自身占用的最大内存设低估了限制,我的Pycharm刚开始默认是256mb。 首先找到自己的Pycharm安装目录 根据合适自己的改 保存&#xff0c;重启Pycharm...

P6792 [SNOI2020] 区间和 Solution

Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1​,a2​,⋯,an​)&#xff0c;有 m m m 个操作分两种&#xff1a; chmax ⁡ ( l , r , v ) \operatorname{chmax}(l,r,v) chmax(l,r,v)&#xff1a;对每个 i ∈ [ l , r ] i \in [l,r] i∈[l,…...

基于ArduPilot开发无人机飞控自动驾驶仪

目录 1、项目参数 2、硬件设计解析 2.1、主控与协处理器架构 2.2、高精度传感器集成 2.3、数据存储与恢复 2.4、电源管理与保护 2.5、通信与接口 本项目基于开源飞行控制固件 ArduPilot 开发&#xff0c;设计并实现了一款高度集成的 自动驾驶仪&#xff0c;可广泛应用于…...

Kotlin Lambda

Kotlin Lambda 在探索Kotlin Lambda之前&#xff0c;我们先回顾下Java中的Lambda表达式&#xff0c;Java 的 Lambda 表达式是 Java 8 引入的一项强大的功能&#xff0c;它使得函数式编程风格的代码更加简洁和易于理解。Lambda 表达式允许你以一种更简洁的方式表示实现接口&…...

UniApp 中制作一个横向滚动工具栏

前言 最近在用 UniApp 开发项目时&#xff0c;需要一个横向滑动的工具栏。常见的工具栏一般都是竖着的&#xff0c;但横向滑动的工具栏不仅能展示更多内容&#xff0c;还能让界面看起来更加丰富。不过很多朋友可能会发现&#xff0c;如何让内容“横着”展示又不变形、能流畅滚…...

Qt的QListWidget样式设置

以下是关于QListWidget样式设置的详细说明&#xff0c;包含常用样式配置和进阶技巧&#xff1a; 1. 基础列表样式 // 设置整体列表容器样式 listWidget->setStyleSheet("QListWidget {"" background-color: #f5f5f5;" // 背景颜色" borde…...

OpenCV 模板匹配

模板匹配算法是一种在目标图像中寻找与模板图像相似区域的方法,模板匹配就是拿一个模板图片在一张比模板图像要大的搜索图像上寻找与模板图像相似的区域,以此来得到目标在搜索图像上的位置,其核心是将模板图像在待搜索图像上从左到右、从上到下依次逐像素平移滑动,每次滑动…...

Vue 3 30天精进之旅:Day 25 - PWA支持

一、引言 在前面的24天中&#xff0c;我们已经深入探讨了Vue 3的许多核心概念和高级特性。今天&#xff0c;我们将进入一个全新的领域——PWA&#xff08;Progressive Web App&#xff09;。PWA是一种现代Web应用程序的开发模式&#xff0c;它结合了Web和原生应用的优点&#…...

arm linux下的中断处理过程。

本文基于ast2600 soc来阐述&#xff0c;内核版本为5.10 1.中断gic初始化 start_kernel() -> init_IRQ() -> irqchip_init() of_irq_init()主要是构建of_intc_desc. 489-514: 从__irqchip_of_table中找到dts node中匹配的of_table(匹配matches->compatible)&#xf…...

Linux上Elasticsearch 集群部署指南

Es 集群部署 Es 集群部署 Es 集群部署 准备好三台服务器。示例使用&#xff1a;110.0.5.141/142/143 1、es用户和用户组创建&#xff0c;使用root账号 groupadd esuseradd -g es es2、将es安装包和ik分词器上传到&#xff1a;/home/es/目录下&#xff08;任意目录都行&#…...

SpringBoot+shardingsphere实现按月分表功能

SpringBootshardingsphere实现按月分表功能 文章目录 前言 ShardingSphere 是一套开源的分布式数据库中间件解决方案&#xff0c;旨在简化数据库分片、读写分离、分布式事务等复杂场景的管理。它由 Apache 软件基金会支持&#xff0c;广泛应用于需要处理大规模数据的系统中 一…...

如何设置 Nginx 连接超时并进行测试(Nginx优化)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Nginx-从零开始的服务器之旅专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年2月15日14点22分 在高并发场景下&#xff0c;如…...

Python实现AWS Fargate自动化部署系统

一、背景介绍 在现代云原生应用开发中,自动化部署是提高开发效率和保证部署质量的关键。AWS Fargate作为一项无服务器计算引擎,可以让我们专注于应用程序开发而无需管理底层基础设施。本文将详细介绍如何使用Python实现AWS Fargate的完整自动化部署流程。 © ivwdcwso (ID…...

ubuntu20.04声音设置

step1&#xff1a;打开pavucontrol&#xff0c;设置Configuration和Output Devices&#xff0c; 注意需要有HDMI / DisplayPort (plugged in)这个图标。如果没有&#xff0c;就先选择Configuration -> Digital Stereo (HDMI 7) Output (unplugged) (unvailable)&#xff0c;…...

AWS Database Migration Service

AWS Database Migration Service (DMS) 是亚马逊 Web 服务&#xff08;AWS&#xff09;提供的一项服务&#xff0c;旨在帮助用户将数据库迁移到 AWS 云环境中。无论是将现有的数据库迁移到 Amazon RDS&#xff08;关系型数据库服务&#xff09;、Amazon Aurora、Amazon Redshif…...

ROS学习

1.ROS工作空间 存放项目开发相关文件的文件夹&#xff1b; src:代码空间&#xff08;Source Space&#xff09;install:安装空间&#xff08;Install Space&#xff09;build:编译空间&#xff08;Build Space&#xff09;log:日志空间&#xff08;Log Space&#xff09; 2.c…...

【NLP 24、模型训练方式】

你的痛苦&#xff0c;我都心疼&#xff0c;想为你解决 —— 25.2.15 一、按学习范式分类 1. 监督学习&#xff08;Supervised Learning&#xff09; 核心思想&#xff1a;使用带有标签&#xff08;已知输入-输出对&#xff09;的数据训练模型。 常见任务&#xff1a;分类&…...

【算法】【区间和】acwing算法基础 802. 区间和 【有点复杂,但思路简单】

题目 假定有一个无限长的数轴&#xff0c;数轴上每个坐标上的数都是 0。 现在&#xff0c;我们首先进行 n 次操作&#xff0c;每次操作将某一位置 x 上的数加 c。 接下来&#xff0c;进行 m 次询问&#xff0c;每个询问包含两个整数 l 和 r&#xff0c;你需要求出在区间 [l,r] …...

DeepSeek 从入门到精通学习指南,2025清华大学《DeepSeek从入门到精通》正式发布104页pdf版超全解析

DeepSeek 是一款强大的 AI 搜索引擎&#xff0c;广泛应用于企业级数据检索和分析。无论您是初学者还是有经验的用户&#xff0c;掌握 DeepSeek 的使用都能为您的工作带来极大的便利。本文将从入门到精通&#xff0c;详细介绍如何学习和使用 DeepSeek。 链接: https://pan.baid…...

PyQt6/PySide6 的自定义信号实现

一、信号与槽基础概念 核心机制&#xff1a;观察者模式的实现&#xff0c;支持对象间的松耦合通信原生信号&#xff1a;内置控件&#xff08;如QPushButton&#xff09;的clicked等预定义信号自定义优势&#xff1a;实现业务逻辑解耦&#xff0c;增强组件复用性 PyQt6/PySide…...

什么是FCC认证

联邦通信委员会&#xff08;FCC&#xff09;认证是美国一种强制性的认证&#xff0c;确保电子设备在上市前符合特定标准。 联邦通讯委员会&#xff08;FCC&#xff09;----管理进口和使用无线电频率装置&#xff0c;包括电脑、传真机、电子装置、无线电接收和传输设备、无线电…...

共享设备管理难?MDM助力Kiosk模式一键部署

目录 1. 简化设备部署与配置&#xff1a;实现一键式部署 2. 自动化应用更新与内容推送&#xff1a;确保设备始终保持最新状态 3. 权限控制与设备安全&#xff1a;防止滥用与数据泄露 4. 远程管理与故障诊断&#xff1a;保障设备长期稳定运行 5. 数据分析与报告&#xff1a…...

RAMinit 程序编译运行考古记录

本科的时候浅浅研究了DOSBox&#xff0c;今天看到网上挺多关于雷军代码的新闻&#xff0c;我把雷军代码在web上编译出来了&#xff0c;但是我是业余选手&#xff0c;所以做得比较差&#xff0c;不过大家感兴趣可以关注我的dosplay项目&#xff0c;旨在利用js-dos实现汇编语言在…...

今日AI和商界事件(2025-02-15)

根据2025年2月15日的科技动态&#xff0c;以下是今日AI领域的重要事件及相关进展总结&#xff1a; 1. DeepSeek日活突破3000万&#xff0c;开源生态加速AI普惠 里程碑意义&#xff1a;开源大模型DeepSeek宣布日活跃用户数突破3000万&#xff0c;其R1模型凭借开源策略和低成本优…...

鸿蒙开发:熟知@BuilderParam装饰器

前言 本文代码案例基于Api13。 在实际的开发中&#xff0c;我们经常会遇到自定义组件的情况&#xff0c;比如通用的列表组件&#xff0c;选项卡组件等等&#xff0c;由于使用方的样式不一&#xff0c;子组件是动态变化的&#xff0c;针对这一情况&#xff0c;就不得不让使用方把…...

基于和声搜索(Harmony Search, HS)的多中心点选址优化算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于和声搜索(Harmony Search, HS)的多中心点选址优化算法matlab仿真。可以设置多个不同的中心点。 2.测试软件版本以及运行结果展示 matlab2022a/matlab2024b版…...

word文档提取信息

目录 一、说明二、Aspose-words方式2.1、Aspose-words介绍2.2、Aspose-words使用说明2.3、Aspose-words解析核心代码一、说明 项目中遇到这样的一个需求问题:“一个docx文档,用户根据关键词能搜索定位到文档的哪一页”。docx文档主要有文本、表格、图片、附件这几类组合,为…...