子词嵌入,词的相似性和类比任务

news2024/9/24 15:18:24
  • fastText模型提出了一种子词嵌入方法:基于word2vec中的跳元模型,它将中心词表示为其子词向量之和。

  • 字节对编码执行训练数据集的统计分析,以发现词内的公共符号。作为一种贪心方法,字节对编码迭代地合并最频繁的连续符号对。

  • 子词嵌入可以提高稀有词和词典外词的表示质量。

  • 在实践中,在大型语料库上预先训练的词向量可以应用于下游的自然语言处理任务。

  • 预训练的词向量可以应用于词的相似性和类比任务。

目录

1.子词嵌入

1.1fastText模型

1.2 字节对编码(Byte Pair Encoding)

2.词的相似性和类比任务

2.1 加载预训练词向量

2.2应用预训练词向量

2.2.1词相似度

2.2.2词类比


1.子词嵌入

在英语中,“helps”“helped”和“helping”等单词都是同一个词“help”的变形形式。“dog”和“dogs”之间的关系与“cat”和“cats”之间的关系相同,“boy”和“boyfriend”之间的关系与“girl”和“girlfriend”之间的关系相同。在法语和西班牙语等其他语言中,许多动词有40多种变形形式,而在芬兰语中,名词最多可能有15种变形。在语言学中,形态学研究单词形成和词汇关系。但是,word2vec和GloVe都没有对词的内部结构进行探讨。

1.1fastText模型

回想一下词在word2vec中是如何表示的。在跳元模型和连续词袋模型中,同一词的不同变形形式直接由不同的向量表示,不需要共享参数。为了使用形态信息,fastText模型提出了一种子词嵌入方法,其中子词是一个字符n-gram (Bojanowski et al., 2017)。fastText可以被认为是子词级跳元模型,而非学习词级向量表示,其中每个中心词由其子词级向量之和表示

让我们来说明如何以单词“where”为例获得fastText中每个中心词的子词。首先,在词的开头和末尾添加特殊字符“<”“>”,以将前缀和后缀与其他子词区分开来。 然后,从词中提取字符n-gram。 例如,值n=3时,我们将获得长度为3的所有子词: “<wh”“whe”“her”“ere”“re>”和特殊子词“<where>”。

fastText的其余部分与跳元模型相同。与跳元模型相比,fastText的词量更大,模型参数也更多。此外,为了计算一个词的表示,它的所有子词向量都必须求和,这导致了更高的计算复杂度。然而,由于具有相似结构的词之间共享来自子词的参数,罕见词甚至词表外的词在fastText中可能获得更好的向量表示。

1.2 字节对编码(Byte Pair Encoding)

在fastText中,所有提取的子词都必须是指定的长度,例如3到6,因此词表大小不能预定义。为了在固定大小的词表中允许可变长度的子词,我们可以应用一种称为字节对编码(Byte Pair Encoding,BPE)的压缩算法来提取子词 (Sennrich et al., 2015)。

字节对编码执行训练数据集的统计分析,以发现单词内的公共符号,诸如任意长度的连续字符。从长度为1的符号开始,字节对编码迭代地合并最频繁的连续符号对以产生新的更长的符号。请注意,为提高效率,不考虑跨越单词边界的对。最后,我们可以使用像子词这样的符号来切分单词。字节对编码及其变体已经用于诸如GPT-2 (Radford et al., 2019)和RoBERTa (Liu et al., 2019)等自然语言处理预训练模型中的输入表示。在下面,我们将说明字节对编码是如何工作的。

首先,我们将符号词表初始化为所有英文小写字符、特殊的词尾符号'_'和特殊的未知符号'[UNK]'

pip install mxnet==1.7.0.post1
pip install d2l==0.17.0
import collections

symbols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
           'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
           '_', '[UNK]']

因为我们不考虑跨越词边界的符号对,所以我们只需要一个字典raw_token_freqs将词映射到数据集中的频率(出现次数)。注意,特殊符号'_'被附加到每个词的尾部,以便我们可以容易地从输出符号序列(例如,“a_all er_man”)恢复单词序列(例如,“a_all er_man”)。由于我们仅从单个字符和特殊符号的词开始合并处理,所以在每个词(词典token_freqs的键)内的每对连续字符之间插入空格。换句话说,空格是词中符号之间的分隔符

raw_token_freqs = {'fast_': 4, 'faster_': 3, 'tall_': 5, 'taller_': 4}
token_freqs = {}
for token, freq in raw_token_freqs.items():
    token_freqs[' '.join(list(token))] = raw_token_freqs[token]
