NLP 開源形近字演演算法之相似字列表(番外篇)

2023-03-28 15:01:04

創作目的

國內對於文字的相似度計算,開源的工具是比較豐富的。

但是對於兩個漢字之間的相似度計算,國內基本一片空白。國內的參考的資料少的可憐,國外相關檔案也是如此。

本專案旨在拋磚引玉,實現一個基本的相似度計算工具,為漢字 NLP 貢獻一點綿薄之力。

推薦閱讀:

NLP 中文形近字相似度計算思路

中文形近字相似度演演算法實現,為漢字 NLP 盡一點綿薄之力

當代中國最貴的漢字是什麼?

NLP 開源形近字演演算法補完計劃(完結篇)

NLP 開源形近字演演算法之形近字列表(番外篇)

開源專案線上化 中文繁簡體轉換/敏感詞/拼音/分詞/漢字相似度/markdown 目錄

需求

有時候我們並不是需要返回兩個字的相似,而是需要返回一個漢字的相似列表。

實現思路

我們可以分別計算所有的漢字之間的相似度,然後保留最大的前100個,放在字典中。

然後實時查詢這個字典即可。

實現方式

bihuashu_2w.txt 中我們主要需要的是對應的 2W 常見漢字。

hanzi_similar_list.txt 用來存放漢字和相似字的對映關係。

資料初始化

public static void main(String[] args) {
    final String path = "D:\\code\\coin\\nlp-hanzi-similar\\src\\main\\resources\\hanzi_similar_list.txt";
    // 讀取列表
    List<String> lines = FileUtil.readAllLines("D:\\code\\coin\\nlp-hanzi-similar\\src\\main\\resources\\nlp\\bihuashu_2w.txt");
    // 所有的單詞
    Set<String> allWordSet = new HashSet<>();
    for(String line : lines) {
        String word = line.split(" ")[0];
        allWordSet.add(word);
    }
    // 迴圈對比
    for(String word : allWordSet) {
        List<String> list = getSimilarListData(word, allWordSet);
        String line = word +" " + StringUtil.join(list, "");
        FileUtil.append(path, line);
    }
}
  • 優先順序佇列取前 100 個

我們通過優先順序佇列儲存:

private static List<String> getSimilarListData(String word, Set<String> wordSet) {
    PriorityQueue<SimilarListDataItem> items = new PriorityQueue<>(new Comparator<SimilarListDataItem>() {
        @Override
        public int compare(SimilarListDataItem o1, SimilarListDataItem o2) {
            // 相似度大的放在前面
            return -o1.getRate().compareTo(o2.getRate());
        }
    });
    for(String other : wordSet) {
        if(word.equals(other)) {
            continue;
        }
        // 對比
        double rate = HanziSimilarHelper.similar(word.charAt(0), other.charAt(0));
        SimilarListDataItem item = new SimilarListDataItem(other, rate);
        items.add(item);
    }
    final int limit = 100;
    List<String> wordList = new ArrayList<>();
    for(SimilarListDataItem item : items) {
        wordList.add(item.getWord());
        if(wordList.size() >= limit) {
            break;
        }
    }
    return wordList;
}

相似字的獲取

初始化好資料之後,一切就變得非常簡單:

  • 介面定義
/**
 * 資料介面-相似列表
 * @author binbin.hou
 * @since 1.3.0
 */
public interface IHanziSimilarListData {

    /**
     * 返回資料資訊
     * @param word 單詞
     * @return 結果
     * @since 1.3.0
     */
    List<String> similarList(String word);

}
  • 資料獲取
public class HanziSimilarListData implements IHanziSimilarListData {

    private static volatile Map<String, List<String>> map = Guavas.newHashMap();


    @Override
    public List<String> similarList(String word) {
        if(MapUtil.isEmpty(map)) {
            initDataMap();
        }

        return map.get(word);
    }

    private void initDataMap() {
        if(MapUtil.isNotEmpty(map)) {
            return;
        }

        //DLC
        synchronized (map) {
            if(MapUtil.isEmpty(map)) {
                List<String> lines = StreamUtil.readAllLines("/hanzi_similar_list.txt");

                for(String line : lines) {
                    String[] words = line.split(" ");
                    // 後面的100個相近詞
                    List<String> list = StringUtil.toCharStringList(words[1]);
                    map.put(words[0], list);
                }
            }
        }
    }

}

便利性

為了使用者使用方便,我們在 HanziSimilarHelper 中新增 2 個工具類方法:

/**
 * 相似的列表
 * @param hanziOne 漢字一
 * @param limit 大小
 * @return 結果
 * @since 1.3.0
 */
public static List<String> similarList(char hanziOne, int limit) {
    return HanziSimilarBs.newInstance().similarList(hanziOne, limit);
}
/**
 * 相似的列表
 * @param hanziOne 漢字一
 * @return 結果
 * @since 1.3.0
 */
public static List<String> similarList(char hanziOne) {
    return similarList(hanziOne, 10);
}

測試效果

我們使用看一下效果:

我們來看一下【愛】的形近字。

List<String> list = HanziSimilarHelper.similarList('愛');
Assert.assertEquals("[爰, 爯, 受, 爭, 妥, 憂, 李, 爳, 叐, 雙]", list.toString());

開源地址

為了便於大家使用學習,專案已開源。

https://github.com/houbb/nlp-hanzi-similar

小結

一個字的形近字可以做很多有趣的事情,這個要看大家的想象力。

實現方式也不難,最核心的還是相似度的計算。

我是老馬,期待與你的下次重逢。