python之朴素贝叶斯对展会数据分类

1.目的

在公司实习,分别从国内国外两个网站爬取了一些展会数据,在数据处理上目前需要将其按照各个类别分类好,并提供对应展会地址的经纬度。
国内数据如下:

01.png

国内数据比较少,占四百多条,在类别上来看有所属行业这一列,所以比较好处理。
国外数据就有些尴尬:
02.png

国外网站展会数据将近五万多条,跟分类有关的只有categories这一列数据,都是一些标签词,还偏少。
现在需要将五万多条展会数据进行分类,如何解决这个问题,我觉得可以写个朴素贝叶斯分类器

2.数据

朴素贝叶斯分类器是利用样本数据来进行训练的,每个样本必须含有一组特征词以及对应的分类。

2.1.数据的准备

获取的国内数据就很适合作为训练数据,可以将其处理成如下格式作为样本输入:

03.png

训练数据主要是这两列,所属行业以及展会范围,每一行的类别都有其对应的展会范围,将这些作为训练数据实在再好不过,我们将其称之为训练集
那么相对应,国外待分类的展会数据便是测试集
我们将利用训练好的分类器,将测试集一一输入,看看能否得到期望的输出结果。
可是现在有个问题,作为训练集的国内数据只有四百多条,实在太少,于是我只能再去那个国内展会网站将以前的展会数据尽量爬取下来,最终训练集达到了39555条,虽说数量还是不够,但是不试试最终分类器的分类结果,说不定准确率还可以呢?

2.2.分词&&提取

不积小流,无以成江海,以小见大,咱们看这样一条数据:

所属行业:建材五金
展会范围:各类卫生洁具、浴室家具和配件、面盆、马桶、淋浴房、浴缸、花洒、水龙头及配件、浴室照明、镜子、五金挂件等

思考一下,这一条数据的所属行业展会范围有什么关系,我们将从这里得到编写分类器的出发点。
在这条数据中,展会范围的内容是具有一定的代表性的,其代表这条数据的描述很偏向建材五金这个行业。
那我么是不是可以提炼出这个描述的关键词,从而让这个关键词代表建材五金这个行业。

# 可以利用结巴分词
import jieba.analyse
con = "各类卫生洁具、浴室家具和配件、面盆、马桶、淋浴房、浴缸、花洒、水龙头及配件、浴室照明、镜子、五金挂件等"
feature = jieba.analyse.extract_tags(con, 8)
print(feature)
# output:['配件', '各类', '家具', '五金', '照明', '浴室', '淋浴房', '浴缸']

那么刚刚那条数据可以这样看:

# data_01
所属行业:建材五金
描述关键词:'配件', '各类', '家具', '五金', '照明', '浴室', '淋浴房', '浴缸'

以此类推,如果一条未分类数据的关键词也是这样,那是不是可以将该数据归为建材五金这个类别,是的,你可以这么干。
但有个问题,若该未分类数据的关键词只含有以上关键词的某个,比如:

# data_02
所属行业:未知
描述关键词:'配件','家具'

这样子若分为建材五金不大对吧,我倒觉得应该分为房产家居,这个问题可以解决,就让我们的概率出场吧。
大三的时候学过概率统计,也记得一个公式名为贝叶斯定理

04.jpg

这表示计算条件概率的公式:

P(A\B) = P(B\A)*P(A)/P(B) == 后验概率 = 先验概率 x 调整因子
# 这样写会不会更加清晰
P(category\keywords) = P(category) * P(keywords\category)/ P(keywords)
## 朴素贝叶斯便是假设即将被组合的各个概率是独立的,可以理解成keyword1出现在category1的概率和keyword2出现在category1的概率是没有关系的,是独立的。
P(category\keywords) = P(category) * P(keyword1\category)P(keyword2\category)...P(keywordn\category)/ P(keywords)

总结就是,我们先求出样本空间中每个分类的概率P(category)——先验概率
再求出一组待分类数据的关键词在各个类别中的概率P(keywords\category)——调整因子
最后先验概率 * 调整因子得出后验概率,再经过比较,后验概率最大的,便是待分类数据最可能的类别。
我们的准备很充足了,但在写分类器之前,还是先将下面要用到的数据提前提取出来。
2.2.1.确定训练数据有多少类别
2.2.2.训练数据的关键词集合,为了方便后续计算,将其转为id:word的格式存为id_word.txt

