二、对新闻网页进行模型训练生成训练集

原理简介

1.训练模型

首先要获得训练数据,训练数据就是一些网页样本,但是原始的网页并不能直接来作为训练集合, 因为原始网页具有复杂无规律的信息,我们需要根据原始网页提取有用的特征,如内容特征,网站特征,url 特征等等。由于网页信息的庞大,过多的特征等于提升了训练样本的维度,这对训练模型来说有很大的负面影响。

信息含量较低的特征, 尽量保留具有区分度的特征集合。良好的特征直接影响到分类的质量,所以特征提取和筛选是非常重要的一步。通过一次训练并不能保证模型的良好性,这有几方面的原因,一来训练数据自身存在偏差, 比如会存在标记错误的现象, 二来训练数据的正负例比例不协调, 导致模型无法学习到好的参数,还有一种情况就是训练数据覆盖的种类太少,对某型特殊页面不能很好分类。对于这些情况,我们需要反复迭代训练数据,通过添加语料,调整正负例比例,完善训练语料覆盖面等等,让训练数据力争达到覆盖全面、无差错的效果。

2. 网页预处理

网页主要是由html语言书写,它与纯文本之间存在很大差别,主要体现在 一下几方面:

(1)网页包含大量的结构化标签,比如<head>,<title>等,它比纯文本更具表现力,有更多的信息能够被利用,比如通过<head>标签我们很容易得到标签中对应的文本就是网页的标题,再比如不同的字体也有相应标签, 通过字体大小我们可以得到对应文本的重要性,一般标题会用大号的字 体,而正文就是普通字体。

(2)网页中存在大量的超链接。超链接将互联网上的网页连成了一张巨大的 网络,网页上的超链接代表着这个网页到另一个网页的路径,通过超链接我们可以获得网页的一些特征,比如索引页就存在大量的超链接。

(3)网页中包含着大量噪音,包括各种广告、导航、注释以及版权申明等一 些和内容无关的东西,在分类之前需要去除这些噪音,否则这些噪音会 影响分类性能。但有时候也可利用这些噪音,比如网页顶部的导航块和 底部的版权块,如果能确定这两个块的位置,那么位于这连个块之间的 我们可以认为是网页的主体部分。

由于网页和纯文本的这些区别,在进行网页分类时我们需要对其进行预处理 工作。

3.中文分词

中文分词指的是将一个汉字序列切分成一个一个单独的词。分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。

IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出 了3个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。

采用了特有的“正向迭代最细粒度切分算法“,具有60万字/秒的高速处理能力。 采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。对中英联合支持不是很好,在这方面的处理比较麻烦,需再做一次查询,同时是支持个人词条的 。优化的词典存储,更小的内存占用。

4.页面特征提取

网页分类的质量很大程度上取决于特征提取的好坏,网页本身具有复杂无规律的很多特征,而我们需要对这些特征进行提取筛选,选取那些具有区分度的特征,特征的选取主要是通过对网页的预处理提取出网页的文本信息和结构信息,然后利用一些成熟的特征筛选方法进行筛选,包括特征频率(TF)、文档频率 (DF)、信息增益(IG)、互信息(MI)、卡方拟和检验(CHI)以及期望交叉熵(ECE),主要是因为原始网页特征维度太大,其中包含很多噪声,区分度不明显,无法对后序 SVM 学习提供帮助,而且这些未经处理的特征甚至会给分类算法的计算带来巨大的开销, 同时也会对分类效果产生负面影响,因此我们有必要在分类前对这些特征进行提取和筛选,对数据维度进行压缩,保证准确度的前提下尽量减少训练数据的空间维度。

需求描述

完成网页分类必不可少的就是要有训练集,此实验重点讲解我们如何通过编写的程序,将已将采集的大量网页一步步变成训练集的过程。我们采集的网页分成10类,并且分类存储在10个不同的文件夹中,即分别是体育、军事、财经、娱乐、文化、健康、汽车、教育、房产和其它。

实验环境

Linux Ubuntu 16.04

Eclipse 4.2

文件下载

打开终端模拟器

1.切换到data目录下,下载文本集,里面包含有5000篇左右的文档,几乎平均分配了10个不同的新闻网页(也可以用自己采集的新闻网页,但是注意保存路径应与程序中设置的路径相符)

cd /data
wget http://10.51.46.104:32600/allfiles/classify/classifydata.tar.gz

2.解压classifydata.tar.gz包到当前目录

tar zxvf classifydata.tar.gz

3.在/data目录下用wget命令下载该项目的配置文件,jar包等

wget http://10.51.46.104:32600/allfiles/classify/pzwj.tar.gz

