首页 > 编程问答 >为什么我在 Python 中的 Skip-Gram 实现会产生不正确的结果?

为什么我在 Python 中的 Skip-Gram 实现会产生不正确的结果?

时间:2024-08-02 15:17:36浏览次数:14  
标签:python word2vec

我正在使用 Python 实现 Word2Vec 的 Skip-Gram 模型。然而,正如生成的嵌入及其可视化所示,我的模型似乎无法正常工作。这是嵌入的 3D 图的示例,它显示单词聚集在一起并重叠,因此很难区分它们:

我怀疑问题在于我的实现而不是绘图函数。

import numpy as np
from nltk.corpus import stopwords
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import re

np.random.seed(10)

def softmax(x):
    '''
                 (xi - max{x1,x2,...,xv})
                e
    xi =    --------------
                  (xj - max{x1,x2,...,xv})
             ∑j  e
    '''
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

class SkipGram:
    def __init__(self,ws=2,dim=8) -> None:
        self.X = []
        self.N = dim
        self.Y = []
        self.window_size = ws
        self.alpha = 0.1
        self.vocab = {}
        self.vocab_size = 0
        
    def __create_vocabulary(self,corpus):
        stop_words = set(stopwords.words("english"))
        filtered_corpus = []
        self.vocab_size = 0
        for i,sentence in enumerate(corpus):
            if isinstance(sentence,str) :
                corpus[i] = sentence.split()
            filtered_corpus.append([])
            j = 0
            for word in corpus[i]:
                w = re.sub(r'[^a-z]+','',word.lower())
                if w != '' and w not in stop_words:
                    corpus[i][j] = w
                    filtered_corpus[i].append(w)
                else:
                    continue
                if corpus[i][j].lower() not in self.vocab:
                    self.vocab[corpus[i][j].lower()] = self.vocab_size
                    self.vocab_size += 1
                j += 1
        return filtered_corpus
        
    def __create_context_and_center_words(self,processed_corpus):
        for sentence in processed_corpus:
            for i,word in enumerate(sentence):
                center_word = np.zeros((self.vocab_size,1))
                center_word[self.vocab[word]][0] = 1
                context = np.zeros((self.vocab_size,1))
                
                for j in range(i-self.window_size,i + self.window_size + 1):
                    if j != i and j >= 0 and j < len(sentence):
                        context[self.vocab[sentence[j]]][0] += 1
                self.X.append(center_word)
                self.Y.append(context)
        self.X = np.array(self.X)
        self.Y = np.array(self.Y)
                
        
    def initialize(self,corpus):
        corpus = self.__create_vocabulary(corpus)
        self.__create_context_and_center_words(corpus)
        self.W1 = np.random.rand(self.vocab_size,self.N)
        self.W2 = np.random.rand(self.N,self.vocab_size)
    
    def feed_forward(self,x):
        h = np.dot(self.W1.T,x) # N V . V 1 -> N 1
        u = np.dot(self.W2.T,h) # V N . N 1 -> V 1
        y = softmax(u)
        return h,u,y
    
    def backpropagate(self,x,y_actual,y_result,h):
        e = y_result - y_actual # V 1
        dw2 = np.dot(h,e.T) # N 1 . 1 V ->  N V
        eh = np.dot(self.W2,e) # N x V . V x 1 ->  N x 1
        dw1 = np.dot(x,eh.T) # V x 1 . 1 x N -> V x N
        return dw1,dw2
        
    def train(self,epochs):
        for i in range(epochs):
            loss = 0
            dw1,dw2 = np.zeros_like(self.W1),np.zeros_like(self.W2)
            for j in range(len(self.X)):
                h,_,y = self.feed_forward(self.X[j])
                a,b = self.backpropagate(self.X[j],self.Y[j],y,h)
                dw1 += a
                dw2 += b
                loss -=  np.sum(self.Y[j] * np.log(y+1e-08))
            loss /= len(self.X)
            [dw1,dw2] = [dw1/len(self.X), dw2/len(self.X)]
            self.W1 -= self.alpha * dw1
            self.W2 -= self.alpha * dw2
            print(f'Epoch : {i+1}, Loss = {loss}')
            
    def get_similar_words(self,word,n):
        if word in self.vocab:
            x = np.zeros((self.vocab_size,))
            x[self.vocab[word]] = 1
            _,_,y = self.feed_forward(x)
            output = {}
            for i in range(self.vocab_size):
                output[y[i]] = i
            words = {i:word for i,word in enumerate(self.vocab.keys())}
            context = []
            for k in sorted(output,reverse=True):
                context.append(words[output[k]])
                if len(context) == n:
                    break
            return context
        else:
            print("Given Word not found")
            
    def get_vector(self,word):
        return self.W1[self.vocab[word]]
            
    def plot(self):
        tsne = TSNE(n_components=3,random_state=0,perplexity=self.vocab_size-1)
        vectors_3d = tsne.fit_transform(self.W1)
        fig = plt.figure(figsize=(12,8))
        ax = fig.add_subplot(111,projection='3d')
        ax.scatter(vectors_3d[:,0],vectors_3d[:,1],vectors_3d[:,2],marker='o',edgecolors='k')
        for word,i in self.vocab.items():
            ax.text(vectors_3d[i,0],vectors_3d[i,1],vectors_3d[i,2],word)
        ax.set_title('Word2Vec Word Embeddings')
        ax.set_xlabel('Dimension 1')
        ax.set_ylabel('Dimension 2')
        ax.set_zlabel('Dimension 3')
        plt.show()
        
