D2L学习记录-10-词嵌入word2vec

NLP-1-词嵌入(word2vec)

参考:

《动手学深度学习 Pytorch 第1版》第10章 自然语言处理 第1、2、3 和 4节 (词嵌入)

词嵌入 (word2vec):

  1. 词向量:自然语言中,词是表义的基本单元。词向量是用来表示词的向量。
  2. 词嵌入 (word embedding):将词映射为实数域向量的技术称为词嵌入。
  3. 词嵌入出现的原因:由于 one-hot 编码的词向量不能准确表达不同词之间的相似度(任何两个不同词的one-hot向量的余弦相似度都为0),为了解决这个问题而出现了词嵌入方法 word2vec
  4. word2vec将每个词表示为一个定长的向量,而且这些向量能够表达不同词之间的相似性。
  5. word2vec包含两个模型:跳字模型 (skip-gram) 和 连续词袋模型 (continuous bag of words, CBOW)。

skip-gram

  1. skip-gram:基于某个词来生成它在文本序列周围的词,即以某个词为中心,与距离该中心不超过窗口大小的背景词出现的条件概率。
  2. skip-gram 中,每个词被表示为两个d维向量(中心词的向量和背景词的向量),用以计算背景词出现的条件概率。
  3. skip-gram 训练结束后,对于任意一个索引为i的词,都可得到该词为中心词和背景词的两组向量 v i v_i vi u i u_i ui
  4. 在 NLP 中,一般使用 skip-gram 的中心词向量作为词的表征向量。

CBOW

  1. CBOWskip-gram 类似,但最大区别在于 CBOW 基于某个中心词在文本序列前后的背景词来生成该中心词。【简单来说:skip-gram假设基于中心词来生成背景词;CBOW假设基于背景词来生成中心词】
  2. CBOW中,因为背景词有多个,所以将这些背景词向量取平均,再使用和skip-gram一样的方法计算条件概率。
  3. 在 NLP 中,一般使用 CBOW 的背景词向量作为词的表征向量。

word2vec 的整个过程实现:

1. word2vec 的数据集的预处理:

所用数据集是 Penn Tree Bank (PTB),该语料库曲取自“华尔街日报”。

1.1 下载数据集:

## 导入模块
import math
import os
import random
import torch
from d2l_model import d2l_torch as d2l
## 使用 d2l 封装的方法下载 PTB 数据集
d2l.DATA_HUB["ptb"] = (d2l.DATA_URL + 'ptb.zip','319d85e578af0cdc590547f26231e4e31cdf1e42')def read_ptb():data_dir = d2l.download_extract("ptb") ## 该方法用来读取zip或者tar文件,返回的数据所在的路径with open(os.path.join(data_dir, "ptb.train.txt")) as f:raw_text = f.read()return [line.split() for line in raw_text.split("\n")] ## 返回文本中每一行句子中以空格分开的每个词所构成的列表#sentences = read_ptb()
#f'# sentences数: {len(sentences)}'

1.2 下采样:

删掉文本中某些高频词,缩短句子长度,加快训练。

def subsample(sentences, vocab):sentences = [[token for token in line if vocab[token]!=vocab.unk] for line in sentences] ## 如果 token 不是 <unk> 的话,就会被保留下来counter = d2l.count_corpus(sentences) ## 统计 token 出现的次数num_tokens = sum(counter.values())def keep(token):return (random.uniform(0,1) < math.sqrt(1e-4 / counter[token]*num_tokens)) ## 如果满足条件,则返回Truereturn ([[token for token in line if keep(token)] for line in sentences], counter)

1.3 中心词和上下文词的提取:

从 corpus 中提取所有中心词和上下文词。
随机采样[1:max_window_size]之间的证书作为上下文窗口。
对于任意一个中心词,与其不超过上下文窗口大小的词为它的上下文词。