05.png

2.2.3.计算出每个关键词在每个不同类别出现的概率,将其转为category=id:pro(id)的格式存为tf_id_word.txt
06.png

2.2.4.求出样本空间中每个分类的概率P(category)——先验概率,将其转为category:pro(category)的格式存为type_pro.txt
07.png

3.编码

经过前面的步骤,现在编写代码实在简单,不过有两点要注意。
其一是关键词并不是在每个类别都会出现,这样会导致P(keyword\category) = 0,进而导致整个后验概率为0,为了解决这个问题,可以引入拉普拉斯平滑,这样便确保不会出现为0的情况,具体代码中有介绍。
其二是若每个调整因子的数值都很小,大家都知道很小的值相乘,会导致结果变得更小,这样子表现出各个分类结果的后验概率便会出现问题,这个问题便是下溢出问题,解决办法便是将其转化为对数,对数相乘便是对数相加,这样便很巧妙的解决了这个问题。
好了,直接上代码,让我们看看分类的结果吧。

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

import jieba.analyse
import pandas as pd
import math


class Pridict(object):
    """
    利用txt/中数据进行朴素贝叶斯算法训练
    """

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

    def getKeyword(self, path):
        """
        通过结巴分词进行关键词提取
        :param path: 待分词文件路径 相关参数可更改 也可不调用直接写
        :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):
        """
        将每条待分类关键词替换成对应的单词向量
        :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):
        """
        计算每一组待分类数据关键词对应概率总和
        :param keywordDic:格式 keywordDic = {'1': ['12198', '16311', '6253', '8302']}
        :return: result = {'1': {'type1': -33.23204707236557, 'type3': -31.376043125934267, 'type3': -27.385192803356617...}
        """
        result = {}
        # print(keywordDic)
        # 获取每个类别关键词出现概率
        with open('txt/tf_id_word.txt', 'r', encoding='utf8') as of:
            lines = of.read().split('\n')[0:-1]
        # 循环待分类数据
        for key, values in keywordDic.items():
            result[key] = {}
            # 读取分类概率文件中读取每行数据
            for eachLine in lines:
                valLen = len(values)
                valPro = list(map(lambda x: '', [x for x in range(0, valLen)]))
                laplace = ''
                # 分类名称 该分类下关键词概率
                lineData = eachLine.split("=")
                # 该分类下每个关键词概率
                eachLineData = lineData[-1].split("#")
                # 循环每个待分类字典的关键字向量
                for index,eachValue in enumerate(values):
                    # 每个关键词对应的词向量以及词概率 得到所有关键词该分类下的概率列表
                    resPro = [eachPro.split(':')[1] for eachPro in eachLineData if eachValue == eachPro.split(':')[0]]
                    # 防止待分类关键词概率为0,添加拉普拉斯平滑
                    laplace = str(1 / (valLen + len(eachLineData)))
                    valPro[index] = (resPro[0] if resPro else laplace)
                valPrlLen = int(valLen / 2)
                # 处理关键词为空 增加限制条件,排除小概率分类影响后验概率大小
                if valPro:
                    if valPro.count(laplace) <= valPrlLen:
                        # 防止下溢出转化为对数相加
                        typePro = sum([math.log(float(x)) for x in valPro])
                        result[key][lineData[0]] = typePro
                else:
                    # 将该待分类数据标记为None
                    result[key] = None
        return result

    def resPro(self, result):
        """
        为各个分类乘上先验概率 提高分类成功率
        :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():
            # 初始化每个待分类数据字典 储存最可能分类的概率
            self.resultData[eachKey] = {}
            # 初始化最终分类结果概率
            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(':')
                    # 乘上该分类概率 即先验概率
                    allPro = dict(allPro,**{key:(value + math.log(float(lineData[1]))) for key, value in eachVal.items() if key == lineData[0]})
                    # 返回各个分类对应后验概率最大值
                    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

初步写好分类器,其实这才是任务的开始,分类器的最重要的是第二部分的数据提取,接下来需要通过不断地训练,让数据变得更加优雅美丽,从而让分类器的结果趋于完美,本人苦逼地调了一个星期,现在也就勉强能用。
让我们看看分类器的分类效果:


08.png

可以看到分类器给出的参考分类很有代表性,这一段就此结束,若有错误,敬请指出,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容