在(二)中,我已经能获取每个词条在百度搜索下的结果。我将所有的可疑词条的百度搜索结果保存为文件。每一行文本是一个词条的搜索结果。例如,我有728个可以词条,那么我将保存728行文本,现在我们要提取出每一行文本中的特征词。

接下来的操作依次是:

标记正负样本并下载它们的全部文本

对文本进行分词

通过Tf-Idf、词频、CHI、或是信息增益等方法取出特征词

将特征词向量化

获取正负样本

我使用手工的方法,在近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。

我的做法是:

  1. 先将正负样本词库整合,并计算每个词出现的次数t,假设出现次数最大的词出现次数为max,每个词的权重即为t/max,导师建议使用每个维度的最大次数而非全局,之后可以试试看。
  2. 构造正负样本矩阵并保存
  3. 构造测试样本的矩阵,矩阵的权重来源于训练样本。

代码如下:

合并词库:

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的格式):

文本信息提取 java_文本特征