【原创】实现ChatGPT中Transformer模型之输入处理

作者:黑夜路人

时间:2023年7月

Inputs Process(输入处理层)实现

我们看整个绿色框的整个位置,就是Inputs Process(输入处理层)。

在输入处理层,其实非常容易理解,主要就是把输入的每个内容(文字)变成能够跟Encoder交互的,深度学习能够理解识别的东西。

里面主要是两个步骤,一个是对输入字符串进行切分(Tokenize)成为一个个token,另外一个步骤是把token放到一个高纬矩阵中(Input Embedding)的过程,还有一个步骤是为了保证知道输入的token的顺序,所以需要把token位置也进行Embedding(Positional Encoding)。

Embedding就是用一个低维稠密的向量“表示”一个对象,这里所说的对象可以是一个词(Word2vec),也可以是一个物品(Item2vec),亦或是网络关系中的节点(Graph Embedding)。其中“表示”这个词意味着Embedding向量能够表达相应对象的某些特征,同时向量之间的距离反映了对象之间的相似性。Embedding目前在NLP领域有很多模型,不同技术解决方案不同,比如传统的 word2vec,还有后面的FastText、GIoVe、Bert Embedding 等等。

需要做Embedding,首先必须把相关的输入内容(文本)变成一个个token(可以简单理解为搜索里面的分词过程),我们看一个OpenAI GPT Tokenize 结果:

针对整个tokenize和token embedding的大概代码,主要是引用 Transformers 中的bert预训练模型和一些相关词表进行tokenize和embedding的过程:

import torch
from transformers import BertTokenizer, BertModel, AutoTokenizer, AutoModel# Tokenizer class
class Tokenizer:def __init__(self, model_path):self.tokenizer = AutoTokenizer.from_pretrained(model_path)def tokenize(self, text):return self.tokenizer.tokenize(text)def convert_tokens_to_ids(self, tokens):return self.tokenizer.convert_tokens_to_ids(tokens)def convert_ids_to_tokens(self, ids):return self.tokenizer.convert_ids_to_tokens(ids)def convert_tokens_to_string(self, tokens):return self.tokenizer.convert_tokens_to_string(tokens)# Input Sequence Embedding class
class InputEmbedding:def __init__(self, model_path):self.embedding_model = BertModel.from_pretrained(model_path)self.tokenizer       = BertTokenizer.from_pretrained(model_path)def get_seq_embedding(self, sequence):input_tokens   = self.tokenizer(sequence, return_tensors='pt')output_tensors = self.embedding_model(**input_tokens)return output_tensorsdef get_input_seq_ids(self, sequence):return self.tokenizer(sequence, return_tensors='pt')def get_input_tokens_embedding(self, input_tokens):return self.embedding_model(**input_tokens)

大概测试 tokenize和embedding 的代码以及输出:

# define pretrained model path
MODEL_BERT_BASE_ZH          = "D:/Data/Models/roc-bert-base-zh"
MODEL_BERT_BASE_CHINESE      = "bert-base-chinese"
MODEL_BERT_BASE              = "bert-base-cased"# input sequence list
seqs = ['我的名字叫做黑夜路人', 'My name is Black',
]# call transformers tokenizer get tokens and token-ids
tokenizer       = Tokenizer(MODEL_BERT_BASE_ZH)
input_embedding = InputEmbedding(MODEL_BERT_BASE_CHINESE)for seq in seqs:tokens = tokenizer.tokenize(seq)print(seq, ' => ', tokens)ids = tokenizer.convert_tokens_to_ids(tokens)print(seq, ' => ', ids)s = input_embedding.get_seq_embedding(seq)print(s[0].shape)print(s[0])

以上Tokenize和Embedding代码最终输出大概如下结果:

对于Transformer等框架来说,基本在处理 Embedding 场景里,基本主要就是每个字符使用 768 个数字来标识一个token(或者是token id)。比如说,假设常用字有5000个,然后基本都只有一个样本,那么大概会生成一个 tensor.Size([5000, 1, 768]) 的矩阵,就能够在一个高纬矩阵里表示所有的字符了。

Positional Encoding就是句子中词语相对位置的编码,让Transformer保留词语的位置信息。

任何一门语言中,词语的位置和顺序对句子意思表达都是至关重要的。传统的RNN模型在处理句子时,以序列的模式逐个处理句子中的词语,这使得词语的顺序信息在处理过程中被天然的保存下来了,并不需要额外的处理。

