ai学习(2)分词、分词算法、加入注意力机制的Seq2Seq结构模型(编码器、解码器、注意力机制)、日期转换实战代码

embedded/2024/9/30 6:21:39/

文章目录

  • 参考书《多模态大模型:算法、应用与微调》
  • 1.分词
  • 2.分词算法
    • 主流的三种分词算法,BPE分词算法(GPT-2、BART、Llama模型)、WordPiece分词算法(BERT模型)、SentencePiece分词算法(ChatGLM、BLOOM、PaLM模型)
  • 3.Seq2Seq结构模型
      • 编码器(Encoder)
      • 解码器(Decoder)
      • 工作流程
  • 4.加入注意力机制的Seq2Seq模型
  • 5.日期转换实战完整代码(加入注意力机制的Seq2Seq结构模型)

参考书《多模态大模型:算法、应用与微调》

1.分词

词元(token)可以理解为最小的语义单元,分词的目的是将输入文本转换为一系列的词元,并且还要保证每个词元拥有相对完整的独立语义。

分词的粒度从细到粗依次是character、subword、word

1.character表示的是单个字符,例如a、b、c、d。

2.word表示的是整个单词,例如water表示水的意思。

3.subword相当于英文中的词根、前缀、后缀等,例如unfortunately中的un、fortun(e)、ly等就是subword,它们都是有含义的。

2.分词算法

分词算法也经历了按词语分、按字分和按子词分三个阶段。

1.按词语分和按字分比较好理解,也有一些工具包可以使用,例如jieba分词。

2.如果是按照子词分,那么词元就可以是偏旁部首,当然对于比较简单的字,一个词元也可以是一个完整的字。
例如,​“江”​“河”​“湖”​“海”这四个字都跟水有关,并且它们都是三点水旁,那么在分词的时候,​“氵”很可能会作为一个词元,​“工”​“可”​“胡”​“每”是另外的词元。
假如“氵”的词元ID为1,​“工”​“可”​“胡”​“每”的词元ID分别是2、3、4、5,那么“江”​“河”​“湖”​“海”的词元序列就可以表示为12、13、14、15。
这样做的好处是,只要字中带有三点水旁,或者词元序列中含有词元ID为1的元素,那么我们就可以认为这个字或者这个词元序列跟水有关。即使是沙漠的“沙”字,是由“氵”和“少”组成的,也可以理解为水很少的地方。

主流的三种分词算法,BPE分词算法(GPT-2、BART、Llama模型)、WordPiece分词算法(BERT模型)、SentencePiece分词算法(ChatGLM、BLOOM、PaLM模型)

1.BPE(Byte Pair Encoding)分词算法是一种用于处理自然语言文本的子词分词技术,它通过统计字符对的频率来迭代地合并最常见的字符对,从而构建词汇表。这种方法能有效处理罕见词汇和新词,减少词汇表的大小,同时保留足够的信息以理解上下文。BPE广泛应用于GPT-2、BART和Llama等模型中,以提高语言生成和理解的能力。

2.WordPiece分词算法是由Google开发的一种子词分词方法,它同样是基于统计的,但与BPE不同的是,WordPiece在构建词汇表时会考虑整个语料库的似然概率,选择能够最大化似然概率的字符对进行合并。BERT模型就使用了WordPiece分词算法,这使得BERT能够有效地处理各种NLP任务,如文本分类、问答和命名实体识别等。

3.SentencePiece是另一种子词分词工具,它不仅支持BPE算法,还支持unigram语言模型。SentencePiece的主要特点是它允许直接从原始句子进行训练,不需要预定义的词汇表,这使得它在处理新词和罕见词时更为灵活。ChatGLM、BLOOM和PaLM等模型采用了SentencePiece分词算法,以增强其对话生成和理解的能力。

3.Seq2Seq结构模型

Seq2Seq(Sequence to Sequence),即序列到序列模型,是一种处理序列数据的模型框架,广泛应用于自然语言处理(NLP)领域中的各种任务,如机器翻译、文本摘要、问答系统等。Seq2Seq模型的核心思想是将一个序列转换为另一个序列,通常包括两个主要部分:编码器(Encoder)和解码器(Decoder)。

在这里插入图片描述

在这里插入图片描述

编码器(Encoder)

编码器通常是一个循环神经网络(RNN)或其变体(如LSTM或GRU),或者是更现代的Transformer架构。它的任务是读取输入序列(如一句话或一段文本),并将其编码成一个固定大小的内部表示,这个表示通常被称为上下文向量(Context Vector)或编码向量。这个向量旨在捕捉输入序列的主要信息。

