Re: [請益] 英文單字還原的lib (snowball)

作者: qrtt1 (有些事,有時候。。。)   2018-09-01 00:00:22
感覺是個有趣的東西,剛好用過 Lucene 也來玩玩。
順便工商一下 JCConf 早鳥票發售中....
※ 引述《asleepme (500年沒換暱稱了)》之銘言:
: 有沒有對語言有研究的大大,用過snowball這套演算法?
: 我們在做語言相關的研究,遇到一個很基本也很重要的問題
: 就是時態問題,過去式、原型、單複數...
: 為了簡化處理,希望能把動詞還原原型、複數還原單數
: 簡單的ed、s、ly之類的都還算好處理,沒有lib也可以自己算
: 網路上找了一陣,最多人討論的就是snowball的演算法,也有很多語言的lib
: 但是遇到y結尾的變化,他只是去ed而已
: 例如replied => repli,沒有還原成reply
: 但是我們系統資料庫通常是紀錄 reply,這樣就會match不到
: 但是像loving、loved這種又還原的很好成love
: 至於不規則變化,snowball看起來是完全不處理
: 想知道大家用snowball都是怎麼用的?
: 我們的狀況是會有很多單字的原型跟相關的資料
: 然後要對進來的資料mapping,給他相關資料
: 不論來的東西是原型、過去式、單複數,都要對的起來
: 或是我們也把自己的資料全部用snowball算一次存起來
: 然後進來的資料也用一樣的算法處理,再一路mapping回原本的資料 XD
: 還是乾脆自己建適合自己應用的表?
因為不知道你用什麼 library,假設是比較熱門的 Lucene !?
(或可能是 scikit-learn 內的 stemmr),我先用 Java 的 Lucene 來探索一下好了。
先上個簡單的程式測了一下:
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.core.LowerCaseFilter;
import org.apache.lucene.analysis.snowball.SnowballFilter;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
public class Lab {
public static void showTerms(String text) throws IOException {
List<String> terms = new ArrayList<>();
try (StandardAnalyzer analyzer = new StandardAnalyzer()) {
TokenStream input = new LowerCaseFilter(
analyzer.tokenStream("field", new StringReader(text)));
// 選用 en 常用的 Porter Stemmer
try (TokenStream tokenizer = new SnowballFilter(input, "Porter")) {
CharTermAttribute term =
tokenizer.addAttribute(CharTermAttribute.class);
tokenizer.reset();
while (tokenizer.incrementToken()) {
terms.add(term.toString());
}
tokenizer.end();
}
}
System.out.println("input: " + text + " => " + terms);
}
public static void main(String[] args) throws IOException {
// 測試一些動詞變化
showTerms("replied reply");
showTerms("become became become");
showTerms("be/am/are/is");
showTerms("was/were/was");
showTerms("been");
}
}
======================================================================
輸出結果是:
input: replied reply => [repli, repli]
input: become became become => [becom, becam, becom] 這組看起來失敗了
input: be/am/are/is => [am] 看起來 standard analzyer 吃掉了什麼
input: was/were/was => [were]
input: been => [been]
因為這東西主要是要建 search index 的 term,
觀察起來原始資料主要是給人看的,
在 index 內是建 tokenized 的資料,
使用者輸入要查詢時,也需要用建 index 一樣的 tokenizer setting
才能查到一致的結果。
所以,就算上面的結果不完全是對的(或說一致),
但因為歪的一致那搜出來的結果應該也會歪得一致
若是你想要有原始的字,那其實多加上原來的位置對應就好。
// 增加 OffsetAttribute
CharTermAttribute term = tokenizer.addAttribute(CharTermAttribute.class);
OffsetAttribute offset = tokenizer.addAttribute(OffsetAttribute.class);
tokenizer.reset();
while (tokenizer.incrementToken()) {
terms.add(term.toString());
// 印出原來的字串
System.err.println(term + " => "
+ text.substring(offset.startOffset(), offset.endOffset()));
}
tokenizer.end();
結果的範例:
repli => replied
repli => reply
becom => become
becam => became
becom => become
am => am
were => were
been => been
PS. 有些 be 動詞被吃掉應該是 StandardAnalyzer 搞的
另外,在 en 部分的 stemmer 還有不同的實作,可能就要再一一測試了
https://github.com/apache/lucene-solr/tree/master/
lucene/analysis/common/src/java/org/apache/lucene/analysis/en
短:
http://bit.ly/2LIF9Nu
作者: alihue (wanda wanda)   2018-09-01 17:54:00
要不要加一下 syntax highlight,良葛格(誤

Links booklink

Contact Us: admin [ a t ] ucptt.com