基于贝叶斯公式的拼音输入法二元模型实现
和英語的直接輸入不同,漢語輸入法是通過拼音轉譯輸入,而由于不同的拼音可以對應同一個字,而不同的字也可能存在多個拼音,是一個多對多的關系,因此在獲得拼音的時候,需要能夠準確地將其轉換為正確的漢字,這就是拼音輸入法的作用。本文介紹了一種基于貝葉斯公式的全拼拼音輸入法實現,全拼也就是輸入完整的拼音,不考慮縮寫等情況。
以下是一段拼音:
huan ying guan zhu wo de bo ke
對于我們人來說,可以很容易的知道每個拼音對應的是哪個字:
歡迎關注我的博客
但是對于計算機,拼音所對應的每個漢字都是等價的,比如huan可以是“歡”,也可以是“換”,那么如果讓計算機知道在這個句子里huan是“歡”?參考一下人將拼音轉化為漢字的過程,可以發現在判斷某個拼音的時候,我們并不是僅僅根據那一個拼音去判斷,而是將其放在整個句子的上下文環境里去判斷,也就是說在給定拼音句子的情況下,找到最大概率的漢字句子。翻譯成數學語言就是,令X為漢字句子,Y為拼音句子,求
根據貝葉斯公式
其中
為常量,因為拼音已經固定,
????使用識別信度代替,
考慮到隨著n的增加,該計算會越來越復雜,并且會增加數據稀疏性,因此使用二元模型,即每個字的概率只和它前一個字相關(當然也可以使用三元模型,即每個字的概率只和它前兩個字相關),簡化后
所以問題轉化為
其中CF識別信度表示當漢字確定時,其拼音為該拼音的概率,在此可以默認為1,故問題的關鍵就是
又由概率公式
其中
可以根據統計的思想認為代表和在語料庫同時出現的次數
代表出現的次數??紤]到和在語料庫中可能從來沒有同時出現過,因此將改寫為
。
這樣算法的思路就很清楚了,首先獲得語料庫,可以是大量的新聞稿,遍歷所以新聞稿,統計每個字出現的次數以及每兩個連續的字出現的次數,然后讀入新的拼音句子從統計結果中選出最佳的漢字。需要注意的是我們需要最大化的是
而不是
也就是說需要找到是整個句子的概率最大的,而不是某個字概率最大的漢字,這一點可以通過動態規劃實現。
以下上代碼:
記錄每個拼音對應的漢字集
private void initPinyin(){char[] cs = Utility.readStrFromFile(Utility.HANZI_TABLE).toCharArray();List<String> ls = Utility.readListFromFile(Utility.PINYIN_TABLE);String[] ps = new String[ls.size()];String[] hs = new String[ls.size()];for (int i = 0; i < ls.size(); ++i){int border = ls.get(i).indexOf(" ");ps[i] = ls.get(i).substring(0, border);hs[i] = ls.get(i).substring(border + 1);}p_map = new HashMap<String, List<Character>>();for (int i = 1; i < cs.length; ++i){for (int j = 0; j < hs.length; ++j){if(hs[j].indexOf(cs[i]) != -1){if(!p_map.containsKey(ps[j])){p_map.put(ps[j], new ArrayList<Character>());}p_map.get(ps[j]).add(cs[i]);}}}writeMap(p_map, Utility.PINYIN_MAP); }根據語料庫進行統計(語料庫為json格式)
private void initHanzi(){char[] cs = Utility.readStrFromFile(Utility.HANZI_TABLE).toCharArray();c_map = new HashMap<Character, Integer>();w_map = new HashMap<Character, Map<Character, Integer>>();for (int i = 1; i < cs.length; ++i){c_map.put(cs[i], 0);w_map.put(cs[i], new HashMap<Character, Integer>());}w_map.put('#', new HashMap<Character, Integer>());writeMap(c_map, Utility.HANZI_MAP);writeMap(w_map, Utility.TWO_HANZI_MAP); }private void updateHanzi(String news_file) {List<String> ss = Utility.readListFromFile(news_file);c_map = readMap(Utility.HANZI_MAP);w_map = readMap(Utility.TWO_HANZI_MAP);for (String s: ss) {s = s.substring(s.indexOf("{\"html\":"));JSONObject object = new JSONObject(s);String content = object.getString("html");char[] hanzi = content.toCharArray();char before = '#';for (char h : hanzi) {if (isChinese(h)) {Integer count = c_map.get(h);if (count != null) {c_map.replace(h, count + 1);if ((count = w_map.get(before).get(h)) != null) {w_map.get(before).replace(h, count + 1);} else {w_map.get(before).put(h, 1);}before = h;} else {before = '#';}} else {before = '#';}}}writeMap(c_map, Utility.HANZI_MAP);writeMap(w_map, Utility.TWO_HANZI_MAP); }使用動態規劃進行翻譯
public String[] predict(String pinyin){String[] ps = pinyin.split(" ");Queue<Possibility> queue = new PriorityQueue<Possibility>();queue.add(new Possibility("#", Utility.INIT_POSSIBILITY));return dynamicPlanning(ps, 0, queue); }private String[] dynamicPlanning(String[] ps, int index, Queue<Possibility> queue){if(index == ps.length){String[] result = new String[Math.min(queue.size(), Utility.OUT_SIZE)];for (int i = 0; i < result.length; ++i){result[i] = queue.poll().str.substring(1);}return result;}Possibility[] before = new Possibility[Math.min(queue.size(), Utility.MAX_SIZE)];double norm = queue.peek().p;for (int i = 0; i < before.length; ++i){before[i] = queue.poll();before[i].p /= norm;}queue.clear();List<Character> cs = p_map.get(ps[index]);for (Possibility s : before) {for (Character c : cs) {Possibility possibility = s.generateNext(c);if(!Double.isNaN(possibility.p)){queue.add(possibility);}}}return dynamicPlanning(ps, index + 1, queue); }class Possibility implements Comparable<Possibility> {String str;double p;Possibility(String str, double p) {this.str = str;this.p = p;}Possibility generateNext(char next){char before = str.charAt(str.length() - 1);return new Possibility(str + next, p * getPossibility(before, next));}private double getPossibility(char before, char next){int n_count = c_map.get(next);if(before == '#'){return n_count / Utility.RATIO;}else {int b_count = c_map.get(before);Integer integer = w_map.get(before).get(next);int bn_count = integer == null ? 0 : integer;return Utility.ALPHA * bn_count / b_count + (1 - Utility.ALPHA) * n_count / Utility.RATIO;}}public int compareTo(Possibility o) {return Double.compare(o.p, p);} }按概率高低輸出概率最高的5個漢字句子
項目地址https://github.com/ChenErTong/PinYin
總結
以上是生活随笔為你收集整理的基于贝叶斯公式的拼音输入法二元模型实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【多媒体基础知识】 --- 什么是流媒体
- 下一篇: 使用unity完成简单的打地鼠游戏2D制