word2vec简介、原理、缺陷及应用。

一、什么是word2vec?
word2vec及word to vector,翻译过来就是从单词到向量,它是将自然语言中的单词转化为向量的一种方法。为什么要把单词转化为向量呢?这是由于在进行自然语言处理时,我们有可能要比较两个短语或者语句的相似性,比较他们的语义信息,而让机器理解句子就比较困难,所以要转化成计算机能看懂的语言——数字,从而我们就能进行后面的一系列操作。这种方法更像是自然语言处理的一个前序工作、一个桥梁,有了这种方法,文本与后面的研究就被连接了起来。
它是一种语言模型,使用向量表示单词,向量空间表示句子。在将单词转化为向量之后,句子也就可以被表示成一个矩阵,这样就把现实中的语言成功转化成了数字。
它是一种词嵌入模型,简单来说,就是可以通过训练词嵌入模型将文本由原来的高维表示形式转化为低维表示形式,每一个维度代表着当前单词与其它单词在该维度下的不同,多个维度的数共同来表示这个单词。word2vec是基于one-hot词向量进行的转化,one-hot是把每一个词都表示成(0,0,1,…,0,0,…)的形式,不仅在文字量巨大时会造成数据灾难,而且在多个词进行比较时效果一般。
word2vec在2013年被提出后,一度火的一塌糊涂,被广泛应用于科研和工作中,然而它也有很多缺陷,这个在后面会详述。于是,后来就出现了越来越多更优秀的语言模型如GPT(1、2、3)、BERT及其变体等。

二、word2vec的原理是什么?

word2vec出现的时间比较久了,已经有很多大佬介绍过,在这里我贴两个比较好的。我个人的建议是:如果不是做算法或者是想要完全深入的了解的博友,只看第一个工作原理就能理解得差不多了。

  1. 这一篇的作者利用图的形式把word2vec的两种模式CBOW(Continuous Bagof-Words)和 Skip-gram 的 工作原理都介绍了一下,里面图片有些模糊,可能是借鉴的国外网站的,但是内容还是很可的。

链接1: 图解词嵌入word2vec

  1. 关于word2vec的 数学原理,可以看下边一篇文章,它几乎把word2vec涉及的公式模型全都介绍了。

链接2:word2vec 中的数学原理详解

三、word2vec能用来做什么?

相信这一点才是很多人真正关心的。我个人认为对于word2vec的定位应当是一项复杂工作或研究的前序,因为本质上它就是用来生成词向量的,有了词向量之后,我们就可以进行很多工作。我在这里列举几个小的应用方向,对于我尝试过的一些应用,每一项的源代码我就放在在第四部分的应用实例中。

  1. 用来做情感分析。情感分析是自然语言处理领域一个重要的分支,在进行过分词、词性标注、词嵌入之后,我们可以利用文本中的情感词(形容词、副词等),通过建立一个用来表征情感强度的数学模型去表示情感强度、极性,或者作为深度学习模型的输入自动判别情感极性。更进一步,我们针对产品特性可以分析客户的满意度,针对用户可以分析用户的情感态度。

2.用来计算文本相似度。既然我们已经获得了每一个文本的向量化表示,那么就可以利用文本向量空间,结合一些常见的相似度计算方法计算文本相似度。基于此,更进一步我们可以通过相似度排序去做推荐,可以是相似商品推荐、相似服务推荐、相似用户推荐。

3.用来挖掘相关词、关系挖掘…基于word2vec的论文已经发了上万篇,感兴趣的朋友可以搜搜看。

四、一个word2vec的训练实例(基于python的gensim库)。

  1. 首先处理原始数据,我的数据是csv文件格式的,存储形式如下图(用excel打开的,看起来整齐些)。由于word2vec的工作原理是取的某单词周围固定长度的单词进行训练的,所以训练文本不需要去除停用词。为了对比去除与不去除停用词的效果,对同一个文本我分别处理为no_stop.txt和stop.txt,分别代表没有停用词的和有停用词的文本。进行预处理的代码如下:

word2vec简介、原理、缺陷及应用。
-*- coding: utf8 -*-

import codecs
import csv
import re
import jieba
from gensim.models import word2vec
import logging

