v2.0本意是進行數據上的優化。可是因爲數據量很大,存儲方式由原先的寫在代碼中,變爲在文件中,所以不得不採用異步方式,這樣原先的代碼絕大部分都不能使用了。java
主要進行了如下幾個步驟的工做:node
從網絡上抓取大量詩詞數據git
按格式將詩詞分類github
對詩詞正文進行分詞操做正則表達式
統計各詞出現的頻率算法
統計五言、七言詩句的句型,並將高頻句型做爲模板保存。編程
根據參數或者隨機挑選模板,而後使用詞庫渲染之。bash
首先選定了 http://so.gushiwen.org/ 這個網站做爲抓取的目標,這個網站上收錄的詩詞數量也很可觀,詞也基本都是主流詩人寫的,是很好的數據源。總共抓取了71000首。網絡
觀察發現,全部詩歌的正文都在形如http://so.gushiwen.org/view_X...的網頁上(XXX是數字),這大大簡化了抓取操做。機器學習
具體抓取和解析代碼在「poem-spider.js」中。最後解析出來的結果被保存在「docs/poems.txt」中。
這一步採用了正則表達式匹配詩文,將五言、七言詩等分開保存。
具體代碼在「poem-selector.js」中。目前解析了七言詩和五言詩,保存在「docs/QiYan.txt」和「docs/WuYan.txt」中
爲了實現詞語頻率的統計,以及後續詞性的分析,首先要進行的是分詞操做。
找到了這樣一個分詞工具:http://thulac.thunlp.org/demo。
下載獲得了兩個部分的文件:
THULAC_lite_java_run.jar models/
jar文件是進行分詞的可執行文件,models文件夾包含一些官方提供的訓練數據,用以執行分詞操做。
針對唐詩,分詞效果其實還有提高的空間,好比說下面這樣的:
神皋福 地 三 秦邑 , 玉臺 金闕九仙家 。 寒光 猶戀 甘泉樹 , 淑景 偏 臨建 始 花 。
其中金闕九仙家應該能夠被分詞爲金闕/九仙/家。可是這些詞對人來講已經比較難懂了,估計不容易經過訓練獲得改善。不過好在後面的統計操做,基本可以把這些無心義的分詞篩掉。
分詞時的命令很簡單:
$> java -jar thulac\\thulac.jar -seg_only -input data\\WuYan.txt -output data\\Separate_WuYan.txt
具體代碼在「poem-separate.js」中。
使用字典顯然是統計詞頻的最好的選擇。
每讀取到一個詞就在字典中建立這個條目,或者將這個詞的計數增長1。
具體的代碼在「high-freq-word.js」中,最後篩選出了全部出現大於1次的詞語,結果保存在「docs/high-frequency-word.csv」中。
光光統計高頻詞語是顯然不夠的,還須要保存詞語相應的信息。
根據以前的思路,須要保存的信息包括:
字數
平仄
詞性
(韻腳)
所以大致上將詞以csv的格式保存在A/[平仄]/[詞性]/[字數]
(若是不須要押韻)或B/[平仄]/[韻腳]/[詞性]/[字數]
(若是須要押韻)的文件中。 。
韻腳和平仄怎麼辦?!求助萬能的github,發現這樣一個前輩寫好的js庫:https://github.com/hotoo/pinyin 。
繼續求助上文的分詞工具,生成形如[詞性][字數]([是否押韻])/...
這樣的模板。
好比:
自從關路入秦川,爭道何人不戲鞭。
將會被分詞爲:
自從_p 關路_n 入_v 秦川_ns ,_w 爭道_v 何人_r 不_d 戲鞭_v 。
進而,其句型信息爲:
['p2/n2/n1/ns2/', 'v2/r2/d1/v2/']
注:因爲一些詞性,例如ns
、ni
等,都屬於同一範疇,能夠將他們都合併到名詞。所以在分詞和保存模板的時候,將許多詞性進行了合併。
固然了,除了句型信息,還要有平仄信息。因爲平仄與句型並無直接的關聯(在絕句中),所以將句型和平仄分兩部分保存。
查閱了一下絕句的平仄規律,以及拗句的狀況等等。。最後講平仄模板寫在了代碼中。預計有30+個不一樣的模板。
最後模板信息保存在「word/WuYan_templates.txt」和「word/QiYan_templates.txt」中。
渲染就是簡單的用生日取當前模板或詞庫文件的模,獲得序號。
大功告成。。。。。。。。。。。。。。。。。。。。。。。。。。
。
。
。
too naive.
實際渲染的時候,經常會出現死循環的狀況。因而打印出了出現死循環狀況的模板信息:發現一個共同點,模板信息中都會出現id7/
、id5/
等類型的單元,這種超長的詞法單元是因爲詞法分析工具沒有正確的分詞形成的。所以,痛下殺手,將全部帶有id
以及包含字數大於3的詞法單元的模板所有剔除。這一下不得了,剔除了大約60%的模板。可見,在詩詞方面,thulac的分詞工具還有很大的提高空間。
好了,這下再也沒有出現過死循環的狀況。
生成多首詩的時候,會出現每首詞都須要可觀的時間。分析後得出診斷,應該是讀取文件的耗時。因而使用單例模式,在第一次訪問某個文件時將這個文件的內容加載到一個字典中,下次直接從字典中讀取。通過測試,後續的詞生成速度很快。
隨便貼首詞出來:
[ '爭心酒歡無近信', '淮北山圍擺震雷', '淪跡蘭芽壘潏潏', '兼僕潏潏蹭白眉' ] [ '弔影自蹉跎', '防知靜近郭', '暨滴昏朔霧', '起望自蹉跎' ]
咦!竟然有重複的詞!
原來,在挑選詞的時候,簡單的根據生日信息取模來取詞,一旦遇到兩個同樣的詞法單元,好比n2/
,平仄信息都是+-
,那麼取出的詞必定是同樣的。因而乎,在遍歷模板的時候,每次將生日增長10.大大減小了重複的機率。
來首優化後的詞吧,意境仍是有的:
寒宵月生籌政事, 韶濩秦關百岸風。 哲匠公堂美利戒, 文學魏帝斷絃聲。
先從最淺層的提及。此次採用了腳本語言nodejs。最明顯的特色就是他的異步性,所以習慣了同步編程的我,經常不當心調入回調的坑,沒法用異步的思想來思考問題。
當我須要從文件中讀取詞彙,而後拼裝成整句。我調用fetch()
函數從詞庫中獲取某行內容,在讀取結束後,經過回調函數返回值。這致使了從比較大的詞庫中獲取的詞將最遲被返回,因而我很不美麗得設置了一個flag,用於標記總詞法單元數,每返回一個詞就檢查是否全部模板中的詞法單元都被渲染了,當全部詞法單元都被渲染了,就調用回調函數返回結果。這致使這段代碼十分難懂。如今以爲,能夠調用async
庫,採用順序執行的方式解決問題,代碼必定簡潔的多,也好讀的多。
另外就是代碼複用性的問題,如今的渲染函數只能渲染每句長度相同的模板,對於詞模板,還缺少拼裝的能力。這個功能,往後還須要加上。
上部分已經說明了,本次的唐詩宋詞項目的基本思路是:
生成模板
取詞
渲染
這能夠知足:押韻的要求、必定程度上的語義要求(僅限於詞性搭配)、平仄的要求。
生成一首完美的詩詞,這顯然是不夠的。至於更高的設想,在後面的部分會探討一下。
這是一個運用機器學習的方法生成唐詩的例子。本人所以稍微去補習了下神經網絡的知識。
神經網絡經過不少神經單元組成的中間層,一層一層將輸入信息簡化,直到最上面一層,將輸入信息提取成一個向量或是矩陣。
在生成近體詩的運用中,咱們能夠把大量的唐詩宋詞數據(得瑟一下:這篇文章做者說收集唐詩數據用了一週,而我———用了在圖書館的某個清閒的下午的三個小時)輸入到神經網絡,讓它學習到怎麼樣的文法纔是近體詩。
在生成階段:不斷向詩詞後隨機拼接詞語,而後選擇語義和文法上得分最高的詞。
顯然,平仄、語義、押韻、句型,都是神經網絡須要動用神經單元分析的內容。而這個做者顯然沒有考慮到平仄和押韻問題。神經網絡對硬件,或者說對算法的優化要求很高。這個做者爲了保證性能上的可行性,顯然在算法和學習程度上作出了很多的讓步。若是神經網絡進行了足夠多的學習,相信效果會很是好。
(挖坑學習中)
基本都是那樣啦。。。效果上,真的沒以爲有好上天的。
以上的全部算法,包括那個我只知其一;不知其二的遺傳算法,包括本身的算不上算法的「套路」,都是從詩詞的表面上,經過分析現有詩詞,逆向解決問題。
思考是否能夠經過模擬做詩的步驟,正向解決問題。
前期工做:
學習句型
組建關聯詞庫,就是包含了「修飾」的關係的詞語對,甚至是詞羣,表示這些詞出如今一塊兒更加有意義
平仄、押韻等等知識的儲備
學習常見的詩詞「主題」和這些主題對應的關鍵詞。
生成:
擬定詩詞主題,再經過必定程度上的發散,肯定若干個關鍵詞
經過相似搜索引擎的工做,用關鍵詞搜索出一些相關的主幹詞,組成主幹詞庫。
根據現有的句型知識,嘗試着填入一些詞,而後搜索關聯詞庫,填入更多有意義的詞。