NLP.001 词向量

NLP.001 词向量

0.序言

深度学习方面的理论还有很多没有写,但是只能告一段落了,后面有时间会再去写.但我们学了深度学习的目的肯定还是引用。接下来几篇文章就会详细去写关于NLP相关的知识。

首先,无论是ML还是DL首要的就是如何将信息转化成计算机能识别并进行处理的形式。这里就是 向量(Tensor) ,如何将文字转化成向量呢,通常包括是 分词->编码->映射 这几步。下面我将详细讲解这个过程

1.分词

你是不是认为我要讲jieba下面一些方法函数的使用,那只能说明你还不够了解我(qwq…)我要讲的是原理,看底层源码,哥们。。。。

1.基于dp原理的分词

先来看一下jieba源代码里面的方法:

def calc(self, sentence, DAG, route):
    N = len(sentence)
    route[N] = (0, 0)
    logtotal = log(self.total)
    #wjs:py小课堂qaq:这里利用了两层for循环,外面一层决定倒着遍历句子中每个单词(下标),内循环决定了每个单词到下一个能到达单词的对应词频
    #这里把每种情况相乘并且取max
    for idx in xrange(N - 1, -1, -1):
        #wjs:这里变成取对数操作
        #or 1是为了防止词频是Long long型的
        route[idx] = max((log(self.FREQ.get(sentence[idx:x + 1]) or 1) -
                          logtotal + route[x + 1][0], x) for x in DAG[idx])

大概解释一下,就是对于每个确定的句子,里面的每个单词都对应了在词典(外部导入)里面的一个固定的词频,那么由于 古典概型,假设每个词之间都是相互独立的,那么每一个词在句子中对应的概率p就等于n(wordi)/n(word1)+n(word2)+n(word3)…+n(wordn)

现在对这个句子进行分词,那么会产生很多可能,假设从最后一个字开始划分,那么它可能到达的位置就有很多很多(可能与下一个结合为一个词,也可能与下下个…bulabula,qwq),然而最后一个字划分结束的位置又会影响到下一次词的划分(划分过的字不能再次使用哈)。那么总会有一种最优的情况,使得整体的概率最大

这种相邻状态存在转移关系,并且求解全局最优的算法,就是背包(dp)啊!!!

在这里插入图片描述

2.TF—IDF模型

我们在对句子或者文档进行关键词提取的时候,如何去衡量一个词在 整篇文档整体中的重要性呢

出现的越多一定就越重要吗,如果如此那怎么区别不同类别文章呢?

出现的越少一定就越重要吗,如果如此那该如何计算呢?

把以上两种情况折中一下,就得到了我们我们的 TF—IDF

TF:一个词在一条句子中出现的频率/这条句子中词的总数

IDF:ln(一个训练文档中句子的总数/包含该词的句子总数)

TF-IDF:TF(wi)*IDF(wi)

2.编码

1.one—hot

ML对于文字的编码一直只停留在one_hot阶段,即对于一句话中有n个单词的话,会对应生成一个

n*n01 矩阵,

从上到下每一行代表着单词顺序的从前到后,

从左到右,出现1的那一列的列坐标即代表着对应的那一个字

这里引用一下大佬的图片:【Transformer系列】深入浅出理解Embedding(词嵌入)_transformer embedding-CSDN博客

在这里插入图片描述 在这里插入图片描述

对应到torch中对应的 F.one_hot() 函数中,看下官方文档怎么说的

在这里插入图片描述

翻译一下,就是生成一个 len(input) 行, num_classes 列的矩阵。这个矩阵中除了在input中每一个值的最后一个维度作为索引对应的地方是1,其余地方都是0。

下面是官方文档给出的例子

torch.nn.functional.one_hot — PyTorch 2.3 documentation

>>> F.one_hot(torch.arange(0, 5) % 3)
tensor([[1, 0, 0],
        [0, 1, 0],
        [0, 0, 1],
        [1, 0, 0],
        [0, 1, 0]])
>>> F.one_hot(torch.arange(0, 5) % 3, num_classes=5)
tensor([[1, 0, 0, 0, 0],
        [0, 1, 0, 0, 0],
        [0, 0, 1, 0, 0],
        [1, 0, 0, 0, 0],
        [0, 1, 0, 0, 0]])
>>> F.one_hot(torch.arange(0, 6).view(3,2) % 3)
tensor([[[1, 0, 0],
         [0, 1, 0]],
        [[0, 0, 1],
         [1, 0, 0]],
        [[0, 1, 0],
         [0, 0, 1]]])
2.如何降低01矩阵的维度?

假如有一串 one_hot 代码如下所示:

[1, 1, 0, 0]#文本1
[1, 0, 0, 1]#文本2
[0, 1, 1, 0]#文本3 

我们可以转换为

[0, 1]  # 文本序号/列下标
[0, 3]  #这个代表第二个句子中第0列和第3列的值不是0(是1)
[1, 2]

这样再去对01矩阵进行读取的时候只需要访问第二个矩阵即知道所有1所在的位置,方便进行操作

3.映射

1.向量

高中的时候我们都学过向量,基本定义就是 一个矢量在坐标系中的表达 ,其巧妙之处就是将一个矢量转换成了坐标的形式,很方便做数学上 标量的计算

