Python自然語言處理工具小結


Python自然語言處理工具小結

作者:白寧超

2016年11月21日21:45:26

目錄


【Python NLP】干貨!詳述Python NLTK下如何使用stanford NLP工具包(1)

【Python NLP】Python 自然語言處理工具小結(2)

【Python NLP】Python NLTK 走進大秦帝國(3)

【Python NLP】Python NLTK獲取文本語料和詞匯資源(4)

【Python NLP】Python NLTK處理原始文本(5)

1 Python 的幾個自然語言處理工具

  1. NLTK:NLTK 在用 Python 處理自然語言的工具中處於領先的地位。它提供了 WordNet 這種方便處理詞匯資源的借口,還有分類、分詞、除莖、標注、語法分析、語義推理等類庫。
  2. Pattern:Pattern 的自然語言處理工具有詞性標注工具(Part-Of-Speech Tagger),N元搜索(n-gram search),情感分析(sentiment analysis),WordNet。支持機器學習的向量空間模型,聚類,向量機。
  3. TextBlob:TextBlob 是一個處理文本數據的 Python 庫。提供了一些簡單的api解決一些自然語言處理的任務,例如詞性標注、名詞短語抽取、情感分析、分類、翻譯等等。

  4. Gensim:Gensim 提供了對大型語料庫的主題建模、文件索引、相似度檢索的功能。它可以處理大於RAM內存的數據。作者說它是“實現無干預從純文本語義建模的最強大、最高效、最無障礙的軟件。
  5. PyNLPI:它的全稱是:Python自然語言處理庫(Python Natural Language Processing Library,音發作: pineapple) 這是一個各種自然語言處理任務的集合,PyNLPI可以用來處理N元搜索,計算頻率表和分布,建立語言模型。他還可以處理向優先隊列這種更加復雜的數據結構,或者像 Beam 搜索這種更加復雜的算法。
  6. spaCy:這是一個商業的開源軟件。結合Python和Cython,它的自然語言處理能力達到了工業強度。是速度最快,領域內最先進的自然語言處理工具。
  7. Polyglot:Polyglot 支持對海量文本和多語言的處理。它支持對165種語言的分詞,對196中語言的辨識,40種語言的專有名詞識別,16種語言的詞性標注,136種語言的情感分析,137種語言的嵌入,135種語言的形態分析,以及69中語言的翻譯。
  8. MontyLingua:MontyLingua 是一個自由的、訓練有素的、端到端的英文處理工具。輸入原始英文文本到 MontyLingua ,就會得到這段文本的語義解釋。適合用來進行信息檢索和提取,問題處理,回答問題等任務。從英文文本中,它能提取出主動賓元組,形容詞、名詞和動詞短語,人名、地名、事件,日期和時間,等語義信息。
  9. BLLIP Parser:BLLIP Parser(也叫做Charniak-Johnson parser)是一個集成了產生成分分析和最大熵排序的統計自然語言工具。包括 命令行 和 python接口 。
  10. Quepy:Quepy是一個Python框架,提供將自然語言轉換成為數據庫查詢語言。可以輕松地實現不同類型的自然語言和數據庫查詢語言的轉化。所以,通過Quepy,僅僅修改幾行代碼,就可以實現你自己的自然語言查詢數據庫系統。GitHub:https://github.com/machinalis/quepy
  11. HanNLPHanLP是由一系列模型與算法組成的Java工具包,目標是普及自然語言處理在生產環境中的應用。不僅僅是分詞,而是提供詞法分析、句法分析、語義理解等完備的功能。HanLP具備功能完善、性能高效、架構清晰、語料時新、可自定義的特點。文檔使用操作說明:Python調用自然語言處理包HanLP 和 菜鳥如何調用HanNLP

OpenNLP:進行中文命名實體識別

OpenNLP是Apach下的Java自然語言處理API,功能齊全。如下給大家介紹一下使用OpenNLP進行中文語料命名實體識別的過程。

首先是預處理工作,分詞去聽用詞等等的就不啰嗦了,其實將分詞的結果中間加上空格隔開就可以了,OpenNLP可以將這樣形式的的語料照處理英文的方式處理,有些關於字符處理的注意點在后面會提到。

其次我們要准備各個命名實體類別所對應的詞庫,詞庫被存在文本文檔中,文檔名即是命名實體類別的TypeName,下面兩個function分別是載入某類命名實體詞庫中的詞和載入命名實體的類別。

