特征模型1
1、文本类的分类任务,特征提取几种方式
1、词集模型
文本中单词的种类的集合,只统计单词的有无,和出现个数无关
使用场景:自定义的词集黑名单,是否可以使用这种模型,具体使用方式(TODO1)?
2、词袋模型
相对于词集模型,还有统计每个单词出现的次数(频率)
实现原理:参考sklearn(TODO2)
3、TF-IDF(词频-逆向文本频率模型)
从字面就可以理解,该模型即考虑单词在本文档出现的频率,同时考虑在其他文本出现的频率,单词频率 x 逆文档频率。主要思想为:单词在本文档出现频率越大,在其他文档出现的越少,则整体的值越大,即具有很好的区分能力。
通常词袋模型和TF-IDF配合使用,可以理解为TF-IDF为对词袋模型归一化处理吗?要回答这个问题,首先要搞明白2个子问题,1、TF-IDF具体做了啥?(TODO3) 2、归一化处理是要做啥,和问题1实现的事一致的吗?(TODO4)
4、词汇表模型
前面3中模型没有表达单词间的关系,于是又了词汇表模型。该模型在词袋模型思想的基础上,按照句子中单词顺序进行排序输出特征
上诉模型的几个问题
1、词袋模型实现(sklearn)
a CountVectorizer初始化
def __init__(self, input='content', encoding='utf-8',
decode_error='strict', strip_accents=None,
lowercase=True, preprocessor=None, tokenizer=None,
stop_words=None, token_pattern=r"(?u)\b\w\w+\b",
ngram_range=(1, 1), analyzer='word',
max_df=1.0, min_df=1, max_features=None,
vocabulary=None, binary=False, dtype=np.int64):
关键参数含义:
lowercase:是否区分大小写(默认True,全部转小写)
preprocessor:自定义预处理(字符串转换)方法(默认None,使用预定义)
analyzer:特征力度,可以为'word', 'char', 'char_wb',默认word
min_df、max_df:忽略词频大于设置值。可为整数或者小数/整数,小数表示该词的比例,整数表示该词的次数
max_features:按照词频排序取前多少个特征,为整数
b、计算模型值
def fit_transform(self, raw_documents, y=None):
......
vocabulary, X = self._count_vocab(raw_documents,self.fixed_vocabulary_) #######计算特征的主要方法
if self.binary:
X.data.fill(1)
if not self.fixed_vocabulary_:
X = self._sort_features(X, vocabulary) #############按照排序词汇表和特征,特征是根据indices属性表示非0列的
................
X, self.stop_words_ = self._limit_features(X, vocabulary,
max_doc_count,
min_doc_count,
max_features) #############过滤特征个数
self.vocabulary_ = vocabulary
return X
def _count_vocab(self, raw_documents, fixed_vocab):
.........
for doc in raw_documents: ########遍历计算特征文档
feature_counter = {}
for feature in analyze(doc): ########遍历文档中的特征(单词、字母)
try:
feature_idx = vocabulary[feature] #########vocabulary为全量特征字典
if feature_idx not in feature_counter: ########本文档特征字典,形式为特征值:个数。如果新增特征,默认返回值为0
feature_counter[feature_idx] = 1
else:
feature_counter[feature_idx] += 1
except KeyError:
# Ignore out-of-vocabulary items for fixed_vocab=True
continue
j_indices.extend(feature_counter.keys()) #########j_indices为csr_matrix中定义一致,为非0元素对应的列索引值所组成数组
values.extend(feature_counter.values()) #########values为按照顺序的全部文档的特征值的次数,和上面的j_indices一一对应
indptr.append(len(j_indices)) #########indptr为csr_matrix中定义一致,第一个元素0,之后每个元素表示稀疏矩阵中每行元素(非零元素)个数累计结果(和上面j_indices、values结合就可以还原每个文档特征值
if not fixed_vocab:
# disable defaultdict behaviour
vocabulary = dict(vocabulary)
if not vocabulary:
raise ValueError("empty vocabulary; perhaps the documents only"
" contain stop words")
if indptr[-1] > 2147483648: # = 2**31 - 1
if _IS_32BIT:
raise ValueError(('sparse CSR array has {} non-zero '
'elements and requires 64 bit indexing, '
'which is unsupported with 32 bit Python.')
.format(indptr[-1]))
indices_dtype = np.int64
else:
indices_dtype = np.int32
j_indices = np.asarray(j_indices, dtype=indices_dtype) ##########转换为csr_matrix要求格式
indptr = np.asarray(indptr, dtype=indices_dtype) ##########转换为csr_matrix要求格式
values = np.frombuffer(values, dtype=np.intc) ##########转换为csr_matrix要求格式
X = sp.csr_matrix((values, j_indices, indptr),
shape=(len(indptr) - 1, len(vocabulary)),
dtype=self.dtype)
X.sort_indices()
return vocabulary, X
2、TF-IDF模型实现(sklearn)
tf-idf(t, d) = tf(t, d) * idf(t)
if ``smooth_idf=False``, idf(t) = log [ n / df(t) ] + 1 () ,其中n为所有文档数,df(t)为所有文档中包含term的文档数量; 后面+1的原因是当所有文档都包含term,则上诉等式就等于0
if ``smooth_idf=True``(默认情况),则 idf(d, t) = log [ (1 + n) / (1 + df(d, t)) ] + 1 ,即分子分母都+1,即一个默认的文档包含所有的term一次
if ``sublinear_tf=True``,则tf(t,d)=n,n为term在d出现的次数
if ``sublinear_tf=False``,则tf(t,d)=log(n),n为term在d出现的次数
a TF-IDF初始化
def __init__(self, norm='l2', use_idf=True, smooth_idf=True,sublinear_tf=False):
self.norm = norm
self.use_idf = use_idf
self.smooth_idf = smooth_idf
self.sublinear_tf = sublinear_tf
关键参数含义:
norm: '11' L1范数, '12' L2范数,None
当为L2范数,向量中元素平方和为1;当为L1范数,向量中元数绝对值和为1.
smooth_idf和sublinear:控制计算td-idf计算公式的,详情见上文
b、计算模型值
fit_transform先调用fit,再调用transform
def fit_transform(self, X, y=None, **fit_params):
if y is None:
# fit method of arity 1 (unsupervised transformation)
return self.fit(X, **fit_params).transform(X)
else:
# fit method of arity 2 (supervised transformation)
return self.fit(X, y, **fit_params).transform(X)
先看fit函数
def fit(self, X, y=None): ######################计算idf
........
if self.use_idf: #############判断是否使用use_idf属性
n_samples, n_features = X.shape ##############输入参数X形状,n_samples行数,n_features列数
df = _document_frequency(X) ##############计算非0列的各列的总数,结果就一行 np.bincount(X.indices, minlength=X.shape[1])
df = df.astype(dtype, **_astype_copy_false(df)) #############按照类型格式化
# perform idf smoothing if required
df += int(self.smooth_idf) ############# smooth设置为True时,分母分子都+1、
n_samples += int(self.smooth_idf)
# log+1 instead of log makes sure terms with zero idf don't get
# suppressed entirely.
idf = np.log(n_samples / df) + 1 ################## idf的计算公式
self._idf_diag = sp.diags(idf, offsets=0, ###############将一维的idf值构成n_feature*n_feature对角矩阵,用于后面和tf矩阵相乘得到tf-idf的值
shape=(n_features, n_features),
format='csr',
dtype=dtype)
return self
def transform(self, X, copy=True): ##################计算matrix的 tf或者tf-idf
n_samples, n_features = X.shape #################计算入参matrix的 行数和列数
if self.sublinear_tf: ################## 按照超参数sublinear_tf=False计算值
np.log(X.data, X.data)
X.data += 1
if self.use_idf: ################## 按照超参数use_idf计算值
check_is_fitted(self, '_idf_diag', 'idf vector is not fitted')
expected_n_features = self._idf_diag.shape[0]
if n_features != expected_n_features:
raise ValueError("Input has n_features=%d while the model"
" has been trained with n_features=%d" % (
n_features, expected_n_features))
# *= doesn't work
X = X * self._idf_diag #################### X乘以idf构成的对角矩阵,正好就把每一个tf的值和idf值乘了一遍(均在相应位置的对角线上有值,其他的位置都为0)
if self.norm:
X = normalize(X, norm=self.norm, copy=False) ################## 对计算出来的tf-idf进行L1或者L2正则化
return X
2、词汇表模型实现(tensorflow)
a 词汇表模型初始化
def __init__(self,max_document_length,min_frequency=0,vocabulary=None,tokenizer_fn=None):
self.max_document_length = max_document_length
self.min_frequency = min_frequency
if vocabulary:
self.vocabulary_ = vocabulary
else:
self.vocabulary_ = CategoricalVocabulary()
if tokenizer_fn:
self._tokenizer = tokenizer_fn
else:
self._tokenizer = tokenizer
关键参数含义:
max_document_length:输出特征的长度
min_frequency:最小频率
vocabulary:对象,使用方法未知
tokenizer_fn:
b Fit_transform
def fit_transform(self, raw_documents, unused_y=None):
self.fit(raw_documents)
return self.transform(raw_documents)
def fit(self, raw_documents, unused_y=None):
for tokens in self._tokenizer(raw_documents): ################### 每一条数据(每一个docment)遍历,tokens为一个document的分词后的list
for token in tokens: ##################### 从 document分词的list中获取每一个单词 token
self.vocabulary_.add(token) #################### 所有词汇的列表CategoricalVocabulary,其中CategoricalVocabulary有_mapping(为{单词:index})、_reverse_mapping(为所有的单词list)、_freq(为{单词:总次数})
if self.min_frequency > 0:
self.vocabulary_.trim(self.min_frequency)
self.vocabulary_.freeze() ########### 设置CategoricalVocabulary的freeze属性
return self
def transform(self, raw_documents):
for tokens in self._tokenizer(raw_documents): ################### 每一条数据(每一个docment)遍历,tokens为一个document的分词后的list
word_ids = np.zeros(self.max_document_length, np.int64) ################### 设置每一条数据的返回结果
for idx, token in enumerate(tokens): ##################### 从 document分词的list中获取每一个单词 token和他的idx
if idx >= self.max_document_length: ##################### 超过最大长度,直接跳出过滤
break
word_ids[idx] = self.vocabulary_.get(token) ##################### 在句子的先后单词位置设置为单词在vocabulary_的编号
yield word_ids ##################### 返回整个句子编码后的内容