python之樸素貝葉斯對展會數(shù)據(jù)分類

1.目的

在公司實(shí)習(xí)耸峭,分別從國內(nèi)國外兩個(gè)網(wǎng)站爬取了一些展會數(shù)據(jù)茉帅,在數(shù)據(jù)處理上目前需要將其按照各個(gè)類別分類好处窥,并提供對應(yīng)展會地址的經(jīng)緯度重付。
國內(nèi)數(shù)據(jù)如下:

01.png

國內(nèi)數(shù)據(jù)比較少,占四百多條障贸,在類別上來看有所屬行業(yè)這一列错森,所以比較好處理。
國外數(shù)據(jù)就有些尷尬:
02.png

國外網(wǎng)站展會數(shù)據(jù)將近五萬多條篮洁,跟分類有關(guān)的只有categories這一列數(shù)據(jù)涩维,都是一些標(biāo)簽詞,還偏少袁波。
現(xiàn)在需要將五萬多條展會數(shù)據(jù)進(jìn)行分類瓦阐,如何解決這個(gè)問題,我覺得可以寫個(gè)樸素貝葉斯分類器篷牌。

2.數(shù)據(jù)

樸素貝葉斯分類器是利用樣本數(shù)據(jù)來進(jìn)行訓(xùn)練的睡蟋,每個(gè)樣本必須含有一組特征詞以及對應(yīng)的分類。

2.1.數(shù)據(jù)的準(zhǔn)備

獲取的國內(nèi)數(shù)據(jù)就很適合作為訓(xùn)練數(shù)據(jù)枷颊,可以將其處理成如下格式作為樣本輸入:

03.png

訓(xùn)練數(shù)據(jù)主要是這兩列戳杀,所屬行業(yè)以及展會范圍叫倍,每一行的類別都有其對應(yīng)的展會范圍,將這些作為訓(xùn)練數(shù)據(jù)實(shí)在再好不過豺瘤,我們將其稱之為訓(xùn)練集吆倦。
那么相對應(yīng),國外待分類的展會數(shù)據(jù)便是測試集坐求。
我們將利用訓(xùn)練好的分類器蚕泽,將測試集一一輸入,看看能否得到期望的輸出結(jié)果桥嗤。
可是現(xiàn)在有個(gè)問題须妻,作為訓(xùn)練集的國內(nèi)數(shù)據(jù)只有四百多條,實(shí)在太少泛领,于是我只能再去那個(gè)國內(nèi)展會網(wǎng)站將以前的展會數(shù)據(jù)盡量爬取下來荒吏,最終訓(xùn)練集達(dá)到了39555條,雖說數(shù)量還是不夠渊鞋,但是不試試最終分類器的分類結(jié)果绰更,說不定準(zhǔn)確率還可以呢?

2.2.分詞&&提取

不積小流锡宋,無以成江海儡湾,以小見大,咱們看這樣一條數(shù)據(jù):

所屬行業(yè):建材五金
展會范圍:各類衛(wèi)生潔具执俩、浴室家具和配件徐钠、面盆、馬桶役首、淋浴房尝丐、浴缸、花灑衡奥、水龍頭及配件爹袁、浴室照明、鏡子杰赛、五金掛件等

思考一下呢簸,這一條數(shù)據(jù)的所屬行業(yè)展會范圍有什么關(guān)系,我們將從這里得到編寫分類器的出發(fā)點(diǎn)乏屯。
在這條數(shù)據(jù)中,展會范圍的內(nèi)容是具有一定的代表性的瘦赫,其代表這條數(shù)據(jù)的描述很偏向建材五金這個(gè)行業(yè)辰晕。
那我么是不是可以提煉出這個(gè)描述的關(guān)鍵詞,從而讓這個(gè)關(guān)鍵詞代表建材五金這個(gè)行業(yè)席揽。

# 可以利用結(jié)巴分詞
import jieba.analyse
con = "各類衛(wèi)生潔具羊精、浴室家具和配件、面盆填抬、馬桶窘问、淋浴房辆童、浴缸、花灑惠赫、水龍頭及配件把鉴、浴室照明、鏡子儿咱、五金掛件等"
feature = jieba.analyse.extract_tags(con, 8)
print(feature)
# output:['配件', '各類', '家具', '五金', '照明', '浴室', '淋浴房', '浴缸']