token_freqs
{'f a s t _': 4, 'f a s t e r _': 3, 't a l l _': 5, 't a l l e r _': 4}

 

我们定义以下get_max_freq_pair函数,其返回词内最频繁的连续符号对,其中词来自输入词典token_freqs的键。

def get_max_freq_pair(token_freqs):
    pairs = collections.defaultdict(int)
    for token, freq in token_freqs.items():
        symbols = token.split()
        for i in range(len(symbols) - 1):
            # “pairs”的键是两个连续符号的元组
            pairs[symbols[i], symbols[i + 1]] += freq
    return max(pairs, key=pairs.get)  # 具有最大值的“pairs”键

作为基于连续符号频率的贪心方法,字节对编码将使用以下merge_symbols函数来合并最频繁的连续符号对以产生新符号。 

def merge_symbols(max_freq_pair, token_freqs, symbols):
    symbols.append(''.join(max_freq_pair))
    new_token_freqs = dict()
    for token, freq in token_freqs.items():
        new_token = token.replace(' '.join(max_freq_pair),
                                  ''.join(max_freq_pair))
        new_token_freqs[new_token] = token_freqs[token]
    return new_token_freqs

现在,我们对词典token_freqs的键迭代地执行字节对编码算法。在第一次迭代中,最频繁的连续符号对是't''a',因此字节对编码将它们合并以产生新符号'ta'。在第二次迭代中,字节对编码继续合并'ta''l'以产生另一个新符号'tal'

num_merges = 10
for i in range(num_merges):
    max_freq_pair = get_max_freq_pair(token_freqs)
    token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols)
    print(f'合并# {i+1}:',max_freq_pair)
合并# 1: ('t', 'a')
合并# 2: ('ta', 'l')
合并# 3: ('tal', 'l')
合并# 4: ('f', 'a')
合并# 5: ('fa', 's')
合并# 6: ('fas', 't')
合并# 7: ('e', 'r')
合并# 8: ('er', '_')
合并# 9: ('tall', '_')
合并# 10: ('fast', '_')

 

在字节对编码的10次迭代之后,我们可以看到列表symbols现在又包含10个从其他符号迭代合并而来的符号。

print(symbols)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]', 'ta', 'tal', 'tall', 'fa', 'fas', 'fast', 'er', 'er_', 'tall_', 'fast_']

 

对于在词典raw_token_freqs的键中指定的同一数据集,作为字节对编码算法的结果,数据集中的每个词现在被子词“fast_”“fast”“er_”“tall_”和“tall”分割。例如,单词“fast er_”和“tall er_”分别被分割为“fast er_”和“tall er_”。

print(list(token_freqs.keys()))
['fast_', 'fast er_', 'tall_', 'tall er_']

 

请注意,字节对编码的结果取决于正在使用的数据集。我们还可以使用从一个数据集学习的子词来切分另一个数据集的单词。作为一种贪心方法,下面的segment_BPE函数尝试将单词从输入参数symbols分成可能最长的子词。

def segment_BPE(tokens, symbols):
    outputs = []
    for token in tokens:
        start, end = 0, len(token)
        cur_output = []
        # 具有符号中可能最长子字的词元段
        while start < len(token) and start < end:
            if token[start: end] in symbols:
                cur_output.append(token[start: end])
                start = end
                end = len(token)
            else:
                end -= 1
        if start < len(token):
            cur_output.append('[UNK]')
        outputs.append(' '.join(cur_output))
    return outputs

我们使用列表symbols中的子词(从前面提到的数据集学习)来表示另一个数据集的tokens

tokens = ['tallest_', 'fatter_']
print(segment_BPE(tokens, symbols))
['tall e s t _', 'fa t t er_']

2.词的相似性和类比任务

在 预训练word2vec,代码_流萤数点的博客-CSDN博客中,我们在一个小的数据集上训练了一个word2vec模型,并使用它为一个输入词寻找语义相似的词。实际上,在大型语料库上预先训练的词向量可以应用于下游的自然语言处理任务。为了直观地演示大型语料库中预训练词向量的语义,让我们将预训练词向量应用到词的相似性和类比任务中。

import os
from mxnet import np, npx
from d2l import mxnet as d2l

npx.set_np()

2.1 加载预训练词向量

以下列出维度为50、100和300的预训练GloVe嵌入,可从GloVe网站下载。预训练的fastText嵌入有多种语言。这里我们使用可以从fastText网站下载300维度的英文版本(“wiki.en”)。

