Java---利用互信息方式进特征选择,用于文本分类

Java—利用互信息方式进特征选择

任务定义

原始材料:多个类别文件,每个文件包含若干样本
目标:从每个类别文件中提取若干词语,这些词具有类区分度,即在类A中常出现,在类B中不常出现。
方法:使用互信息方式进行词语提取。

步骤
  1. 构建字典:Map<词语,出现的该词的文档数>
    1)构建两种字典:大字典—所有文本;小字典—单个类别文件。
    2)逐个读取文件夹内的每个类别文件,每个文件中包含有若干行样本。对每行文本进行分词,过滤,去除重复词。
    3)根据返回的词集合,更新小字典,大字典。
    4)得到若干个类别字典和一个大字典

  2. 计算互信息值
    计算公式请看:互信息公式
    其中: (以下为个人理解,不敢保证为公式所定义的)
    N11:为在该类中,该词出现的样本数
    N01:为在该类中,该词未出现的样本数
    N10:为除该类外,该词出现的样本数
    N00:为除该类外,该词未出现的样本数
    N:为总的样本数
    N1.:为在大字典中该词出现的样本数
    N0.:为在大字典中该词未出现的样本数
    N.1:为该类别的样本数
    N.0:为其他类别的样本数

代码
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.ansj.recognition.impl.StopRecognition;
import org.ansj.splitWord.analysis.ToAnalysis;

public class mutual_info {
	
	//存放所有的大词典
	private static Map<String, Double> bigMap = new HashMap<String, Double>();
	// 存放每个知识点文件中的样本数
	private static Map<String, Double> classNum = new HashMap<String, Double>();
	// 样本总数
	private static Double samplesum = 0.0;

	public static void main(String[] args) throws Exception {
		// Analyzer analyzer = new AnsjAnalyzer(TYPE.index_ansj);
		System.out.println("Begin:");
		// 知识点文件夹路径
		String path = "xxxxx/";
		//互信息值文件夹保存路径
		String savepath = "xxxxx/";
		String[] filename = new File(path).list();
		
		System.out.println("Building Map...");
		// 用于存放各个小词典
		Map<String, Map<String, Double>> allMap = new HashMap<String, Map<String, Double>>();

		for (String file : filename) {
			System.out.println(file);
			allMap.put(file.replace(".txt", ""), buildMap(path + file, file.replace(".txt", "")));
		}
		System.out.println("Saving...");
		// 计算互信息值,每个知识点文件对应一个互信息文件,包含知识点内每个词汇的互信息值
		for (int i = 0; i < filename.length; i++) {
			// 保存互信息
			String temp = filename[i].replace(".txt", "");
			System.out.println(temp);
			saveMutualInfo(allMap.get(temp), savepath + filename[i], classNum.get(temp));
		}
		
		//测试单个词语于单个类别当中的互信息值
//		String className = "电磁学-传感器";
//		String word = "用气";
//		Double value = getMutualInfo(word, allMap.get(className).get(word), classNum.get(className));
//		System.out.println(value);
		
		System.out.println("End.");
	}

