在ML分类器中对文本进行编码

5

我正在尝试构建一个机器学习模型。然而,我在理解何时应用编码方面遇到了困难。 请看下面的步骤和函数,以复制我一直在遵循的过程。

首先,我将数据集分为训练集和测试集:

# Import the resampling package
from sklearn.naive_bayes import MultinomialNB
import string
from nltk.corpus import stopwords
import re
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from nltk.tokenize import RegexpTokenizer
from sklearn.utils import resample
from sklearn.metrics import f1_score, precision_score, recall_score, accuracy_score
# Split into training and test sets

# Testing Count Vectorizer

X = df[['Text']] 
y = df['Label']


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=40)

# Returning to one dataframe
training_set = pd.concat([X_train, y_train], axis=1)

现在我进行(欠)取样:

# Separating classes
spam = training_set[training_set.Label == 1]
not_spam = training_set[training_set.Label == 0]

# Undersampling the majority
undersample = resample(not_spam, 
                       replace=True, 
                       n_samples=len(spam), #set the number of samples to equal the number of the minority class
                       random_state=40)
# Returning to new training set
undersample_train = pd.concat([spam, undersample])

然后我应用所选择的算法:

full_result = pd.DataFrame(columns = ['Preprocessing', 'Model', 'Precision', 'Recall', 'F1-score', 'Accuracy'])

X, y = BOW(undersample_train)
full_result = full_result.append(training_naive(X_train, X_test, y_train, y_test, 'Count Vectorize'), ignore_index = True)

BOW 的定义如下:

def BOW(data):
    
    df_temp = data.copy(deep = True)
    df_temp = basic_preprocessing(df_temp)

    count_vectorizer = CountVectorizer(analyzer=fun)
    count_vectorizer.fit(df_temp['Text'])

    list_corpus = df_temp["Text"].tolist()
    list_labels = df_temp["Label"].tolist()
    
    X = count_vectorizer.transform(list_corpus)
    
    return X, list_labels

basic_preprocessing的定义如下:

def basic_preprocessing(df):
    
    df_temp = df.copy(deep = True)
    df_temp = df_temp.rename(index = str, columns = {'Clean_Titles_2': 'Text'})
    df_temp.loc[:, 'Text'] = [text_prepare(x) for x in df_temp['Text'].values]
    
    #le = LabelEncoder()
    #le.fit(df_temp['medical_specialty'])
    #df_temp.loc[:, 'class_label'] = le.transform(df_temp['medical_specialty'])
    
    tokenizer = RegexpTokenizer(r'\w+')
    df_temp["Tokens"] = df_temp["Text"].apply(tokenizer.tokenize)
    
    return df_temp

text_prepare 是一个函数名:

def text_prepare(text):

    REPLACE_BY_SPACE_RE = re.compile('[/(){}\[\]\|@,;]')
    BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')
    STOPWORDS = set(stopwords.words('english'))
    
    text = text.lower()
    text = REPLACE_BY_SPACE_RE.sub('', text) # replace REPLACE_BY_SPACE_RE symbols by space in text
    text = BAD_SYMBOLS_RE.sub('', text) # delete symbols which are in BAD_SYMBOLS_RE from text
    words = text.split()
    i = 0
    while i < len(words):
        if words[i] in STOPWORDS:
            words.pop(i)
        else:
            i += 1
    text = ' '.join(map(str, words))# delete stopwords from text
    
    return text

and

def training_naive(X_train_naive, X_test_naive, y_train_naive, y_test_naive, preproc):
    
    clf = MultinomialNB() # Gaussian Naive Bayes
    clf.fit(X_train_naive, y_train_naive)

    res = pd.DataFrame(columns = ['Preprocessing', 'Model', 'Precision', 'Recall', 'F1-score', 'Accuracy'])
    
    y_pred = clf.predict(X_test_naive)
    
    f1 = f1_score(y_pred, y_test_naive, average = 'weighted')
    pres = precision_score(y_pred, y_test_naive, average = 'weighted')
    rec = recall_score(y_pred, y_test_naive, average = 'weighted')
    acc = accuracy_score(y_pred, y_test_naive)
    
    res = res.append({'Preprocessing': preproc, 'Model': 'Naive Bayes', 'Precision': pres, 
                     'Recall': rec, 'F1-score': f1, 'Accuracy': acc}, ignore_index = True)

    return res 