#@save
d2l.DATA_HUB['glove.6b.50d'] = (d2l.DATA_URL + 'glove.6B.50d.zip',
                                '0b8703943ccdb6eb788e6f091b8946e82231bc4d')

#@save
d2l.DATA_HUB['glove.6b.100d'] = (d2l.DATA_URL + 'glove.6B.100d.zip',
                                 'cd43bfb07e44e6f27cbcc7bc9ae3d80284fdaf5a')

#@save
d2l.DATA_HUB['glove.42b.300d'] = (d2l.DATA_URL + 'glove.42B.300d.zip',
                                  'b5116e234e9eb9076672cfeabf5469f3eec904fa')

#@save
d2l.DATA_HUB['wiki.en'] = (d2l.DATA_URL + 'wiki.en.zip',
                           'c1816da3821ae9f43899be655002f6c723e91b88')

为了加载这些预训练的GloVe和fastText嵌入,我们定义了以下TokenEmbedding类。

#@save
class TokenEmbedding:
    """GloVe嵌入"""
    def __init__(self, embedding_name):
        self.idx_to_token, self.idx_to_vec = self._load_embedding(
            embedding_name)
        self.unknown_idx = 0
        self.token_to_idx = {token: idx for idx, token in
                             enumerate(self.idx_to_token)}

    def _load_embedding(self, embedding_name):
        idx_to_token, idx_to_vec = ['<unk>'], []
        data_dir = d2l.download_extract(embedding_name)
        # GloVe网站:https://nlp.stanford.edu/projects/glove/
        # fastText网站:https://fasttext.cc/
        with open(os.path.join(data_dir, 'vec.txt'), 'r') as f:
            for line in f:
                elems = line.rstrip().split(' ')
                token, elems = elems[0], [float(elem) for elem in elems[1:]]
                # 跳过标题信息,例如fastText中的首行
                if len(elems) > 1:
                    idx_to_token.append(token)
                    idx_to_vec.append(elems)
        idx_to_vec = [[0] * len(idx_to_vec[0])] + idx_to_vec
        return idx_to_token, np.array(idx_to_vec)

    def __getitem__(self, tokens):
        indices = [self.token_to_idx.get(token, self.unknown_idx)
                   for token in tokens]
        vecs = self.idx_to_vec[np.array(indices)]
        return vecs

    def __len__(self):
        return len(self.idx_to_token)

下面我们加载50维GloVe嵌入(在维基百科的子集上预训练)。创建TokenEmbedding实例时,如果尚未下载指定的嵌入文件,则必须下载该文件。

glove_6b50d = TokenEmbedding('glove.6b.50d')

输出词表大小。词表包含400000个词(词元)和一个特殊的未知词元。

len(glove_6b50d)

我们可以得到词表中一个单词的索引,反之亦然。

glove_6b50d.token_to_idx['beautiful'], glove_6b50d.idx_to_token[3367]

2.2应用预训练词向量

使用加载的GloVe向量,我们将通过下面的词相似性和类比任务中来展示词向量的语义。

2.2.1词相似度

为了根据词向量之间的余弦相似性为输入词查找语义相似的词,我们实现了以下knn(k近邻)函数。

def knn(W, x, k):
    # 增加1e-9以获得数值稳定性
    cos = np.dot(W, x.reshape(-1,)) / (
        np.sqrt(np.sum(W * W, axis=1) + 1e-9) * np.sqrt((x * x).sum()))
    topk = npx.topk(cos, k=k, ret_typ='indices')
    return topk, [cos[int(i)] for i in topk]

然后,我们使用TokenEmbedding的实例embed中预训练好的词向量来搜索相似的词。

def get_similar_tokens(query_token, k, embed):
    topk, cos = knn(embed.idx_to_vec, embed[[query_token]], k + 1)
    for i, c in zip(topk[1:], cos[1:]):  # 排除输入词
        print(f'{embed.idx_to_token[int(i)]}:cosine相似度={float(c):.3f}')

glove_6b50d中预训练词向量的词表包含400000个词和一个特殊的未知词元。排除输入词和未知词元后,我们在词表中找到与“chip”一词语义最相似的三个词。

get_similar_tokens('chip', 3, glove_6b50d)

下面输出与“baby”和“beautiful”相似的词。

get_similar_tokens('baby', 3, glove_6b50d)

get_similar_tokens('beautiful', 3, glove_6b50d)

2.2.2词类比

除了找到相似的词,我们还可以将词向量应用到词类比任务中。 例如,“man” : “woman” :: “son” : “daughter”是一个词的类比。 “man”是对“woman”的类比,“son”是对“daughter”的类比。 具体来说,词类比任务可以定义为: 对于单词类比a:b::c:d,给出前三个词a、b和c,找到d。 用vec(w)表示词w的向量, 为了完成这个类比,我们将找到一个词, 其向量与vec(c)+vec(b)−vec(a)的结果最相似。

