背景:为从海量公司招聘信息中提取有用信息,为精准推荐做准备,需要对发布招聘信息的各公司所属的行业进行自动分类,使得给定公司文本基本信息,实现所属行业的自动分类预测。
方法:利用爬虫爬取各行业下公司的名称,以公司名称为索引,检索包含各公司基本信息的数据库文件,提取各公司文本数据信息,生成原始训练数据集。经分词处理后,利用卡方检验进行特征的选择,训练Naive Bayes模型进行文本分类。
总述:针对128个行业,其中一级行业16个,共需分类模型17个,其中1个一级行业分类模型,16个二级行业分类模型。
1.训练数据的提取与处理
为学习17个训练模型,需生成17个训练数据集,因此,需要分别提取128个行业下各公司的文本信息,然后将其分别根据公司所属行业标签,归到相应的数据集中。通过公司名称检索数据库,对公司文本数据进行提取,该数据库中包含千万级的公司文本信息,因此,数据提取的第一步是利用爬虫爬取各行业下包含的公司名称,以公司名称为索引,检索数据库文件,得到该行业下的文本数据集。
利用urllib2进行网页的爬取,BeautifulSoup进行网页的解析,按1-128对各行业进行编号,并将爬取和解析到的各行业包含的公司名称保存为*.txt,其中*表示1-128的数字,每个公司占一行,如图1是银行行业下的公司列表。
图1 银行行业下的公司列表
关键代码如下:
req = urllib2.Request(//爬取
url='http://www.kanzhun.com/plc'+str(lable_num)+'p'+str(i)+'.html?ka=paging'+str(i),
headers = headers
)
m = urllib2.urlopen(req).read()
爬取和解析过程中出现的问题及解决方案:
(1) 爬取过程出现拒绝请求,IP被封的问题。原因在于网站根据流量和日志分析具有反爬虫机制,一旦被识别为爬虫,就会拒绝该IP的访问。一种解决方法是伪装成浏览器访问,每爬取一个网页,休眠一段时间(50s左右).还有一种方法是使用代理服务器,通过多IP轮询的方式访问,较高效。
(2) 爬取的网页不能解析的问题。原因不明,解决方法是不对整个网页进行解析,而是读取包含目标信息的部分,存到新的文件中,对新文件进行解析,解析成功。
(3) 解析不准确。解决方法是利用正则表达式(import re;)解析,关键代码如下:
soup.findAll(name='a',attrs={'ka':re.compile("title$"),'href':True,'target':True,'class':None})
获取各行业标签数据后,将该行业下的所有公司名称存入list表中,按行读取公司数据库文件,查找该行的第一项(公司名称)是否在list列表中,若在则将该行写入新文件中,直至读取完成,则实现一个行业的所有公司文本数据的抽取。同样的方法抽取其他行业,存入新文件中,命名为*d.txt,其中*表示1-128的数字,即得到各行业的训练集。
数据抽取过程存在的问题:
(1) 数据库中包含缺失样本重复样本,如果重复抽取的话,可能引起样本均衡问题,影响模型的准确率。解决方法是比较各重复样本的长度,优先选择长样本。
(2) 数据库中包含缺失样本,缺少对分类有用信息,解决方法是抽取之前检测样本长度,若太短则作丢弃处理。
将各二级行业训练集归到其所属的一级行业下,归并为新的文件,即得到一级行业训练集,为一级行业的分类做准备,一级行业具体分如下16个类别:
Com=['Ad_media','consumer_goods','energy_environmental','IT','Machine_manufacturing','buliding','education','farm_forest_animal_fish_other',
'medical','transportation_logistics','car','electrocommunication','finance','life_service','professional_services','unprofitable_firm']
生成训练集后,对各训练集作分词处理,进行特征抽取。
Shell命令:
./segmentor.sh sogou_seg_dict_cleaned.txt input.txt output.txt true
2.卡方特征选择
卡方检验的原理
开方检验最基本的思想就是通过观察实际值与理论值的偏差来确定理论的正确与否。具体做的时候常常先假设两个变量确实是独立的(原假设),然后观察实际值(也可以叫做观察值)与理论值(这个理论值是指“如果两者确实独立”的情况下应该有的值)的偏差程度,如果偏差足够小,我们就认为误差是很自然的样本误差,是测量手段不够精确导致或者偶然发生的,两者确确实实是独立的,此时就接受原假设;如果偏差大到一定程度,使得这样的误差不太可能是偶然产生或者测量不精确所致,我们就认为两者实际上是相关的,即否定原假设,而接受备择假设。
在文本分类问题的特征选择阶段,我们主要关心一个词t(一个随机变量)与一个类别c(另一个随机变量)之间是否相互独立?如果独立,就可以说词t对类别c完全没有表征作用,即我们根本无法根据t出现与否来判断一篇文档是否属于c这个分类。但与最普通的开方检验不同,我们不需要设定阈值,因为很难说词t和类别c关联到什么程度才算是有表征作用,我们只想借用这个方法来选出一些最最相关的即可。一般都使用”词t与类别c不相关“来做原假设。选择的过程也变成了为每个词计算它与类别c的开方值,从大到小排个序(此时开方值越大越相关),取前k个就可以,如图2所示,是IT行业经排序后的卡方。
图2 IT行业经排序后的卡方
卡方检验存在的问题:
卡方检验仅统计文档中是否出现词t,却不管t在该文档中出现了几次,这会使得他对低频词有所偏袒(因为它夸大了低频词的作用)。甚至会出现有些情况,一个词在一类文章的每篇文档中都只出现了一次,其开方值却大过了在该类文章99%的文档中出现了10次的词,其实后面的词才是更具代表性的,但只因为它出现的文档数比前面的词少了“1”,特征选择的时候就可能筛掉后面的词而保留了前者。这就是开方检验著名的‘低频词缺陷’,但有文献针对英文纯文本的实验结果表明:作为特征选择方法时,开方检验和信息增益的效果最佳。
Shell命令:python ChiSquare.py ./datalist ./all.stop ./path
参数1是待分词文本数据列表,参数2是停用词,参数3是分词结果存放的路径
卡在选择过程遇到的问题:
(1) 对卡方值排序时,结果不准确。原因是卡方值都是浮点型的,如不加float不能正确排序,如下:list=sorted(D.iteritems(), key=lambda d:float(d[1]), reverse = True )
(2) 对卡方排序后,取前5000特征作为最终的选择,用于模型训练。
3.基于Naive Bayes的文本分类算法

