第一、先说下应用场景吧,用户给出一段文字然后我返回十个与这段文字最相似的文件名称。
第二、什么是TF-IDF算法?我就简单介绍一下,因为百度上也有许多的介绍,TF-IDF用中文来解释就是词频、逆文档频率的意思,TF-IDF体现了词项与文档关联度的直观理解,一个词在文档中出现的越多则越重要,但是词项是不平等的文档中出现罕见词项的意义比常见词更大,因此就要取词项出现次数的倒数,词项在语料库中的分布式呈指数型的一个常用词出现次数往往是罕见词的数十倍,假如直接除以原始文档的频率则罕见词的权重就会过大,所以算法应对逆文档率取对数,让文档频率差别由乘数级变为加数级。
第三、由于spark MLlib库已经有TF-IDF算法的实现我们就不亲自实现了,而是去调用它的,对于整个功能的逻辑如下:
(1)获取数据、(2)用中文分词工具分词(这里使用的是ansj)、(3)计算TF、IDF、(4)计算向量间的余弦相似度
废话不多说直接上代码:
import org.ansj.recognition.impl.StopRecognition
import org.ansj.splitWord.analysis.ToAnalysis
import org.apache.spark.mllib.feature.{ HashingTF, IDF }
import org.apache.spark.{ SparkConf, SparkContext }
import org.apache.spark.mllib.linalg.{ SparseVector => SV }
import scala.collection.mutable.ArrayBuffer
import scala.util.matching.Regex
object TfIdfs {
def main(args: Array[String]): Unit = {
val conf =new SparkConf().setAppName("tdidf")
val sc =new SparkContext(conf)
//读取2600份法律案例
valrdd = sc.wholeTextFiles("hdfs://master:8020/data2/*")
val text =rdd.map { case (file,text) => text }
val title =rdd.map { case (title,text) => title }.collect()
val dim = math.pow(2,18).toInt
val hashingTF =new HashingTF(dim)
val filter =new StopRecognition()
filter.insertStopNatures("w")//过滤掉标点
//使用ansj对文本进行分词
val tokens2 =text.map(doc => ToAnalysis.parse(doc).recognition(filter).toStringWithOutNature(" ").split(" ").toSeq)
//tf计算
val tf =hashingTF.transform(tokens2)
// cache data in memory
tf.cache
//idf计算
val idf =new IDF().fit(tf)
val tfidf =idf.transform(tf)
val vectorArr =tfidf.collect()
//需要匹配相似度的文本
valrdd3 = sc.parallelize(Seq("被告人王乃胜,高中文化,户籍所在地:白山市,居住地:白山市。"))
val predictTF2 =rdd3.map(doc => hashingTF.transform(ToAnalysis.parse(doc).recognition(filter).toStringWithOutNature(" ").split(" ").toSeq))
valpredictTfIdf = idf.transform(predictTF2)
import breeze.linalg._
val predictSV =predictTfIdf.first.asInstanceOf[SV]
val c =new ArrayBuffer[(String, Double)]()
valbreeze1 = new SparseVector(predictSV.indices,predictSV.values,predictSV.size)
var tag =0
for (i <-vectorArr) {
val Svector =i.asInstanceOf[SV]
val breeze2 =new SparseVector(Svector.indices,Svector.values, Svector.size)
valcosineSim = breeze1.dot(breeze2) / (norm(breeze1) * norm(breeze2))
c ++= Array((title(tag),cosineSim))
tag += 1
}
val cc =c.toArray.sortBy(_._2).reverse.take(10)
println(cc.toBuffer)
sc.stop()
}
}
————————————————
原文链接:https://blog.csdn.net/cap3396g/article/details/79256625
近期在负责公司的POI领域,全称为point of intrest即兴趣点,这个应用的最广泛的应该是地图行业,地图里每一个地址标注即为一个POI,在我们公司对它的含义进行了精简以契合公司业务的发展,将兴趣点集中在了餐饮及新零食相关的商户&超市等范畴。
听上去这个业务只是做一些商户数据的收集校正,那为什么这个业务会牵扯到了机器学习呢?真实原因很尴尬不便多说,目前我们拿到了一些商户的数据,但是无法获取品类,而品类对于我们当前业务来说非常重要,涉及到不同业务线的利益问题。所以需要通过一些特殊手段来识别出这些商户的品类。
场景
通过已有的商户数据,包括商户名称、商户菜品,识别出该商户属于什么品类(川湘菜、日料等)
解决办法
- 委托算法团队协助计算商户品类
已有相似项目,可基于该项目做品类计算,实现快速 - 自己琢磨,研究算法
学习算法以及应用到生产,较为耗时
最终选择
算法团队在人力资源安排上出现问题,不得已由自己来做算法计算,好在相关算法项目交接到我们团队,可以借此做为参考。
考虑到当前快速实现以及以后发展方向,最终选择两路同时并进,一路基于python脚本进行计算用于短期快速实现,另一路尝试通过spark ML进行分布式计算为我们长期目标。
算法思路
将商户的所有的菜品信息以及商户信息当成一串文本来处理,这样就可以把这个问题看成是“文本相似度”的问题,TF-IDF、LSI、LDA等一系列算法可以参考,python脚本采用TF-IDF和LSI来进行相似度计算(简单示例参考)。spark采用TF-IDF以及余弦相似度进行验证性计算(后续优化算法)。因长期规则为使用spark进行机器学习的相关计算,以下重点介绍spark上如何应用。
算法介绍
spark ML实现
分为两个spark任务,第一个任务为计算当前线上已经存在的且正确匹配的商户&菜品的TF-IDF值并且将计算出的值保存到hive表里。
任务一
数据预处理以及参考数据TF-IDF计算,通过计算
首先通过hive任务将商户的菜品数据拍平,这一步很简单,拍平后的数据如下:
然后另起spark任务对商户菜品进行TF-IDF处理,将结果保存到如下表里面。vector_indices及vector_values都为数组且长度一致,两者共同表示为多组向量
如下为tfidf工具类
public class TfidfUtil {
/**
* visit below website to get more detail about tfidf
* @see <a href="http://dblab.xmu.edu.cn/blog/1261-2/">Spark入门:特征抽取: TF-IDF</a>
* @param dataset
* @return
*/
public static Dataset<Row> tfidf(Dataset<Row> dataset) {
Tokenizer tokenizer = new Tokenizer().setInputCol("goodsSegment").setOutputCol("words");
Dataset<Row> wordsData = tokenizer.transform(dataset);
HashingTF hashingTF = new HashingTF()
.setInputCol("words")
.setOutputCol("rawFeatures");
Dataset<Row> featurizedData = hashingTF.transform(wordsData);
IDF idf = new IDF().setInputCol("rawFeatures").setOutputCol("features");
IDFModel idfModel = idf.fit(featurizedData);
return idfModel.transform(featurizedData);
}
}
如下为spark预处理任务,主要步骤为获取商户及拍平的菜品数据,再做TF-IDF,再保存到hive表
public class CategorySuggestionTrainning {
private static SparkSession spark;
private static final String YESTERDAY = DateTimeUtil.getYesterdayStr();
public static final String TRAINNING_DATA_SQL = "select id, coalesce(shop_name,'') as name,coalesce(category_id,0) as category_id, coalesce(food_name,'') as food " +
"from dw.category_and_foodname where dt='%s' limit 100000";
public static void main(String[] args){
spark = initSaprk();
try{
Dataset<Row> rawTranningDataset = getTrainDataSet();
Dataset<Row> trainningTfidfDataset = TfidfUtil.tfidf(rawTranningDataset);
JavaRDD<TrainningFeature> trainningFeatureRdd = getTrainningFeatureRDD(trainningTfidfDataset);
Dataset<Row> trainningFeaturedataset = spark.createDataFrame(trainningFeatureRdd,TrainningFeature.class);
saveToHive(trainningFeaturedataset);
System.out.println("poi suggest trainning stopped");
spark.stop();
} catch (Exception e) {
System.out.println("main method has error " + e.getMessage());
e.printStackTrace();
}
}
/**
* to get the origin ele shop data including category and goods which is separated by '|'
* and then divide the goods into words
* @return Dataset<Row>
*/
private static Dataset<Row> getTrainDataSet(){
String trainningSql = String.format(TRAINNING_DATA_SQL,YESTERDAY);
System.out.println("tranningData sql is "+trainningSql);
spark.sql("use dw");
Dataset<Row> rowRdd = spark.sql(trainningSql);
JavaRDD<TrainningData> trainningDataJavaRDD = rowRdd.javaRDD().map((row) -> {
String goods = (String) row.getAs("food");
String shopName = (String) row.getAs("name");
if (StringUtil.isBlank(shopName) || StringUtil.isBlank(goods) || goods.length() < 50) {
System.out.println("some field is null " + row.toString());
return null;
}
TrainningData data = new TrainningData();
data.setShopId((Long) row.getAs("id"));
data.setShopName(shopName);
data.setCategory((Long) row.getAs("category_id"));
List<Word> words = WordSegmenter.seg(goods);
StringBuilder wordsOfGoods = new StringBuilder();
for (Word word : words) {
wordsOfGoods.append(word.getText()).append(" ");
}
data.setGoodsSegment(wordsOfGoods.toString());
return data;
}).filter((data) -> data != null);
return spark.createDataFrame(trainningDataJavaRDD, TrainningData.class);
}
private static JavaRDD<TrainningFeature> getTrainningFeatureRDD(Dataset<Row> trainningTfidfDataset){
return trainningTfidfDataset.javaRDD().map(new Function<Row, TrainningFeature>(){
@Override
public TrainningFeature call(Row row) throws Exception {
TrainningFeature data = new TrainningFeature();
data.setCategory(row.getAs("category"));
data.setShopId(row.getAs("shopId"));
data.setShopName(row.getAs("shopName"));
SparseVector vector = row.getAs("features");
data.setVectorSize(vector.size());
data.setVectorIndices(Arrays.toString(vector.indices()));
data.setVectorValues(Arrays.toString(vector.values()));
return data;
}
});
}
private static SparkSession initSaprk(){
long startTime = System.currentTimeMillis();
return SparkSession
.builder()
.appName("poi-spark-trainning")
.enableHiveSupport()
.getOrCreate();
}
private static void saveToHive(Dataset<Row> trainningTfidfDataset){
try {
trainningTfidfDataset.createTempView("trainData");
String sqlInsert = "insert overwrite table dw.poi_category_pre_data " +
"select shopId,shopName,category,vectorSize,vectorIndices,vectorValues from trainData ";
spark.sql("use dw");
System.out.println(spark.sql(sqlInsert).count());
} catch (AnalysisException e) {
System.out.println("save tranning data to hive failed");
e.printStackTrace();
}
}
}
任务二
取出预处理好的数据以及待确定分类的商户数据,将两者做余弦相似度计算,选择相似度最高的预处理的商户的分类做为待确认商户的分类。
相关代码如下
public class CategorySuggestion {
private static SparkSession spark;
private static final String YESTERDAY = DateTimeUtil.getYesterdayStr();
private static boolean CALCULATE_ALL = false;
private static long MT_SHOP_COUNT = 2000;
public static final String TRAINNING_DATA_SQL = "select shop_id, coalesce(shop_name,'') as shop_name,coalesce(category,0) as category_id, " +
"vector_size, coalesce(vector_indices,'[]') as vector_indices, coalesce(vector_values,'[]') as vector_values " +
"from dw.poi_category_pre_data limit %s ";
public static final String COMPETITOR_DATA_SQL = "select id,coalesce(name,'') as name,coalesce(food,'') as food from dw.unknow_category_restaurant " +
"where dt='%s' and id is not null limit %s ";
public static void main(String[] args){
spark = initSaprk();
try{
MiniTrainningData[] miniTrainningDataArray = getTrainData();
final Broadcast<MiniTrainningData[]> trainningData = spark.sparkContext().broadcast(miniTrainningDataArray, ClassTag$.MODULE$.<MiniTrainningData[]>apply(MiniTrainningData[].class));
System.out.println("broadcast success and list is "+trainningData.value().length);
Dataset<Row> rawMeituanDataset = getMeituanDataSet();
Dataset<Row> meituanTfidDataset = TfidfUtil.tfidf(rawMeituanDataset);
Dataset<SimilartyData> similartyDataList = pickupTheTopSimilarShop(meituanTfidDataset, trainningData);
saveToHive(similartyDataList);
System.out.println("poi suggest stopped");
spark.stop();
} catch (Exception e) {
System.out.println("main method has error " + e.getMessage());
e.printStackTrace();
}
}
private static SparkSession initSaprk(){
long startTime = System.currentTimeMillis();
return SparkSession
.builder()
.appName("poi-spark")
.enableHiveSupport()
.getOrCreate();
}
/**
* to get the origin ele shop data including category and goods which is separated by '|'
* and then divide the goods into words
* @return Dataset<Row>
*/
private static MiniTrainningData[] getTrainData(){
String trainningSql = String.format(TRAINNING_DATA_SQL,20001);
System.out.println("tranningData sql is "+trainningSql);
spark.sql("use dw");
Dataset<Row> rowRdd = spark.sql(trainningSql);
List<MiniTrainningData> trainningDataList = rowRdd.javaRDD().map((row) -> {
MiniTrainningData data = new MiniTrainningData();
data.setEleShopId( row.getAs("shop_id"));
data.setCategory( row.getAs("category_id"));
Long vectorSize = row.getAs("vector_size");
List<Integer> vectorIndices = JSON.parseArray(row.getAs("vector_indices"),Integer.class);
List<Double> vectorValues = JSON.parseArray(row.getAs("vector_values"),Double.class);
SparseVector vector = new SparseVector(vectorSize.intValue(),integerListToArray(vectorIndices),doubleListToArray(vectorValues));
data.setFeatures(vector);
return data;
}).collect();
MiniTrainningData[] miniTrainningDataArray = new MiniTrainningData[trainningDataList.size()];
return trainningDataList.toArray(miniTrainningDataArray);
}
private static int[] integerListToArray(List<Integer> integerList){
int[] intArray = new int[integerList.size()];
for (int i = 0; i < integerList.size(); i++) {
intArray[i] = integerList.get(i).intValue();
}
return intArray;
}
private static double[] doubleListToArray(List<Double> doubleList){
double[] doubleArray = new double[doubleList.size()];
for (int i = 0; i < doubleList.size(); i++) {
doubleArray[i] = doubleList.get(i).intValue();
}
return doubleArray;
}
private static Dataset<Row> getMeituanDataSet() {
String meituanSql = String.format(COMPETITOR_DATA_SQL, YESTERDAY, 10000);
System.out.println("meituan sql is " + meituanSql);
spark.sql("use dw");
Dataset<Row> rowRdd = spark.sql(meituanSql);
JavaRDD<MeiTuanData> meituanDataJavaRDD = rowRdd.javaRDD().map((row) -> {
MeiTuanData data = new MeiTuanData();
String goods = (String) row.getAs("food");
String shopName = (String) row.getAs("name");
data.setShopId((Long) row.getAs("id"));
data.setShopName(shopName);
if (StringUtil.isBlank(goods)) {
return null;
}
StringBuilder wordsOfGoods = new StringBuilder();
try {
List<Word> words = WordSegmenter.seg(goods.replace("|", " "));
for (Word word : words) {
wordsOfGoods.append(word.getText()).append(" ");
}
} catch (Exception e) {
System.out.println("exception in segment " + data);
}
data.setGoodsSegment(wordsOfGoods.toString());
return data;
}).filter((data) -> data != null);
System.out.println("meituan data count is " + meituanDataJavaRDD.count());
return spark.createDataFrame(meituanDataJavaRDD, MeiTuanData.class);
}
private static Dataset<SimilartyData> pickupTheTopSimilarShop(Dataset<Row> meituanTfidDataset, Broadcast<MiniTrainningData[]> trainningData){
return meituanTfidDataset.map(new MapFunction<Row, SimilartyData>() {
@Override
public SimilartyData call(Row row) throws Exception {
SimilartyData similartyData = new SimilartyData();
Long mtShopId = row.getAs("shopId");
Vector meituanfeatures = row.getAs("features");
similartyData.setMtShopId(mtShopId);
MiniTrainningData[] trainDataArray = trainningData.value();
if(ArrayUtils.isEmpty(trainDataArray)){
return similartyData;
}
double maxSimilarty = 0;
long maxSimilarCategory = 0L;
long maxSimilareleShopId = 0;
for (MiniTrainningData trainData : trainDataArray) {
Vector trainningFeatures = trainData.getFeatures();
long categoryId = trainData.getCategory();
long eleShopId = trainData.getEleShopId();
double dot = BLAS.dot(meituanfeatures.toSparse(), trainningFeatures.toSparse());
double v1 = Vectors.norm(meituanfeatures.toSparse(), 2.0);
double v2 = Vectors.norm(trainningFeatures.toSparse(), 2.0);
double similarty = dot / (v1 * v2);
if(similarty>maxSimilarty){
maxSimilarty = similarty;
maxSimilarCategory = categoryId;
maxSimilareleShopId = eleShopId;
}
}
similartyData.setEleShopId(maxSimilareleShopId);
similartyData.setSimilarty(maxSimilarty);
similartyData.setCategoryId(maxSimilarCategory);
return similartyData;
}
}, Encoders.bean(SimilartyData.class));
}
private static void saveToHive(Dataset<SimilartyData> similartyDataset){
try {
similartyDataset.createTempView("records");
String sqlInsert = "insert overwrite table dw.poi_category_suggest PARTITION (dt = '"+DateTimeUtil.getYesterdayStr()+"') \n" +
"select mtShopId,eleShopId,shopName,similarty,categoryId from records ";
System.out.println(spark.sql(sqlInsert).count());
} catch (AnalysisException e) {
System.out.println("create SimilartyData dataFrame failed");
e.printStackTrace();
}
//Dataset<Row> resultSet = spark.createDataFrame(similartyDataset,SimilartyData.class);
spark.sql("use platform_dw");
}
}
作者:adam_go
链接:https://www.jianshu.com/p/52298e5e0473
相关推荐
在本文中,我们将深入探讨如何使用Java编程语言实现基于文本相似度匹配的文本聚类算法。文本聚类是自然语言处理领域的一个重要课题,它的目标是将大量无结构的文本数据按照其内在的语义关系划分为不同的类别,使得同...
* TF-IDF算法可以用于文本分类、主题模型和文档相似度计算等领域。 知识点4:大数据技术在文本挖掘中的应用 * 大数据技术可以用于处理和分析大量的文本数据,例如Hadoop框架和Spark框架。 * 大数据技术可以提供...
《数学建模-基于k-means的中文文本聚类算法的研究与实现》 在这个压缩包文件中,我们主要探讨的是如何运用k-means算法进行中文文本的聚类分析,这是机器学习领域中一种广泛应用于数据挖掘的技术。k-means算法是一种...
5. **文本相似度**:通过TF-IDF、Word2Vec或BERT等技术计算文本之间的相似度,用于推荐系统、问答系统或文档聚类。 6. **句法分析**:解析句子结构,识别出词与词之间的语法关系,有助于理解文本的深层含义。 7. *...
推荐系统(基于内容,基于项目的协作过滤,基于用户的协作过滤) ...train_review.json –包含审阅数据的主文件,RS将主要使用此文件。 test_review.json –仅包含预测任务的目标用户和... 使用的算法为:TF-IDF分数和
本项目"知网-基于Java实现的知网的语义相似度计算"就是针对这一需求,利用Java编程语言来构建一个系统,用于计算知网中不同文献之间的语义相似度。 语义相似度计算是自然语言处理(NLP)中的核心问题,它的目的是...
在实际应用中,可能还需要考虑稀疏性问题,如使用基于TF-IDF的相似度计算。 五、毕业设计流程 这个毕业设计涵盖了从数据预处理(如数据清洗、格式转换)、模型构建(包括CBCF、UBCF、IBCF的算法实现)、模型训练、...
3. **向量化表示**:将文本转化为数值形式,如词袋模型(Bag of Words, BoW)或TF-IDF(词频-逆文档频率)。这些模型将每个文档表示为一个向量,其中每个维度对应一个词汇项,值表示该词汇项在文档中的重要性。 4. ...
5. **应用**:这个任务在多种场景下都有应用,如搜索引擎的查询相关性分析、文档相似度计算(如TF-IDF、余弦相似度等)、文本分类和信息抽取等。 6. **优化与扩展**:对于大规模文本数据,可能需要分布式计算或并行...
标题中的“行业分类-设备装置-基于Spark平台的快速高精度语义相关度计算方法”揭示了一个专注于特定行业(可能是制造业、自动化或物联网)的设备装置类应用,它利用大数据处理框架Apache Spark来实现高效的语义相关...
对于文本文件,我们通常会将其转化为词袋模型(Bag-of-Words)或者TF-IDF(Term Frequency-Inverse Document Frequency)表示,将文本内容转化为数值向量。 2. **距离度量**:有了文件的向量表示后,我们可以使用...
在实际应用中,用户需要根据具体需求对这些功能进行调用和定制,以实现文本分析的各种任务。 总之,gensim是Python NLP领域不可或缺的工具,无论你是进行学术研究还是商业项目,都能从中受益。其强大的文本表示和...
总的来说,`gensim` 是 Python 中强大的文本处理工具,它提供了一系列先进的算法和技术,使数据科学家和研究人员能够高效地处理和分析大规模文本数据。通过深入理解和应用 `gensim`,可以提升文本挖掘和信息提取的...
基于Vue+SpringBoot实现的基于朴素贝叶斯的敏感词...基于内容的离线推荐 采用词频-逆文档算法(TF-IDF) 对标签进行计算相似度矩阵 #### 实时推荐 采用KafkaStream和SparkStream以及Flume完成对日志的采集、过滤、处理
文本内容比对工具是一种在信息技术领域中用于检测和分析两份或多份文本相似度的软件应用。这种工具广泛应用于各种场景,包括学术论文查重、法律文档对比、代码审查、内容抄袭检测等。以下是关于文本内容比对工具的...
TF-IDF是衡量一个词在整个文档集合中的重要性的方法,而TextRank则是一种基于图论的排序算法,可以找出文本中最关键的句子。 为了实现查重功能,我们可以将每篇论文的题目和内容转化为特征向量,然后利用机器学习...
传统的词频(TF)或TF-IDF方法可能无法捕捉到文本的深度语义信息。因此,本研究引入了信息熵作为特征量化手段,以增强对文本特征的理解和表示。 信息熵是一个衡量信息不确定性的度量,常用于信息理论和数据挖掘中。...
《gensim 4.3.1:Python中的主题建模与文本相似度工具》 gensim是一个开源的Python库,专为处理文本数据而设计,主要用于主题建模和计算文本相似度。在这个版本"gensim-4.3.1-cp310-cp310-win_amd64.whl.zip"中,...
常见的算法有Trie树(字典树)、Levenshtein距离(编辑距离)和基于TF-IDF的文本相似度计算等。Trie树适用于大量关键词的快速查找,能有效减少搜索时间,而Levenshtein距离则用于衡量两个字符串的相似程度。TF-IDF则...