def get_centers_and_contexts(corpus, max_window_size):centers, contexts = [], []for line in corpus:if len(line) < 2: ## 要构成“中心词-上下文词”对,每个句子至少有2个词continuecenters += line ## 所有句子中的每一个词都可作为中心词for i in range(len(line)):window_size = random.randint(1, max_window_size) ## 生成一个随机整数作为窗口大小indices = list(range(max(0, i-window_size), min(len(line), i+1+window_size))) ## 以i为中心,获取[i-window: i+window]范围内的词indices.remove(i) ## 去掉中心词i本身,剩下上下文词contexts.append([line[idx] for idx in indices])return centers, contexts

1.4 负采样:

使用负采样进行近似训练,根据定义的分布对噪声词进行采样。

class RandomGenerator:def __init__(self, sampling_weights):self.population = list(range(1, len(sampling_weights)+1))self.sampling_weights = sampling_weightsself.candidates = []self.i = 0def draw(self):if self.i == len(self.candidates):## 缓存 k 个随机采样结果,每次从里面取一个,取完后再生成新的缓存结果self.candidates = random.choices(self.population, self.sampling_weights, k=10000) ## 按照 sampling_weight 采样概率对 population 进行采样,采样k次self.i = 0self.i += 1return self.candidates[self.i-1]
## 负采样
def get_negatives(all_contexts, vocab, counter, K):sampling_weights = [counter[vocab.to_tokens(i)]**0.75 for i in range(1, len(vocab))] ## 采样权重 = token出现次数 * 0.75all_negatives, generator = [], RandomGenerator(sampling_weights)for contexts in all_contexts:negatives = []while len(negatives) < len(contexts) * K: ## K 对于一对“中心词-上下文词”,随机抽取的噪声词的个数neg = generator.draw()if neg not in contexts: ## 噪声词不能是该中心词的上下文词,其他的上下文词是可以的negatives.append(neg)all_negatives.append(negatives)return all_negatives

1.5 定义 dataloader 的处理方式:

class PTBDataset(torch.utils.data.Dataset):def __init__(self, centers, contexts, negatives):assert len(centers) == len(contexts) == len(negatives) ## 不成立则引发AssertionErrorself.centers = centersself.contexts = contextsself.negatives = negativesdef __getitem__(self, index):return (self.centers[index], self.contexts[index], self.negatives[index])def __len__(self):return len(self.centers)def batchify(data):max_len = max(len(c) + len(n) for _, c, n in data) ## 因为不同中心词对应的上下文、负采样的向量长度不一样,所以按照最长的进行填充centers, contexts_negatives, masks, labels = [], [], [], []for center, context, negative in data: ## 中心词、上下文、负采样cur_len = len(context) + len(negative)centers += [center]contexts_negatives += [context + negative + [0]*(max_len - cur_len)] ## 用0进行填充masks += [[1]*cur_len + [0]*(max_len - cur_len)] ## 填充部分用0标记,非填充部分用1标记 (主要用于计算损失时,填充部分不参与计算)labels += [[1]*len(context) + [0]*(max_len - len(context))] ## 标签,上下文词为1,其他(负采样部分、填充部分)为0return (torch.tensor(centers).reshape((-1,1)),\torch.tensor(contexts_negatives),\torch.tensor(masks),\torch.tensor(labels)) ## reshape((-1,1)) => .shape=(n,1)## 中心词(centers), 上下文及负采样(context_negatives), 掩码(masks),标签(labels)

代码合并及数据集的生成:

包括上面的1.1, 1.2, 1.3, 1.4, 1.5

def load_data_ptb(batch_size, max_window_size, num_noise_words):#num_workers = d2l.get_dataloader_workers() ## 使用4个进程读取数据(但实际操作会出错)sentences = read_ptb() ## 第一步的读取数据vocab = d2l.Vocab(sentences, min_freq=10) ## 第一步中用 "<unk>" 替换低频词subsampled, counter = subsample(sentences, vocab) ## 第二步下采样,去掉某些意义不大的高频词,缩短句子长度corpus = [vocab[line] for line in subsampled] ## 第二步将下采样后的句子映射为词表中的索引all_centers, all_contexts = get_centers_and_contexts(corpus, max_window_size) ## 第三步,中心词和上下文词(上或下文词数目不超过max_window_size)all_negatives = get_negatives(all_contexts, vocab, counter, num_noise_words) ## 第四步负采样,生成噪声词dataset = PTBDataset(all_centers, all_contexts, all_negatives)data_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True,collate_fn=batchify, ## collate_fn 定义了小批量数据加载后需要做的处理(可见http://t.csdn.cn/4zhEj 的评论)num_workers=0)return data_iter, vocab
## 生成数据集
batch_size, max_window_size, num_noise_words = 512, 5, 5
data_iter, vocab = load_data_ptb(batch_size, max_window_size, num_noise_words)

