从零开始复现GPT2(三):词表,Tokenizer和语料库的实现


源码地址:https://gitee.com/guojialiang2023/gpt2


GPT2

  • 模型
    • 词表
    • Tokenizer
      • `Tokenizer` 类
      • `_normalize` 方法
      • `_tokenize` 方法
      • `_CHINESE_CHAR_RANGE` 和 `_PUNCTUATION_RANGE`
    • 数据集
    • 语料库
      • `TokenizedCorpus` 类

模型

在这里插入图片描述

词表

定义了一个名为 Vocab 的类,用于处理和管理一个词汇表。这个词汇表是从一个文本文件中加载的,通常用于自然语言处理任务。

  1. 类定义与初始化 (__init__ 方法)

    • Vocab 类在初始化时接收几个参数:

      • vocab_path: 一个字符串,表示词汇表文件的路径。
      • unk_token, bos_token, eos_token, pad_token: 这些是特殊的标记,分别用于表示未知词汇、句子开始、句子结束和填充。这些特殊标记有默认值。
    • 在初始化过程中,类首先会保存这些特殊标记为属性,然后从 vocab_path 指定的文件中加载词汇,并将特殊标记添加到词汇表的开始。

    • self.words 是一个列表,包含了所有的词汇(包括额外的特殊标记)。

    • self.vocab 是一个字典,将每个词汇映射到其在列表中的索引,以便快速查找。

  2. 索引或词汇检索 (__getitem__ 方法)

    • 这个方法允许使用词汇(字符串)或索引(整数)来检索对应的索引或词汇。如果输入是字符串,它返回该字符串对应的索引;如果输入是整数,它返回该索引对应的词汇。
  3. 词汇存在性检查 (__contains__ 方法)

    • 此方法用于检查一个特定的词汇是否存在于词汇表中。
  4. 获取词汇表长度 (__len__ 方法)

    • 这个方法返回词汇表的长度。这里有一个特殊的处理:词汇表的大小被调整为8的倍数。这是为了确保某些计算上的效率,如前面所讨论的。
  5. 特殊标记的索引属性

    • unk_idx, bos_idx, eos_idx, pad_idx 分别提供了 unk_token, bos_token, eos_token, pad_token 这些特殊标记在词汇表中的索引。这对于某些处理流程(如输入预处理或模型的解码过程)是很有用的。
from typing import Unionclass Vocab(object):def __init__(self,vocab_path: str,unk_token: str = '<unk>',bos_token: str = '<s>',eos_token: str = '</s>',pad_token: str = '<pad>'):self.unk_token = unk_tokenself.bos_token = bos_tokenself.eos_token = eos_tokenself.pad_token = pad_tokenwith open(vocab_path, 'r', encoding='utf-8') as fp:self.additional_tokens = [bos_token, eos_token, pad_token]# The additional tokens would be inserted before the words.self.words = self.additional_tokens + fp.read().split()self.vocab = {word: i for i, word in enumerate(self.words)}def __getitem__(self, idx_or_token: Union[int, str]) -> Union[str, int]:if isinstance(idx_or_token, str):return self.vocab[idx_or_token]else:return self.words[idx_or_token]def __contains__(self, token: str) -> bool:return token in self.wordsdef __len__(self) -> int:# Note that vocabulary size must be a multiple of 8 although the actual# number of words is less than it.return (len(self.words) + 7) // 8 * 8@propertydef unk_idx(self) -> int:return self.vocab[self.unk_token]@propertydef bos_idx(self) -> int:return self.vocab[self.bos_token]@propertydef eos_idx(self) -> int:return self.vocab[self.eos_token]@propertydef pad_idx(self) -> int:return self.vocab[self.pad_token]

