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