那么剛剛那條數(shù)據(jù)可以這樣看:

# data_01
所屬行業(yè):建材五金
描述關(guān)鍵詞:'配件', '各類', '家具', '五金', '照明', '浴室', '淋浴房', '浴缸'

以此類推庭砍,如果一條未分類數(shù)據(jù)的關(guān)鍵詞也是這樣,那是不是可以將該數(shù)據(jù)歸為建材五金這個(gè)類別混埠,是的怠缸,你可以這么干。
但有個(gè)問題钳宪,若該未分類數(shù)據(jù)的關(guān)鍵詞只含有以上關(guān)鍵詞的某個(gè)揭北,比如:

# data_02
所屬行業(yè):未知
描述關(guān)鍵詞:'配件','家具'

這樣子若分為建材五金不大對吧,我倒覺得應(yīng)該分為房產(chǎn)家居吏颖,這個(gè)問題可以解決罐呼,就讓我們的概率出場吧。
大三的時(shí)候?qū)W過概率統(tǒng)計(jì)侦高,也記得一個(gè)公式名為貝葉斯定理嫉柴。

04.jpg

這表示計(jì)算條件概率的公式:

P(A\B) = P(B\A)*P(A)/P(B) == 后驗(yàn)概率 = 先驗(yàn)概率 x 調(diào)整因子
# 這樣寫會不會更加清晰
P(category\keywords) = P(category) * P(keywords\category)/ P(keywords)
## 樸素貝葉斯便是假設(shè)即將被組合的各個(gè)概率是獨(dú)立的,可以理解成keyword1出現(xiàn)在category1的概率和keyword2出現(xiàn)在category1的概率是沒有關(guān)系的奉呛,是獨(dú)立的计螺。
P(category\keywords) = P(category) * P(keyword1\category)P(keyword2\category)...P(keywordn\category)/ P(keywords)

總結(jié)就是,我們先求出樣本空間中每個(gè)分類的概率P(category)——先驗(yàn)概率
再求出一組待分類數(shù)據(jù)的關(guān)鍵詞在各個(gè)類別中的概率P(keywords\category)——調(diào)整因子
最后先驗(yàn)概率 * 調(diào)整因子得出后驗(yàn)概率瞧壮,再經(jīng)過比較登馒,后驗(yàn)概率最大的,便是待分類數(shù)據(jù)最可能的類別咆槽。
我們的準(zhǔn)備很充足了陈轿,但在寫分類器之前,還是先將下面要用到的數(shù)據(jù)提前提取出來秦忿。
2.2.1.確定訓(xùn)練數(shù)據(jù)有多少類別
2.2.2.訓(xùn)練數(shù)據(jù)的關(guān)鍵詞集合麦射,為了方便后續(xù)計(jì)算,將其轉(zhuǎn)為id:word的格式存為id_word.txt

05.png

2.2.3.計(jì)算出每個(gè)關(guān)鍵詞在每個(gè)不同類別出現(xiàn)的概率灯谣,將其轉(zhuǎn)為category=id:pro(id)的格式存為tf_id_word.txt
06.png

2.2.4.求出樣本空間中每個(gè)分類的概率P(category)——先驗(yàn)概率潜秋,將其轉(zhuǎn)為category:pro(category)的格式存為type_pro.txt
07.png

3.編碼

經(jīng)過前面的步驟,現(xiàn)在編寫代碼實(shí)在簡單胎许,不過有兩點(diǎn)要注意峻呛。
其一是關(guān)鍵詞并不是在每個(gè)類別都會出現(xiàn)罗售,這樣會導(dǎo)致P(keyword\category) = 0,進(jìn)而導(dǎo)致整個(gè)后驗(yàn)概率為0钩述,為了解決這個(gè)問題寨躁,可以引入拉普拉斯平滑,這樣便確保不會出現(xiàn)為0的情況牙勘,具體代碼中有介紹职恳。
其二是若每個(gè)調(diào)整因子的數(shù)值都很小,大家都知道很小的值相乘谜悟,會導(dǎo)致結(jié)果變得更小话肖,這樣子表現(xiàn)出各個(gè)分類結(jié)果的后驗(yàn)概率便會出現(xiàn)問題,這個(gè)問題便是下溢出問題葡幸,解決辦法便是將其轉(zhuǎn)化為對數(shù)最筒,對數(shù)相乘便是對數(shù)相加,這樣便很巧妙的解決了這個(gè)問題蔚叨。
好了床蜘,直接上代碼,讓我們看看分類的結(jié)果吧蔑水。

