在分类问题上表现太高

数据挖掘 机器学习 分类 准确性 f1score
2022-03-05 03:55:15

我有一个.json文件作为类型的数据集:

在此处输入图像描述

我正在研究一个分类问题,我必须预测 4 个类别,它们是语义的。我已经解决了这个问题,在将 dtataset 拆分为训练集和测试集后,我得到了1.我有一个不平衡的数据集,所以我对它进行了过采样:

在此处输入图像描述

我的代码如下:

dataFrame = pd.read_json('dataset.json',lines = True)
df = dataFrame[["lista_asm", "semantic"]].copy()


from sklearn.feature_extraction.text import TfidfVectorizer 
tfidf_vectorizer=TfidfVectorizer()
df_x = df['lista_asm']

X_all = tfidf_vectorizer.fit_transform(df_x)
y_all = df['semantic']


#oversampling
from imblearn.over_sampling import RandomOverSampler

 ros = RandomOverSampler()
 X_ros, y_ros = ros.fit_sample(X_all, y_all)

 print(X_ros.shape[0] - X_all.shape[0], 'new random picked points')

#splitting

X_train, X_test, y_train, y_test = train_test_split(X_ros, y_ros, 
    test_size=0.2, random_state=15)

from sklearn import svm
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

#fitting the model
clf = svm.SVC(kernel='linear', C=10).fit(X_train,y_train)

y_pred = clf.predict(X_test)

#checking accuracy
acc = clf.score(X_test, y_test)    
print("Accuracy %.3f" %acc)          #from here I get accuracy 1

我没有编写导入以避免在此处使代码过长,但如果需要,我可以添加它们。

所以我没有在训练集上进行测试,但是结果太好了,所以肯定有什么问题。我不明白出了什么问题。

这是出于任何特定原因而发生的事情吗?

我试图更改一些东西以使代码变得更糟,以查看准确性是否会下降,但它仍然是一个。

[编辑]我已经尝试使用你的建议,我已经很抱歉,因为我肯定做错了什么。

这是我所做的:

X_all = df['lista_asm']
y_all = df['semantic']

X_train, X_test, y_train, y_test = train_test_split(X_all, y_all, 
    test_size=0.2, random_state=15)

tf = TfidfVectorizer(analyzer='word', ngram_range=(1,2), lowercase = True, 
max_features = 20000)
tf_transformer = tf.fit(X_train)
xtrain = tf.fit_transform(X_train)

from sklearn import svm
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

clf = svm.SVC(kernel='linear', C=10).fit(xtrain,y_train)

tf2=TfidfVectorizer(analyzer='word', ngram_range=(1,2), lowercase = True, 
max_features = 20000)
tf_transformer = tf2.fit(X_test)
X_test = tf2.fit_transform(X_test)

y_pred = clf.predict(X_test)

acc = clf.score(X_test, y_test)    
print("Accuracy %.3f" %acc)       #gives accuracy 0.277

如果我绘制其他性能指标:

在此处输入图像描述

带有以下消息:

UndefinedMetricWarning:精度和 F 分数定义不明确,在没有预测样本的标签中设置为 0.0。使用zero_division参数来控制此行为。_warn_prf(平均值,修饰符,msg_start,len(结果))

[编辑]我也尝试使用相同的方法,但使用.jsonl文件,在这种情况下,它可以很好地提供合理的准确性。也许这样做是错误的,但问题可能就在这里,所以我没有正确处理 .json 文件吗?

有人能帮帮我吗?

3个回答

在这种情况下,获得 100% 的准确率是正常的。我的意思是,获得 100% 的准确率是不可避免的。如果你用 10% 的数据训练你的算法,你将再次获得 100% 的准确率。

这段代码中的异常之处在于使用了矢量化器。Vectorizer 是一种算法,用于从中提到的文档中提取单词:

Tf-idf 代表词频-逆文档频率,常用于信息检索和文本挖掘。

如果您打印它从您的lista_asm变量中提取的内容,您将在特征样本中看到几乎每个唯一的单词。

print(tfidf_vectorizer.get_feature_names())

输出(其中只有 6 个适合图像,总共 23125 个可用): 在此处输入图像描述

结果,它为您创建了 23125 个新特征,其中所有这些特征都是样本中的唯一词。

当您使用这些“特征”对数据建模时,即使是简单的模型也可以轻松学习任务。由于您只有 4 个目标值['string' 'math' 'encryption' 'sort'],因此 SVM 很容易学会如何预测它们您非常空闲的特征矩阵换句话说,如果它看到一个或多个特定的词,它很容易预测你的结果。为了更清楚地说明,我们假设一个简单的训练集如下:

![在此处输入图像描述

因此,当它看到一个包含apple orange的字符串时,它很容易将其标记为sort

如果您想了解 Tf-idf Vectorizer 的工作原理,请查看此示例。

在此处输入图像描述

因此,获得 100% 的准确率是正常的。由于我不知道这些功能究竟代表什么,因此我无法对它们发表评论,但是,考虑到您将lista_asm(字符串列表)的样本转换为单个字符串,然后从中提取关键字。

如果您问这是否是一个好的解决方案。我认为这是一个奇怪的解决方案。如果你的数据很好地代表了未来的数据,那么它将正常工作。我会说它很好地代表了你的未来数据,因为当你甚至用 10% 的数据训练你的模型并用 90% 的数据进行测试时,它完美地(以 100% 的准确度)预测了目标。

附加说明:

检查每个类别的唯一值数量的以下结果(仅使用 1000 个样本)。也就是说,只出现在一个特定类别中的单词,而不出现在其他类别中。

在此处输入图像描述

和下面的代码(这是一个有点肮脏和低效的代码,因为我写得很快。对不起。)

import pandas as pd
import re

# Read the file into dataframe
dataFrame = pd.read_json('dataset.json', lines = True)

dataFrame = dataFrame[:1000]
dataFrame['opcodes'] = dataFrame['lista_asm'].apply(lambda x: re.findall("'([^']*)'", x))
df = dataFrame[['opcodes', 'semantic']]

print(df['semantic'].unique())

categories = df['semantic'].unique()

for cat in categories:
    catList = []
    refList = []
    catDf = df[df.semantic == cat]
    restDf = df[df.semantic != cat]
    for index, row in catDf.iterrows():
        catList.append(row['opcodes'])
    for index, row in restDf.iterrows():
        refList.append(row['opcodes'])
    print(len([i for i in catList if i not in refList]))

代码的作用和上图显示的是查找属于一个特定类别而不属于另一个类别的单词。例如出现在排序类别中但不在其他类别中的单词。string、math、encryption、sort分别有 222、305、197 和 276 个(仅在 1000 个样本中,随着您增加样本量,这些值会爆炸)唯一词(仅属于其类别)。通过使用这些独特的词(不完全相同但相似),您的模型可以轻松区分类别。

更新 2: 如果您采用这些特征(或单词)的子集,您的准确性会显着下降,因为您的独特单词不再存在。例如前 100 个特征:

X_ros = X_ros[:,0:100]

将此代码粘贴到train_test_split.

在此处输入图像描述

或前 1000 个特征

X_ros = X_ros[:,0:1000]

在此处输入图像描述

或者让我们使用 20% 的功能。

X_ros = X_ros[:,0:4625]

在此处输入图像描述

如您所见,您的 20% 的特征不能很好地预测您的 4 个类别。

但不要将它与max_features混淆

如果不是 None,则构建一个仅考虑按语料库中的词频排序的最高 max_features 的词汇表。如果词汇表不是无,则忽略此参数。

这会选择最好的特征,而不是随机的特征子集。

tfidf_vectorizer=TfidfVectorizer(max_features=10)

甚至其中有 10 个能够识别出与前 20% 的特征一样好的类别。它们的组合能够为您提供正确的课程。

在此处输入图像描述

  • 正如 Peter 所指出的,在将 TfidfVectorizer 拟合到训练集(而不是所有数据)之前,您应该将数据拆分为测试集和训练集。

  • Pandas read_json() 没有按照您想要的方式从文件中读取数据。您期望 df["lista_asm"] 是一系列列表对象(每个都包含字符串)。这个系列是一个字符串系列(格式化为字符串列表)。如果这是您想要的,您必须将字符串解析为列表。但我相信 TfidfVectorizer().fit 方法需要字符串,而不是字符串列表。(这个问题在您编辑之前很明显)

  • 与第一个问题类似,您应该避免在测试数据上使用 RandomOverSampler()。如果要使用它,请仅将其应用于训练数据。我怀疑它可能会创建重复或近乎重复的记录,其中一些后来被拆分到测试集中。尽量不要过采样并评估性能。

您可以检查的一件事是是否在整个文本上使用矢量化器可能是一个问题。测试集应该是“新的”看不见的数据,不一定要与训练数据一起向量化。您可以使用“旧”词汇(来自训练集)在新文本(测试集)上“改装”矢量化器。但是,我不确定这是否真的是您的问题。本质上,您需要使用“旧” vocabulary(来自训练集)来准备您的(看不见的)测试数据。

最小的例子TfidfVectorizer()

from sklearn.linear_model import LogisticRegressionCV
from sklearn.feature_extraction.text import TfidfVectorizer

# Corpus (X train) and target (y train) from a pandas df
corpus = df[['text']].values[:,0].astype('U').tolist()
ytrain = pd.Series(df['yvar'])

# Prepare tfidf vectorizer
tf = TfidfVectorizer(analyzer='word', ngram_range=(1,2), lowercase = True, max_features = 20000)
tf_transformer = tf.fit(corpus)
xtrain = tf.fit_transform(corpus)
# Save transformer
pickle.dump(tf_transformer, open(mypath + "tfidf.pkl", "wb"))

# Do classification
clf = LogisticRegressionCV(n_jobs=2, penalty='l2', solver='liblinear', cv=10, scoring = 'accuracy', random_state=0)
clf.fit(xtrain, ytrain)

# Save classifier
with open(mypath + 'clf.pkl', 'wb') as f:
    pickle.dump(clf, f)

##### New text (test data)
# Load classifier
with open(modelpath + "clf.pkl", 'rb') as f:
    clf = pickle.load(f)

# Load transformer (I use the "old" vocabulary from tf1 here to generate tf1_new)
tf1 = pickle.load(open(modelpath + "tfidf.pkl", 'rb'))
tf1_new = TfidfVectorizer(analyzer='word', ngram_range=(1,2), lowercase = True, max_features = 20000, vocabulary = tf1.vocabulary_)

# Predict classifier on tf1_new...
xtest = tf1_new.fit_transform([mynewtext])
res = clf.predict_proba(xtest)

也可以在这里找到代码:https ://github.com/Bixi81/Python-ml