赞
踩
最近在研究情感分析,感谢CSDN上很多博主的文章,让我受益匪浅。因此在跑出准确率高达88%的分类结果后,写下自己的代码和总结,希望对大家有所帮助~
情感分析主要涉及两个数据集,一个是人工标注好的数据集,即包含了情感标签和文本的数据集A,另一个是用于情感分析的原始数据集,即只包含文本的数据集B。数据集A用于训练和测试,数据集B用于得到想要的情感分析结果。
本文数据集A为斯坦福大学的Sentiment140数据集,包含了160万条标注情感极性的推文。数据集B为我从TwitterAPI上爬取的两个月推文。
预处理主要是处理数据集B中的原始数据和数据集A中的文本,目的是将包含网页链接、标点符号及无意义单词的杂乱数据转化为干净的由有意义单词组成的字符串,并使用pandas dataframe存储。
初始爬取到的推文为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
使用正则表达式去除http,https网页链接
import re
def remove_urls(vTEXT):
vTEXT = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', vTEXT, flags=re.MULTILINE)
return (vTEXT)
使用ASCII字符串的取值范围判断是否是英文字符串
def judge_english(text):
return all(ord(c) < 128 for c in text) #包括英文文本、英文标点符号
这里使用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
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
使用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')
不同于处理数据集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中封装的模型进行训练
分别使用词袋模型和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)
将数据集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) #划分测试集和训练集
本文选取五种算法模型: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()
为了防止出错和精简代码,本文使用pipe管道封装文本特征表示和算法模型两个步骤,后续直接使用pipe拟合即可,这里以串联词袋模型和朴素贝叶斯为例,也可以串联词袋模型和随机森林等等,根据需要串联即可。
from sklearn.pipeline import make_pipeline
pipe = make_pipeline(bow_vectorizer, Nb) #词袋模型串联朴素贝叶斯算法
pipe.fit(x_train, y_train)
y_pred = pipe.predict(x_test) #进行预测
df['pred']=y_pred #将预测结果保存到dataframe中
常用的分类算法评估指标包括:准确率(Accuracy)、精准度(Precision)、召回率(Recall)和F1值(F1-score),使用sklearn自带的metrics即可得到包含上面几个指标的分类效果评估报告。
from sklearn import metrics
print(metrics.classification_report(y_test, y_pred))
为了防止以后每次进行数据时都要进行拟合,直接使用sklearn自带的joblib即可保存训练好的模型。
from sklearn.externals import joblib
joblib.dump(pipe, 'D:/data/Bayes.pkl')
Bayes=joblib.load('D:/data/Bayes.pkl') #加载模型
y_pred = Bayes.predict(x_test) #预测结果和pipe一致
df['pred02']=y_pred #将预测结果保存到dataframe中
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') #保存模型
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') #保存模型
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。