2021-11-04

记一次nlp学习
数据来源:
这里我忘记了,但是是已经打好标签的酒店评论的数据集,1表示正向评论,0表示负向评论。

1、数据导入与预处理

1.1数据导入

import pandas as pd
df = pd.read_csv('../hoteData.csv',index_col=0)

数据展示如下


图片.png

1.2数据预处理

1.2.1 去掉空缺值

df.dropna(how="any",inplace=True)

1.2.2 划分正负样本

posdf = df[df["label"] == 1] # 正样本
negdf = df[df["label"] == 0] # 负样本

划分正负样本后,查看正负样本得到,正样本有5322条,负样本有2443条负样本。

1.2.3 分词和停词处理

在分词这里将主要使用python的jieba库,并且加上正则表达式来处理一些数字

import jieba
import re
  • 添加自定义的用户词典
    这里一般来说是要添加有关领域的专业词典,但是我没有找到,就不添加了
  • 删除数字
    这里是发现数字及数据后面紧跟的第一个字符,往往是可以看成一个整体的,并且这些数字往往是形容日期等等一些无关的信息。所以自己想方法将其剔除(这是自己的想法,并没有找到依据,并且代码有一定的不合理之处)。
def drop_num_near(string):
#将汉字中的数字替换为阿拉伯数字
    han = ["一","二","三","四","五","六","七","八","九","十"]
    yin = [1,2,3,4,5,6,7,8,9,10]
    for k in range(len(han)):
        string = string.replace(han[k],str(yin[k]))
#将数字及数字后一个字符一起删除
    pattern  = re.compile('[0-9]+.+?')
    term = re.findall(pattern,string)
    if len(term) != 0:
        for i in term:
            string = string.replace(i,"")
        return string
    else :
        return string
# 函数的一个实例如下
drop_num_near("这次是308的行政大床,总体感觉非常不错,就是价格稍许高了点,旁边有个五星的豪华客房才398。估计小天鹅也只有这个房型以上的,看得过去,以前住过的房间实在是很差。以后大家如果要住这里,还是选这个行政大床吧!")
# '这次是行政大床,总体感觉非常不错,就是价格稍许高了点,旁边有个的豪华客房才估计小天鹅也只有这个房型以上的,看得过去,以前住过的房间实在是很差。以后大家如果要住这里,还是选这个行政大床吧!'        
  • 分词和停词处理
    首先是读入停词表,这里的停词使用的是哈工大和川大的停词表合并去重后的结果
# 读入哈工大停词表
with open("../stop/hit.txt", 'r', encoding='utf-8') as fp:
    stop3 = []
    for item in fp.readlines():
        stop3.append(item.strip())
fp.close()
# 读入川大停词表
with open("../stop/scu.txt", 'r', encoding='utf-8') as fp:
    stop4 = []
    for item in fp.readlines():
        stop4.append(item.strip())
fp.close()
# 停词表合并与去重
sumstop = stop3 + stop4
stopdf = pd.DataFrame(sumstop)# 这里的去重方法用的是pandas的drop_dupllicates()方法。不要问我为什么用这个方法,我也不知道,自己怎么会想到这个方法
stopwordlist = list(stopdf.drop_duplicates()[0])

stopwordlist就是最终的停词表
然后是定义分词函数,这里之所以定义一个分词函数,是为了使用pandas的apply方法。

#  评论文本中还有一些英文,这里要将英文也给删除
# 定义判断是不是英文字符的函数
def judge(item):
    pattern  = re.compile('[a-zA-Z]+')
    result = re.findall(pattern,item)
    if len(result)== 0:
        return True
    else:
        return False
def cut_words(string):
# 传入一个字符串,首先去掉数字
    string = drop_num_near(string)
#调用jieba的lcut方法进行分词,分词的结果以list形式表示
    cut_list = jieba.lcut(string)
# 定义一个新的列表,用来存储在不在停词表和非英文的词
    cut_result_list = []
# 遍历 分词结果,存储到cut_result_list中
    for item in cut_list:
        if (item not in stopwordlist) and (judge(item)):
            cut_result_list.append(item)
#返回分词结果
    return cut_result_list
# 分词函数的一个实例如下
cut_result = cut_words("这次是行政大床,总体感觉非常不错,就是价格稍许高了点,旁边有个的豪华客房才估计小天鹅也只有这个房型以上的,看得过去,以前住过的房间实在是很差。以后大家如果要住这里,还是选这个行政大床吧!")
for item in cut_result:
    print(item,end = " ")
# 结果如下
# 这次 行政 大床 总体 感觉 不错 价格 稍许 高 点 旁边 有个 豪华 客房 才 估计 小天鹅 房型 以上 看 过去 以前 住 房间 实在 差 以后 住 选 行政 大床 

对posdf和negdf调用分词函数,得到分词结果,存储到list_cuted_review列中

posdf["list_cuted_review"] = posdf["review"].apply(cut_words)
negdf["list_cuted_review"] = negdf["review"].apply(cut_words)