注意,构建词表时,词表的长度必须为8的倍数。
在构建词表的场景中,将词表大小设置为8的倍数可以确保数据在内存中的对齐。内存对齐是指数据在内存中按照一定的边界存储,这样做可以减少CPU或GPU在访问内存时的负载,从而提高数据处理的速度和效率。如果数据没有对齐,处理器可能需要进行额外的内存访问操作来获取完整的数据,这会增加处理时间和能耗。‘

Tokenizer

代码实现了一个文本标记化(Tokenization)工具,特别适用于处理中文文本。它包含了一个Tokenizer类,这个类使用了一个词汇表(Vocab)实例和一些其他参数来进行文本的处理和标记化。下面是对代码中主要部分的详细解释:

Tokenizer

  • 构造函数 (__init__):

    • vocab: 一个Vocab类的实例,包含了词汇表和一些特殊标记(如未知词标记unk_token)。
    • max_word_len: 最大词长,默认为100。这是为了防止处理极长的单词时出现性能问题。
  • encode 方法:

    • 输入一个字符串text,返回一个标记化后的字符串列表。
    • 它首先对文本进行标准化(_normalize),然后对每个标准化后的词进行标记化(_tokenize)。
  • decode 方法:

    • 将标记列表转换回字符串形式。
    • 主要是将特殊字符(如标点符号)重新还原到它们在文本中的正确位置。

_normalize 方法

  • 对输入文本进行预处理。
  • 首先,它通过正则表达式删除控制字符和替换空白字符。
  • 接着,它在中文字符之间插入空格,以确保在后续的标记化过程中中文字符被正确分隔。
  • 最后,它对文本进行额外的分割,特别是在标点符号处进行分割。

_tokenize 方法

  • 对文本进行实际的标记化处理。
  • 这个方法通过对每个单词进行分解,尝试找到词汇表中的匹配项。
  • 如果单词太长或者无法匹配词汇表中的任何词,它会使用未知词标记(unk_token)。
  • 对于每个词,它使用贪心算法逐步减少词的长度,直到找到词汇表中的匹配项。

_CHINESE_CHAR_RANGE_PUNCTUATION_RANGE

  • 这两个变量定义了用于正则表达式的字符范围。
  • _CHINESE_CHAR_RANGE 包含了中文字符的Unicode范围。
  • _PUNCTUATION_RANGE 包含了标点符号的字符范围。
import regex as re
from data import Vocab
from typing import List_CHINESE_CHAR_RANGE = ('\u4e00-\u9fff\u3400-\u4dbf\U00020000-\U0002a6df''\U0002a700-\U0002b73f\U0002b740-\U0002b81f''\U0002b820-\U0002ceaf\uf900-\ufaff''\U0002f800-\U0002fa1f')
_PUNCTUATION_RANGE = '\\p{P}\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e'class Tokenizer(object):def __init__(self,vocab: Vocab,max_word_len: int = 100):self.vocab = vocabself.exclude_tokens = [vocab.unk_token] + vocab.additional_tokensself.max_word_len = max_word_lendef encode(self, text: str) -> List[str]:return [tokenfor normalized in self._normalize(text)for token in self._tokenize(normalized)]def decode(self, tokens: List[str]) -> str:return (' '.join(tokens).replace(' ##', '').replace(' .', '.').replace(' ?', '?').replace(' !', '!').replace(' ,', ',').replace(' \' ', '\'').replace(' \" ', '\"').replace('\'\'', '\' \'').replace('\"\"', "\" \""))def _normalize(self, text: str) -> List[str]:# Normalize whitespace characters and remove control characters.text = ' '.join(re.sub('[\x00\uFFFD\\p{C}]', '', t)for t in text.split())# Insert whitespaces between chinese characters.text = re.sub(f'([{_CHINESE_CHAR_RANGE}])', r' \1 ', text)normalized = []for t in text.split():if t in self.exclude_tokens:normalized.append(t)else:# Prevent from treating tokens with punctuations.normalized += re.split(f'([{_PUNCTUATION_RANGE}])', t.lower())return ' '.join(normalized).split()def _tokenize(self, text: str) -> List[str]:subwords = []for token in text.split():if len(token) > self.max_word_len:subwords.append(self.vocab.unk_token)continuechildren = []while token and token != '##':current, token = token, ''while current and current != '##':# If subword is in vocabulary, add to list and re-calibrate# the target token.if current in self.vocab:children.append(current)token = '##' + tokenbreak# If subword is not in vocabulary, reduce the search range# and test it again.current, token = current[:-1], current[-1] + token# Process current token as `unknown` since there is no any# proper tokenization (in greedy).if not current:children, token = None, Nonesubwords += children or [self.vocab.unk_token]return subwords

