数据挖掘:基于朴素贝叶斯分类算法的文本分类实践
前言:
? 如果你想對一個陌生的文本進行分類處理,例如新聞、游戲或是編程相關類別。那么貝葉斯分類算法應該正是你所要找的了。貝葉斯分類算法是統計學中的一種分類方法,它利用概率論中的貝葉斯公式進行擴展。所以,這里建議那些沒有概率功底或是對概率論已經忘記差不多的讀者可以先去學習或是溫習一下《概率論與數理統計》中的條件概率那一個章節。
??由于貝葉斯定理假設一個屬性值對給定類的影響獨立于其它屬性的值,而此假設在實際情況中經常是不成立的,因此其分類準確率可能會下降。為此,就衍生出許多降低獨立性假設的貝葉斯分類算法,如TAN(tree augmented Bayes network)算法。關于TAN算法不在本文的敘述范圍之內,這里我們不作討論。
? 下面我們就針對樸素貝葉斯分類算法,進行原理淺析和文本分類實踐(這里筆者使用Java語言開發)。
本文鏈接:http://blog.csdn.net/lemon_tree12138/article/details/48520315 --Coding-Naga
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?--轉載請注明出處
公式說明:
1.全概率公式:
2.貝葉斯公式:
上面的兩個公式是最簡單的兩個公式說明,旨在簡化理解。
思路分析:
? 在開始理解貝葉斯算法之前,獨立于本文之外。如果有人問你如何讓程序給一篇陌生的文章分類?你要怎么做呢?
? 我能想到的就是以關鍵詞來區分。比如分類為郵箱的類別中,我選取三個關鍵詞:郵箱、郵件和收發。然后使用這三個關鍵詞在文章中去依次查找,統計出此三個關鍵詞總共出現了多少次,再與其他的類別進行比較。次數最多的即為這篇文章的分類。如果要再精確一些,可以采用不同權重的方式,上面說的方法,權重為1。如果采用權重的策略,那么這里就不是出現次數最多的類別了,而是以計分的方式,次數*權重并累加。最高分的類別即為本文的分類。
? 當然,這是一種解決方法。這個其實跟貝葉斯分類算法還是有一些類似的,有了這種想法,再去理解貝葉斯就容易得多了。
流程說明:
樸素貝葉斯分類的流程可以由下圖表示(圖片來源網絡):
? 針對上圖有一些需要說明的地方,首先這張流程圖的確可以很清楚地表達我們樸素貝葉斯模型的流程。需要注意的是,這里如果P(x|yi)中的x如果是在訓練集中不存在的一個特征值,我們是無法進行計算它的先驗概率的。不過還好,因為x在訓練中不存在,那么我們就可以粗略認為,x是一個與yi無關的值,即概率為0。
代碼展示:
1.準備階段:
? 在準備階段有兩個步驟,確定特征屬性和獲取樣本。確定特征屬性這個會因個人對分類的理解以及需求不同而不同;而獲取樣本則是比較簡單的讀取文件。如下:
/*** 讀取訓練文檔中的訓練數據* 并進行封裝* * @param filePath* 訓練文檔的路徑* @return* 訓練數據集*/public static ArrayList<ArrayList<String>> read(String filePath) {if (Tools.isEmptyString(filePath)) {return null;}ArrayList<ArrayList<String>> trainningSet = new ArrayList<ArrayList<String>>();List<String> datas = readFile(filePath);ArrayList<String> singleTrainning = null;for (int i = 0; i < datas.size(); i++) {String[] characteristicValues = datas.get(i).split(" ");singleTrainning = new ArrayList<String>();for (int j = 0; j < characteristicValues.length; j++) {if (!Tools.isEmptyString(characteristicValues[j])) {singleTrainning.add(characteristicValues[j]);}}trainningSet.add(singleTrainning);}return trainningSet;}
2.訓練階段:
? 在訓練階段,我們就是預先計算出一些先驗概率,這些先驗概率是與待計算的特征值x無關的。不關這個x是否在訓練集中存在,都是無關的,這個在前面已經說過了。那么先驗概率主要有P(classify),P(key)和P(key|classify)。
? P(classify):
/*** 預先計算出每個分類出現的概率* * @param map* 所有分類總的數據集* @param classifyProbablityMap* 每個分類classify的出現概率*/public void preCalculateClassifyProbablity(Map<String, ArrayList<ArrayList<String>>> map, Map<String, Double> classifyProbablityMap) {if (map == null || classifyProbablityMap == null) {return;}Object[] classes = map.keySet().toArray();int totleClassifyCount = 0;for (int i = 0; i < classes.length; i++) {totleClassifyCount += map.get(classes[i].toString()).size();}if (totleClassifyCount == 0) {return;}for (int i = 0; i < classes.length; i++) {if (!classifyProbablityMap.containsKey(classes[i])) {classifyProbablityMap.put(classes[i].toString(), 1.0 * map.get(classes[i]).size() / totleClassifyCount);}}} ? P(key):
/*** 預先計算出每個關鍵字出現的概率* TODO* @param map* 所有分類總的數據集* @param keyProbablityMap* 每個特征值key的出現概率*/public void preCalculateKeyProbablity(Map<String, ArrayList<ArrayList<String>>> map, Map<String, Double> keyProbablityMap) {if (map == null || keyProbablityMap == null) {return;}Object[] classes = map.keySet().toArray();String key = "";int totleKeyCount = 0;for (int i = 0; i < map.size(); i++) {ArrayList<ArrayList<String>> classify = map.get(classes[i]);ArrayList<String> featureVector = null; // 分類中的某一特征向量for (int j = 0; j < classify.size(); j++) {featureVector = classify.get(j);for (int k = 0; k < featureVector.size(); k++) {key = featureVector.get(k);totleKeyCount++;if (keyProbablityMap.get(key) == null) {keyProbablityMap.put(key, 1.0);} else {keyProbablityMap.replace(key, keyProbablityMap.get(key) + 1.0);}}}}if (totleKeyCount == 0) {return;}Set<String> keys = keyProbablityMap.keySet();for (String string : keys) {keyProbablityMap.replace(string, keyProbablityMap.get(string) / totleKeyCount);}} ? P(key|classify):
/*** 計算先驗概率P(key|classify)* * @param map* 所有分類總的數據集* @param keyClassifyMap* 先驗概率P(key|classify)的所有數據集*/public void preCalculateKeyInClassifyProbablity(Map<String, ArrayList<ArrayList<String>>> map, Map<String, Map<String, Double>> keyClassifyMap) {if (map == null || keyClassifyMap == null) {return;}// 統計每種分類共有多少個特征值Map<String, Double> keyCountMap = new HashMap<String, Double>();// 統計key|classify的個數Object[] classes = map.keySet().toArray();Map<String, Double> vector = null;for (int i = 0; i < map.size(); i++) {ArrayList<ArrayList<String>> classify = map.get(classes[i]);for (int j = 0; j < classify.size(); j++) {ArrayList<String> featureVector = classify.get(j);for (int k = 0; k < featureVector.size(); k++) {// 統計特征值if (keyClassifyMap.containsKey(classes[i])) {if (keyClassifyMap.get(classes[i]).containsKey(featureVector.get(k))) {double lastValue = keyClassifyMap.get(classes[i]).get(featureVector.get(k));vector = keyClassifyMap.get(classes[i]);vector.put(featureVector.get(k), 1.0 + lastValue);keyClassifyMap.replace(classes[i].toString(), vector);} else {vector = keyClassifyMap.get(classes[i]);vector.put(featureVector.get(k), 1.0);keyClassifyMap.put(classes[i].toString(), vector);}} else {vector = new HashMap<String, Double>();vector.put(featureVector.get(k), 1.0);keyClassifyMap.put(classes[i].toString(), vector);}// 統計每種分類共有多少個特征值 keyCountMapif (keyCountMap.containsKey(classes[i])) {keyCountMap.put(classes[i].toString(), 1.0 + keyCountMap.get(classes[i]));} else {keyCountMap.put(classes[i].toString(), 1.0);}}}}// 遍歷keyClassifyMap計算概率Map<String, Double> keyVector = null;Object[] keys = null;for (int i = 0; i < keyClassifyMap.size(); i++) {keyVector = keyClassifyMap.get(classes[i]);keys = keyVector.keySet().toArray();for (int j = 0; j < keyVector.size(); j++) {keyVector.put(keys[j].toString(), keyVector.get(keys[j]) / keyCountMap.get(classes[i]));}keyClassifyMap.put(classes[i].toString(), keyVector);}}
3.應用階段:
? 對于貝葉斯的應用,即是針對上面的貝葉斯公式進行的。即計算P(classify|key)=?.
? 也就是說,在特征值為key時,分類為classify的概率為多少?這是我們所求的。這一步很簡單,只要我們拿到公式右邊的三個概率值,就可以計算出貝葉斯公式左邊的值:
/*** 計算在出現key的情況下,是分類classify的概率 [ P(Classify | key) ]* * @param map* 所有分類的數據集* @param classify* 某一特定分類* @param key* 某一特定特征* @return* P(Classify | key)*/private double calProbabilityClassificationInKey(Map<String, ArrayList<ArrayList<String>>> map, Map<String, Double> classPMap, Map<String, Double> keyPMap, Map<String, Map<String, Double>> keyClassifyMap, String classify, String key) {double pkc = (keyClassifyMap.get(classify).containsKey(key) ? keyClassifyMap.get(classify).get(key) : 0); // p(key|classify)double pc = classPMap.get(classify); // p(classify)double pk = keyPMap.get(key) == null ? 0 : keyPMap.get(key); // p(key)double pck = 0.0; // p(classify | key)if (pk == 0) {pck = 0;} else {pck = (pkc * pc / pk) * pk;}return pck;} ? 以上就是本文關于貝葉斯分類算法的全部內容。如有疑問可以留言,大家一起討論學習。
參考:
1.《概率論與數理統計》(第四版)?浙大版
2.《數據之美》
3.http://www.cnblogs.com/leoo2sk/archive/2010/09/17/naive-bayesian-classifier.html
4.http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html
附件源碼:
下面的代碼是最初的一個版本,大家可以結合本文對代碼進行修改。
http://download.csdn.net/detail/u013761665/9114225
總結
以上是生活随笔為你收集整理的数据挖掘:基于朴素贝叶斯分类算法的文本分类实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法:模式匹配之KMP算法
- 下一篇: 算法:程序设计之并查集