Python基于机器学习的文本情感分析详细步骤[附代码和文字解释]

最近在研究情感分析,感谢CSDN上很多博主的文章,让我受益匪浅。因此在跑出准确率高达88%的分类结果后,写下自己的代码和总结,希望对大家有所帮助~

一、文本数据预处理

情感分析主要涉及两个数据集,一个是人工标注好的数据集,即包含了情感标签和文本的数据集A,另一个是用于情感分析的原始数据集,即只包含文本的数据集B。数据集A用于训练和测试,数据集B用于得到想要的情感分析结果。

本文数据集A为斯坦福大学的Sentiment140数据集,包含了160万条标注情感极性的推文。数据集B为我从TwitterAPI上爬取的两个月推文。

预处理主要是处理数据集B中的原始数据和数据集A中的文本,目的是将包含网页链接、标点符号及无意义单词的杂乱数据转化为干净的由有意义单词组成的字符串,并使用pandas dataframe存储。

1、读取json并转化为列表

初始爬取到的推文为json格式,因此需要读取json文件,并将其中的每条推文存储到列表中,目标格式为 [‘今天天气真好’,‘This is my bag’]

import json

def get_tweets(filepath):
    tweets = []
    file = open(filepath, 'r')
    for line in file.readlines():#由于json为多层嵌套,所以分行读取
    	dic = json.loads(line) #将json转化为字典
    	text = str(dic.get('text', '!')) #如果字典中'text'键为空,则返回'!',不处理会返回None
    	tweets.append(text)
    return tweets

2、文本清洗与处理

1)去除网页链接

使用正则表达式去除http,https网页链接

import re

def remove_urls(vTEXT):
	vTEXT = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', vTEXT, flags=re.MULTILINE)
	return (vTEXT)

2)判断字符串是否全是英文

使用ASCII字符串的取值范围判断是否是英文字符串

def judge_english(text):
	return all(ord(c) < 128 for c in text) #包括英文文本、英文标点符号

3)分词、识别英文单词、去除标点符号

  • 这里使用nltk的TweetTokenizer进行英文分词,strip_handles参数表示删除句柄,即@Justin这种格式的短语,如果没有删除句柄的需求可以使用nltk的tokenize,将字符串分词为列表

  • 使用enchant检查字符串是否是英文单词,这是比nltk更全面的英文词典库,但是enchant将 They’re 这种缩略词也判定为英文单词,因此使用 isalpha() 函数加一层过滤,筛选掉含有标点符号的字符串,只保留纯英文字符串

import enchant
from nltk.tokenize import TweetTokenizer

def get_word(text):
	USdict = enchant.Dict("en_US") #英语词典
    text = text.replace('.', ' ') #将句号替换为空格
    tknzr = TweetTokenizer(strip_handles=True, reduce_len=True) #删除句柄
    rawwords = tknzr.tokenize(text) #分词
    words = [word.lower() for word in rawwords if USdict.check(word) and word.isalpha() and len(word) > 2]  #判断是否是长度大于2的英文单词并转化为小写字母
    return words

4)词性标注、词形还原、停用词过滤

  • 相较于词干提取,词形还原的结果更具有可读性,词干提取把词的后缀删减掉,比如luckily缩减为lucki,而lucki并不是单词,词形还原则是将ate还原为eat,将luckily还原为lucky,还原结果仍是英文单词。
  • 词形还原依赖于对词性进行准确的标注,不进行标注的话函数默认词性为名词进行还原,还原结果并不好,如wnl.lemmatize(‘ate’)结果仍是‘ate’,标注动词词性后wnl.lemmatize(‘ate’,‘V’)还原为‘eat’
  • 停用词过滤使用nltk的英文停用词词典
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from nltk import pos_tag
from nltk.corpus import wordnet

stoplist = set(stopwords.words('english'))#停用词词典

def get_pos_word(words):
	# 
    def get_wordnet_pos(tag):
        if tag.startswith('J'):
            return wordnet.ADJ
        elif tag.startswith('V'):
            return wordnet.VERB
        elif tag.startswith('N'):
            return wordnet.NOUN
        elif tag.startswith('R'):
            return wordnet.ADV
        else:
            return None

    words = pos_tag(words) #词性标注

    pos_word = [wnl.lemmatize(tag[0], pos=get_wordnet_pos(tag[1]) or wordnet.NOUN) for tag in words] #词形还原

    # 停用词过滤
    cleanwords = [word for word in pos_word if word not in stoplist]

    return cleanwords

3、处理数据集B完整代码及主函数

使用pandas dataframe存储处理后的文本数据并保存为csv格式文件

import json
import re
import enchant
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
import pandas as pd
from nltk import pos_tag
from nltk.corpus import wordnet
from nltk.tokenize import TweetTokenizer

wnl = WordNetLemmatizer()
USdict = enchant.Dict("en_US")
stoplist = set(stopwords.words('english'))

