Transformer数据预处理(Tokenizer)的问题

文章探讨了在使用Transformer处理中英文混杂字符串时遇到的序列长度错误,特别是‘expectedsequenceoflength4atdim1(got0)’的问题。解决方案包括在预处理阶段使用encode转换字符串并根据ID序列长度过滤样本,以及在处理句子对时使用truncation=longest_first策略。同时,文章还介绍了BertTokenizer的三种模式:FullTokenizer,BasicTokenizer和WordpieceTokenizer,以及它们在处理数据和生成attention_mask中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

加载数据报错‘expected sequence of length 4 at dim 1 (got 0)’

使用 transformer 将字符串转为 id 序列,字符串为中英文混杂形式,

运行中出现报错:'expected sequence of length 4 at dim 1 (got 0)'

发现是在encoder_plus转换时,将输入的文本根据max_length截断了,导致[MASK]等字段没剪除了,生成的 mask_pos (mask 在字符串中的位置)为空,这样在转为 Tensor 时产生了维度错误。

在预处理时,虽然通过设置的最大长度的对字符串做了筛选,但长度是通过空格split计算的,在encoder转换时,每个汉字对应一个id,英文(注:英文是单词或英文字符的拼接) 则根据vocab.txt中的词替换为 id,这样句长就会超过max_length,截断后会将最后的 [MASK] 删除。

解决方法:

        在预处理中用 encode 将字符串转为id序列,根据 id 序列长度及max_length过滤样本

BertTokenizer 中的 encoder、encode_plus 参数 truncation

        Sentence-Pair 分类任务中,遇到有关句子长度不一致的报错,可将 truncation设为'longest_first',这样就会依次删除较长句子的最后一个token,直到符合max_length的最大token数量为止。

        例如:设置句子最大长度是256,报错为最后一行显示不同的输入中,句子长度不一致问题。但在encode中已经设置了padding='max_length'和truncation=True,truncation=True 默认是only_first,仅对前面一句话做截断,不对后面一句做截断。当遇到sent1长度是50,sent2 长度是300的极端情况,使用“truncation=True”,总长度还是会超出256;truncation='longest_first' 会依次删除较长句子的最后一个token,直到符合 maxlength 的最大 token 数量为止(最新的tokenizer中True和longest_first已经等价了,暂未验证,Tokenizer (huggingface.co))

tokenizer时出现‘##’的词

Bert 有三个tokenizer:FullTokenizer,BasicTokenizer,WordpieceTokenizer;

FullTokenizer 会调用后面两个。
BasicTokenizer:清理特殊字符,去除控制符以及替换空白字符为空格,一些序列标注任务输入需要保持输入长度不变,否则标注无法对上,一般要么使用BasicTokenizer把原数据过一遍,再把标注对上,要么使用list方法代替 bert 的 tokenizer;中文分割:将中文每个汉字单独作为一个token
小写:可选,如果小写,还会对字符做做NFD标准化;标点符号分割:除中文汉字外,其它字符通过标点符号分割开
WordpieceTokenizer:对输入进行split,得到token列表,如果token的一部分在vocab中存在,就将token继续分割,分割后,除开第一部分,其它部分都会加上##,这就是wordpiece,例如visiable分割为:‘vi’, ‘##sia’, ‘##ble’,这个主要针对英文,但是在中文的全词mask,会利用这个特点,把一个词分割开,如“图书馆”分割为:图,#书,#馆
FullTokenizer:调用 BasicTokenizer 得到初步的token列表,对于token再用WordpieceTokenizer生成更细的token。

tokenizer 处理数据中的 attention_mask:多句子输入需要标准化,使其能够整齐划一的进行比较。如短句填充 padding 使其长度一致。由此产生的问题是训练时,模型不理解填充值,仍按照填充值计算,可能引入错误信息。为此,"attention_mask" 会告诉模型返回的数字编码中哪些是需要注意的实际数据,哪些是不需要关心的填充数据。

参考:

BERT句子对(sentence pair)分类任务的truncation='longest_first'之我见 - 知乎 (zhihu.com)

Bert中文vocab ##的作用_bert vocab_eryihahaha的博客-CSDN博客

Pytorch Transformer Tokenizer常见输入输出实战详解-CSDN博客

### 创建或使用自定义分词器 在构建或调整 Transformer 模型时,创建或使用自定义分词器是一个重要的环节。这不仅影响到模型性能还涉及到数据预处理的质量。 #### 自定义分词器的设计原则 为了使自定义分词器能够有效地工作于特定领域或是解决某些特定问题,在设计之初应当考虑如下几个方面: - **适应性**:确保分词器可以灵活应对不同类型的文本输入并能适配目标应用的需求。 - **效率**:尽可能提高分词的速度特别是当面对大规模语料库的时候[^3]。 对于 Python 实现而言,可以通过继承 `transformers.PreTrainedTokenizer` 类来扩展功能;而对于追求更高运行效能的情况,则可以选择基于 Rust 开发的快速版本。 #### 编写自定义分词逻辑 下面展示了一个简单的例子,说明如何利用 Hugging Face Transformers 库编写一个基础版的自定义分词器: ```python from transformers import PreTrainedTokenizerFast class CustomTokenizer(PreTrainedTokenizerFast): vocab_files_names = {"vocab_file": "custom_vocab.txt"} def __init__(self, vocab_file=None, unk_token="[UNK]", pad_token="[PAD]", cls_token="[CLS]", sep_token="[SEP]", mask_token="[MASK]", **kwargs): super().__init__( unk_token=unk_token, pad_token=pad_token, cls_token=cls_token, sep_token=sep_token, mask_token=mask_token, **kwargs ) with open(vocab_file, 'r', encoding='utf8') as f: vocab = [line.strip() for line in f.readlines()] self.vocab = dict([(token, idx) for idx, token in enumerate(vocab)]) self.reverse_vocab = {v:k for k,v in self.vocab.items()} def tokenize(self, text): tokens = [] current_word = "" for char in text.lower(): if char.isalnum(): # 如果是字母或数字则继续累积当前单词 current_word += char elif current_word != "": tokens.append(current_word) current_word = "" if not char.isalnum() and char != " ": # 对非字母数字字符单独作为token处理 tokens.append(char) if current_word != "": # 添加最后一个未完成的单词 tokens.append(current_word) return tokens def convert_tokens_to_ids(self, tokens): ids = [] for tok in tokens: try: id_ = self.vocab[tok] except KeyError: id_ = self.vocab[self.unk_token] ids.append(id_) return ids def save_vocabulary(self, path): with open(path,'w+',encoding="utf8") as file: for word in sorted(list(self.vocab.keys()), key=lambda w:self.vocab[w]): print(word,file=file) return (path,) ``` 此代码片段展示了如何通过继承 `PreTrainedTokenizerFast` 来创建一个新的类 `CustomTokenizer` ,该类实现了基本的功能如加载词汇表、将字符串分割成tokens以及将这些tokens转换为对应的ID编号等操作[^1]。 需要注意的是上述实现仅作为一个起点,实际应用场景下可能还需要进一步优化和完善,例如支持更多的正则化选项或者更复杂的匹配规则等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值