Поиск сетки параметров с различными наборами текста для создания словаря и перекрестной проверки
Я должен обучить классификатор для обнаружения спама.
Набор данных, который у меня есть.
Под рукой у меня есть один помеченный набор данных электронных писем с [text, class]
, И у меня также есть много электронных писем без ярлыков классов.
Что я хочу сделать.
Я хочу использовать gridsearchcv()
Функция для оценки лучших гиперпараметров для моей модели. И один из параметров связан с созданием словаря (например, 1-грамм или 2-грамм, минимальная частота и т. Д.). Что я хочу gridsearchcv()
Для этого нужно использовать весь набор данных электронной почты (электронные письма с метками + электронные письма без меток) для CountVectorizer
в моем конвейере, чтобы создать словарь. Но я хочу, чтобы он проверял результат только на помеченных электронных письмах. Итак, в основном я хочу использовать весь набор данных для создания словаря, и я хочу оценить параметры, используя перекрестную проверку только на помеченном наборе данных.
Любая помощь будет оценена:)
Обновить:
важно: чтобы ответить @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]))
Но у меня также есть unlabled_data['text']
, Как я могу добавить смесь data['text']
а также unlabled_data['text']
в конвейер, чтобы создать словарь (и оценить параметры) из этой смеси, но проверить его на помеченных данных. Проблема в том, что когда я делаю grid_search.fit()
он использует предоставленный набор данных для создания словаря, и я не вижу способа поместить туда все электронные письма.
2 ответа
Простое решение обеспечивает соответствие данных подгонки постоянным независимо от данных перекрестной проверки:
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), чтобы удалить немеченые данные (обратите внимание, что вы не можете просто реализовать это как
Transformer
). (Возможно, расширение из 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
"s cv
параметр к пользовательскому генератору, обтекание StripUnlabeled
вокруг SVC
Экземпляр и префикс имен SVC параметров с estimator__
Это на самом деле не построит модель TFIDF на всех данных, но будет использовать все немаркированные данные плюс все обучающие сгибы помеченных данных.
Также обратите внимание, что все аналогичные решения, использующие Pipeline
будет весьма неэффективным, учитывая, что повторная работа не кэшируется при изменении параметров в нисходящем направлении, хотя существуют общие решения, которые были предложены для кэширования частей конвейеров.
Вы можете сделать это, используя заранее заданный словарь. Однако это не имеет значения. Если слова не появляются в обучающих данных, их коэффициент будет равен нулю, поэтому добавление их в словарь ничего не делает.