4.解压pzwj.tar.gz包到当前目录

tar zxvf pzwj.tar.gz

项目搭建

打开Eclipse,创建一个Java Project项目,命名为createtraintxts。

选中该项目中的src,右键新建一个包,命名为com.yxcx.gettrain

选中该项目名,右键,新建一个Folder,命名为lib

将/data/pzwj下的jar包复制到该项目的lib目录下中,然后选中这两个jar包,右键选中Build Path,点击Add to Build Path

将/date/pzwj下的IKAnalyzer.cfg.xml配置文件和chinese_stopword.dic停用词文件复制到src下面

这时我们就完成了整个项目的搭建,项目结构如下图所示:

程序编写

1.接下来选择com.yxcx.gettrain,右键,创建类文件

在Name处,填写类名

2.第一步,我们创建的类的作用是需要对网页进行预处理,去除网页中大量的标签、超链接和script代码等,然后进行中文分词,并通过IKAnalyzer.cfg.xml加载chinese_stopword.dic停用词文件,去掉停用词,完成文本的预处理。

由此我们新建一个类,命名为DataPreProcess,完整代码如下,代码详细解释可以参照代码中注释。

package com.yxcx.gettrain;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class DataPreProcess {

	public void doProcess(String strDir) throws IOException {
		// strDir 要进行分词的网页的路径
		System.out.println(strDir);
		File fileDir = new File(strDir);
		if (!fileDir.exists()) {
			System.out.println("File not exist:" + strDir);
			return;
		}

		File[] srcFiles = fileDir.listFiles();
		String[] stemFileNames = new String[srcFiles.length];
		for (int i = 0; i < srcFiles.length; i++) {
			String fileFullName = srcFiles[i].getCanonicalPath();

			// 文件名称
			String fileShortName = srcFiles[i].getName();
			if (!new File(fileFullName).isDirectory()) {
				System.out.println("Begin preprocess:" + fileFullName);
				StringBuilder stringBuilder = new StringBuilder();
				// 路径/名称
				stringBuilder.append(strDir + "/" + fileShortName);
				createProcessFile(fileFullName, stringBuilder.toString());
				stemFileNames[i] = stringBuilder.toString();
			} else {
				doProcess(fileFullName);
			}
		}
	}

	public static void createProcessFile(String fileFullName, String toFile) {
		BufferedWriter bw = null;
		BufferedReader br = null;
		FileWriter fileWriter = null;
		FileReader fileReader = null;
		String line, resLine = null;
		try {
			StringBuilder sb = new StringBuilder();

			fileReader = new FileReader(fileFullName);
			br = new BufferedReader(fileReader);
			// readLine方法:一行一行的读取文件
			// 去掉a标签和html标签
			while ((line = br.readLine()) != null) {
				String conts = "";
				line = line.replaceAll("<a(.*?)</a>.", "")
						.replaceAll("</?[^>]+>", "")
        .replaceAll("[^\\u4e00-\\u9fa5]", "");
        conts += line;
        // 开始进行中文分词,用/n分割
        if (!conts.isEmpty()) {
        IKAnalyzer analyzer = new IKAnalyzer(true);
        // 通过分析器Analyzer将一个字符串创建成Taken流,第一个参数是一个名字没有实际作用
        TokenStream tokenStream = analyzer.tokenStream("content",
        new StringReader(conts));
        while (tokenStream.incrementToken()) {
        // 保存相应词汇
        CharTermAttribute charTermAttribute = tokenStream
        .getAttribute(CharTermAttribute.class);
        sb.append(charTermAttribute.toString());
        sb.append("\n");
        }
        resLine = sb.toString();
        analyzer.close();

        }

        }
        fileWriter = new FileWriter(toFile);
        bw = new BufferedWriter(fileWriter);
        bw.write(resLine);
        } catch (Exception e) {
        e.printStackTrace();
        } finally {
        try {
        bw.close();
        br.close();
        fileWriter.close();
        fileReader.close();
        } catch (IOException e) {
        e.printStackTrace();
        }
        }
        }

        /**
        * @param args
        * @throws IOException
        */
        public void BPPMain(String[] args) throws IOException {
        // TODO Auto-generated method stub
        DataPreProcess dataPrePro = new DataPreProcess();
        dataPrePro.doProcess("/data/classifydata/traindata/");
        }

        }

第二步,在进行文本预处理以后,需要提取特征词,为下一步生成训练集做准备。由此我们新建一个类,命名为ComputeWordsVector,在这个类中编写生成特征词的方法,用于最后一步的CreateTrainAndTestSample.java中生成训练集时调用。完整代码如下,代码详细解释可以参照代码注释。

