大模型基础——从零实现一个Transformer(1)

一、Transformer模型架构图

主要模块:

   embedding层:

  • Input/Output Embedding: 将每个标记(token)转换为对应的向量表示。

  • Positional Encoding:由于没有时序信息,需要额外加入位置编码。

   

    N个 block堆叠:

  • Multi-Head Attention: 利用注意力机制对输入进行交互,得到具有上下文信息的表示。根据其所处的位置有不同的变种:邻接解码器嵌入位置是掩码多头注意力,特点是当前位置只能注意本身以及之前位置的信息;掩码多头注意力紧接的多头注意力特点是Key和Value来自编码器的输出,而Query来自底层的输出,目的是在计算输出时考虑输入信息。

  • Layer Normalization: 作用于Transformer块内部子层的输出表示上,对表示序列进行层归一化。

  • Residual connection :作用于Transformer块内部子层输入和输出上,对其输入和输出进行求和。

  • Position-wise Feedforward Network: 通过多层全连接网络对表示序列进行非线性变换,提升模型的表达能力。

二、分词器BPE 

2.1 原理

字节对编码(BPE, Byte Pair Encoder),又称 digram coding 双字母组合编码,是一种数据压缩 算法,用来在固定大小的词表中实现可变⻓度的子词。

BPE 首先将词分成单个字符,然后依次用另一个字符替换频率最高的一对字符 ,直到循环次数结束。

BPE训练算法的步骤如下:

  1. 初始化语料库

  2. 将语料库中每个单词拆分成字符作为子词,并在单词结尾增加一个</w>字符

  3. 将拆分后的子词构成初始子词词表

  4. 在语料库中统计单词内相邻子词对的频次

  5. 合并频次最高的子词对,合并成新的子词,并将新的子词加入到子词词表

  6. 重复步骤4和5直到进行了设定的合并次数或达到了设定的子词词表大小

2.2 tokenize实现

