zoukankan      html  css  js  c++  java
  • 【计算语言学实验】基于 Skip-Gram with Negative Sampling (SGNS) 的汉语词向量学习和评估

    一、概述

    训练语料来源:维基媒体 https://dumps.wikimedia.org/backup-index.html 汉语数据

    用word2vec训练词向量,并用所学得的词向量,计算 pku_sim_test.txt 文件中每行两个词间的余弦距离作为两词相似度,并输出到文件中。

    二、数据准备及预处理

    语料库的下载地址:https://dumps.wikimedia.org/zhwiki/20191120/zhwiki-20191120-pages-articles-multistream.xml.bz2

    语料库文章的提取

    下载完成之后,解压缩得到的是一个xml文件,里面包含了许多的文章,也有许多的日志信息。此实验只需要提取xml文件里面的文章就可以了。
    可以通过工具WikiExtractor来提取xml文件中的文章。先将整个WikiExtractor项目clone或者下载到本地,通过命令行窗口来运行,命令如下:(每个文件分割的大小为500M)

    > git init
    > git clone https://github.com/attardi/wikiextractor
    > python .wikiextractorWikiExtractor.py -b 500M -o zhwiki zhwiki-20190401-pages-articles-multistream.xml.bz2
    

    使用WikiExtractor提取文章,会在指定目录下产生一个AA的文件夹,里面会包含几个文件。

    中文简体和繁体的转换

    因为维基百科语料库中的文章内容里面的简体和繁体是混乱的,所以我们需要将所有的繁体字转换成为简体。这里我们利用OpenCC来进行转换。
    OpenCC项目地址: https://github.com/BYVoid/OpenCC, 将OpenCC安装到本地电脑后,执行命令:(t2s.json: 繁体转简体)

    > opencc -i .zhwikiAAwiki_00
    -o .zhwikiBBwiki_00
    -c D:opencc-1.0.4-win32opencc-1.0.4shareopencc	2s.json
    

    正则表达式提取文章内容并进行分词:

    当前目录下的segmentWords.py中的代码共执行了三个步骤的操作:
    (1)过滤标签内容:使用WikiExtractor提取的文章,会包含许多的,所以需要将这些不相关的内容通过正则表达式来去除。
    (2)分词及去停用词:通过jieba对文章进行分词,在分词的时候还需要将停用词去除。
    (3)合并保存文件:将分割之后的文章保存到文件中,每一行表示一篇文章,每个词之间使用空格进行分隔。

    Jieba项目地址:https://github.com/fxsjy/jieba

    #segmentWords.py
    
    import logging
    import jieba
    import os
    import re
    
    def get_stopwords():
        logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s',level=logging.INFO)
        #加载停用词表
        stopword_set = set()
        with open("./stop_words/stopwords.txt",'r',encoding="utf-8") as stopwords:
            for stopword in stopwords:
                stopword_set.add(stopword.strip("
    "))
        return stopword_set
    
    '''
    使用正则表达式解析文本
    '''
    def parse_zhwiki(read_file_path,save_file_path):
        #过滤掉<doc>
        regex_str = "[^<doc.*>$]|[^</doc>$]"
        file = open(read_file_path,"r",encoding="utf-8")
        #写文件
        output = open(save_file_path,"w+",encoding="utf-8")
        content_line = file.readline()
        #获取停用词表
        stopwords = get_stopwords()
         #定义一个字符串变量,表示一篇文章的分词结果
        article_contents = ""
        cnt = 0
        while content_line:
            match_obj = re.match(regex_str,content_line)
            content_line = content_line.strip("
    ")
            if len(content_line) > 0:
                if match_obj:
                    #使用jieba进行分词
                    words = jieba.cut(content_line,cut_all=False)
                    for word in words:
                        if word not in stopwords:
                            article_contents += word+" "
                else:
                    if len(article_contents) > 0:
                        output.write(article_contents+"
    ")
                        article_contents = ""
            cnt += 1
            if (cnt % 10000 == 0):
                print("已处理", cnt/10000, "万行")
            content_line = file.readline()
        output.close()
    
    '''
    将维基百科语料库进行分类
    '''
    def generate_corpus():
        zhwiki_path = "./zhwiki/BB"
        save_path = "./zhwiki/BB"
        for i in range(3):
            print("开始处理第", i, "个文件")
            file_path = os.path.join(zhwiki_path,str("wiki_0%s"%str(i)))
            parse_zhwiki(file_path,os.path.join(save_path,"wiki_corpus0%s"%str(i)))
    
    
    '''
    合并分词后的文件
    '''
    def merge_corpus():
        output = open("./zhwiki/BB/wiki_corpus","w",encoding="utf-8")
        input = "./zhwiki/BB"
        for i in range(3):
            print("开始合并第", i, "个文件")
            file_path = os.path.join(input,str("wiki_corpus0%s"%str(i)))
            file = open(file_path,"r",encoding="utf-8")
            line = file.readline()
            while line:
                output.writelines(line)
                line = file.readline()
            file.close()
        output.close()
    
    if __name__ == "__main__":
        # # wiki数据处理
        print("开始正则,jieba处理数据")
        generate_corpus()
    
        # 文件合并
        print("开始合并文件")
        merge_corpus()
    
        # 打印数据 显示
        input_file = "./zhwiki/BB/wiki_corpus"
        file = open(input_file,"r",encoding="utf-8")
        line = file.readline()
        num = 1
        while line:
            print(line)
            line = file.readline()
            num += 1
            if num > 10:
                break
    

    word2vec模型的训练

    当前目录下的train.py中word2vec的参数设置(size=100, window=5, sg=1, hs=0, negative=5),符合实验要求(前后2窗口,100维,SGNS)。

    #train.py
    
    import logging
    from gensim.models import word2vec
    
    def main():
        logging.basicConfig(format="%(asctime)s:%(levelname)s:%(message)s",level=logging.INFO)
        sentences = word2vec.LineSentence("./zhwiki/BB/wiki_corpus")
        # size:单词向量的维度
        # window: 窗口大小
        # sg=1: 使用skip-gram
        # hs=0: 使用negative sample
        model = word2vec.Word2Vec(sentences, size=100, window=5, sg=1, hs=0, negative=5)
        # 保存模型  必须3个一起用
        # model.save("./model/wiki_corpus.bin")
        # model.save("./model/wiki_corpus.model")
    
        # 训练为一个单独二进制压缩文件  可独立使用
        model.wv.save_word2vec_format("./model/wiki_corpus_binary.bin", binary=True)
    
    if __name__ == "__main__":
        main()
    

    计算两个词的相似度:

    当前目录下的compute.py中的代码共执行了以下步骤:
    (1)读取训练得到的模型,以及待计算相似的pku_sim_test.txt文件
    (2)字符串以 为分隔符切分为列表格式,并计算相似度
    (3)结果保存为result.txt文件

    #compute.py
    
    import re
    from gensim.models import KeyedVectors
    
    def main():
        # 读取模型以及待计算数据
        model = KeyedVectors.load_word2vec_format("./model/wiki_corpus_binary.bin", binary=True)
        f = open('./pku_sim_test.txt', encoding='utf-8')
        out = open('result.txt', 'w', encoding='utf-8')
        
        # 字符串切分为列表
        wordlist = []
        while True:
            line = f.readline()
            if not line:
                break
            wordlist.append(re.split(r'[	
    ]', line))
        
        # 计算相似度
        cnt = 0
        resTotal = 0.0
        for i in range(len(wordlist)):
            words = wordlist[i]
            try:
                res = model.similarity(words[0], words[1])
            except KeyError:
                words[2] = 'OOV'
                wordlist[i] = words
                print(words)
                continue
    
            words[2] = str("%.4f"%res)
            wordlist[i] = words
            print(words)
            cnt += 1
            resTotal += res
    
        print("查到的比例为:%.4f"%(cnt/len(wordlist)))
        print("平均相似度为:%.4f"%(resTotal/cnt))
        
        # 结果保存
        lines = []
        for i in range(len(wordlist)):
            line = wordlist[i]
            oneline = line[0] + '	' + line[1] + '	' + line[2] + '
    '
            lines.append(oneline)
        out.writelines(lines)
        f.close()
        out.close()
        
    
    if __name__ == '__main__':
        main()
    

  • 相关阅读:
    [LeetCode] 52. N-Queens II N皇后问题之二
    [LeetCode] 51. N-Queens N皇后问题
    [LeetCode] 53. Maximum Subarray 最大子数组
    [LeetCode] 45. Jump Game II 跳跃游戏之二
    [LeetCode] 55. Jump Game 跳跃游戏
    [LeetCode] 56. Merge Intervals 合并区间
    [LeetCode] 57. Insert Interval 插入区间
    Android中渐变图片失真的解决方案
    Android compress 压缩 会不会失真
    Android ImageView的几种对图片的缩放处理 解决imageview放大图片后失真问题解决办法
  • 原文地址:https://www.cnblogs.com/yanqiang/p/12109901.html
Copyright © 2011-2022 走看看