Out of Vocabulary处理方法

news2025/1/16 9:13:19

Out of Vocabulary

我们在NLP任务中一般都会有一个词表,这个词表一般可以使用一些大牛论文中的词表或者一些大公司的词表,或者是从自己的数据集中提取的词。但是无论当后续的训练还是预测,总有可能会出现并不包含在词表中的词,这种情况叫做Out of Vocabulary。

那么当我们遇到OOV的问题时,有以下解决方式。

Ignore

直接忽略OOV的情形,也就是不做处理,效果肯定不好。

UNK

这种方式就是把所有超出词表的词,转换为统一的<unk>标识,对于性能的提升有限,特别是当超出词表的词是比较重要的词时。

在这里插入图片描述

Enlarge vocabulary

扩充词表就是把所有的词都尽可能的添加到词表中(包括非常低频率的词),这样就会带来问题:

  1. 计算量大,速度变慢,因为每次要预测的数量会变大;
  2. 低频率词的训练样本会很少,难以充分训练,最终结果就是训练不到一个理想的效果。

Individual Character

拆分成字符(如英文字母)训练,缺点就是丢失了语义和语法。

Spell Check

拼写检查,避免因为数据错误而导致的OOV

subword

子词(类似于词根的思想),此方法介于单词和字符之间,借助n-gram实现。

在这里插入图片描述

缺点:会生成非常多的子词,为了解决这一问题,利用hash将词哈希到数字以减少内存的占用。

BPE(Byte Pair Encoding)

BPE的方法其实是对subword方法的一种优化,就是减少子词。在GPTRoBERTa中就是使用的BPE的方法。

在这里插入图片描述

代码实现

自己动手实现(简化版)
import re
from typing import Dict, Tuple, List, Set

# 这里的词表需要根据自己的文本,把词的频率统计出来,并按字符分割
vocab = {
    'l o w </w>': 5,
    'l o w e r </w>': 2,
    'n e w e s t </w>': 6,
    'w i d e s t </w>': 3,
    'h a p p i e r </w>': 2
}


# 获取所有的组合和频率
def get_pair_status(vocab: Dict[str, int]) -> Dict[Tuple[str, str], int]:
    pairs = {}
    for word, frequency in vocab.items():
        symbols = word.split()

        for i in range(len(symbols) - 1):
            pair = (symbols[i], symbols[i+1])
            current_frequency = pairs.get(pair, 0)
            pairs[pair] = current_frequency+frequency
    return pairs


pair_status = get_pair_status(vocab)
print(pair_status)

{('l', 'o'): 7, ('o', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 8, ('e', 'r'): 4, ('r', '</w>'): 4, ('n', 'e'): 6, ('e', 'w'): 6, ('e', 's'): 9, ('s', 't'): 9, ('t', '</w>'): 9, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'e'): 3, ('h', 'a'): 2, ('a', 'p'): 2, ('p', 'p'): 2, ('p', 'i'): 2, ('i', 'e'): 2}

# 根据已经筛选到的最大频率组合,合并词表
def merge_vocab(pair: Tuple[str, str], vocab_in: Dict[str, int]) -> Dict[str, int]:
    vocab_out = {}
    pattern = re.escape(' '.join(pair))
    replacement = ''.join(pair)

    for word_in in vocab_in:
        word_out = re.sub(pattern, replacement, word_in)
        vocab_out[word_out] = vocab_in[word_in]

    return vocab_out


# 根据频率值,获取最大字符对
best_pair = max(pair_status, key=pair_status.get)
print(best_pair)

new_vocab = merge_vocab(best_pair, vocab)
print(new_vocab)


('e', 's')
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3, 'h a p p i e r </w>': 2}

上述过程只是一轮迭代的结果,在BPE算法中,最终要生成预先设定好的词表大小的字符对,那么也就是需要不断循环来实现。

# 完整迭代
bpe_codes = {}
nums_merge = 10
for i in range(nums_merge):
    print('\niteration', i)
    pair_status = get_pair_status(vocab)
    if not pair_status:
        break

    best_pair = max(pair_status, key=pair_status.get)
    bpe_codes[best_pair] = i

    print('vocabulary: ', vocab)
    print('best pair: ', best_pair)
    vocab = merge_vocab(best_pair, vocab)

print('\n final vocabulary: ', vocab)
print('\n byte pair encoding: ', bpe_codes)