而对于Transformer来说,由于句子中的词语都是同时进入网络进行处理,顺序信息在输入网络时就已丢失。因此,Transformer是需要额外的处理来告知每个词语的相对位置的。其中的一个解决方案,就是论文中提到的Positional Encoding,将能表示位置信息的编码添加到输入中,让网络知道每个词的位置和顺序。

如果你对现有训练好的Tokenize不满意,因为这个会对最终的深度学习模型效果产生影响,那么我们可以自己简单做Tokenize的技术实现。

比如我们依赖于GPT-2的BPE词表生成自己的一个Tokenize程序,下面是一个自主实现的Tokenize的核心代码:

    # API: BlackTokenizer.encode(text)# 将一个字符串编码成一个整数列表(tokens)def encode(self, text):""" Transforms a string into an array of tokens :param text: string to be encoded:type text: str:returns: an array of ints (tokens)"""if not isinstance(text, str):text = text.decode(self._DEFAULT_ENCODING)    bpe_tokens = []matches = self._regex_compiled.findall(text)for token in matches:token = ''.join([self._byte_encoder[x] for x in self._encode_string(token)])new_tokens = [self._encoder[x] for x in self._bpe(token, self._bpe_ranks).split(' ')]bpe_tokens.extend(new_tokens)return bpe_tokens# API: BlackTokenizer.decode(tokens)# 将输入的整数列表 tokens 转换成原始字符串def decode(self, tokens):""" Transforms back an array of tokens into the original string:param tokens: an array of ints:type tokens: list:returns: the original text which was encoded before"""text = ''.join([self._decoder[x] for x in tokens])textarr = [int(self._byte_decoder[x]) for x in list(text)]text = bytearray(textarr).decode("utf-8")return text

里面比较核心的是针对bpe编码和配套encoder.json的处理,BPE文件格式大概是这样:

配套的encoder.json 大概长这样:

处理BPE的核心代码大概是这样的:

   # use BPE algorithm encode input word or phrase# 使用 BPE(Byte Pair Encoding)算法将输入的单词或词组进行编码, 该方法只负责对单个单词或词组进行 BPE 编码,如果要对一组文本数据进行 BPE 编码,需要调用 _bpe_batch 方法"""BPE 是一种压缩算法,用于将文本数据中常见的连续字符序列合并成单个字符,以减少词汇量并提高压缩效率1. 基于训练数据生成 BPE 码表,即生成常见字母或字符串的组合,并给组合编码一个整数作为标识符。2. 将文本中所有的单词划分成字符或者字符组成的子串。3. 在所有单词中找出出现次数最多的字符或者字符组合,将这个字符或者字符组合当做一个新的字符来替代原有单词中的这个字符或者字符组合。并在编码表中添加这个字符或者字符组合的编码。3. 重复步骤 3 直到达到预设的 BPE 编码次数或者到达最小词频。"""def _bpe(self, token, bpe_ranks):if token in self._cache:return self._cache[token]word = list(token)pairs = self._get_pairs(word)if not pairs:return tokenwhile True:min_pairs = {}for pair in pairs:pair_key = ','.join(pair)rank = bpe_ranks.get(pair_key, float("nan"))min_pairs[10e10 if math.isnan(rank) else rank] = pair_keybigram = min_pairs[min(map(int, min_pairs.keys()))]if not bigram in bpe_ranks:breakbigram = bigram.split(',', 1)first = bigram[0]second = bigram[1]new_word = []i = 0while i < len(word):j = -1try:j = word.index(first, i)except:passif j == -1:new_word.extend(word[i:])breaknew_word.extend(word[i:j])i = jif word[i] == first and i < len(word)-1 and word[i+1] == second:new_word.append(first+second)i += 2else:new_word.append(word[i])i += 1word = new_wordif len(word) == 1:breakpairs = self._get_pairs(word)word = ' '.join(word)self._cache[token] = wordreturn word

基于以上的Tokenize,我们通过一段测试代码:

