我仍然很擅长机器学习,并试图自己解决问题。我正在使用SciKit学习并拥有大约20,000个功能的推文数据集(n_features = 20,000)。到目前为止,我的精确度,召回率和f1得分都达到了79%左右。我想使用RFECV进行特征选择并提高模型的性能。我已经阅读了SciKit学习文档,但对如何使用RFECV仍然有点困惑。
这是我到目前为止的代码:
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.naive_bayes import MultinomialNB
from sklearn.cross_validation import StratifiedShuffleSplit
from sklearn.cross_validation import cross_val_score
from sklearn.feature_selection import RFECV
from sklearn import metrics
# cross validation
sss = StratifiedShuffleSplit(y, 5, test_size=0.2, random_state=42)
for train_index, test_index in sss:
docs_train, docs_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# feature extraction
count_vect = CountVectorizer(stop_words='English', min_df=3, max_df=0.90, ngram_range=(1,3))
X_CV = count_vect.fit_transform(docs_train)
tfidf_transformer = TfidfTransformer()
X_tfidf = tfidf_transformer.fit_transform(X_CV)
# Create the RFECV object
nb = MultinomialNB(alpha=0.5)
# The "accuracy" scoring is proportional to the number of correct classifications
rfecv = RFECV(estimator=nb, step=1, cv=2, scoring='accuracy')
rfecv.fit(X_tfidf, y_train)
X_rfecv=rfecv.transform(X_tfidf)
print("Optimal number of features : %d" % rfecv.n_features_)
# train classifier
clf = MultinomialNB(alpha=0.5).fit(X_rfecv, y_train)
# test clf on test data
X_test_CV = count_vect.transform(docs_test)
X_test_tfidf = tfidf_transformer.transform(X_test_CV)
X_test_rfecv = rfecv.transform(X_test_tfidf)
y_predicted = clf.predict(X_test_rfecv)
#print the mean accuracy on the given test data and labels
print ("Classifier score is: %s " % rfecv.score(X_test_rfecv,y_test))
三个问题:
1)这是使用交叉验证和RFECV的正确方法吗?我特别想知道我是否有过度装配的风险。
2)我使用上述代码实现RFECV之前和之后模型的准确性几乎相同(大约78-79%),这让我很困惑。我希望通过使用RFECV来提高性能。我可能在这里错过了什么或者可以做些不同的事情来提高模型的性能?
3)您可以推荐我尝试哪些其他功能选择方法?到目前为止,我已尝试过RFE和SelectKBest,但它们都没有给我任何模型精度方面的改进。
答案 0 :(得分:5)
回答你的问题:
在RFECV功能选择(因此名称)中内置了交叉验证,因此您不需要为此单步执行额外的交叉验证。但是,由于我了解您正在进行多项测试,因此最好进行整体交叉验证,以确保您不会过度拟合特定的列车测试分组。我想在这里提到2点:
我怀疑代码的行为与您认为的完全相同;)。
# cross validation
sss = StratifiedShuffleSplit(y, 5, test_size=0.2, random_state=42)
for train_index, test_index in sss:
docs_train, docs_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# feature extraction
count_vect = CountVectorizer(stop_words='English', min_df=3, max_df=0.90, ngram_range=(1,3))
X_CV = count_vect.fit_transform(docs_train)
这里我们首先完成循环,它有5次迭代(n_iter
中的StratifiedShuffleSplit
参数)。然后我们退出循环,我们只使用train_index, test_index
的最后一个值运行所有代码。所以这相当于一个列车测试分区,你可能想要有5个。如果你想让它像'正确的'交叉验证一样运行,你应该将代码移回循环中。
看到功能选择没有那么大的影响可能会令人费解。为了进一步反省,您可以根据所选要素的数量来研究得分的演变(参见the example from the docs)。 话虽如此,我认为这不是RFE的正确用例。基本上使用您的代码,您将逐个删除功能,这可能需要很长时间才能运行,并且当您拥有20000个功能时没有那么多意义。
其他功能选择方法:在此提及SelectKBest
但您没有告诉我们您使用哪种方法对功能进行评分! SelectKBest
将根据分数函数选择K个最佳功能。我猜你正在使用默认值,但是最好知道默认值是什么;)。
我会尝试SelectPercentile
chi2
作为分数函数。 SelectPercentile
可能比SelectKBest
更方便,因为如果您的数据集增长百分比可能比硬编码的功能更有意义。
Another example from the docs就是这样(以及更多)。
TfidfVectorizer
代替CountVectorizer
,后跟TfidfTransformer
。这完全相同。 您可以使用pipeline object将分类器的不同步骤打包到一个可以运行交叉验证的对象中(我鼓励您阅读文档,这非常有用)。
from sklearn.feature_selection import chi2_sparse
from sklearn.feature_selection import SelectPercentile
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
pipeline = Pipeline(steps=[
("vectorizer", TfidfVectorizer(stop_words='English', min_df=3, max_df=0.90, ngram_range=(1,3))),
("selector", SelectPercentile(score_func=chi2, percentile=70)),
('NB', MultinomialNB(alpha=0.5))
])
然后,您就可以对管道对象运行交叉验证,以找到alpha和百分位数的最佳组合,这对单独的估算器来说要难得多。
希望这会有所帮助,快乐学习;)。