项目 | Word2Vec + LSTM 电商评论情感标签预测

本科三年级课程大作业,做一个记录,欢迎吐槽。

整体思路

本实验中,目标解决的问题是:根据文本,预测句子总体情感特征。
首先调用jieba包对句子进行分词,然后去除一些停用词、特殊文本(如纯数字、过短或过长词语),再去除出现频次过少的词语,构建高频词词典,one-hot编码后做词嵌入(Word embedding),最后用各类模型进行预测。

图1 实验整体思路
主要分为了四个文件:

  • PreProcessings.py 读取数据,分词,构建字典,预训练模型
  • LSTMs.py 构建神经网络,自定义评价函数和损失函数
  • Predict.py 预测过程,包括神经网络和其他所有模型
  • main.py 主函数

模型

Word Embedding

对于一个句子样本输入,可以先对它one-hot然后乘上词嵌入矩阵就可得到这个句子的词嵌入向量表示。要想得到好的词向量,我们需要训练的就是这个矩阵W(shape=(input_dim,output_dim))。Embedding层的作用就是训练这个矩阵W并进行词的嵌入为每一个词分配与它对应的词向量。

在Keras中,这个词嵌入矩阵W可以先随机初始化,然后根据下游任务训练获得,也可以使用预训练的词嵌入矩阵来初始化它(用weights来为layer初始化任意权重),然后再训练,也可以直接用预训练的词嵌入矩阵来初始化它并冻结它,不让它变化,不让它可训练(trainable=False)。

在本实验中,分别采用两种方法来进行训练。一种为直接随机初始化,然后再训练中进行更新;另一种则是采用Word2Vec模型,预训练好后嵌入(再训练/固定)。

Focal Loss

原始数据中,正面、中性与负面的评论比例大致为100:6:6。
Cross Entropy Error Function(交叉熵损失函数)是一类常被用于多分类问题的损失函数,其计算公式为
Cross Entropy Error Function
其中,M为分类的数量,y_ic为指示变量(0/1),如果该类别和样本i的类别相同就是1,否则是0,p_ic为观测样本i属于类别c的预测概率。

然而,对于样本不均衡的问题,我希望对于分类错误的少数类样本加大惩罚,采用了自定义的Focal loss(焦点损失)作为损失函数。Focal loss在17年被Lin等人提出,是对交叉熵损失函数的改进,它在标准交叉熵准则中增加了一个因子,解决类不平衡问题。

原文以二元分类问题为例,原始交叉熵Cross Entropy为

原始交叉熵

平衡交叉熵Balanced Cross Entropy为类别1引入加权因子α∈[0,1],为类别0引入加权因子1-α,

平衡交叉熵

Focal loss在Cross Entropy上加入调制因子
  ( 1 − p t ) γ \ (1- p_t)^\gamma  (1pt)γ ,其中可调聚焦参数γ≥0,

Focal loss

在本实验中,采用的是Focal loss的α平衡变体,也即结合了Balanced Cross Entropy和Focal loss:

本实验中使用的Focal loss

Metric

混淆矩阵

TP: 预测值为正,实际值为正
FP: 预测值为正,实际值为负
FN: 预测值为负,实际为正
TN: 预测值为负,实际为负

图2:混淆矩阵

在n-分类问题中,混淆矩阵可以有两种形式。一种为一个n * n的矩阵,可以分别表示有多少个第i类的真实值被预测为第j类;另一种则是n个2 * 2的矩阵,每个矩阵分别表示第i类的混淆矩阵,其中正样本表示标签为第i类的样本,负样本表示标签为不是第i类的样本。以第一个类别“负面”为例,它的负样本,“中性”和 “正面”预测对的有TN个。“中性”预测成“正面”也算对的,因为它们是一类,都是负样本。

在本实验中,我主要使用的是前者。

Accuracy, Precision, Recall, F1

Accuracy
Accuracy——预测正确的样本在全部样本中的比例。对于不平衡问题,用Accuracy作为指标是不合理的。如果全部预测为多数类,也能达到非常高的Accuracy。

Precision
Precision——预测为正的样本中实际为正的比例。
Recall
Recall——实际为正的样本中预测为正的比例。

F1
Precision和 Recall的调和平均值。Precision体现了模型对负样本的区分能力,Precision越高,模型对负样本的区分能力越强;Recall体现了模型对正样本的识别能力,Recall越高,模型对正样本的识别能力越强。F1 score是两者的综合,F1 score越高,说明模型越稳健。F1 score 是本次实验评价模型的主要指标。

特别地,由于本实验为多分类问题。对于以上指标,如果是“macro”,则总指标是每一类的指标的均值;若为“micro”,则是所有样本的均值。由于本实验存在类别不平衡问题,使用“macro”更能反映模型的优劣。

