TF-IDF、词袋模型与特征工程

如标题,TF-IDF与词集词袋模型都是数据预处理中常用的算法,这里展示一下这两种算法的联合应用。

一. 词集与词袋模型

这个算法的主要作用也就是对文本做单词切分,有点从一篇文章里提取关键词这种意思,旨在用向量来描述文本的主要内容,其中包含了词集与词袋两种。

词集模型:单词构成的集合,集合中每个元素只有一个,即词集中的每个单词都只有一个。

词袋模型:如果一个单词在文档中出现不止一次,就统计其出现的次数,词袋在词集的基础上加入了频率这个维度,使统计拥有更好的效果,通常我们在应用中都选用词袋模型。

python代码示例

使用xss攻击语句来测试词袋模型的效果

from sklearn.feature_extraction.text import CountVectorizer
#词袋模型,这里的min_df取值为3,即该向量在整个payload中至少出现了三次
vec=CountVectorizer(min_df=3,ngram_range=(1,1))
content=[
    '<s[NULL]cript>alert(1)</s[NULL]cript>X</a>',
    '\'><video><source o?UTF-8?Q?n?error="alert(1)">',
    '\'><FRAMESET><FRAME RC=""+"javascript:alert(\'X\');"></FRAMESET>',
    '"></script>\'//<svg "%0Aonload=alert(1) //>',
    '"></script><img \'//"%0Aonerror=alert(1)// src>',
    'id%3Den%22%3E%3Cscript%3Ealert%28%22AKINCILAR%22%29%3C/script%3E',
    '?a%5B%5D%3D%22%3E%3Cscript%3Ealert%28document.cookie%29%3C/script%3E',
    '><iframe src="data:data:javascript:,% 3 c script % 3 e confirm(1) % 3 c/script %3 e">',
    '?mess%3D%3Cscript%3Ealert%28document.cookie%29%3C/script%3E%26back%3Dsettings1',
    'title%3D%3Cscript%3Ealert%28%22The%20Best%20XSSer%22%29%3C/script%3E',
    '<script charset>alert(1);</script charset>',
    '"><meta charset="x-mac-farsi">??script ??alert(1)//??/script ??',
    '</script><script>/*"/*\'/**/;alert(1)//</script>#',
]

X=vec.fit_transform(content)
print vec.get_feature_names()

Output:
[u'22', u'29', u'3c', u'3cscript', u'3d', u'3e', u'3ealert', u'alert', u'script']

可以看到我们的词袋模型成功提取出了部分特征,这个特征在专业的安全人员眼里看来或许不够好,但完全可以作为参考特征之一,相较于人,使用词袋模型可能能挖掘到专业人员意想不到但实际效果较好的特征向量。

二. TF-IDF算法

TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术。TF意思是词频(Term Frequency),IDF意思是逆向文件频率(Inverse Document Frequency),因此TF-IDF其实就是TF*IDF。

说句人话来解释下这两个名词的意思,举个例子,下面有这么几句话:

1.“我今天跑你家去偷吃了你家的大米,但是被你家狗咬了,你要赔我钱”

2.“我想下午找你玩,但是天气预报说下午要下雨,所以你还是自己玩泥巴去吧”

3.“从见到你老婆的第一天起,你这个兄弟我就交定了”

假设以上三句话我们分别写在三张纸上,那么这个词频,也就是TF,代表的就是某个词在它所处的那张纸上的出现频率,比如“你”这个词,在第一张纸上出现的频率。

而IDF则代表这个词和所有文档整体的相关性,如果某个词在某一类别出现的多,在其他类别出现的少,那IDF的值就会比较大。如果这个词在所有类别中都出现的多,那么IDF值则会随着类别的增加而下降,比如例中的“你”,它的TF值可能很高,但由于其在三个文本中均有出现,所以IDF值就会比较低。IDF反映的是一个词能将当前文本与其它文本区分开的能力。

在python中集成了该算法,可以很方便的使用,大家可以自己去搜索一下。

TF-IDF的缺陷

由于IDF值的公式,使其存在一些天然的缺陷:

  • 没有考虑特征词的位置因素对文本的区分度,词条出现在文档的不同位置时,对区分度的贡献大小是不一样的。
  • 按照传统TF-IDF函数标准,往往一些生僻词的IDF(反文档频率)会比较高、因此这些生僻词常会被误认为是文档关键词。(换句话说,如果一个特征项只在某一个类别中的个别文本中大量出现,在类内的其他大部分文本中出现的很少,那么不排除这些个别文本是这个类中的特例情况,因此这样的特征项不具有代表性。)
  • TF-IDF没有考虑到特征项在类间和类内的分布情况,比如某个特征项在某类文档中大量分布,而在其它文档中少量分布,那么该特征项其实能很好的作为区分特征,但根据TF-IDF的公式,该特征就会受到抑制。

