词集与词袋模型
这个算法的主要作用也就是对文本做单词切分,有点从一篇文章里提取关键词这种意思,旨在用向量来描述文本的主要内容,其中包含了词集与词袋两种。
词集模型 DictVectorizer:单词构成的集合,集合中每个元素只有一个,即词集中的每个单词都只有一个。
词袋模型 CountVectorizer:在词集的基础上加入了频率这个维度,即统计单词在文档中出现的次数(令牌化和出现频数统计),通常我们在应用中都选用词袋模型。
以词袋模型为例1
from sklearn.feature_extraction.text import CountVectorizer
#使用默认参数实例化分词对象
vec=CountVectorizer()
#查看词袋模型的参数
vec.get_params()
输出:
{'analyzer': 'word',
'binary': False,
'decode_error': 'strict',
'dtype': numpy.int64,
'encoding': 'utf-8',
'input': 'content',
'lowercase': True,
'max_df': 1.0,
'max_features': None,
'min_df': 1,
'ngram_range': (1, 1),
'preprocessor': None,
'stop_words': None,
'strip_accents': None,
'token_pattern': '(?u)\\b\\w\\w+\\b',
'tokenizer': None,
'vocabulary': None}
##让我们使用它对文本corpus进行简单文本全集令牌化,并统计词频:
corpus = [
... 'This is the first document.',
... 'This is the second second document.',
... 'And the third one.',
... 'Is this the first document?',
... ]
X = vec.fit_transform(corpus)
#在拟合期间发现的每个项都被分配一个与所得矩阵中的列对应的唯一整数索引
X
输出:
<4x9 sparse matrix of type '<class 'numpy.int64'>'
with 19 stored elements in Compressed Sparse Row format>
#获取特征名称(列名)
vec.get_feature_names()
输出:
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
X.toarray()
输出:
array([[0, 1, 1, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 0, 2, 1, 0, 1],
[1, 0, 0, 0, 1, 0, 1, 1, 0],
[0, 1, 1, 1, 0, 0, 1, 0, 1]], dtype=int64)
#获取词汇表(训练语料库),词汇表由特征名和出现频次构成
vec.vocabulary_
输出:
{'and': 0,
'document': 1,
'first': 2,
'is': 3,
'one': 4,
'second': 5,
'the': 6,
'third': 7,
'this': 8}
针对其他文本进行词袋处理时, 可以直接使用现有的词汇表
voc=vec.vocabulary_
new_vec=CountVectorizer(vocabulary=voc)
在将来的调用转换方法中,在训练语料库vocabulary_中未出现的词将被完全忽略:
vec.transform(['Something third new.']).toarray()
输出:
array([[0, 0, 0, 0, 0, 0, 0, 1, 0]])
以词袋模型为例2
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=vec2.fit_transform(content)
vec2.get_feature_names()
输出:
['22', '29', '3c', '3cscript', '3d', '3e', '3ealert', 'alert', 'script']
In [124]:
vec2.vocabulary_
输出:
{'22': 0,
'29': 1,
'3c': 2,
'3cscript': 3,
'3d': 4,
'3e': 5,
'3ealert': 6,
'alert': 7,
'script': 8}
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反映的是一个词能将当前文本与其它文本区分开的能力。
TF-IDF的缺陷
由于IDF值的公式,使其存在一些天然的缺陷:
- 没有考虑特征词的位置因素对文本的区分度,词条出现在文档的不同位置时,对区分度的贡献大小是不一样的。
- 按照传统TF-IDF函数标准,往往一些生僻词的IDF(反文档频率)会比较高、因此这些生僻词常会被误认为是文档关键词。(换句话说,如果一个特征项只在某一个类别中的个别文本中大量出现,在类内的其他大部分文本中出现的很少,那么不排除这些个别文本是这个类中的特例情况,因此这样的特征项不具有代表性。)
- TF-IDF没有考虑到特征项在类间和类内的分布情况,比如某个特征项在某类文档中大量分布,而在其它文档中少量分布,那么该特征项其实能很好的作为区分特征,但根据TF-IDF的公式,该特征就会受到抑制。
联合使用
tf-idf的主要作用就是找出某个词或某些词用以区别于其它文本,而词袋模型恰好又是找出文本中出现频率高的词语,那我们可以试想:
如果我先用词袋模型筛选出一些高热度词汇,再用tf-idf计算其权值,我们将得到词袋模型中词汇的tf-idf值,值越高说明该词区分每条语句的效果越好。
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>#',
]
X=vec.fit_transform(content)
trans=TfidfTransformer()
tfidf=trans.fit_transform(X)
print (vec.get_feature_names())
print (tfidf.toarray())
输出结果:
['22', '29', '3c', '3cscript', '3d', '3e', '3ealert', 'alert', '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显然泛化效果又更加的优秀。
两者结合使用,我们就可以自动化的从大文本中提取优质的特征向量,以减少人工干预,大大降低特征工程中的成本。