解码器(Decoder)

解码器也是一个循环神经网络或Transformer架构,它使用编码器的输出作为初始状态,并逐步生成输出序列。在每个时间步,解码器会生成一个输出单元(如一个词或字符),并将其作为下一个时间步的输入,直到生成一个特殊的结束符号,表示序列结束。

工作流程

  1. 输入处理:输入序列首先被分词(Tokenized)和编码(如转换为词向量)。
  2. 编码阶段:编码器读取输入序列,并生成一个上下文向量。
  3. 解码阶段:解码器使用上下文向量作为初始状态,并逐步生成输出序列,直到生成结束符号。
  4. 输出处理:生成的序列通常需要被解码或转换回可读的文本形式。

4.加入注意力机制的Seq2Seq模型

注意力机制(Attention Mechanism)是Seq2Seq模型的一个重要改进,它允许解码器在生成每个输出元素时“关注”输入序列中的不同部分。这种机制通过计算输入序列中每个部分与当前输出的相关性权重来实现,从而使得模型能够更加关注与当前生成任务最相关的输入部分。

注意力机制的引入显著提高了Seq2Seq模型处理长序列和捕捉复杂依赖关系的能力,尤其是在机器翻译等任务中,它使得模型能够更好地理解输入序列的全局上下文,从而生成更加准确和流畅的输出。
在这里插入图片描述
在这里插入图片描述

5.日期转换实战完整代码(加入注意力机制的Seq2Seq结构模型)

