我有一个桩可变拼写的n元语法的,我想每个的ngram到它的最佳匹配单词映射出已知的期望的输出的列表。
例如,['mob','MOB','mobi','MOBIL','Mobile]映射到所需的'mobile'输出。
['desk','Desk + Tab','Tab + Desk','Desktop','dsk']的每个输入都映射到所需的“ desktop”输出
我大约有30个这样的“输出”字,还有大约几百万个ngram(唯一性更少)。
我目前最好的办法是让所有的独特的n-gram,复制并粘贴到Excel和手工创建一个映射表,耗时过长,且不能扩展。 第二个想法是带有模糊(模糊-模糊)匹配的东西,但是匹配得不好。
我不是在自然语言术语或图书馆在所有经历过,所以我无法找到一个答案,如何可能做得更好,更快,更可延伸时的独特的n-gram增加或“输出”字变化的数量。
有什么建议吗?
答案 0 :(得分:1)
经典方法是为每个ngram建立一个“特征矩阵”。每个单词映射到一个输出,该输出是0
和29
之间的分类值(每个类一个)
例如,特征可以是模糊的模糊给定的余弦相似度,但是通常您需要更多。然后,您可以根据创建的特征训练分类模型。该模型通常可以是任何东西,神经网络,增强树等。
答案 1 :(得分:1)
由于只有大约30个类,因此您可以确定一个距离度量,例如对于单个单词,说Levenshtein distance,然后为每个ngram分配最接近的类。
那样,无需存储整个整数。
(如果ngram是整个数组,则可以平均数组中每个元素的距离)。
答案 2 :(得分:1)
这个想法是使用前缀树来构建一个映射的字典 字从你的列表,其最大的独特的超弦。一旦构建了该字符串,对于其超字符串与单词本身相同的单词,我们尝试对列表中最接近的单词进行模糊匹配,并返回其超字符串。因此“ dsk”会发现“ des”或“ desk”最接近,然后提取其超字符串。
import org.apache.commons.collections4.Trie;
import org.apache.commons.collections4.trie.PatriciaTrie;
import java.util.*;
import java.util.SortedMap;
public class Test {
static Trie trie = new PatriciaTrie<>();
public static int cost(char a, char b) {
return a == b ? 0 : 1;
}
public static int min(int... numbers) {
return Arrays.stream(numbers).min().orElse(Integer.MAX_VALUE);
}
// this function taken from https://www.baeldung.com/java-levenshtein-distance
static int editDistance(String x, String y) {
int[][] dp = new int[x.length() + 1][y.length() + 1];
for (int i = 0; i <= x.length(); i++) {
for (int j = 0; j <= y.length(); j++) {
if (i == 0) {
dp[i][j] = j;
} else if (j == 0) {
dp[i][j] = i;
} else {
dp[i][j] = min(dp[i - 1][j - 1] + cost(x.charAt(i - 1), y.charAt(j - 1)), dp[i - 1][j] + 1,
dp[i][j - 1] + 1);
}
}
}
return dp[x.length()][y.length()];
}
/*
* custom dictionary that map word to its biggest super string.
* mob -> mobile, mobi -> mobile, desk -> desktop
*/
static void initMyDictionary(List<String> myList) {
for (String word : myList) {
trie.put(word.toLowerCase(), "0"); // putting 0 as default
}
for (String word : myList) {
SortedMap<String, String> prefixMap = trie.prefixMap(word);
String bigSuperString = "";
for (Map.Entry<String, String> m : prefixMap.entrySet()) {
int max = 0;
if (m.getKey().length() > max) {
max = m.getKey().length();
bigSuperString = m.getKey();
}
// System.out.println(bigString + " big");
}
for (Map.Entry<String, String> m : prefixMap.entrySet()) {
m.setValue(bigSuperString);
// System.out.println(m.getKey() + " - " + m.getValue());
}
}
}
/*
* find closest words for a given String.
*/
static List<String> findClosest(String q, List<String> myList) {
List<String> res = new ArrayList();
for (String w : myList) {
if (editDistance(q, w) == 1) // just one char apart edit distance
res.add(w);
}
return res;
}
public static void main(String[] args) {
List<String> myList = new ArrayList<>(
Arrays.asList("mob", "MOB", "mobi", "mobil", "mobile", "desk", "desktop", "dsk"));
initMyDictionary(myList); // build my custom dictionary using prefix tree
// String query = "mob"
// String query = "mobile";
// String query = "des";
String query = "dsk";
// if the word and its superstring are the same, then we try to find the closest
// words from list and lookup the superstring in the dictionary.
if (query.equals(trie.get(query.toLowerCase()))) {
for (String w : findClosest(query, myList)) { // try to resolve the ambiguity here if there are multiple closest words
System.out.println(query + " -fuzzy maps to-> " + trie.get(w));
}
} else {
System.out.println(query + " -maps to-> " + trie.get(query));
}
}
}