Word2Ve c - Модель с высокой оценкой перекрестной проверки работает очень плохо с тестовыми данными - PullRequest
1 голос
/ 13 июля 2020

Работая над анализом тональности данных твиттера, я столкнулся с проблемой, которую просто не могу решить. Я хотел обучить классификатор RandomForest для обнаружения языка вражды. Поэтому я использовал маркированный набор данных с твитами, которые помечены как 1 для разжигания ненависти и 0 для обычных твитов. Для векторизации я использую Word2Ve c. Сначала я провел гиперпараметризацию, чтобы найти хорошие параметры для классификатора. Во время гиперпараметризации я использовал многократную стратифицированную перекрестную проверку KFold (оценка = точность). Средняя точность здесь составляет около 99,6%. Однако, как только я применяю модель к набору тестовых данных и строю матрицу путаницы, точность оказывается чуть выше 50%, что, конечно, ужасно для двоичного классификатора. Я успешно использую тот же подход с Bag of Words, и здесь у меня не было никаких проблем. Может ли кто-нибудь взглянуть на мой код? Это было бы так полезно. Я просто не могу найти, что не так. Большое спасибо!

(Я также загрузил код в Google Collab на случай, если вам будет проще: https://colab.research.google.com/drive/15BzElijL3vwa_6DnLicxRvcs4SPDZbpe?usp=sharing)

Сначала я предварительно обработал свои данные:

train_csv = pd.read_csv(r'/content/drive/My Drive/Colab Notebooks/MLDA_project/data2/train.csv')
train = train_csv     
#check for missing values (result shows that there are no missing values)
train.isna().sum()    
# remove the tweet IDs
train.drop(train.columns[0], axis = "columns", inplace = True)    
# create a new column to save the cleansed tweets
train['training_tweet'] = np.nan

# remove special/unknown characters
train.replace('[^a-zA-Z#]', ' ', inplace = True, regex = True)    
# generate stopword list and add the twitter handles "user" to the stopword list
stopwords = sw.words('english')
stopwords.append('user')    
# convert to lowercase
train = train.applymap(lambda i:i.lower() if type(i) == str else i)    
# execute tokenization and lemmatization
lemmatizer = WordNetLemmatizer()

for i in range(len(train.index)):
    #tokenize the tweets from the column "tweet"
    words = nltk.word_tokenize(train.iloc[i, 1])
    #consider words with more than 3 characters
    words = [word for word in words if len(word) > 3] 
    #exclude words in stopword list
    words = [lemmatizer.lemmatize(word) for word in words if word not in set(stopwords)] 
    #Join words again
    train.iloc[i, 2]  = ' '.join(words)  
    words = nltk.word_tokenize(train.iloc[i, 2])
train.drop(train.columns[1], axis = "columns", inplace = True)

majority = train[train.label == 0]
minority = train[train.label == 1]
# upsample minority class
minority_upsampled = resample(minority, replace = True, n_samples = len(majority))      
# combine majority class with upsampled minority class
train_upsampled = pd.concat([majority, minority_upsampled])
train = train_upsampled
np.random.seed(10)
train = train.sample(frac = 1)
train = train.reset_index(drop = True)

Теперь train имеет метки в столбце 0 и предварительно обработанные твиты в столбце 1.

Затем я определил Word2Ve c Vectorizer:

def W2Vvectorize(X_train):
tokenize=X_train.apply(lambda x: x.split())
w2vec_model=gensim.models.Word2Vec(tokenize,min_count = 1, size = 100, window = 5, sg = 1)
w2vec_model.train(tokenize,total_examples= len(X_train), epochs=20)
w2v_words = list(w2vec_model.wv.vocab)
vector=[]
from tqdm import tqdm
for sent in tqdm(tokenize):
    sent_vec=np.zeros(100)
    count =0
    for word in sent: 
        if word in w2v_words:
            vec = w2vec_model.wv[word]
            sent_vec += vec 
            count += 1
    if count != 0:
        sent_vec /= count #normalize
    vector.append(sent_vec)
return vector

Я разделил набор данных на тестовый и обучающий набор и векторизовал оба подмножества, используя W2V, как определено выше:

x = train["training_tweet"]
y = train["label"]

X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, stratify=train['label'])

print('X Train Shape = total * 0,8 =', X_train.shape)
print('y Train Shape = total * 0,8 =', y_train.shape)
print('X Test Shape = total * 0,2 =', X_test.shape)
print('y Test Shape = total * 0,2 =', y_test.shape) # change 0,4 & 0,6

train_tf_w2v = W2Vvectorize(X_train)
test_tf_w2v = W2Vvectorize(X_test)

Теперь я выполняю гиперпараметризацию:

# define models and parameters
model = RandomForestClassifier()
n_estimators = [10, 100, 1000]
max_features = ['sqrt', 'log2']
# define grid search
grid = dict(n_estimators=n_estimators,max_features=max_features)
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
grid_search = GridSearchCV(estimator=model, param_grid=grid, n_jobs=-1, cv=cv, scoring='accuracy',error_score=0)
grid_result = grid_search.fit(train_tf_w2v, y_train)
# summarize results
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

