用 JS 做語意分析是不是搞錯了什麼(一):斷詞篇

身為一個 JavaScript 愛好者,實在是不太喜歡大家都在 Python 上做 Machine Learning;恰好近期 Tensorflow 也開始支援 JS,想必會興起一股熱潮吧?

使用 jieba 斷詞

之前在 開發 Chatbot 前的那兩三事(三):中文語意分析、使用分析平臺 有稍微提到語意分析的方式,這篇將會介紹如何用 jieba(結巴) 套件來斷詞。

我們會使用 npm 上的 nodejieba 套件,雖然這個套件一段時間了,而且還不支援 Promise 等較新的寫法,但仍然是 js 社群中最強的斷詞套件沒有之一。

使用 JS 做語意分析是不是搞錯了什麼

噢不,別再提 Python 了,你知道 JS 跑 jieba 比 Python 快很多嗎?根據 "结巴"(Jieba)中文分词系列性能评测,筆者做了 C++、Go 語言、JS 和 Python 的分析,我不像筆者那麼厲害精通各個語言,所以大家只要知道這個過程中 JS 花了 10 秒、Python 花了快 90 秒(大概是 JS 的九倍),所以 JS 比較厲害就可以了。

事前準備

這篇是後端,用 node.js 開發的文章,你可能需要準備 node.js 環境。如果你不知道要下載哪個版本,就抓 LTS 的版本吧。

安裝 jieba

首先安裝 jieba 套件:

npm install nodejieba

接著在 js 檔案裡引入 jieba 套件:

const nodejieba = require('nodejieba');

這樣就完成了 jieba 的初始化。

由於結巴一開始是為了簡體中文而開發的,故內建的詞庫可能切簡體中文會比較優秀一點。建議可以去找繁體中文詞庫,並用 nodejieba.load() 函式去載入字典檔。
例如將 ldkrsi/jieba-zh_TW 中的 dict.txt 抓下來後,
再透過 nodejieba.load({dict: './dict.txt'}); 來載入字典。

使用 jieba 斷詞

nodejieba 內建 4 種斷詞模式:

  • cut 精確模式:試圖將句子最精確地分開,適合文本分析(Contextual Analysis)。
  • cutHMM 精確模式並套用 HMM:使用隱藏式馬可夫鍊(HMM)模型,試圖找出不在字典檔裡面的字。
  • cutAll 全模式:把句子中可以成詞的詞語都掃瞄出來,速度非常快但不能解決歧義。
  • cutForSearch 搜尋引擎模式:在精確模式的基礎下,對長詞再次切分,提高召回率(recall)。

一般的句子都可以用精確模式切開來,例如:

nodejieba.cut('我們不禁哄堂大笑,同樣的一件衣料,每個人卻有不同的感覺。')

會得到:

['我們', '不禁', '哄堂大笑', ',', '同樣', '的', '一', '件', '衣料', ',', '每', '個', '人', '卻', '有', '不同', '的', '感覺', '。' ]

如果要切跟人名有關的東西可以考慮用 cutHMM,例如這句話「自此以後,又長久沒有看見孔乙己。」用精確模式切出來會是:

[ '自此', '以後', ',', '又', '長久', '沒有', '看見', '孔乙', '己', '。' ]

但用 cutHMM 就能切出:

[ '自此', '以', '後', ',', '又', '長', '久', '沒', '有', '看見', '孔乙己', '。' ]

用 jieba 找出關鍵詞和詞性

jieba 也能透過 tf-idf 的方法找出一段文章中的關鍵詞,例如:

const sentence = '我與父親不相見已有二年餘了,我最不能忘記的是他的背影。那年冬天,祖母死了,父親的差使也交卸了,正是禍不單行的日子,我從北京到徐州,打算跟父親奔喪回家。';
const topN = 5; /* 找出前五個關鍵詞 */

nodejieba.extract(sentence, topN);

會得到:

[ { word: '父親', weight: 35.21761292125063 },
  { word: '交卸', weight: 11.9547675029 },
  { word: '相見', weight: 11.739204307083542 },
  { word: '奔喪', weight: 11.739204307083542 },
  { word: '忘記', weight: 11.739204307083542 } ]

若是要找出詞性的話,也可以把整個句子丟進去,會自動斷詞並標記詞性:

nodejieba.tag('你總要踏上你老子的腳步。');

會得到:

 [{ word: '你', tag: 'N' },
  { word: '總要', tag: 'x' },
  { word: '踏上', tag: 'Vt' },
  { word: '你', tag: 'N' },
  { word: '老子', tag: 'N' },
  { word: '的', tag: 'T' },
  { word: '腳步', tag: 'N' },
  { word: '。', tag: 'x' } ]

至於詞性標記是什麼,可以參考這篇 词性标记

結語

斷詞以後就能拿來做很多應用了,例如斷詞後拿來做文字雲、轉向量做相似度分析等等,有趣的應用未來可以再介紹。

至於 jieba 是不是能夠切得精準?我認為大部分情況還算堪用,如果是比較專業的術語或是不常見的詞,可以考慮加入自訂字典檔,並透過 nodejieba.load({userDict: './xxx.txt'}) 來載入。

參考資料

用 JS 做語意分析是不是搞錯了什麼 目錄

  • 用 JS 做語意分析是不是搞錯了什麼(一):斷詞篇
  • 待續......

其實我還沒想到要寫什麼,歡迎許願 (?