def get_analogy(token_a, token_b, token_c, embed):
    vecs = embed[[token_a, token_b, token_c]]
    x = vecs[1] - vecs[0] + vecs[2]
    topk, cos = knn(embed.idx_to_vec, x, 1)
    return embed.idx_to_token[int(topk[0])]  # 删除未知词

让我们使用加载的词向量来验证“male-female”类比。

get_analogy('man', 'woman', 'son', glove_6b50d)

下面完成一个“首都-国家”的类比: “beijing” : “china” :: “tokyo” : “japan”。 这说明了预训练词向量中的语义。

get_analogy('beijing', 'china', 'tokyo', glove_6b50d)

另外,对于“bad” : “worst” :: “big” : “biggest”等“形容词-形容词最高级”的比喻,预训练词向量可以捕捉到句法信息。

get_analogy('bad', 'worst', 'big', glove_6b50d)

为了演示在预训练词向量中捕捉到的过去式概念,我们可以使用“现在式-过去式”的类比来测试句法:“do” : “did” :: “go” : “went”。

get_analogy('do', 'did', 'go', glove_6b50d)

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

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

相关文章

文献阅读:Finetuned Language Models Are Zero-Shot Learners

文献阅读&#xff1a;Finetuned Language Models Are Zero-Shot Learners 1. 文章简介2. 方法介绍3. 实验 1. 数据集整理2. 基础实验3. 消解实验 1. finetune任务数量2. 模型size3. Instruct Tuning4. Few-Shot5. Prompt Tuning 4. 结论 文献链接&#xff1a;https://arxiv.o…

简单理解小目标分割中的weighted BCE Loss与weighted IoU Loss

这两个损失函数出自《FNet: Fusion, Feedback and Focus for Salient Object Detection》一文中&#xff0c;用于处理显著性检测(二分割)中小目标的问题。对于传统的BCE Loss&#xff0c;其存在以下三个问题&#xff1a; 只是简单的将每个像素求BCE再平均&#xff0c;忽视了目…

day5——冒泡排序,选择排序和插入排序的学习

选择排序冒泡排序插入排序 选择排序 选择排序的基本思路就是&#xff1a; 首先假定第一个的下表为所有元素中最小的一个&#xff0c; 然后用后面的每一个元素跟这个元素进行比较&#xff0c; 如果后面的元素比这个元素更小一点&#xff0c; 那么就将找到的最小的元素的下标和…

【c++】vector实现(源码剖析+手画图解)

vector是我接触的第一个容器&#xff0c;好好对待&#xff0c;好好珍惜&#xff01; 目录 文章目录 前言 二、vector如何实现 二、vector的迭代器&#xff08;原生指针&#xff09; 三、vector的数据结构 图解&#xff1a; 四、vector的构造及内存管理 1.push_back() …

《爆肝整理》保姆级系列教程python接口自动化(十二)--https请求(SSL)(详解)

简介 本来最新的requests库V2.13.0是支持https请求的&#xff0c;但是一般写脚本时候&#xff0c;我们会用抓包工具fiddler&#xff0c;这时候会 报&#xff1a;requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590) 小编…

C++:提高篇: 栈-寄存器和函数状态:windows X86-64寄存器介绍