# -*-coding:utf-8-*-
__author__ = 'howie'

import jieba.analyse
import pandas as pd
import math


class Pridict(object):
    """
    利用txt/中數(shù)據(jù)進(jìn)行樸素貝葉斯算法訓(xùn)練
    """

    def __init__(self):
        # 初始化結(jié)果字典
        self.resultData = {}

    def getKeyword(self, path):
        """
        通過結(jié)巴分詞進(jìn)行關(guān)鍵詞提取
        :param path: 待分詞文件路徑 相關(guān)參數(shù)可更改 也可不調(diào)用直接寫
        :return: keywordDic = {index:['word1','word2'...]}
        """
        keywordDic = {}
        df = pd.read_csv(path)
        for des in df.values:
            jieba.analyse.set_idf_path('txt/type_dict.txt')
            feature = jieba.analyse.extract_tags(des[15], 8)
            keywordDic[des[16]] = feature
        return keywordDic

    def idDic(self, keywordDic):
        """
        將每條待分類關(guān)鍵詞替換成對應(yīng)的單詞向量
        :param keywordDic:格式 keywordDic = {index:['word1','word2'...]}
        :return: keywordDic = {'1': ['12198', '16311', '6253', '8302']}
        """
        with open('txt/id_word.txt', 'r', encoding='utf8') as of:
            lines = of.read().split('\n')
        for key, values in keywordDic.items():
            id_word = []
            for eachValue in values:
                id_word += [eachLine.split(':')[0] for eachLine in lines if eachValue == eachLine.split(':')[-1]]
            keywordDic[key] = id_word
        return keywordDic

    def calPro(self, keywordDic):
        """
        計(jì)算每一組待分類數(shù)據(jù)關(guān)鍵詞對應(yīng)概率總和
        :param keywordDic:格式 keywordDic = {'1': ['12198', '16311', '6253', '8302']}
        :return: result = {'1': {'type1': -33.23204707236557, 'type3': -31.376043125934267, 'type3': -27.385192803356617...}
        """
        result = {}
        # print(keywordDic)
        # 獲取每個(gè)類別關(guān)鍵詞出現(xiàn)概率
        with open('txt/tf_id_word.txt', 'r', encoding='utf8') as of:
            lines = of.read().split('\n')[0:-1]
        # 循環(huán)待分類數(shù)據(jù)
        for key, values in keywordDic.items():
            result[key] = {}
            # 讀取分類概率文件中讀取每行數(shù)據(jù)
            for eachLine in lines:
                valLen = len(values)
                valPro = list(map(lambda x: '', [x for x in range(0, valLen)]))
                laplace = ''
                # 分類名稱 該分類下關(guān)鍵詞概率
                lineData = eachLine.split("=")
                # 該分類下每個(gè)關(guān)鍵詞概率
                eachLineData = lineData[-1].split("#")
                # 循環(huán)每個(gè)待分類字典的關(guān)鍵字向量
                for index,eachValue in enumerate(values):
                    # 每個(gè)關(guān)鍵詞對應(yīng)的詞向量以及詞概率 得到所有關(guān)鍵詞該分類下的概率列表
                    resPro = [eachPro.split(':')[1] for eachPro in eachLineData if eachValue == eachPro.split(':')[0]]
                    # 防止待分類關(guān)鍵詞概率為0邢锯,添加拉普拉斯平滑
                    laplace = str(1 / (valLen + len(eachLineData)))
                    valPro[index] = (resPro[0] if resPro else laplace)
                valPrlLen = int(valLen / 2)
                # 處理關(guān)鍵詞為空 增加限制條件,排除小概率分類影響后驗(yàn)概率大小
                if valPro:
                    if valPro.count(laplace) <= valPrlLen:
                        # 防止下溢出轉(zhuǎn)化為對數(shù)相加
                        typePro = sum([math.log(float(x)) for x in valPro])
                        result[key][lineData[0]] = typePro
                else:
                    # 將該待分類數(shù)據(jù)標(biāo)記為None
                    result[key] = None
        return result

    def resPro(self, result):
        """
        為各個(gè)分類乘上先驗(yàn)概率 提高分類成功率
        :param result: 格式 result = {'1': {'type1': -33.23204707236557, 'type3': -31.376043125934267, 'type3': -27.385192803356617...}
        :return: resultData = {'1': {'type': -28.135469210164924}}
        """
        for eachKey, eachVal in result.items():
            # 初始化每個(gè)待分類數(shù)據(jù)字典 儲存最可能分類的概率
            self.resultData[eachKey] = {}
            # 初始化最終分類結(jié)果概率
            allPro = {}
            if eachVal:
                # print(eachVal)
                with open('txt/type_pro.txt', 'r', encoding='utf8') as of:
                    lines = of.read().split('\n')
                for line in lines:
                    lineData = line.split(':')
                    # 乘上該分類概率 即先驗(yàn)概率
                    allPro = dict(allPro,**{key:(value + math.log(float(lineData[1]))) for key, value in eachVal.items() if key == lineData[0]})
                    # 返回各個(gè)分類對應(yīng)后驗(yàn)概率最大值
                    largest = max(zip(allPro.values(),allPro.keys()))
                    self.resultData[eachKey][largest[1]] = largest[0]
            else:
                # 無法分類
                self.resultData[eachKey] = {'failed': None}
        return self.resultData


