春衣少年当酒歌,起舞四顾以笑和。这篇文章主要讲述基于机器学习和TFIDF的情感分类算法,详解自然语言处理相关的知识,希望能为你提供帮助。
摘要:这篇文章将详细讲解自然语言处理过程,基于机器学习和TFIDF的情感分类算法,并进行了各种分类算法(SVM、RF、LR、Boosting)对比本文分享自华为云社区??《[Python人工智能] 二十三.基于机器学习和TFIDF的情感分类(含详细的NLP数据清洗)》??,作者: eastmount。
在数据分析和数据挖掘中,通常需要经历前期准备、数据爬取、数据预处理、数据分析、数据可视化、评估分析等步骤,而数据分析之前的工作几乎要花费数据工程师近一半的工作时间,其中的数据预处理也将直接影响后续模型分析的好坏。图是数据预处理的基本步骤,包括中文分词、词性标注、数据清洗、特征提取(向量空间模型存储)、权重计算(TF-IDF)等。
一.中文分词当读者使用python爬取了中文数据集之后,首先需要对数据集进行中文分词处理。由于英文中的词与词之间是采用空格关联的,按照空格可以直接划分词组,所以不需要进行分词处理,而中文汉字之间是紧密相连的,并且存在语义,词与词之间没有明显的分隔点,所以需要借助中文分词技术将语料中的句子按空格分割,变成一段段词序列。下面开始详细介绍中文分词技术及Jiaba中文分词工具。
中文分词(Chinese Word Segmentation)指将汉字序列切分成一个个单独的词或词串序列,它能够在没有词边界的中文字符串中建立分隔标志,通常采用空格分隔。下面举个简单示例,对句子“我是程序员”进行分词操作。
输入:我是程序员
输出1:我\\是\\程\\序\\员
输出2:我是\\是程\\程序\\序员
输出3:我\\是\\程序员
简单举个例子,代码中主要导入Jieba扩展包,然后调用其函数进行中文分词。
#encoding=utf-8
import jieba
text = "北京理工大学生前来应聘"
data = https://www.songbingjia.com/android/jieba.cut(text,cut_all=True)#全模式
print("[全模式]: ", " ".join(data))
data = https://www.songbingjia.com/android/jieba.cut(text,cut_all=False)#精确模式
print("[精确模式]: ", " ".join(data))
data = https://www.songbingjia.com/android/jieba.cut(text)#默认是精确模式
print("[默认模式]: ", " ".join(data))
data = https://www.songbingjia.com/android/jieba.cut_for_search(text)#搜索引擎模式
print("[搜索引擎模式]: ", " ".join(data))
上述代码输出如下,包括全模式、精确模式和搜索引擎模式输出的结果。
二.数据清洗在分析语料的过程中,通常会存在一些脏数据或噪声词组干扰我们的实验结果,这就需要对分词后的语料进行数据清洗(Data Cleaning)。比如前面使用Jieba工具进行中文分词,它可能存在一些脏数据或停用词,如“我们”、“的”、“吗”等。这些词降低了数据质量,为了得到更好的分析结果,需要对数据集进行数据清洗或停用词过滤等操作。
- 残缺数据
- 重复数据
- 错误数据
- 停用词
下面是从大众点评、美团之类的网站抓取“黄果树瀑布”的评论信息,我们通过Jieba工具对其进行中文分词。
- 好评:5000条
- 差评:1000条
完整代码:
# -*- coding:utf-8 -*-
import csv
import pandas as pd
import numpy as np
import jieba
import jieba.analyse
#添加自定义词典和停用词典
jieba.load_userdict("user_dict.txt")
stop_list = pd.read_csv(stop_words.txt,
engine=python,
encoding=utf-8,
delimiter="\\n",
names=[t])[t].tolist()
#中文分词函数
def txt_cut(juzi):
return [w for w in jieba.lcut(juzi) if w not in stop_list]
#写入分词结果
fw = open(fenci_data.csv, "a+", newline = ,encoding = gb18030)
writer = csv.writer(fw)
writer.writerow([content,label])
# 使用csv.DictReader读取文件中的信息
labels = []
contents = []
file = "data.csv"
with open(file, "r", encoding="UTF-8") as f:
reader = csv.DictReader(f)
for row in reader:
# 数据元素获取
if row[label] == 好评:
res = 0
else:
res = 1
labels.append(res)
content = row[content]
seglist = txt_cut(content)
output =.join(list(seglist))#空格拼接
contents.append(output)
#文件写入
tlist = []
tlist.append(output)
tlist.append(res)
writer.writerow(tlist)
print(labels[:5])
print(contents[:5])
fw.close()
运行结果如下图所示,一方面它将特殊标点符号、停用词过滤,另一方面导入了user_dict.txt词典,将“黄果树瀑布”、“风景区”等专有名词分词,否则它可能会划分为“黄果树”和“瀑布”、“风景”和“区”。
- 数据清洗前
- 数据清洗后
三.特征提取及TF-IDF计算1.基本概念
权重计算是指通过特征权重来衡量特征项在文档表示中的重要程度,给特征词赋予一定的权重来衡量统计文本特征词。TF-IDF(Term Frequency-Invers Document Frequency)是近年来用于数据分析和信息处理经典的权重计算技术。该技术根据特征词在文本中出现的次数和在整个语料中出现的文档频率来计算该特征词在整个语料中的重要程度,其优点是能过滤掉一些常见却无关紧要的词语,尽可能多的保留影响程度高的特征词。
TF-IDF的计算公式如下,式中TF-IDF表示词频TF和倒文本词频IDF的乘积,TF-IDF中权重与特征项在文档中出现的频率成正比,与在整个语料中出现该特征项的文档数成反比。TF-IDF值越大则该特征词对这个文本的重要程度越高。
其中,TF词频的计算公式如下,ni,j 为特征词 ti 在训练文本 Dj 中出现的次数,分母是文本 Dj 中所有特征词的个数,计算的结果即为某个特征词的词频。
倒文档频率(Inverse Document Frequency,简称IDF)是Spark Jones在1972年提出的,用于计算词与文献相关权重的经典方法。计算公式如下,参数|D|表示语料的文本总数,|Dt| 表示文本所包含特征词 tj 的数量。
在倒文档频率方法中,权重是随着特征词的文档数量的变化呈反向变化。如某些常用词“我们”、“但是”、“的”等,在所有文档中出现频率很高,但它的IDF值却非常低。甚至如果它每篇文档都出现,则log1的计算结果为0,从而降低了这些常用词的作用;相反,如果某篇介绍“人工智能”的词,仅仅在该篇文档中出现很多次,它的作用就非常高。
TF-IDF技术的核心思想是如果某个特征词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来做权重计算。TF-IDF算法简单快速,结果也符合实际情况,是文本挖掘、情感分析、主题分布等领域的常用手段。
2.代码实现
Scikit-Learn中主要使用Scikit-Learn中的两个类CountVectorizer和TfidfTransformer,用来计算词频和TF-IDF值。
- CountVectorizer
- TfidTransformer
完整代码:
# -*- coding:utf-8 -*-
import csv
import pandas as pd
import numpy as np
import jieba
import jieba.analyse
from scipy.sparse import coo_matrix
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
#----------------------------------第一步 读取文件--------------------------------
with open(fenci_data.csv, r, encoding=UTF-8) as f:
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
labels.append(row[label]) #0-好评 1-差评
contents.append(row[content])
print(labels[:5])
print(contents[:5])
#----------------------------------第二步 数据预处理--------------------------------
#将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer()
#该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer()
#第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(contents))
for n in tfidf[:5]:
print(n)
print(type(tfidf))
# 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
for n in word[:10]:
print(n)
print("单词数量:", len(word))
#将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
#X = tfidf.toarray()
X = coo_matrix(tfidf, dtype=np.float32).toarray() #稀疏矩阵 注意float
print(X.shape)
print(X[:10])
输出结果如下所示:
< class scipy.sparse.csr.csr_matrix>
aaaaa
achievements
amazing
ananananan
ancient
anshun
aperture
app
单词数量: 20254
(6074, 20254)
[[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
...
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]]
3.MemoryError内存溢出错误
当我们数据量很大时,矩阵往往存储不了这么大的数据,会出现如下错误:
- ValueError: array is too big; arr.size * arr.dtype.itemsize is larger than the maximum possible size.
- MemoryError: Unable to allocate array with shape (26771, 69602) and data type float64
- 停用词过滤降低不需要的特征词
- scipy包的提供了稀疏矩阵的创建,使用coo_matrix(tfidf, dtype=np.float32)转换tfidf
- CountVectorizer(min_df=5)增加min_df参数,过滤掉出现频率少的特征词,该参数可以不断调试
- 使用GPU或扩大内存解决
- 对中文分词和数据清洗后的语料进行词频矩阵生成操作。主要调用CountVectorizer类计算词频矩阵,生成的矩阵为X。
- 调用TfidfTransformer类计算词频矩阵X的TF-IDF值,得到Weight权重矩阵。
- 调用Sklearn机器学习包执行分类操作,调用fit()函数训练,并将预测的类标赋值给pre数组。
- 调用Sklearn库PCA()函数进行降维操作,将这些特征降低为二维,对应X和Y轴,接着进行可视化呈现。
- 算法优化及算法评估。
# -*- coding:utf-8 -*-
import csv
import pandas as pd
import numpy as np
import jieba
import jieba.analyse
from scipy.sparse import coo_matrix
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn import svm
from sklearn import neighbors
from sklearn.naive_bayes import MultinomialNB
#----------------------------------第一步 读取文件--------------------------------
with open(fenci_data.csv, r, encoding=UTF-8) as f:
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
labels.append(row[label]) #0-好评 1-差评
contents.append(row[content])
print(labels[:5])
print(contents[:5])
#----------------------------------第二步 数据预处理--------------------------------
#将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer(min_df=5)
#该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer()
#第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(contents))
for n in tfidf[:5]:
print(n)
print(type(tfidf))
# 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
for n in word[:10]:
print(n)
print("单词数量:", len(word))
#将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
#X = tfidf.toarray()
X = coo_matrix(tfidf, dtype=np.float32).toarray() #稀疏矩阵 注意float
print(X.shape)
print(X[:10])
#----------------------------------第三步 数据划分--------------------------------
#使用 train_test_split 分割 X y 列表
X_train, X_test, y_train, y_test = train_test_split(X,
labels,
test_size=0.3,
random_state=1)
#--------------------------------第四步 机器学习分类--------------------------------
# 逻辑回归分类方法模型
LR = LogisticRegression(solver=liblinear)
LR.fit(X_train, y_train)
print(模型的准确度:.format(LR.score(X_test, y_test)))
pre = LR.predict(X_test)
print("逻辑回归分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
print("\\n")
运行结果如下图所示:
五.算法性能评估算法评价很多实时需要我们自己编写程序去实现,比如绘制ROC曲线、统计各种特征信息、显示4位数结果。这里作者尝试自定义准确率(Precision)、召回率(Recall)和F特征值(F-measure),其计算公式如下:
由于本文主要针对2分类问题,其实验评估主要分为0和1两类,完整代码如下:
# -*- coding:utf-8 -*-
import csv
import pandas as pd
import numpy as np
import jieba
import jieba.analyse
from scipy.sparse import coo_matrix
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn import svm
from sklearn import neighbors
from sklearn.naive_bayes import MultinomialNB
#----------------------------------第一步 读取文件--------------------------------
with open(fenci_data.csv, r, encoding=UTF-8) as f:
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
labels.append(row[label]) #0-好评 1-差评
contents.append(row[content])
print(labels[:5])
print(contents[:5])
#----------------------------------第二步 数据预处理--------------------------------
#将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer(min_df=5)
#该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer()
#第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(contents))
for n in tfidf[:5]:
print(n)
print(type(tfidf))
# 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
for n in word[:10]:
print(n)
print("单词数量:", len(word))
#将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
#X = tfidf.toarray()
X = coo_matrix(tfidf, dtype=np.float32).toarray() #稀疏矩阵 注意float
print(X.shape)
print(X[:10])
#----------------------------------第三步 数据划分--------------------------------
#使用 train_test_split 分割 X y 列表
X_train, X_test, y_train, y_test = train_test_split(X,
labels,
test_size=0.3,
random_state=1)
#--------------------------------第四步 机器学习分类--------------------------------
# 逻辑回归分类方法模型
LR = LogisticRegression(solver=liblinear)
LR.fit(X_train, y_train)
print(模型的准确度:.format(LR.score(X_test, y_test)))
pre = LR.predict(X_test)
print("逻辑回归分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
#----------------------------------第五步 评价结果--------------------------------
def classification_pj(name, y_test, pre):
print("算法评价:", name)
# 正确率 Precision = 正确识别的个体总数 /识别出的个体总数
# 召回率 Recall = 正确识别的个体总数 /测试集中存在的个体总数
# F值 F-measure = 正确率 * 召回率 * 2 / (正确率 + 召回率)
YC_B, YC_G = 0,0#预测 bad good
ZQ_B, ZQ_G = 0,0#正确
CZ_B, CZ_G = 0,0#存在
#0-good 1-bad 同时计算防止类标变化
i = 0
while i< len(pre):
z = int(y_test[i])#真实
y = int(pre[i])#预测
if z==0:
CZ_G += 1
else:
CZ_B += 1
if y==0:
YC_G += 1
else:
YC_B += 1
if z==y and z==0 and y==0:
ZQ_G += 1
elif z==y and z==1 and y==1:
ZQ_B += 1
i = i + 1
print(ZQ_B, ZQ_G, YC_B, YC_G, CZ_B, CZ_G)
print("")
# 结果输出
P_G = ZQ_G * 1.0 / YC_G
P_B = ZQ_B * 1.0 / YC_B
print("Precision Good 0:", P_G)
print("Precision Bad 1:", P_B)
R_G = ZQ_G * 1.0 / CZ_G
R_B = ZQ_B * 1.0 / CZ_B
print("Recall Good 0:", R_G)
print("Recall Bad 1:", R_B)
F_G = 2 * P_G * R_G / (P_G + R_G)
F_B = 2 * P_B * R_B / (P_B + R_B)
print("F-measure Good 0:", F_G)
print("F-measure Bad 1:", F_B)
#函数调用
classification_pj("LogisticRegression", y_test, pre)
输出结果如下:
逻辑回归分类
1823 1823
precisionrecallf1-scoresupport
00.940.990.971520
10.930.700.80303
accuracy0.941823
macro avg0.940.850.881823
weighted avg0.940.940.941823
算法评价: LogisticRegression
213 1504 229 1594 303 1520
Precision Good 0: 0.9435382685069009
Precision Bad 1: 0.9301310043668122
Recall Good 0: 0.9894736842105263
Recall Bad 1: 0.7029702970297029
F-measure Good 0: 0.9659601798330122
F-measure Bad 1: 0.800751879699248
六.算法对比实验1.RandomForest
代码如下:
# 随机森林分类方法模型 n_estimators:森林中树的数量
clf = RandomForestClassifier(n_estimators=20)
clf.fit(X_train, y_train)
print(模型的准确度:.format(clf.score(X_test, y_test)))
print("\\n")
pre = clf.predict(X_test)
print(预测结果:, pre[:10])
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("RandomForest", y_test, pre)
print("\\n")
输出结果:
2.SVM
代码如下:
# SVM分类方法模型
SVM = svm.LinearSVC() #支持向量机分类器LinearSVC
SVM.fit(X_train, y_train)
print(模型的准确度:.format(SVM.score(X_test, y_test)))
pre = SVM.predict(X_test)
print("支持向量机分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("LinearSVC", y_test, pre)
print("\\n")
输出结果:
3.朴素贝叶斯
代码如下:
#朴素贝叶斯模型
nb = MultinomialNB()
nb.fit(X_train, y_train)
print(模型的准确度:.format(nb.score(X_test, y_test)))
pre = nb.predict(X_test)
print("朴素贝叶斯分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("MultinomialNB", y_test, pre)
print("\\n")
输出结果:
4.KNN
该算法准确率不高,并且执行时间较长,不建议大家用于文本分析。某些情况的算法对比倒是还行,核心代码如下:
#最近邻算法
knn = neighbors.KNeighborsClassifier(n_neighbors=7)
knn.fit(X_train, y_train)
print(模型的准确度:.format(knn.score(X_test, y_test)))
pre = knn.predict(X_test)
print("最近邻分类")
print(classification_report(y_test, pre))
classification_pj("KNeighbors", y_test, pre)
print("\\n")
输出结果:
【基于机器学习和TFIDF的情感分类算法,详解自然语言处理】
5.决策树
代码如下:
#决策树算法
dtc = DecisionTreeClassifier()
dtc.fit(X_train, y_train)
print(模型的准确度:.format(dtc.score(X_test, y_test)))
pre = dtc.predict(X_test)
print("决策树分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("DecisionTreeClassifier", y_test, pre)
print("\\n")
输出结果:
6.SGD
代码如下:
#SGD分类模型
from sklearn.linear_model.stochastic_gradient import SGDClassifier
sgd = SGDClassifier()
sgd.fit(X_train, y_train)
print(模型的准确度:.format(sgd.score(X_test, y_test)))
pre = sgd.predict(X_test)
print("SGD分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("SGDClassifier", y_test, pre)
print("\\n")
输出结果:
7.MLP
该算法时间比较慢,核心代码如下:
#MLP分类模型
from sklearn.neural_network.multilayer_perceptron import MLPClassifier
mlp = MLPClassifier()
mlp.fit(X_train, y_train)
print(模型的准确度:.format(mlp.score(X_test, y_test)))
pre = mlp.predict(X_test)
print("MLP分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("MLPClassifier", y_test, pre)
print("\\n")
输出结果:
8.GradientBoosting
该算法时间比较慢,代码如下:
#GradientBoosting分类模型
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier()
gb.fit(X_train, y_train)
print(模型的准确度:.format(gb.score(X_test, y_test)))
pre = gb.predict(X_test)
print("GradientBoosting分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("GradientBoostingClassifier", y_test, pre)
print("\\n")
输出结果:
9.AdaBoost
代码如下:
#AdaBoost分类模型
from sklearn.ensemble import AdaBoostClassifier
AdaBoost = AdaBoostClassifier()
AdaBoost.fit(X_train, y_train)
print(模型的准确度:.format(AdaBoost.score(X_test, y_test)))
pre = AdaBoost.predict(X_test)
print("AdaBoost分类")
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
classification_pj("AdaBoostClassifier", y_test, pre)
print("\\n")
输出结果:
??点击关注,第一时间了解华为云新鲜技术~??
推荐阅读
- 程序员非常实用的十个工具网站,值得收藏
- 静态照片一键动态化,教你如何集成人像复活能力
- docker版本AWVS14
- AzureVMPowerShell 批量创建VM
- #星光计划2.0# 鸿蒙设备开发Hi3861-IoT落地-自动门锁(附多案例)
- 14前缀树
- Airflow 2.2.3 + MySQL 8.0.27 + Redis 6.2 部署Airflow任务调度平台
- Linux之目录结构
- 使用Jenkins更改文件所有权