导读:
word2VEC 是什么?
百科描述:
Word2vec,是为一群用来产生词向量的相关模型。这些模型为浅而双层的神经网络,用来训练以重新建构语言学之词文本。网络以词表现,并且需猜测相邻位置的输入词,在word2vec中词袋模型假设下,词的顺序是不重要的。训练完成之后,word2vec模型可用来映射每个词到一个向量,可用来表示词对词之间的关系,该向量为神经网络之隐藏层。
入门篇的理解:
Word2vec可处理文本(有关系的文本数据),通过一些我不懂的公式然后构建每个词的向量,有关系的词会放在一起(既向量相近),两词之间的相关度可以用余弦表示,越相关则值越小。
干活:
怎么使用这个玩意呢,(人生苦短,我学python,java众该怎么办呢?python也是根据论文写的算法,java也一样行,其中Deeplearning4J就实现了这些网络,怎么导入项目看快速指引)
前提:
1.因为word2VEC的分词器是默认是英文进行分词,对中文不支持,需要中文的分词器,这里使用了一些分词器,发现还是教授的分词器好用:FNLP,这个fnlp不光有有分词的功能,还包括:信息检索( 文本分类 新闻聚类)、中文处理( 中文分词 词性标注 实体名识别 关键词抽取 依存句法分析 时间短语识别)、结构化学习( 在线学习 层次分类 聚类),这里使用分词功能,怎么导入fnlp后面会讲。
注:word2Vec在处理时,默认标识是由空格分隔的单词,对单词进行向量标识。
2.将https://github.com/deeplearning4j/dl4j-examples.git 的项目导入你的eclipse,然后在pom.xml中加入分词功能,这里加入word分词器进行比较(尴尬了) 。
<dependency>
<groupId>org.apdplat</groupId>
<artifactId>word</artifactId>
<version>1.3</version>
</dependency>
开始测试,让word2Vec来读一读关于猴子的故事:
1.下载了基本关于猴子的几本书:西游记、悟空传、大妖孙悟空、重回西游、大泼猴、西游岁月
2.使用fnlp进行分词:
提起 神通 广大 的 “ 齐天 大圣 ” 孙悟空 , 不论 是 大陆 还是 港 台 , 是 星洲 还是 槟屿 , 凡 我 炎黄子孙 、 华夏 儿女 , 乃是 家喻户晓 , 妇孺皆知 , 几乎 人人 都 能 讲
使用word分词:
提起 神通 广大 的 齐天 大圣 孙 悟空 不论是 大 陆 还是 港台 是 星洲 还是 槟 屿 凡 我 炎黄 子孙 华夏 儿女 乃是 家喻户晓 妇孺 皆知 几乎 人 人都 能讲。
这只是其中一段,两者分词的效果还是很明显的,并且两者的性能天差地别..
3.设置模型参数读取分隔好的文本进行测试:
//1.加载数据,创建分词器
log.info("Load data....");
SentenceIterator iter = new LineSentenceIterator(new File(targetFile));
iter.setPreProcessor(new SentencePreProcessor() {
@Override
public String preProcess(String sentence) {
return sentence.toLowerCase();
}
});
TokenizerFactory t = new DefaultTokenizerFactory();
t.setTokenPreProcessor(new CommonPreprocessor());
//2.培训模型
log.info("Building model....");
// Word2Vec vec = new Word2Vec.Builder()
// .minWordFrequency(10) //是单词必须出现在语料库中的最小次数。在这里,如果它出现少于5次,则不会学习。单词必须出现在多个上下文中才能学习有关它们的有用功能。在非常大的语料库中,提高最小值是合理的。
// .layerSize(100) //layerSize指定单词向量中的要素数。这等于要素空间中的维数。由500个特征表示的单词成为500维空间中的点。
// .seed(42) //该方法为随机数发生器定义了随机种子
// .windowSize(5)
// .iterate(iter) //告诉网络它正在训练的数据集的批次。
.tokenizerFactory(t) //将当前批次中的单词提供给它。
// .iterations(1)
// .batchSize(30)
// .build();
Word2Vec vec = new Word2Vec.Builder()
.minWordFrequency(5)
.iterations(1)
.layerSize(100)
.seed(42)
.windowSize(15)
.iterate(iter)
.build();
log.info("Fitting Word2Vec model....");
vec.fit();
// batchSize是您一次处理的单词数量。
// minWordFrequency是单词必须出现在语料库中的最小次数。在这里,如果它出现少于5次,则不会学习。单词必须出现在多个上下文中才能学习有关它们的有用功能。在非常大的语料库中,提高最小值是合理的。
// useAdaGrad - Adagrad为每个要素创建不同的渐变。在这里,我们并不关心这一点。
// layerSize指定单词向量中的要素数。这等于要素空间中的维数。由500个特征表示的单词成为500维空间中的点。
// learningRate是系数每次更新的步长,因为在特征空间中重新定位了单词。
// minLearningRate是学习率的最低点。随着您训练的单词数减少,学习率会下降。如果学习率下降太多,网络的学习就不再有效。这使系数保持移动。
// iterate告诉网络它正在训练的数据集的批次。
// tokenizer将当前批次中的单词提供给它。
// vec.fit()告诉配置的网络开始训练。
//3.模型评估
WordVectorSerializer.writeWordVectors(vec, "pathToWriteto.txt");
log.info("保存模型前进行一次数据匹配----");
Collection<String> lst = vec.wordsNearest(word, 10);
System.out.println(lst);
//4.保存训练的模型,读取训练的模型
log.info("Save vectors....");
WordVectorSerializer.writeWord2VecModel(vec, "pathToSaveModel.txt");
vec = WordVectorSerializer.readWord2VecModel("pathToSaveModel.txt");
log.info("读取序列化模型后进行数据匹配---------");
log.info("Closest Words:");
lst = vec.wordsNearest(word, 10);
System.out.println(lst);
4.测试结果:
匹配词为 猴子,输出结果:
// windowSize 5 [六耳猕, 六耳猕猴, 通背猿, 龙血, 林夕, 抓到, 碧海, 猿猴和, 马猴, 猿猴]
// windowSize 15 [六耳猕, 通背, 耳, 猿猴和, 猴子, 六, 赤尻, 六耳猕猴, 耳猕猴, 名字]
模型参数不同,得到的结果也不一致(下一篇会讲解参数设置),
后记:
1.使用fnlp:
首先将项目down下来:
git clone https://github.com/FudanNLP/fnlp.git
编译:
进入fnlp目录,执行下面的命令
mvn install -Dmaven.test.skip=true 会编译fnlp的核心包
mvn dependency:copy-dependencies -DoutputDirectory=libs 会将需要的jar都拷贝到libs这个目录
将commons-cli-1.2.jar 、trove4j-3.0.3.jar 、hamcrest-core-1.3.jar 、fnlp-core-2.1-SNAPSHOT.jar 将如build path 环境中,
还需要下载下面的文件,并将模型文件放在“models”目录。(下载地址:https://github.com/FudanNLP/fnlp/releases)
- seg.m 分词模型
- pos.m 词性标注模型
- dep.m 依存句法分析模型
最后的目录结构:
创建工具类:
public class FudanTokenizer {
private CWSTagger tag;
private StopWords stopWords;
public FudanTokenizer() {
String path = this.getClass().getClassLoader().getResource("").getPath();
System.out.println(path);
try {
tag = new CWSTagger(path + "models/seg.m");
} catch (LoadModelException e) {
e.printStackTrace();
}
}
public String processSentence(String context) {
return tag.tag(context);
}
public String processSentence(String sentence, boolean english) {
if (english) {
tag.setEnFilter(true);
}
return tag.tag(sentence);
}
public String processFile(String filename) {
return tag.tagFile(filename);
}
/**
* 设置分词词典
*/
public boolean setDictionary() {
String dictPath = this.getClass().getClassLoader().getResource("models/dict.txt").getPath();
Dictionary dict;
try {
dict = new Dictionary(dictPath);
} catch (IOException e) {
return false;
}
tag.setDictionary(dict);
return true;
}
/**
* 去除停用词
*/
public List<String> flitStopWords(String[] words) {
try {
return stopWords.phraseDel(words);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
//分词工具类
public class FNLPTokenizerUtil {
private static Logger log = LoggerFactory.getLogger(FNLPTokenizerUtil.class);
private static FudanTokenizer tokenizer = new FudanTokenizer();
public static boolean processFile(String targetFile, String outFile) {
boolean flag = false;
log.info("开始处理文本,进行分词处理:文件名位置:{},文件处理后的输出位置:{}", targetFile, outFile);
try {
BufferedReader in = new BufferedReader(new FileReader(targetFile));
File outfile = new File(outFile);
if (outfile.exists()) {
outfile.delete();
}
FileOutputStream fop = new FileOutputStream(outfile);
// 构建FileOutputStream对象,文件不存在会自动新建
String line = in.readLine();
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
while (line != null) {
line = tokenizer.processSentence(line);
writer.append(line);
line = in.readLine();
}
in.close();
writer.close(); // 关闭写入流,同时会把缓冲区内容写入文件
fop.close(); // 关闭输出流,释放系统资源
log.info("文件分词处理完成,输出位置:{}", outFile);
flag = true;
} catch (Exception e) {
log.error("分词器处理文本异常", e);
}
return flag;
}
}
word分词工具方法:
/**
* 获取文本的所有分词结果
* @param text 文本
* @return 所有的分词结果,去除重复
*/
public static Set<String> defaultSeg(String text) {
return segMore(text).values().stream().collect(Collectors.toSet());
}
public static Map<String, String> segMore(String text) {
Map<String, String> map = new HashMap<>();
for(SegmentationAlgorithm segmentationAlgorithm : SegmentationAlgorithm.values()){
map.put(segmentationAlgorithm.getDes(), seg(text, segmentationAlgorithm));
}
return map;
}
private static String seg(String text, SegmentationAlgorithm segmentationAlgorithm) {
StringBuilder result = new StringBuilder();
for(Word word : WordSegmenter.segWithStopWords(text, segmentationAlgorithm)){
result.append(word.getText()).append(" ");
}
return result.toString();
}
下面参考官网文档的描述:
2.word2vec的基本概念:
1.Word2vec的应用程序不仅仅是解析野外的句子。它也可以应用于基因,代码,喜欢,播放列表,社交媒体图以及可以辨别模式的其他语言或符号系列。
2.特征向量:
将相似单词的向量组合在空间中,它以数学方式检测数据相似性,输出一个词汇表,每个单词都对应一个向量,可以将其输入网络中检测单词之间的关系,他们的相对意义已被转为为可被检测的距离。
3.构建步骤:
1.迭代文本库创建文档列表,senteceIterator(通过分割文本库来创建字符串集合)
2.Tokenizer 将文本分隔为单个单词,某人是由空格分隔的单词
每个文档都必须被标记化以创建词汇,即对该文档或语料库重要的词汇集。这些单词存储在词汇缓存中,其中包含有关文档中计算的单词子集的统计信息,即“重要”字样。区分显着和不重要词语的界限是移动的,但区分两组的基本思想是,只发生一次(或少于,比如五次)的单词难以学习,并且它们的存在代表无用的。
4.doc2vec(下一篇一起介绍) :可将文档与标签相关联,是word2vec的扩展。
更多关于word2vec的文章和论文:
介绍word2vec是如何工作:
https://www.quora.com/How-does-word2vec-work-Can-someone-walk-through-a-specific-example
https://skymind.ai/wiki/ai-vs-machine-learning-vs-deep-learning
这一篇更多是参考官方文档做一个小demo,简单的使用word2vec,下一篇对word2vec模型参数详细介绍和将文档和标签进行关联。