回到最开始提出的问题,何为向量呢?什么又是词向量?其实很简单,我们将所有输入的语句标准化之后,要投入到使用层面,即对它们之间的关系要做运算从而有一个 可视化,可计算的标准,但只停留在one_hot编码远远不够的,因为其是一个稀疏矩阵,很多维度上的信息都是无用的(都是0),所以我们为了更加全面的去分析不同句子之间的联系,要做的第一步就是

将一个稀疏矩阵转换成相同维度的稠密矩阵

2.weight矩阵

1.全连接(nn.Linear())

在前面神经网络中,通过加入linear使数据有了weight,即有了可优化的对象。但在这里的Linear()的主要作用有两点

1.控制输出矩阵特征的数量

先看官方文档:Linear — PyTorch 2.3 documentation

在这里插入图片描述

下面列出一个例子,将一个稀疏矩阵映射到稠密矩阵上

import torch.nn as nn
import torch
import torch.nn.functional as F
import numpy as np
def t0() :
    features = [
        [1, 1, 0, 0, 0],  
        [1, 1, 1, 0, 0],  
        [0, 1, 1, 0, 0],  
        [0, 0, 0, 1, 1],  
        [0, 0, 1, 0, 1],  
        [0, 0, 1, 1, 0],  
        [0, 0, 1, 1, 1],
    ]
    features = np.asarray(features, dtype=np.float32)
    features = torch.tensor(features)
    m=nn.Linear(5,5)
    output=m(features)
    print(output)
if __name__ == '__main__':
    t0()
    '''tensor([[ 0.0301, -0.6789,  0.7991,  0.2303, -1.1344],
        [ 0.0171, -1.0850,  0.4215, -0.0491, -1.0711],
        [ 0.1171, -0.9259,  0.0714, -0.4162, -0.7722],
        [-0.1048,  0.4798,  0.7283, -0.1678, -0.1963],
        [ 0.1736, -0.3083,  0.3366, -0.0535, -0.0304],
        [-0.5325, -0.3701, -0.0686, -0.8803, -0.4750],
        [-0.1178,  0.0737,  0.3507, -0.4471, -0.1330]],
       grad_fn=<AddmmBackward0>)'''

但是毕竟映射不能保证学习到原数据所有的特征,所以是有误差滴,所以才要BP鸭…

2.embedding(嵌入)

还是先看官方文档: Embedding — PyTorch 2.3 documentation

在这里插入图片描述

解读

1.最重要也是必须输入的两个参数 就是 num_embeddings 和embedding_dim 这两个数字中后者可以随意输入,前者 需要保证输入的值比input中所有的值要大,因为只有如此才能保证输入的值做为索引的时候不会超出范围

2.Output: (∗,𝐻)(∗,H), where * is the input shape and 𝐻=embedding_dimH=embedding_dim 这句话的意思就是表明embedding 在原先的基础上增加了一维,维度里面有embedding_dim个元素

作用
1.大体来说就是将原数组在原来的顺序上,在不同的 embeddeing_dim上的相同下标的值拿出来作为新的维度

替代原来input里面的元素

2.大体原理和linear差不多,只不过 Linear不改变矩阵的维度而embedding将矩阵进行了升维操作。 但都与原矩阵之间存在着映射关系和差异

import torch.nn as nn
import torch
import torch.nn.functional as F
import numpy as np
from torch.nn import Embedding
def t0() :
    features = [
        [1, 1, 0, 0, 0],  # 文本1
        [1, 1, 1, 0, 0],  # 文本2
        [0, 1, 1, 0, 0],  # 文本3
        [0, 0, 0, 1, 1],  # 文本4
        [0, 0, 1, 0, 1],  # 文本5
        [0, 0, 1, 1, 0],  # 文本6
        [0, 0, 1, 1, 1]  # 文本7
    ]
    features = np.asarray(features, dtype=np.float32)
    features = torch.LongTensor(features)
    emb = Embedding(num_embeddings=3, embedding_dim=2)
    #m=nn.Linear(5,5)
    #output=m(features)
    output = emb(features)
    print(emb.weight.size())
    print(features.size())
    print(output.size())
    #torch.Size([3, 2])
    #torch.Size([7, 5])
    #torch.Size([7, 5, 2])
    print(emb.weight.T)
    '''tensor([[0.8567, -0.6881, -0.5146],
            [-1.1263, -1.0687, 1.8672]], grad_fn= < PermuteBackward0 >)'''
    print(output)
    '''tensor([[[-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [0.6933, -1.1181],
             [0.6933, -1.1181],
             [0.6933, -1.1181]],

            [[-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [0.6933, -1.1181],
             [0.6933, -1.1181]],

            [[0.6933, -1.1181],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [0.6933, -1.1181],
             [0.6933, -1.1181]],

            [[0.6933, -1.1181],
             [0.6933, -1.1181],
             [0.6933, -1.1181],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975]],

            [[0.6933, -1.1181],
             [0.6933, -1.1181],
             [-1.2768, -0.2975],
             [0.6933, -1.1181],
             [-1.2768, -0.2975]],

            [[0.6933, -1.1181],
             [0.6933, -1.1181],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [0.6933, -1.1181]],

            [[0.6933, -1.1181],
             [0.6933, -1.1181],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975]]], grad_fn= < EmbeddingBackward0 >)'''

if __name__ == '__main__':
    t0()
           [0.6933, -1.1181]],

            [[0.6933, -1.1181],
             [0.6933, -1.1181],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975],
             [-1.2768, -0.2975]]], grad_fn= < EmbeddingBackward0 >)'''

if __name__ == '__main__':
    t0()
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值