from collections import defaultdict
import jieba
from  typing import *class BPETokenizer:def __init__(self,special_tokens=[]) -> None:''':param sepcial_tokens: 额外添加的特殊token,'''self.word_freqs = defaultdict(int)self.merges = {}self.token_to_id =  {}self.id_to_token = {}if special_tokens is None:special_tokens = []special_tokens = ['<PAD>', '<UNK>', '<BOS>', '<EOS>'] +  special_tokensfor token in special_tokens:self._add_token(token)self.unk_token = "<UNK>"self.unk_token_id = self.token_to_id.get(self.unk_token)def _add_token(self,token:str) -> None:'''将token添加到词表中:param token::return:'''# 新添加的token添加到最后,所以idx默认是当前词表的长度if token not in self.token_to_id:idx = len(self.token_to_id)self.token_to_id[token] = idxself.id_to_token[idx] = token@propertydef vobcab_size(self) -> int:return len(self.token_to_id)## 以下是训练bpe相关函数 startdef _learn_vocab(self,corpus: list[str]) -> None:'''统计词频:param corpus::return:'''for sentence in corpus:sentence = sentence.lower()# 分词统计词频words = [w for w in jieba.cut(sentence) if w != " "]for word in words:self.word_freqs[word] += 1def _compute_pair_freqs(self,splits) -> dict[Tuple, int]:'''统计相邻字符的共现频率:param splits::return:'''pair_freqs = defaultdict(int)## 遍历word里面的相关的子字符,统计共现频率for word,freq in self.word_freqs.items():split = splits[word]if len(split) == 1:continuefor i in range(len(split) - 1):pair = (split[i], split[i + 1])pair_freqs[pair] += freqreturn pair_freqsdef _merge_pair(self,a:str,b:str,splits):'''合并字符并跟新split里面word对应的字符组成比如 splits里面有个单词是 “hello”:['h','e','l','l','o']如果合并了字符h和字符e,那么就要变成“hello”:['he','l','l','o']:param a::param b::param splits::return:'''for word in self.word_freqs:split = splits[word]if len(split) == 1:continuei = 0while i < len(split) -1:if split[i] == a and split[i+1] == b:split = split[:i] + [a + b] + split[i+2:]else:i += 1splits[word] = splitreturn  splitsdef _merge_vocab(self,vocab_size,splits):''':param vocab_size: 预期的词表的大小,当达到词表大小,bpe算法就结束:param splits::return:'''merges = {}while self.vobcab_size < vocab_size:# 先统计共现频率pari_freqs = self._compute_pair_freqs(splits)best_pair = Nonemax_freq = 0for pair,freq in pari_freqs.items():if max_freq < freq:best_pair = pairmax_freq = freq# 合并新词splits = self._merge_pair(*best_pair,splits)# 将新词加入词表merges[best_pair] = best_pair[0] + best_pair[1]self._add_token(best_pair[0] + best_pair[1])return mergesdef train(self,corpus,vocab_size):'''bpe训练代码:param corpus: 文本语料:param vocab_size: 期望的词表大小:return:'''self._learn_vocab(corpus)splits = {word: [c for c in word] for word in self.word_freqs.keys()}for split in splits.values():for c in split:self._add_token(c)self.merges = self._merge_vocab(vocab_size, splits)## 训练bpe相关函数 enddef tokenize(self,text):'''bpe分词:param text::return:'''text = text.lower()words = [w for w in jieba.cut(text) if w != " "]splits = [[c for c in word] for word in words]for pair,merge in self.merges.items():for idx,split in enumerate(splits):i = 0while i < len(split) -1:if split[i] == pair[0] and split[i+1] == pair[1]:split = split[:i] + [merge] + split[i + 2:]else:i += 1splits[idx] = splitreturn sum(splits,[])def _convert_token_to_id(self,token:str):'''将token转换为具体的id:param token::return:'''return self.token_to_id.get(token,self.unk_token_id)def _convert_id_to_token(self,index:int) -> str:return self.id_to_token.get(index,self.unk_token)def _convert_ids_to_tokens(self,token_ids: list[int]) -> list[str]:return [self._convert_id_to_token(index) for index in token_ids]def _convert_tokens_to_ids(self, tokens: list[str]) -> list[int]:return [self._convert_token_to_id(token) for token in tokens]def encode(self, text: str) -> list[int]:tokens = self.tokenize(text)return self._convert_tokens_to_ids(tokens)def clean_up_tokenization(self, out_string: str) -> str:out_string = (out_string.replace(" .", ".").replace(" ?", "?").replace(" !", "!").replace(" ,", ",").replace(" ' ", "'").replace(" n't", "n't").replace(" 'm", "'m").replace(" 's", "'s").replace(" 've", "'ve").replace(" 're", "'re"))return out_stringdef decode(self, token_ids: list[int]) -> str:tokens = self._convert_ids_to_tokens(token_ids)return self.clean_up_tokenization(" ".join(tokens))if __name__ == '__main__':corpus = ["This is the Hugging Face Course.","This chapter is about tokenization.","This section shows several tokenizer algorithms.","Hopefully, you will be able to understand how they are trained and generate tokens.",]tokenizer = BPETokenizer()tokenizer.train(corpus, 50)print(tokenizer.tokenize("This is not a token."))print(tokenizer.vobcab_size)token_ids = tokenizer.encode("This is not a token.")print(token_ids)# this is not a token.#由于分词的时候没有 对单词的尾部/w处理,导致decode的时候不知道单词是否结束print(tokenizer.decode(token_ids))

三. Embedding层

import math
from torch import  nn,Tensor
class Embedding(nn.Module):def __init__(self,vocab_size:int, d_model:int) -> None:''':param vocab_size: 词库大小:param d_model: embedding的向量维度'''super().__init__()self.embed = nn.Embedding(vocab_size,d_model)self.sqrt_d_model = math.sqrt(d_model)def forward(self,x:Tensor) -> Tensor:''':param x: 输入的Tensor,(batch_size,seq_length):return: Tensor,shape为:(batch_size,seq_length,d_model)'''## 论文里面,初始化以后要乘以 d_model的平方根return self.embed(x) * self.sqrt_d_model

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

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