数据集

import torch
from typing import Optional, Dict, Anyclass Dataset(object):def skip(self, count: int):raise NotImplementedError()def fetch(self, batch: Optional[int] = None) -> Dict[str, torch.Tensor]:raise NotImplementedError()def where(self) -> Dict[str, Any]:raise NotImplementedError()def assign(self, where: Dict[str, Any]):raise NotImplementedError()

语料库

代码定义了一个名为 TokenizedCorpus 的类,它继承自 Dataset 类。这个类的主要目的是为了处理一个经过分词处理的语料库,并在此基础上提供一些实用功能,适用于深度学习和自然语言处理任务中。以下是对代码的详细解释:

TokenizedCorpus

  • 构造函数 (__init__):

    • corpus_path: 语料库文件的路径。
    • vocab: 一个 Vocab 类的实例,包含词汇表。
    • seq_len: 序列长度,定义了语料库中每个样本的固定长度。
    • repeat: 一个布尔值,指示是否在语料库读取完毕后从头开始重复。
  • skip 方法:

    • 跳过指定数量的行(即样本)。
    • 如果到达文件末尾且 repeat 为真,则会从文件开始处继续读取。
    • 如果 repeat 为假,则在达到文件末尾时抛出 StopIteration 异常。
  • _fetch_one 方法:

    • 私有方法,用于获取单个样本。
    • 从文件中读取一行,将其分割为标记,并将这些标记转换为它们在词汇表中的索引。
    • 在序列的开始和结束添加特殊标记(如 BOS(开始标记)和 EOS(结束标记))。
    • 如果必要,使用 PAD(填充标记)将序列长度扩充至 seq_len
    • 返回一个包含输入和输出序列的字典。
  • fetch 方法:

    • 公开方法,用于获取一个或多个样本。
    • 如果未指定 batch,则获取单个样本;如果指定了 batch,则获取指定数量的样本。
    • 将样本数据转换为 PyTorch 张量。
  • where 方法:

    • 返回当前文件读取位置的信息。
    • 这对于记录和恢复数据读取位置很有用。
  • assign 方法:

    • 设置文件读取位置。
    • 通过 where 方法得到的位置信息可以用来恢复读取位置。
import torch
from gpt2.data import Dataset, Vocab
from typing import Dict, Any, List, Optionalclass TokenizedCorpus(Dataset):def __init__(self,corpus_path: str,vocab: Vocab,seq_len: int,repeat: bool = True):self.corpus_fp = open(corpus_path, 'r', encoding='utf-8')self.vocab = vocabself.seq_len = seq_lenself.repeat = repeatdef skip(self, count: int):for _ in range(count):if not self.corpus_fp.readline():# Raise error when all sequences are fetched.if not self.repeat:raise StopIteration()# Or, move to the first of the corpus.self.corpus_fp.seek(0)self.corpus_fp.readline()def _fetch_one(self) -> Dict[str, List[int]]:while True:# Read subword-tokenized sequence from corpus.line = self.corpus_fp.readline()if not line:# Raise error when all sequences are fetched.if not self.repeat:raise StopIteration()# Or, move to the first of the corpus.self.corpus_fp.seek(0)continue# Use token indices rather than the token names directly.indices = [self.vocab[t] for t in line.split()]if len(indices) + 2 > self.seq_len:continue# Decorate the sequence with additional tokens.indices = [self.vocab.bos_idx] + indices + [self.vocab.eos_idx]indices += [self.vocab.pad_idx] * (self.seq_len - len(indices) + 1)return {'input': indices[:-1], 'output': indices[1:]}def fetch(self, batch: Optional[int] = None) -> Dict[str, torch.Tensor]:if batch is None:data = self._fetch_one()else:data = [self._fetch_one() for _ in range(batch)]data = {k: [d[k] for d in data] for k in data[0]}return {k: torch.tensor(v, dtype=torch.long) for k, v in data.items()}def where(self) -> Dict[str, Any]:return {'offset': self.corpus_fp.tell()}def assign(self, where: Dict[str, Any]):self.corpus_fp.seek(where['offset'])

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

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

