余弦定理实现新闻自动分类算法

前言

余弦定理,这个在初中课本中就出现过的公式,恐怕没有人不知道的吧。但是另外一个概念,可能不是很多的人会听说过,他叫空间向量,一般用e表示,高中课本中有专门讲过这个东西,有了余弦定理和向量空间,我们就可以做许多有意思的事情了,利用余弦定理计算文本相似度的算法就是其中一个很典型的例子。当然这个话题太老,说的人太多,没有什么新意,恰巧周末阅读了吴军博士的<<数学之美>>这门书,书中讲到了利用余弦定理实现新闻分类,于是就索性完成这个算法的初步模型。感兴趣的可以继续往下看。

算法背景

在以往,如果对一则新闻进行归类,一般使用的都是人工分类的办法,大体上看一下标题和首尾两段文字,就能知道新闻是属于财经的,体育的又或者是健康类的。但是在当今信息爆炸的时代,这显然是不可能完成的任务,所以我们急切的相用机器自己帮我们”分类“。最好的形式是我给计算机提供大量的已分类好的数据,等强大的计算机大脑训练好了这个分类模型,后边的事情就是他来完成了。看起来这好像很高深,很困难的样子,但是其实我们自己也可以写一个,只是效果可能不会那么好。

分类器实现原理

新闻自动分类器实现的本质也是利用余弦定理比较文本的相似度,于是这个问题的难点就在于这个特征向量哪里来,怎么去获得。特征向量,特征向量,关键两个字在于特征,新闻的特征就在于他的关键词,我的简单理解就是专业性的词语,换句话说,就是属于某类新闻特有的词语,比如金融类的新闻,关键词一般就是股票啊,公司啊,上市啊等等词语。这些词的寻找可以通过统计词频的方式实现,最后统计出来的关键词,进行降序排列,一个关键词就代表一个新的维度。 那么新的问题又来了,我要统计词频,那么就得首先进行分词,要把每个新闻句子的主谓宾统统挖掘出来啊,好像这个工作比我整个算法还要复杂的样子。OK,其实已经有人已经帮我们把这个问题解决了,在这个算法中我使用的是中科大的ICTCLAS分词系统,效果非常棒,举个例子,下面是我原始的新闻内容:

教育部副部长:教育公平是社会公平重要基础
7月23日,教育部党组副书记、副部长杜玉波为全国学联全体代表作《教育综合改革与青年学生成长成才》的专题报告。 中国青年网记者 张炎良 摄
人民网北京7月24日电(记者 贺迎春 实习生 王斯慧

经过分词系统处理后的分词效果:

教育部/nt 副/b 部长/n :/wm 教育/v 公平/an 是/vshi 社会/n 公平/a 重要/a 基础/n
7月/t 23日/t ,/wd 教育部/nt 党组/n 副/b 书记/n 、/wn 副/b 部长/n 杜玉波/nr 为/p 全国学联/nt 全体/n 代表作/n 《/wkz 教育/vn 综合/vn 改革/vn 与/cc 青年/n 学生/n 成长/vi 成才/vi 》/wky 的/ude1 专题/n 报告/n 。/wj  中国/ns 青年/n 网/n 记者/n  张/q 炎/ng 良/d  摄/vg
人民/n 网/n 北京/ns 7月/t 24日/t 电/n (/wkz 记者/n  贺/vg 迎春/n  实习生/n  王斯慧/nr )/wky 昨日/t ,/wd 教育部/nt 副/b 部长

OK,有了这个分词的结果之后,后面的事情就水到渠成了。

算法的实现步骤

1、给定训练的新闻数据集。

2、通过分词系统统计词频的方式,统计词频最高的N位作为特征词,即特征向量

3、输入测试数据,同样统计词频,并于训练数据的进行商的操作,得到特征向量值

4、最后利用余弦定理计算相似度,并与最小阈值做比较。

算法的代码实现

ICTCLAS工具类ICTCLAS.java:

package NewsClassify;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.StringTokenizer;

public class ICTCLAS50 {
	static {
		try {
			String libpath = System.getProperty("user.dir") + "\\lib";
			String path = null;
			StringTokenizer st = new StringTokenizer(libpath,
					System.getProperty("path.separator"));
			if (st.hasMoreElements()) {
				path = st.nextToken();
			}

			// copy all dll files to java lib path
			File dllFile = null;
			InputStream inputStream = null;
			FileOutputStream outputStream = null;
			byte[] array = null;

			dllFile = new File(new File(path), "ICTCLAS50.dll");
			if (!dllFile.exists()) {
				inputStream = ICTCLAS50.class.getResource("/lib/ICTCLAS50.dll")
						.openStream();
				outputStream = new FileOutputStream(dllFile);
				array = new byte[1024];
				for (int i = inputStream.read(array); i != -1; i = inputStream
						.read(array)) {
					outputStream.write(array, 0, i);
				}
				outputStream.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			// load JniCall.dll
			System.loadLibrary("ICTCLAS50");
			System.out.println("4444");
		} catch (Error e) {

			e.printStackTrace();
		}
	}

	public native boolean ICTCLAS_Init(byte[] sPath);

	public native boolean ICTCLAS_Exit();

	public native int ICTCLAS_ImportUserDictFile(byte[] sPath, int eCodeType);

	public native int ICTCLAS_SaveTheUsrDic();

	public native int ICTCLAS_SetPOSmap(int nPOSmap);

	public native boolean ICTCLAS_FileProcess(byte[] sSrcFilename,
			int eCodeType, int bPOSTagged, byte[] sDestFilename);

	public native byte[] ICTCLAS_ParagraphProcess(byte[] sSrc, int eCodeType,
			int bPOSTagged);

	public native byte[] nativeProcAPara(byte[] sSrc, int eCodeType,
			int bPOStagged);
}

新闻实体类New.java

package NewsClassify;

/**
 * 词语实体类
 *
 * @author lyq
 *
 */
public class Word implements Comparable<Word> {
	// 词语名称
	String name;
	// 词频
	Integer count;

	public Word(String name, Integer count) {
		this.name = name;
		this.count = count;
	}

	@Override
	public int compareTo(Word o) {
		// TODO Auto-generated method stub
		return o.count.compareTo(this.count);
	}
}

分类算法类NewsClassify.java:

package NewsClassify;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

/**
 * 分类算法模型
 *
 * @author lyq
 *
 */
public class NewsClassifyTool {
	// 余弦向量空间维数
	private int vectorNum;
	// 余弦相似度最小满足阈值
	private double minSupportValue;
	// 当前训练数据的新闻类别
	private String newsType;
	// 训练新闻数据文件地址
	private ArrayList<String> trainDataPaths;

	public NewsClassifyTool(ArrayList<String> trainDataPaths, String newsType,
			int vectorNum, double minSupportValue) {
		this.trainDataPaths = trainDataPaths;
		this.newsType = newsType;
		this.vectorNum = vectorNum;
		this.minSupportValue = minSupportValue;
	}

	/**
	 * 从文件中读取数据
	 */
	private String readDataFile(String filePath) {
		File file = new File(filePath);
		StringBuilder strBuilder = null;

		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			String str;
			strBuilder = new StringBuilder();
			while ((str = in.readLine()) != null) {
				strBuilder.append(str);
			}
			in.close();
		} catch (IOException e) {
			e.getStackTrace();
		}

		return strBuilder.toString();
	}

	/**
	 * 计算测试数据的特征向量
	 */
	private double[] calCharacterVectors(String filePath) {
		int index;
		double[] vectorDimensions;
		double[] temp;
		News news;
		News testNews;
		String newsCotent;
		String testContent;
		String parseContent;
		// 高频词汇
		ArrayList<Word> frequentWords;
		ArrayList<Word> wordList;

		testContent = readDataFile(filePath);
		testNews = new News(testContent);
		parseNewsContent(filePath);

		index = filePath.indexOf(‘.‘);
		parseContent = readDataFile(filePath.substring(0, index) + "-split.txt");
		testNews.statWords(parseContent);

		vectorDimensions = new double[vectorNum];
		// 计算训练数据集的类别的特征向量
		for (String path : this.trainDataPaths) {
			newsCotent = readDataFile(path);
			news = new News(newsCotent);

			// 进行分词操作
			index = path.indexOf(‘.‘);
			parseNewsContent(path);
			parseContent = readDataFile(path.substring(0, index) + "-split.txt");
			news.statWords(parseContent);

			wordList = news.wordDatas;
			// 将词频统计结果降序排列
			Collections.sort(wordList);

			frequentWords = new ArrayList<Word>();
			// 截取出前vectorDimens的词语
			for (int i = 0; i < vectorNum; i++) {
				frequentWords.add(wordList.get(i));
			}

			temp = testNews.calVectorDimension(frequentWords);
			// 将特征向量值进行累加
			for (int i = 0; i < vectorDimensions.length; i++) {
				vectorDimensions[i] += temp[i];
			}
		}

		// 最后取平均向量值作为最终的特征向量值
		for (int i = 0; i < vectorDimensions.length; i++) {
			vectorDimensions[i] /= trainDataPaths.size();
		}

		return vectorDimensions;
	}

	/**
	 * 根据求得的向量空间计算余弦相似度值
	 *
	 * @param vectorDimension
	 *            已求得的测试数据的特征向量值
	 * @return
	 */
	private double calCosValue(double[] vectorDimension) {
		double result;
		double num1;
		double num2;
		double temp1;
		double temp2;
		// 标准的特征向量,每个维度上都为1
		double[] standardVector;

		standardVector = new double[vectorNum];
		for (int i = 0; i < vectorNum; i++) {
			standardVector[i] = 1;
		}

		temp1 = 0;
		temp2 = 0;
		num1 = 0;

		for (int i = 0; i < vectorNum; i++) {
			// 累加分子的值
			num1 += vectorDimension[i] * standardVector[i];

			// 累加分母的值
			temp1 += vectorDimension[i] * vectorDimension[i];
			temp2 += standardVector[i] * standardVector[i];
		}

		num2 = Math.sqrt(temp1) * Math.sqrt(temp2);
		// 套用余弦定理公式进行计算
		result = num1 / num2;

		return result;
	}

	/**
	 * 进行新闻分类
	 *
	 * @param filePath
	 *            测试新闻数据文件地址
	 */
	public void newsClassify(String filePath) {
		double result;
		double[] vectorDimension;

		vectorDimension = calCharacterVectors(filePath);
		result = calCosValue(vectorDimension);

		// 如果余弦相似度值满足最小阈值要求,则属于目标分类
		if (result >= minSupportValue) {
			System.out.println(String.format("最终相似度结果为%s,大于阈值%s,所以此新闻属于%s类新闻",
					result, minSupportValue, newsType));
		} else {
			System.out.println(String.format("最终相似度结果为%s,小于阈值%s,所以此新闻不属于%s类新闻",
					result, minSupportValue, newsType));
		}
	}

	/**
	 * 利用分词系统进行新闻内容的分词
	 *
	 * @param srcPath
	 *            新闻文件路径
	 */
	private void parseNewsContent(String srcPath) {
		// TODO Auto-generated method stub
		int index;
		String dirApi;
		String desPath;

		dirApi = System.getProperty("user.dir") + "\\lib";
		// 组装输出路径值
		index = srcPath.indexOf(‘.‘);
		desPath = srcPath.substring(0, index) + "-split.txt";

		try {
			ICTCLAS50 testICTCLAS50 = new ICTCLAS50();
			// 分词所需库的路径、初始化
			if (testICTCLAS50.ICTCLAS_Init(dirApi.getBytes("GB2312")) == false) {
				System.out.println("Init Fail!");
				return;
			}
			// 将文件名string类型转为byte类型
			byte[] Inputfilenameb = srcPath.getBytes();

			// 分词处理后输出文件名、将文件名string类型转为byte类型
			byte[] Outputfilenameb = desPath.getBytes();

			// 文件分词(第一个参数为输入文件的名,第二个参数为文件编码类型,第三个参数为是否标记词性集1 yes,0
			// no,第四个参数为输出文件名)
			testICTCLAS50.ICTCLAS_FileProcess(Inputfilenameb, 0, 1,
					Outputfilenameb);
			// 退出分词器
			testICTCLAS50.ICTCLAS_Exit();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}
}

场景测试了Client.java:

package NewsClassify;

import java.util.ArrayList;

/**
 * 新闻分类算法测试类
 * @author lyq
 *
 */
public class Client {
	public static void main(String[] args){
		String testFilePath1;
		String testFilePath2;
		String testFilePath3;
		String path;
		String newsType;
		int vectorNum;
		double minSupportValue;
		ArrayList<String> trainDataPaths;
		NewsClassifyTool classifyTool;

		//添加测试以及训练集数据文件路径
		testFilePath1 = "C:\\Users\\lyq\\Desktop\\icon\\test\\testNews1.txt";
		testFilePath2 = "C:\\Users\\lyq\\Desktop\\icon\\test\\testNews2.txt";
		testFilePath3 = "C:\\Users\\lyq\\Desktop\\icon\\test\\testNews3.txt";
		trainDataPaths = new ArrayList<String>();
		path = "C:\\Users\\lyq\\Desktop\\icon\\test\\trainNews1.txt";
		trainDataPaths.add(path);
		path = "C:\\Users\\lyq\\Desktop\\icon\\test\\trainNews2.txt";
		trainDataPaths.add(path);

		newsType = "金融";
		vectorNum = 10;
		minSupportValue = 0.45;

		classifyTool = new NewsClassifyTool(trainDataPaths, newsType, vectorNum, minSupportValue);
		classifyTool.newsClassify(testFilePath1);
		classifyTool.newsClassify(testFilePath2);
		classifyTool.newsClassify(testFilePath3);
	}

}

结果输出:

最终相似度结果为0.39999999999999997,小于阈值0.45,所以此新闻不属于金融类新闻
最终相似度结果为0.4635393084189425,大于阈值0.45,所以此新闻属于金融类新闻
最终相似度结果为0.661835948543857,大于阈值0.45,所以此新闻属于金融类新闻

测试数据以及全部代码,链接在此:https://github.com/linyiqun/news-classifier

参考文献

百度百科

<<数学之美>>第二版.吴军博士

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-06 19:21:15

余弦定理实现新闻自动分类算法的相关文章

从网页相关性TF-IDF到余弦定理的新闻分类的程序实现

前提:TF-IDF 模型是搜索引擎等实际应用中被广泛使用的信息检索模型,但对于 TF-IDF 模型一直存在各种疑问.本文为信息检索问题一种基于条件概率的盒子小球模型,其核心思想是把"查询串q和文档d的匹配度问题"转化为"查询串q来自于文档d的条件概率问题".它从概率的视角为信息检索问题定义了比 TF-IDF 模型所表达的匹配度更为清晰的目标.此模型可将 TF-IDF 模型纳入其中,一方面解释其合理性,另一方面也发现了其不完善之处.另外,此模型还可以解释 PageRa

读《数学之美系列十二——余弦定理和新闻的分类》有感 + 代码规范

Google的新闻是自动分类而产生的,但是计算机只懂算法,是看不懂我们人类的新闻.若是人为地一个新闻一个新闻地划分又会浪费不必要的人力.物理.由此,我们设计出一个算法,帮助我们利用计算机,自动地划分每天数量庞大地新闻. 算法涉及的方面不多:TF-IDF算法.余弦定理 看过的TF-IDF算法写的比较详细的,链接如下: https://blog.csdn.net/asialee_bird/article/details/81486700 简单来说可以运用这个算法,把一个个的新闻映射成为向量的形式.而

自己实现文本相似度算法(余弦定理)

最近由于工作项目,需要判断两个txt文本是否相似,于是开始在网上找资料研究,因为在程序中会把文本转换成String再做比较,所以最开始找到了这篇关于 距离编辑算法 Blog写的非常好,受益匪浅. 于是我决定把它用到项目中,来判断两个文本的相似度.但后来实际操作发现有一些问题:直接说就是查询一本书中的相似章节花了我7.8分钟:这是我不能接受…… 于是停下来仔细分析发现,这种算法在此项目中不是特别适用,由于要判断一本书中是否有相同章节,所以每两个章节之间都要比较,若一本书书有x章的话,这里需对比x(

利用余弦定理计算文本的相似度

#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import division import jieba.analyse from math import sqrt class Similarity(): def __init__(self, target1, target2, topK=10): self.target1 = target1 self.target2 = target2 self.topK = topK

《zw版&#183;Halcon-delphi系列原创教程》 水果自动分类脚本(机器学习、人工智能)

<zw版·Halcon-delphi系列原创教程> 水果自动分类脚本(机器学习.人工智能) 前面介绍了超市,流水线,酸奶的自动分类算法,下面再介绍一个水果的自动分类算法. Halcon强大的图像处理能力,令人往往会忽视其更加彪悍的机器学习.人工智能.       分类,聚类分析,是机器学习.人工智能的核心算法之一,也是个典型的应用. Halcon内置的聚类分析.机器学习模块,就有:knn邻近算法.向量机SVM.GMM高斯混合模型(Gaussian Mixture Model,或者混合高斯模型,

朴树贝叶斯新闻分类系统

基于搜狗语料库,建立的一个新闻分类系统:类别包括: classifierMap.put(0, "IT"); classifierMap.put(1, "体育"); classifierMap.put(2, "健康"); classifierMap.put(3, "军事"); classifierMap.put(4, "招聘"); classifierMap.put(5, "教育"); c

数据挖掘算法之k-means算法

系列文章:数据挖掘算法之决策树算法 [QQ群: 189191838,对算法和C++感兴趣可以进来]       k-means算法可以说是数据挖掘中十大经典算法之一了,属于无监督的学习.该算法由此衍生出了很多类k-means算法,比如k中心点等等,在数据挖掘领域,很多地方都会用到该算法,他能够把相似的一类很好的聚在一起.一类指的是,他们之间的相似度较高,计算相似度的常用度量有欧氏距离.余弦定理等.本算法采用的是欧式距离度量.这个对理解k-means算法不会造成任何实质性的影响. 为了更好的说明k

读《数学之美》

其实准备读<数学之美>这本书,是从很久开始的.记得去年夏天的时候,是读了<浪潮之巅>,然后就认识了吴军这样的一个人,很喜欢他的写作风格.于是就准备读<数学之美>的. <数学之美>这本书,本身和<浪潮之巅>一样,已经在众多的读者中有了一个很好的口碑了,吴军博士本身的才学和研究,对于更多的人来说,也形成了一种尊重的心理.在我读了<浪潮之巅>后,有种很厚重的历史沧桑感堆积于胸.不得不承认<浪潮之巅>对于我的影响. <数学之

[读书笔记]数学之美里的机器学习

这几天陆陆续续把吴军博士的<数学之美>看完了. 整体来说,<数学之美>是一本非常适合于数学不好的人入门机器学习和理解计算机算法原理的科普书.作者结合他多年搞研究和在GOOGLE的经验,把他所理解的机器学习/自然语言处理的发展史一一得梳理了出来,颇有提纲挈领的功效. 在看完这本书后,可以按着里面的线索再去搜相关资料来看,比以前直接上手就看数据挖掘.算法啥的靠谱多了.作者在书里多次推崇[简单的数学模型可以做大事],[换个思路],[做搜索的人要经常研究一下不好的结果/异常值分析],[道]