1、word2vec简介、作用
1)解决哪些问题
word2vec,字面意思,将word转化为vector,word是顺序有意义的实体,比如文档中单词、用户依次点击的商品。
word2vec得到实体向量,可以用来度量实体间相似度,在此基础上,以下方向都可以应用(部分方向未实践,参考资料所得):
分类
聚类
推荐
句子向量
短文本分类
2)两种实现方式
Skip-gram:用一个词语作为输入,来预测它周围的上下文。同义词
CBOW :用一个词语的上下文作为输入,来预测这个词语本身。完形填空
2、已实现word2vec的工具
1)Genvim,python版本
2)Spark.ml word2vec,DataFrames实现Skip-gram模型
3)Spark.mllib word2vec,RDD实现Skip-gram模型
3、Spark mllib word2vec实践&tips
1)语料
http://www.sogou.com/labs/resource/ca.php
,全网新闻数据,下载.tar.gz
解压、转码、替换:
tar -zxvf news_tensite_xml.full.tar.gz
cat news_tensite_xml.dat | iconv -f gbk -t utf-8 -c | grep "" > news.txt
sed -i "s/<\/content>//g" news
sed -i "s///g" news
语料较大,单机处理较慢,建议集群上分词、训练word2vec模型。
2)分词
使用哈工大分词器分词,maven依赖如下:
com.hankcs
hanlp
portable-1.2.8
org.scalanlp
nak_2.10
1.3
分词过程:全角转半角、停用词处理、分词、存储
def segment(sc:SparkContext): Unit = {
//stop words
val stopWordPath = "停用词路径"
val bcStopWords = sc.broadcast(sc.textFile(stopWordPath).collect().toSet)
//content segment
val inPath = "训练语料路径"
val segmentRes = sc.textFile(inPath)
.map(AsciiUtil.sbc2dbcCase) //全角转半角
.mapPartitions(it =>{
it.map(ct => {
try {
val nlpList = NLPTokenizer.segment(ct)
import scala.collection.JavaConverters._
nlpList.asScala.map(term => term.word)
.filter(!bcStopWords.value.contains(_))
.mkString(" ")
} catch {
case e: NullPointerException => println(ct);""
}
})
})
//save segment result
segmentRes.saveAsTextFile("分词结果路径")
bcStopWords.unpersist()
}
public class AsciiUtil {
public static final char SBC_SPACE = 12288; // 全角空格 12288
public static final char DBC_SPACE = 32; //半角空格 32
// ASCII character 33-126 <-> unicode 65281-65374
public static final char ASCII_START = 33;
public static final char ASCII_END = 126;
public static final char UNICODE_START = 65281;
public static final char UNICODE_END = 65374;
public static final char DBC_SBC_STEP = 65248; // 全角半角转换间隔
private static char sbc2dbc(char src){
if (src == SBC_SPACE) {
return DBC_SPACE;
}
if (src >= UNICODE_START && src <= UNICODE_END) {
return (char) (src - DBC_SBC_STEP);
}
return src;
}
/**
* Convert from SBC case to DBC case
*
* @param src
* @return DBC case
*/
public static String sbc2dbcCase(String src) {
if (src == null) {
return null;
}
char[] c = src.toCharArray();
for (int i = 0; i < c.length; i++) {
c[i] = sbc2dbc(c[i]);
}
return new String(c);
}
private static char dbc2sbc(char src){
if (src == DBC_SPACE) {
return SBC_SPACE;
}
if ((src <= ASCII_END) && (src >= ASCII_START)) {
return (char) (src + DBC_SBC_STEP);
}
return src;
}
/**
* Convert from DBC case to SBC case.
*
* @param src
* @return SBC case string
*/
public static String dbc2sbcCase(String src) {
if (src == null) {
return null;
}
char[] c = src.toCharArray();
for (int i = 0; i < c.length; i++) {
c[i] = dbc2sbc(c[i]);
}
return new String(c);
}
}
停用词,链接: https://pan.baidu.com/s/1brfLc7P 密码: g66b
3)Skip-gram模型训练,参考spark
spark.mllib word2vec训练:
def word2VecRun(sc:SparkContext) = {
val input = sc.textFile("分词结果路径").map(line => line.split(" ").toSeq)
//model train
val word2vec = new Word2Vec()
.setVectorSize(50)
.setNumPartitions(64)
val model = word2vec.fit(input)
println("model word size: " + model.getVectors.size)
//Save and load model
model.save(sc, "word2vec模型路径")
val local = model.getVectors.map{
case (word, vector) => Seq(word, vector.mkString(" ")).mkString(":")
}.toArray
sc.parallelize(local).saveAsTextFile("word2vec词向量路径")
//predict similar words
val like = model.findSynonyms("中国", 40)
for ((item, cos) <- like) {
println(s"$item $cos")
}
//val sameModel = Word2VecModel.load(sc, "word2vec模型路径")
}
//中国,word2vec同义词结果:
大国 0.7586380435991317
国际舞台 0.7484820784140288
重振 0.7443842915770801
强国 0.7440432134955456
亚洲地区 0.7336309627565089
值得羡慕 0.7254971855043949
拉美 0.7199528766085572
走向世界 0.7195522594105694
世界第二大 0.7176901062252401
国力 0.7152167405425096
国际 0.7129751314747532
世界 0.711542837711965
与日俱增 0.7105297033943727
衰落 0.7080864535531732
政治化 0.7068762377142492
经济大国 0.7046257249995131
版图 0.7036082614700551
净土宗 0.7005121068489949
广播网 0.6990517002783354
全球华语 0.698620969507982
朱迪思·柯林斯 0.6985765569099095
中国政府 0.6946647084746524
21世纪 0.6935087636144573
蒸蒸日上 0.6934873373151971
超级大国 0.689131237979483
非洲 0.6862589700025773
世界工厂 0.6850311314455563
25所孔子学院 0.6794086519488903
中国崛起 0.6771866694599513
威胁论 0.6761148442425337
深远影响 0.6760546747445543
新兴国家 0.6751478515878965
前景 0.6750538485175515
之路 0.6735198878198577
发展史 0.6727135014527849
在世界上 0.6692064139295489
国际形象 0.6677791969781638
现代史 0.6668052040062915
国际关系 0.6667875117151274
强盛 0.6643625930792731
4、可能遇到的问题:
1)单机本地训练,heap size过小
run-->edit configurations --> vim options -Xmx1024m(或者更大)
2)集群模式训练,tast只有一个
word2vec默认参数只有一个partition,需设置分区数:
new Word2Vec().setNumPartitions(64)
另外,word2vec训练中,需要collect训练样本,因此driver memory需要设置大一些
3)minCount和vector_size设置问题
spark word2vector 默认 vocab * vector_size < Integer.Max_Value/8,否则无法训练
minCount:词频量,默认参数5
vectorSize:词向量大小,默认参数100
4)集群模式分词,Class not found exception
spark任务提交时,添加jar依赖:--jars "nak_2.10-1.3.jar,hanlp-portable-1.2.8.jar"