乐于分享
好东西不私藏

告别晦涩!神经网络与Word2Vec,NLP入门核心

告别晦涩!神经网络与Word2Vec,NLP入门核心

01NLP的难点

NLP自然语言处理,大模型是NLP的一个分支,自然语言处理是大模型的基础。NLP有以下难点: 
  • 第一文本词汇量多,每个单词都是一个特征,类别也很多。

  • 第二语义信息丰富,各种同义词、近义词等。

  • 第三语言的差异性,比如中文要做分词,但英语天然是空格隔开的没有分词的概念,英语有时态的概念,完成时、进行时,中文没有时态,各种语言的差异比较大。 

02贝叶斯公式/语言的马尔可夫性

NLP里最典型的概念是语言模型,就是计算一句话合法的概率,比如我喜欢吃饭,让计算机去学语言合法的概率,合法率对于语言是有顺序的,我喜欢吃饭,饭喜欢吃我、吃饭喜欢我,这些都是不合法。
比如求“我喜欢你”连续4个词出现的概率,先求第一个词“我”出现的概率,再求当我出现之后“”出现的概率,再求当出现“我喜”这两个词出现的时候“”所对应的概率,最后求“我喜欢”出现后“”出现的概率,通过贝叶斯公式展开如下。
这是个万能公式,对所有概率都成立,但这样求解太麻烦,比如一句话有20个词,越到后面就越多很不好求,因此有一个马尔可夫性的简化版。比如爷爷生爸爸,爸爸生儿子,儿子生孙子, 孙子样貌的概率肯定受爷爷的影响, 但当儿子已经确定了,再预测孙子样貌,就不需要爷爷和爸爸的信息,这个叫马尔可夫性

对于词语来说做一个简化,每个词出现的概率只和他前面的有关。比如说W2只和W1有关,W3只和W2有关,W4只和W3有关,概率的这种简化叫做一阶马尔克夫,一阶马尔克夫就说只跟前面那个有关。隔代遗传就是二阶、三阶。

贝叶斯公式对任何场景都适用,但马尔可夫性有特定的适用场景。对语言来说,用马尔可夫信进行一个简化。当出现W1的时候,W2出现的概率接近于1,比如葡萄、鸳鸯,只要出现一个,它基本上就是成对出现,所以说语言它其实是具备一阶马尔可夫性。

两个词,先统计W1出现的次数N,W1出现之后紧接着W1W2出现的次数M,那么P(W2/W1)的概率为: 

通过这种方式来计算概率,任何一个语言都可以把概率给算出来,但这个算法有很大的问题。

  • 第一个问题:比如二阶马尔可夫,中文有5万常用词汇,两两之间出现的概率要预测5万×5万=25亿,肯定是统计不过来的,很有可能两个词在有限的数据里,根本就没有挨着出现过,但并不是它们真的没有出现,只是数据量不够大,没有把这个问题给暴露出来。因为数据量的原因漏掉很多。

  • 第二个问题:没有考虑语义相似度。计算机、电脑、computer这三个其实是一回事, 但统计的时候会把这三个当成三个不同的词来做,这样会导致语义的概率整体偏低,一个词的表述方式越多就越稀释它的概率会变得特别低,这是直接统计最大的问题。

03多分类的神经网络和NLP的结合

对于二阶马尔可夫,通过两个词预测下一个词是什么,它其实是一个多分类,通过W1,W2预测下个W等于什么,这就是多分类。类别数就是词数,预测下一个词的概率就变成了一个多分类的概率。有5万个词,那么就是5万分类。 多分类如何设计神经网络。

假设5万个词从0到4999编号,输入一个5万维的向量,这个向量是one hot,一个向量来表示一个词,比如说000100,第四个词的时候为1,其他的都为0。

上图有两个词各是一个V维向量,把这两个向量通过全连接层,变成一个V维的向量,输入2V变成V维向量,再通过softmax函数输出所对应的概率,这个就是一个最简单的多分类的神经网络。

但是这里有很大的问题,参数量太大了,这一个全连接2V乘V, 十几亿的参数,参数量实在太大了,改进方案是,每个词对应128维的向量,而且是128维稠密向量,one hot向量只有一个为1,其他都为0,会很稀疏,稠密向量就是0.2、0.1、0.3、0.9等,每个位置都有数,把这两个向量相加,就是128维,再通过一个全连接加softmax函数输出,其实把稀疏变成128的稠密。

但是这7个向量哪里填1就按词表顺序来填,知道我们了,但是这128凭什么填0.3、0.9等等,所以这个稠密向量开始随机通过训练得到,之前神经网络只有参数进行训练,输入是一个固定值,输入不是随机的,这个地方它连输入都是一个随机的向量。