def formal_price(string):
    """用来标准化价格列数据的格式"""
    pattern = re.compile(r'[.\d+]')
    string_list = pattern.findall(string)
    formal_str = ''
    for s in string_list:
        formal_str += s
    return formal_str

def cut_words(data_address):
    """分词"""
    seg_text = []
    with codecs.open(data_address, encoding='utf-8') as f:
        for row in csv.DictReader(f, skipinitialspace=True):
            review_row = [row['model'], formal_price(row['price']), row['rate_whole']]
            row_cut = jieba.cut(row['review'])
            review_row.append(' '.join(row_cut).replace("\n", ""))
            seg_text.append(review_row)
    f.close()

    return seg_text

def remove_punctuation(seg_text):
    """删除总体评分标点符号和换行符,评论中的标点符号和数字"""
    review = []
    for row in seg_text:
        review_row = [row[0], row[1]]
        txt_1 = re.sub('[a-zA-Z"{}《》【】()::??!!''''"",,。.·、/&@#¥%...*()<>~\s]+', ' ', row[2])
        txt_2 = re.sub('[a-zA-Z"{}《》【】()::??!!''''"",,。.·、/&@#¥%...*()<>~\d ]+', ' ', row[3])
        review_row.append(txt_1)
        review_row.append(txt_2)
        review.append(review_row)

    return review

def remove_stop_words(review, stop_word_address):
    """去除停用词"""
    stop_words = [line.strip() for line in open(stop_word_address, encoding='utf-8').readlines()]
    for row in review:
        out_s = ''
        for s in row[-1]:
            if s not in stop_words:
                out_s += s
        row[-1] = out_s
    return review

def write_review(review, stop_words_address, stop_address, no_stop_address):
    """写出处理好的数据"""
    with open(stop_address, 'a', encoding='utf-8') as f:
        for row in review:
            f.write(row[-1])
            f.write("\n")
    f.close()

    no_stop_review = remove_stop_words(review, stop_words_address)
    with open(no_stop_address, 'a', encoding='utf-8') as f:
        for row in no_stop_review:
            f.write(row[-1])
            f.write("\n")
    f.close()

if __name__ == '__main__':
    segment_text = cut_words('audito_whole.csv')
    review_text = remove_punctuation(segment_text)
    write_review(review_text, 'stop_words.txt', 'no_stop.txt', 'stop.txt')