2. 预训练 word2vec:

构建并训练模型。

from torch import nn

2.1 构建嵌入层:

  1. 嵌入层将词元的索引映射到其特征向量 (上面数据预处理已经得到了词元的索引)。
  2. 嵌入层的权重是一个矩阵,行数等于字典大小,列数等于向量的维数。
  3. 在嵌入层训练完成之后,权重矩阵就是所需要的。每一行都是一个词的特征向量。
  4. 该层的输入就是词元的索引,对于任何词元索引 i i i,其向量表示可以从嵌入层中的权重矩阵的第 i i i行获得。

2.2 定义 skip-gram:

通过 embedding 层将索引映射为特征向量。

def skip_gram(center, contexts_and_negatives, embed_v, embed_u):v = embed_v(center)u = embed_u(contexts_and_negatives)pred = torch.bmm(v, u.permute(0,2,1))return pred

2.3 定义二元交叉熵损失函数:

class SigmoidBCELoss(nn.Module):def __init__(self):super().__init__()def forward(self, inputs, target, mask=None):out = nn.functional.binary_cross_entropy_with_logits(inputs, target, weight=mask, reduce="none")return out.mean()loss = SigmoidBCELoss()

2.3 定义初始化模型参数:

## 两个嵌入层,特征向量维度为100
## 第一层计算中心词,第二层计算上下文词embed_size = 100
net = nn.Sequential(nn.Embedding(num_embeddings=len(vocab),embedding_dim=embed_size),nn.Embedding(num_embeddings=len(vocab),embedding_dim=embed_size))

2.4 训练:

## 定义训练函数
def train(net, data_iter, lr, num_epochs, device=d2l.try_gpu()):## 模型初始化def init_weights(m):if type(m) == nn.Embedding:nn.init.xavier_uniform_(m.weight) ## 函数最后有一个下划线表示该函数输出直接替换net.apply(init_weights)net = net.to(device)optimizer = torch.optim.Adam(net.parameters(), lr=lr)animator = d2l.Animator(xlabel="epoch", ylabel="loss", xlim=[1, num_epochs]) ## 训练过程中的 epoch-loss 进行可视化metric = d2l.Accumulator(2) ## 加快求和计算的速度for epoch in range(num_epochs):timer, num_batches = d2l.Timer(), len(data_iter)for i, batch in enumerate(data_iter):optimizer.zero_grad()center, conter_negative, mask, label = [data.to(device) for data in batch]pred = skip_gram(center, conter_negative, net[0], net[1])l = (loss(pred.reshape(label.shape).float(), label.float(), mask) / mask.sum(axis=1)*mask.shape[1])l.sum().backward()optimizer.step()metric.add(l.sum(), l.numel())if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:animator.add(epoch + (i+1)/num_batches, (metric[0]/metric[1],))print(f'loss {metric[0] / metric[1]:.3f}, 'f'{metric[1] / timer.stop():.1f} tokens/sec on {str(device)}')
## 进行训练
lr, num_epochs = 0.001, 10
train(net, data_iter, lr, num_epochs)

在这里插入图片描述

loss 0.566, 223737.2 tokens/sec on mps

3. 使用预训练的word2vec寻找语义上相近的词:

def get_similar_tokens(query_token, k, embed):W = embed.weight.data ## 我们预训练词嵌入就是为了得到这个权重矩阵,该权重矩阵就是由每个词的特征向量构成的x = W[vocab[query_token]]## 计算余弦相似度cos = torch.mv(W,x) / torch.sqrt(torch.sum(W*W, dim=1) * torch.sum(x*x)+1e-9)topk = torch.topk(cos, k=k+1)[1].cpu().numpy().astype("int32")for i in topk[1:]:print(f'cosine sim={float(cos[i]):.3f}: {vocab.to_tokens(i)}')get_similar_tokens('chip', 3, net[0])
cosine sim=0.777: intel
cosine sim=0.714: bugs
cosine sim=0.647: computer

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

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

相关文章

常见的正则表达式

目录 问题现象&#xff1a; 问题分析&#xff1a; 解决方法&#xff1a; 拓展&#xff1a; 1、手机号或座机&#xff1a; 2、邮箱&#xff1a; 3、中文&#xff1a; 4、数字&#xff1a; 5、英文&#xff1a; 6、组合&#xff1a; 问题现象&#xff1a; 今天在项目中&…

Bootstrap-学习文档

Bootstrap 简介 什么是 Bootstrap&#xff1f; Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架。 Bootstrap是前端开发中比较受欢迎的框架&#xff0c;简洁且灵活。它基于HTML、CSS和JavaScript&#xff0c;HTML定义页面元素&#xff0c;CSS定义页面布局&#x…

数据库转换分析软件:EasyMorph 5.X Crack

EasyMorph 为您和您的团队提供数据超能力。无需编码技能。不要问 IT 人员。 自己做。 内置动作 即使您不是 IT 专家&#xff0c;也可以从任何地方检索数据并自动执行复杂的数据转换 无需 SQL 或编程知识 — 设计简单且 100% 可视化 减少对企业IT部门的依赖&#xff0c;减少繁琐…

清风学习笔记—层次分析法—matlab对判断矩阵的一致性检验

在判断矩阵是否为正互反矩阵这块&#xff0c;我写了两种代码&#xff0c;改进前很麻烦且有错误&#xff0c;改进后简洁多了&#xff0c;改进前的代码还有错误&#xff0c;忽略了对角线的值必须都是1&#xff0c;只考虑了除开对角线的元素相乘为1。 %% 改进前代码 A[3 2 4;1/2 …

Git的远程操作与多人协作

"爱在地图上剥落&#xff0c;我离孤单几公里~" 我们目前所说、所学的内容&#xff08;工作区、暂存区、版本库&#xff09;都只是存在于本地上&#xff0c;也就是说你的一台机器上只有这么一个你维护的版本库。可是Git是一个分布式版本控制系统&#xff0c;这又是什…

k8s kubelet coredns ubuntu修改dns配置文件读取路径

kubelet 修改默认使用的dns配置文件 coredns服务默认使用节点上的dns配置&#xff0c;由于在Ubuntu18及以上版本&#xff0c;默认是启用systemd-resolved服务的&#xff0c;且配置nameserver配置文件默认为 /run/systemd/resolve/resolv.conf &#xff0c; 且kubelet默认的dns…

YOLO 划分数据集(训练集、验证集、测试集)

目录 前言训练集、验证集&#xff08;8:2&#xff09;训练集、验证集、测试集&#xff08;7:2:1&#xff09; 前言 本博客是在我的另一篇博客 VOC 格式与 YOLO 格式的相互转换 的基础上进行的&#xff0c;有需要可以参考 以下代码亲测可以直接复制运行&#xff08;以下所有的路…

利用Redis实现向量相似度搜索:解决文本、图像和音频之间的相似度匹配问题

在自然语言处理领域&#xff0c;有一个常见且重要的任务就是文本相似度搜索。文本相似度搜索是指根据用户输入的一段文本&#xff0c;从数据库中找出与之最相似或最相关的一段或多段文本。它可以应用在很多场景中&#xff0c;例如问答系统、推荐系统、搜索引擎等。 比如&#…

数据库CAST()函数,格式(CAST AS decimal)

语法&#xff1a; CAST (expression AS data_type) 参数说明&#xff1a; expression&#xff1a;任何有效的SQServer表达式。 AS&#xff1a;用于分隔两个参数&#xff0c;在AS之前的是要处理的数据&#xff0c;在AS之后是要转换的数据类型。 data_type&#xff1a;目标系统…

