SMOTE初始化期望n_neighbors <= n_samples,但n_samples < n_neighbors。

16

我已经预先清理了数据,以下显示前4行的格式:

     [IN] df.head()

    [OUT]   Year    cleaned
         0  1909    acquaint hous receiv follow letter clerk crown...
         1  1909    ask secretari state war whether issu statement...
         2  1909    i beg present petit sign upward motor car driv...
         3  1909    i desir ask secretari state war second lieuten...
         4  1909    ask secretari state war whether would introduc...

我已经按如下方式调用了train_test_split():

     [IN] X_train, X_test, y_train, y_test = train_test_split(df['cleaned'], df['Year'], random_state=2)
   [Note*] `X_train` and `y_train` are now Pandas.core.series.Series of shape (1785,) and `X_test` and `y_test` are also Pandas.core.series.Series of shape (595,)

我随后使用以下TfidfVectorizer和拟合/转换过程对X训练和测试数据进行了向量化:

     [IN] v = TfidfVectorizer(decode_error='replace', encoding='utf-8', stop_words='english', ngram_range=(1, 1), sublinear_tf=True)
          X_train = v.fit_transform(X_train)
          X_test = v.transform(X_test)

目前我已到了通常需要应用分类器等方法的阶段(如果这是一组平衡数据的话)。然而,我初始化了imblearn的SMOTE()类(用于执行过采样)...

     [IN] smote_pipeline = make_pipeline_imb(SMOTE(), classifier(random_state=42))
          smote_model = smote_pipeline.fit(X_train, y_train)
          smote_prediction = smote_model.predict(X_test)

...但这会导致:

     [OUT] ValueError: "Expected n_neighbors <= n_samples, but n_samples = 5, n_neighbors = 6.

我尝试减少n_neighbors的数量,但无济于事,有任何提示或建议将不胜感激。谢谢阅读。

------------------------------------------------------------------------------------------------------------------------------------

编辑:

完整追溯信息

数据集/数据框(df)包含两列,共2380行,如上所示df.head()X_train以字符串列表的格式(df['cleaned'])包含了其中的1785行,y_train也是以字符串的形式(df['Year'])包含了1785行。

在使用TfidfVectorizer()向量化之后:X_trainX_test从格式为'(1785,)'和'(595,)'的pandas.core.series.Series分别转换为形状为“(1785, 126459)”和“(595, 126459)” 的scipy.sparse.csr.csr_matrix

至于类别数:使用Counter(),我计算出有199个类别(年份),每个类别的实例都与前述包含从文本语料库中提取的字符串列表的df['cleaned']数据中的一个元素相关联。

该过程的目的是根据存在的词汇自动确定/猜测输入文本数据的年份、十年或世纪(任何分类程度都可以!)。


1
错误信息已经很明显了,不是吗?我猜你需要在X_train中增加更多的样本(行)。 - MaxU - stand with Ukraine
1
请添加完整的错误堆栈跟踪。 - Vivek Kumar
1
请告诉我们您的类别不平衡情况。每个类别有多少个样本?共有多少个类别? - Stev
谢谢大家的回复,我已经尽力在编辑原帖时回答了你们的问题。如果还有需要纠正的地方,请让我知道! - Dbercules
5个回答

26

由于训练集中大约有200个类别和1800个样本,因此每个类别平均有9个样本。错误信息的原因是a)可能数据不完全平衡,存在少于6个样本的类别;b)邻居数量为6。以下是解决问题的几种方法:

  1. 计算199个类别中最小样本数(n_samples),并选择SMOTE类的n_neighbors参数小于或等于n_samples。

  2. 使用SMOTE类的ratio参数排除n_samples < n_neighbors的类别。

  3. 使用RandomOverSampler类,该类没有类似的限制。

  4. 结合3和4的解决方案:创建一个管道,以满足smoted类的n_neighbors <= n_samples条件,并在不满足条件时使用随机过采样的方式使用SMOTE和RandomOversampler。


谢谢回答,那么仅针对预测将类别数量减少到例如十年代而不是逐年分类如何?与此同时,我会继续按照您的建议努力! - Dbercules
由于某些类别仅拥有一个样本,我无法对 (1) 和 (2) 进行调查。但是,我成功地将 RandomOverSampler(和/或 FakeSampler 类)与 SMOTE 和分类器组合为管道,如下所示:make_pipeline(sampler, SMOTE(), clf)。我将继续使用这种方法,并看看能做到什么!感谢您的时间! - Dbercules
2
@Dbercules:你好,请问你能指导我如何制作管道吗?我尝试了以下代码:sm = SMOTE(random_state=42)rm = RandomOverSampler(random_state=42)my_pipe = make_pipeline(sm, rm)X_res, Y_res = my_pipe.fit_resample(X, y)但是出现了错误,和标题中的问题一样。 - cappy0704

5
尝试使用以下代码进行SMOTE: oversampler=SMOTE(kind='regular',k_neighbors=2) 这对我有效。

1
我收到了这个错误 TypeError: __init__() got an unexpected keyword argument 'kind' - amin

3

问题原因:

在我的情况下,出现这个问题是因为某些值/类别的样本数量只有1个。由于SMOTE是基于KNN概念的,所以无法在只有1个样本的值上应用SMOTE。

解决方法:

由于那些只有1个样本/类别的数据等同于异常值,我将它们从数据集中删除,然后应用SMOTE就可以了。

另外尝试减小k_neighbors参数的值也可以解决这个问题。

xr, yr = SMOTE(k_neighbors=3).fit_resample(x, y)

0

我能够通过遵循this答案的第一条解决此问题。

from collections import Counter

Count(df) # get the classes

# drop the classes with 1 as their value because it's lower than k_neighbors which has 2 as minimum value in my case

X_res, y_res = SMOTE(k_neighbors = 2).fit_resample(X, y)

0

我认为可以使用以下代码:

sampler = SMOTE(ratio={1: 1927, 0: 300},random_state=0)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接