# 获取处理后的英文推特
def get_tweets(filepath):
    tweets = []
    file = open(filepath, 'r')

    # 去除链接
    def remove_urls(vTEXT):
        vTEXT = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', vTEXT, flags=re.MULTILINE)
        return (vTEXT)

    # 筛选英文
    def judge_english(text):
        return all(ord(c) < 128 for c in text)

    # 获取文本
    for line in file.readlines():
        dic = json.loads(line)
        text = str(dic.get('text', '!'))
        if judge_english(text):
            tweets.append(remove_urls(text))

    return tweets


def get_word(text):
    text = text.replace('.', ' ')
    tknzr = TweetTokenizer(strip_handles=True, reduce_len=True)
    rawwords = tknzr.tokenize(text)
    words = [word.lower() for word in rawwords if USdict.check(word) and word.isalpha() and len(word) > 2]
    return words


# 词性还原
def get_pos_word(words):
    def get_wordnet_pos(tag):
        if tag.startswith('J'):
            return wordnet.ADJ
        elif tag.startswith('V'):
            return wordnet.VERB
        elif tag.startswith('N'):
            return wordnet.NOUN
        elif tag.startswith('R'):
            return wordnet.ADV
        else:
            return None

    words = pos_tag(words)

    pos_word = [wnl.lemmatize(tag[0], pos=get_wordnet_pos(tag[1]) or wordnet.NOUN) for tag in words]

    # 停用词过滤
    cleanwords = [word for word in pos_word if word not in stoplist]

    return cleanwords


if __name__ == '__main__':
	filepath='D:/data/test.json' 
	storename = 'D:/data/test.csv' 
	tweets = get_tweets(filepath)

	df = pd.DataFrame()
	df['tweets'] = tweets
	# 分词
	df['tweets'] = df['tweets'].apply(get_word)
	# 词形还原
	df['tweets'] = df['tweets'].apply(get_pos_word)
	# 删除tweets中的空列表
	df = df[~(df['tweets'].str.len() == 0)]
	# 将列表转换为字符串
	df['tweets'] = df['tweets'].apply(lambda x: ' '.join(x))
	# 保存文本
	df.to_csv(storename, encoding='utf-8')

4、处理数据集A的完整代码与主函数

不同于处理数据集B,数据集A为行列数据整齐的英文文本,但是没有列名,且有几列多余数据,因此主要加入了对csv格式文件的读取和处理,注意本文使用的数据集A编码格式为 ISO-8859-1,大家读取csv文件 读取csv文件 encoding=‘ISO-8859-1’ 报错时改成 encoding=‘utf-8’

import enchant
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from nltk import pos_tag
from nltk.corpus import wordnet
from nltk.tokenize import TweetTokenizer
import pandas as pd
import re

wnl = WordNetLemmatizer()
USdict = enchant.Dict("en_US")
stoplist = set(stopwords.words('english'))

def remove_urls(vTEXT):
    vTEXT = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', vTEXT, flags=re.MULTILINE)
    return (vTEXT)


def get_word(text):
    text = text.replace('.', ' ')
    tknzr = TweetTokenizer(strip_handles=True, reduce_len=True)
    rawwords = tknzr.tokenize(text)
    words = [word.lower() for word in rawwords if USdict.check(word) and not str.isdigit(word) and len(word) > 2]
    return words


def get_pos_word(words):
    def get_wordnet_pos(tag):
        if tag.startswith('J'):
            return wordnet.ADJ
        elif tag.startswith('V'):
            return wordnet.VERB
        elif tag.startswith('N'):
            return wordnet.NOUN
        elif tag.startswith('R'):
            return wordnet.ADV
        else:
            return None

    words = pos_tag(words)

    pos_word = [wnl.lemmatize(tag[0], pos=get_wordnet_pos(tag[1]) or wordnet.NOUN) for tag in words]

    # 停用词过滤
    cleanwords = [word for word in pos_word if word not in stoplist]

    return cleanwords
    
if __name__ == '__main__':
	file = pd.read_csv('D:/data/Train.csv', encoding='ISO-8859-1',
	                   header=None, names=['label', 'id', 'day', 'query', 'user', 'tweets']) #pandas dataframe自定义列表名
	                   
	file = file.drop(file.columns[1:5], axis=1) #删除多余列
	
	#去除链接
	df['tweets'] = df['tweets'].apply(remove_urls)
	
	# 分词
	df['tweets'] = df['tweets'].apply(get_word)
	
	# 文本处理结果
	df['tweets'] = df['tweets'].apply(get_pos_word)
	
	# 删除tweets中的空列表
	df = df[~(df['tweets'].str.len() == 0)]
	
	# 转换字符串
	df['tweets'] = df['tweets'].apply(lambda x: ' '.join(x))
	
	# 打乱顺序
	df = df.sample(frac=1.0).reset_index(drop=True)
	
	#保存文本
	df.to_csv("D:\data\Test5000.csv", encoding='utf-8',index=None)