1.2.4 分词后结果查看

这里主要是绘制词云图和查看频率前10的单词,首先绘制正样本的词云图

# 导入wordcloud包
from wordcloud import WordCloud
# 正样本的词云图绘制
#这里是调整为wordcloud绘制词云图需要的格式也即是用,分割的字符串
long_string = ""
for item in posdf["list_cuted_review"]:
    string = ','.join(item)
    long_string = long_string+string + ','
# 创建一个词云对象
wordcloud = WordCloud(font_path='C:\windows\Fonts# \STZHONGS.TTF',# 字体
                      background_color="white",
                      max_words=100, contour_width=3,
                      contour_color='steelblue',
                     width=1500,height=1000)
# 生成词云
wordcloud.generate(long_string)
# 可视化词云
wordcloud.to_image()

正样本词云图如下所示


图片.png

然后绘制负样本的词云图


#这里是调整为wordcloud绘制词云图需要的格式也即是用,分割的字符串
long_string = ""
for item in negdf["list_cuted_review"]:
    string = ','.join(item)
    long_string = long_string+string + ','
# 创建一个词云对象
wordcloud = WordCloud(font_path='C:\windows\Fonts# \STZHONGS.TTF',# 字体
                      background_color="white",
                      max_words=100, contour_width=3,
                      contour_color='steelblue',
                     width=1500,height=1000)
# 生成词云
wordcloud.generate(long_string)
# 可视化词云
wordcloud.to_image()

负样本词云图如下所示


图片.png

通过查看正负样本的词云图,发现如酒店、房间、服务员等词都是高频词汇,并且同时出现在正样本和负样本中。于是决定进一步查看正负样本的词频

#自己定义一个获取单词和词频的函数
def get_ci_ping(string):
    word_dict={}
    long_list = string.split(",")
    for item in long_list:
        if item in word_dict.keys():
            word_dict[item] += 1
        else:
            word_dict[item]  = 1
    return word_dict
# 获取正向评论的分词表的词频结果
long_string = ""
for item in posdf["list_cuted_review"]:
    string = ','.join(item)
    long_string = long_string+string + ','
# 获取正向评论的词频词典
pos_word_dict = get_ci_ping(long_string)
# 获取正向评论的频数排名前10的词
pos_word_df = pd.DataFrame(pos_word_dict.values(),index = pos_word_dict.keys())
pos_list  = list(pos_word_df.sort_values(0,ascending=False).iloc[0:10,].index)
pos_list
# ['酒店', '房间', '不错', '好', '还', '服务', '都', '住', '比较', '不']
# 同理可得负样本的频数前10的词
long_string = ""
for item in negdf["list_cuted_review"]:
    string = ','.join(item)
    long_string = long_string+string + ','
# 获取正向评论的词频词典
neg_word_dict = get_ci_ping(long_string)
# 获取正向评论的频数排名前10的词
neg_word_df = pd.DataFrame(neg_word_dict.values(),index = neg_word_dict.keys())
neg_list  = list(neg_word_df.sort_values(0,ascending=False).iloc[0:10,].index)
neg_list
#['酒店', '房间', '不', '都', '说', '住', '还', '入住', '服务', '前台']

可以看到,频数前10的词有很大的重叠,于是决定去掉频数前十的重合的词

#获取重叠的词的列表
lap_word_list = []
for item in pos_list:
    if item in neg_list:
        lap_word_list.append(item)
#定义删除重叠的词的函数
def drop_lap_word(wlist):
    for item in wlist:
        if item in lap_word_list:
            wlist.remove(item)
    return wlist
#删除正负样本中的高频重叠词
negdf["list_cuted_review"].apply(drop_lap_word)
posdf["list_cuted_review"].apply(drop_lap_word)

至此数据预处理部分基本完成,但是考虑到后面要使用sklearn中的一些工具包,这里将list形式的分词结果要转换为str形式的

def str_cuted_review(wlist):
    return ' '.join(wlist)
posdf["str_cuted_review"] = posdf["list_cuted_review"].apply(str_cuted_review)
negdf["str_cuted_review"] = negdf["list_cuted_review"].apply(str_cuted_review)

至此处理完毕,正负样本如下所示


图片.png

图片.png

2 模型构建预处理

2.1 构建词向量与降维

这里使用正负样本整体进行词向量的训练,所以要将正负样本进行重新拼接

df = pd.concat([posdf[["label","str_cuted_review"]],negdf[["label","str_cuted_review"]]])
#重新设置index
df.index = list(range(df.shape[0]))
#查看正负样本的index分布
df[df["label"] == 0]

导入有关的包,对分词后的结果进行词向量的训练

from sklearn.feature_extraction.text import CountVectorizer
#构建模型
countvectorizer = CountVectorizer(token_pattern=r"(?u)\b\w+\b")
#训练模型
count = countvectorizer.fit_transform(df["str_cuted_review"])
#查看词向量的纬度
count.toarray().shape
#(7765, 26832)

