K折交叉验证实现python

Posted

技术标签:

【中文标题】K折交叉验证实现python【英文标题】:K-fold cross validation implementation python 【发布时间】:2016-12-15 12:12:01 【问题描述】:

我正在尝试在 python 中实现 k 折交叉验证算法。 我知道 SKLearn 提供了一个实现,但仍然...... 这是我现在的代码。

from sklearn import metrics
import numpy as np

class Cross_Validation:

@staticmethod
def partition(vector, fold, k):
    size = vector.shape[0]
    start = (size/k)*fold
    end = (size/k)*(fold+1)
    validation = vector[start:end]
    if str(type(vector)) == "<class 'scipy.sparse.csr.csr_matrix'>":
        indices = range(start, end)
        mask = np.ones(vector.shape[0], dtype=bool)
        mask[indices] = False
        training = vector[mask]
    elif str(type(vector)) == "<type 'numpy.ndarray'>":
        training = np.concatenate((vector[:start], vector[end:]))
    return training, validation

@staticmethod
def Cross_Validation(learner, k, examples, labels):
    train_folds_score = []
    validation_folds_score = []
    for fold in range(0, k):
        training_set, validation_set = Cross_Validation.partition(examples, fold, k)
        training_labels, validation_labels = Cross_Validation.partition(labels, fold, k)
        learner.fit(training_set, training_labels)
        training_predicted = learner.predict(training_set)
        validation_predicted = learner.predict(validation_set)
        train_folds_score.append(metrics.accuracy_score(training_labels, training_predicted))
        validation_folds_score.append(metrics.accuracy_score(validation_labels, validation_predicted))
    return train_folds_score, validation_folds_score

learner 参数是来自 SKlearn 库的分类器,k 是折叠数,examples 是由 CountVectorizer(同样是 SKlearn)生成的稀疏矩阵,它是词袋的表示。 例如:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from Cross_Validation import Cross_Validation as cv

vectorizer = CountVectorizer(stop_words='english', lowercase=True, min_df=2, analyzer="word")
data = vectorizer.fit_transform("""textual data""")
clfMNB = MultinomialNB(alpha=.0001)
score = cv.Cross_Validation(clfMNB, 10, data, labels)
print "Train score" + str(score[0])
print "Test score" + str(score[1])

我假设某处存在一些逻辑错误,因为训练集上的分数为 95%(如预期的那样),但在测试测试中几乎为 0,但我找不到。

我希望我很清楚。 提前致谢。

__________________________________编辑___________________________________

这是将文本加载到可以传递给矢量化器的矢量的代码。它还返回标签向量。

from nltk.tokenize import word_tokenize
from Categories_Data import categories
import numpy as np
import codecs
import glob
import os
import re

class Data_Preprocessor:

def tokenize(self, text):
    tokens = word_tokenize(text)
    alpha = [t for t in tokens if unicode(t).isalpha()]
    return alpha

def header_not_fully_removed(self, text):
    if ":" in text.splitlines()[0]:
        return len(text.splitlines()[0].split(":")[0].split()) == 1
    else:
        return False

def strip_newsgroup_header(self, text):
    _before, _blankline, after = text.partition('\n\n')
    if len(after) > 0 and self.header_not_fully_removed(after):
        after = self.strip_newsgroup_header(after)
    return after

def strip_newsgroup_quoting(self, text):
    _QUOTE_RE = re.compile(r'(writes in|writes:|wrote:|says:|said:'r'|^In article|^Quoted from|^\||^>)')
    good_lines = [line for line in text.split('\n')
        if not _QUOTE_RE.search(line)]
    return '\n'.join(good_lines)

def strip_newsgroup_footer(self, text):
    lines = text.strip().split('\n')
    for line_num in range(len(lines) - 1, -1, -1):
        line = lines[line_num]
        if line.strip().strip('-') == '':
            break
    if line_num > 0:
        return '\n'.join(lines[:line_num])
    else:
        return text