package com.yxcx.gettrain;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.SortedMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Iterator;

public class ComputeWordsVector {
	// 统计每个词的总的出现次数,返回出现次数大于3词的词语的词汇构成最终的属性词典
	public SortedMap<String, Double> countWords(String strDir,
			Map<String, Double> wordMap) throws IOException {
		File sampleFile = new File(strDir);// strDir 预处理好的文件的路径
		File[] sample = sampleFile.listFiles();
		String word;
		for (int i = 0; i < sample.length; i++) {
			if (!sample[i].isDirectory()) {
				FileReader samReader = new FileReader(sample[i]);
				BufferedReader samBR = new BufferedReader(samReader);
				while ((word = samBR.readLine()) != null) {
					if (!word.isEmpty() && wordMap.containsKey(word)) {
						double count = wordMap.get(word) + 1;
						wordMap.put(word, count);
					} else {
						wordMap.put(word, 1.0);
					}
				}
			} else
				countWords(sample[i].getCanonicalPath(), wordMap);
		}
		// 只返回出现次数大于3的词语
		SortedMap<String, Double> newWordMap = new TreeMap<String, Double>();
		Set<Map.Entry<String, Double>> allWords = wordMap.entrySet();
		for (Iterator<Map.Entry<String, Double>> it = allWords.iterator(); it
				.hasNext();) {
			Map.Entry<String, Double> me = it.next();
			if (me.getValue() >= 3) {
				newWordMap.put(me.getKey(), me.getValue());
			}
		}
		return newWordMap;
	}

	// 打印属性词典
	void printWordMap(Map<String, Double> wordMap) throws IOException {
		// TODO Auto-generated method stub
		int countLine = 0;
		File outPutFile = new File(
				"/data/classifydata/docVector/allDicWordCountMap.txt");
		FileWriter outPutFileWriter = new FileWriter(outPutFile);
		Set<Map.Entry<String, Double>> allWords = wordMap.entrySet();
		for (Iterator<Map.Entry<String, Double>> it = allWords.iterator(); it
				.hasNext();) {
			Map.Entry<String, Double> me = it.next();
			outPutFileWriter.write(me.getKey() + " " + me.getValue() + "\n");
			countLine++;
		}
		System.out.println("WordMap size" + countLine);
	}
}

最后一步,生成训练集,新建一个类,命名为CreateTrainAndTestSample,完整代码如下,代码详细解释可以参照代码注释。

package com.yxcx.gettrain;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.SortedMap;
import java.util.TreeMap;

//创建训练集合与测试集合
public class CreateTrainAndTestSample {
	// 根据包含非特征词的文档集生成只包含特征词的文档集到trainSpecial目录下
	ComputeWordsVector cwv = new ComputeWordsVector();

	void trainSpecialWords() throws IOException {
		File file2 = new File("/data/classifydata/trainSpecial/" );
		if(!file2.exists()){
			file2.mkdir();
		}
		File file3 = new File("/data/classifydata/docVector/" );
		if(!file3.exists()){
			file3.mkdir();
		}
		// TODO Auto-generated method stub
		String word;

		String fileDir = "/data/classifydata/traindata/";
		SortedMap<String, Double> wordMap = new TreeMap<String, Double>();

		wordMap = cwv.countWords(fileDir, wordMap);
		// 把wordMap输出到文件
		cwv.printWordMap(wordMap);
		File[] sampleDir = new File(fileDir).listFiles();
		for (int i = 0; i < sampleDir.length; i++) {
			File[] sample = sampleDir[i].listFiles();
			String targetDir = "/data/classifydata/trainSpecial/"
					+ sampleDir[i].getName();
			File targetDirFile = new File(targetDir);
			if (!targetDirFile.exists()) {
				targetDirFile.mkdir();
			}
			for (int j = 0; j < sample.length; j++) {
				String fileShortName = sample[j].getName();
				targetDir = "/data/classifydata/trainSpecial/"
						+ sampleDir[i].getName() + "/" + fileShortName;
				FileWriter tgWriter = new FileWriter(targetDir);
				FileReader samReader = new FileReader(sample[j]);
				BufferedReader samBR = new BufferedReader(samReader);
				while ((word = samBR.readLine()) != null) {
					if (wordMap.containsKey(word)) {
						tgWriter.append(word + "\n");
					}
				}
				tgWriter.flush();
				tgWriter.close();
			}
		}
	}

	public void NaiveBayesianClassifierMain(String[] args) throws Exception {
		CreateTrainAndTestSample ctt = new CreateTrainAndTestSample();
		ctt.trainSpecialWords();

	}