如你所见,顺序如下:

  • 定义text_prepare用于文本清洗;
  • 定义basic_preprocessing;
  • 定义BOW;
  • 将数据集分为训练集和测试集;
  • 应用抽样;
  • 应用算法。

我不明白的是如何正确编码文本才能使算法正常工作。 我的数据集名为df,列分别为:

Label      Text                                 Year
1         bla bla bla                           2000
0         add some words                        2012
1         this is just an example               1998
0         unfortunately the code does not work  2018
0         where should I apply the encoding?    2000
0         What am I missing here?               2005

我在应用BOW时的顺序有误,因为我收到了以下错误:ValueError: could not convert string to float: 'Expect a good results if ... ' 我按照这个链接中的步骤(和代码)进行操作:kaggle.com/ruzarx/oversampling-smote-and-adasyn 。 然而,采样部分是错误的,因为应该只对训练集进行采样,所以应该在拆分之后进行。原则应该是:(1)拆分训练/测试;(2)在训练集上应用重新采样,使模型训练具有平衡的数据; (3)将模型应用于测试集并在其上进行评估。
我很乐意提供进一步的信息、数据和/或代码,但我认为我已经提供了所有最相关的步骤。
非常感谢。

你能提供完整的回溯吗?BOW函数中哪一行抛出了错误? - Lars Vagnes
我认为问题出在count_vectorizer = CountVectorizer(analyzer=fun)这行代码中的fun函数。这是可能的吗? - Ezriel_S
我认为这不是由于fun函数引起的,因为它只是如下定义的:def fun(text): remove_punc = [c for c in text if c not in string.punctuation] remove_punc = ''.join(remove_punc) cleaned = [w for w in remove_punc.split() if w.lower() not in stopwords.words('english')] return cleaned - LdM
从我看来,问题似乎出在这一行 count_vectorizer.fit(df_temp['Text']) 上,您正在将文本数据传递给算法,但它只能处理整数或浮点数数据。在传递给拟合函数之前,您应该使用某种编码方式。您可以尝试使用 LabelEncoderOneHotEncoder - Chandan
同时,最好使用最少的代码来复现你的问题,简化你的问题描述。否则你可能会失去你的问题的潜在回答者。 - Venkatachalam
显示剩余2条评论
1个回答

4

您需要一个测试BOW函数,它应该重用在训练阶段构建的计数向量化模型。

考虑使用流水线来减少代码冗长。

from sklearn.naive_bayes import MultinomialNB
import string
from nltk.corpus import stopwords
import re
from sklearn.model_selection import train_test_split
from io import StringIO
from sklearn.feature_extraction.text import CountVectorizer
from nltk.tokenize import RegexpTokenizer
from sklearn.utils import resample
from sklearn.metrics import f1_score, precision_score, recall_score, accuracy_score

def fun(text):
    remove_punc = [c for c in text if c not in string.punctuation]
    remove_punc = ''.join(remove_punc)
    cleaned = [w for w in remove_punc.split() if w.lower()
               not in stopwords.words('english')]
    return cleaned
# Testing Count Vectorizer

def BOW(data):

    df_temp = data.copy(deep=True)
    df_temp = basic_preprocessing(df_temp)

    count_vectorizer = CountVectorizer(analyzer=fun)
    count_vectorizer.fit(df_temp['Text'])

    list_corpus = df_temp["Text"].tolist()
    list_labels = df_temp["Label"].tolist()

    X = count_vectorizer.transform(list_corpus)

    return X, list_labels, count_vectorizer

def test_BOW(data, count_vectorizer):

    df_temp = data.copy(deep=True)
    df_temp = basic_preprocessing(df_temp)

    list_corpus = df_temp["Text"].tolist()
    list_labels = df_temp["Label"].tolist()

    X = count_vectorizer.transform(list_corpus)

    return X, list_labels

def basic_preprocessing(df):

    df_temp = df.copy(deep=True)
    df_temp = df_temp.rename(index=str, columns={'Clean_Titles_2': 'Text'})
    df_temp.loc[:, 'Text'] = [text_prepare(x) for x in df_temp['Text'].values]


    tokenizer = RegexpTokenizer(r'\w+')
    df_temp["Tokens"] = df_temp["Text"].apply(tokenizer.tokenize)

    return df_temp