Это приводит к следующий вывод:

Best: 0.996628 using {'max_features': 'log2', 'n_estimators': 1000}
0.995261 (0.000990) with: {'max_features': 'sqrt', 'n_estimators': 10}
0.996110 (0.000754) with: {'max_features': 'sqrt', 'n_estimators': 100}
0.996081 (0.000853) with: {'max_features': 'sqrt', 'n_estimators': 1000}
0.995885 (0.000872) with: {'max_features': 'log2', 'n_estimators': 10}
0.996481 (0.000691) with: {'max_features': 'log2', 'n_estimators': 100}
0.996628 (0.000782) with: {'max_features': 'log2', 'n_estimators': 1000}

Затем я хотел нарисовать матрицу путаницы с тестовыми данными, используя модель:

clf = RandomForestClassifier(max_features = 'log2', n_estimators=1000) 
   
clf.fit(train_tf_w2v, y_train)
name = clf.__class__.__name__
        
expectation = y_test
test_prediction = clf.predict(test_tf_w2v)
acc = accuracy_score(expectation, test_prediction)   
pre = precision_score(expectation, test_prediction)
rec = recall_score(expectation, test_prediction)
f1 = f1_score(expectation, test_prediction)

fig, ax = plt.subplots(1,2, figsize=(14,4))
plt.suptitle(f'{name} \n', fontsize = 18)
plt.subplots_adjust(top = 0.8)
skplt.metrics.plot_confusion_matrix(expectation, test_prediction, ax=ax[0])
skplt.metrics.plot_confusion_matrix(expectation, test_prediction, normalize=True, ax = ax[1])
plt.show()
    
print(f"for the {name} we receive the following values:")
print("Accuracy: {:.3%}".format(acc))
print('Precision score: {:.3%}'.format(pre))
print('Recall score: {:.3%}'.format(rec))
print('F1 score: {:.3%}'.format(f1))

Это выводит:

матрица путаницы

для RandomForestClassifier мы получаем следующие значения: Точность: 57,974% Оценка точности: 99,790% Оценка отзыва: 15,983% Оценка F1: 27,552%

Ответы [ 2 ]

2 голосов
/ 14 июля 2020

Ууу ... Теперь я чувствую себя глупо. Я обнаружил, что было не так.

После разделения на поезд / тест я отправил оба подмножества независимо в функцию W2Vvectorize().

train_tf_w2v = W2Vvectorize(X_train)
test_tf_w2v = W2Vvectorize(X_test)

Оттуда функция W2Vvectorize() обучает два независимые модели Word2Ve c, основанные на двух независимых подмножествах. Следовательно, когда я передаю векторизованные тестовые данные test_tf_w2v моему обученному классификатору RandomForest, чтобы проверить правильность точности и для тестового набора, он отображается для обученного классификатора RandomForest, как если бы тестовый набор был на другом языке . Две отдельные модели word2ve c просто векторизуются по-разному.

Я решил это следующим образом:

def W2Vvectorize(X_train):
    tokenize=X_train.apply(lambda x: x.split())
    vector=[]
    for sent in tqdm(tokenize):
        sent_vec=np.zeros(100)
        count =0
        for word in sent: 
            if word in w2v_words:
                vec = w2vec_model.wv[word]
                sent_vec += vec 
                count += 1
        if count != 0:
            sent_vec /= count #normalize
        vector.append(sent_vec)
    return vector

И обучение Word2Ve c отдельно от этого:

x = train["training_tweet"]
y = train["label"]

X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, stratify=train['label'])

print('X Train Shape = total * 0,8 =', X_train.shape)
print('y Train Shape = total * 0,8 =', y_train.shape)
print('X Test Shape = total * 0,2 =', X_test.shape)
print('y Test Shape = total * 0,2 =', y_test.shape) #

tokenize=X_train.apply(lambda x: x.split())
w2vec_model=gensim.models.Word2Vec(tokenize,min_count = 1, size = 100, window = 5, sg = 1)
w2vec_model.train(tokenize,total_examples= len(X_train), epochs=20)
w2v_words = list(w2vec_model.wv.vocab)

train_tf_w2v = W2Vvectorize(X_train)
test_tf_w2v = W2Vvectorize(X_test)

Итак, обучение моделей Word2Ve c выполняется только на обучающих данных. Однако векторизация тестовых данных должна выполняться с использованием той же самой модели Word2Ve c.

0 голосов
/ 15 июля 2020

Просто для полноты: причина слишком высокой точности заключалась в том, что я сбалансировал набор данных, чтобы иметь равное распределение классов в окончательном наборе обучения. Поэтому я увеличил выборку класса с меньшим количеством данных. Это, конечно, означает, что впоследствии набор данных будет содержать данные с повышенной дискретизацией несколько раз. Если вы, как я это сделал, отделили тестовые данные после повышающей дискретизации, очень, очень вероятно, что ваши обучающие данные будут содержать элементы из данных после повышающей дискретизации. Я нарисовал картинку, поясняющую это: Никогда не повышайте дискретизацию перед разделением на обучение / тест

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...