【个人笔记】Linux命令之watch命令

1.命令简介 watch 以周期性方式执行给定的命令&#xff0c;并全屏显示执行结果&#xff0c;可以帮助监测一个命令的运行结果。 2.命令格式及参数选项说明 命令格式&#xff1a; watch [OPTIONS] COMMAND选项说明&#xff1a; -d, --differences [PERMANENT]高亮显示最近两…

redis中List<String>缓存处理

放入redis List<String> strList ["1","2"]; // 把list转化成String放入缓存中 redisUtil.set(key, JSONObject.toJSONString(strList),300);从redis取出 Object object redisUtil.get(key); List<String> strList1 null; if (Objects.no…

c语言的数据类型 -- 与GPT对话

1 c语言的数据类型 在C语言中,数据类型用于定义变量的类型和存储数据的方式。C语言支持多种数据类型,包括基本数据类型和派生数据类型。以下是C语言中常见的数据类型: 基本数据类型(Primary Data Types): int: 整数类型,通常表示带符号的整数。char: 字符类型,用于存储…

Java 生成随机数据

文章目录 1. Java-faker依赖demo 2. common-random依赖demo 1. Java-faker 依赖 <dependency><groupId>com.github.javafaker</groupId><artifactId>javafaker</artifactId><version>1.0.2</version> </dependency>https://…

ES6基础知识二:ES6中数组新增了哪些扩展?

一、扩展运算符的应用 ES6通过扩展元素符…&#xff0c;好比 rest 参数的逆运算&#xff0c;将一个数组转为用逗号分隔的参数序列 console.log(...[1, 2, 3]) // 1 2 3console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5[...document.querySelectorAll(div)] // [<div>, &l…

exoplayer3 ffmpeg 扩展库编译 aar,导入集成

exoplayer3 ffmpeg 扩展库编译 aar&#xff0c;导入集成。 已经编译完成的aar&#xff1a;https://download.csdn.net/download/mhhyoucom/88086822 编译项目方法&#xff1a; github下载项目&#xff1a;https://github.com/google/ExoPlayer FFmpeg 模块提供 &#xff0c;…

【机器学习】基础知识点的汇总与总结!更新中

文章目录 一、监督学习1.1、单模型1.1.1、线性回归1.1.2、逻辑回归&#xff08;Logistic Regression&#xff09;1.1.3、K近邻算法&#xff08;KNN&#xff09;1.1.4、决策树1.1.5、支持向量机&#xff08;SVM&#xff09;1.1.6、朴素贝叶斯 1.2、集成学习1.2.1、Boosting1&…

IFIX5.8安装教程

管理员身份运行&#xff1a; 安装&#xff1a; 下次安装的时候选择SCADA服务器&#xff0c;独立。然后下图就不会出现了。 重启电脑&#xff1a;

安装python需要多大内存,python下载安装包多大

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;安装python需要多大内存&#xff0c;python安装占多大空间&#xff0c;现在让我们一起来看看吧&#xff01; 1、pytorch包有多大 938.79MB。pytorch包有938.79MB&#xff0c;pytorch离线安装包是一个不错的学习资源&am…

8款常见的自动化测试开源框架

在如今开源的时代&#xff0c;我们就不要再闭门造车了&#xff0c;热烈的拥抱开源吧&#xff01;本文针对性能测试、Web UI 测试、API 测试、数据库测试、接口测试、单元测试等方面&#xff0c;为大家整理了github或码云上优秀的自动化测试开源项目&#xff0c;希望能给大家带来…

OSPF路由协议(红茶三杯CCNA)

链路状态路由协议 OSPF&#xff08;开放式最短路径优先&#xff09;Open Shortest Path First 是一种链路状态路由协议&#xff0c;无路由循环&#xff08;全局拓扑&#xff09;&#xff0c;RFC2328 “开放”意味着非私有的 管理型距离&#xff1a;110 OSPF采用SPF算法计算到达…