6-词向量应用

发布于:2025-07-06 ⋅ 阅读:(11) ⋅ 点赞:(0)
词的向量化方式
  • one-hot编码
  • word2vec
  • one-hot+word2vec基于窗口
    CBOW、SkipGram
    1.输出层使用one-hot向量会面临维度灾难,因为词表可能很大。
    2.收敛速度缓慢
  • Huffman树
  • Glove
负采样
  • 假设:词向量训练最终采取softmax作为激活函数,得到预测词的分布
    1
    如果V中元素很多,则该计算非常耗时
    反向传播时,所有权重一起更新非常耗时
  • 替代方案
    1-不再计算所有词的概率,只挑选某些词计算其概率
    2-使用sigmoid函数逐个计算概率,代替softmax
    3-只更新选择的部分词的权重矩阵
词向量训练总结

一、根据词与词之间关系的某种假设,制定训练目标
二、设计模型,以词向量为输入
三、随机初始化词向量,开始训练
四、训练过程中词向量作为参数不断调整,获取一定的语义信息
五、使用训练好的词向量做下游任务

词向量存在的问题
  • 词向量是“静态”的。每个词使用固定向量,没有考虑前后文
  • 一词多义的情况。西瓜 - 苹果 - 华为
  • 影响效果的因素非常多
    维度选择、随机初始化、skip-gram/cbow/glove、分词质量、词频截断、未登录词、窗口大小、迭代轮数、停止条件、语料质量等
  • 没有好的直接评价指标。常需要用下游任务来评价
词向量应用-寻找近义词
  • 依赖分词正确
  • 与A最接近的词是B,不代表B最接近的是A
  • 有时也会有反义词很相似
词向量应用-句向量或文本向量
  • 将一句话或一段文本分成若干个词
  • 找到每个词对应的词向量
  • 所有词向量加和求平均或通过各种网络模型,得到文本向量
  • 使用文本向量计算相似度或进行聚类等
词向量应用-KMeans

随机选择k个点作为初始质心
repeat
将每个点指派到最近的质心,形成k个簇
重新计算每个簇的质心
until
质心不发生变化
0

import numpy as np
import random
import sys
'''
Kmeans算法实现
原文链接:https://blog.csdn.net/qingchedeyongqi/article/details/116806277
'''
class KMeansClusterer:  # k均值聚类
    def __init__(self, ndarray, cluster_num):
        self.ndarray = ndarray
        self.cluster_num = cluster_num
        self.points = self.__pick_start_point(ndarray, cluster_num)

    def cluster(self):
        result = []
        for i in range(self.cluster_num):
            result.append([])
        for item in self.ndarray:
            distance_min = sys.maxsize
            index = -1
            for i in range(len(self.points)):
                distance = self.__distance(item, self.points[i])
                if distance < distance_min:
                    distance_min = distance
                    index = i# 记录当前点距离最小的质心下标
            result[index] = result[index] + [item.tolist()]# [index:[[item1],[item2],...,[itemN]]]
        # 计算出新的聚类中心
        new_center = []
        for item in result:
            new_center.append(self.__center(item).tolist())
        # 中心点未改变,说明达到稳态,结束递归
        if (self.points == new_center).all():
            sum = self.__sumdis(result)
            return result, self.points, sum
        # 聚类中心改变,更新聚类中心
        self.points = np.array(new_center)
        return self.cluster()

    def __sumdis(self,result):
        #计算总距离和
        sum=0
        for i in range(len(self.points)):
            for j in range(len(result[i])):
                sum+=self.__distance(result[i][j],self.points[i])
        return sum

    def __center(self, list):
        # 计算每一列的平均值
        return np.array(list).mean(axis=0)

    def __distance(self, p1, p2):
        #计算两点间距
        tmp = 0
        for i in range(len(p1)):
            tmp += pow(p1[i] - p2[i], 2)
        return pow(tmp, 0.5)

    def __pick_start_point(self, ndarray, cluster_num):
        if cluster_num < 0 or cluster_num > ndarray.shape[0]:
            raise Exception("簇数设置有误")
        # 取点的下标
        indexes = random.sample(np.arange(0, ndarray.shape[0], step=1).tolist(), cluster_num)
        points = []
        for index in indexes:
            points.append(ndarray[index].tolist())
        return np.array(points)

