利用t-SNE可视化Glove向量

一、GloVe词向量简介

GloVe:全称Global Vectors for Word Representations。其文献[2]是2014年在EMNLP会议上提出来的。其结合了词向量矩阵分解的思想对原始语料进行预训练,得到了低维、连续、稀疏的表示形式。对预训练后的词向量进行可视化可以发现发现某些词与词之间的联系。
(附:2种常用于估计词向量的方法,
1是基于神经网络的语言模型和word2vec的词向量预训练方法,其本质都是利用文本中词与词在局部上下文中的共现信息作为自监督学习信号。
2.基于矩阵分解的方法,如LSA(Latent Semantic Analysis潜在语义分析)等。其首先对语料进行统计分析,并获得含有全局统计信息的“词-上下文”共现矩阵,然后利用奇异值分解(Singular Value Decomposition,SVD)对该矩阵进行降维,进而得到词的低维表示。然而, 传统的矩阵分解方法得到的词向量不具备良好的几何性质,因此,GloVe词向量就提出来了,其结合了词向量以及矩阵分解的思想,结果较为好)[1]

二、t-SNE简介

t-SNE,全称t-distributed stochastic neighbor embedding。具体过程我不太了解, 但是其可以将高维数据压缩成低维输出。
如输入是(n_sample, n_dimension),输出是(n_sample, 2)。该过程将n_sample个维度为n_dimension的样本压缩成2维。

三、预训练的GloVe词向量