/**
	 * 載入詞庫中的命名實體
	 * 
	 * @param nameListFile
	 * @return
	 * @throws Exception
	 */
	public static List<String> loadNameWords(File nameListFile)
			throws Exception {
		List<String> nameWords = new ArrayList<String>();

		if (!nameListFile.exists() || nameListFile.isDirectory()) {
			System.err.println("不存在那個文件");
			return null;
		}

		BufferedReader br = new BufferedReader(new FileReader(nameListFile));
		String line = null;
		while ((line = br.readLine()) != null) {
			nameWords.add(line);
		}

		br.close();

		return nameWords;
	}

	/**
	 * 獲取命名實體類型
	 * 
	 * @param nameListFile
	 * @return
	 */
	public static String getNameType(File nameListFile) {
		String nameType = nameListFile.getName();

		return nameType.substring(0, nameType.lastIndexOf("."));
	}

因為OpenNLP要求的訓練語料是這樣子的:

XXXXXX<START:Person>????<END>XXXXXXXXX<START:Action>????<END>XXXXXXX  

被標注的命名實體被放在<START><END>范圍中,並標出了實體的類別。接下來是對命名實體識別模型的訓練,先上代碼:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;

import opennlp.tools.namefind.NameFinderME;
import opennlp.tools.namefind.NameSample;
import opennlp.tools.namefind.NameSampleDataStream;
import opennlp.tools.namefind.TokenNameFinderModel;
import opennlp.tools.util.ObjectStream;
import opennlp.tools.util.PlainTextByLineStream;
import opennlp.tools.util.featuregen.AggregatedFeatureGenerator;
import opennlp.tools.util.featuregen.PreviousMapFeatureGenerator;
import opennlp.tools.util.featuregen.TokenClassFeatureGenerator;
import opennlp.tools.util.featuregen.TokenFeatureGenerator;
import opennlp.tools.util.featuregen.WindowFeatureGenerator;

/**
 * 中文命名實體識別模型訓練組件
 * 
 * @author ddlovehy
 *
 */
public class NamedEntityMultiFindTrainer {

	// 默認參數
	private int iterations = 80;
	private int cutoff = 5;
	private String langCode = "general";
	private String type = "default";

	// 待設定的參數
	private String nameWordsPath; // 命名實體詞庫路徑
	private String dataPath; // 訓練集已分詞語料路徑
	private String modelPath; // 模型存儲路徑

	public NamedEntityMultiFindTrainer() {
		super();
		// TODO Auto-generated constructor stub
	}

	public NamedEntityMultiFindTrainer(String nameWordsPath, String dataPath,
			String modelPath) {
		super();
		this.nameWordsPath = nameWordsPath;
		this.dataPath = dataPath;
		this.modelPath = modelPath;
	}

	public NamedEntityMultiFindTrainer(int iterations, int cutoff,
			String langCode, String type, String nameWordsPath,
			String dataPath, String modelPath) {
		super();
		this.iterations = iterations;
		this.cutoff = cutoff;
		this.langCode = langCode;
		this.type = type;
		this.nameWordsPath = nameWordsPath;
		this.dataPath = dataPath;
		this.modelPath = modelPath;
	}

	/**
	 * 生成定制特征
	 * 
	 * @return
	 */
	public AggregatedFeatureGenerator prodFeatureGenerators() {
		AggregatedFeatureGenerator featureGenerators = new AggregatedFeatureGenerator(
				new WindowFeatureGenerator(new TokenFeatureGenerator(), 2, 2),
				new WindowFeatureGenerator(new TokenClassFeatureGenerator(), 2,
						2), new PreviousMapFeatureGenerator());

		return featureGenerators;
	}

	/**
	 * 將模型寫入磁盤
	 * 
	 * @param model
	 * @throws Exception
	 */
	public void writeModelIntoDisk(TokenNameFinderModel model) throws Exception {
		File outModelFile = new File(this.getModelPath());
		FileOutputStream outModelStream = new FileOutputStream(outModelFile);
		model.serialize(outModelStream);
	}