import torch as th
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
import numpy as np
import random
import datetime# Constants
SOS_token = 0
EOS_token = 1
PAD_token = 2
MAX_LENGTH = 11# EncoderRNN
class EncoderRNN(nn.Module):def __init__(self, input_size, hidden_size, dropout_p=0.1):super(EncoderRNN, self).__init__()self.hidden_size = hidden_sizeself.embedding = nn.Embedding(input_size, hidden_size)self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True)self.dropout = nn.Dropout(dropout_p)def forward(self, x):x = self.embedding(x)x = self.dropout(x)output, hidden = self.rnn(x)return output, hidden# DecoderRNN
class DecoderRNN(nn.Module):def __init__(self, hidden_size, output_size):super(DecoderRNN, self).__init__()self.embedding = nn.Embedding(output_size, hidden_size)self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True)self.out = nn.Linear(hidden_size, output_size)def forward(self, encoder_outputs, encoder_hidden, target_tensor=None):batch_size = encoder_outputs.size(0)decoder_input = th.empty(batch_size, 1, dtype=th.long).fill_(SOS_token)decoder_hidden = encoder_hiddendecoder_outputs = []for i in range(MAX_LENGTH):decoder_output, decoder_hidden = self.forward_step(decoder_input, decoder_hidden)decoder_outputs.append(decoder_output)if target_tensor is not None:decoder_input = target_tensor[:, i].unsqueeze(1)else:_, topi = decoder_output.topk(1)decoder_input = topi.squeeze(-1).detach()decoder_outputs = th.cat(decoder_outputs, dim=1)decoder_outputs = F.log_softmax(decoder_outputs, dim=-1)return decoder_outputs, decoder_hiddendef forward_step(self, x, hidden):x = self.embedding(x)x = F.relu(x)x, hidden = self.rnn(x, hidden)output = self.out(x)return output, hidden# Attention
class Attention(nn.Module):def __init__(self, hidden_size):super(Attention, self).__init__()self.Wa = nn.Linear(hidden_size, hidden_size)self.Ua = nn.Linear(hidden_size, hidden_size)self.Va = nn.Linear(hidden_size, 1)def forward(self, query, keys):scores = self.Va(th.tanh(self.Wa(query) + self.Ua(keys)))scores = scores.squeeze(2).unsqueeze(1)weights = F.softmax(scores, dim=-1)context = th.bmm(weights, keys)return context, weights# AttentionDecoderRNN
class AttentionDecoderRNN(nn.Module):def __init__(self, hidden_size, output_size, dropout_p=0.1):super(AttentionDecoderRNN, self).__init__()self.embedding = nn.Embedding(output_size, hidden_size)self.attention = Attention(hidden_size)self.rnn = nn.RNN(2 * hidden_size, hidden_size, batch_first=True)self.out = nn.Linear(hidden_size, output_size)self.dropout = nn.Dropout(dropout_p)def forward(self, encoder_outputs, encoder_hidden, target_tensor=None):batch_size = encoder_outputs.size(0)decoder_input = th.empty(batch_size, 1, dtype=th.long).fill_(SOS_token)decoder_hidden = encoder_hiddendecoder_outputs = []attentions = []for i in range(MAX_LENGTH):decoder_output, decoder_hidden, attn_weights = self.forward_step(decoder_input, decoder_hidden, encoder_outputs)decoder_outputs.append(decoder_output)attentions.append(attn_weights)if target_tensor is not None:decoder_input = target_tensor[:, i].unsqueeze(1)else:_, topi = decoder_output.topk(1)decoder_input = topi.squeeze(-1).detach()decoder_outputs = th.cat(decoder_outputs, dim=1)decoder_outputs = F.log_softmax(decoder_outputs, dim=-1)attentions = th.cat(attentions, dim=1)return decoder_outputs, decoder_hidden, attentionsdef forward_step(self, input, hidden, encoder_outputs):embedded = self.dropout(self.embedding(input))query = hidden.permute(1, 0, 2)context, attn_weights = self.attention(query, encoder_outputs)input_rnn = th.cat((embedded, context), dim=2)output, hidden = self.rnn(input_rnn, hidden)output = self.out(output)return output, hidden, attn_weights# DateDataset
class DateDataset(Dataset):def __init__(self, n):self.date_cn = []self.date_en = []for _ in range(n):year = random.randint(1950, 2050)month = random.randint(1, 12)day = random.randint(1, 28)date = datetime.date(year, month, day)self.date_cn.append(date.strftime("%y-%m-%d"))self.date_en.append(date.strftime("%d/%b/%Y"))self.vocab = set([str(i) for i in range(0, 10)] + ["-", "/"] + [i.split("/")[1] for i in self.date_en])self.word2index = {v: i for i, v in enumerate(sorted(list(self.vocab)), start=2)}self.word2index["<SOS>"] = SOS_tokenself.word2index["<EOS>"] = EOS_tokenself.word2index["<PAD>"] = PAD_tokenself.vocab.add("<SOS>")self.vocab.add("<EOS>")self.vocab.add("<PAD>")self.index2word = {i: v for v, i in self.word2index.items()}self.input, self.target = [], []for cn, en in zip(self.date_cn, self.date_en):self.input.append([self.word2index[v] for v in cn])self.target.append([self.word2index["<SOS>"], ] +[self.word2index[v] for v in en[:3]] +[self.word2index[en[3:6]]] +[self.word2index[v] for v in en[6:]] +[self.word2index["<EOS>"], ])self.input, self.target = np.array(self.input), np.array(self.target)def __len__(self):return len(self.input)def __getitem__(self, index):return self.input[index], self.target[index], len(self.target[index])@propertydef num_word(self):return len(self.vocab)# Training
n_epochs = 100
batch_size = 32
hidden_size = 128
learning_rate = 0.001dataset = DateDataset(1000)  # Example size
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=True)
encoder = EncoderRNN(dataset.num_word, hidden_size)
decoder = AttentionDecoderRNN(hidden_size, dataset.num_word)
encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)
decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate)
criterion = nn.NLLLoss()for i in range(n_epochs + 1):total_loss = 0for input_tensor, target_tensor, target_length in dataloader:encoder_optimizer.zero_grad()decoder_optimizer.zero_grad()encoder_outputs, encoder_hidden = encoder(input_tensor)decoder_outputs, _, _ = decoder(encoder_outputs, encoder_hidden, target_tensor)loss = criterion(decoder_outputs.view(-1, decoder_outputs.size(-1)),target_tensor.view(-1).long())loss.backward()encoder_optimizer.step()decoder_optimizer.step()total_loss += loss.item()total_loss /= len(dataloader)if i % 10 == 0:print(f"epoch: {i}, loss: {total_loss}")# Evaluation
def evaluate(encoder, decoder, x):encoder.eval()decoder.eval()# Ensure input is in tensor form and matches batch sizex = th.tensor(np.array([x]))  # Convert input to a batch of size 1encoder_outputs, encoder_hidden = encoder(x)# Correctly initialize the decoder input for a batch size of 1decoder_input = th.ones(x.shape[0], 1).long().fill_(SOS_token)  # [batch_size, 1]decoder_hidden = encoder_hiddendecoder_outputs = []# Generate output step-by-stepfor _ in range(MAX_LENGTH):decoder_output, decoder_hidden, _ = decoder.forward_step(decoder_input, decoder_hidden, encoder_outputs)decoder_outputs.append(decoder_output)# Use the model's own output as the next input_, topi = decoder_output.topk(1)decoder_input = topi.squeeze(-1).detach()decoder_outputs = th.cat(decoder_outputs, dim=1)_, topi = decoder_outputs.topk(1)# Decode the output wordsdecoded_ids = topi.squeeze()decoded_words = []for idx in decoded_ids:decoded_words.append(dataset.index2word[idx.item()])return ''.join(decoded_words)for i in range(5):predict = evaluate(encoder, decoder, dataset[i][0])print(f"input: {dataset.date_cn[i]}, target: {dataset.date_en[i]}, predict: {predict}")