二、训练算法模型

本文主要使用sklearn中封装的模型进行训练

1、文本特征表示

分别使用词袋模型和TF-IDF进行文本特征表示,max_features参数是指选取的特征数最大值,大家根据各自的数据情况和需要制定最大值

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer

#词袋模型
bow_vectorizer = CountVectorizer(max_df=0.80, min_df=2, max_features=5000)

# TF-IDF feature
tfidf_vectorizer = TfidfVectorizer(max_df=0.80, min_df=2, max_features=5000)

2、训练算法模型

1)随机划分数据集和训练集

将数据集A中人工标注的标签和文本分别作为因变量y和自变量x,并使用sklearn中的train_test_split进行随机划分,test_size参数为测试集比例,一般选择0.2,即80%训练集,20%测试集

from sklearn.model_selection import train_test_split

df = pd.read_csv("D:\data\Train.csv", encoding='utf-8')
x=df['tweets'] #自变量
y=df['label']  #因变量

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=1) #划分测试集和训练集

2)导入算法模型

本文选取五种算法模型:K近邻、逻辑回归、随机森林、支持向量机、朴素贝叶斯进行模型训练

from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import SGDClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier

# KNN Classifier K近邻算法
Knn = KNeighborsClassifier()

# Logistic Regression Classifier 逻辑回归
Lr = LogisticRegression()

# Random Forest Classifier 随机森林
Rf = RandomForestClassifier()

# SVM Classifier 支持向量机
Svm = SGDClassifier()

# Naive Bayes 朴素贝叶斯
Nb = MultinomialNB()

3)训练算法模型

为了防止出错和精简代码,本文使用pipe管道封装文本特征表示和算法模型两个步骤,后续直接使用pipe拟合即可,这里以串联词袋模型和朴素贝叶斯为例,也可以串联词袋模型和随机森林等等,根据需要串联即可。

from sklearn.pipeline import make_pipeline

pipe = make_pipeline(bow_vectorizer, Nb) #词袋模型串联朴素贝叶斯算法
pipe.fit(x_train, y_train)

4)预测测试集中的文本情感

y_pred = pipe.predict(x_test) #进行预测
df['pred']=y_pred #将预测结果保存到dataframe中

5)评估分类效果

常用的分类算法评估指标包括:准确率(Accuracy)、精准度(Precision)、召回率(Recall)和F1值(F1-score),使用sklearn自带的metrics即可得到包含上面几个指标的分类效果评估报告。

from sklearn import metrics
print(metrics.classification_report(y_test, y_pred))

3、保存及调用训练好的算法模型

1)保存训练好的模型

为了防止以后每次进行数据时都要进行拟合,直接使用sklearn自带的joblib即可保存训练好的模型。

from sklearn.externals import joblib
joblib.dump(pipe, 'D:/data/Bayes.pkl')

2)调用之前保存的模型

Bayes=joblib.load('D:/data/Bayes.pkl') #加载模型
y_pred = Bayes.predict(x_test) #预测结果和pipe一致
df['pred02']=y_pred #将预测结果保存到dataframe中

4、词袋模型和贝叶斯模型串联的完整代码

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
from sklearn.externals import joblib

df = pd.read_csv("D:\data\Train.csv", encoding='utf-8')
df = df.sample(frac=1.0).reset_index(drop=True)

x=df['tweets'] #自变量
y=df['label']  #因变量

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=1) #划分测试集和训练集

# 词袋模型
bow_vectorizer = CountVectorizer(max_df=0.80, min_df=2)

#贝叶斯模型
Nb = MultinomialNB()
pipe = make_pipeline(bow_vectorizer, Nb)
pipe.fit(x_train, y_train)

y_pred = pipe.predict(x_test) #预测

df['pred']=y_pred #保存预测结果到dataframe中

print(metrics.classification_report(y_test, y_pred)) #评估

joblib.dump(pipe, 'D:/data/Bayes.pkl') #保存模型

5、TF-IDF和支持向量机串联预测的完整代码

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import make_pipeline
from sklearn.externals import joblib

df = pd.read_csv("D:\data\Train.csv", encoding='utf-8')
df = df.sample(frac=1.0).reset_index(drop=True)

x=df['tweets'] #自变量
y=df['label']  #因变量

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=1) #划分测试集和训练集

# TF-IDF
tfidf_vectorizer = TfidfVectorizer(max_df=0.80, min_df=2)

# SVM Classifier
Svm = SGDClassifier()
pipe = make_pipeline(tfidf_vectorizer, Svm)
pipe.fit(x_train, y_train)

y_pred = pipe.predict(x_test) #预测
df['pred']=y_pred #保存预测结果到dataframe中

print(metrics.classification_report(y_test, y_pred)) #评估

joblib.dump(pipe, 'D:/data/SVM.pkl') #保存模型
  • 54
    点赞
  • 452
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值