	/**
	 * 讀出標注的訓練語料
	 * 
	 * @return
	 * @throws Exception
	 */
	public String getTrainCorpusDataStr() throws Exception {

		// TODO 考慮入持久化判斷直接載入標注數據的情況 以及增量式訓練

		String trainDataStr = null;
		trainDataStr = NameEntityTextFactory.prodNameFindTrainText(
				this.getNameWordsPath(), this.getDataPath(), null);

		return trainDataStr;
	}

	/**
	 * 訓練模型
	 * 
	 * @param trainDataStr
	 *            已標注的訓練數據整體字符串
	 * @return
	 * @throws Exception
	 */
	public TokenNameFinderModel trainNameEntitySamples(String trainDataStr)
			throws Exception {
		ObjectStream<NameSample> nameEntitySample = new NameSampleDataStream(
				new PlainTextByLineStream(new StringReader(trainDataStr)));
		
		System.out.println("**************************************");
		System.out.println(trainDataStr);

		TokenNameFinderModel nameFinderModel = NameFinderME.train(
				this.getLangCode(), this.getType(), nameEntitySample,
				this.prodFeatureGenerators(),
				Collections.<String, Object> emptyMap(), this.getIterations(),
				this.getCutoff());

		return nameFinderModel;
	}

	/**
	 * 訓練組件總調用方法
	 * 
	 * @return
	 */
	public boolean execNameFindTrainer() {

		try {
			String trainDataStr = this.getTrainCorpusDataStr();
			TokenNameFinderModel nameFinderModel = this
					.trainNameEntitySamples(trainDataStr);
			// System.out.println(nameFinderModel);
			this.writeModelIntoDisk(nameFinderModel);

			return true;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

			return false;
		}
	}
}

注:

  • 參數:iterations是訓練算法迭代的次數,太少了起不到訓練的效果,太大了會造成過擬合,所以各位可以自己試試效果;
  • cutoff:語言模型掃描窗口的大小,一般設成5就可以了,當然越大效果越好,時間可能會受不了;
  • langCode:語種代碼和type實體類別,因為沒有專門針對中文的代碼,設成“普通”的即可,實體的類別因為我們想訓練成能識別多種實體的模型,於是設置為“默認”。

說明:

  • prodFeatureGenerators()方法用於生成個人訂制的特征生成器,其意義在於選擇什么樣的n-gram語義模型,代碼當中顯示的是選擇窗口大小為5,待測命名實體詞前后各掃描兩個詞的范圍計算特征(加上自己就是5個),或許有更深更准確的意義,請大家指正;
  • trainNameEntitySamples()方法,訓練模型的核心,首先是將如上標注的訓練語料字符串傳入生成字符流,再通過NameFinderME的train()方法傳入上面設定的各個參數,訂制特征生成器等等,關於源實體映射對,就按默認傳入空Map就好了。

源代碼開源在:https://github.com/Ailab403/ailab-mltk4j,test包里面對應有完整的調用demo,以及file文件夾里面的測試語料和已經訓練好的模型。

3 StanfordNLP:

Stanford NLP Group是斯坦福大學自然語言處理的團隊,開發了多個NLP工具。其開發的工具包括以下內容:

  • Stanford CoreNLP : 采用Java編寫的面向英文的處理工具,下載網址為:。主要功能包括分詞、詞性標注、命名實體識別、語法分析等。
  • Stanford Word Segmenter : 采用CRF(條件隨機場)算法進行分詞,也是基於Java開發的,同時可以支持中文和Arabic,官方要求Java版本1.6以上,推薦內存至少1G。

簡單的示例程序:

//設置分詞器屬性。
   Properties props = new Properties();
//字典文件地址,可以用絕對路徑,如d:/data
   props.setProperty("sighanCorporaDict", "data");
//字典壓縮包地址,可以用絕對路徑
   props.setProperty("serDictionary","data/dict-chris6.ser.gz");
//輸入文字的編碼;
   props.setProperty("inputEncoding", "UTF-8");
   props.setProperty("sighanPostProcessing", "true");
//初始化分詞器,
   CRFClassifier classifier = new CRFClassifier(props);
//從持久化文件中加載分詞器設置;
   classifier.loadClassifierNoExceptions("data/ctb.gz", props);
   // flags must be re-set after data is loaded
   classifier.flags.setProperties(props);
//分詞
   List words = classifier.segmentString("語句內容");

最后附上關於中文分詞器性能比較的一篇文章:http://www.cnblogs.com/wgp13x/p/3748764.html