	/**
	 * 将属于该知识点内词汇的互信息值保存
	 * @param sMap
	 * @param savepath
	 */
	public static void saveMutualInfo(Map<String, Double> sMap, String savepath, Double num) throws Exception {
		// 打开写入文件
		FileWriter fw = new FileWriter(savepath, true);
		PrintWriter out = new PrintWriter(fw);
		Double value = 0.0;
		out.write("key" + "\t" + "value");
		out.println();
		for (Map.Entry<String, Double> entry : sMap.entrySet()) {
			// 计算互信息值
			value = getMutualInfo(entry.getKey(), entry.getValue(), num);
			// 写入数据
			out.write(entry.getKey() + "\t" + String.valueOf(value));
			out.println();
		}
		// 关闭文件
		fw.close();
		out.close();
	}
	/**
	 * 
	 * @param word
	 *            该词
	 * @param fre
	 *            该词在文档中出现的数量
	 * @param num
	 *            该文档的样本总数
	 * @return
	 */
	public static double getMutualInfo(String word, Double fre, Double num) {
		// 在总样本数上,该词出现的样本数与未出现的样本数
		Double N1 = bigMap.get(word);
		Double N0 = samplesum - N1;

		Double N11 = fre; //在该类中,该词出现的样本数
		Double N01 = num - fre; //在该类中,该词未出现的样本数
		Double N10 = N1 - fre + 1; // 加 1 防止NaN的出现,除该类外,该词出现的样本数
		Double N00 = samplesum - num - N10;// 除该类外,该词未出现的样本数

		return ((N11 * Math.log((samplesum * N11) / (N1 * num)) / Math.log(2.0))
				+ (N01 * Math.log((samplesum * N01) / (N0 * num)) / Math.log(2.0))
				+ (N10 * Math.log((samplesum * N10) / (N1 * (samplesum - num))) / Math.log(2.0))
				+ (N00 * Math.log((samplesum * N00) / (N0 * (samplesum - num))) / Math.log(2.0)));
	}

	/**
	 * 根据文件名,构建该知识点的map;同时,对大词典进行更新
	 * 
	 * @param filepath
	 *            文件名
	 * @return
	 */
	public static Map<String, Double> buildMap(String filepath, String className) throws Exception {
		// 专属该知识点的 小词典
		Map<String, Double> result = new HashMap<String, Double>();

		// 打开文件
		FileInputStream fis = new FileInputStream(filepath);
		BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
		String line = "";
		Double count = 0.0;
		while ((line = br.readLine()) != null) {
			// 更新小词典
			updateSmallMap(result, wordAnalyzer(line));
			// 更新大词典
			updateBigMap(wordAnalyzer(line));
			count++;
		}
		// 关闭文件
		br.close();
		fis.close();
		// 保存该文件内的文本数量
		classNum.put(className, count);
		samplesum += count;

		return result;
	}
	/**
	 * 对大字典进行更新
	 * 
	 * @param words
	 */
	public static void updateBigMap(Set<String> words) {
		// 具有某个词的文档数
		Double count;
		for (String string : words) {
			count = bigMap.get(string);
			if (count == null) {
				bigMap.put(string, 1.0);
			} else {
				bigMap.put(string, count + 1);
			}
		}
	}
	
	/**
	 * 对单个字典进行更新
	 * @param sMap
	 * @param words
	 * @return
	 */
	public static Map<String, Double> updateSmallMap(Map<String, Double> sMap, Set<String> words) {
		// 某个词出现的频次
		Double count;
		for (String string : words) {
			count = sMap.get(string);
			if (count == null) {
				sMap.put(string, 1.0);
			} else {
				sMap.put(string, count + 1);
			}
		}
		return sMap;
	}

	/**
	 * 对单行文本进行分词,分词后每个词以空格相隔开
	 * @param line
	 * @return
	 */
	public static Set<String> wordAnalyzer(String line) {
		Set<String> result = new TreeSet<String>();
		StopRecognition filter = new StopRecognition();
		filter.insertStopNatures("w"); // 过滤标点
		filter.insertStopNatures("null"); // 过滤空格
		filter.insertStopNatures("m"); //过滤数词,会将 半数 该词当做是数词进行过滤
		filter.insertStopWords("的"); // 过滤单个词
		Result fliterContent = ToAnalysis.parse(line).recognition(filter);
		for (Term term : fliterContent) {
			if (!result.contains(term.getName())&&(term.getName().length()!=1)) {
				result.add(term.getName());
			}
		}
		return result;
	}
}

所使用的分词依赖:(可尝试多种分词方式,会带来不同结果)

<dependency>
    <groupId>org.ansj</groupId>
    <artifactId>ansj_seg</artifactId>
    <version>5.1.3</version>
</dependency>
完!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值