Kaggle Bag of Words Meets Bags of Popcorn 文本情感分类

1. 数据描述

  数据集为IMDB影评数据,每条评论根据正负态度标注为0或1。数据集包括已标注数据集、未标注的额外训练数据集、测试数据集以及提交样例。

2. 使用方法

  针对文本分类问题,首先需要对文本进行预处理,之后对文本进行特征提取,最后使用机器学习里面的分类器或RNN/CNN神经网络对文本分类。

3. 具体实现

3.1 文本预处理

  由于数据集为英文文本,因此可以根据空格直接对评论进行分词。由于数据中包含一些HTML标签,因此需要对HTML标签以及其他一些非英文字符去除。可以通过使用BeautifulSoup以及正则表达式替换操作完成。

def clean_stopwords(df: pd.DataFrame) -> pd.DataFrame:
    reviews_len = len(df['review'].values)
    for i in range(reviews_len):
        review = df.loc[i, 'review']
        review = BeautifulSoup(review, features='lxml').get_text().lower()
        review = re.sub('[^a-zA-Z]', ' ', review).split(' ')
        review = [w for w in review if w not in stopwords.words('english')]
        review = ' '.join(review)
        df.loc[i, 'review'] = review
    return df

  除了对文本进行清理外,由于因为语法、时态等原因,一些单词会以不同的形式出现,比如ateeatgoodbest等,需要将它们还原到原始形式。使用nltk包中的WordNetLemmatizer可以对英文单词的词性进行还原,但是使用该方法对词性还原前需要知道该单词当前的词性。比如还原friendly需要在方法中添加参数r表示当前的单词为副词形式。获取单词词性的方法可以参考[1]。不过由于获取的词性标签不能直接传入作为参数,因此需要进行一下转换:

def get_word_net_pos(tag: str):
    if tag.startswith('J'):
        return 'a'
    elif tag.startswith('V'):
        return 'v'
    elif tag.startswith('R'):
        return 'r'
    elif tag.startswith('N'):
        return 'n'
    else:
        return None

标签以及对应的字符串的对应关系可以查看nltk的文档[2]

3.2 文本特征

  由于分类器、神经网络需要输入数值型的数据,因此需要将文本转换为向量形式。常用的文本表示方法有: 1 ◯ \raisebox{.5pt}{\textcircled{\raisebox{-.9pt} {1}}} 1基于one-hottf-idf的词袋模型(bag of word); 2 ◯ \raisebox{.5pt}{\textcircled{\raisebox{-.9pt} {2}}} 2基于文本主题模型的SVDLDA等; 3 ◯ \raisebox{.5pt}{\textcircled{\raisebox{-.9pt} {3}}} 3基于词向量静态特征的word2vecfasttext等; 4 ◯ \raisebox{.5pt}{\textcircled{\raisebox{-.9pt} {4}}} 4基于词向量的动态特征的BERTGPT[3]
  本次文本情感分类尝试使用了词袋模型、word2vec以及fasttext对文本进行表示。

3.3 文本表示及分类

  共尝试使用词袋模型+tensorflowword2vec+分类器stacking以及fasttext+Keras分别对待测文本进行分类。其中前两种方法未能取得较高的分类效果,第三种方法的效果较好,分别在训练集以及验证集上取得90%以及87%左右的准确率。

3.3.1 词袋模型+tensorflow

  基本方法是首先根据TF-IDF对训练语料库中的词语进行过滤,将TF-IDF值较低的词语过滤掉。之后创建一个词库(list列表),其中下标0位置置为unk意为未登录词。之后对训练文本根据此词库转换为对应的向量。
  由于很多评论较长,因此采取将评论分为一个个的句子,对句子进行编码。之后对每个句子使用0padding,补齐长度至64,同时每个评论的句子个数也要padding。(也许这样麻烦了一点,直接对整个评论编码、padding会更好?)。最后训练数据x的shape为[review_num,sentence_num,sentence_len]time_step设置为sentence_num,关于time_step可以参考[4]
  完整代码如下:

# -*- coding:utf-8 -*-
import pandas as pd
import tensorflow as tf
from nltk.corpus import stopwords
from bs4 import BeautifulSoup
import re
import nltk
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
from os import listdir, path
import pickle

nltk.download('stopwords')
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
nltk.download('wordnet')

train = pd.read_csv('labeledTrainData.tsv', delimiter='\t', header=0)
train = train[:2000]
test = pd.read_csv('testData.tsv', delimiter='\t', header=0)
test = test[:400]


def clean_stopwords(df: pd.DataFrame) -> pd.DataFrame:
    """
    去除停用词、HTML标签
    :param df:
    :return:
    """
    assert 'review' in df.columns, 'Error: DataFrame doesn\'t has review column'
    review_len = len(df['review'].values)
    for i_cl in range(review_len):
        review = df.iloc[i_cl]['review']
        review = BeautifulSoup(review, features='lxml').get_text()  # 去除HTML标签
        review = re.sub('[^a-zA-Z]', ' ', review).lower()
        review = review.split(' ')
        review = [w for w in review if review not in stopwords.words('english')]
        review = ' '.join(review)
        df.loc[i_cl, 'review'] = review
    return df


# 预处理文本内容
if 'train.pkl' not in listdir('.'):
    train = clean_stopwords(train)
    with open('train.pkl', 'wb') as fw:
        pickle.dump(train, fw)
else:
    with open('train.pkl', 'rb') as fr:
        train = pickle.load(fr)

if 'test.pkl' not in listdir('.'):
    test = clean_stopwords(test)
    with open('test.pkl', 'wb') as fw:
        pickle.dump(test, fw)
else:
    with open('test.pkl', 'rb') as fr:
        test = pickle.load(fr)


def get_wordnet_pos(tag: str):
    if tag.startswith('J'):  # 形容词
        return 'a'
    else:
        return tag[0].lower()


def preprocess_sentence(paragraph: str) -> list:
    """
    将review段落转换成句子列表,并使用nltk还原词形
    :param paragraph:
    :return:
    """
    sentence_list = paragraph.split('  ')
    c
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值