def raw_to_vector(self, path, to_be_stripped=["header", "footer", "quoting"], noise_threshold=-1):
    base_dir = os.getcwd()
    train_data = []
    label_data = []
    for category in categories:
        os.chdir(base_dir)
        os.chdir(path+"/"+category[0])
        for filename in glob.glob("*"):
            with codecs.open(filename, 'r', encoding='utf-8', errors='replace') as target:
                data = target.read()
                if "quoting" in to_be_stripped:
                    data = self.strip_newsgroup_quoting(data)
                if "header" in to_be_stripped:
                    data = self.strip_newsgroup_header(data)
                if "footer" in to_be_stripped:
                    data = self.strip_newsgroup_footer(data)
                if len(data) > noise_threshold:
                    train_data.append(data)
                    label_data.append(category[1])
    os.chdir(base_dir)
    return np.array(train_data), np.array(label_data)

这是“从 Categories_Data 导入类别”导入的内容...

categories = [
    ('alt.atheism',0),
    ('comp.graphics',1),
    ('comp.os.ms-windows.misc',2),
    ('comp.sys.ibm.pc.hardware',3),
    ('comp.sys.mac.hardware',4),
    ('comp.windows.x',5),
    ('misc.forsale',6),
    ('rec.autos',7),
    ('rec.motorcycles',8),
    ('rec.sport.baseball',9),
    ('rec.sport.hockey',10),
    ('sci.crypt',11),
    ('sci.electronics',12),
    ('sci.med',13),
    ('sci.space',14),
    ('soc.religion.christian',15),
    ('talk.politics.guns',16),
    ('talk.politics.mideast',17),
    ('talk.politics.misc',18),
    ('talk.religion.misc',19)
 ]

【问题讨论】:

对不起,但我认为在 sklearn 中实现如此容易获得的东西有点浪费时间。唯一的一点可能是出于教学目的 - 如果您想学习自己编码,或者遇到一些您无法弄清楚的语言点。在每种情况下,将这堵代码墙扔给某人并让他们为您调试它有什么意义?充其量你会有另一个 k-fold 工作实现,并且已经有一个这样的...... 嗯,这当然只是为了理解我做错了什么。由于已经过了几天而且我无法弄清楚我问是否可能存在明显的逻辑错误或我不知道的关于 scipy ecc 的事情......我不知道任何其他方式来解释这个问题没有代码的其他人(特别是因为我不知道问题是什么) 您是否可以上传一些我们可以测试的数据集并导入所有相关的 scikit 包? 我编辑了这个问题。你可以在这里找到数据集qwone.com/~jason/20Newsgroups。我使用原始(第一个)一个。谢谢 太棒了。让我们看看:) 【参考方案1】:

验证分数低的原因很微妙。

问题在于您如何对数据集进行分区。请记住,在进行交叉验证时,您应该随机拆分数据集。你缺少的是随机性。

您的数据是按类别加载的,这意味着在您的输入数据集中,类标签和示例一个接一个。通过不进行随机拆分,您已经完全删除了您的模型在训练阶段从未见过的类,因此您在测试/验证阶段得到了一个糟糕的结果。

您可以通过随机播放来解决此问题。所以,这样做:

from sklearn.utils import shuffle    

processor = Data_Preprocessor()
td, tl = processor.raw_to_vector(path="C:/Users/Pankaj/Downloads/ng/")
vectorizer = CountVectorizer(stop_words='english', lowercase=True, min_df=2, analyzer="word")
data = vectorizer.fit_transform(td)
# Shuffle the data and labels
data, tl = shuffle(data, tl, random_state=0)
clfMNB = MultinomialNB(alpha=.0001)
score = Cross_Validation.Cross_Validation(clfMNB, 10, data, tl)

print("Train score" + str(score[0]))
print("Test score" + str(score[1]))

【讨论】:

就是这样!非常感谢! @LorenzoNorcini 我很高兴。我也不同意 cmets 认为这是无意义的练习。 交叉验证数据不应该总是随机拆分。时间序列数据有不同的分割方式,比如滑动窗口。 @PankajDaga,它一定花了很多时间来检查代码并弄清楚关于洗牌的事情。我很高兴有像你这样的人在附近提供帮助。 :)

以上是关于K折交叉验证实现python的主要内容,如果未能解决你的问题,请参考以下文章

在 MLPClassification Python 中实现 K 折交叉验证

小白学习之pytorch框架之实战Kaggle比赛:房价预测(K折交叉验证*args**kwargs)

k折交叉验证模型选择方法

交叉验证(cross validation)是什么?K折交叉验证(k-fold crossValidation)是什么?

机器学习100天(二十九):029 K折交叉验证

机器学习100天(二十九):029 K折交叉验证