iteration 0
vocabulary:  {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('e', 's')
iteration 1
vocabulary:  {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('es', 't')
iteration 2
vocabulary:  {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('est', '</w>')
iteration 3
vocabulary:  {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('l', 'o')
iteration 4
vocabulary:  {'lo w </w>': 5, 'lo w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('lo', 'w')
iteration 5
vocabulary:  {'low </w>': 5, 'low e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('n', 'e')
iteration 6
vocabulary:  {'low </w>': 5, 'low e r </w>': 2, 'ne w est</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('ne', 'w')
iteration 7
vocabulary:  {'low </w>': 5, 'low e r </w>': 2, 'new est</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('new', 'est</w>')
iteration 8
vocabulary:  {'low </w>': 5, 'low e r </w>': 2, 'newest</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('low', '</w>')
iteration 9
vocabulary:  {'low</w>': 5, 'low e r </w>': 2, 'newest</w>': 6, 'w i d est</w>': 3, 'h a p p i e r </w>': 2}
best pair:  ('e', 'r')
    
 final vocabulary:  {'low</w>': 5, 'low er </w>': 2, 'newest</w>': 6, 'w i d est</w>': 3, 'h a p p i er </w>': 2}
 byte pair encoding:  {('e', 's'): 0, ('es', 't'): 1, ('est', '</w>'): 2, ('l', 'o'): 3, ('lo', 'w'): 4, ('n', 'e'): 5, ('ne', 'w'): 6, ('new', 'est</w>'): 7, ('low', '</w>'): 8, ('e', 'r'): 9}

这里我们自行实现(思考:怎么判断生成的词数量达到预先设定的词表大小了?),锻炼动手能力,而实际上已经有相关的包封装了BPE的实现过程,主要有下面两种。

Google的sentencepiece

参考github项目:https://github.com/google/sentencepiece

安装:pip install sentencepiece

input_file = 'botchan.txt'  # 在上面的github项目中
max_num_words = 10000
model_type = 'bpe'
model_prefix = 'sentencepiece'
pad_id = 0
unk_id = 1
bos_id = 2
eos_id = 3

sentencepiece_params = ' '.join([
    '--input={}'.format(input_file),
    '--model_type={}'.format(model_type),
    '--model_prefix={}'.format(model_prefix),
    '--vocab_size={}'.format(max_num_words),
    '--pad_id={}'.format(pad_id),
    '--unk_id={}'.format(unk_id),
    '--bos_id={}'.format(bos_id),
    '--eos_id={}'.format(eos_id)
])

print(sentencepiece_params)
SentencePieceTrainer.train(sentencepiece_params)

这里方法名叫做train,实际上也就是计算,适合大规模的数据处理,速度比较快。最后会生成并保存两个文件分别为:sentencepiece.modelsentencepiece.vocab

通过以下方式可以加载上面训练得到的词表

sp = SentencePieceProcessor()
sp.load("{}.model".format(model_prefix))
print('Found {} unique tokens.'.format(sp.get_piece_size()))

Found 10000 unique tokens.

original = 'This is a test'
encoded_piece = sp.encode_as_pieces(original)
print(encoded_piece)

encoded_ids = sp.encode_as_ids(original)
print(encoded_ids)

['▁This', '▁is', '▁a', '▁t', 'est']
[475, 98, 6, 4, 264]

decoded_pieces = sp.decode_pieces(encoded_piece)
print(decoded_pieces)

decoded_ids = sp.decode_ids(encoded_ids)
print(decoded_ids)

This is a test
This is a test

piece_id = sp.piece_to_id('▁This')
print(piece_id)

print(sp.id_to_piece(piece_id))

475
▁This
huggingface transformer的 tokenizers

安装:pip install tokenizers

tokenizer = Tokenizer(BPE())

tokenizer.normalizer = Sequence([NFKC(), Lowercase()])
tokenizer.pre_tokenizer = ByteLevel()

tokenizer.decoder = ByteLevelDecoder()
trainer = BpeTrainer(vocab_size=10000, show_progress=True, initial_alphabet=ByteLevel.alphabet())
tokenizer.train(files=["botchan.txt"], trainer=trainer)
print("Trained vocab size: {}".format(tokenizer.get_vocab_size()))
tokenizer.model.save(".")

tokenizer.model = BPE('vocab.json', 'merges.txt')
encoding = tokenizer.encode("This is a simple input to be tokenized")
print("Encoded string: {}".format(encoding.tokens))

decoded = tokenizer.decode(encoding.ids)
print("Decoded string: {}".format(decoded))

Trained vocab size: 9579
Encoded string: ['Ġthis', 'Ġis', 'Ġa', 'Ġsimple', 'Ġin', 'p', 'ut', 'Ġto', 'Ġbe', 'Ġto', 'ken', 'ized']
Decoded string:  this is a simple input to be tokenized
import sys; print('Python %s on %s' % (sys.version, sys.platform))

WordPiece

在这里插入图片描述

wordpieceBPE算法本质一样,都是基于subword的优化算法,主要区别在于如何选择子词进行合并,下面的unigram language model也是这样。

from tokenizers import Tokenizer
from tokenizers.models import WordPiece
from tokenizers import normalizers
from tokenizers.normalizers import Lowercase, NFD, StripAccents
from tokenizers.pre_tokenizers import Whitespace
from tokenizers.processors import TemplateProcessing
from tokenizers.trainers import WordPieceTrainer


bert_tokenizer = Tokenizer(WordPiece(unk_token="[UNK]"))
bert_tokenizer.normalizer = normalizers.Sequence([NFD(), Lowercase(), StripAccents()])
bert_tokenizer.pre_tokenizer = Whitespace()

bert_tokenizer.post_process = TemplateProcessing(
    single="[CLS] $A [SEP]",
    pair="[CLS] $A [SEP] $B:1 [SEP]:1",
    special_tokens=[
        ("[CLS]", 1),
        ("[SEP]", 2)
    ],
)

trainer = WordPieceTrainer(
    vocab_size=10000, special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"]
)
files = ["botchan.txt"]
bert_tokenizer.train(files, trainer)

encoding = bert_tokenizer.encode("This is a simple input to be tokenized")
print("Encoded string: {}".format(encoding.tokens))
decoded = bert_tokenizer.decode(encoding.ids)
print("Decoded string: {}".format(decoded))

Encoded string: ['this', 'is', 'a', 'simple', 'in', '##p', '##ut', 'to', 'be', 'to', '##ke', '##n', '##ized']
Decoded string: this is a simple in ##p ##ut to be to ##ke ##n ##ized

Unigram Language Model

在这里插入图片描述

同样可以借助Google的SentencePiece包来实现,只需要把model_type改为unigram即可。

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

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

相关文章

(教程)如何在BERT模型中添加自己的词汇(pytorch版)

来源&#xff1a;投稿 作者&#xff1a;皮皮雷 编辑&#xff1a;学姐 参考文章&#xff1a; NLP | How to add a domain-specific vocabulary (new tokens) to a subword tokenizer already trained like BERT WordPiece | by Pierre Guillou | Medium https://medium.com/pi…

ROS2机器人编程简述humble-第三章-BUMP AND GO IN C++ .3

简述本章项目&#xff0c;参考如下&#xff1a;ROS2机器人编程简述humble-第三章-PERCEPTION AND ACTUATION MODELS .1流程图绘制&#xff0c;参考如下&#xff1a;ROS2机器人编程简述humble-第三章-COMPUTATION GRAPH .2然后&#xff0c;在3.3和3.4分别用C和Python编程实现&am…

Bus Hound 工具抓取串口数据(PC端抓取USB转串口数据)

测试环境&#xff1a; PC端 USB转串口 链接终端板卡串口 目标&#xff1a;抓取通信过程中的通信数据 工具介绍&#xff1a;Bus Hound是是由美国perisoft公司研制的一款超级软件总线协议分析器&#xff0c;它是一种专用于PC机各种总线数据包监视和控制的开发工具软件&#xff0c…

通信原理简明教程 | 数字调制传输

文章目录1 二进制数字调制和解调1.1 二进制数字调制的基本原理1.2 二进制数字调制信号的特性1.3 解调方法2 二进制差分相移键控2.1 2PSK的倒π现象2.2 2DPSK调制和解调3 二进制调制系统的抗噪性能3.1 2ASK系统的抗噪声性能3.2 2FSK系统的抗噪声性能4 二进制数字调制系统性能比较…

服务器配置定时脚本 crontab + Python;centos6或centos 7或centos8 实现定时执行 python 脚本

一、crontab的安装 默认情况下,CentOS 7中已经安装有crontab,如果没有安装,可以通过yum进行安装。 yum install crontabs 二、crontab的定时语法说明 corntab中,一行代码就是一个定时任务,其语法结构可以通过这个图来理解。 字符含义如下: * 代表取值范围内的数字 /…

Linux内核驱动初探(三) 以太网卡

目录 0. 前言 1. menuconfig 2. 设备树 0. 前言 这次的网卡驱动就比较顺利&#xff0c;基本就是参考 4.19.x 内核以及 imx6qdl-sabrelite.dtsi、imx6qdl-sabreauto.dtsi 中的设备树&#xff0c;来设置以太网各项参数。 1. menuconfig 其实笔者接手的时候&#xff0c;网口这…

本质安全设备标准(IEC60079-11)的理解(三)

本质安全设备标准&#xff08;IEC60079-11&#xff09;的理解&#xff08;三&#xff09; 对于标准中“fault”的理解 第一&#xff0c;标准中对fault的定义是这样的&#xff1a; 3.7.2 fault any defect of any component, separation, insulation or connection between c…

C++空间命名

前言 提示&#xff1a;由于C是在C语言基础之上&#xff0c;增加了很多新的东西。 本文讲解命名空间的具体使用方法 文章目录 目录 前言 一、命名空间 二、命名空间定义 1.嵌套性 2.和并性 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一…

【华为上机真题】区间交集

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

CleanMyMac X真的有必要买吗?CleanMyMac2023最新版下载

CleanMyMac X是一款集所有功能于一身的先进程序卸载清理器&#xff0c;只需两个简单步骤就可以把系统里那些乱七八糟的无用文件统统清理掉&#xff0c;节省宝贵的磁盘空间。CleanMyMac为您喜爱的东西腾出空间。它不仅有着赏心悦目的UI交互页面&#xff0c;更有着强大的“超能力…

HTB-BountyHunter

HTB-BountyHunter信息收集开机提权信息收集 80端口的网页如下。 注意有一个db.php&#xff0c;虽然现在打不开&#xff0c;估计后面会用上。 还有resources里面的readme文件。 完成了tracker提交编写和developer组权限。没有完成portal的test用户禁用、选择哈希加密的密码以…

Webshell(网页后门)

数据来源 本文仅用于信息安全的学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 一、Webshell简介 01 什么是 Webshell webshell是以 asp、php、jsp或者cgi等网页文…

【数据结构与算法】第十九篇:回溯,剪枝,N皇后问题

知识导航一、回溯思想概述二、八皇后问题引入八皇后问题的解决思路(1)思路一&#xff1a;暴力出奇迹(2&#xff09;思路二&#xff1a;根据题意减小暴力程度(3&#xff09;思路三&#xff1a;回溯法剪枝三、四皇后问题八皇后问题四、N皇后的实现1.实现方法一&#xff1a;利用数…

程序员的自我修养第七章——动态链接 (上)

继续更新《程序员的自我修养》这个系列&#xff0c;主要是夏天没把它看完&#xff0c;补上遗憾。本篇来自书中第七章。 再说动态链接前&#xff0c;我们先阐明为什么要动态链接&#xff1a; 动态链接的产生来自静态链接的局限性。随着静态链接的发展&#xff0c;其限制也越来越…

十二、创建和管理表

文章目录一、基础知识1.1 一条数据存储的过程1.2 标识符命名规则1.3 数据类型及数据库操作二、创建表三、查看表结构3.1 使用 SHOW COLUMNS 语句查看3.2 使用 DESCRIBE 语句查看3.3 查看表详细结构语句 SHOW CREATE TABLE四、修改表结构4.1 添加新字段和修改字段定义4.2 修改字…

用户画像增量更新系列二

进行用户日志数据处理 原始日志数据 结果: 思路&#xff1a;按照user_id的行为一条条处理&#xff0c;根据用户的行为类型判别。 由于sqlDF每条数据可能会返回多条结果&#xff0c;我们可以使用rdd.flatMap函数或者yield 格式&#xff1a;["user_id", "action…

总结:计算机中字符串比较大小的规则

总结&#xff1a;计算机中字符串比较大小的规则一背景&#xff1a;二Unicode编码表&#xff1a;字符越靠后&#xff0c;对应的十进制值越大三单个字符之间比较规则&#xff1a;四案例演示&#xff1a;单个字符与单个字符之间比较大小1.前提&#xff1a;汉字“一”与汉字“万”&…

Elasticsearch:Elasticsearch percolate 查询

Elasticsearch 通常如何工作&#xff1f; 我们将文档索引到 Elasticsearch 中并对其运行查询以获得满足提供的搜索条件的文档。 我们构造一个匹配或术语查询作为输入&#xff0c;匹配查询的文档作为结果返回。 但这不是 percolate query 的情况..... 让我们看看这篇文章中的 p…

10.Java方法学习知识点大全

文章目录前言一、什么是方法1.什么是方法?2.实际开发中,什么时候用到方法?3.实际开发中,方法有什么好处?二、最简单的方法定义和调用1.方法的格式2.方法的调用3.看代码说结果4.为什么要有带参数的方法呢?三、带参数的方法定义和调用1.带参数的方法定义和调用2.形参和实参3.…

NuSphere PhpED Pro 19.5 Crack

PhpED是PHP&#xff08;PHP IDE&#xff09;&#xff0c;HTML&#xff0c;CSS&#xff0c;XML&#xff0c;SMARTY&#xff0c;XHTML等的I ntegated Development Environment。 高级代码编辑器、可靠的 dbg 调试器、高效的数据库连接客户端以及快速安全的部署能力的平衡组合使 P…