/**
*
* APDPlat - Application Product Development Platform
* Copyright (c) 2013, 杨尚川, yang-shangchuan@qq.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see.
*
*/
packageorg.apdplat.word.vector;
importorg.apdplat.word.util.Utils;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importjava.io.*;
importjava.util.*;
importjava.util.Map.Entry;
importjava.util.stream.Collectors;
/**
* 用词向量来表达一个词
*@author杨尚川
*/
public classWord2Vector {
private static finalLoggerLOGGER= LoggerFactory.getLogger(Word2Vector.class);
public static voidmain(String[] args){
String input ="data/word.txt";
String output ="data/vector.txt";
String vocabulary ="data/vocabulary.txt";
intwindow =2;
intvectorLength =30;
if(args.length==3){
input = args[];
output = args[1];
vocabulary = args[2];
}
if(args.length==5){
input = args[];
output = args[1];
vocabulary = args[2];
window = Integer.parseInt(args[3]);
vectorLength = Integer.parseInt(args[4]);
}
longstart = System.currentTimeMillis();
word2Vec(input,output,vocabulary,window,vectorLength);
longcost = System.currentTimeMillis()-start;
LOGGER.info("cost time:"+cost+" ms");
}
private static voidword2Vec(String input,String output,String vocabulary, intwindow, intvectorLength) {
floatmax=(float)Runtime.getRuntime().maxMemory()/1000000;
floattotal=(float)Runtime.getRuntime().totalMemory()/1000000;
floatfree=(float)Runtime.getRuntime().freeMemory()/1000000;
String pre="执行之前剩余内存:"+max+"-"+total+"+"+free+"="+(max-total+free);
File outputFile =newFile(output);
//准备输出目录
if(!outputFile.getParentFile().exists()){
outputFile.getParentFile().mkdirs();
}
File vocabularyFile =newFile(vocabulary);
//准备输出目录
if(!vocabularyFile.getParentFile().exists()){
vocabularyFile.getParentFile().mkdirs();
}
try(BufferedReader reader =newBufferedReader(newInputStreamReader(newFileInputStream(input),"utf-8"));
BufferedWriter writer =newBufferedWriter(newOutputStreamWriter(newFileOutputStream(outputFile),"utf-8"));
BufferedWriter vocabularyWriter =newBufferedWriter(newOutputStreamWriter(newFileOutputStream(vocabularyFile),"utf-8"))){
inttextLength=;
longstart = System.currentTimeMillis();
LOGGER.info("1、开始计算相关词");
LOGGER.info("上下文窗口:"+window);
Map> map =newHashMap();
Map frq =newHashMap();
String line =null;
while((line = reader.readLine()) !=null){
textLength += line.length();
word2Vec(map,frq,line,window);
}
longcost = System.currentTimeMillis()-start;
LOGGER.info("计算完毕,速度:"+ textLength/cost+" 字符/毫秒,耗时:"+cost/1000+" 秒,数据大小:"+map.size()+",词大小:"+frq.size());
LOGGER.info("2、开始整理数据,归一化、排序、截取TOPN");
LOGGER.info("向量长度:"+vectorLength);
start = System.currentTimeMillis();
normalize(map,frq,vectorLength);
LOGGER.info("处理完毕,耗时:"+ (System.currentTimeMillis()-start)/1000+" 秒,数据大小:"+map.size());
LOGGER.info("3、开始输出结果");
start = System.currentTimeMillis();
List list =newArrayList();
for(Entry> entry : map.entrySet()){
list.add(entry.getKey()+" : "+entry.getValue().toString());
}
Collections.sort(list);
for(String item : list){
writer.write(item+"\n");
}
list.clear();
List> entrys = frq.entrySet().parallelStream().sorted((a,b)->b.getValue().compareTo(a.getValue())).collect(Collectors.toList());
for(Entry entry : entrys){
vocabularyWriter.write(entry.getKey()+" "+entry.getValue()+"\n");
}
LOGGER.info("输出完毕,耗时:"+ (System.currentTimeMillis()-start)/1000+" 秒,数据项:"+list.size());
}catch(Exception e){
LOGGER.error("操作错误",e);
}
max=(float)Runtime.getRuntime().maxMemory()/1000000;
total=(float)Runtime.getRuntime().totalMemory()/1000000;
free=(float)Runtime.getRuntime().freeMemory()/1000000;
String post="执行之后剩余内存:"+max+"-"+total+"+"+free+"="+(max-total+free);
LOGGER.info(pre);
LOGGER.info(post);
}
private static voidword2Vec(Map> map,Map frq,String line, intdistance){
String[] words = line.split(" ");
if(words.length>10000){
LOGGER.info("行大小:"+words.length);
}
for(inti=;i
if(i >&& i %10000==){
LOGGER.info("行处理进度: "+i/(float)words.length*100+" %");
}
String word = words[i];
if(!Utils.isChineseCharAndLengthAtLeastTwo(word)){
continue;
}
//计算总词频
Integer count = frq.get(word);
if(count ==null){
count =1;
}else{
count++;
}
frq.put(word,count);
//计算word的上下文关联词
for(intj=1;j
//计算word的上文
intindex = i-j;
contextWord(words,index,j,word,map);
//计算word的下文
index = i+j;
contextWord(words,index,j,word,map);
}
}
}
/**
* 计算词的相关词
*@paramwords词数组
*@paramindex相关词索引
*@paramdistance词距
*@paramword核心词
*@parammap
*/
private static voidcontextWord(String[] words, intindex, intdistance,String word,Map> map){
String _word =null;
if(index > -1&& index
_word = words[index];
}
if(_word !=null&& Utils.isChineseCharAndLengthAtLeastTwo(_word)){
addToMap(map,word,_word,distance);
}
}
private static voidaddToMap(Map> map,String word,String _word, intdistance){
List value = map.get(word);
if(value ==null){
value =newArrayList();
map.put(word,value);
}
floats = (float)1/distance;
booleanfind=false;
for(ContextWord item : value){
if(item.getWord().equals(_word)){
floatscore = item.getScore()+s;
item.setScore(score);
find =true;
break;
}
}
if(!find){
ContextWord item =newContextWord(_word,s);
value.add(item);
}
}
private static voidnormalize(Map> map,Map frq, intcount) {
for(String key : map.keySet()){
List value = map.get(key);
//分值归一化
floatmax=;
for(ContextWord word : value){
//加入词频信息,重新计算分值
//float score = word.getScore() / (float)Math.sqrt(frq.get(word.getWord()));
//word.setScore(score);
if(word.getScore() > max){
max = word.getScore();
}
}
for(ContextWord word : value){
word.setScore(word.getScore()/max);
}
//排序
Collections.sort(value);
intlen = value.size();
//截取需要的TOPN
if(len > count){
value = value.subList(,count);
}
map.put(key,value);
}
}
private static classContextWordimplementsComparable{
privateStringword;
private floatscore;
publicContextWord(String word,Float score) {
this.word= word;
this.score= score;
}
@Override
publicStringtoString() {
returnword+" "+score;
}
publicStringgetWord() {
returnword;
}
public voidsetWord(String word) {
this.word= word;
}
public floatgetScore() {
returnscore;
}
public voidsetScore(floatscore) {
this.score= score;
}
@Override
public intcompareTo(Object o) {
floattarget = ((ContextWord)o).getScore();
if(this.getScore()
return1;
}
if(this.getScore() == target){
return;
}
return-1;
}
}
}
这个代码是在word分词器中提取出来的代码,我在看完这个代码之后感觉异常的震惊,为什么呢word2vec
这种词向量模型第一次出现在我的认知范围中是由python的seq2seq中的一个算法。而后在逐步的了解的过程中。(我之前的词向量初始标定采用的是tfidf算法)
tfidf:
图片取自于网络,如有侵权请联系删帖
原来一些很神奇的算法早就围绕在我们身边。
word2vec 到底是什么意思呢
百度中对word2vec的描述是
随着计算机应用领域的不断扩大,自然语言处理受到了人们的高度重视。机器翻译、语音识别以及信息检索等应用需求对计算机的自然语言处理能力提出了越来越高的要求。为了使计算机能够处理自然语言,首先需要对自然语言进行建模。自然语言建模方法经历了从基于规则的方法到基于统计方法的转变。从基于统计的建模方法得到的自然语言模型称为统计语言模型。有许多统计语言建模技术,包括n-gram、神经网络以及 log_linear 模型等。在对自然语言进行建模的过程中,会出现维数灾难、词语相似性、模型泛化能力以及模型性能等问题。寻找上述问题的解决方案是推动统计语言模型不断发展的内在动力。在对统计语言模型进行研究的背景下,Google 公司在 2013年开放了 Word2vec这一款用于训练词向量的软件工具。Word2vec 可以根据给定的语料库,通过优化后的训练模型快速有效地将一个词语表达成向量形式,为自然语言处理领域的应用研究提供了新的工具。Word2vec依赖skip-grams或连续词袋(CBOW)来建立神经词嵌入。Word2vec为托马斯·米科洛夫(Tomas Mikolov)在Google带领的研究团队创造。该算法渐渐被其他人所分析和解释。
那么所依赖的一般情况下分为两类模型。一种是词袋模型。分为伯努利模型和多项式模型。
伯努利模型的解释是基数模型,也可以称之为非重复词袋模型,多项式模型就是我们不考虑单独性的一个模型。
讲到这里就是监督学习和非监督学习两种不同的实现机制。
为什么要产生学习算法。那么可以这么解释,在不同的条件之下我们采用的一维算法,和现在的多维算法展现出的效果是极其不一样的。
在权重的世界,某些已经发生的且重复发生的事情
词袋模型
词袋模型(Bag-of-words model)是个在自然语言处理和信息检索(IR)下被简化的表达模型。此模型下,像是句子或是文件这样的文字可以用一个袋子装着这些词的方式表现,这种表现方式不考虑文法以及词的顺序。最近词袋模型也被应用在计算机视觉领域。词袋模型被广泛应用在文件分类,词出现的频率可以用来当作训练分类器的特征。关于"词袋"这个用字的由来可追溯到泽里格·哈里斯于1954年在Distributional Structure的文章。
Skip-gram 模型
Skip-gram 模型是一个简单但却非常实用的模型。在自然语言处理中,语料的选取是一个相当重要的问题: 第一,语料必须充分。一方面词典的词量要足够大,另一方面要尽可能多地包含反映词语之间关系的句子,例如,只有“鱼在水中游”这种句式在语料中尽可能地多,模型才能够学习到该句中的语义和语法关系,这和人类学习自然语言一个道理,重复的次数多了,也就会模仿了; 第二,语料必须准确。 也就是说所选取的语料能够正确反映该语言的语义和语法关系,这一点似乎不难做到,例如中文里,《人民日报》的语料比较准确。 但是,更多的时候,并不是语料的选取引发了对准确性问题的担忧,而是处理的方法。 n元模型中,因为窗口大小的限制,导致超出窗口范围的词语与当前词之间的关系不能被正确地反映到模型之中,如果单纯扩大窗口大小又会增加训练的复杂度。Skip-gram 模型的提出很好地解决了这些问题。顾名思义,Skip-gram 就是“跳过某些符号”,例如,句子“中国足球踢得真是太烂了”有4个3元词组,分别是“中国足球踢得”、“足球踢得真是”、“踢得真是太烂”、“真是太烂了”,可是我们发现,这个句子的本意就是“中国足球太烂”可是上述 4个3元词组并不能反映出这个信息。Skip-gram 模型却允许某些词被跳过,因此可以组成“中国足球太烂”这个3元词组。 如果允许跳过2个词,即 2-Skip-gram[1]。
跳词的这个算法的前置主题就是我们需要句序列的一个分析,也就是短语标定之后的一个分析,这种分析策略最开始的解决方案是类似于
IKanlyzer这种解决方案,后来我们出现了很多优秀的分词器,后期我也会讲到三种分词器的应用。
一种是hanlp还有海量分词器以及word分词器。
这些分词器中包含着极大数量的优质的算法但是却不一定具有一定的市场知晓性。科技的利润很大,但是对于技术人员来说又非常的少。
我不是一个好的作家,我会想到哪里就说道哪里。
Google 公司在 2013年开放了 Word2vec这一款用于训练词向量的软件工具
领取专属 10元无门槛券
私享最新 技术干货