一、概述
训练语料来源:维基媒体 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 .\wikiextractor\WikiExtractor.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 .\zhwiki\AA\wiki_00
- -o .\zhwiki\BB\wiki_00
- -c D:\opencc-1.0.4-win32\opencc-1.0.4\share\opencc\t2s.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("\n"))
- 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("\n")
- 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+"\n")
- 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)字符串以\t\n为分隔符切分为列表格式,并计算相似度
(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'[\t\n]', 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] + '\t' + line[1] + '\t' + line[2] + '\n'
- lines.append(oneline)
- out.writelines(lines)
- f.close()
- out.close()
-
-
- if __name__ == '__main__':
- main()
