在熊猫中编码多列 1绿豆数据 2火车测试拆分 3使用scikit-learn对文本进行标记 4将它们转换为频率tf或tf-idf 5好的,现在您可以开始训练分类器了 6找出与每个类别相关的标记作为嗅探测试 7作为第二次嗅探测试进行预测 8和9如本教程所述完成管道和评估有关问题的补充信息

问题描述

对于将分类变量用作逻辑回归或SVM等模型中的参数,我对编码(我不熟悉此类任务)有一些疑问。我的数据集如下所示:

Text                                  Symbol    Note    Account    Age   Label 
There is a red car                      !        red      John    24   1
My bag was very expensive               ?       orange    Luke    36  0
Where are my keys?                      @        red      Red     58  1
I promise: I will never let you go!    ...       green    Aoife   28  0

在“文本”中,存储了社区中用户评论。 “符号”包括用户最常用的符号。 “注释”代表其级别(绿色经验更丰富;红色为新加入者)“帐户”为用户名。 “标签”提供有关用户可信度的信息(如果为0,则表示该用户不是伪造的;如果为1,则该用户可能是可能的漫游器。)

我想根据当前信息对新用户进行分类(请参阅上面的列)。我的数据集包括1000多个行和400个用户。 由于要使用分类器,我需要对分类字段和文本字段进行编码,因此我尝试通过在sklearn中使用multicolumnLabelEncoder来进行以下操作:

multicolumnLabelEncoder(columns = ['Text','Symbol','Note','Account']).fit_transform(df)

其中df是我的数据框。但是,我知道OneHotEncoder也应该是更可取的。我还添加了“帐户”,因为同一帐户可能会有更多评论,因此,如果我将某个帐户归类为假帐户,并且收到来自同一帐户的新评论,则该帐户很容易被检测为假帐户。

正如我提到的,目标是基于给定的信息(符号,注释,年龄,文本)对测试集中的新元素进行一定精度的分类,即在这些变量之间寻找可能的相关性。我可以说一个新帐户是假的(1)或不是(0)。

如您所见,该问题与分类器有关,在分类器中参数不仅是数字,而且是分类的。

对于数据预处理(删除停用词和清除数据),我使用了NLTK的Python包;关于特征提取(这应该是关键点,因为它链接到下一步,即使用分类器来预测类-1或0),我发现难以理解我希望从编码中得到什么输出才能成为能够将上述信息用作模型中的输入(目标称为label,它是一个二进制值)。 我既使用分类器逻辑回归,也使用SVM。

