词嵌入模型(Word Embeddings)——Word2Vec

  • 简介:Word2Vec 是由 Google 团队提出的一种词嵌入方法,通过神经网络模型将词语映射到一个低维的连续向量空间中。你可以直接通过它训练生成词向量,也就是一个新的Word2Vec,也可以使用预训练好的词向量,也就是那里直接用。它有两种模型结构:CBOW(Continuous Bag of Words)和 Skip-Gram。
    1. CBOW(连续词袋模型):
      • 预测上下文词语的中心词语。输入是上下文词语,输出是中心词语。
      • 适合处理小数据集,训练速度较快。
    2. Skip-Gram:
      • 预测中心词语的上下文词语。输入是中心词语,输出是上下文词语。
      • 适合处理大数据集,能够生成更高质量的词向量。
  • 优点
    1. 能捕捉词语的语义关系和语法特性。
    2. 计算效率高,能够处理大规模语料库。
    3. 可以使用预训练的词向量进行迁移学习。
  • 缺点
    1. 需要大量的语料库进行训练。
    2. 无法直接处理未见过的词语(OOV,Out of Vocabulary)。

一、自训练Word2Vec

from gensim.models import Word2Vec
# 假设有一个大规模的语料库corpus,是一个列表,每个元素是一个分好词的文本列表
# 举个例子corpus,
corpus = [
    "这个产品非常好,我很喜欢。",
    "客服服务态度非常差。",
    "物流速度太慢了,等了好久。",
    "产品质量不错,但价格有点贵。",
    "售后服务很好,很满意。"
]
corpus = [list(jieba.cut(s)) for s in corpus]
# 设置 Word2Vec 模型的参数
vector_size = 100  # 设置词向量的维度
window = 5  # 窗口大小,控制上下文窗口的大小
min_count = 1  # 最小词频,过滤掉低频词
sg = 0  # 0表示使用 CBOW 模型,1示使用 Skip-Gram 模型

# 训练 Word2Vec 模型
model = Word2Vec(corpus, vector_size=vector_size, window=window, min_count=min_count, sg=sg)
# 举个例子
# 我们要对电影评论进行情感分析,假设我们已经有了一个基于大量电影评论数据训练好的Word2Vec模型。在这个模型中,每个词被映射为一个高维向量。这里假设的我们已有的模型,就是通过上面的方式训练出来的。训练好之后,再保存该模型。使用时加载该模型,实现文本向量化

但是这里你是否有个疑问,如果是大规模语料库,那全部需要一次放到这个列表里吗?能放得下或者合适吗?

首先讲能不能放的下,在 Python 中,列表(List)的大小理论上是受限于系统的可用内存。因此,如果系统内存足够大,并且你有足够的空间来存储数据,那么理论上可以将非常大的数据存储在一个列表中。

然而,实际上在处理大规模数据时,需要考虑的问题不仅仅是内存大小,还包括数据读取、处理效率以及系统资源的管理等方面。特别是在处理大规模语料库时,如果一次性加载整个语料库到内存中可能会遇到内存压力的问题和处理效率的问题

因此,对于大规模数据的处理,常见的方法包括:

  • 分批次处理: 将数据分成多个小批次进行处理,每次处理一部分数据,可以减少单次内存消耗,并且可以逐步积累模型的信息。

  • 增量学习: 如果数据不断增加,可以采用增量学习的方法,先训练部分数据,然后逐步添加新数据并更新模型。

  • 分布式计算: 对于非常大的数据集,可以考虑使用分布式计算框架(如Spark、TensorFlow分布式等),在多台计算机上并行处理和训练数据,以提高效率和处理能力。

下面代码实现分批次处理和增量学习的方法:

分批次处理的方法

from gensim.models import Word2Vec
import logging
import os
import tempfile
import shutil

# 设置日志级别以便查看进度
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

# 定义一个函数来分批次读取语料库文件
def read_corpus(file_path, chunk_size=10000):
    with open(file_path, 'r', encoding='utf-8') as f:
        lines = []
        for i, line in enumerate(f):
            lines.append(line.strip().split())  # 假设每行已经分好词了,按空格分割
            if i > 0 and i % chunk_size == 0:
                yield lines
                lines = []
        if lines:
            yield lines

# 示例:假设有一个大规模的语料库文件
corpus_file = 'path/to/your/large_corpus.txt'

# 定义 Word2Vec 模型的参数
vector_size = 100  # 词向量维度
window = 5  # 窗口大小
min_count = 5  # 最小词频,过滤掉低频词
workers = 4  # 使用多少个 CPU 核心来训练模型

# 用于保存中间模型文件的临时目录
temp_dir = tempfile.mkdtemp()

# 初始化 Word2Vec 模型
model = Word2Vec(vector_size=vector_size, window=window, min_count=min_count, workers=workers)

