Python 使用不同文本集进行参数网格搜索以创建字典和交叉验证
我必须训练一个用于垃圾邮件检测的分类器 我拥有的数据集。 我手头有一个标有Python 使用不同文本集进行参数网格搜索以创建字典和交叉验证,python,email,pandas,scikit-learn,text-classification,Python,Email,Pandas,Scikit Learn,Text Classification,我必须训练一个用于垃圾邮件检测的分类器 我拥有的数据集。 我手头有一个标有[文本,类]的电子邮件数据集。 我也有很多没有班级标签的电子邮件 我想做什么。 我想使用gridsearchcv()函数估计模型的最佳超参数。其中一个参数与字典创建有关(如1-gram或2-gram、最小频率等)。我想要gridsearchcv()函数做的是使用管道中countvectorier的整个电子邮件数据集(带标签的电子邮件+不带标签的电子邮件)来创建字典。但我想让它只在有标签的电子邮件上测试结果。所以,基本上,我
[文本,类]
的电子邮件数据集。
我也有很多没有班级标签的电子邮件
我想做什么。
我想使用gridsearchcv()
函数估计模型的最佳超参数。其中一个参数与字典创建有关(如1-gram或2-gram、最小频率等)。我想要gridsearchcv()
函数做的是使用管道中countvectorier
的整个电子邮件数据集(带标签的电子邮件+不带标签的电子邮件)来创建字典。但我想让它只在有标签的电子邮件上测试结果。所以,基本上,我想使用整个数据集来创建字典,并且我只想在标记的数据集上使用交叉验证来估计参数
任何帮助都将受到欢迎:)
更新:
重要提示:解决@AndreasMueller问题回答:结果会有所不同,因为我还调整了CountVectorizer的参数,并使用了反向文档频率。因此,我正在寻找一种方法,通过包含未标记的数据,使我的分类器更加通用
这就是我现在所拥有的:
pipeline = Pipeline([
('features', FeatureUnion([
('words', Pipeline([
('vect', CountVectorizer()),
('frequency_transform', TfidfTransformer())
])),
('url_feature', Contains_URL_Transformer()),
('html_feature', Contains_HTML_Transformer()),
('length_feature', Text_Length_Transformer()),
('response_feature', Contains_Re_Transformer())
])),
('clf', SVC())
])
parameters = {
'features__words__vect__min_df': (1, 3, 5),
'features__words__vect__token_pattern': (r"\b[^\W\d_]+\b",),
'features__words__vect__binary': (False,),
'features__words__frequency_transform__use_idf' : (True,),
#'vect__max_features': (None, 5000, 10000, 50000),
'features__words__vect__ngram_range': ((1, 1), (1, 2)), # unigrams or bigrams
'clf__C': (1, 5, 10),
'clf__kernel': ('linear', 'rbf')
#'tfidf__use_idf': (True, False)
#'tfidf__norm': ('l1', 'l2'),
#'clf__alpha': (0.00001, 0.000001),
#'clf__penalty': ('l2', 'elasticnet'),
#'clf__n_iter': (10, 50, 80),
}
grid_search = GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=1)
data_column = numpy.asarray(data['text'])
data_column = numpy.append(data_column, ['test'])
grid_search.fit(data_column, numpy.asarray(data['class']))
best_parameters = grid_search.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
print("\t%s: %r" % (param_name, best_parameters[param_name]))
但是我还有
未启用的数据['text']
。如何将data['text']
和unlabled_data['text']
的组合添加到管道中,以便从该组合创建字典(并估计参数),但在标记的数据上测试它。问题是,当我执行grid\u search.fit()
时,它使用提供的数据集创建字典,我看不出有任何方法可以将所有电子邮件放在那里。您可以使用预先指定的字典。然而,这并不重要。如果单词没有出现在训练数据中,它们的系数将为零,因此将它们添加到词汇表中没有任何作用。简单的解决方案会强制拟合数据保持不变,而不管交叉验证数据:
X_all = full dataset
class MyVectorizer(sklearn.feature_extraction.text.TfidfVectorizer):
def fit(self, X, y=None):
return super(MyVectorizer, self).fit(X_all)
def fit_transform(self, X, y=None):
return super(MyVectorizer, self).fit(X_all).transform(X)
用它代替上面的“words”
子管道
可以说,一个不太老套但更复杂的解决方案是:
- 连接已标记和未标记的数据,设置
后者的实例到
-1
- 使用自定义交叉验证生成器,该生成器始终在训练集中保留未标记的实例
- 在管道的特征提取后部分(此处为SVC)周围使用包装器来删除未标记的数据(注意,您不能仅使用
将其实现为一个
)。(也许从SVC扩展更简单,有点像上面的转换器
所做的,但不使用全局数据黑客。)MyVectorizer
GridSearchCV
输入(而不是通过全局变量注入完整数据)
示例代码:
def semisupervised_stratified_kfold(y, *args, **kwargs):
labeled_idx = np.flatnonzero(y != -1)
unlabeled_idx = np.flatnonzero(y == -1)
for train, test in StratifiedKFold(y[labelled_idx], *args, **kwargs):
train = np.concatenate([unlabeled_idx, labeled_idx.take(train)])
test = labeled_idx.take(test)
yield train, test
from sklearn.utils.metaestimators import if_delegate_has_method
class StripUnlabelled(sklearn.base.BaseEstimator):
def __init__(self, estimator):
self.estimator = sklearn.base.clone(estimator)
def fit(self, X, y, **kwargs):
return self.estimator.fit()
@if_delegate_has_method(delegate='estimator')
def predict(self, X):
return self.estimator.predict(X)
# and similar for decision_function, predict_proba, score, etc.
然后将GridSearchCV
的cv
参数设置为自定义生成器,在SVC
实例周围环绕StripUnlabeled
,并在SVC参数名称前面加上estimator\uuuu
这实际上不会在所有数据上构建TFIDF模型,而是使用所有未标记的数据加上标记数据的所有训练折叠
另外,请注意,所有使用
管道的类似解决方案都会非常低效,因为参数在下游更改时不会缓存重复的工作,尽管已经提出了一些通用解决方案来缓存管道的一部分。您选择了哪一部分?试过什么吗?在线文档很漂亮clear@EdChum,我被卡在管道部分。我更新了我的问题。如果你能指出我能找到答案的文档,我会非常高兴。我认为这是一个非常合理的问题,我能想到的最好的解决方案是有点令人失望。在scikit learn通用邮件列表中提出这个问题可能是值得的。@joeln,尽管如此,谢谢你。为了更清楚地说明这一点,您应该编辑该问题以突出显示您的参数搜索包括CountVectorizer参数这一事实。我认为这个想法是使用从更大的语料库派生的TFIDF模型。我不确定,因为他使用的是count vectorizer,然后是TFIDF transformer。应用于CountVectorizer的解决方案不会改变任何内容。为什么不在数据上安装一个矢量器,然后在创建矢量器时使用固定的词汇表呢?因为OP也想合并来自整个语料库的文档频率统计信息,但仅限于由网格搜索参数确定的词汇表。嗯,对,我不知道他在搜索矢量器参数,too@joeln对这正是我想做的。