#main.py
from nltk.corpus import gutenberg

corpus = gutenberg.sents()[:40]
w2v = SkipGram(3,20)
w2v.initialize(corpus)
w2v.train(200)
w2v.plot()

Output of my model

I已尝试调整学习率并使用不同的值初始化权重,但问题仍然存在。

任何人都可以帮助我确定我的实现可能出现的问题吗?

查看了用于生成词汇和上下文单词的代码。 检查权重初始化和学习率设置。


代码中有几个地方可能会导致 Skip-gram 实现出现问题,从而导致嵌入聚集在一起,并且性能不佳:

  1. Softmax 函数: 的 softmax 实现虽然在技术上是正确的,但可能会遇到数值稳定性问题。指数项会导致非常大的数字,从而导致上溢。为了解决这个问题,请从指数中减去输入向量的最大值。以下是更新后的 softmax 函数:
def softmax(x):
    """计算 softmax 以提高数值稳定性"""
    exp_x = np.exp(x - np.max(x))
    return exp_x / exp_x.sum(axis=0) 
  1. 反向传播中的维度: 需要仔细检查反向传播步骤中矩阵和向量的维度,尤其是在计算 dw2 eh 时。确保维度在这些计算中对齐,并且正在更新正确的权重。当前,的代码似乎正在转置应该转置的内容。请尝试以下更改:
    def backpropagate(self,x,y_actual,y_result,h):
        e = y_result - y_actual 
        dw2 = np.dot(h,e.T) 
        eh = np.dot(self.W2,e) 
        dw1 = np.dot(x,eh.T) 
        return dw1,dw2
  1. 学习率: 虽然提到过调整学习率,但它仍然可能是罪魁祸首。过高的学习率会导致模型在最佳值附近波动,而过低的学习率会导致收敛速度太慢。尝试使用不同的学习率值(例如,在 0.1 到 0.001 之间)进行实验,以找到最适合数据集的学习率。还可以考虑使用随时间衰减的学习率。

  2. 训练数据: 此代码示例仅使用古登堡语料库中的一小部分句子(40 个句子)进行训练。对于 Word2Vec 和 Skip-gram 模型,通常需要更大的数据集才能学习有意义的词嵌入。尝试增加训练数据量,看看是否可以提高性能。

  3. 嵌入维度: 使用的是大小为 8 的嵌入。虽然这对于较小的数据集可能足够了,但增加嵌入维度通常可以提高性能,因为它允许模型捕获更多单词之间的关系。尝试将维度增加到 50 或 100。

  4. 窗口大小: 窗口大小确定在预测目标词时要考虑的上下文词数。虽然窗口大小为 3 是一个合理的默认值,但也可以尝试不同的值,以查看是否可以提高性能。

  5. TSNE 可视化: 虽然 TSNE 是可视化高维数据的有用工具,但它并不总是嵌入质量的完美表示。如果在解决上述问题后嵌入仍然聚集在一起,则可能需要尝试使用不同的可视化技术,例如 PCA 或使用更大的数据集。