try:
    # 分批次读取语料库并训练模型
    for i, chunk in enumerate(read_corpus(corpus_file)):
        print(f"Processing chunk {i+1}")
        model.build_vocab(chunk, progress_per=10000)  # 建立词汇表
        model.train(chunk, total_examples=model.corpus_count, epochs=model.epochs)  # 训练模型

finally:
    # 删除临时目录
    shutil.rmtree(temp_dir)

# 保存训练好的模型
model.save('path/to/your/word2vec_model')

# 加载模型进行使用
# model = Word2Vec.load('path/to/your/word2vec_model')

示例解释:

  1. read_corpus 函数: 这个函数用于从语料库文件中按照 chunk_size(每批次的文本数量)分批次读取文本数据。在示例中,假设语料库文件中每行是一个已经分好词的文本。

  2. 模型参数设置: 设置了 Word2Vec 模型的参数,包括词向量维度(vector_size)、窗口大小(window)、最小词频(min_count)、并行处理的 CPU 核心数(workers)等。

  3. 模型训练过程:

  • 通过循环读取每个批次的文本数据,并使用 build_vocab 方法建立词汇表。
    使用 train 方法对每个批次的文本数据进行模型训练,通过 total_examples=model.corpus_count 和 epochs=model.epochs 来确保模型能够正确迭代。
  • 模型保存与加载: 在训练完成后,将训练好的 Word2Vec 模型保存到指定路径,并可以随时加载以便后续使用。
for i, chunk in enumerate(read_corpus(corpus_file)):
        print(f"Processing chunk {i+1}")
        model.build_vocab(chunk, progress_per=10000)  # 建立词汇表
        model.train(chunk, total_examples=model.corpus_count, epochs=model.epochs)
#这块详细的执行过程:
#第一次调用 read_corpus 生成器:
#生成器从文件的第1行到第10000行逐行读取数据,并将其存储在 lines 列表中。
#当 lines 列表中的行数达到 chunk_size(10000行)时,暂停read_corpus函数执行,生成器通过 yield 返回 lines 列表,然后清空 lines 列表以准备读取下一块数据。for 循环处理这个数据块。

#第二次调用 read_corpus 生成器:
#生成器从文件的第10001行到第20000行继续读取数据,并将其存储在 lines 列表中。
#同样,当 lines 列表中的行数再次达到 chunk_size(10000行)时,暂停read_corpus函数执行,生成器通过 yield 返回 lines 列表,然后再次清空 lines 列表以准备下一块数据。for 循环处理这个数据块。

#重复上述过程:
#每次生成器从上次停止的位置继续读取数据,直到文件结尾。
#当文件中剩余行数不足 chunk_size 时,生成器会返回剩余的所有行。

增量学习

from gensim.models import Word2Vec
import logging

# 设置日志级别以便查看进度
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

# 假设有一个大规模的语料库文件
corpus_file = 'path/to/your/large_corpus.txt'

# 定义 Word2Vec 模型的参数
vector_size = 100  # 词向量维度
window = 5  # 窗口大小
min_count = 5  # 最小词频,过滤掉低频词
workers = 4  # 使用多少个 CPU 核心来训练模型

# 初始化空的 Word2Vec 模型
model = Word2Vec(vector_size=vector_size, window=window, min_count=min_count, workers=workers)

# 逐步加载和训练数据
def read_and_train_model(model, corpus_file, chunk_size=10000):
    with open(corpus_file, 'r', encoding='utf-8') as f:
        lines = []
        for i, line in enumerate(f):
            lines.append(line.strip().split())  # 假设每行已经分好词了,按空格分割
            if i > 0 and i % chunk_size == 0:
                if model.corpus_count == 0:
                    # 第一次建立词汇表
                    model.build_vocab(lines)
                else:
                    # 更新词汇表
                    model.build_vocab(lines, update=True)
                # 训练模型
                model.train(lines, total_examples=len(lines), epochs=model.epochs)
                # 清空 lines 列表,以便下一个批次数据
                lines = []

        # 处理最后一个不完整的数据块
        if lines:
            if model.corpus_count == 0:
                model.build_vocab(lines)
            else:
                model.build_vocab(lines, update=True)
            model.train(lines, total_examples=len(lines), epochs=model.epochs)

# 开始增量学习
read_and_train_model(model, corpus_file)

# 保存训练好的模型
model.save('path/to/your/word2vec_model')

# 加载模型进行使用
# model = Word2Vec.load('path/to/your/word2vec_model')

二、预训练模型Word2Vec

需要先下载好模型,:确保下载的模型文件格式与你使用的加载函数兼容。通常,预训练的 Word2Vec 模型以二进制格式(.bin)或 Gensim 的模型格式(.model)保存。

from gensim.models import KeyedVectors