seqs = ['我的名字叫做黑夜路人', 'My name is Black',"我的nickname叫heiyeluren","はじめまして","잘 부탁 드립니다","До свидания!","😊😁😄😉😆🤝👋","今天的状态很happy,表情是😁",
]print('\n------------------BlackTokenize Test------------------')tk = BlackTokenize()
for seq in seqs:token_list = tk.get_token_list(seq)# print('Text:', seq, ' => Tokens:', tokens)enc_seq = tk.encode(seq)# continuedec_seq = tk.decode(enc_seq)token_count = tk.count_tokens(seq)print( 'RawText:', seq, ' => TokenList:', token_list, ' => TokenIDs', enc_seq, ' => TokenCount:', token_count, '=> DecodeText:', dec_seq)print('------------------BlackTokenize Test------------------\n')

测试代码输出结果:

输出结果可以看到,本质就是把不同的字符或者是字符串转成了一个或多个int类型的数字编码,整个Tokenize的过程算完成。

除了Tokenize(token to id)的过程,还有就是Embedding的过程,对于Transformer来说,主要包含词嵌入和位置嵌入两个环节。

我们实现一下这个输入嵌入(Inputs Embedding)的核心代码:

# 输入嵌入(Input Embeddings)层的构建
'''
这个层的作用是将 tokens 的整数列表编码成相应的向量集合,以便后续可以输入到神经网络中.
为了解决能够体现词与词之间的关系,使得意思相近的词有相近的表示结果,这种方法即 Word Embedding(词嵌入)。
最方便的途径是设计一个可学习的权重矩阵 W,将词向量与这个矩阵进行点乘,即得到新的表示结果。
假设 “爱” 和 “喜欢” 这两个词经过 one-hot 后分别表示为 10000 和 00001,权重矩阵设计如下:
[ w00, w01, w02w10, w11, w12w20, w21, w22w30, w31, w32w40, w41, w42 ]
那么两个词点乘后的结果分别是 [w00, w01, w02] 和 [w40, w41, w42],在网络学习过程中(这两个词后面通常都是接主语,如“你”,“他”等,或者在翻译场景,
它们被翻译的目标意思也相近,它们要学习的目标一致或相近),权重矩阵的参数会不断进行更新,从而使得 [w00, w01, w02] 和 [w40, w41, w42] 的值越来越接近。
我们还把向量的维度从5维压缩到了3维。因此,word embedding 还可以起到降维的效果。
另一方面,其实,可以将这种方式看作是一个 lookup table:对于每个 word,进行 word embedding 就相当于一个lookup操作,在表中查出一个对应结果。
'''
class Embeddings(nn.Module):def __init__(self, d_model, vocab):super(Embeddings, self).__init__()self.lut = nn.Embedding(vocab, d_model)self.d_model = d_modeldef forward(self, x):return self.lut(x) * math.sqrt(self.d_model)

另外把相应位置做嵌入:

