转自:https://fancoo.wordpress.com/2016/03/09/三-文本特征提取和向量化/
在(二)中,我已经能获取每个词条在百度搜索下的结果。我将所有的可疑词条的百度搜索结果保存为文件。每一行文本是一个词条的搜索结果。例如,我有728个可以词条,那么我将保存728行文本,现在我们要提取出每一行文本中的特征词。
接下来的操作依次是:
获取正负样本
我使用手工的方法,在近3000个样本分离出了150个负样本,也就是诸如’asdf’之类看上去很不正常的名称,正样本就选取带’有限公司’的公司名,并使用(二)中的方法,将正负样本的的网页搜索结果解析出来的 文本保存下来。
分词
中文分词我使用的是jieba,jieba有多种切词模式,还支持自定义停用词和idf词库,如果你使用的是jieba.analyse这个库,它支持tfidf和textRank两种特征,在我的使用过程中有一个很不爽的地方是,如果你仅仅使用jieba.cut,那么在原文本中包含的多种特殊符号将令人头疼(当然你也可以用正则消除掉),而一旦你使用jieba.analyse,你会发下它已经自动过滤了这些词,可是它已经使用了tf-idf或是textRank,一句话:如果你仅仅想用它来分词,提取特征的事情后续由自己来做,那么jieba.analyse会令人讨厌的先给你的词预筛选一遍。这里我topK设置为前10000个词,基本上不会筛掉词了。
分词代码段:
def extract_tag(text_file, tag_file):
with open(text_file, 'r') as tf:
for text in tf:
utf8_text = text.decode('utf-8', 'ignore')
#print utf8_text
seg_list = jieba.cut(utf8_text, cut_all=True)
clean_text = " ".join(seg_list)
jieba.analyse.set_stop_words("extra/stop_words.dict")
print '\n'
tags = jieba.analyse.textrank(clean_text, topK=10000, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))
print(" ".join(tags))
mstr = " ".join(tags)
mlist = mstr.split()
mlist = list(set(mlist))
with open(tag_file, 'a') as f:
for item in mlist:
utf8_item = item.encode('utf-8', 'ignore')
f.write(utf8_item+'\t')
f.write('\n')
f.close()
tf.close()
这里我得到一个tag_file,每一行有若干个词,代表这一个文本的特征集合,下面要做的事就是提取特征。
特征提取
我没有使用Tf-Idf,是因为它存在的一个严重缺陷是:假如一个词仅仅在某个类中出现过多次,那么其实该词是该类的一个非常好的特征,但根据Tf-Idf的机制,该词由于出现过太多次反而权重不高,在本次中,我目前使用的仅仅是词频,未来可能尝试其他方法。
向量化
所谓向量化,即对存在的词,每个词赋予其一个维度,然后对正负样本的每一行特征,按照其是否在特征向量中的某一维,决定该行特征是0还是非0。
我的做法是:
代码如下:
合并词库:
def merge_dict(pos_tag, neg_tag):
"""
Parameter
-------
pos_tag: file of pos tag
neg_tag: file of neg tags
Return
-----
mixed tags list
"""
words = []
with open(pos_tag, 'r') as pt:
for line in pt:
word = line.split()
for t in word:
words.append(t)
with open(neg_tag, 'r') as nt:
for line in nt:
word = line.split()
for t in word:
words.append(t)
tag_list = list(set(words))
print tag_list
return tag_list
构造正负样本矩阵:
def get_libsvm_data(pos_tag, neg_tag, word_list, matrix_file):
fmatrix = open(matrix_file, 'a+')
with open(pos_tag, 'r') as pt:
for line in pt:
tag = []
word_flag = []
word = line.split()
if len(word) == 0:
continue
word = list(set(word))
tag.append('+1')
for t in word:
p = word_list.index(t)
word_flag.append(p)
word_flag.sort()
for t in word_flag:
tag.append(' '+str(t)+':'+'1')
mstr = ''.join(tag)
fmatrix.write(mstr)
fmatrix.write('\n')
with open(neg_tag, 'r') as nt:
for line in nt:
tag = []
word_flag = []
word = line.split()
if len(word) == 0:
continue
word = list(set(word))
tag.append('-1')
for t in word:
p = word_list.index(t)
word_flag.append(p)
word_flag.sort()
for t in word_flag:
tag.append(' '+str(t)+':'+'1')
mstr = ''.join(tag)
fmatrix.write(mstr)
fmatrix.write('\n')
构造测试样本矩阵:
def shape_testdata(pos_test_tag, neg_test_tag, word_list, matrix_file):
fmatrix = open(matrix_file, 'a+')
with open(pos_test_tag, 'r') as pt:
for line in pt:
tag = []
last_flag = 0 # if the last has value
word_flag = []
word = line.split()
if len(word) == 0:
continue
word = list(set(word))
tag.append('+1')
for t in word:
if t in word_list:
p = word_list.index(t)
if p == len(word_list):
last_flag = 1
word_flag.append(p)
word_flag.sort()
if len(word_flag) != 0:
for t in word_flag:
tag.append(' '+str(t)+':'+'1')
if last_flag == 0:
tag.append(' '+str(len(word_list))+':'+'0')
mstr = ''.join(tag)
fmatrix.write(mstr)
fmatrix.write('\n')
with open(neg_test_tag, 'r') as nt:
for line in nt:
tag = []
word_flag = []
last_flag = 0
word = line.split()
if len(word) == 0:
continue
word = list(set(word))
tag.append('-1')
for t in word:
if t in word_list:
p = word_list.index(t)
if p==len(word_list):
last_flag = 1
word_flag.append(p)
word_flag.sort()
if len(word_flag) != 0:
for t in word_flag:
tag.append(' '+str(t)+':'+'1')
if last_flag == 0:
tag.append(' '+str(len(word_list))+':'+'0')
mstr = ''.join(tag)
fmatrix.write(mstr)
fmatrix.write('\n')
于是我们成功得到了训练样本和测试样本的矩阵,中间出现过一个bug是:训练样本和测试样本矩阵维度不同,这是由于记录时自动去除了值为0的维度,因此假如在测试样本中,未出现训练样本的末尾的维度,测试样本的维度将小于训练样本。改进是判断最后一维是否在测试样本中出现。未出现则注’0′
以下为矩阵格式(其实是libsvm的格式):