def text_prepare(text):

    REPLACE_BY_SPACE_RE = re.compile('[/(){}\[\]\|@,;]')
    BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')
    STOPWORDS = set(stopwords.words('english'))

    text = text.lower()
    # replace REPLACE_BY_SPACE_RE symbols by space in text
    text = REPLACE_BY_SPACE_RE.sub('', text)
    # delete symbols which are in BAD_SYMBOLS_RE from text
    text = BAD_SYMBOLS_RE.sub('', text)
    words = text.split()
    i = 0
    while i < len(words):
        if words[i] in STOPWORDS:
            words.pop(i)
        else:
            i += 1
    text = ' '.join(map(str, words))  # delete stopwords from text

    return text

s = """Label      Text                                 Year
1         bla bla bla                           2000
0         add some words                        2012
1         this is just an example               1998
0         unfortunately the code does not work  2018
0         where should I apply the encoding?    2000
0         What am I missing here?               2005"""


df = pd.read_csv(StringIO(s), sep='\s{2,}')


X = df[['Text']]
y = df['Label']


X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=40)

# Returning to one dataframe
training_set = pd.concat([X_train, y_train], axis=1)
# Separating classes
spam = training_set[training_set.Label == 1]
not_spam = training_set[training_set.Label == 0]

# Undersampling the majority
undersample = resample(not_spam,
                       replace=True,
                       # set the number of samples to equal the number of the minority class
                       n_samples=len(spam),
                       random_state=40)
# Returning to new training set
undersample_train = pd.concat([spam, undersample])

full_result = pd.DataFrame(columns=['Preprocessing', 'Model', 'Precision',
                                    'Recall', 'F1-score', 'Accuracy'])
train_x, train_y, count_vectorizer  = BOW(undersample_train)
testing_set = pd.concat([X_test, y_test], axis=1)
test_x, test_y = test_BOW(testing_set, count_vectorizer)



def training_naive(X_train_naive, X_test_naive, y_train_naive, y_test_naive, preproc):
    
    clf = MultinomialNB() # Gaussian Naive Bayes
    clf.fit(X_train_naive, y_train_naive)

    res = pd.DataFrame(columns = ['Preprocessing', 'Model', 'Precision', 'Recall', 'F1-score', 'Accuracy'])
    
    y_pred = clf.predict(X_test_naive)
    
    f1 = f1_score(y_pred, y_test_naive, average = 'weighted')
    pres = precision_score(y_pred, y_test_naive, average = 'weighted')
    rec = recall_score(y_pred, y_test_naive, average = 'weighted')
    acc = accuracy_score(y_pred, y_test_naive)
    
    res = res.append({'Preprocessing': preproc, 'Model': 'Naive Bayes', 'Precision': pres, 
                     'Recall': rec, 'F1-score': f1, 'Accuracy': acc}, ignore_index = True)

    return res 

full_result = full_result.append(training_naive(train_x, test_x, train_y, test_y, 'Count Vectorize'), ignore_index = True)

enter image description here


嗨@Venkatachalam,请查看更新的问题,其中包含training_naive的部分。我认为错误只在于对train进行编码,而不是test,因此当我将算法应用于测试集时,出现了错误:“ValueError:无法将字符串转换为浮点数:...”。您能否检查一下是否在应用training_naive时遇到问题?我希望以一种可行的方式进行编码,并返回分类报告(仅通过测试/预测才能实现)。非常感谢。 - LdM
1
@LdM,我认为你仍然会遇到错误,因为你正在尝试使用未转换的数据来拟合和测试朴素贝叶斯模型。通过使用BOW函数X,y = BOW(undersample_train)进行转换的数据实际上并没有被使用:full_result = full_result.append(training_naive(X_train, X_test, y_train, y_test, 'Count Vectorize'), ignore_index = True)。所以在@Venkatachalam的答案中,朴素贝叶斯模型正在传递转换后的数据,这就是为什么现在可以工作的原因。 - user6386471
2
正如 @Venkatachalam 指出的,正确使用计数向量化器的方法是将其拟合到训练语料库(以便它获取训练数据的词汇),然后使用相同的计数向量化器模型转换训练和测试文档。拟合训练数据而不是测试数据的目的是获得分类模型在实际数据上的代表性泛化误差——如果未看到的数据中有新的词汇,我们希望了解这如何影响分类模型的性能。 - user6386471

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