相关文章

【QT5】<总览四> QT常见绘图、图表及动画

文章目录 前言 一、QFile类读写文件 二、QPainter绘简单图形 三、QChart图表 四、QPropertyAnimation属性动画 五、Q_PROPERTY宏简介 六、自定义属性动画 前言 承接【QT5】&#xff1c;总览三&#xff1e; QT常用控件。若存在版权问题&#xff0c;请联系作者删除&#…

Debezium系列之:记录一次debezium采集tdsql-c数据库数据丢失原因的排查

Debezium系列之:记录一次debezium采集tdsql-c数据库数据丢失原因的排查 一、背景二、排查数据丢失时间段数据采集情况三、捕获提交异常信息四、定位原因五、解决方案一、背景 debezium采集tdsql-c数据库,偶尔会出现数据丢失的情况,出现多次后决定整个链路排查定位问题二、排…

UE5 Mod Support 思路——纯蓝图

原创作者&#xff1a;Chatouille 核心功能 “Get Blueprint Assets”节点&#xff0c;用于加载未来的mod。用基础类BP_Base扩展即可。打包成补丁&#xff0c;放到Content\Paks目录下&#xff0c;即可让游戏访问到内容。 与文中所写不同的地方 5.1或者5.2开始&#xff0c;打…

uniapp封装picker选择器组件,支持关键字查询

CommonPicker.vue组件 路径在 components\CommonPicker.vue <template><view><uni-easyinput v-model"searchQuery" :placeholder"placeholder" /><picker :range"filteredOptions" :range-key"text" v-model&…

【Kubernetes】k8s中,Deployment、Service(svc)和 Pod 之间的关系

在Kubernetes&#xff08;k8s&#xff09;中&#xff0c;Deployment、Service&#xff08;svc&#xff09;和Pod之间的关系是构建、管理和访问容器化应用程序的核心组件。以下是它们之间的关系&#xff0c;以分点表示和归纳的方式描述&#xff1a; Pod&#xff1a; Pod是Kuber…

从零开始:疾控中心实验室装修攻略,让你的实验室一步到位!

在当今充满挑战和变化的世界中&#xff0c;疾病的控制和预防成为了人类生存与发展的重要课题。而疾控中心作为防控疾病的核心机构&#xff0c;其疾控中心实验室设计建设显得尤为重要。下面广州实验室装修公司小编将分享疾控中心实验室设计建设方案&#xff0c;为疾病防控工作提…

如何复制文件描述符

在Linux系统编程中&#xff0c;复制文件描述符是一个常见的操作&#xff0c;通常使用dup或dup2函数来实现。 复制文件描述符的主要原理是创建一个新的文件描述符&#xff0c;该描述符与原始描述符共享相同的文件表项。这意味着它们引用同一个打开的文件&#xff0c;可以进行相…

玩转STM32-通信协议SPI(详细-慢工出细活)

文章目录 一、SPI的基础知识1.1 接口定义1.2 单机和多机通信 二、STM32的SPI工作过程2.1 从选择&#xff08;NSS&#xff09;脚管理2.2 时钟相位与极性2.3 SPI主模式2.4 SPI从模式 三、应用实例 一、SPI的基础知识 1.1 接口定义 SPI系统可直接与各个厂家生产的多种标准外围器…

Python | 开房门(map)

常把map称之为映射&#xff0c;就是将一个元素&#xff08;通常称之为key键&#xff09;与一个相对应的值&#xff08;通常称之为value&#xff09;关联起来 通常用**字典dict**实现了映射这种数据结构 字典也是使用{}来包裹&#xff08;set也是{}&#xff09;&#xff0c;每…

ChatGPT-4o独家揭秘:全国一卷高考语文作文如何轻松斩获满分?