寄存器1、什么是寄存器2、寄存器分类3、windows X86寄存器命名规则4、寄存器相关术语5、寄存器分类5.1、RAX(accumulator register)5.2、RBX(Base register)5.3、RDX(Data register)5.4、RCX(counter register)5.5、RSI(Source index)5.6、RDI(Destination index)5.7、RSP(stac…

iptables和nftables的使用

文章目录前言iptable简介iptable命令使用iptables的四表五链nftables简介nftables命令的时候nftables与iptables的区别iptables-legacy和iptables-nft实例将指定protocol:ip:port的流量转发到本地指定端口前言 本文展示了&#xff0c;iptables和nftable命令的使用。 # 实验环…

win10 安装rabbitMQ详细步骤

win10 安装rabbitMQ详细步骤 win10 安装rabbitMQ详细步骤win10 安装rabbitMQ详细步骤一、下载安装程序二、安装配置erlang三、安装rabbitMQ四、验证初始可以通过用户名&#xff1a;guest 密码guest来登录。报错&#xff1a;安装RabbitMQ出现Plugin configuration unchanged.问题…

力扣SQL刷题10

目录标题618. 学生地理信息报告--完全不会的新题型1097. 游戏玩法分析 V - 重难点1127. 用户购买平台--难且不会618. 学生地理信息报告–完全不会的新题型 max()函数的功效&#xff1a;&#xff08;‘jack’, null, null&#xff09;中得出‘jack’&#xff0c;&#xff08;nul…

基于微信小程序图书馆座位预约管理系统

开发工具&#xff1a;IDEA、微信小程序服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8项目构建&#xff1a;maven数据库&#xff1a;mysql5.7前端技术&#xff1a;vue、uniapp服务端技术&#xff1a;springbootmybatis本系统分微信小程序和管理后台两部分&#xff0c;项目采用…

索引的基本介绍

索引概述-优缺点 索引介绍&#xff1a;索引是一种高效获取数据的数据结构&#xff1b; 索引优点&#xff1a;提供查询效率&#xff1b;降低IO成本&#xff1b;怎么减低IO成本呢&#xff1f;因为数据库的数据是存放在磁盘的&#xff0c;你要操作数据就会涉及到磁盘IO&#xff0…

Windows11 安装Apache24全过程

Windows11 安装Apache24全过程 一、准备工作 1、apache-httpd-2.4.55-win64-VS17.zip - 蓝奏云 2、Visual Studio Code-x64-1.45.1.exe - 蓝奏云 二、实际操作 1、将下载好的zip文件解压放到指定好的文件夹。我的是D:\App\PHP下 个人习惯把版本号带上。方便检测错误。 2…

数组常使用的方法

1. join (原数组不受影响)该方法可以将数组里的元素,通过指定的分隔符,以字符串的形式连接起来。返回值:返回一个新的字符串const arr[1,3,4,2,5]console.log(arr.join(-)&#xff1b;//1-3-4-2-52. push该方法可以在数组的最后面,添加一个或者多个元素结构: arr.push(值)返回值…

(考研湖科大教书匠计算机网络)第四章网络层-第一、二节:网络层概述及其提供的服务

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;网络层概述&#xff08;1&#xff09;概述&#xff08;2&#xff09;学习内容二&#xff1a;网络层提供的两种服务&#xff08;1&#xff09;面向连…

nginx越界读取缓存漏洞(CVE-2017-7529)

range格式: Range: <unit><range-start>- Range: <unit><range-start>-<range-end> Range: <unit><range-start>-<range-end>, <range-start>-<range-end> range事例&#xff1a; Range: bytes500-999 //表示第…

Spring Security简介

前面我们已经完成了传智健康后台管理系统的部分功能&#xff0c;例如检查项管理、检查组管理、套餐管理、预 约设置等。接下来我们需要思考2个问题&#xff1a; 问题1&#xff1a;在生产环境下我们如果不登录后台系统就可以完成这些功能操作吗&#xff1f; 答案显然是否定的&am…

微前端-模块联邦

一、 Module Federation 模块联邦概述 Module Federation 即为模块联邦&#xff0c;是 Webpack 5 中新增的一项功能&#xff0c;可以实现跨应用共享模块。 二、快速上手 需求 通过模块联邦在容器应用中加载微应用。 应用结构 products ├── package-lock.json ├──…

程序的机器级表示part3——算术和逻辑操作

目录 1.加载有效地址 2. 整数运算指令 2.1 INC 和 DEC 2.2 NEG 2.3 ADD、SUB 和 IMUL 3. 布尔指令 3.1 AND 3.2 OR 3.3 XOR 3.4 NOT 4. 移位操作 4.1 算术左移和逻辑左移 4.2 算术右移和逻辑右移 5. 特殊的算术操作 1.加载有效地址 指令效果描述leaq S, DD…

【项目实战】32G的电脑启动IDEA一个后端服务要2min!谁忍的了?

一、背景 本人电脑性能一般&#xff0c;但是拥有着一台高性能的VDI&#xff08;虚拟桌面基础架构&#xff09;&#xff0c;以下是具体的配置 二、问题描述 但是&#xff0c;即便是拥有这么高的性能&#xff0c;每次运行基于Dubbo微服务架构下的微服务都贼久&#xff0c;以下…

使用太极taichi写一个只有一个三角形的有限元

公式来源 https://blog.csdn.net/weixin_43940314/article/details/128935230 GAME103 https://games-cn.org/games103-slides/ 初始化我们的三角形 全局的坐标范围为0-1 我们的三角形如图所示 ti.kernel def init():X[0] [0.5, 0.5]X[1] [0.5, 0.6]X[2] [0.6, 0.5]x[0…