相关文章

【项目日记(六)】第二层: 中心缓存的具体实现(下)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

给准备从事软件开发工作的年轻人的13个建议

从事软件开发是一个不断学习和适应变化的过程。这里有一些针对刚入行或准备从事软件开发工作的年轻人的建议&#xff1a; 掌握基础知识&#xff1a;确保你有扎实的编程基础。了解至少一种编程语言的语法和核心概念&#xff0c;比如C语言、Python、Java或C#。同时&#xff0c;理…

Spring 中获取 Bean 对象的三种方式

目录 1、根据名称获取Bean 2、根据Bean类型获取Bean 3、根据 Bean 名称 Bean 类型来获取 Bean&#xff08;好的解决方法&#xff09; 假设 Bean 对象是 User&#xff0c;并存储到 Spring 中&#xff0c;注册到 xml 文件中 public class User {public String sayHi(){retur…

Meta开源Code Llama 70B,缩小与GPT-4之间的技术鸿沟

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

MIT6.5830 实验1

GoDB 介绍 实验中实现的数据库被称为GoDB&#xff0c;根据 readMe1 中的内容可知&#xff0c;GoDB 含有&#xff1a; Structures that represent fields, tuples, and tuple schemas; Methods that apply predicates and conditions to tuples; One or more access methods …

LeetCode 829. 连续整数求和

一开始我想的是质因数分解&#xff0c;然后项数 为奇数的好解决但是偶数弄不了 然后看题解发现了你直接写出通项公式&#xff1a; 假设首项是a&#xff0c;项数为k 则 (ak-1a)*k 2*n 看看k 的范围 2*a 2n/k 1-k>2 2*n/k >k1 2n>k*k 所以可以暴力枚举k sqrt…

Java 开发环境 全套包含IDEA

一、JDK配置 1.下载 JDK Builds from Oracle 去这边下载open JDK 2.JDK环境变量配置 按win&#xff0c;打开设置 找到环境变量编辑 这边输入的是你下载的那个JDK的bin的路径 检擦配置是否正确在cmd中输入 二、IDEA安装配置 1.下载&#xff08;社区版&#xff09; JetBrai…

分类预测 | Matlab实现DT决策树多特征分类预测

分类预测 | Matlab实现DT决策树多特征分类预测 目录 分类预测 | Matlab实现DT决策树多特征分类预测分类效果基本描述程序设计参考资料分类效果

如何用Docker+jenkins 运行 python 自动化?

1.在 Linux 服务器安装 docker 2.创建 jenkins 容器 3.根据自动化项目依赖包构建 python 镜像(构建自动化 python 环境) 4.运行新的 python 容器&#xff0c;执行 jenkins 从仓库中拉下来的自动化项目 5.执行完成之后删除容器 前言 环境准备 Linux 服务器一台(我的是 CentOS7)…

【排序算法】归并排序

文章目录 一&#xff1a;基本概念1.1 定义1.2 算法思路1.3 图解算法1.4 合并两个有序数组流程1.5 动画展示 二&#xff1a;性能2.1 算法性能2.2 时间复杂度2.3 空间复杂度2.4 稳定性 三&#xff1a;代码实现 一&#xff1a;基本概念 1.1 定义 归并排序&#xff08;Merge sort…

