【问题标题】:SGD breaks down when encountering unseen values遇到看不见的值时,SGD 会崩溃
【发布时间】:2022-01-12 03:52:12
【问题描述】:

这是我的代码:

from sklearn.linear_model import SGDClassifier, LogisticRegression
from sklearn.metrics import classification_report, accuracy_score
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.preprocessing import LabelEncoder, MaxAbsScaler
from sklearn.metrics import precision_recall_fscore_support
from sklearn.decomposition import TruncatedSVD
from scipy.sparse import csr_matrix, hstack
import os


sgd_classifier = SGDClassifier(loss='log', penalty='elasticnet', max_iter=30, n_jobs=60, alpha=1e-6, l1_ratio=0.7, class_weight='balanced', random_state=0)


vectorizer = TfidfVectorizer(analyzer="char_wb", ngram_range=(4,4), min_df=10)
X_train = vectorizer.fit_transform(X_text_train.ravel())
X_test = vectorizer.transform(X_text_test.ravel())
print('TF-IDF number of features:', len(vectorizer.get_feature_names()))


scaler = MaxAbsScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)


print('Inputs shape:', X_train.shape)
sgd_classifier.fit(X_train, y_train)
y_predicted = sgd_classifier.predict(X_test)
y_predicted_prob = sgd_classifier.predict_proba(X_test)


results_report = classification_report(y_test, y_predicted, labels=classes_trained, digits=2, output_dict=True)


df_results_report = pd.DataFrame.from_dict(results_report)
pd.set_option('display.max_rows', 300)
print(df_results_report.transpose())

X_text_train & X_text_test 的形状分别为 (2M, 2) 和 (100k, 2)。

他们的第一栏是关于金融交易的描述,一般来说每个描述由5-15个单词组成;所以每行包含大约 5-15 个单词。 第二列是一个分类变量,只有与该银行交易相关的银行名称。

我将这两列合并到一个描述中,所以现在 X_text_train 和 X_text_test 的形状分别为 (2M, ) 和 (100k, )。

然后我应用 TF-IDF,现在 X_text_train 和 X_text_test 的形状分别为 (2M, 50k) 和 (100k, 50k)。

我观察到的是,当第二列上有一个看不见的值(因此合并描述中的新银行名称)时,SGDClassifier 会返回一些非常不同且完全随机的预测,而不是如果我完全放弃它会返回的预测第二列是银行名称。

如果我只对描述执行 TF-IDF 并将银行名称单独保留为分类变量,也会发生同样的情况。

为什么SGDClassifier 会发生这种情况? 是不是因为 SGD 以这种随机方式收敛,所以一般来说 SGD 不能很好地处理所有看不见的值?

有趣的是,在 TF-IDF 上,词汇表是预先确定的,因此测试集中看不见的值基本上不会在特征中考虑(即所有各自的特征都只有 0 作为值),但仍然是新元突破。

(我也在 skLearn 的 Github https://github.com/scikit-learn/scikit-learn/issues/21906 上发布了这个)

【问题讨论】:

  • 能否添加堆栈跟踪错误?
  • 您好@AntoineDubuis,谢谢您的提问。在严格的一般意义上没有错误,只是 SGDClassifier 在遇到看不见的值时会返回一些非常不同且非常随机的预测(即使只是一个或几个特征,而不是全部)。不确定这是否有帮助。

标签: machine-learning scikit-learn stochastic-gradient


【解决方案1】:

X_text_train & X_text_test 的形状分别为 (2M, 2) 和 (100k, 2),在 TF-IDF 之后它们的形状分别为 (2M, 50k) 和 (100k, 50k)。

我不明白:在 scikit-learn 中,文本矢量化器不应该接受 2D 输入。他们期望 str 对象的可迭代:

https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html#sklearn.feature_extraction.text.TfidfVectorizer.fit

所以X_text_train 不可能有除(n_documents,) 以外的形状。

X_train = vectorizer.fit_transform(X_text_train.ravel())
X_test = vectorizer.transform(X_text_test.ravel())

这对我来说没有任何意义:np.array([["a", "b"], ["c", "d"]], dtype=object).ravel() 将返回 array(['a', 'b', 'c', 'd'], dtype=object)。所以这将在X_text_train 中的每个原始行生成 2 行。

也许您想执行以下操作?

X_concat_text_train = [x[0] + " " + x[1] for x in X_text_train]

为什么 SGDClassifier 会出现这种情况?

如果没有使用最少的合成数据或公开可用的数据访问minimal reproducible example,实际上不可能准确地回答您的问题。

SGD 通常不能很好地处理所有看不见的值,因为它以这种随机方式收敛吗?

您可以通过使用非随机 LBFGS 求解器的 LogisticRegression 替换 SGDClassifier 自行回答问题。

【讨论】:

  • "这个我不明白:" 嗯严格来说好点。我的帖子不是很清楚,但正如我上面所说,我首先合并文本描述和分类,所以基本上形状是(2M,)和(100k,)。
  • 对问题的任何英文描述都会在许多层面上保持模棱两可。这就是为什么任何人都需要一个包含独立数据或合成数据的最小可重复示例来帮助您。只要你不提供,没有人可以帮助你。这真的很重要。如果您不努力,我们怎么能提供帮助?