其他提示:

  • 考虑在训练过程中监控验证集上的损失或指标,以了解模型是否过拟合训练数据。
  • 探索使用 gensim TensorFlow 等库中可用的 Word2Vec 实现。这些实现通常经过优化,并且包含诸如负采样等功能,可以提高性能。

通过解决这些问题并尝试不同的超参数,应该能够改进 Skip-gram 实现并生成更有意义的词嵌入。

标签:python,word2vec
From: 78824197

相关文章

  • 基于SpringBoot的智能购房推荐系统-09040(免费领源码)可做计算机毕业设计JAVA、PHP、爬
    Springboot智能购房推荐系统摘 要近年来随着我国经济的高速发展,房地产业也随之蓬勃发展,尤其是最近国家新出台的房改政策。鼓励居民购房,这对房产公司无疑是一个极好的发展势头。尤为重要的是,近几年随着信息技术和电子商务的快速发展,许多企业都开发了自己房产信息软件。智......
  • Python 基础教学 - 开发规范
    Python基础教学-开发规范一、引言在Python编程中,遵循良好的开发规范是编写高质量、可维护代码的关键。本文将为您详细介绍Python开发中的一些重要规范,帮助您养成良好的编程习惯。二、代码布局缩进使用4个空格进行缩进,避免使用制表符。示例:ifTrue:p......
  • Python基础学习笔记(一)
    文章目录一、下载Python二、变量三、数据类型四、运算符五、语句六、容器类型七、函数function八、常用API九、面向对象类的创建:创建对象:实例成员:实例方法:类成员:静态方法:十、三大特征:封装、继承、多态十一、六大原则:Python基础学习笔记(二)一、下载Python官网:https......
  • 随机森林的可解释性分析(含python代码)
    随机森林的可解释性分析1.引言可解释性的重要性2.随机森林的原理2.1基本原理:2.2随机森林的实现3.随机森林的可解释性分析3.1特征重要性3.2特征重要性3.3SHAP值3.4部分依赖图(PDP)3.5交互特征效应3.6变量依赖图4.结论5.参考文献1.引言在机器学习领域,随机森林......
  • 【Python】模块
    1.模块的概念Python中有一种方法可以把定义放在一个文件里面,并在脚本或者解释器的交互实例中使用它们。这样的文件被称作Python的模块。2.自定义模块在Python中,自定义模块有两个作用,一个作用是规范代码,让代码更容易阅读;另一个作用是方便其他程序使用已经编写好的代码,提高开......
  • 计算机毕业设计-基于python的房屋租赁系统【源码+文档+PPT】
    精彩专栏推荐订阅:在下方主页......
  • 基于python高考志愿填报辅助系统【源码+文档+PPT】
    精彩专栏推荐订阅:在下方主页......
  • nb python 语法
    在bytesized32的开源代码里面看到了一个玄学内容。whileTrue:try:stream=call_gpt(stream=True,model=model,messages=messages,**kwargs)pbar=tqdm(stream,unit="token",total=kwargs.get("max_tokens",8*1024),leave=......
  • Python:下载数据集
    打开网站:搜索        ​​​​​​https://www.kaggle.com直接下载即可(要登陆注册哦),下载完成一定要放到桌面哦,因为读取的是当前目录 在网页上打开上一篇文章所讲的HelloWorld,如果没有安装请跳转http://t.csdnimg.cn/NDJpG输入:importpandasaspddf=pd.read......
  • (四)Python中的字符串类型及操作
    一、字符串类型的表示在Python中,字符串可以使用单引号(')、双引号(")、三引号('''或""")来表示#使用单引号string1='Hello,World!'#使用双引号string2="Hello,World!"#使用三引号string3='''Hello,World!''&......