动态词典构造
-
在文本分类中,在处理流程上一般都大致会包括以下步骤:
- 文本分词(含去停用词)
- 建立测试集词列表与类别映射
- 统计词的频率,按照某种方法计算词权重
- 选择策略提取(抽取)文本特征
- 构造分类器,训练
- 模型评估
在步骤3和4中,目前有很多方法对文本进行特征表示,如TF-IDF,CHI权重计算,基于互信息,交叉熵等。这里我主要试着阐述一下动态词典来计算词权重,并构建向量来对文本进行特征表示。
在动态词典中,文本表示的词并不是像词袋模型那样----根据构建的词表[wd1,wd2,wd3,......],计算全文档集中的词的权重,最后对每一个训练文档进行词对应的权重赋值,构建一个高维的稀疏矩阵[0,0,0.342,0,0,0.1353,0,0,......]等等。
- 构建文档分词列表与对应类别的映射关系
表示为:
[[类别1,类别3,类别2,类别3,.....]
[[词列表1],[词列表2],[词列表3],[词列表4],....]
category_mapping = {u'其他': 0, u'电子商务': 1, u'社交网络': 2}
def get_label_mapping(source):
""" 载入标签映射表 """
mapping = {}
with open(source, 'rb') as f:
for line in f:
filename, label = line.decode('utf-8').split()
mapping[filename] = category_mapping[label]
return mapping
def get_data_mapping(source, stopwords):
""" 载入数据隐射表 """
mapping = {}
for root, dirs, files in os.walk(source):
for folder in dirs:
raw_file = os.path.join(root, folder, 'raw_%s.txt' % folder)
with open(raw_file, 'rb') as f:
content = f.read().strip().decode('utf-8')
mapping['raw_%s.txt' % folder] = cut(content, stopwords)
return mapping
def get_filename_mapping(label_mapping, data_mapping):
idx = 0
idx_label = {}
idx_data = {}
for filename, label in label_mapping.iteritems():
idx_label[idx] = label
idx_data[idx] = data_mapping[filename]
idx += 1
return idx_label, idx_data
- 统计构建文档集总词集{wd1:num1,wd2:num2,....}
def get_vocab(idx_data):
""" 获取文档列表的总词集
:param idx_data:
:return:
"""
vocab = defaultdict(int)
for idx, words in idx_data.iteritems():
for word in words:
vocab[word] += 1
return vocab
- 构建动态词典
- 首先根据第一步中文档词列表与类别映射表,按照类别,统计每个类别下所有词的词频,即label_dicts;同时,记录各个类别的总数label_counts。
- 然后根据第二步中统计出的全文档集中的词(不重复),对每个词应用以下公式计算权重:
wj(wdi) = [ (nj(wdi) + 0.1) / Cj ] / [(N(wdi) - nj(wdi) + 1) / (L - Cj)]
其中wj(wdi) —— 总词表中第i个词在类别j下的权重
nj(wdi) —— 第i个词在类别j中出现的次数
N(wdi) —— 第i个词在工文档集中出现的次数
Cj —— 属于类别j的文档数
L —— 总文档数
def build_dynamic_dict(idx_data, idx_label, vocab):
'''
构建动态词典
:param idx_data: 映射文档词集
[[word1,word2,word3],[word3,word4],....]
:param idx_label: 映射文档分类标签
[1,0,2,1,0,....]
:param vocab:词与词频
{word1:freq1,Word2:freq2,......}
:return: 动态词典
{类别1:{word1:p1,word2:p2,.....},
类别2:{word1:p1,word2:p2,....},
类别3:{word1:p1,word2:p2,....}
}
'''
# 构造类别词典
label_dicts = OrderedDict()
label_counts = defaultdict(int)
for idx, label in idx_label.iteritems():
if not locals().has_key('dict_%d' % label):
locals()['dict_%d' % label] = defaultdict(int)
label_dicts[label] = eval('dict_%d' % label)
label_counts[label] += 1
for word in idx_data[idx]:
label_dicts[label][word] += 1
L = sum([x for _, x in label_counts.iteritems()])
# 构造动态词典
dynamic_dicts = OrderedDict()
for label, label_dict in label_dicts.iteritems():
if not locals().has_key('dynamic_dict_%d' % label):
locals()['dynamic_dict_%d' % label] = defaultdict(float)
dynamic_dicts[label] = eval('dynamic_dict_%d' % label)
for word, N in vocab.iteritems():
freq = label_dict.get(word, 0)
other_freq = N - freq
dynamic_dicts[label][word] = (
(freq + 0.1) / label_counts[label]) / ((other_freq + 1) / (L - label_counts[label]))
return dynamic_dicts
- 文本向量表示
- 对于待表示文本,遍历词表与动态词表,生成文本词表对应的词权重,按权重大小倒序排列。并如代码注释,根据权重列表生成6个特征,添加到向量vec中。
- 对每一个类别执行上述操作,则vec的长度为6*类别数 = 18(此处类别数为3)
def sentence2vec(sentence, dynamic_dicts):
""" 使用动态词典将句子转换成特征向量
每个类别分别生成6个特征:
- average: 平均强度
- top_1: 类别强度最高
- top_3: 类别强度前3之和
- top_6: 类别强度前6之和
- top_10: 类别强度前10之和
- top_15: 类别强度前15之和
"""
vec = []
sentence = set(sentence)
for label, dynamic_dict in dynamic_dicts.iteritems():
weights = [dynamic_dict.get(word, 0.1) for word in sentence]
weights = sorted(weights, reverse=True)
# average
vec.append(sum(weights) / len(weights))
# top_1
vec.append(weights[0])
# top_3
vec.append(sum(weights[:3]))
# top_6
vec.append(sum(weights[:6]))
# top_10
vec.append(sum(weights[:10]))
# top_15
vec.append(sum(weights[:15]))
return vec