输出结果
epoch: 0, loss: 1.974310448092799
epoch: 10, loss: 0.010178287904108725
epoch: 20, loss: 0.002612524500657474
epoch: 30, loss: 0.001208579239074982
epoch: 40, loss: 0.0007198411941287979
epoch: 50, loss: 0.0022603232458594347
epoch: 60, loss: 0.00045849111460660014
epoch: 70, loss: 0.0002735930226457816
epoch: 80, loss: 0.00019530811668148324
epoch: 90, loss: 0.00014774623512846206
epoch: 100, loss: 0.00011550318477900638
input: 85-08-18, target: 18/Aug/1985, predict: 18/Aug/1985
input: 23-04-25, target: 25/Apr/2023, predict: 25/Apr/2023
input: 19-07-27, target: 27/Jul/2019, predict: 27/Jul/2019
input: 97-06-11, target: 11/Jun/1997, predict: 11/Jun/1997
input: 66-06-16, target: 16/Jun/1966, predict: 16/Jun/1966


http://www.ppmy.cn/embedded/108606.html

相关文章

Facebook群控系统,零门槛营销

群控营销软件是提升营销效率的有力工具&#xff0c;它能够帮助营销人员在短时间内实现多倍的宣传效果。市面上常见的群控营销手段是通过群发信息来实现的。如果你想在Facebook上进行群控营销&#xff0c;那么市场上确实有相关的软件可供选择。今天&#xff0c;我们将介绍一款Fa…

@[TOC](力扣题目-滑动窗口-qsort排序-二分法查找)

通信 LCR 009. 乘积小于 K 的子数组268. 丢失的数字287. 寻找重复数 LCR 009. 乘积小于 K 的子数组 已解答 滑动窗口 给定一个正整数数组 nums和整数 k &#xff0c;请找出该数组内乘积小于 k 的连续的子数组的个数。 示例 1: 输入: nums [10,5,2,6], k 100 输出: 8 解释…

FPGA开发:模块 × 实例化

模块的结构 对于C语言&#xff0c;其基本单元为函数。与此类似&#xff0c;Verilog的基本设计单元称之为"模块"&#xff08;block&#xff09;。对于整个项目的设计思想就是模块套模块。 一个模块由两个部分组成&#xff1a;一部分描述接口&#xff0c;一部分描述逻…

STM32(十一):ADC数模转换器实验

AD单通道&#xff1a; 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO&#xff0c;把GPIO配置成模拟输入的模式。 3.配置多路开关&#xff0c;把左面通道接入到右面规则组列表里。 4.配置ADC转换器&#xff0c; 包括AD转换器和AD数据寄存器。单次转换&#xff0c;连…

记一种常用的实时数据同步方案:Canal+Kafka+Flume

记一种常用的实时数据同步方案&#xff1a;CanalKafkaFlume 在当今数据驱动的业务环境中&#xff0c;数据同步是确保系统间数据一致性的关键环节。一种高效、稳定且可扩展的数据同步方案对于支撑企业的数据处理和分析需求至关重要。本文将介绍一种结合了Canal、Kafka和Flume的…

使用 Python 实现一个支持基本消息传递的 TCP 客户端和服务器

使用 Python 实现一个支持基本消息传递的 TCP 客户端和服务器 在网络编程中,TCP(传输控制协议)是一种可靠的、面向连接的协议,广泛应用于各种网络通信场景。通过 TCP,我们可以实现客户端和服务器之间的可靠数据传输。本文将详细介绍如何使用 Python 实现一个支持基本消息…

二、再识Django

PyCharm创建Django项目 Django的安装及测试 进入虚拟环境或PyCharm提供的terminal终端&#xff0c;输入如下命令即可安装最新Django版本。当然你还可以通过指定所需的Django版本。 pip install django pip install django3.1.6 # 安装指定版本进入命令终端使用如下命令启动测…

仿论坛项目--Kafka,构建TB级异步消息系统

阻塞队列 • BlockingQueue 解决线程通信的问题。阻塞方法&#xff1a;put、take。 • 生产者消费者模式生产者&#xff1a;产生数据的线程。消费者&#xff1a;使用数据的线程。 • 实现类ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue、SynchronousQueue、D…