04Word2Vec

把句子分成单词,单词映射成词向量的过程称为叫词嵌入也就是word embedding,把一个自然语言变成一个数值化向量的过程。

用前面的词预测后面的一个词主要是训练效率太低,比如有句话”我喜欢猫,温顺可爱”,选择猫作为中心词,猫是要被预测的,作为一个label,用前面两个词和后面两个词来预测中间这个词。就是每个词都对应一个向量,把这些向量全部相加在一起,原来是k维还是k维,做一个全连接层,从k维到v维是词表数词汇量大小词的数量,比如共有20万词,v就是 20万,v再经过softmax把猫给预测出来,这个就是Word2Vec。就是最终这些词经过训练都能够训练的特别好,具备语义信息。

比如第一句话“我喜欢温顺可爱”,预测猫,可能还有一句话是“我喜欢猫,活泼好动”,它预测出来也是猫。这两句话对应的目标是一样的,都是要预测猫。目标一样,网络也是固定的,可以倒推输入。输出一样的情况下,模型中间不变,那就希望输入尽可能一样。

要想输入一样,活泼、好动、温顺、可爱,它们之间的向量就要比较接近,这样通过模型训练出来的每个词对应的向量,具有同义性,如果说两个词本身是同义的,那么说明这两个词在数据中出现的时候,经常是可以彼此替代,它们上下文之间彼此接近,因此这两个词的向量就会特别接近,就具备了同义词的概念。同义就是在这个位置上可以经常彼此替代的词,这个就是word2vec。

word2vec有两种形式:

  • 第一种是CBOW形式:“我喜欢猫,温顺可爱”,用周围四个作为输入,然后来预测猫。

  • 第二种是Skip-gram形式:“我喜欢猫,温顺可爱”,用猫作为输入,只有一个向量,来预测它周边四个词。

这两种方法的区别,在于样本数量,一个样本和四个样本,同样的数据,四个样本训练的更加充分。

如果第一次预测的不准,这四个向量调整力度是一样的,有三个向量都已经训练很充分了,只有第四个向量还没有训练充分,那么还要不停的调,对这三个也要施加同等的作用。

Skip-gram就是一对一的来调。

有语料A、B、C、D、E,用Skip-gram方法,每一条训练数据C对应一个向量叫Vector为K维,这个向量经过一个矩阵,词表是V维,经过V*K维矩阵,变成一个V维向量,再经过softmax最终得到类别A。 

就这样一对一的训练,比如说有一句话是“我喜欢猫”,还有一句话是我喜欢狗”,用这个猫能预测出“我”, 狗也能预测“我”,也就是说“我”作为中间变量,就会得到猫和狗的相似度比较接近。这两个词能预测出同一类,那么这两个词向量就比较接近。两个人虽然没有直接接触,但他们有特别多共同的朋友,那么这两个人很有可能也会成为朋友,会比较相似。

从数学上看:一个one hot向量h(比如只有第四位为1,其他都为0),拿一个k*v维的w矩阵,w矩阵乘以向量h,w*h等于k*1的向量。这个乘以实际上乘的是它的第四列,乘出来就是这个矩阵的第四列,其他都为0。h只有第四行为1,其他的全为0,这两个相乘就是取它的第四列。一个词对应一个向量,也可以理解成一个one hot形式再乘一个矩阵,对应的向量就是矩阵的某一列。

04词袋模型

Word2vec其实是一个词袋模型,词袋模型不考虑词之间的顺序,比如“我借你钱”,“你借我钱”,这两句话的意思是完全相反,它对应的词袋都是四个词,这里“我喜欢猫”,它在训练的时候带了一点顺序,猫的旁边是这四个,但是“我喜欢”和“喜欢我”、“温顺和可爱”,调转顺序其实是不会影响这个向量的,这个就是Word2vec比较大的问题它对这个顺序不敏感

Word2vec比较适合长文本,文章越长,更关心一个整体的信息,对于顺序其实没有那么敏感。如果文章比较短,变换一下意思差异会很大,那Word2vec就不适用了。Word2vec最大的功绩是第一次把神经网络和自然语言结合在了一起

05代码fasttest