實現中文命名實體識別

1、分詞介紹

斯坦福大學的分詞器,該系統需要JDK 1.8+,從上面鏈接中下載stanford-segmenter-2014-10-26,解壓之后,如下圖所示
進入data目錄,其中有兩個gz壓縮文件,分別是ctb.gz和pku.gz,其中 CTB:賓州大學的中國樹庫訓練資料 , PKU:中國北京大學提供的訓練資料。當然了,你也可以自己訓練,一個訓練的例子可以在這里面看到 http://nlp.stanford.edu/software/trainSegmenter-20080521.tar.gz

2、NER介紹

斯坦福NER是采用Java實現,可以識別出(PERSON,ORGANIZATION,LOCATION),使用本軟件發表的研究成果需引用下述論文:
下載地址在: http://nlp.sta nford.edu/~manning/papers/gibbscrf3.pdf
在NER頁面可以下載到兩個壓縮文件,分別是stanford-ner-2014-10-26和stanford-ner-2012-11-11-chinese
將兩個文件解壓可看到
默認NER可以用來處理英文,如果需要處理中文要另外處理。
3、分詞和NER使用
在Eclipse中新建一個Java Project,將data目錄拷貝到項目根路徑下, 再把stanford-ner-2012-11-11-chinese解壓的內容全部拷貝到classifiers文件夾下將stanford-segmenter-3.5.0加入到classpath之中, classifiers文件夾拷貝到項目根目錄,將stanford-ner-3.5.0.jar和stanford-ner.jar加入到classpath中。最后,去 http://nlp.stanford.edu/software/corenlp.shtml下載stanford-corenlp-full-2014-10-31,將解壓之后的stanford-corenlp-3.5.0也加入到classpath之中。最后的Eclipse中結構如下:
Chinese NER:這段說明,很清晰,需要將中文分詞的結果作為NER的輸入,然后才能識別出NER來。
同時便於測試,本Demo使用junit-4.10.jar,下面開始上代碼
import edu.stanford.nlp.ie.AbstractSequenceClassifier; 
import edu.stanford.nlp.ie.crf.CRFClassifier; 
import edu.stanford.nlp.ling.CoreLabel; 

/** 
* 
* <p> 
* ClassName ExtractDemo 
* </p> 
* <p> 
* Description 加載NER模塊 
* 
*/ 
public class ExtractDemo { 
private static AbstractSequenceClassifier<CoreLabel> ner; 
public ExtractDemo() { 
InitNer(); 
} 
public void InitNer() { 
String serializedClassifier = "classifiers/chinese.misc.distsim.crf.ser.gz"; // chinese.misc.distsim.crf.ser.gz 
if (ner == null) { 
ner = CRFClassifier.getClassifierNoExceptions(serializedClassifier); 
} 
} 

public String doNer(String sent) { 
return ner.classifyWithInlineXML(sent); 
} 

public static void main(String args[]) { 
String str = "我 去 吃飯 , 告訴 李強 一聲 。"; 
ExtractDemo extractDemo = new ExtractDemo(); 
System.out.println(extractDemo.doNer(str)); 
System.out.println("Complete!"); 
} 

} 

 

import java.io.File; 
import java.io.IOException; 
import java.util.Properties; 

import org.apache.commons.io.FileUtils; 

import edu.stanford.nlp.ie.crf.CRFClassifier; 
import edu.stanford.nlp.ling.CoreLabel; 

/** 
* 
* <p> 
* Description 使用Stanford CoreNLP進行中文分詞 
* </p> 
* 
*/ 
public class ZH_SegDemo { 
public static CRFClassifier<CoreLabel> segmenter; 
static { 
// 設置一些初始化參數 
Properties props = new Properties(); 
props.setProperty("sighanCorporaDict", "data"); 
props.setProperty("serDictionary", "data/dict-chris6.ser.gz"); 
props.setProperty("inputEncoding", "UTF-8"); 
props.setProperty("sighanPostProcessing", "true"); 
segmenter = new CRFClassifier<CoreLabel>(props); 
segmenter.loadClassifierNoExceptions("data/ctb.gz", props); 
segmenter.flags.setProperties(props); 
} 

public static String doSegment(String sent) { 
String[] strs = (String[]) segmenter.segmentString(sent).toArray(); 
StringBuffer buf = new StringBuffer(); 
for (String s : strs) { 
buf.append(s + " "); 
} 
System.out.println("segmented res: " + buf.toString()); 
return buf.toString(); 
} 

public static void main(String[] args) { 
try { 
String readFileToString = FileUtils.readFileToString(new File("澳門141人食物中毒與進食“問題生蚝”有關.txt")); 
String doSegment = doSegment(readFileToString); 
System.out.println(doSegment); 

ExtractDemo extractDemo = new ExtractDemo(); 
System.out.println(extractDemo.doNer(doSegment)); 

System.out.println("Complete!"); 
} catch (IOException e) { 
e.printStackTrace(); 
} 

} 
} 

 
注意一定是JDK 1.8+的環境,最后輸出結果如下:

 4 IKAnalyzer