# 实现的是 Transformer 模型中的位置编码(Positional Encoding)
'''
word embedding,我们获得了词与词之间关系的表达形式,但是词在句子中的位置关系还无法体现。
由于 Transformer 是并行地处理句子中的所有词,因此需要加入词在句子中的位置信息,结合了这种方式的词嵌入就是 Position Embedding
预定义一个函数,通过函数计算出位置信息,大概公式如下:
\begin{gathered}
PE_{(pos,2i)}=\sin{(pos/10000^{2i/d})} \\
P E_{(p o s,2i+1)}=\operatorname{cos}\left(p o s_{\substack{i=1}{\mathrm{osc}}/\mathrm{1}{\mathrm{999}}\mathrm{2}i/d\right) 
\end{gathered}
Transformer 模型使用自注意力机制来处理序列数据,即在编码器和解码器中分别使用自注意力机制来学习输入数据的表示。
由于自注意力机制只对序列中的元素进行注意力权重的计算,它没有固定位置的概念,
因此需要为序列中的元素添加位置信息以帮助 Transformer 模型学习序列中元素的位置。
'''
class PositionalEncoding(nn.Module):def __init__(self, d_model, dropout, max_len=5000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(p=dropout)  pe = torch.zeros(max_len, d_model)  # max_len代表句子中最多有几个词position = torch.arange(0, max_len).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))  # d_model即公式中的dpe[:, 0::2] = torch.sin(position * div_term)pe[:, 1::2] = torch.cos(position * div_term)pe = pe.unsqueeze(0)self.register_buffer('pe', pe)def forward(self, x):x = x + self.pe[:, :x.size(1)]  # 原向量加上计算出的位置信息才是最终的embeddingreturn self.dropout(x)

实际中Tokenize有很多实现方法,可以用已经预训练好的模型直接调用,或者是现成的各种包进行实现。比如 bert tokenize、spacy Tokenize、tiktoken、gpt3_tokenizer等等都可以,只是中英文处理效果不同,或者是最后的token数量切割大小不同,当然,这个最终也会影响训练效果。

取代你的不是AI,而是比你更了解AI和更会使用AI的人!

##End##

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

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

相关文章

Visual Studio Community 2022 + Win10 编译 OpenCPN 5.9.0 记录

前言 前两天尝试用vs2017编译OpenCPN5.0.0&#xff0c;前后折腾了两三天总算编译成功了。官网给出的编译过程比较简单&#xff0c;我在实际编译过程中遇上了很多很多的问题&#xff0c;最多的就是缺少库&#xff0c;好在最后编译通过了。 后来浏览OpenCPN官网的时候发现发布了…

jmeter简单使用以及报告生成

jmeter入门 前置准备jmeter目录介绍jmeter常用组件jmeter简单食用 第一次写文章&#xff0c;主要就是一些简单的自学笔记&#xff0c;记录一下自己以后能方便看&#xff0c;大家需要的话也可自行取用&#xff0c;这些年干下来&#xff0c;我发现内容不重要&#xff0c;重要的是…

学会 IDEA 中的这个功能,就可以丢掉 Postman 了

点击关注公众号&#xff0c;Java干货及时送达&#x1f447; 转自&#xff1a;oschina 作者&#xff1a;凯京技术团队 my.oschina.net/keking 前言 接口调试是每个软件开发从业者必不可少的一项技能&#xff0c;一个项目的的完成&#xff0c;可能接口测试调试的时间比真正开发写…

分享:python+Windows自带任务计划程序,实现定时自动删微信的video文件夹

目录 0. 背景1. 使用python实现删微信的video文件夹1.1 代码1.2 打包 2. 使用python实现自动添加任务计划程序2.1 代码2.2 打包 3. 使用bat脚本方便操作3.1 手动删.bat3.2 加入定时任务.bat 0. 背景 pc微信实在太占用磁盘空间了&#xff0c;特别是其中的视频文件夹。所以有了这…

搭建本地服务器+jshtml访问mysql并显示输出

文章目录 人生苦短&#xff0c;先问chatgpt1. 用Tomcat配置本地网站2. 访问本地网页3. js访问本地mysql并显示在html中 PS: Chatgpt实在是有点强~~ 今天是悲伤的一天&#xff0c;把mysql密码忘了&#xff0c;网上搜的方法没用&#xff0c;本来以为卸载部分组件就好&#xff0c;…

3句代码,实现自动备份与版本管理

前言&#xff1a; 服务器开发程序、测试版本等越来越多&#xff0c;需要及时做好数据的版本管理和备份&#xff0c;作为21世界的青年&#xff0c;希望这些事情都是可以自动完成&#xff0c;不止做了数据备份&#xff0c;更重要的是做好了版本管理&#xff0c;让我们可以追溯我们…

【编程实践】Linux Shell 编程:使用 循环和递归 实现斐波那契数列代码

用 Linux Shell 编程语言递归实现斐波那契数列代码:循环和递归 Linux Shell 编程语言是一种强大的工具,它可以轻松地实现程序设计语言中的常见算法。 斐波那契数列 斐波那契数列是一种经典的算法,它的定义如下: 斐波那契数列(Fibonacci Sequence)是以递归的方法来定义: …

chatgpt赋能python:Python下载与运行指南-让Python脚本更容易使用

Python下载与运行指南 - 让Python脚本更容易使用 Python已经成为世界上最流行的编程语言之一。Python具有简单易用的特点&#xff0c;几乎可以开始创建任何类型的应用程序或脚本。如果你是一名新手或是一名经验丰富的开发者&#xff0c;Python都是一款优秀的编程语言。 本篇文…

人教版初中英语听力软件,人教版初中英语听力mp3

新目标英语九年级上册Tapescripts所有的翻译 east东方的、eagerness热心、eagle鹰、ear耳朵&#xff0c;听力、early早等。 词汇解析一、east 英 [iːst]   美 [iːst]    adj. 东方的adv. 向东方n. 东方&#xff1b;东部1、east的基本意思是“东&#xff0c;东方”&…

英语语音题

语音题 a i i在nd和gh前发[ai] y th 词首[θ] the、ther结尾[] ch tion ow[哦][奥] oo ar ed 清读[t] 浊元读[d] t、d后读[id] 清&#xff1a;破客服吃屎&#xff0c;戳死赐死 ea o s ou e 欸 b ure 呃 优呃 屋呃 h gh ex 后有元[igz] 后有辅[iks] c al ai wh w在wr、…

ChatGPT: Optimizing Language Models for Dialogue

1.简介 最近ChatGPT很好&#xff0c;本文根据https://openai.com/blog/chatgpt/翻译总结的。 ChatGPT: Optimizing Language Models for Dialogue。如标题&#xff0c;ChatGPT为了对话使用的。 ChatGPT是InstructGPT的兄弟模型&#xff0c;它被训练为以提示prompt的方式遵循…

挑战OpenAI,马斯克宣布xAI正式成立:目标是“了解宇宙真实本质”!

挑战OpenAI&#xff0c;马斯克宣布xAI正式成立&#xff1a;目标是“了解宇宙真实本质”&#xff01; 一、马斯克官宣xAl硬刚OpenAl二、创始团队1/3成员是华人三、马斯克下了一盘棋 北京时间7月13日凌晨&#xff0c;马斯克在Twitter上宣布&#xff1a;“xAI正式成立&#xff0c;…

昨天OpenAI GPT-4,今天微软Copilot,轮番上阵碾压谁?

来源&#xff1a;《飞哥说AI》公众号 作者&#xff5c;高佳 创意&#xff5c;李志飞 微软Copilot横扫Office全系产品&#xff0c;正式改名为Microsoft 365 &#xff0c;其野心昭然。 微软全家桶自带AI智能驾驶模式&#xff0c;意味着 Windows 全AI系列新品将火速渗透各行各业…

自然语言处理:Word2Vec

每种动物都有自己的语言&#xff0c;机器也是&#xff01;自然语言处理&#xff08;NLP&#xff09;就是在机器语言和人类语言之间沟通的桥梁。 NLP通常有情感分析、文本挖掘、信息抽取、信息检索、问答/对话系统&#xff08;聊天机器人&#xff09;、语音识别、机器翻译等应用…

周鸿祎:ChatGPT可能两三年内产生自我意识;字节加入大模型之战;Ubuntu下一版本默认不再支持Flatpak| 极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 苏宓 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#xf…

chatGPT在线网页写论文

帮我写一篇关于秦始皇对于中国的影响论文 秦始皇&#xff0c;即嬴政&#xff0c;是中国历史上的伟大统治者之一。他在位期间统一了中国各个诸侯国&#xff0c;建立了秦朝&#xff0c;并为中国历史上的统一奠定了基础。秦始皇对于中国的影响是深远而持久的&#xff0c;不仅在政…

如何加速大模型开发?技术方案拆解来了:昇思MindSpore技术一览

随着ChatGPT爆火出圈&#xff0c;狂飙之势从22年底持续到23年初&#xff0c;与以往的技术突破不同的是&#xff0c;此次的大模型不仅被技术界关注&#xff0c;而且备受投资界、产业界和大众消费者的追捧&#xff0c;使它成为历史上最快月活过亿的现象级应用&#xff0c;继而引发…

Python项目:基于爬虫+Flask实现招聘岗位数据分析与可视化

在 基于爬虫Flask实现豆瓣电影Top250数据分析 的基础上,进一步实战,开发出本项目 基于爬虫Flask实现的招聘岗位数据分析与可视化,干货多多,也学到了很多的东西。 文章目录 1、项目简介:2、成果展示:3、后台展示4、项目总结: 1、项目简介: 项目主要是对招聘网站 51job 上的招聘信…

微信小程序输入框字数限制以及计算

wxml代码如下: <textarea class"textarea-bg font_s33 font_c31" id"information" maxlength150 placeholder"请输入遇到的问题或建议" name"information" value"{{information}}"bindinput"getDataBindTap"&…

百度推出可24时直播带货的AI数字人

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 前几天我还说做直播太内卷&#xff0c;运营成本太高了呢&#xff0c;今天百度就推出了AI数字人&#xff0c;同时推出了曦灵数字人直播平台。它最大的特色功能就是&#xff1a; (1)有基础文本&#…