3-3 Transformers Tokenizer API 的使用

3-3 Transformers Tokenizer API 的使用

10 个月前 · 来自专栏 transformers 教程

Transformers Tokenizer 的使用

Tokenizer 分词器,在NLP任务中起到很重要的任务,其主要的任务是将文本输入转化为模型可以接受的输入,因为模型只能输入数字,所以 tokenizer 会将文本输入转化为数值型的输入,下面将具体讲解 tokenization pipeline.

Tokenizer 类别

例如我们的输入为:

Let's do tokenization!

不同的tokenization 策略可以有不同的结果,常用的策略包含如下: - word base:按照词进行分词 - character base:按照单词进行分词 - subword tokenization:按照subword进行分词 等

Word-based

我们如果需要根据 word base 进行 tokenization 的话,有两种方式,一种是根据 whitespace 进行分割,一种是根据标点符号进行分割,然后再做数字的映射。



每个 word 都被赋予一个ID,这个 ID 的范围是从0到 vocabulary size,这种方式有一种问题,就是很容易出现例如,dogdogs,虽然是相近的词,但是被分配了完全不同的无关的id。对于不在vocabulary 库里面的词,我们会分配 [UNK],代表未知词。

Character base

Char base的 tokenization 方式,就是用char,而不是word。 这种方式的好处在于:

  • vocabulary size 很小
  • 比较少机会出现 out of vocabulary 的问题。



但是这种方式的硬伤也很重,因为这种方式会导致文本无意义,每个character 都是无意义的,只有组成了单词才是有意义的,对于character 进行embedding无法满足文本的语义要求。当然这种问题主要出现在英文中或者拉丁语系中,中文的话,每个字的意思就丰富多了,所以也常用这种方式。

此外,对于英文,一个单词可以分为N个字母,这就导致了每个模型都需要处理多个token,导致模型速度慢,可以输入的文本长度变小。

Subword tokenization

为了平衡上述两者的问题,聪明的科学家想出了使用 subword tokenization 的方式进行分词。subword tokenization 依赖的原则是:

常见词不应该分成subword,不常见的词应该分为更有意义的subword

例如:tokenization 代表不常见的词,可以被分为:tokenizationannoyingly 被分为 annoyingly,这对于英文来说是很有意义的,因为英文本来就是由于词根和词缀组成的。

下面可以看看 “Let’s do tokenization!“ 在经过subword tokenization 后的情况:


其他的方式

还有很多常见的方式例如:

  • Byte-level BPE, as used in GPT-2
  • WordPiece, as used in BERT
  • SentencePiece or Unigram, as used in several multilingual models

load 和 save

tokenizer 的加载和保存和 models 的方式一致,都是使用方法:from_pretrained, save_pretrained. 这个方法会加载和保存tokenizer使用的模型结构(例如sentence piece就有自己的模型结构),以及字典。

下面是一个使用的example:

from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-cased", use_fast=True) # 可以使用use fast加速

AutoModel 类似,也有 AutoTokenizer这种class,它可以根据传入的 checkpoint,找到适当的 tokenizer class,并且加载 checkpoint:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

在加载完模型之后,我们可以直接使用tokenzer对文本进行tokenizer pipeline:

tokenizer("Using a Transformer network is simple")
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}

并且可以进行保存:

tokenizer.save_pretrained("directory_on_my_computer")

Encoding

将文本转化为数字的过程成为 encoding,encoding 主要包含了两个步骤: - 1. tokenization: 对文本进行分词 - 2. convert_tokens_to_ids:将分词后的token 映射为数字

Tokenization

Tokenization 的过程是通过 tokenize 的方法实现的:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)
>>>
['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']

这边使用的是wordpiece 的分词器,它将 transformer 分为了 transform##er

convert_tokens_to_ids

分完词之后,需要将每个token映射为 id, 这边是使用 convert_tokens_to_ids 的方法进行:

ids = tokenizer.convert_tokens_to_ids(tokens)

print(ids)

>>>
[7993, 170, 11303, 1200, 2443, 1110, 3014]

Decoding

Decoding 的作用是将输出的 ids 转化为文本,这可以使用 tokenizerdecode 方法:

decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)

>>>
'Using a Transformer network is simple'

Handling multiple sequences

我们在使用 tokenizer 的时候,可能会出现一些问题: - 对于batch的输入,如果我们输入了多个长度的句子,我们将如何处理? - 只输入 tokens 对应id 是不是就足够了? - 如果文本长度过长,怎么处理?

模型只支持batch 的输入

我们可以看下面的例子:

import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids) # model 只支持 tensor
# This line will fail.
model(input_ids)

可以看到,爆了如下的错误:

IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)

这是因为,模型需要传入的输入是 [batch_size, sequence_length]tensor

于是我们需要,对上面代码进行修改:

import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize([sequence])  # 变成batch size = 1 
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor([ids])
print("Input IDs:", input_ids)

output = model(input_ids)
print("Logits:", output.logits)
>>>
Input IDs: [[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607, 2026,  2878,  2166,  1012]]
Logits: [[-2.7276,  2.8789]]

batch 内的 ids 需要 padding

我们都知道 model 只接受 tensor 作为输入,不能接受 list of lists,而tensor 必须保证每个vector 都有相同的长度: 所以下面的数组是不能转华为 tensor 的。

batched_ids = [
  [200, 200, 200],
  [200, 200]
]

为了解决这个问题,我们往往需要对输入进行 padding,使得每个输入的 vector 都具有相同的长度,如果我们有2个句子,其中一个句子只有3个字,一个句子2个字,我们为了导出为tensor,可以将其他的句子都进行padding,然后得到两个3个字的句子。

padding_id = 100

batched_ids = [
  [200, 200, 200],
  [200, 200, padding_id]
]

tokenizer 中,我们可以通过 tokenizer.pad_token_id 确认 padding id。