​一、2024年全国一卷高考 二、2018年全国一卷高考 三、2016年全国一卷高考 一、2024年全国一卷高考 技术进步的悖论&#xff1a;我们的问题真的在减少吗&#xff1f; 引言 随着互联网的普及和人工智能的应用&#xff0c;越来越多的问题能够快速得到解答。然而&#xff0c;这引…

网络空间安全数学基础·同余式

6.1 剩余系&#xff08;掌握&#xff09; 6.2 同余式概念与一次同余式&#xff08;熟练&#xff09; 6.3 中国剩余定理&#xff08;熟练&#xff09; 6.1 剩余系 设m是正整数&#xff0c;模m同余的全体整数是一个模m剩余类&#xff0c;即可表示为a qmr&#xff0c; 0≤r<…

idea使用和了解

官网&#xff1a;IntelliJ IDEA – the Leading Java and Kotlin IDE

【MyBatisPlus条件构造器】

文章目录 什么是条件构造器&#xff1f;使用步骤1. 引入 MyBatisPlus 依赖2. 创建实体类3. 使用条件构造器查询4. 执行查询 示例代码 什么是条件构造器&#xff1f; 条件构造器是 MyBatisPlus 提供的一种灵活的查询条件设置方式&#xff0c;它可以帮助开发者构建复杂的查询条件…

持续总结中!2024年面试必问 20 道分布式、微服务面试题(二)

上一篇地址&#xff1a;持续总结中&#xff01;2024年面试必问 20 道分布式、微服务面试题&#xff08;一&#xff09;-CSDN博客 三、CAP定理是什么&#xff1f; CAP定理是分布式系统理论中的一个基本概念&#xff0c;由计算机科学家Eric Brewer在2000年提出&#xff0c;并由…

常见的api:Runtime Object

一.Runtiem的成员方法 1.getRuntime() 当前系统的运行环境 2.exit 停止虚拟机 3.avaliableProcessors 获取Cpu线程的参数 4.maxMemory JVM能从系统中获取总内存大小(单位byte) 5.totalMemory JVM已经从系统中获取总内大小(单位byte) 6.freeMemory JVM剩余内存大小(…

数组对象数据修改后页面没有更新,无法进行编辑,校验失效问题

在 Vue 中&#xff0c;当你通过 Object.assign 或其他方式修改了对象中的某个属性时&#xff0c;Vue 并不会触发组件重新渲染&#xff0c;因此表单中的 input 框无法及时更新。这可能导致在修改表单数据后&#xff0c;页面没有更新&#xff0c;而且表单校验也失效的情况。这是因…

【MATLAB高级编程】入门篇 | 向量化编程

【入门篇】向量化编程 1. 什么是向量?2. 向量的创建2.1 行向量2.2 列向量2.3 使用冒号运算符2.4 使用`linspace`和`logspace`3. 向量的基本操作3.1 向量元素访问3.2 向量的长度3.3 向量的加法和减法3.4 向量的点乘和叉乘3.5 向量的元素乘法和除法4. 向量的高级操作4.1 逻辑索引…

牛客练习赛126(O(n)求取任意大小区间最值)

牛客练习赛126(O(n)求取任意大小区间最值) 牛客练习赛126 A.雾粉与签到题 题意&#xff1a;给出长度为n的数组, 顺序选出任意三个元素&#xff0c;最小化第二个元素 思路&#xff1a; 遍历除了第一个和最后一个元素取最小值即可 AC code&#xff1a; void solve() {int…

深度学习模型中的 `.clone` 作用分析----针对CUDA可能出现的BUG

深度学习模型中的 .clone 作用分析 问题描述与解决 在训练自定义网络结构时出现了两个错误&#xff1a; RuntimeError: CUDA error: device-side assert triggeredRuntimeError: one of the variables needed for gradient computation has been modified by an inplace ope…

使用 tc (Traffic Control)控制网络延时

设置网络延时 1500ms 800ms tc qdisc add dev eth0 root netem delay 1500ms 800msping 测试 ping www.baidu.com取消设置网络延时 sudo tc qdisc del dev eth0 root