2.文本处理好以后在写入的时候一定要注意标明编码格式为utf-8,不然在后面训练模型的时候可能会出现编码错误问题。为什么是utf-8呢?这里我们可以查看gensim\utils.py里面的如下语句,可以看出应该是严格的utf-8编码才行。(详细解析见我的另一篇文章,链接:训练word2vec模型时碰到的两个问题

word2vec简介、原理、缺陷及应用。

3.训练文本准备好了,那么我们就可以训练模型啦。为了对比有无停用词的训练效果,我在代码中加入了对比,输出结果我也在下边给出来了,这里只用了两个简单的例子,求最相关的词和求相似度。

    # 模型训练主程序
    logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
    sentences_1 = word2vec.LineSentence('no_stop.txt')
    model_1 = word2vec.Word2Vec(sentences_1)
    sentences_2 = word2vec.LineSentence('no_stop.txt')
    model_2 = word2vec.Word2Vec(sentences_1)

    # model.wv.save_word2vec_format('test_01.model.txt', 'test_01.vocab.txt', binary=False)  # 保存模型,后面可直接调用
    # model = word2vec.Word2Vec.load("test_01.model")  # 调用模型

    # 计算某个词的相关词列表
    a_1 = model_1.wv.most_similar(u"空间", topn=20)
    print(a_1)
    a_2 = model_2.wv.most_similar(u"空间", topn=20)
    print(a_2)

    # 计算两个词的相关度
    b_1 = model_1.wv.similarity(u"空间", u"后座")
    print(b_1)
    b_2 = model_2.wv.similarity(u"空间", u"后座")
    print(b_2)

输出的结果如下:

&#x53BB;&#x9664;&#x505C;&#x7528;&#x8BCD;&#x7684;&#x7ED3;&#x679C;&#xFF1A;

a_1 = [('&#x50A8;&#x7269;', 0.9834187626838684), ('&#x540E;&#x5907;&#x7BB1;', 0.9637455940246582), ('&#x540E;&#x6392;', 0.9601808190345764), ('&#x4E58;&#x5750;', 0.9551437497138977), ('&#x5927;', 0.9503835439682007), ('&#x8EAB;&#x9AD8;', 0.9371528029441833), ('&#x4E2D;&#x95F4;', 0.9352531433105469), ('&#x7FD8;&#x90CE;', 0.9324509501457214), ('&#x524D;&#x6392;', 0.9304963946342468), ('&#x5F88;&#x5927;', 0.9212892651557922), ('&#x817F;&#x90E8;', 0.921205997467041), ('&#x5C0F;&#x70B9;', 0.9193155765533447), ('&#x5BBD;&#x655E;', 0.9164393544197083), ('&#x5750;', 0.9072367548942566), ('&#x591F;', 0.9008159637451172), ('&#x5730;&#x65B9;', 0.8993428945541382), ('&#x653E;', 0.8991451859474182), ('&#x4F38;&#x76F4;', 0.8987218141555786), ('&#x7EB5;&#x5411;', 0.8972957134246826), ('&#x5934;&#x90E8;', 0.8889123201370239)]

b_1 = 0.64887434

&#x6CA1;&#x6709;&#x53BB;&#x9664;&#x505C;&#x7528;&#x8BCD;&#x7684;&#x7ED3;&#x679C;&#xFF1A;

a_2 = [('&#x540E;&#x5907;&#x7BB1;', 0.975005567073822), ('&#x50A8;&#x7269;', 0.9747353196144104), ('&#x540E;&#x6392;', 0.9679359793663025), ('&#x4E58;&#x5750;', 0.9631804823875427), ('&#x5927;', 0.9558444619178772), ('&#x524D;&#x6392;', 0.9451466202735901), ('&#x7FD8;&#x90CE;', 0.9444395303726196), ('&#x4E2D;&#x95F4;', 0.9439687728881836), ('&#x5750;', 0.9406882524490356), ('&#x8EAB;&#x9AD8;', 0.9374524354934692), ('&#x5F88;&#x5927;', 0.9309908747673035), ('&#x653E;', 0.9283692240715027), ('&#x7FD8;&#x4E2A;', 0.9282485246658325), ('&#x5BBD;&#x655E;', 0.9257299304008484), ('&#x817F;&#x90E8;', 0.9193307757377625), ('&#x5934;&#x90E8;', 0.9171958565711975), ('&#x538B;&#x6291;', 0.9169957041740417), ('&#x7EB5;&#x5411;', 0.9168768525123596), ('&#x591F;', 0.9165091514587402), ('&#x9686;&#x8D77;', 0.9087113738059998)]

b_2 = 0.66836894

当然,这里有无去除停用词的结果不具有代表性,因为这只是一次输出的结果,其真正差异在哪里,还需要多跑几次才能知道。

并且,我也尝试了多次使用同一个训练文本训练模型,得出来的结果是不同的。在实际的研究中,要多跑几次取均值比较稳妥。

最后,上面两段代码直接拼起来就是完整的过程,tab空格我也没删。关于word2vec的调参,可以参考文章:Word2Vec的参数解释,这篇文章简要的介绍了每个参数的作用。

五、word2vec的缺陷有哪些?

1.word2vec的分布式假设即一个单词的意思由频繁出现在它上下文的词决定的,这一点从第二部分的工作原理中也可以看出来。这也就决定了word2vec最大的缺陷——无法解决一词多义问题。比如,”你别打扰他,他正在算账呢”和”今天算我倒霉,改天再找你算账”两句中的”算账”是不同的含义,但是在word2vec训练后得到的向量中,二者的表示方式是一样的。

2.word2vec没有考虑单词的上下文语义,仅仅是通过滑动窗口的形式取单词周围的几个单词作为样本进行训练,然而现实语言中单词的含义往往与上下文离不开关系。

以上均为个人观点,有不对的地方敬请指出,版权所有,转载请注明出处。

Original: https://blog.csdn.net/qq_43160348/article/details/124313872
Author: 小鲤鱼
Title: word2vec简介、原理、缺陷及应用。

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/548590/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球