model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence1_ids = [[200, 200, 200]]
sequence2_ids = [[200, 200]]
batched_ids = [[200, 200, 200], [200, 200, tokenizer.pad_token_id]]

print(model(torch.tensor(sequence1_ids)).logits)
print(model(torch.tensor(sequence2_ids)).logits)
print(model(torch.tensor(batched_ids)).logits)
>>>
tensor([[ 1.5694, -1.3895]], grad_fn=<AddmmBackward>)
tensor([[ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
tensor([[ 1.5694, -1.3895],
        [ 1.3373, -1.2163]], grad_fn=<AddmmBackward>)

上面我们注意到,同样输入 [200, 200][200, 200, tokenizer.pad_token_id], 我们在经过模型的时候,得到的结果是完全不同的。 这是因为我们的模型在做句子表征的时候,也将padding token id 进行了考虑,导致每个词对应的输出不同,为了告诉模型我们的输入中,某些词是不需要考虑的,我们需要传入 attention mask

Attention masks

Attention masks 和输入的 input ids 具有完全一样的shape,其中1 代表了这个id需要attention,0代表这个id不需要attention。

batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id]
]

attention_mask = [
  [1, 1, 1],
  [1, 1, 0]
]

outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
print(outputs.logits)
>>>
tensor([[ 1.5694, -1.3895],
        [ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)

Longer sequences 长句子

每个预训练的语言模型在模型定义的时候,都限定了最长的输入长度,例如 BERT-base 为512, BERT-large 为1024 个tokens。 - 所以如果文本 tokenized 后的长度超过模型可以处理的长度,我们需要进行截断(truncate)。 - 或者选用可以处理更长文本的模型,例如 Longformer,LED,或者 Big Bird

我们可以指定,max_sequence_length 参数对文本进行截断:

sequence = sequence[:max_sequence_length]

Tokenizer 的封装

我们了解了 tokenize,conver to ids, padding, attention mask,以及truncate 后,我们发现,对于文本的输入,我们需要进行一些列的 pipeline 才能得到模型的输入。这时候我们是否可以有封装的方法可以直接使用,而不用一次又一次地调用完整的步骤?

其实可以的,我们可以直接使用 tokenizer(text(s)) 就可以直接获得所有的模型的输入。而且它支持输入单句子或者句子list。

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)

或者直接输入句子串:

sequences = [
  "I've been waiting for a HuggingFace course my whole life.",
  "So have I!"
]

model_inputs = tokenizer(sequences)

对于文本长度的限定,我们可以通过指定 padding,以及 max_length。 - padding = 'longest': padding 到batch 中句子最长的长度 - padding = 'max_length': padding 到模型最大的输入长度,如果指定了 max_length, 则padding 到 max_length

# Will pad the sequences up to the maximum sequence length
model_inputs = tokenizer(sequences, padding="longest")

# Will pad the sequences up to the model max length
# (512 for BERT or DistilBERT)
model_inputs = tokenizer(sequences, padding="max_length")

# Will pad the sequences up to the specified max length
model_inputs = tokenizer(sequences, padding="max_length", max_length=8)

同样,如果文本太长,可以设定 truncation=True

sequences = [
  "I've been waiting for a HuggingFace course my whole life.",
  "So have I!"
]

# Will truncate the sequences that are longer than the model max length
# (512 for BERT or DistilBERT)
model_inputs = tokenizer(sequences, truncation=True)

# Will truncate the sequences that are longer than the specified max length
model_inputs = tokenizer(sequences, max_length=8, truncation=True)

tokenizer 默认返回的结果是np,但是模型的输入必须是tensor: - "np" returns NumPy arrays: - "pt" returns PyTorch tensors - "tf" returns TensorFlow tensors

所以需要传入参数 return_tensors

sequences = [
  "I've been waiting for a HuggingFace course my whole life.",
  "So have I!"
]

# Returns PyTorch tensors
model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")

# Returns TensorFlow tensors
model_inputs = tokenizer(sequences, padding=True, return_tensors="tf")

# Returns NumPy arrays
model_inputs = tokenizer(sequences, padding=True, return_tensors="np")

特殊的字符

如果我们查看 tokenizer 的返回结果:model_inputs["input_ids"] 我们可以查看到,文本的输出和之前的输出不太一样。

sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
print(model_inputs["input_ids"])

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
>>>
[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]

我们对输出ids 进行decode ,可以得到如下的结果:

print(tokenizer.decode(model_inputs["input_ids"]))
print(tokenizer.decode(ids))
>>>
"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
"i've been waiting for a huggingface course my whole life."

我们可以看到模型的输入增加了特殊的字符 [CLS][SEP], 这是因为,bert 模型训练的时候,也增加了这些特殊字符,为了保证训练和预测一致,其tokenizer也对输入进行了改造。不同的model在训练的时候,新增特殊的字符也不一样,例如 roberta 加的 字符为 和 。所以我们尽量可以直接使用 tokenizer 对模型进行embedding 的构造,可以减少很多数据的处理流程。

除此之外,模型还需要很多其他输入,例如BERT 需要输入 token type ids,代表文本的类型,具体每个模型都有点差异。

Wrapping up: From tokenizer to model

所以我们可以发现,tokenizer 帮我们处理了所有,

  • 对文本进行特殊字符的添加
  • padding
  • truncation
  • encoding (tokenize,convert_tokens_to_ids)
  • 转化为tensor
  • 输出 model 需要的attention mask
  • (optional) 以及输出 token type ids
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
  "I've been waiting for a HuggingFace course my whole life.",
  "So have I!"
]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
output = model(**tokens)

编辑于 2022-04-25 15:24
有没有双向奔赴的小甜文?
黄小玖
(已完结)听说死对头沈言被车撞坏了脑子我第一时间赶到他家,决定强撸他的猫。「沈言,你是我的舔狗,你说了你的都是我的。」沈言拧眉,片刻后道,「嗯,是有这么回事。」「……」嚯,舔狗也认?1收到沈言被撞的消息的时候,我正在猫舍痛心疾首的「捶胸。」还我芝麻!闺蜜江蒋不知从哪儿得了消息,激动道,「甜甜,你的机会来了,沈言不仅被撞了,还失忆了,芝麻,它非你莫属。」好闺蜜,你懂我。我立刻夺门而出。说起来,我和沈言也算的上是青梅竹马了,我们家和他们家更是门对门。只不过,这竹马过于优秀,以至于我这个小青梅常年生活在他的阴影之下,日子过得水深火热。尤其是每次考试或者我犯错的时候,耳边总是伴随着一句「你看看人家沈言」的训斥声。偏偏,我每次被训斥的时候,沈言都在场。这谁能忍?嗐,我忍了。这一忍就是好几年。后来,沈言的爸妈生意越做越大,他们也搬走了。我的身边,再也没了参照物。我妈的训斥声,也越来越少。直到我们一家参加了表姐的婚礼,婚礼上,帅气的表姐夫深情告白,讲了他和表姐的爱情故事。还把他们的爱情信物,两只猫,虾条和十九抱到了台上。这可把我妈刺激的,回到家就在我耳边开始了唐僧念经模式。我亲爱的妈妈,果然只听她想听的。表姐和表姐夫明明就是大学时候就互相喜欢对方了。哪里是两只猫就能促成的姻缘。难不成,我养只猫,就能找到一个男人了。可是,我妈非是不听呀。没办法,细胳膊拧不过我妈的大粗腿。迫于无奈,我准备去家门口的宠物店随便买一只猫,让我妈养着。只一眼我就看到了芝麻。那一刻,我就知道,我的铲屎官生涯即将开启。要不说我和芝麻有眼缘呢,它见到我,立马就过来了。看样子,像是要抱。我的心都化了。「喜欢吗?」冷不丁,一道清冷低沉的嗓音忽然从我身后响起。我没在意,以为是老板,于是点点头。「不好意思,它是我的。」「……」我一转身,就撞入了一双深邃的眼眸里。嚯。熟人!看着那张英俊帅气的面庞,我再次感慨,老天爷真是偏心。「甜甜,好久不见。」我可听不出来沈言的语气里有「好久不见」的欣喜。反倒是那双眼里,透着几分莫名的笑意。此刻,我只想抱猫走人。许是看到了我们这边的情况,老板过来了,我刚想开口,老板就将猫柜里的芝麻抱了出来,下一秒,沈言接了过去。看着芝麻乖巧软萌的样子,我急了。「老板,这只猫我要了。」「不好意思呀,小姐,这只猫早就被沈先生预定了,今天正好是他来接猫的日子。」「……」沈言临走的时候,特地回头看了我一眼,说,「甜甜,你真的喜欢这只猫吗?」有机会?我用力的点点头,甚至感谢的话都要出口了。「那以后你可以来我家看看它。」就这?看着沈言抱着猫离开的背影,我后槽牙都快咬碎了。瞧,这就是我竹马。啊呸,死对头。……2「姑娘,到了。」滴滴司机的声音,将我的思绪拉了回来。我抬头,看着车窗外的小区,嗯,是我住不起的地儿。下车之后,我直奔沈言家,到了门口,我缓了一口气之后,就按了门铃。刚好钟点工阿姨拎着垃圾要走。我说我是沈言的朋友,阿姨直接就让我进去了。临走的时候,还交代了一句,沈言正在屋里休息。在休息?那太好了,方便我下手。关上门,我蹑手蹑脚的进了客厅,四处搜寻着芝麻的身影。终于,在阳台上看到了正在晒太阳的芝麻。我立马走过去,将芝麻抱到了怀里,蹭了它好几下。芝麻,我的好大儿,妈妈这就接你回家。结果,走到门口,我手刚碰到门把。「你是谁?」「……」我一回头,一道黑影立马压了过来,逆着光,我只看得到沈言头上那缠了一圈的白纱布。不认识了?嗯?果然脑子撞坏了。没等我开口,沈言逼近了一步,我立马被困在了他的胸膛和门板之间。「你怎么进来的,想干什么,为什么抱着我的猫?」一连三问,许是鼻翼间那股气息过于强烈,我脑子一热,直接脱口而出。「沈言,你是我的舔狗,你说过你的一切都是我的!」包括我怀里的猫。我抱紧芝麻。气氛静默了片刻。下一秒,沈言的那张俊脸在我面前渐渐放大,他似乎在细细的打量我。「……」紧张。「嗯,是有这么回事。」「……」我傻了!3嚯。我确定以及肯定,沈言的脑子真坏了。怔愣的片刻,怀里的芝麻一下子窜到了沈言的怀里。我????晴天霹雳。我再次伸手,芝麻立马将小脑袋埋进了沈言的怀里,连个眼皮都没朝我掀。啊。我酸了。然而,下一秒,一只大掌朝着我的脸颊伸了过来,动作温柔的捏了捏。「芝麻刚接回来没几天,有些认生,你别难过。」还没等我从沈言宠溺的语气,亲昵的动作中反应过来呢,芝麻竟然抬头,朝着我叫了几声。「……」嚯。我是被爱屋及乌了?这滋味,谁尝谁知道。偏偏,沈言还嘴角勾着,一副体贴的模样安慰我,「你看,芝麻也是喜欢你的。」呵。我谢谢你全家。看着沈言刺眼的笑,我暗暗发誓:男人我没有,猫,我必须拥有。「沈言,你这伤看着好严重,医生有没有说什么时候能把以前的事情想起来呀?」我得先打探清楚情况。「医生说了,我的失忆只是暂时性的,最迟三个月,只要我这段时间多和老朋友见见面,很快就能想起来了。」三个月的时间,那太够了。我还不信,三个月,我还不能征服一只猫。看着沈言怀里的芝麻,我势在必得。嘿嘿。至于老朋友,我不就是吗。见我也是一样的。我故作善解人意,「这样呀,那太好了!沈言,你放心,我会帮你的,这段时间,我会把你怎么舔我的事情,都告诉你的。这样,肯定有利于你的恢复。」呵呵呵。沈言啊,你也有今天!听着我的话,沈言的眸底露出了几分我看不懂的情绪,没等我细细琢磨呢。沈言笑了,笑的意味深长。「好呀,我也很想知道,我是怎么当你的舔狗的。」「……」那我可太能编了。就在我洋洋得意之际,浑然不知,我和沈言之间的距离,无形之间又拉近了许多。抬头的时候,我的额头从沈言的薄唇上擦过。……他的唇,这么软。我有点晕。沈言眼里极快的闪过一丝笑意,轻轻的说,「我有没有对你这样过,嗯?」「……」我慌了。这特么要怎么演?4眼瞅着我俩的鼻尖都靠鼻尖了,一阵兴奋的喵喵喵声响了起来。原本在沈言怀里的芝麻,开始伸出肉肉的小爪子扒拉我了。沈言脸上有些红,抱着芝麻往后退了退。新鲜的空气顿时涌了进来,我也清醒了。天哪。我差点趁人之危。不怪我,实在是失忆的沈言太好欺负了。就在这时,一阵急促的手机铃声打破了此刻有些尴尬的气氛。沈言转身,走到客厅,拿起茶几上的手机。我一口气还没吸完,就听到手机里传来的那句「沈言,听说你被撞了,哥几个马上就来看看你」。这哪行?要是沈言的朋友来了,那岂不是露馅了。不行。我立马冲到客厅,对着沈言摆摆手,示意他不行。沈言深邃的眸子看着我,不明所以的笑了笑,不过还是听我的,婉拒了。为了早点将芝麻搞到手,我决定放个大招。「沈言,你需要好好休息。至于兄弟,等你好了随时都能见,我答应了会帮你,就肯定不会食言。我想过了,在你恢复记忆之前,你就和我一起住。」我得让芝麻早点适应我住的环境。这样,更能方便我得手。沈言的眼里闪过一丝诧异和……欣喜?「你确定?」「当然了。」芝麻我势在必得。「好。」看着收拾的身影,我觉得好像哪里不对劲。沈言的速度很快,我还没想好哪里不对劲的时候,沈言已经拎着行李箱,抱着芝麻,站在我眼前了。「走吧,甜甜。」我???沈言不是失忆了吗,怎么知道我叫甜甜?「你……」「刚才看到的。」「……」顺着沈言的视线,我低头。这个角度,能看到的不仅仅只是那条刻着我名字的吊坠,还有……「沈言!」我怒。「嗯?」对上沈言玩味的眼神,我立马偃旗息鼓。「走吧。」我转身,顶着又红又烫的一张脸,咬牙切齿。呵。沈竹马,来日方长,走着瞧!……事发突然,我早上出门的时候,家里也没怎么收拾。沈言站在唯一能落脚的门口,眉头紧皱,「你……」没等沈言开口,我就把扫把塞到了他的手里,然后接过他怀里的芝麻,笑语晏晏,「身为舔狗的你最喜欢帮我打扫了,给,开始吧。」看着沈言难得怔愣的模样,我嘴都快咧到耳后根了。打开电视,我抱着芝麻坐在了沙发上,余光却时不时的看一眼沈言,他在扫地。嘻嘻。沈言在我家扫地。「沈言,厨房里,也需要收拾一下了。」「沈言,你看看桌子要不要擦?」「沈言,衣服洗好了,快去晾吧。」……我一集电视剧看完,沈言收拾的战场已经转移到阳台了。大片大片的阳光从窗户射进来,照在沈言背后的墙壁上,金黄一片,沐浴在那片犹如神迹般的阳光里的沈言,格外的耀眼闪目,就像阿波罗一样。只可惜,阿波罗手上不会拿着一件几乎透明的黑色 bra……那是闺蜜江蒋送我的礼物,说是有朝一日,我一定会狠狠的感谢她的。呵。我现在就想狠狠的「感谢」她。那本该被我毁尸灭迹的东西,究竟是怎么出现在我洗衣机里的。我现在把沈言赶出去,还来得及吗?突然,一阵风吹了进来,沈言手上的透明黑色 bra 飘了起来。5脑子嗡的一声,我回过神来以最快的速度,冲到了沈言面前,一把将他手里飘扬着的玩意儿拽了下来,然后藏到了身后。我故作镇定,「这里不需要你打扫了,你去做饭吧,我饿了。」沈言嘴角微勾,看着我的眼神都变了,深沉又浓厚。与我擦身而过的时候,说了一句,「我也饿了。」「……」我拿着黑色 bra 的手一抖,总觉得沈言的这句话透着几分的……暧昧。看着沈言进了厨房,我立马弯腰在洗衣机里摸索了一阵,还真找到了同款小内内。我两眼一抹黑,直呼万幸。要真让沈言看到了这两根小细绳,结果不是他死就是我亡。我连忙在柜子里找了一个袋子,将两玩意儿塞了进去。本来想扔到垃圾桶的,又怕被沈言看到,没办法,只能跑到房间,塞到了柜子里。找个恰当的时机再带出去扔了。我有预感,这玩意儿就是一个炸弹,随时会把我炸个粉身碎骨。等我藏好出去的时候,一阵阵的香味直往我鼻翼里钻。这可是外卖达不到的层次。被这香味勾的,我真饿了。走到餐桌旁,桌子上已经摆好了三菜一汤。沈言可真会。看着沈言围着围裙,正在拿着饭勺替我盛饭,那一瞬间,我心里竟然有些恍惚。就好像,我和沈言,已经在一起生活了很多年似的。这段饭,吃的我五味杂陈,尤其是看到沈言眉梢之间尽是深情的模样,替我夹菜,帮我剥虾的时候,我差点筷子都没拿住。是的,我怂了。我刚想开口,沈言突然起身,越过桌子,那白净修长的手指就这么轻轻的摩挲着我的嘴角。「怎么还和个孩子似的,嘴角都沾上米粒了,好了。」「……」沈言的声音落在我的耳里,酥痒的我不禁的打了一个冷颤。果然是舔狗。6好不容易吃完了,沈言很有舔狗的自觉性,立马把碗拿到厨房洗了,还特地给我切了水果。看着白瓷盘里切成块还插着签儿的水果,追的剧顿时不香了。整整一个下午,沈言将我的小窝收拾的干干净净、整整齐齐的。地板上连根头发丝儿都没有。嚯。沈言这体内舔狗的潜质,算是被我激发了。既然这样,那我就不客气了。我躺在沙发上,朝着对面的沈言伸出了脚,「我脚冷,帮我捂捂。」「这也是我以前经常做的?」沈言嘴角挂着莫名的笑意。我一本正经的扯着慌,「是。」话音刚落,沈言毫不犹豫的坐了过来。沈言撩开上衣,我看着那块块肌理分明的腹肌,下意识的咽了一口口水。然后,沈言握着我的双脚,直接就往腹肌上靠。滚烫的温度灼的我下意识的就想缩脚,却被沈言按住了。「甜甜,别乱动!」沈言的声音沙沙的。「……」我哪里敢动。就这么老老实实的任他按着脚。过了一会儿,我就老实不起来了。沈言的大掌,以攀延的趋势,打着转儿的在我的小腿上摩挲着。又痒又麻。我必须得动。要不然,那手……「沈言,我脚不冷了,你松开。」我稳着声音,生怕沈言听出些什么来。沈言很听话,真的松开了。舔狗的沈言,太勾人了,我自制力太弱,怕。我得抱着芝麻壮壮胆。结果,我刚摸到芝麻的毛,它就嗖的一下子窜出去了。等我转过身的时候,就看到芝麻已经在沈言的怀里拱着了。我???芝麻,你等着,我不信拿不下你。7房间里。我躺在床上,看着天花板,连手机都没心思玩了。今天的沈言,怪怪的,难不成失忆的人都是这样的?想着想着,一阵阵的困意来袭,我连自己什么时候睡着的都不知道。迷迷糊糊间,我好像听到有人在叫我「甜甜」。一声一声的。谁?谁把我的名字叫的这么缠绵缱绻,深情万年的。我想睁开眼睛看一眼,可是眼睛沉沉的,压根睁不开。算了,可能是做梦吧。不过,这梦,做的可真美!……清晨,我是被一阵扰人的手机震动声吵醒的。我闭着眼睛,像往常一样,在床上摸索着。不对呀。我的大床,是软软的。可手下摸到的东西却是硬的,不仅硬,捏一捏,还特别有弹性。「……」我混沌的大脑一下子就清醒了。一眼望去,我的手,正在一大片光裸着的肌肤上停留着呢。啊啊啊!我是谁,我在哪,我在干什么!一定是梦还没醒呢我再次闭上眼睛。这时,头顶上一句沙哑暗沉的「甜甜」响起。我懵了,随即而来的就是一阵滔天的怒火。沈言,竟然趁着我睡着了,进我的房间,还睡我的床。我掀被就起,指着沈言,义愤填膺。「沈言,我是想帮你恢复记忆才把你带回家的,没想到,你竟然趁人之危。你、你这个小人。」沈言皱了皱眉头,一脸的委屈。「甜甜,你看清楚,这是谁的房间。」呃……我下意识的看了一眼,客房?!「甜甜,昨天夜里,是你进了我的房间,一进来,什么话都没说,就上了床直往我怀里钻,我吓坏了,叫了你好几声,你都没反应。我想着,你是不是在梦游,就没敢再叫你。」「……」这种生扑的事情,我平时……顶多就在梦里做做。视线不经意的落在了沈言的胸膛上。我脸一热。再也顾不上和他对峙,跑出了房间。自然没有看到身后,沈言眼里那溢出来的笑意。回到房间,我倚靠在门板上,绞尽脑汁的想了好一会儿,也没想清楚究竟是怎么回事。我妈也没说过我有梦游这毛病呀。洗漱的时候,我看着镜子,眼前不由自主的浮现出了今早的画面。我被沈言搂在怀里,我的头枕在他的手臂上,我的手搭在了他的腰上……我竟然一点也不反感这样的亲密。反而,心里竟然隐隐的有些……窃喜?啊。色令智昏呀!8从洗手间刚出来,我就感觉到家里的气氛不对劲。出去一看。嚯。好家伙。闺蜜江蒋正站在门口,那张圆嘟嘟的脸就跟微信上第七排第三个表情包一样。而沈言正蹲在那给芝麻铲屎呢。「你……」眼瞅着闺蜜就要张口了,我立马冲过去,一把捂住了她的嘴。「这是我闺蜜,江蒋。」「这是我舔狗,沈言。」「……」气氛并没有因为我的介绍好到哪里去。闺蜜江蒋倒是安静了下来。只是,看着她那滴溜溜直往我和沈言之间转儿的八卦小眼神,我生怕她会说漏嘴,连忙将人往我房间里带。刚进屋,江蒋极其兴奋的看着我,「哇,甜甜,你这竹马可真帅啊,说说,你怎么把他变成舔狗的?是不是你趁人之危?」「……」好闺蜜,你清醒点呀。怎么能因为男人的一副皮囊就怀疑自己的姐妹呢。虽然,这副皮囊的确帅的有点过分了。「姐妹,还是你牛,知道肥水不流外人田,现在猫有了,男人也有了。」「……」男人?在哪?沈言吗?我不禁的打了一个冷颤。「你别瞎说,我和他没什么,我只对芝麻感兴趣。」我极力否认。江蒋笑了,笑的露骨,「是吗?赶紧说说,外面那位是怎么回事?你俩怎么就同居了,到哪一步了?拉手没?亲嘴没?我送给你的那套魅惑黑丝用了没?」「……」我深深的呼了一口气。呵。差点忘了「谢」她。……房间里的鬼哭狼嚎,直到敲门声响起,才停了下来。江蒋被我压在墙上,泪眼婆娑,那张肉嘟嘟的小圆脸,更是红了一片。嘿嘿。我揉的。我去开门的时候,特地恶狠狠的瞪了她一眼,做了一个抹脖子的动作。门打开,沈言正端着水果站在那。「甜甜,我给你切了水果。」沈言一脸宠溺的看着我。我感动哭了,假的。接过水果盘,沈言伸手,温柔的将我额前落下来的那一抹飘着的碎发别到了耳后。「有事情随时叫我,我就在客厅,嗯?」「……好。」房门合上,不知何时凑过来的闺蜜捏起了果盘里的葡萄,含糊不清的调侃,「苏甜甜,你要是和沈言没亲密过,我把脑袋拧下来给你当装饰。」「……」那还是算了。挺血腥的,受不起。见我不说话,闺蜜来劲了,一张口,我直接就把她送走了。她说。「我是过来人,肯定能给你战术性指导。」 9客厅里的沈言,看到我和闺蜜推推搡搡的,刚要过来,我立马一个高抬腿,直接就把她踹出去了。然后,顺手改了大门密码。回头,看到沈言一脸诧异。「那什么,她男朋友找她,急着回去。」沈言笑了,又是那种我看不懂的笑。想到闺蜜说的话,我再看沈言的时候,心里竟然莫名的有些拧巴。就形容不上来的感觉。我和沈言???不不不。我立马摇摇头,将脑子里那个危险的想法甩出去。先不说我现在在骗他,光是等他恢复记忆了,就肯定还是那副冰山样儿。就在这时,兜里的手机响了起来,恍恍惚惚的我直接就解锁了,闺蜜的声音立马响遍了整个屋子。「啊,甜甜,我忘了说了,我送你的那套黑色 bra,是特地按照你的尺寸定制的,你穿上的效果绝对好到爆。」「……」我连忙挂电话,可越急就挂的越慢。「到时候,直接就穿着这套去扑倒沈言,只要他是个直的,就一定会臣服在你的丰满之下的。」「……」闺蜜的声音戛然而止。我怔在原地,只感觉一阵血液直往脑子里涌。我现在开门出去追杀闺蜜应该还来得及。沈言没有给我这个机会,他拦下了我,眸光流转的看着我,「甜甜,原来我们的关系已经这么亲密了。」「不……」未说出口的话,被沈言的指腹盖住了。「甜甜,以后扑倒这种活,交给我就行。」「……」呵。江蒋,我要宰了你!……中午,还是沈言做的饭,我在书房里工作。在我家睡了一夜的芝麻,已经能主动的跑到我书房里晃悠一圈了,有时候还会缩在我脚边,四脚朝天的翻滚着。光是看着芝麻那双眯眯眼,我就快乐极了。邮件处理结束,我站起来伸了一个懒腰,活动了一下四肢,然后,就听到了客厅里的动静。不知何时,我家不算宽敞的客厅里,竟然站了好几个西装革履的男人。我???看着,倒像是在和沈言汇报工作,一个个手上还拿着文件。见我出来了,沈言朝着几人使了一个眼色。不到片刻,客厅恢复了安静。我懵了。沈言不是失忆了吗?「他们是?」「公司里的员工,过来送文件的。」「你还记得他们?」沈言理所当然的看着我,「昨晚和你睡了一夜之后,早上我就想起来了一些关于公司里的事情,甜甜,你的方法,还挺管用的,和你在一起,我肯定很快就能好的。」「……」这话,好像对又不对的。我还想继续追问,沈言却牵着我,大拇指的指腹不停的在我掌心揉捏着,「甜甜,有你真好。」我晕了。直觉有哪里不对,可沈言没给我机会,将打包好的甜品塞到我怀里。「工作一下午饿了吧,这原味芝士刚买的,尝尝。」「……」呜呜。有沈言真好。真好吃!10夜,悄悄的深了。等我回到房间的时候,沈言正坐在我的床上。「甜甜,床,我已经暖好了,不会冷脚了。」「……」我的脚一直都冷,小时候我爸就是个人形热水袋,都会先帮我把被子捂热了,再让我进去睡。沈言他……在我怔愣间,沈言已经下床了,看着那道朝着我走来的颀长身影,我的心竟然有些紧张。「甜甜,晚安!」沈言揉了揉我的脑袋,然后就出去了,还很贴心的帮我合上了房门。嚯。我心里已经想好了一万种沈言要睡我房间的应对政策。结果,就这?啥也不是。睡觉!躺在床上,我再一次的辗转难眠。其实,沈言好像也不是那么讨厌,难不成就为了小时候、因为沈言这个优秀参照物挨得骂就讨厌他?或许,闺蜜说的没错,我讨厌沈言,更多的是因为嫉妒?要是,我把这个优秀的参照物拿下的话,那我妈……嘿嘿,想着想着,我闭上了眼睛。这一夜,我好像又做梦了。梦里,我妈喜笑颜开的宣布,我是全家人的骄傲,为苏家引进了一个基因优良的女婿!……沈言住进来之后,日子好像过得特别快。我俩竟然在同一屋檐下,相处小半个月了。上次这种和谐的画面,还得追溯到小时候。不仅如此,这段时间,我家狭小的客厅里,每天都会站上几个西装革履、不同面孔的男人。无一例外,都是来找沈言的。我的书房,都被沈言征用了,俨然成了小型会议室。而我,这个家的主人,只能抱着芝麻窝在沙发上,看着茶几上的电脑。日薄西山。最后一个人从书房里走出来了,看样子,工作已经谈好了。门合上的时候,沈言出来了。走到我身边,声音里透着些愉悦,「甜甜,家里冰箱快空了,我们出去逛逛?」我眼睛一亮。那太可以了。……11超市里。沈言推着车,一脸宠溺的看着我不停的往车里塞着各种各样的零食。路过生鲜区的时候,沈言会俯身凑到我耳边,轻声的问我想吃什么。就好像,我和沈言,已经是相处了很多年的老夫老妻一样。工作完了,出来逛个超市,一起商量晚上要吃什么。我竟然有了温馨的感觉……从超市出来的时候,天已经黑了。沈言一手一个购物袋,还时不时的护着我,生怕来来往往的行人碰到我。我的手插入衣兜里,摸到了凉凉的包装袋。然后,趁着沈言将购物袋放入后背箱的时候,我跑了过去。「给!」我的掌心里,躺着一颗包装袋已经撕了一半的玉米软糖。这是我的最爱。沈言一愣,然后笑了。那张英俊的面庞,在路灯的映射下,像是在发光,那道光直往我心里照。下一秒,沈言微微低头,我能感觉到有一阵温热的湿濡在我的掌心晕染……我脑子一懵。沈言他有洁癖的。「很甜!」瞬间,我感觉全身的血液全部堆砌在我的脸上了。我不敢再抬头。心里更是有一股说不清道不明的情绪在滋生。……一到家,我和沈言都很有默契的没有提起刚刚的事情。他做饭,我撸猫。一副相安无事的样子。只有我自己知道,被沈言舔过的掌心像是在发热,一直在灼着我。吃完饭,我就进了房间,开始平复着自己心里那几头乱撞的小鹿。直到入睡前,我的脑海里都是沈言低头吃我掌心那颗糖的一幕……完了,妈妈,我可能不只是想要猫,还想要男人。……12清晨,刺眼的阳光照了进来,我迷迷糊糊的睁开眼,看到床边坐着一个人,正笑眯眯的看着我。我闭上眼翻了一个身,准备接着睡。然后,下一秒,我猛地睁开眼睛,立马清醒了,整个人都坐了起来。真的是我妈!我傻眼了。什么情况。「赶紧起来洗漱。」我妈拍了拍我的肩膀,就出去了。留下我,一个人在房间怀疑人生。我用最快的速度将自己收拾好,然后冲了出去。好家伙。客厅里的沙发上,正在热络的聊着的可不就是我妈和沈言他妈吗。「甜甜起来啦,来,到阿姨这来。」说起来,沈言妈妈小时候特别喜欢我,经常拉我去她家吃饭,说我听话懂事,希望能有一个像我这样的女儿。「沈阿姨好。」我木木的走过去,任由沈言妈妈拉着我的手。视线,却在屋内搜索着沈言的踪迹。沈言去哪了?「妈,阿姨,你们怎么一起来了?」我临危不乱。「我去找你妈准备一起去寺庙还愿的,正好你妈要来看你,我就跟着一起来了。」「那你们,有没有看到沈言呀?」这次,我妈开口了,「对呀,我已经好久没见过小言了。」「……」我心里一喜。嚯。没露馅。「他呀,最近一段时间神神秘秘的,上次回家的时候,还跟我说最近在追一个女孩子,很快就能把人带回家给我们看了。」沈言妈妈说这话的时候,眼里的笑意根本藏不住。我的心,顿时不喜了。「是吗,那可要恭喜你了,很快就要有儿媳妇了。哎,你说,咱俩一起去的西郊阳山月老祠,你那这么快就有动静了,我这。」我妈看了我一眼,恨铁不成钢的摇摇头。呜呜。我心里特委屈。月老是不是真的把我忘了?沈言妈妈看着我,笑了笑,不愧是母子,连笑容都是一样的令人捉摸不透。「不行,我得赶紧再去拜拜,这次,我得请尊月老像回来。」我妈,风风火火的,拉着沈言妈妈就走了。客厅里,我一脸茫然。脑子里尽是沈言妈妈的那番话。沈言已经有喜欢的女孩子了,正在追人家,而且,还好事将近?!结果,却因为一场意外,失忆了,还被我骗回了家,在我这打扫做饭还捂脚。13许是察觉到我情绪有些失落,芝麻竟然主动的跳到了我的怀里,小脑袋直往我怀里拱。「芝麻,你是不是感觉到我的身上有沈言的气息,把我当自己人了,所以才愿意和我亲近的。」「喵~」「那你以后跟我一起好不好,我肯定会好好的疼你爱你的,我绝对不会有第二只猫的。」「喵~喵~」呜呜。为什么好难过。我要抱着芝麻离家出走。刚换好鞋,门开了。门口的沈言,袖口的衬衫挽了上去,露出了一截白净的手腕,一眼都能看到那充满荷尔蒙气息又禁欲十足的青筋。我不看。不看即不想。「甜甜,怎么起这么早,你不是最喜欢睡懒觉的吗?」沈言怎么知道我喜欢睡懒觉?没等我开口,沈言低头看了我一眼,「要出去?」对了,这里是我家呀,我走个什么劲儿。我豪横起来了,抱着芝麻,指着沈言,「给你十分钟的时间,赶紧收拾你的行李,走人。」沈言蹙了蹙眉,「甜甜,你是下了床就不想负责了?嗯?」沈言逼近,将我困在了玄关。怀里的芝麻见势不妙,嗖的一下,竟然从我怀里跳了下去,溜了。「你、你别乱说。」我这是拨乱反正,让一切回归正轨,他喜欢的女孩子还在等着他呢。我垂下了眼眸,遮住眼里的失落。「乱说?脚凉了我帮你捂,床冷了我帮你暖,甜甜,你想赖账,嗯?」「我……」「哎呦,要死了,苏甜甜,你要是敢对小言始乱终弃,我把你耳朵拧下来给你爸下酒。」「……」我抬头望去,电梯门口,正站着我妈和他妈呢。完了!我脑子一下子就被灌入了一桶浆糊,晕晕乎乎的。想开口解释,可我妈压根就没给我这个机会。一边拉着沈言妈一边亲切的挽着沈言,进了屋,独留下我这个亲生的女儿在门口受着冷风吹。「小言呀,你放心,既然你和甜甜关系都这么亲密了,阿姨绝对不会委屈了你。」「妈!」我开口,想要打断我妈的一厢情愿。只可惜,我妈连看都没看我一眼。倒是沈言,看了我一眼,然后,缓慢且认真,「阿姨,你做主就行。」「……」我妈当即掏出手机给我爸打了电话。不到半小时,我那小小的客厅,已经聚集了两方家长。我和沈言站在四人的对面,听着他们在开心的商量婚事。我连插嘴都插不上。沈言妈,你是不是忘了什么?没办法,我只能拉了拉沈言的衣角。我准备向他摊牌。沈言俯身凑了过来,眉梢间都染上了笑意,声音低缠,「天宝,放心,有我在。」一句卧槽脱口而出。沙发上的四人齐齐朝我看了过来,尤其是我妈的眼神,那叫一个凌厉。我脖子一冷,立马低下了头。呜呜。没人懂我。小时候,我爸特别希望有一个女儿,终于等到了我,一激动,直接给我起了一名,叫苏天宝。寓意,上天赐给他的宝贝。嚯。好家伙。谁能懂,我那么一个可爱粉嫩的小姑娘,被人在后面追着叫「天宝,天宝」的痛。等我有了自主的意识之后,哭天喊地的逼着我爸去派出所改了名字。这才有了现在这么好听的「苏甜甜」。我是苏甜甜,不是傻白甜。14难怪觉得沈言不对劲了,原来这厮压根就没失忆。闺蜜江蒋的情报有误,这头猪,只知道睡觉吃饭缠林奕。我正在心里恶狠狠的宰着猪呢,突然,手被牵了起来。沈言对着我,单膝着地,从衣兜里掏出了一个精致的绒盒,打开。嚯。好闪!「甜甜,我爱你!」「要是你问我,这份喜欢是从什么时候开始的,或许是你第一次会走路、迈着腿跌跌撞撞的向我跑来的时候,或许是你拉着我的手、冲我笑,然后将你最爱吃的玉米软糖放在我掌心说很甜的时候。」「我一直在等你长大,即使在等你长大的过程中,有波澜有曲折有意外,却依旧不会减少我爱你的心。」「甜甜,嫁给我。」「到时候芝麻就是夫妻共同财产,不只是它,我,也是你的!」「……」呜呜。我的眼前一片水雾,沈言这厮,太会了。猫诱、色诱,齐活了。这谁能扛得住。嘻嘻。在我爸妈和他爸妈的见证下,沈言将象征着承诺和永恒的钻戒,套进了我的无名指。当天下午,我和沈言就被双方父母催着去领了证。看着结婚证上的日期,我恍然,今天是我生日,阴历的。我一激动,直接抱着沈言吧唧了一口。然后……沈言一路风驰电掣的赶回家,一进门,一顿热吻劈头盖脸的落了下来。玄关就是战场。喘息之间,我抵着他,「沈言,别在这……」「叫我什么。」「老公!」沈言笑了,笑的蛊惑。「甜甜,乖,那套黑色的,我想看你穿!」「……」沈言眼里的火,又深又沉。嘻嘻。好羞!15沈言番外:不知道从什么时候起,苏甜甜对我越来越排斥,还时不时的躲着我。明明小时候,最喜欢抓着我的衣角,一口一个言哥哥的叫着。还会在我失落的时候,听话乖巧的仰着粉嘟嘟的小脸蛋,笑眯眯的将她最爱吃的玉米软糖塞到我的手上。果然女大十八变,变得还有那颗心。呵。我怎么会允许!但凡出现在苏甜甜身边的男性,只要流露出苗头,都无一不被我掐灭在摇篮里。就这样,苏甜甜做了二十三年的单身狗。至于我,比她多了几年。不过没关系,往后余生,我会身体力行的补偿她做这么多年单身狗的损失的。毕业之后,公司逐渐稳定,我也开始着手准备把媳妇骗回家的计划。苏甜甜的表姐夫,是我的学长,在他的婚礼上,我拜托他讲了他和他妻子的爱情故事,尤其是那两只猫。丈母娘的心思我最懂。果不其然,苏甜甜被她妈逼着出去买狗了。她最懒了,家门口的宠物店是她的极限。我事先和宠物店的老板说好了,不管苏甜甜看中哪只猫,我都出双倍的价格。后来,苏甜甜看中了芝麻。看着她望着芝麻的眼神,我竟然有些吃醋。呵。我还不如一只猫。苏甜甜闺蜜的男朋友林奕,是我的舍友,我让他将我被撞失忆的消息透露给了他女朋友。苏甜甜来了。不愧是我看中的媳妇,就是这么大胆,一本正经的说我是她的舔狗。是呀。从小到大,我一直都是你苏甜甜的舔狗。就算是舔一辈子,我也甘之如饴!苏甜甜,我爱你,远比你能想到的更多!

文章被以下专栏收录

    transformers 教程

    transformers 教程

    NLP 库 🤗 transformers 教程

推荐阅读

想来知乎工作?请发送邮件到 jobs@zhihu.com