	public static void main(String[] args) throws Exception {
		DataPreProcess DataPP = new DataPreProcess();
		DataPP.BPPMain(args);
		CreateTrainAndTestSample ctt = new CreateTrainAndTestSample();
		ctt.trainSpecialWords();
	}
}

程序编写完成,执行Run As,运行项目

至此,我们已经得到训练集了,打开文件系统

进入到/data/classifydata/目录中,traindata中存放的是将网页预处理完后的文本集,trainSpecial目录下存放的是我们下一步实验所需要的训练集,docVector目录下存放的是属性词典。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RoBERTa中文预训练模型 概述 中文预训练RoBERTa模型 RoBERTa是BERT的改进版,通过改进训练任务和数据生成方式、训练更久、使用更大批次、使用更多数据等获得了State of The Art的效果;可以用Bert直接加载。 本项目是用TensorFlow实现了在大规模中文上RoBERTa的预训练,也会提供PyTorch的预训练模型和加载方式。 中文预训练RoBERTa模型-下载 6层RoBERTa体验版 RoBERTa-zh-Layer6: Google Drive 或 百度网盘,TensorFlow版本,Bert 直接加载, 大小为200M 推荐 RoBERTa-zh-Large 通过验证 RoBERTa-zh-Large: Google Drive 或 百度网盘 ,TensorFlow版本,Bert 直接加载 RoBERTa-zh-Large: Google Drive 或 百度网盘 ,PyTorch版本,Bert的PyTorch版直接加载 RoBERTa 24/12层版训练数据:30G原始文本,近3亿个句子,100亿个中文字(token),产生了2.5亿个训练数据(instance);覆盖新闻、社区问答、多个百科数据等; 本项目与中文预训练24层XLNet模型 XLNet_zh项目,使用相同的训练数据。 RoBERTa_zh_L12: Google Drive 或 百度网盘 TensorFlow版本,Bert 直接加载 RoBERTa_zh_L12: Google Drive 或百度网盘 PyTorch版本,Bert的PyTorch版直接加载 Roberta_l24_zh_base TensorFlow版本,Bert 直接加载 24层base版训练数据:10G文本,包含新闻、社区问答、多个百科数据等 什么是RoBERTa: 一种强大的用于预训练自然语言处理(NLP)系统的优化方法,改进了Transformers或BERT的双向编码器表示形式,这是Google在2018年发布的自监督方法。 RoBERTa在广泛使用的NLP基准通用语言理解评估(GLUE)上产生最先进的结果。 该模型在MNLI,QNLI,RTE,STS-B和RACE任务上提供了最先进的性能,并在GLUE基准上提供了可观的性能改进。 RoBERTa得分88.5,在GLUE排行榜上排名第一,与之前的XLNet-Large的表现相当。 效果测试与对比 Performance 互联网新闻情感分析:CCF-Sentiment-Analysis 模型 线上F1 BERT 80.3 Bert-wwm-ext 80.5 XLNet 79.6 Roberta-mid 80.5 Roberta-large (max_seq_length=512, split_num=1) 81.25 注:数据来源于guoday的开源项目;数据集和任务介绍见:CCF互联网新闻情感分析 自然语言推断:XNLI 模型 开发集 测试集 BERT 77.8 (77.4) 77.8 (77.5) ERNIE 79.7 (79.4) 78.6 (78.2) BERT-wwm 79.0 (78.4) 78.2 (78.0) BERT-wwm-ext 79.4 (78.6) 78.7 (78.3) XLNet 79.2 78.7 RoBERTa-zh-base 79.8 78.8 RoBERTa-zh-Large 80.2 (80.0) 79.9 (79.5) 注:RoBERTa_l24_zh,只跑了两次,Performance可能还会提升; BERT-wwm-ext来自于这里;XLNet来自于这里; RoBERTa-zh-base,指12层RoBERTa中文模型 问题匹配语任务:LCQMC(Sentence Pair Matching) 模型 开发集(Dev) 测试集(Test) BERT 89.4(88.4) 86.9(86.4) ERNIE 89.8 (89.6) 87.2 (87.0) BERT-wwm 89.4 (89.2) 87.0 (86.8) BERT-wwm-ext - - RoBERTa-zh-base 88.7 87.0 RoBERTa-zh-Large 89.9(89.6) 87.2(86.7) RoBERTa-zh-Large(20w_steps) 89.7 87.0 注:RoBERTa_l24_zh,只跑了两次,Performance可能还会提升。保持训练轮次和论文一致: 阅读理解测试 目前阅读理解类问题bert和roberta最优参数均为epoch2, batch=32, lr=3e-5, warmup=0.1 cmrc20

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值