mirror of https://github.com/apache/lucene.git
LUCENE-3730: improve Kuromoji search mode heuristics
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1239061 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
2f159c4059
commit
995c5b9ef1
|
@ -142,6 +142,9 @@ New Features
|
||||||
* LUCENE-3305: Added Kuromoji morphological analyzer for Japanese.
|
* LUCENE-3305: Added Kuromoji morphological analyzer for Japanese.
|
||||||
(Christian Moen, Masaru Hasegawa, Simon Willnauer, Uwe Schindler, Mike McCandless, Robert Muir)
|
(Christian Moen, Masaru Hasegawa, Simon Willnauer, Uwe Schindler, Mike McCandless, Robert Muir)
|
||||||
|
|
||||||
|
* LUCENE-3730: Refine Kuromoji search mode (Mode.SEARCH) decompounding
|
||||||
|
heuristics. (Christian Moen via Robert Muir)
|
||||||
|
|
||||||
* LUCENE-3685: Add ToChildBlockJoinQuery and renamed previous
|
* LUCENE-3685: Add ToChildBlockJoinQuery and renamed previous
|
||||||
BlockJoinQuery to ToParentBlockJoinQuery, so that you can now do
|
BlockJoinQuery to ToParentBlockJoinQuery, so that you can now do
|
||||||
joins in both parent to child and child to parent directions.
|
joins in both parent to child and child to parent directions.
|
||||||
|
|
|
@ -54,11 +54,13 @@ public class Viterbi {
|
||||||
|
|
||||||
private static final int DEFAULT_COST = 10000000;
|
private static final int DEFAULT_COST = 10000000;
|
||||||
|
|
||||||
private static final int SEARCH_MODE_LENGTH_KANJI = 3;
|
private static final int SEARCH_MODE_KANJI_LENGTH = 2;
|
||||||
|
|
||||||
private static final int SEARCH_MODE_LENGTH = 7;
|
private static final int SEARCH_MODE_OTHER_LENGTH = 7; // Must be >= SEARCH_MODE_KANJI_LENGTH
|
||||||
|
|
||||||
private static final int SEARCH_MODE_PENALTY = 10000;
|
private static final int SEARCH_MODE_KANJI_PENALTY = 3000;
|
||||||
|
|
||||||
|
private static final int SEARCH_MODE_OTHER_PENALTY = 1700;
|
||||||
|
|
||||||
private static final char[] BOS = "BOS".toCharArray();
|
private static final char[] BOS = "BOS".toCharArray();
|
||||||
|
|
||||||
|
@ -137,7 +139,7 @@ public class Viterbi {
|
||||||
char[] surfaceForm = node.getSurfaceForm();
|
char[] surfaceForm = node.getSurfaceForm();
|
||||||
int offset = node.getOffset();
|
int offset = node.getOffset();
|
||||||
int length = node.getLength();
|
int length = node.getLength();
|
||||||
if (length > SEARCH_MODE_LENGTH_KANJI) {
|
if (length > SEARCH_MODE_KANJI_LENGTH) {
|
||||||
boolean allKanji = true;
|
boolean allKanji = true;
|
||||||
// check if node consists of only kanji
|
// check if node consists of only kanji
|
||||||
for (int pos = 0; pos < length; pos++) {
|
for (int pos = 0; pos < length; pos++) {
|
||||||
|
@ -148,9 +150,9 @@ public class Viterbi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allKanji) { // Process only Kanji keywords
|
if (allKanji) { // Process only Kanji keywords
|
||||||
pathCost += (length - SEARCH_MODE_LENGTH_KANJI) * SEARCH_MODE_PENALTY;
|
pathCost += (length - SEARCH_MODE_KANJI_LENGTH) * SEARCH_MODE_KANJI_PENALTY;
|
||||||
} else if (length > SEARCH_MODE_LENGTH) {
|
} else if (length > SEARCH_MODE_OTHER_LENGTH) {
|
||||||
pathCost += (length - SEARCH_MODE_LENGTH) * SEARCH_MODE_PENALTY;
|
pathCost += (length - SEARCH_MODE_OTHER_LENGTH) * SEARCH_MODE_OTHER_PENALTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,5 @@
|
||||||
package org.apache.lucene.analysis.kuromoji;
|
package org.apache.lucene.analysis.kuromoji;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
|
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
|
||||||
import org.apache.lucene.analysis.BaseTokenStreamTestCase;
|
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
|
||||||
import org.apache.lucene.analysis.Tokenizer;
|
|
||||||
import org.apache.lucene.analysis.kuromoji.Segmenter.Mode;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
|
||||||
import org.apache.lucene.util.UnicodeUtil;
|
|
||||||
import org.apache.lucene.util._TestUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
@ -30,6 +17,19 @@ import org.apache.lucene.util._TestUtil;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
|
import org.apache.lucene.analysis.BaseTokenStreamTestCase;
|
||||||
|
import org.apache.lucene.analysis.TokenStream;
|
||||||
|
import org.apache.lucene.analysis.Tokenizer;
|
||||||
|
import org.apache.lucene.analysis.kuromoji.Segmenter.Mode;
|
||||||
|
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||||
|
import org.apache.lucene.util.UnicodeUtil;
|
||||||
|
import org.apache.lucene.util._TestUtil;
|
||||||
|
|
||||||
public class TestExtendedMode extends BaseTokenStreamTestCase {
|
public class TestExtendedMode extends BaseTokenStreamTestCase {
|
||||||
private final Segmenter segmenter = new Segmenter(Mode.EXTENDED);
|
private final Segmenter segmenter = new Segmenter(Mode.EXTENDED);
|
||||||
private final Analyzer analyzer = new Analyzer() {
|
private final Analyzer analyzer = new Analyzer() {
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package org.apache.lucene.analysis.kuromoji;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.LineNumberReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
|
||||||
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
|
import org.apache.lucene.analysis.BaseTokenStreamTestCase;
|
||||||
|
import org.apache.lucene.analysis.Tokenizer;
|
||||||
|
import org.apache.lucene.analysis.kuromoji.Segmenter.Mode;
|
||||||
|
import org.apache.lucene.util.IOUtils;
|
||||||
|
|
||||||
|
public class TestSearchMode extends BaseTokenStreamTestCase {
|
||||||
|
private final static String SEGMENTATION_FILENAME = "search-segmentation-tests.txt";
|
||||||
|
private final Segmenter segmenter = new Segmenter(Mode.SEARCH);
|
||||||
|
private final Analyzer analyzer = new Analyzer() {
|
||||||
|
@Override
|
||||||
|
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
|
||||||
|
Tokenizer tokenizer = new KuromojiTokenizer(segmenter, reader);
|
||||||
|
return new TokenStreamComponents(tokenizer, tokenizer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Test search mode segmentation */
|
||||||
|
public void testSearchSegmentation() throws IOException {
|
||||||
|
InputStream is = TestSearchMode.class.getResourceAsStream(SEGMENTATION_FILENAME);
|
||||||
|
if (is == null) {
|
||||||
|
throw new FileNotFoundException("Cannot find " + SEGMENTATION_FILENAME + " in test classpath");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
LineNumberReader reader = new LineNumberReader(new InputStreamReader(is, IOUtils.CHARSET_UTF_8));
|
||||||
|
String line = null;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
// Remove comments
|
||||||
|
line = line.replaceAll("#.*$", "");
|
||||||
|
// Skip empty lines or comment lines
|
||||||
|
if (line.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (VERBOSE) {
|
||||||
|
System.out.println("Line no. " + reader.getLineNumber() + ": " + line);
|
||||||
|
}
|
||||||
|
String[] fields = line.split("\t", 2);
|
||||||
|
String sourceText = fields[0];
|
||||||
|
String[] expectedTokens = fields[1].split("\\s+");
|
||||||
|
assertAnalyzesTo(analyzer, sourceText, expectedTokens);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
###
|
||||||
|
### Tests for Kuromoji's search mode heuristic
|
||||||
|
###
|
||||||
|
### In search-mode, Kuromoji uses a heuristic to do extra splitting of words
|
||||||
|
### to get a decompounding effect useful for search. This file includes tests
|
||||||
|
### for this heuristic and demonstrates its usefulness, but also weaknesses.
|
||||||
|
###
|
||||||
|
### This file's format is as follows:
|
||||||
|
### <text><tab><token1> <token2> ... <token>
|
||||||
|
###
|
||||||
|
### This file should use UTF-8 encoding and there is one test per line. The
|
||||||
|
### text to be segmented and its expected surface form token sequence is
|
||||||
|
### separated by a tab ('\t'). Tokens are separated by a half-width space.
|
||||||
|
### Whitespace lines and lines starting with a '#' are ignored. Comments
|
||||||
|
### are not allowed on entry line.
|
||||||
|
###
|
||||||
|
### NOTE: These tests depends on IPADIC
|
||||||
|
###
|
||||||
|
### Revision history:
|
||||||
|
### - 2012-01-29: Initial version
|
||||||
|
###
|
||||||
|
|
||||||
|
##
|
||||||
|
## Organizations
|
||||||
|
##
|
||||||
|
|
||||||
|
# Kansai Internationl Airport
|
||||||
|
関西国際空港 関西 国際 空港
|
||||||
|
# Narita Airport
|
||||||
|
成田空港 成田 空港
|
||||||
|
# Haneda Airport
|
||||||
|
羽田空港 羽田 空港
|
||||||
|
# Nara Institute of Science and Technology
|
||||||
|
奈良先端科学技術大学院大学 奈良 先端 科学 技術 大学院 大学
|
||||||
|
# Tokyo University
|
||||||
|
東京大学 東京 大学
|
||||||
|
# Kyoto University
|
||||||
|
京都大学 京都 大学
|
||||||
|
# Kyoto University Baseball Club
|
||||||
|
京都大学硬式野球部 京都 大学 硬式 野球 部
|
||||||
|
|
||||||
|
##
|
||||||
|
## Katakana titles
|
||||||
|
##
|
||||||
|
|
||||||
|
# Senior Software Engineer
|
||||||
|
シニアソフトウェアエンジニア シニア ソフトウェア エンジニア
|
||||||
|
# Software Engineer
|
||||||
|
ソフトウェアエンジニア ソフトウェア エンジニア
|
||||||
|
# Senior Project Manager
|
||||||
|
シニアプロジェクトマネジャー シニア プロジェクト マネジャー
|
||||||
|
# Project Manager
|
||||||
|
プロジェクトマネジャー プロジェクト マネジャー
|
||||||
|
# Senior Sales Engineer
|
||||||
|
シニアセールスエンジニア シニア セールス エンジニア
|
||||||
|
# System Architect
|
||||||
|
システムアーキテクト システム アーキテクト
|
||||||
|
# Senior System Architect
|
||||||
|
シニアシステムアーキテクト シニア システム アーキテクト
|
||||||
|
# System Administrator
|
||||||
|
システムアドミニストレータ システム アドミニストレータ
|
||||||
|
システムアドミニストレーター システム アドミニストレーター
|
||||||
|
# Senior System Administrator
|
||||||
|
シニアシステムアドミニストレーター シニア システム アドミニストレーター
|
||||||
|
|
||||||
|
##
|
||||||
|
## Company names (several are fictitious)
|
||||||
|
##
|
||||||
|
|
||||||
|
# SoftBank Mobile
|
||||||
|
ソフトバンクモバイル ソフトバンク モバイル
|
||||||
|
# Alpine Materials
|
||||||
|
アルパインマテリアルズ アルパイン マテリアルズ
|
||||||
|
# Sapporo Holdings
|
||||||
|
サッポロホールディングス サッポロ ホールディングス
|
||||||
|
# Yamada Corporation
|
||||||
|
ヤマダコーポレーション ヤマダ コーポレーション
|
||||||
|
# Canon Semiconductor equipement NOTE: Semiconductor becomes semi + conductor
|
||||||
|
キヤノンセミコンダクターエクィップメント キヤノン セミ コンダクター エクィップメント
|
||||||
|
# Orental Chain
|
||||||
|
オリエンタルチエン オリエンタル チエン
|
||||||
|
# Ally Projects Japan NOTE: Becomes one token as プロジェクツ is not in IPADIC
|
||||||
|
アーリープロジェクツジャパン アーリープロジェクツジャパン
|
||||||
|
# Peter Pan Corporation
|
||||||
|
ピーターパンコーポレーション ピーター パン コーポレーション
|
||||||
|
# AIM Create
|
||||||
|
エイムクリエイツ エイムクリエイツ
|
||||||
|
# Mars Engineering
|
||||||
|
マースエンジニアリング マース エンジニアリング
|
||||||
|
# Fuji Protein Technology
|
||||||
|
フジプロテインテクノロジー フジ プロテイン テクノロジー
|
||||||
|
|
||||||
|
##
|
||||||
|
## Person names
|
||||||
|
##
|
||||||
|
|
||||||
|
# Michael Jackson
|
||||||
|
マイケルジャクソン マイケル ジャクソン
|
||||||
|
# Steve Jobs
|
||||||
|
スティーブジョブズ スティーブ ジョブズ
|
||||||
|
# Harry Potter NOTE: Becomes one token (short word)
|
||||||
|
ハリーポッター ハリーポッター
|
||||||
|
# Bill Gates NOTE: Becomes one token (short work)
|
||||||
|
ビルゲイツ ビルゲイツ
|
||||||
|
# Sean Connery NOTE: Becomes one token (okay)
|
||||||
|
ショーンコネリー ショーンコネリー
|
||||||
|
|
||||||
|
##
|
||||||
|
## Other nouns
|
||||||
|
##
|
||||||
|
|
||||||
|
# Holdings
|
||||||
|
ホールディングス ホールディングス
|
||||||
|
# Engineering
|
||||||
|
エンジニアリング エンジニアリング
|
||||||
|
# Software Engineering
|
||||||
|
ソフトウェアエンジニアリング ソフトウェア エンジニアリング
|
||||||
|
# Shopping center
|
||||||
|
ショッピングセンター ショッピング センター
|
||||||
|
# Game center (arcade) NOTE: One token because of short word
|
||||||
|
ゲームセンター ゲームセンター
|
||||||
|
# Christmas shopping
|
||||||
|
クリスマスショッピング クリスマス ショッピング
|
||||||
|
# Download file
|
||||||
|
ダウンロードファイル ダウンロード ファイル
|
||||||
|
# Technology
|
||||||
|
テクノロジー テクノロジー
|
||||||
|
# Lillehammer Olympics
|
||||||
|
リレハンメルオリンピック リレハンメル オリンピック
|
||||||
|
|
||||||
|
##
|
||||||
|
## Problematic terms
|
||||||
|
##
|
||||||
|
|
||||||
|
# JT Engineering NOTE: Becomes J Tien ginia ring (substrings are in IPADIC)
|
||||||
|
ジェイティエンジニアリング ジェイ ティエン ジニア リング
|
||||||
|
# Anchovy pasta NOTE: Become Anch yvipasta
|
||||||
|
アンチョビパスタ アンチ ョビパスタ
|
||||||
|
# Surprise gift NOTE: Becomes one token (surprise not in IPADIC)
|
||||||
|
サプライズギフト サプライズギフト
|
Loading…
Reference in New Issue