图3基于Naive Bayes的文本分类算法
Naive Bayes的文本分类算法如图3所示,A即公司的描述文本,B即行业类别,对于一级行业分类模型,B有16个取值,若p(Bi|A)最大,i=1-16,则认为文本A属于行业Bi。为实现行业的自动分类,分别需利用各自训练集训练17个模型,关键代码如图4所示。

图4先验概率和条件概率的关键代码
训练模型shell命令:
python NaiveBayes.py train ./datalist.fst_ind ./fst_ind_Chi/fst_Chi_sorted model_fst_ind
参数1:train;参数2:训练集数据列表;参数3:经卡方后选择的特征;参数4:保存的模型文件。
模型预测shell命令:
python NaiveBayes.py predict ./companydata2/IT model_fst_ind result_fst_ind
参数1:predict;参数2:测试集数据;参数3:训练得到的模型文件;参数4:保存的预测结果文件。
4.测试与评估
对训练模型分别在训练集和测试集上进行了测试,一级行业分类模型在训练集上的准确率为97.6940133038%,二级行业的分类模型也均在97以上,在测试集上的准确率在80%-90%左右,模型评估的关键代码如图5所示。

图5 模型评估的关键代码
测试的构建方法:从源数据库随机抽取50000样本,利用一级行业分类模型对样本进行一级行业分类,根据分类结果,从50000样本中分别抽取各行业的样本,分别构建了各一级行业下二级行业分类的测试集。
对错分类的分析:对错分类项进行了分析:
(1) 存在文本无效问题,即文本中的信息是无意义的,对分类效果没有帮助,甚至是缺失的,这是错分类的主要原因。
(2) 标签不易区分的,如生活服务和专业服务,有些看起来倾向于分到专业服务的,可能会分到生活服务中。但是这种情况,专业服务的概率也比较高,大多情况仅次于生活服务。
(3) 文本中有混淆性表述的,如一家为物流公司提供IT服务的公司,模型会将该公司分到物流行业下。
(4)不同行业的特征具有相关性,如通信与运营商,大多通信行业下的公司文本中均有大量运营商的信息,因此,很容易将通信错分到运营商下。
对于随机抽样的文本依次对其进行两级分类,准确率在80%左右,去除抽样文本中的文本缺失的样本,分类效果有所提升,但还需进一步的优化。
附源代码:https://github.com/menyusen/Text_Mining_project
浙公网安备 33010602011771号