三. 联合使用

上述文中专门说到了tf-idf的缺陷,为什么要提到其缺陷呢?因为我们后续的特征优化恰好可以利用到其特点。

tf-idf的主要作用就是找出某个词或某些词用以区别于其它文本,而词袋模型恰好又是找出文本中出现频率高的词语,那我们可以试想:

如果我先用词袋模型筛选出一些高热度词汇,再用tf-idf计算其权值,那得到的结果是什么呢?我们还是以之前的python代码模稍作修改看看测试结果:

#!/usr/bin/env python
# encoding: utf-8
#@author: stardustsky
#@file: validate.py
#@time: 2017/9/8 10:36
#@desc:

import numpy as np
from sklearn import preprocessing
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

#词袋模型
vec=CountVectorizer(min_df=3,ngram_range=(1,1))
content=[
    '<s[NULL]cript>alert(1)</s[NULL]cript>X</a>',
    '\'><video><source o?UTF-8?Q?n?error="alert(1)">',
    '\'><FRAMESET><FRAME RC=""+"javascript:alert(\'X\');"></FRAMESET>',
    '"></script>\'//<svg "%0Aonload=alert(1) //>',
    '"></script><img \'//"%0Aonerror=alert(1)// src>',
    'id%3Den%22%3E%3Cscript%3Ealert%28%22AKINCILAR%22%29%3C/script%3E',
    '?a%5B%5D%3D%22%3E%3Cscript%3Ealert%28document.cookie%29%3C/script%3E',
    '><iframe src="data:data:javascript:,% 3 c script % 3 e confirm(1) % 3 c/script %3 e">',
    '?mess%3D%3Cscript%3Ealert%28document.cookie%29%3C/script%3E%26back%3Dsettings1',
    'title%3D%3Cscript%3Ealert%28%22The%20Best%20XSSer%22%29%3C/script%3E',
    '<script charset>alert(1);</script charset>',
    '"><meta charset="x-mac-farsi">??script ??alert(1)//??/script ??',
    '</script><script>/*"/*\'/**/;alert(1)//</script>#',
]

trans=TfidfTransformer()
tfidf=trans.fit_transform(vec.fit_transform(content))
print vec.get_feature_names()
print tfidf.toarray()

Output:
[u'22', u'29', u'3c', u'3cscript', u'3d', u'3e', u'3ealert', u'alert', u'script']
[[ 0.          0.          0.          0.          0.          0.          0.
   1.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.
   1.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.
   1.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.75787695  0.65239752]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.75787695  0.65239752]
 [ 0.60865989  0.27418507  0.27418507  0.27418507  0.          0.54837013
   0.27418507  0.          0.16767089]
 [ 0.33715382  0.30375763  0.30375763  0.30375763  0.33715382  0.60751526
   0.30375763  0.          0.18575524]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          1.        ]
 [ 0.          0.38907452  0.38907452  0.38907452  0.43185075  0.38907452
   0.38907452  0.          0.23792861]
 [ 0.39646122  0.35719043  0.35719043  0.35719043  0.39646122  0.35719043
   0.35719043  0.          0.21843071]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.50226141  0.86471583]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.50226141  0.86471583]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.36109936  0.93252735]]

可以看到,我们得到了词袋模型中词汇的tf-idf值,值越高说明该词区分每条语句的效果越好。

但我们做特征工程追求的是泛化能力,即寻找能更好的概括整体文本的特征的词汇,与tf-idf追求的结果恰恰相反,所以我们可以看到像alert、script这种在安全从业者看来明显的攻击特征在上面结果中的权值反而很低。

我们再回过头来看看tf-idf的缺陷,其中的第二点和第三点以相反角度来看都有助于我们对词袋模型中特征向量的优化(这个需要各位好好理解一下)。

那么我们正好可以利用这个特征来判断词袋模型中向量的泛化效果
即:tf-idf值越高其泛化能力越低,也就越不适合作为我们的特征向量。

从上面的结果中我们可以看出来,script、alert这两个向量相比于其它能更好的反映出我们整体攻击语句的特征,符合我们人工判断的结果。而在script和alert两者中alert显然泛化效果又更加的优秀。

两者结合使用,我们就可以自动化的从大文本中提取优质的特征向量,以减少人工干预,大大降低特征工程中的成本。

    原文作者:Stardustsky
    原文地址: https://segmentfault.com/a/1190000011480420
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