# 加载预训练的 Word2Vec 模型
model_path = 'path/to/your/pretrained/word2vec/model.bin'
model = KeyedVectors.load_word2vec_format(model_path, binary=True)

三、再次理解下Word2vec的原理,示例

再举个小示例理解下Word2vec的原理:

from gensim.models import Word2Vec
# 假设有一个大规模的语料库corpus,是一个列表,每个元素是一个分好词的文本列表,这里可能会存在一个问题,一次全部读取大规模的语料库用于训练,显然不合适,因此这里可以采用分批次读取
# 举个例子corpus,
corpus1 = [
    "这个产品非常好,我很喜欢。",
    "客服服务态度非常差。",
    "物流速度太慢了,等了好久。",
    "产品质量不错,但价格有点贵。",
    "售后服务很好,很满意。",
    "你是不是想当皇上",
    "你长了一幅帝王的面相"
]
corpus2 = [
    "这个产品非常好,我很喜欢。",
    "客服服务态度非常差。",
    "物流速度太慢了,等了好久。",
    "产品质量不错,但价格有点贵。",
    "你是不是想当帝王",
    "你是不是想当皇上",
    "皇上和帝王都是一国之君",
    "皇上和帝王在不同语境下使用",
    "皇上和帝王都拥有至高无上的权力",
	"许多人梦想成为皇上或帝王",
	"皇上和帝王的宫殿都非常壮丽",
	"历史上的皇上和帝王都是国家的象征",
	"皇上和帝王的权力都非常大",
	"皇上和帝王都需要治理国家",
	"皇上和帝王都是古代的最高统治者",
	"皇上和帝王的职责都是保护国家和人民",
	"许多故事中都有皇上和帝王的传奇",
	"皇上和帝王都生活在豪华的宫殿中"
]
corpus = [list(jieba.cut(s,True)) for s in corpus1]
print(corpus)
# 设置 Word2Vec 模型的参数
vector_size = 100  # 设置词向量的维度
window = 5  # 窗口大小,控制上下文窗口的大小
min_count = 1  # 最小词频,过滤掉低频词
sg = 0  # 0表示使用 CBOW 模型,1示使用 Skip-Gram 模型

# 训练 Word2Vec 模型
model = Word2Vec(corpus, vector_size=vector_size, window=window, min_count=min_count, sg=sg)
#model.wv['皇上'],model.wv['帝王']
model.wv.similarity('皇上','帝王'),
model.wv.most_similar('服务', topn=5)

我们都知道‘皇上’和‘帝王’从语义上理解,它们是很相似的,但是我们通过语料库corpus1训练模型,返回两个词相似性结果是-0.24778272,余弦相似度的取值范围通常在 [-1, 1] ,相似度为 0 表示两个向量是正交的,即它们之间没有相关性。该值靠近0,所以可以得出它们相似性很低。这与从语义上的结果是相悖的。那这里我们就可以根据Word2Vec的原理来理解下,为什么会是这样的结果。

Word2Vec模型是基于上下文的词向量表示方法,它通过分析大量文本语料中词语的共现关系来学习词向量。具体来说,Word2Vec模型使用了以下两种经典的方法来训练词向量:

  • Skip-gram
    模型:该模型试图根据目标词汇预测其周围的上下文词汇。因此,对于一个给定的中心词汇(如“皇上”),模型会尝试预测周围词汇(如上下文中的词语)。如果在训练过程中,“皇上”和“帝王”在上下文中没有经常同时出现,或“皇上”和“帝王”经常出现在不同的上下文中,模型会学习到不同的词向量表示。它们的词向量在模型中可能会很远,表现为不相似。
    例如:
    如果“皇上”常出现在“你是不是想当皇上”这样的上下文中,而“帝王”常出现在“你长了一幅帝王的面相”这样的上下文中,那么模型会将“皇上”的词向量调整为更适合“是不是”、“想当”等词,而将“帝王”的词向量调整为更适合“长了”、“面相”等词。

  • CBOW
    模型:该模型与Skip-gram相反,它根据上下文词汇预测中心词汇。同样地,如果在训练数据中,“皇上”和“帝王”的上下文词汇不重叠或出现模式不一致,它们的词向量在CBOW模型中也可能会较远,表现为不相似。

因此,Word2Vec模型并不直接考虑词语的语义关系,而是通过分析它们在上下文中的使用模式来学习词向量。如果想要更好地捕捉词语之间的语义相似性,可能需要使用更复杂的模型或者增加训练数据的多样性,以确保模型能够学习到更准确的语义信息。

当我们把训练语料更换为corpus2时,此时得到的相似性为0.90389603,表示两个词非常相似。虽然在语料库和参数上并不能确定训练出的模型具有通用性,但是从这个简单例子可以深入去理解下Word2vec的原理。

Logo

更多推荐