import fasttextimport fasttext.utilfrom sklearn.metrics.pairwise import cosine_similarityfrom langchain.embeddings.base import Embeddingsimport numpy as npfrom langchain.docstore.document import Documentfrom langchain_community.vectorstores import FAISSimport pickle# 进行向量的归一化def normal(vector):    ss = sum([s**2 for s in vector])**0.5    return [s/ss for s in vector]# 训练模型(如果已有预训练模型可跳过此步骤)def train_fasttext_model(corpus_path, model_path='fasttext_model.bin'):    """    训练FastText词向量模型    :param corpus_path: 训练语料路径,每行是一个句子    :param model_path: 模型保存路径    :return: 训练好的模型    """    model = fasttext.train_unsupervised(        corpus_path,        model='skipgram',  # 也可以选择'cbow'        dim=100,           # 词向量维度        ws=5,              # 上下文窗口大小        minCount=5,        # 最小词频        epoch=5            # 训练轮数    )    model.save_model(model_path)    return model# 加载预训练模型def load_pretrained_model(model_path=None):    """加载预训练模型,如果路径为空则下载英文预训练模型"""    if model_path:        return fasttext.load_model(model_path)    # 下载英文预训练模型(需要联网)    ft = fasttext.load_model('cc.en.300.bin')    # 如需降低维度以提高性能    fasttext.util.reduce_model(ft, 100)    return ft# 获取词向量def get_word_vector(model, word):    """获取单个词的向量表示"""    return model.get_word_vector(word)# 计算词语相似度def compute_similarity(model, word1, word2):    """计算两个词语的余弦相似度"""    vec1 = get_word_vector(model, word1)    vec2 = get_word_vector(model, word2)    similarity = cosine_similarity([vec1], [vec2])    return similarity[0][0]# 查找近义词def find_nearest_neighbors(model, word, k=500):    """查找与给定词语最相似的k个词语"""    return model.get_nearest_neighbors(word, k=k)class CustomEmbeddings(Embeddings):    def __init__(self, model):        self.model = model    def embed_documents(self, texts):        """将文档转换为嵌入向量"""        results = [self.embed_query(text) for text in texts]        return results    def embed_query(self, word: str):        vector = normal(self.model[word])        return vector# 主函数示例if __name__ == "__main__":    corpus_path = "datacorpus"  # 替换为实际语料路径    custom_model = train_fasttext_model(corpus_path)    # for word  in custom_model.get_words():    #     print (word,custom_model[word])    # 自定义了一个Embedding模型,和langchain兼容    embeddings = CustomEmbeddings(custom_model)    documents = []    for word in custom_model.get_words():        documents.append(Document(page_content=word,))    # 向量数据库,入库    db = FAISS.from_documents(documents, embeddings)    results = []    for word in custom_model.get_words():        # 每个词找寻最近的10个        similar_words = db.similarity_search(word, k=11)        words = [s.page_content for s in similar_words][1:]        results.append(str([word, words]))    with open("word_similar","w",encoding="utf-8"as f:        f.writelines("\n".join(results))

有一些万能搭配的词“的”、“呢”、“了”,“,”、“。”等,跟所有大部分词,它的这个词向量有什么特点,

  • 万能词全挤在空间正中心/原点附近,实词都散在四周。

  • 实词同簇内相似度极高(猫↔狗),实词跨簇相似度很低(猫↔饭),万能词和所有词相似度都中等、均匀,不高不低。

  • 它们不是 “跟谁都亲”,而是语义最 “淡”、被所有词平均拉到中间,所以百搭。

  • 它们向量本身 中心词模长短、各维度都接近 0、无强特征;四周实词:模长长、有明显激活的语义维度

机器学习系列文章: 
AI界的Hello world 线性回归
新手实战任务:逻辑回归(一)
逻辑回归企业级调参
无监督模型kmeans算法
劝退无数人的神经网络公式,大白话手推拆解 
神经网络softmax如何一眼认对10个数字
破局Sigmoid,Tanh与Relu的进阶逻辑
深度学习优化:参数初始化、梯度下降、动态学习率避坑指南
Adam优化器:破解神经网络局部极小值与鞍点的核心逻辑
深入理解偏差-方差困境,用L1/L2正则化找回模型的“大道至简”
为何CNN能“看懂”图像而DNN不能?卷积与多通道多卷积核的奥秘
不用公式!带你看懂CNN三大经典网络:LeNet、AlexNet、VGG

吃透迁移学习:从BatchNorm到ResNet的实战思路

Hey!
我是小魔仙,
欢迎描下二维码,让我们产生链接~
–欢迎关注公众号–
90后北漂男孩
现任互联网大厂研发工程师
想要影响1000+个人变得更好
一起遇见未知的自己
一起人生长跑

成为自己的光

💛

多点一下在看多一条小鱼干

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 告别晦涩!神经网络与Word2Vec,NLP入门核心

猜你喜欢

  • 暂无文章