【数据挖掘实战】—使用 word2vec 和 k-mean 聚类寻找相似城市

举报
Lingxw_w 发表于 2023/04/27 14:39:05 2023/04/27
【摘要】 目录理解业务理解数据准备数据与模型训练训练 Word2Vec 模型训练K-means模型 总结(使用 XGB 实现酒店信息消歧)中其实没有涉及太多的代码,主要是以介绍思路为主。在这一课时中,我将提供一个较为完整的代码,带领你亲自实践一下。理解业务在旅行场景下,城市——我们通常称为目的地,是一个很重要的信息。根据用户对于目的地的偏好,我们既可以把目的地作为一个特征用于推荐系统中,也可以把目的地...
目录

理解业务

理解数据

准备数据与模型训练

训练 Word2Vec 模型

训练K-means模型

 总结


(使用 XGB 实现酒店信息消歧)中其实没有涉及太多的代码,主要是以介绍思路为主。在这一课时中,我将提供一个较为完整的代码,带领你亲自实践一下。

理解业务

在旅行场景下,城市——我们通常称为目的地,是一个很重要的信息。根据用户对于目的地的偏好,我们既可以把目的地作为一个特征用于推荐系统中,也可以把目的地当作一个被推荐的信息直接推荐给用户。所以,我们有一个需求,就是把相似的目的地整理出来,然后可以通过这些相似目的地做相关推荐,或者是相关目的地的推荐。

理解数据

可以想到,这是一个比较典型的聚类问题,我们只要能够把相似的城市按照一定的相关性聚在一起,就可以完成我们的需求,当然具体的效果要根据结果不断地进行调整。那我们就来看一下我们的数据。

思来想去我们只有很多目的地的名字,但这些目的地并没有什么统一标准的特征可以给我们做向量,那么该怎么去给这些目的地计算相关性呢?

这时不禁想到,我们有很多用户写过游记,这些游记里总会出现各种各样的目的地名字,对于相似的目的地,那用户所写的内容也会有一定的相似性,不管是地理位置接近,还是消费价位类似,或者是可以玩的内容存在一定的相似性。

总之,我们可以靠这些内容把这些城市的名字关联起来,而且不同于结构化的信息,游记是用户自己来写的内容,里面对于目的地的认知也是用户的认知,所以如果我们能够从中发现关联性,再应用到用户身上也是比较合理的。比如说“三亚”如果只是按客观属性来划分,那应该是“海边”,但是很多用户去三亚,除了看海本身,还有家庭出游等,这些是只能从用户的角度才会产生的认知。

这里,我们就要用到一个 Word2Vec 算法,它可以学习输入的文本,并输出一个词向量模型,经过 Word2Vec 算法处理之后,每一个词都会变成一个预设长度的数值向量。这个算法会在后面的章节进行更详细的讲解,这里我们大概知道它的功能就可以了。下面我们进入到具体代码实现的环节,看看如何训练一个这样的模型。

准备数据与模型训练

准备数据
我们获取所有需要用到的文本数据,在这里使用了全量的游记文本数据。我们首先要对数据进行清洗,去除掉异常的数据,比如内容过短、获取失败,或者是存在特殊字符、使用纯英文 / 泰语写的游记,等等。

 完成了这个步骤之后我们要对文本内容进行分词,因为我期望 Word2Vec 最终构建的向量是词级别的。完成分词之后,我们把数据存储在文本文件中,其中每一行是一篇内容。

接下来就要训练我们的 Word2Vec模型了。

训练 Word2Vec 模型

这里我们使用了一个新的算法包:Gensim。不知道你是否还记得我在之前介绍过这个工具包,它主要用于从原始的非结构化文本信息中,通过无监督算法学习文本向量表达。这里面支持 TF-IDF、LSA、LDA 和 Word2Vec 等多种算法模型。来看一下代码。

import gensim #引入gensim
import os
import re
import sys
import multiprocessing #引入多线程操作
from time import time
class getSentence(object):
#初始化,获取文件路径
    def __init__(self, dirname):
        self.dirname = dirname

文本可以存储在多个文本文件中,存放在一个文件目录下,这里构建了一个迭代方法,循环读取目录下的所有文件。

我这里使用的文件目录为 traindata,在 traindata 下面有 31 个语料文件,其中每个有 1G 左右,如下图所示。

#构建一个迭代器
    def __iter__(self):
        for root, dirs, files in os.walk(self.dirname):
            for filename in files:
                file_path = root + '/' + filename
                for line in open(file_path):
                    try:
                        #清除异常数据,主要是去除空白符以及长度为0的内容
                        s_line = line.strip()
                        if s_line== "":
                                continue
                       #把句子拆成词
                        word_line = [word for word in s_line.split( )]
                        yield word_line
                    except Exception:
                        print("catch exception")
                        yield ""