【论文阅读|小目标分割算法ASF-YOLO】

论文阅读|小目标分割算法ASF-YOLO 摘要&#xff08;Abstract&#xff09;1 引言&#xff08;Introduction&#xff09;2 相关工作&#xff08;Related work&#xff09;2.1 细胞实例分割&#xff08;Cell instance segmentation&#xff09;2.2 改进的YOLO用于实例分割&#xf…

OpenCV 0 - VS2019配置OpenCV

1 配置好环境变量 根据自己的opencv的安装目录配置 2 新建一个空项目 3 打开 视图->工具栏->属性管理器 4 添加新项目属性表 右键项目名(我这是opencvdemo)添加新项目属性表,如果有配置好了的属性表选添加现有属性表 5 双击选中Debug|x64的刚添加的属性表 6 (重点)添…

vue使用mpegts.js教程

vue使用mpegts.js教程 最简单好用的H265网页播放器-mpegts.js简介特征受限性 使用步骤安装引入HTML 中添加视频标签video知识扩展 在容器里创建播放器 最简单好用的H265网页播放器-mpegts.js H265是新一代视频编码规范&#xff0c;与H264相比压缩比更高&#xff0c;同样的码率下…

JUC CompletableFuture

文章目录 CompletableFuture^1.8^CompletionStage 接口thenApply 系列thenAccept 系列thenRun 系列thenCombine 系列thenAcceptBothrunAfterBothapplyToEitheracceptEitherrunAfterEitherthenComposewhenCompletehandle其他 CompletionStage 的方法总结 CompletableFuture 实例…

excel中提取一串数字中的某几个数字

excel中提取一串数字中的某几个数字 提取一串数字中的某几个数字&#xff0c;使用公式函数截取数据 LEFT函数&#xff1a;用于截取单元格左边的字符&#xff0c;例如“LEFT(A1,5)”会返回A1单元格中的前5个字符。RIGHT函数&#xff1a;用于截取单元格右边的字符&#xff0c;例…

软件工程知识梳理6-运行和维护

软件维护需要的工作量很大&#xff0c;大型软件的维护成本高达开发成本的4倍左右。所以&#xff0c;软件工程的主要目的就是要提高软件的可维护性&#xff0c;减少软件维护所需要的工作量&#xff0c;降低软件系统的总成本。 定义&#xff1a;软件已经交付使用之后&#xff0c;…

真机调试,微信小程序,uniapp项目在微信开发者工具中真机调试,手机和电脑要连同一个wifi,先清空缓存,页面从登录页进入,再点真机调试,这样就不会报错了

微信小程序如何本地进行真机调试&#xff1f;_unity生成的微信小程序怎么在电脑上真机测试-CSDN博客 微信小程序 真机调试 注意事项 uniapp项目在微信开发者工具中真机调试&#xff0c;手机和电脑要连同一个wifi&#xff0c;先清空缓存&#xff0c;页面从登录页进入&#xf…

Flask 入门2:路由

1. 前言 在上一节中&#xff0c;我们使用到了静态路由&#xff0c;即一个路由规则对应一个 URL。而在实际应用中&#xff0c;更多使用的则是动态路由&#xff0c;它的 URL是可变的。 2. 定义一个很常见的路由地址 app.route(/user/<username>) def user(username):ret…

安装 vant-ui 实现底部导航栏 Tabbar

本例子使用vue3 介绍 vant-ui 地址&#xff1a;介绍 - Vant 4 (vant-ui.github.io) Vant 是一个轻量、可定制的移动端组件库 安装 通过 npm 安装&#xff1a; # Vue 3 项目&#xff0c;安装最新版 Vant npm i vant # Vue 2 项目&#xff0c;安装 Vant 2 npm i vantlatest-v…