预训练好的词向量放在了官网上,请自行下载:
(网址:https://nlp.stanford.edu/projects/glove/)
利用t-SNE可视化Glove向量_第1张图片
如上图所示,根据不同的语料有不同的预训练模型,如有从Wikipedia、Twitter、以及普通爬取的语料信息。我选择了第一个,即从Wikipedia爬取的数据。
解压改文件后,选择了glove.6B.50d.txt(即从6B个token中提取,有400k的vocab,每个vocab是50维的)。在这里插入图片描述
任意提取该txt文档的5行看一看,发现每行第1个数是token,剩下的50个float类型的数字是该token的50维表示。

were 0.73363 -0.74815 0.45913 -0.56041 0.091855 0.33015 -1.2034 -0.15565 -1.1205 -0.5938 0.23299 -0.46278 -0.34786 -0.47901 0.57621 -0.16053 -0.26457 -0.13732 -0.91878 -0.65339 0.05884 0.61553 1.2607 -0.39821 -0.26056 -1.0127 -0.38517 -0.096929 -0.11701 -0.48536 3.6902 0.30744 0.50713 -0.6537 0.80491 0.23672 0.61769 0.030195 -0.57645 0.60467 -0.63949 -0.11373 0.84984 0.41409 0.083774 -0.28737 -1.4735 -0.20095 -0.17246 -1.0984
not 0.55025 -0.24942 -0.0009386 -0.264 0.5932 0.2795 -0.25666 0.093076 -0.36288 0.090776 0.28409 0.71337 -0.4751 -0.24413 0.88424 0.89109 0.43009 -0.2733 0.11276 -0.81665 -0.41272 0.17754 0.61942 0.10466 0.33327 -2.3125 -0.52371 -0.021898 0.53801 -0.50615 3.8683 0.16642 -0.71981 -0.74728 0.11631 -0.37585 0.5552 0.12675 -0.22642 -0.10175 -0.35455 0.12348 0.16532 0.7042 -0.080231 -0.068406 -0.67626 0.33763 0.050139 0.33465
this 0.53074 0.40117 -0.40785 0.15444 0.47782 0.20754 -0.26951 -0.34023 -0.10879 0.10563 -0.10289 0.10849 -0.49681 -0.25128 0.84025 0.38949 0.32284 -0.22797 -0.44342 -0.31649 -0.12406 -0.2817 0.19467 0.055513 0.56705 -1.7419 -0.91145 0.27036 0.41927 0.020279 4.0405 -0.24943 -0.20416 -0.62762 -0.054783 -0.26883 0.18444 0.18204 -0.23536 -0.16155 -0.27655 0.035506 -0.38211 -0.00075134 -0.24822 0.28164 0.12819 0.28762 0.1444 0.23611
who -0.19461 -0.051277 0.26445 -0.57399 1.0236 0.58923 -1.3399 0.31032 -0.89433 -0.13192 0.21305 0.29171 -0.66079 0.084125 0.76578 -0.42393 0.32445 0.13603 -0.29987 -0.046415 -0.74811 1.2134 0.24988 0.22846 0.23546 -2.6054 0.12491 -0.94028 -0.58308 -0.32325 2.8419 0.33474 -0.33902 -0.23434 0.37735 0.093804 -0.25969 0.68889 0.37689 -0.2186 -0.24244 1.0029 0.18607 0.27486 0.48089 -0.43533 -1.1012 -0.67103 -0.21652 -0.025891
they 0.70835 -0.57361 0.15375 -0.63335 0.46879 -0.066566 -0.86826 0.35967 -0.64786 -0.22525 0.09752 0.27732 -0.35176 -0.25955 0.62368 0.60824 0.34905 -0.27195 -0.27981 -1.0183 -0.1487 0.41932 1.0342 0.17783 0.13569 -1.9999 -0.56163 0.004018 0.60839 -1.0031 3.9546 0.68698 -0.53593 -0.7427 0.18078 0.034527 0.016026 0.12467 -0.084633 -0.10375 -0.47862 -0.22314 0.25487 0.69985 0.32714 -0.15726 -0.6202 -0.23113 -0.31217 -0.3049

4. 编程用t-SNE可视化Glove向量。

scikit-learn库中含有t-SNE的实现,结合官方给出的代码,这里实现了t-SNE可视化Glove词向量。另外由于token太多时图片显示杂乱和算力限制, 这里只显示前100个Glove向量。感兴趣的读者还可以在该代码基础上提取特定的token和不同的token数量进行可视化。

代码如下:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import offsetbox
from sklearn.preprocessing import MinMaxScaler
from sklearn.manifold import TSNE
from time import time
from sklearn.datasets import load_digits

digits = load_digits(n_class=6)

# 1.读取GloVe向量数据库,取6种类。共400,000个token,每个token是50维。
glove_txt = "/home/lwq/20210922/glove.6B.50d.txt"
with open(glove_txt) as f:
    lines = f.readlines()
    lines = lines[:100]
    X = []
    y = []
    for line in lines:
        now = line.split(' ')
        y.append(now[0])
        X.append(now[1:])
    X = np.array(X)
    y = np.array(y)

n_samples, n_features = X.shape


# 2.编写绘画函数,对输入的数据X进行画图。
def plot_embedding(X, title, ax):
    X = MinMaxScaler().fit_transform(X)

    shown_images = np.array([[1.0, 1.0]])  # just something big
    for i in range(X.shape[0]):
        # plot every digit on the embedding
        ax.text(
            X[i, 0],
            X[i, 1],
            str(y[i]),
            # color=plt.cm.Dark2(y[i]),
            fontdict={"weight": "bold", "size": 9},
        )
        '''
        # show an annotation box for a group of digits
        dist = np.sum((X[i] - shown_images) ** 2, 1)
        if np.min(dist) < 4e-3:
            # don't show points that are too close
            continue
        shown_images = np.concatenate([shown_images, [X[i]]], axis=0)
        imagebox = offsetbox.AnnotationBbox(
            offsetbox.OffsetImage(digits.images[i], cmap=plt.cm.gray_r), X[i]
        )
        ax.add_artist(imagebox)
        '''
    ax.set_title(title)
    ax.axis("off")


# 3.选择要用那种方式对原始数据编码(Embedding),这里选择TSNE。
#  n_components = 2表示输出为2维,learning_rate默认是200.0,
embeddings = {
    "t-SNE embeedding": TSNE(
        n_components=2, init='pca', learning_rate=200.0, random_state=0
    ),
}


# 4.根据字典里(这里只有TSNE)的编码方式,生成压缩后的编码矩阵
# 即把每个样本生成了2维的表示。维度由原来的50位变成了2位。
# Input: (n_sample, n_dimension)
# Output: (n_sample, 2)

projections, timing = {}, {}
for name, transformer in embeddings.items():
    # 原作者的dict里有多种比较方法,我只用了t-SNE,需要的可以查询原链接:https://scikit-learn.org/stable/auto_examples/manifold/plot_lle_digits.html#manifold-learning-on-handwritten-digits-locally-linear-embedding-isomap
    if name.startswith("Linear Discriminant Analysis"):
        data = X.copy()
        data.flat[:: X.shape[1] + 1] += 0.01  # Make X invertible
    else:
        data = X

    print(f"Computing {name}...")
    start_time = time()
    print(data.shape, type(data.shape))
    projections[name] = transformer.fit_transform(data, y)
    timing[name] = time() - start_time



# 6.把编码矩阵输出到二维图像中来。
fig, ax = plt.subplots()
title = f"{name} (time {timing[name]:.3f}s)"
plot_embedding(projections[name], title, ax)
plt.show()

结果如下:
利用t-SNE可视化Glove向量_第2张图片

上图 :利用t-SNE对前100个GloVe词向量可视化结果

从图中我们可以发现,最上面的标点符号聚到了一起,表明他们是相似的。his、her、he、who分到了一起。year和years分到了一起,表现了一些语义的相似性。这在不同的corpus上、不同维度上可能显示的结果是不一样的。

根据结果这表明Glove词向量是具有一定的相关性的。但是GloVe词向量是静态词向量,不能解决NLP任务中的“一词多义”问题。因此,可以考虑一些动态的词向量表示,如Elmo模型、以及现在特别流行的GPT、BERT等预训练模型。


参考文献

1.《自然语言处理:基于预训练模型的方法》车万翔等著,2021年。
2.Jeffrey Pennington, Richard Socher, and Christopher D. Manning. 2014. GloVe: Global Vectors for Word Representation.
3.https://scikit-learn.org/stable/auto_examples/manifold/plot_lle_digits.html#manifold-learning-on-handwritten-digits-locally-linear-embedding-isomap

你可能感兴趣的:(机器学习,NLP,自然语言处理,机器学习,人工智能)