用户X的情况下(年龄16,符号#,注意Wonderful,并注意红色-重新加入),我的预期输出将被分类为假的,具有一定百分比。

如果有人可以逐步向我解释将数据集转换为数据集的方式,我可以在逻辑回归中使用该变量以确定新用户标签(假冒或假冒),我将不胜感激

解决方法

我是基于我自己的一些旧代码(基于scikit-learn working with text)来完成此操作的。让我也参考Scikit-learn 6.2.3,并注意CountVectorizer会特别令人感兴趣,因为它包含您要对OneHotEncoder进行的操作以及更多操作。从CountVectorizer文档中:

CountVectorizer实现标记化和事件计数 在一个班级:

在您提供的示例案例中,您总共有95个单词,其中包含22个唯一单词-假设您使用了所有可能不是您想要的单词。换句话说,诸如“那里,一个,我,那个,我,哪里,哪个和哪个”之类的字词可能无法帮助您从虚假的账目中说出好账,但诸如“尼日利亚,王子,转账,银行,阴茎,或扩大”可能表示垃圾邮件。

因此,在转到年龄,符号等其他列之前,您将拥有22维数据(减去任何排除的数据)。这是很多无用的数据(全都是0。不需要任何东西),因此人们要么将其存储为稀疏矩阵和/或使用某种尺寸缩减功能(例如Lasso或Ridge)。您可能会认为这正是您现在想要做的,而且您处在正确的轨道上。这与您的要求有所不同。您还有一点需要解决。

首先,我认为这很重要,您的某些字段应该是可疑的,因为它们是用户报告的(如年龄)或无用/多余的(如名称)。没有孩子会在色情网站或酿酒厂网站上说他们15岁。没有一个能干的老人说他65岁想和未成年的孩子聊天。即使是您认为人们最终会发现的约会网站。人们撒谎年龄。名称也一样。您可以根据需要添加它们,但要记住一句古老的格言:垃圾进,垃圾出。

第二,Lasso和Ridge回归均分配成本函数以帮助模型过度拟合。因此,基于平方英尺和邮政编码的房价是合理的。但是,当您踏上最后一次进行财产税评估或距最近的图书馆的距离时,您可能会想“真的吗?”但这并不是您真正拥有的东西。

将这两者放在一起,在您的情况下,您将拥有文本(肯定有用),符号(文本的派生),帐户和年龄(请参阅上述注释),注释(可能在他们使用之前一直有用,并且有效),并贴上标签-您的评估。因此,在五个领域中,只有两个可能对预测评估有用。这就是说,尽管可以使用套索或山脊,但使用贝叶斯来完成此任务可能会更好。如果您愿意的话,会有多个页面显示它们在某些条件下是等效的 [example]。但是考虑贝叶斯的原因是此示例的计算量。

符号(第四部分)我一直不愿意这么说,但是根据经验,标点符号并不是一个好的指标。我讨厌的原因是您可能会提出一些新颖的实现。但是尝试了很多,所以几率很小。其中部分与Zipf定律有关,该定律与单词而不是标点有关。但是,如果标点符号带有某种附加的语义含义,则它实际上是另一个词。请记住,目标并不是要发现某个符号是否在垃圾邮件中,而是要找到该符号是否是垃圾邮件的可靠指示并且足够唯一

但是,如果您真的想添加标点符号作为某种指标,则可能需要以不同的方式来考虑。例如,仅存在问号就足够了吗?还是连续三个或更多个?或者,每个{文本,电子邮件,消息,帖子等}中的字符百分比很高?这进入了功能工程,这就是为什么我要说您需要仔细考虑的一部分。就个人而言(快速浏览我的垃圾邮件文件夹),我将查看表情符号,外来字符(例如£)和文本效果(粗体,带下划线等)。但是,您还有一个单独的第二个问题。对于文本内容,您可能会产生概率负载,并带有一个粗略的度量值:

print(f"{message} is flagged for consideration at {loading}%.

但是在上面建议的那些选项中,您将需要为该功能开发某种权重。您可以将符号附加到每个“文本”字段中,但要在TF-IDF之前。但是随后您需要使用其他方法。您还可以根据主要成分分析和/或混淆矩阵为内容分配权重,并为工程功能分配权重。

例如-已知文本34是垃圾邮件:

Nw瘦丸可杀死过多脂肪?这种饮食正在席卷全国

贝叶斯方法将垃圾邮件的总概率分配为94%,远高于89%的阈值。但这是已知的垃圾邮件,可能性为1(00%)。 6%的差异可能是由于最可能的原因?在这种情况下,我认为是英镑。

标签也是如此。从您的火车中,您可能会发现2年内发送垃圾邮件的帐户为零,而90%来自不到1周的帐户。

无论如何,继续进行代码和实现。

1。绿豆数据。

这是受监督的,因此根据定义,“标签”至关重要。

2。火车测试拆分

您没有提到这一点,但值得注意。 sklearn.model_selection.train_test_split

3。使用scikit-learn对文本进行标记。

这是您特别要问的地方。将语料库(文档集合)变成一个词袋。您说您正在使用对学术界有益的NLTK,但我觉得这太麻烦了。 SpacCy很棒,Gensim很棒。但是我正在使用scikit-learn。我的代码与示例有所不同,因为它显示了幕后发生的事情。

from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer(lowercase=True,tokenizer=None,stop_words='english',analyzer='word',max_df=1.0,min_df=1,max_features=None)
count_vect.fit(your training data)

# uncomment if you'd like to know the mapping of the columns to the words.
# count_vect.vocabulary_
# for key in sorted(count_vect.vocabulary_.keys()):
#     print("{0:<20s} {1}".format(key,count_vect.vocabulary_[key]))

关于训练集:

X_train_counts = count_vect.transform(your training data)
print("The type of X_train_counts is {0}.".format(type(X_train_counts)))
print("The X matrix has {0} rows (documents) and {1} columns (words).".format(
        X_train_counts.shape[0],X_train_counts.shape[1]))

这会给你这样的东西:

The type of X_train_counts is <class 'scipy.sparse.csr.csr_matrix'>.
The X matrix has 2257 rows (documents) and 35482 columns (words).

4。将它们转换为频率(tf或tf-idf)。

您有单词出现。 CountVectorizer只是每个单词在每个文档中出现的次数。对于每个文档,我们希望通过单词数进行归一化。这是术语(或单词)频率。 IDF有助于避免因将一个单词的一次出现除以庞大的单词数据集而导致的下溢错误。在您的情况下,这是不正确的,但通常是个问题。

5。好的,现在您可以开始训练分类器了。

至少在现在,与Scikit学习示例相关。他们使用的是朴素的贝叶斯,因此我提出了我为什么认为套索和里奇在这种情况下最不适合的理由。但是,如果您要使用回归模型,则也可以进行设置。如果要添加其他字段(符号,年龄等),则可以考虑将其附加到每个记录中。

这时我还有另外两个步骤:

6。找出与每个类别相关的标记作为嗅探测试。

通常,选择与每个类别相关的类别和词有点艺术。您可能需要对此进行迭代。

feature_words = count_vect.get_feature_names()
n = 7 #number of top words associated with the category that we wish to see

for cat in range(len(categories)):
    print(f"\nTarget: {cat},name: {target_names[cat]}")
    log_prob = nb_model.feature_log_prob_[cat]
    i_topn = np.argsort(log_prob)[::-1][:n]
    features_topn = [feature_words[i] for i in i_topn]
    print(f"Top {n} tokens: ",features_topn)

7。作为第二次嗅探测试进行预测。

根据相似的分类,您组成的一三个新文档。然后:

X_new_counts = count_vect.transform(docs_new)
X_new_tfidf = tfidf_transformer.transform(X_new_counts)
predictions = nb_model.predict(X_new_tfidf)
print('Predictions')
for doc,category in zip(docs_new,predictions):
    print("{0} => {1}".format(doc,twenty_train.target_names[category]))

8和9。如本教程所述完成管道和评估。

有关问题的补充信息。

  • 您的某些问题的答案已在上面合并。
  • 在标记化之前进行训练测试,反之亦然。我在这里仔细选择了我的话,因此请仔细阅读本部分。当前,优良作法是先分割后再标记化。基本原理是可重复性。其他人标记化然后拆分。基本原理是计算效率,术语freq对于这两者都是相同的。您会一直看到它们都完成了。数据 科学家 会对其进行广泛的测试。
  • 输出将是什么样?这取决于您的问题是什么,您处于什么阶段以及如何编码。您似乎只是在做垃圾邮件过滤器。在某个时候,您将获得一组加载,通常采用word: tf-idf加载的形式,每个文档具有多个术语/加载。您可能已经或可能已经设置了阈值,因此除了模型的概率结果之外,您还只能看到过滤器结果。
  • 其他列呢?如我之前所说,“标签”至关重要,因为这是监督学习。年龄是无用的; ,除非所有名称都是唯一的 ,否则名称可能毫无用处。有趣的事实:StackOverflow上大约有150个名为“ math”或“ Math”的东西。大概只有一个拥有您的用户号码。 “符号”很棘手,您应该认真考虑。

最后一点。这是一个单独的字段的原因。之所以写书是有原因的,为什么有多篇文章也有其原因。因此,尽管简明扼要,但将其填入一堵文本墙可能是次优的,因为其中有很多内容都不包含在内,您需要知道。