def mainPri(keywordDic):
    pri = Pridict()
    dic = pri.idDic(keywordDic)
    result = pri.calPro(dic)
    resultData = pri.resPro(result)
    return resultData

初步寫好分類器搀别,其實(shí)這才是任務(wù)的開始丹擎,分類器的最重要的是第二部分的數(shù)據(jù)提取,接下來需要通過不斷地訓(xùn)練歇父,讓數(shù)據(jù)變得更加優(yōu)雅美麗蒂培,從而讓分類器的結(jié)果趨于完美,本人苦逼地調(diào)了一個(gè)星期榜苫,現(xiàn)在也就勉強(qiáng)能用护戳。
讓我們看看分類器的分類效果:


08.png

可以看到分類器給出的參考分類很有代表性,這一段就此結(jié)束垂睬,若有錯(cuò)誤媳荒,敬請指出,謝謝驹饺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钳枕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逻淌,更是在濱河造成了極大的恐慌么伯,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卡儒,死亡現(xiàn)場離奇詭異田柔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)骨望,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門硬爆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人擎鸠,你說我怎么就攤上這事缀磕。” “怎么了劣光?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵袜蚕,是天一觀的道長。 經(jīng)常有香客問我绢涡,道長牲剃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任雄可,我火速辦了婚禮凿傅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘数苫。我一直安慰自己聪舒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布虐急。 她就那樣靜靜地躺著箱残,像睡著了一般。 火紅的嫁衣襯著肌膚如雪止吁。 梳的紋絲不亂的頭發(fā)上被辑,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音赏殃,去河邊找鬼敷待。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仁热,可吹牛的內(nèi)容都是我干的榜揖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抗蠢,長吁一口氣:“原來是場噩夢啊……” “哼举哟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起迅矛,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤妨猩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后秽褒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壶硅,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡威兜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庐椒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椒舵。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖约谈,靈堂內(nèi)的尸體忽然破棺而出笔宿,到底是詐尸還是另有隱情,我是刑警寧澤棱诱,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布泼橘,位于F島的核電站,受9級特大地震影響迈勋,放射性物質(zhì)發(fā)生泄漏炬灭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一粪躬、第九天 我趴在偏房一處隱蔽的房頂上張望担败。 院中可真熱鬧,春花似錦镰官、人聲如沸提前。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狈网。三九已至,卻和暖如春笨腥,著一層夾襖步出監(jiān)牢的瞬間拓哺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工脖母, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留士鸥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓谆级,卻偏偏與公主長得像烤礁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子肥照,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容