x = np.random.rand(100, 8)
kmeans = KMeansClusterer(x, 10)
result, centers, distances = kmeans.cluster()
print(result)
print(centers)
print(distances)
  • KMeans优点:
    速度很快,可以支持很大量的数据
    样本均匀特征明显的情况下,效果不错
  • KMeans缺点:
    人为设定聚类数量
    初始化中心影响效果,导致结果不稳定
    对于个别特殊样本敏感,会大幅影响聚类中心位置
    不适合多分类或样本较为离散的数据
  • KMeans一些使用技巧:
    先设定较多的聚类类别
    聚类结束后计算类内平均距离
    排序后,舍弃类内平均距离较长的类别
    计算距离时可以尝试欧式距离、余弦距离或其他距离
    短文本的聚类记得先去重,以及其他预处理

1

#基于训练好的词向量模型进行聚类
#聚类采用Kmeans算法
import math
import jieba
import numpy as np
from gensim.models import Word2Vec
from sklearn.cluster import KMeans
from collections import defaultdict

#输入模型文件路径
#加载训练好的模型
def load_word2vec_model(path):
    model = Word2Vec.load(path)
    return model

def load_sentence(path):
    sentences = set()
    with open(path, encoding="utf8") as f:
        for line in f:
            sentence = line.strip()
            sentences.add(" ".join(jieba.cut(sentence)))
    print("获取句子数量:", len(sentences))
    return sentences

#将文本向量化
def sentences_to_vectors(sentences, model):
    vectors = []
    for sentence in sentences:
        words = sentence.split()  #sentence是分好词的,空格分开
        vector = np.zeros(model.vector_size)
        #所有词的向量相加求平均,作为句子向量
        for word in words:
            try:
                vector += model.wv[word]
            except KeyError:
                #部分词在训练中未出现,用全0向量代替
                vector += np.zeros(model.vector_size)
        vectors.append(vector / len(words))
    return np.array(vectors)


def main():
    model = load_word2vec_model("model.w2v") #加载词向量模型
    sentences = load_sentence("titles.txt")  #加载所有标题
    vectors = sentences_to_vectors(sentences, model)   #将所有标题向量化

    n_clusters = int(math.sqrt(len(sentences)))  #指定聚类数量
    print("指定聚类数量:", n_clusters)
    kmeans = KMeans(n_clusters)  #定义一个kmeans计算类
    kmeans.fit(vectors)          #进行聚类计算

    sentence_label_dict = defaultdict(list)
    for sentence, label in zip(sentences, kmeans.labels_):  #取出句子和标签
        sentence_label_dict[label].append(sentence)         #同标签的放到一起

    #计算类内距离
    density_dict = defaultdict(list)
    for vector_index, label in enumerate(kmeans.labels_):
        vector = vectors[vector_index]   #某句话的向量
        center = kmeans.cluster_centers_[label]  #对应的类别中心向量
        distance = cosine_distance(vector, center)  #计算距离
        density_dict[label].append(distance)    #保存下来
    for label, distance_list in density_dict.items():
        density_dict[label] = np.mean(distance_list)   #对于每一类,将类内所有文本到中心的向量余弦值取平均
    density_order = sorted(density_dict.items(), key=lambda x:x[1], reverse=True)  #按照平均距离排序,向量夹角余弦值越接近1,距离越小

    #按照余弦距离顺序输出
    for label, distance_avg in density_order:
        print("cluster %s , avg distance %f: " % (label, distance_avg))
        sentences = sentence_label_dict[label]
        for i in range(min(10, len(sentences))):  #随便打印几个,太多了看不过来
            print(sentences[i].replace(" ", ""))
        print("---------")

#向量余弦距离
def cosine_distance(vec1, vec2):
    vec1 = vec1 / np.sqrt(np.sum(np.square(vec1)))  #A/|A|
    vec2 = vec2 / np.sqrt(np.sum(np.square(vec2)))  #B/|B|
    return np.sum(vec1 * vec2)

#欧式距离
def eculid_distance(vec1, vec2):
    return np.sqrt((np.sum(np.square(vec1 - vec2))))

if __name__ == "__main__":
    main()