实验

原始数据中,正面、中性与负面的评论比例大致为100:6:6。这迫使了我采取一些防止过拟合的措施,比如采样方法(过采样、欠采样、综合采样),设置惩罚,正则化等等。

设置惩罚在本实验中效果最好。如果使用原始的交叉熵函数,模型几乎无法摆脱过拟合。采用过拟合和综合采样方法在本实验中的表现并不好,依然预测为多数类。究其原因,可能是过采样是对样本的增强,而在文本中一点小的改动可能就会带来很大的影响,改变语义。

原始数据Word2Vec嵌入

下表中分别列出各个模型的训练结果。将直接使用one-hot编码后的词典作为输入变量的朴素贝叶斯模型作为基准模型。

对于随机森林模型,为了避免过拟合,我按照样本的比例设置了平衡的权重,然而从混淆矩阵中可以看出,模型分类仍然偏向正面情感,也就是多数类。

下面三种深度学习模型,在训练层均采用单层单向的LSTM层,区别在于word embedding的方法和是否在训练时更新权重。在embed层和训练层均随机dropout20%的神经元。采用focal loss作为损失函数,gamma取值为2,alpha分别设置为100,100和1,加大对少数类样本分类错误的惩罚。采用SoftMax作为输出函数,输出三类的概率。

可以看出,在本数据集上的效果:嵌入Word2Vec训练后的词向量,在训练时不更新权重 < 随机初始化embed层,在训练时更新权重 < 嵌入Word2Vec训练后的词向量,在训练时更新权重。

这和Kim, Yoon得出的结论是不吻合的。他们使用了四种词向量方法进行CNN的句子。分别是: 1)随机初始化向量,再训练;2)使用预训练词向量,不再训练;3)使用预训练词向量,再训练;4)使用两个预训练的词向量表示一个词,两个词向量交替训练。在多数情况下,1)< 2)< 3)。

那么,为什么会出现再训练的随机权重比不更新的预训练的词向量好的情况呢?可能的原因是:语料不足。预训练的优势在于可以用大量的语料做无监督训练,而我们的数据仅仅有一万多条,这相比一般NLP的数据集是比较小的。
图3 Kim, Yoon的训练结果

模型 Macro F1 混淆矩阵
朴素贝叶斯 0.26 Column 2
支持向量机 0.36 在这里插入图片描述
随机森林(balanced weight) 0.44 在这里插入图片描述
单层单向LSTM(随机初始化,更新) 0.56 在这里插入图片描述
单层单向LSTM(word2vec训练,不更新) 0.46 在这里插入图片描述
单层单向LSTM(word2vec训练,更新) 0.57 在这里插入图片描述

预训练语料嵌入

为了解决这个问题,我在网上下载了开源的26G的使用Word2Vec训练好的百度百科语料直接嵌入。我仅进行了单层单向LSTM不带权重更新的模型训练(如果需要更新,可能需要批量feed数据),F score为0.55。虽然结果没有超过随机初始化的0.56,但是相比原本语料嵌入的0.46提升颇多。

如果条件允许进行权重更新,很有可能会产生更好的预测结果。另外,预训练的语料类型也很重要,如果能找到更加相关的数据集进行预训练,那么精度会更好。

由于开源数据集缺少一个重要文件,所以无法进行增量训练。而根据之前的研究,将自己的语料和开源语料增量训练后,分类准确度会得到一定的提升。如果有合适的数据集,这是一个值得尝试的方向。

预训练向量嵌入的混淆矩阵

不考虑数据平衡问题

在前面所有的实验中,数据不平衡问题大大影响了实验结果。使用复杂的网络结构容易会陷入过拟合的窘境。

在正面情绪的评论中随机选取了1200条,和约800条负面以及中性评论构成新的大致类别平衡的数据集。由于数据大致平衡,所以选用了原本的交叉熵作为损失函数。对于所有的LSTM结构,均迭代50个epoch,为防止过拟合,当连续8个epoch验证集上的损失函数均上升超过原来的1%时,停止训练。

对于Accuracy: 单层单向LSTM - 0.69 < 单层双向LSTM - 0.71 < 双层双向LSTM - 0.73

代码

预处理

# -*- coding: utf-8 -*-

import warnings
warnings.filterwarnings("ignore")

import os
os.chdir('...')

import pandas as pd
import numpy as np
import jieba
import re
from collections import Counter
import pickle
from keras.preprocessing import sequence
import LSTMs #引用模块 
from gensim.models.keyedvectors import KeyedVectors

EMBEDDING_DIM = 128

def get_stop_list():
    stop = []
    f = open
  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值