if __name__ == '__main__':
#记录一个起始时间
    begin = time()
#获取句子迭代器
    sentences = getSentence("traindata")
#训练word2vec模型 使用句子迭代器作为语料的输入,设定的最终向量长度为200维;窗口长度为15;词的最小计数为10,词频少于10的词不会进行计算;使用并行处理
    model = gensim.models.Word2Vec(sentences,size=200,window=15,min_count=10, workers=multiprocessing.cpu_count())
#模型存储,这块记得先预先新建一个model路径,或者也可以增加一段代码来识别是否已经创建,如果没有则新建一个路径
    model.save("model/word2vec_gensim")
    model.wv.save_word2vec_format("model/word2vec_org",
                                  "model/vocabulary",
                                  binary=False)
    end = time()
#输出运算所用时间
    print ("Total procesing time: %d seconds" % (end - begin))

在正常的情况下,我们会在 model 路径下看到几个文件。其中比较重要的两个,一个 vocabulary 是词典文件,记录了出现过的词汇以及词汇出现的次数;一个 word2vec_gensim 是生成的向量文件。

通过上面的方法,我们成功获取到了很多词汇的向量,这里我的词汇量大概有 1000w 左右。但是我们这次所需要的是寻找相似城市,所以对于那些非城市名字的词汇就没有什么价值了。

于是我们这里使用我们自己的城市词库与词汇表进行匹配,对于没有在词汇表中出现过的城市名称也没有办法计算,要把这部分剔除掉。不用担心,如果这么多的语料都没有出现过的城市也一定是没有人去过的城市。

训练K-means模型

下面我们就可以开始训练我们的 K-means 模型了。像我们前面用过的一样,K-means 是在 sklearn 里面的一个模块。具体步骤如下所示。

import gensim
from sklearn.cluster import KMeans
from sklearn.externals import joblib
from time import time
#加载之前已经训练好的word2vec模型
def load_model():
    model = gensim.models.Word2Vec.load('../word2vec/model/word2vec_gensim')
    return model
#加载城市名称词库
def load_filterword():
    fd = open("mddwords.txt","r")
    filterword=[]
    for line in fd.readlines():
        line=line.strip()
        filterword.append(line)
    return filterword
if __name__=="__main__":
    start = time()
#加载word2vec模型
    model = load_model()
#加载词汇表
    filterword = load_filterword()
#输出词汇表长度
    print(len(filterword))
    wordvector = []
    filterkey={}
#获取我们的城市名称词库的词向量
    for word in filterword:
        wordvector.append(model[word])
        filterkey[word]=model[word]
   #输出词汇数量
    print(len(wordvector))
#训练K-means模型,这里代码设置的聚类数为2000,最大迭代次数为100,n_jobs设置的是有多少个任务同时在跑,这样可以进行多组实验来消除初始化点带来的影响
    clf = KMeans(n_clusters=2000,max_iter=100,n_jobs=10)
    s = clf.fit_predict(wordvector)
#把模型保存下来
    joblib.dump(clf,"kmeans_mdd2000.pkl")
    labels = clf.labels_
    labellist = labels.tolist()
    print(clf.inertia_)
#把所有城市名称的聚类标签保存下来
    fp = open("label_mdd2000",'w')
    fp.write(str(labellist))
    fp.close()
#把所有城市名称保存下来,其中顺序与聚类标签顺序一致
    fp1 = open("keys_mdd2000",'w')
    for key in filterkey:
        fp1.write(key+'\n')
    print("over")
    end = time()
    print("use time")
    print(end-start)

经过上面的步骤,我们就训练好了 K-means 模型,当然,经过反复尝试,最终确定的不是 2000 这个簇数量,而是使用了 100 个簇的结果。我们尝试了 50、100、200、500、1000、2000 等多个聚类的结果,经过我们最后的对比评估,100 个簇的时候效果较好,于是我们最终选择了这个模型。

下图是我从结果中抽了一些簇的 TOP 结果生成的图片,可以看到聚类的效果还是很不错的。比如右下角那一簇基本都是日本关西的城市名字,左下角基本都是川藏线上的地点。

有了已经训练好的模型,我们就知道了这些相似城市的名称以及它们所属的簇。接下来我们要做的,就是把这些数据存储到数据库中,并在具体的业务中进行应用了。

当然,随着时间的推移,在积累了一段时间的数据之后,我们还要对模型进行重新迭代,以期望获得更好的结果。

 总结

在这一节实践课程中,我着重介绍了整个模型训练环节的代码,其中主要写了两段代码,分别训练了 Word2Vec 模型和 K-means 模型。

在数据缺少标注的时候,聚类算法是十分常用的,它可以帮助我们了解数据情况。当然,聚类方法也存在一些局限,还需要在日常的工作中多加练习,不断积累自己的经验。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。