IK Analyzer是一個開源的,基於Java語言開發的輕量級的中文分詞工具包。IK支持細粒度和智能分詞兩種切分模式,支持英文字母、數字、中文詞匯等分詞處理,兼容韓文、日文字符。可以支持用戶自定義的詞典,通過配置IKAnalyzer.cfg.xml文件來實現,可以配置自定義的擴展詞典和停用詞典。詞典需要采用UTF-8無BOM格式編碼,並且每個詞語占一行。配置文件如下所示:

<properties>  
	<comment>IK Analyzer 擴展配置</comment>
	<!--用戶可以在這里配置自己的擴展字典--> 
	<entry key="ext_dict">ext.dic;</entry> 
	
	<!--用戶可以在這里配置自己的擴展停止詞字典-->
	<entry key="ext_stopwords">stopword.dic;chinese_stopword.dic</entry> 
	
</properties>

 只需要把IKAnalyzer2012_u6.jar部署於項目的lib中,同時將IKAnalyzer.cfg.xml文件以及詞典文件置於src中,即可通過API的方式開發調用。IK簡單、易於擴展,分詞結果較好並且采用Java編寫,因為我平時的項目以Java居多,所以是我平時處理分詞的首選工具。示例代碼:

/**
	 * IK分詞功能實現
	 * @return
	 */
	public String spiltWords(String srcString){
		StringBuffer wordsBuffer = new StringBuffer("");
        try{
        	IKSegmenter ik=new IKSegmenter(new StringReader(srcString), true);  
        	Lexeme lex=null;  
        	while((lex=ik.next())!=null){  
//        		System.out.print(lex.getLexemeText()+" ");
        		wordsBuffer.append(lex.getLexemeText()).append(" ");
        	}
        }catch(Exception e){
        	logger.error(e.getMessage());
        }
		return wordsBuffer.toString();
	}

 5 中科院ICTCLAS

ICTCLAS是由中科院計算所歷經數年開發的分詞工具,采用C++編寫。最新版本命名為ICTCLAS2013,又名為NLPIR漢語分詞系統。主要功能包括中文分詞、詞性標注、命名實體識別、用戶詞典功能,同時支持GBK編碼、UTF8編碼、BIG5編碼,新增微博分詞、新詞發現與關鍵詞提取。可以可視化界面操作和API方式調用。

 

6 FudanNLP

FudanNLP主要是為中文自然語言處理而開發的工具包,也包含為實現這些任務的機器學習算法和數據集。FudanNLP及其包含數據集使用LGPL3.0許可證。主要功能包括:

  • 信息檢索:文本分類,新聞聚類。
  • 中文處理:中文分詞,詞性標注,實體名識別,關鍵詞抽取,依存句法分析,時間短語識別。
  • 結構化學習:在線學習,層次分類,聚類,精確推理。

工具采用Java編寫,提供了API的訪問調用方式。下載安裝包后解壓后,內容如下圖所示:


在使用時將fudannlp.jar以及lib中的jar部署於項目中的lib里面。models文件夾中存放的模型文件,主要用於分詞、詞性標注和命名實體識別以及分詞所需的詞典;文件夾example中主要是使用的示例代碼,可以幫助快速入門和使用;java-docs是API幫助文檔;src中存放着源碼;PDF文檔中有着比較詳細的介紹和自然語言處理基礎知識的講解。初始運行程序時初始化時間有點長,並且加載模型時占用內存較大。在進行語法分析時感覺分析的結果不是很准確。

 

 
 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM