<p align = "center">可以看我的博客 lmwen.top</p>
<p align = "center">或者訂閱我的公眾號</p>
簡介
因為公司項目的原因,最近花了點時間去研究NLP
自然語言處理是一門比較復雜的技術,說深了,它涉及到機器學習和神經網絡模型,說淺了,你會用他人實現好的工具就ok了。這么復雜的東西,當然首選python來實現啦!
本章我們來實現一個簡單的軟件,可以對word文檔、pdf文檔進行關鍵詞提取、自動摘要、中文分詞和短語提取,效果如下
環境配置
我使用的環境:python2.7+ubuntu16.04+java1.8.0
python使用了的第三方庫有:wxpython、Jpype、docx2txt、pdfminer
漢語處理庫:HanLP
簡單說說這些庫wxpython:一個跨平臺的python圖形界面庫,非常友好,使用它可以比較輕松的搭建一個界面還ok的圖形界面,可以通過pip下載,也可以直接去wxpython官網下載,它有很多中文教程,隨便搜搜就有了
Jpype:讓python可以調動Java程序,一般為jar包,Jpype不同Jython,Jython運行在JVM上,而Jpype依舊運行在python上,只是在運行期間嵌入了JVM,你依舊可以通過pip下載,ubuntu還可以通過apt-get下載,主要不同版本的python對應不同版本的Jpype,Python2.7對應Jpype1,Python3對應Jpype1-py3,這里使用這個庫的原因是,我們使用的自然語言處理算法使用java實現的,所以要通過Jpype來調用相應jar包中的方法
docx2txt:從庫的名字也可以輕易的看出,docx2txt用于將docx格式的文檔,也就是word文檔轉成txt,該庫使用起來非常簡單
pdfminer:pdfminer主要用于處理pdf格式的文件,可以將pdf格式的文件轉成txt
HanLP:一個中國開發者開發的漢語處理庫,這個庫很強大,而且完全開源,非常感謝這個開發者,非常感謝開源,只要使用庫,最核心的中文處理功能都可以輕松實現,你要的只是通過Python將實現好的JAVA語言,其核心算法都通過JAVA實現,可以去相應的github上了解該項目,HanLP
自然語言處理
雖說我們已經找到了第三方自然語言處理庫,但是為了更好的理解自然語言處理或跟好的利用這些工具,我覺得一些基本的理念還是要清楚的,下面我來總結一下這幾天學習到的
中文分詞工具
中華文化博大精深,精深到可以搞死我們,不同英文,每個單詞之間都一個空格隔開,這種特性讓英文分詞相對好實現,但是中文不一樣,中文之間是沒有間隔的,而且不同的分詞方式可以帶來完全不一樣的結果,如對春藥店進行分詞,會有兩個完全不同的結果
12
春/ 藥店/春藥/ 店/
正是因為中文具有這些復雜的特性,什么成語啊,一詞多義啊之類的,讓中文分詞變得非常難搞,需要研究出相應的算法,但是市面上已經有一些中文分詞工具可以使用了,除了前面提到的HanLP,還有哈工的LTP、中科院計算所的NLPIR、清華大學的THULAC和Jieba,這些都是比較優秀的分詞工具,因為我不是什么專業人士,所以對這些工具的效率啊,準確性啊無法評估,但是網上有人對這些工具做過評估,發現哈工的LTP和清華的THULAC不錯,但是我使用HanLP,主要是因為HanLP文檔友好,同時社區比較活躍,github上2000多個星,而且時常更新與維護
下面給出中文分詞工具對應的github網址哈工大LTP
中科院計算所NLPIR
清華大學THULAC
jieba
背景知識
在自然語言處理領域有一些很常見的術語,下面解釋一下這些術語
中文分詞
機器是無法識別出一句中文所表達的意義的,所以第一步要進行分詞,目的就是將一段話分割成一個個有意義的詞,要實現這個目的就要有一個優雅的語言模型和語料庫
語言模型
語言模型用大白話說就是一個算法,一個自然語言處理工具一般實現了多種語言模型,通過不同語言模型對內容極進行計算可以獲得不同的結果,所以我們應該根據需求來選擇不同的語言模型,可以閱讀下面的文章加深理解語言模型的基本概念
語料庫
語料庫其實就是經過整理的語言文本,將真實語言中出現的一些詞匯、語言材料整理起來,語料庫的質量十分重要,是分詞工具的根基,網上也有很多開源的語料庫,語料基本格式如下
1234567891011
一一 一一列舉 一一對應 一丁點 一丁點兒 一萬年 一丈紅 一下 一下子 一不做 一不小心 一專多能 一世 一丘之貉
詞向量
首先我們要明白向量的作用。向量是人把自然界的東西抽象出來交給機器處理的東西,基本上可以說向量是人對機器輸入的主要方式了。理解了上面那句話,那么詞向量也就好解釋了,所謂詞向量就是用來將語言中的詞進行數學化的一種方式??梢耘e個例子弄明白詞向量的作用,對于一個詞,可以將它抽象成為一個向量,如(0,1)(最簡單的一個形式),該向量在二維空間就是一條線,那么另外一個詞,同樣可以抽象成一個向量,如(0,2),它同樣在二維空間中是一條線,通過計算兩條線的夾角cos值,就可以判斷出兩個詞之間的相似性,這樣就解決了一個實際的問題,如何判斷兩個句子或兩篇文章的相似性,那么其中詞向量的作用就是將語言抽象成數學化的一種表示。
具體實現
那么下面我們就來看看如何具體實現它
首先明確軟件運行的成功流程,一開始,啟動圖形界面,然后用戶選擇文件,乳溝用戶選擇的是pdf或docx,那么點擊運算程序就進行運算處理,其中的步驟就是將pdf或docx轉成字符串,然后再進行分詞,最后顯示在圖形界面上,如果不是pdf也不是docx,那么給出相應的錯誤提示
PDF TO TXT
首先來完成PDF轉TXT,代碼比較簡單,學習一下如何使用pdfminer則可,基礎用法可以上網查查,這里直接上代碼了
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
-- coding:utf-8 --from cStringIO import StringIOfrom pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreterfrom pdfminer.converter import TextConverterfrom pdfminer.layout import LAParamsfrom pdfminer.pdfpage import PDFPageimport osimport sys, getoptimport HanLP as ayuimport string# PDF轉txtdef convert(fname, pages=None): if not pages: pagenums = set() else: pagenums = set(pages) output = StringIO() manager = PDFResourceManager() converter = TextConverter(manager, output, laparams=LAParams()) interpreter = PDFPageInterpreter(manager, converter) infile = file(fname, 'rb') for page in PDFPage.get_pages(infile, pagenums): interpreter.process_page(page) infile.close() converter.close() text = output.getvalue() output.close return text# 構造文件路徑,進行分詞處理def convertMultiple(pdfDir, txtDir): if pdfDir == "": pdfDir = os.getcwd() + "http://" for pdf in os.listdir(pdfDir): # iterate through pdfs in pdf directory fileExtension = pdf.split(".")[-1] if fileExtension == "pdf": pdfFilename = pdfDir + pdf text = convert(pdfFilename) # get string of text content of pdf\ res = ayu.GetKeyWord(text,5) res2 = ayu.GetSummary(text,5) res3 = ayu.GetPhrase(text,5) p2tlist = list(); p2tlist.append(res) p2tlist.append(res2) p2tlist.append(res3) return p2tlist# i : info# p : pdfDir# t = txtDirdef main(argv,pdfDir = "",txtDir = ""): try: opts, args = getopt.getopt(argv, "ip:t:") except getopt.GetoptError: print("pdfToT.py -p <pdfdirectory> -t <textdirectory>") sys.exit(2) for opt, arg in opts: if opt == "-i": print("pdfToT.py -p <pdfdirectory> -t <textdirectory>") sys.exit() elif opt == "-p": pdfDir = arg elif opt == "-t": txtDir = arg return convertMultiple(pdfDir, txtDir)if name == "main": main("/home/ayuliao/HanLP/AyuZRYY/word2vec.pdf")
在該Python中,一開始調用main()方法,做一下預處理操作,使用了getopt,可以通過命令行參數來直接傳入PDF文件的路徑,然后就調用convertMultiple()方法,構建pdf文件的具體路徑,將路徑傳入convert()方法,將pdf轉成了相應的字符流,將這些字符流傳遞給分裝好的中文分詞方法,GetKeyWord()方法用于獲取關鍵字,GetSummary()方法用于獲得文章的簡述,GetPhrase()方法獲得文章的短語,將獲得的內容都添加都list中,回傳回去
DOCX TO TXT
接著將word文檔也就是docx類型的文檔轉成字符串,DOCX轉TXT其實比較麻煩,因為docx這個格式比較惡心,但是python太強大了,直接一個庫docx2txt,輕松搞定
1234567891011121314151617
-- coding:utf-8 --import docx2txtimport osimport HanLP as ayu# 將docx轉成字符串并進行分詞def WToT(filePath): text = docx2txt.process(filePath) res = ayu.GetKeyWord(text, 4) res2 = ayu.GetSummary(text, 4) res3 = ayu.GetPhrase(text, 4) w2tlist = list(); w2tlist.append(res) w2tlist.append(res2) w2tlist.append(res3) return w2tlist
直接使用docx2txt庫中的process()方法,將docx文件具體的路徑傳進去就ok了,有興趣可以看看具體是怎么實現的(反正我沒看;))
進行中文分詞
如果你自己仔細看完了HanLP這個開源項目的文檔,就會知道,使用這個庫其實十分簡單,唯一有點麻煩的是,它是java實現的,而我們編寫的python代碼,但HanLP社區非?;钴S,所以這個坑已經有前輩幫我踩過了,可以看https://github.com/yesseecity/hanLP-python這個項目,已經完美實現python調用HanLP了,只是他使用的是python3的版本,所以還是需要自己摸索一下,如果你使用過Jpype這個牛逼的庫,會發現實現python調用Java中的方法也不是特別麻煩,首先你要確定你的電腦中下載并配置了Java,比較HanLP里是Java代碼,要跑到JVM下的,確定java環境沒問題,就可以通過JPype來讓python調用Java中實現的代碼
可以通過下面的代碼測試一下python是否可以連接上Java
1234
from jpype import *startJVM(getDefaultJVMPath())java.lang.System.out.println("Hello ayuliao")shutdownJVM()
如果出現
1234
Hello ayuliaoJVM activity report : classes loaded : 31JVM has been shutdown
說明你可以通過python調用JAVA了,我在windows上嘗試了幾次沒有成功,懷疑過JAVA環境配置問題,但是環境似乎沒問題,所以最終還是在Ubuntu上運行,果然在意開發效率,遠離windows
在正式寫這部分核心代碼前,你需要下載HanLP的Java包,可以去這里下載HanLP Java包
下面就是正式的python代碼了
123456789101112131415161718192021222324252627282930313233
-- coding:utf-8 --from jpype import *import osimport sysreload(sys)# 因為解碼的時候會默認使用系統的編碼,一般是ascii,如果是中文就會出現編碼錯誤,直接將系統編碼改成utf8則可sys.setdefaultencoding('utf8')path = os.getcwd();# print(path)startJVM(getDefaultJVMPath(), "-Djava.class.path="+path+"/hanlp-portable-1.3.4.jar:"+path+"/hanlp", "-Xms1g", "-Xmx1g") # 啟動JVM,Windows需要將冒號:替換為分號;HanLP = JClass('com.hankcs.hanlp.HanLP')# 獲得分詞def GetFenWord(content): return HanLP.segment(content)# 獲得文章關鍵字def GetKeyWord(content,number): return HanLP.extractKeyword(content,number)# 獲得文章簡述def GetSummary(content,number): return HanLP.extractSummary(content,number)# 獲得文章短語def GetPhrase(content,number): return HanLP.extractPhrase(content, number)# 停止JVM運行def ShopJVM(): shutdownJVM()
python編寫圖像界面
python本身自帶有圖像界面庫Tkinter,對應Tkinter,網上的資料比較多,但是我沒有選它,因為感覺有點丑,所以選擇了wxPython發現其實也沒有漂亮到哪里去…..
學習了一下wxPython的用法,其實不難,就是一開始沒有概念,寫起來比較麻煩,但是wxPython封裝的非常好,而且通過wxPython開發圖形化界面還可以使用GUI可視化構建工具wxFormBuilder,有點像Android開發界面布局一樣了,哈哈
使用wxFormBuilder是不錯的選擇,但是我不怎么喜歡,我下載下來看了看看它生成的代碼,知道布局大概該怎么寫后,就不怎么用了,因為通過代碼來控制,感覺可以更精細點,可以看wxPython 基礎使用教程這篇博文來學習使用wxPython和wxFormBuilder
下面直接看代碼
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
!/usr/bin/python# coding:utf-8import wximport osimport Pdf2Txt as p2timport Word2Txt as w2tdef OnOpen(event): wilcard = "Python source (.pdf)|.pdf|Compiled Python (.pdf)|.pdf|All files (.)|." wilcard = "" dialog = wx.FileDialog(None, "選擇一個文件", os.getcwd(), "", wilcard, wx.OPEN) if dialog.ShowModal() == wx.ID_OK: filename.SetValue(dialog.GetPath()) dialog.Destroy()def OnSave(event): if filename.GetValue() == '': contents.SetValue("必須選擇一個文件才可進行運算?。。?) else: filepath = filename.GetValue() fileExtension = filepath.split(".")[-1] if(fileExtension == "pdf"): res = p2t.main(filepath) if(fileExtension == "docx"): res = w2t.WToT(filepath) if(fileExtension != "pdf" or fileExtension != "docx"): contents.SetValue("只能運算pdf格式和docx格式的文件?。?!") str = "運算開始\n\n" y=0 for r in res: i = 0 for k in r: if(i==0): if(y==0): str += "文章關鍵詞:\n" if(y==1): str += "文章摘要:\n" if(y==2): str += "文章短語:\n" str += "-----"+k+"\n" i = i+1 y = y+1 str +="\n運算結束" contents.SetValue(str)app = wx.App()win = wx.Frame(None, title="文章摘要運算器 ayuliao", size=(600, 400))bkg = wx.Panel(win)loadButton = wx.Button(bkg, label='選擇文件')loadButton.Bind(wx.EVT_BUTTON, OnOpen)saveButton = wx.Button(bkg, label='運算')saveButton.Bind(wx.EVT_BUTTON, OnSave)filename = wx.TextCtrl(bkg)contents = wx.TextCtrl(bkg, style=wx.TE_MULTILINE | wx.HSCROLL)hbox = wx.BoxSizer()hbox.Add(filename, proportion=1, flag=wx.EXPAND)hbox.Add(loadButton, proportion=0, flag=wx.LEFT, border=5)hbox.Add(saveButton, proportion=0, flag=wx.LEFT, border=5)vbox = wx.BoxSizer(wx.VERTICAL)vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)vbox.Add(contents, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, border=5)bkg.SetSizer(vbox)win.Show()app.MainLoop()
在代碼中,我們構建了兩個Button按鈕,并給它設置了相應的點擊函數,分別是從系統中選擇文件(目的是得到文件的路徑)和進行中文分詞運算
結尾
代碼我已經上傳到github,我稱這個項目為GAS(Get Article Summary),方便你直接下載下來使用,別忘了給我一個star,畢竟我也很不容易
GAS下載
有代碼創造樂趣—ayuliao