可以看出,词向量的维度特别高,不能直接拿来进行训练和测试,但是本来是准备用pca方法来进行降维的,但是pca不能对稀疏矩阵进行降维,这里转用svd方法

from sklearn.decomposition import TruncatedSVD
#设置模型的输出结果为200维
svd = TruncatedSVD(200)
#对词向量的结果进行转换
data = svd.fit_transform(count)

2.2 划分训练集和测试集

这里划分训练集和测试集的方法是抽取随机数

def get_rand_index(rate,num1,num2):
    N = int((num2-num1) * rate)
    resultList=random.sample(range(num1,num2),N)
    return resultList
def get_train_test_index(rate):
    train_index = get_rand_index(rate,0,5322)+get_rand_index(rate,5322,7765)
    test_index = []
    for item in range(0,7764):
        if item not in train_index:
            test_index.append(item)
    return train_index,test_index
#获取训练集和测试集的index组成的list
train_index ,test_index = get_train_test_index(0.7)

根据train_index和test_index划分x_train,x_test,y_train,y_test

x_train  = data[train_index]
y_train = list(df.iloc[train_index,0])
x_test  = data[test_index]
y_test = list(df.iloc[test_index,0])

3模型构建

3.1 svm模型

from sklearn import svm
#构建模型
model=svm.SVC(C=0.6,kernel="linear",gamma=10,decision_function_shape='ovo',probability = False) 
#训练
model.fit(x_train,y_train) 
#训练集得分
model.score(x_train,y_train)
#0.8809567617295309
#测试集得分
model.score(x_test,y_test)
#0.8750536711034779

可以看到,在训练集上准确率达到了87.5。为了进一步探究recall和,1score,可以用以下代码

from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
from sklearn.metrics import recall_score
#首先使用训练好的模型,预测测试集的结果
svmresult = model.predict(x_test)
#使用预测的结果来计算F1,recall,准确率
accuracy_score(y_test,svmresult)
#准确率为0.8750536711034779
f1_score(y_test,svmresult)
#f1得分为0.9107635694572216
recall_score(y_test,svmresult)
#recall值为0.9298685034439574

可以看到svm模型表现的很不错,接下来使用朴素贝叶斯、随机森林、逻辑回归、KNN算法分别看看表现

3.2 朴素贝叶斯

from sklearn.naive_bayes import  BernoulliNB
model = BernoulliNB()
model.fit(x_train,y_train)
#查看在训练集上的准确率
model.score(x_train,y_train)
#0.8099356025758969
#预测测试集上的输出结果
nbresult = model.predict(x_test)
#使用预测的结果来计算F1,recall,准确率
accuracy_score(y_test,nbresult)
#准确率为0.8016316015457278
f1_score(y_test,nbresult)
#f1得分为0.8598300970873787
recall_score(y_test,nbresult)
#recall值为0.8872886662492173

可以看到朴素贝叶斯表现不如svm

3.3 随机森林

from sklearn.ensemble import RandomForestClassifier as RFC
model = RFC(n_estimators= 220)
model.fit(x_train,y_train)
#查看在训练集上的准确率
print(model.score(x_train,y_train))
#0.9996320147194112
#预测测试集上的输出结果
nbresult = model.predict(x_test)
#使用预测的结果来计算F1,recall,准确率
print(accuracy_score(y_test,nbresult))
#准确率为0.8355517389437527
print(f1_score(y_test,nbresult))
#f1得分为0.886045819696519
print(recall_score(y_test,nbresult))
#recall值为0.9323731997495304

随机森林要好于朴素贝叶斯,部分值高于svm

3.4 最近邻算法

from sklearn import neighbors
model = neighbors.KNeighborsClassifier(n_neighbors=15)
model.fit(x_train,y_train)
#查看在训练集上的准确率
print(model.score(x_train,y_train))
#0.8132474701011959
#预测测试集上的输出结果
nbresult = model.predict(x_test)
#使用预测的结果来计算F1,recall,准确率
print(accuracy_score(y_test,nbresult))
#准确率为0.793902962644912
print(f1_score(y_test,nbresult))
#f1得分为0.861910241657077
print(recall_score(y_test,nbresult))
#recall值为0.9380087664370695

KNN算法的recall是表现的最好的,但是其他指标表现并不理想

3.5 逻辑回归

from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(x_train,y_train)
#查看在训练集上的准确率
print(model.score(x_train,y_train))
#0.8776448942042319
#预测测试集上的输出结果
nbresult = model.predict(x_test)
#使用预测的结果来计算F1,recall,准确率
print(accuracy_score(y_test,nbresult))
#准确率为0.8656075568913697
print(f1_score(y_test,nbresult))
#f1得分为0.9038992938286767
print(recall_score(y_test,nbresult))
#recall值为0.9217282404508453

逻辑回归表现也还行。
感觉机器学习的算法都还行,接下来将学习bilstm方法

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容