序列依赖问题——文本情感分类

文章发布时间:

文章总字数:
1.1k

预计阅读时间:
4 分钟

之前的LeNet-5卷积神经网络模型体现了数据在空间上的关联性,但除此之外,数据也有可能在时间上有关联性,比如气温数据,股票数据等。当然,最典型的还是人类的语言。面对这样在时间上有关联的数据,神经网络该如何去识别和处理呢?

我们一般把词作为自然语言处理的基本单位,这样就可以利用“词典”用一个数字来代表一个词,这个数字就是索引值。这样,由词组成的一句话就会转化为一个由数字组成的向量,自然就可以送入神经网络进行识别了。

如果有些词在词典中由于种种原因数据接近,但是含义却相差甚远,这时候如果再进行归一化操作,计算机就很可能错误的识别到完全相反的意思。这样的数据就会给预测模型带来不必要的麻烦。所以我们可以使用one-hot的编码方式对词汇进行编码。通过one-hot编码后每个词都完全不一样。

但是这样进行严格区分的话,又丢失了词汇的关联性。我们可以提取出一个词汇的多个特征值形成一个词向量,即NLP中的词嵌入。

最后我们结合一下one-hot和词向量。让词向量组成的矩阵(字典)点乘按词语组成句子的one-hot编码形成的矩阵,这样就可以提取出句子中每一个词的词向量,这样就可以送入神经网络进行学习了。

明白了原理,我们就来实现一个简单的文本情感分类模型

首先在网上找到了一个别人爬的网购评论的csv文件,然后封装一个shoppingdata.py用来读取和处理这些数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import os
import keras
import numpy as np
import keras.preprocessing.text as text
import re
import jieba
import random



def load_data():
xs = []
ys = []
with open(os.path.dirname(os.path.abspath(__file__))+'/online_shopping_10_cats.csv','r',encoding='utf-8') as f:
line=f.readline()#escape first line"label review"
while line:
line=f.readline()
if not line:
break
contents = line.split(',')

# if contents[0]=="书籍":
# continue

label = int(contents[1])
review = contents[2]
if len(review)>20:
continue

xs.append(review)
ys.append(label)

xs = np.array(xs)
ys = np.array(ys)

#打乱数据集
indies = [i for i in range(len(xs))]
random.seed(666)
random.shuffle(indies)
xs = xs[indies]
ys = ys[indies]

m = len(xs)
cutpoint = int(m*4/5)
x_train = xs[:cutpoint]
y_train = ys[:cutpoint]

x_test = xs[cutpoint:]
y_test = ys[cutpoint:]



print('总样本数量:%d' % (len(xs)))
print('训练集数量:%d' % (len(x_train)))
print('测试集数量:%d' % (len(x_test)))

return x_train,y_train,x_test,y_test


def createWordIndex(x_train,x_test):
x_all = np.concatenate((x_train,x_test),axis=0)
#建立词索引
tokenizer = text.Tokenizer()
#create word index
word_dic = {}
voca = []
for sentence in x_all:
# 去掉标点
sentence = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()]+", "", sentence)
# 结巴分词
cut = jieba.cut(sentence)
#cut_list = [ i for i in cut ]

for word in cut:
if not (word in word_dic):
word_dic[word]=0
else:
word_dic[word] +=1
voca.append(word)
word_dic = sorted(word_dic.items(), key = lambda kv:kv[1],reverse=True)

voca = [v[0] for v in word_dic]

tokenizer.fit_on_texts(voca)
print("voca:"+str(len(voca)))
return len(voca),tokenizer.word_index

def word2Index(words,word_index):
vecs = []
for sentence in words:
# 去掉标点
sentence = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()]+", "", sentence)
# 结巴分词
cut = jieba.cut(sentence)
#cut_list = [ i for i in cut ]
index=[]

for word in cut:
if word in word_index:
index.append(float(word_index[word]))

# if len(index)>25:
# index = index[0:25]
vecs.append(np.array(index))

return np.array(vecs)

首先得到数据的索引表示:

1
2
x_train_index = shopping_data.word2Index(x_train, word_index)
x_test_index = shopping_data.word2Index(x_test, word_index)

因为每句话长短不同,我们需要利用keras.preprocessing中的sequence进行一个对齐操作

1
2
3
maxlen = 25
x_train_index = sequence.pad_sequences(x_train_index, maxlen=maxlen)
x_test_index = sequence.pad_sequences(x_test_index, maxlen=maxlen)

首先创造一个Sequential模型,堆叠Embedding层,然后利用Flatten把数据平铺开,再送入一个256-256-256的神经网络中进行训练,最后利用sigmoid函数进行输出。

1
2
3
4
5
6
7
8
9
model = Sequential()
model.add(Embedding(trainable=Ture, input_dim=vocalen, output_dim=300, input_length=maxlen))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(256, activation='relu'))


model.add(Dense(1, activation='sigmoid'))

然后使用交叉熵代价函数和adam优化器,训练200回合,批尺寸为512。

1
2
3
4
5
6
7
model.compile(loss='binary_crossentropy', 
optimizer='adam',
metrics=['accuracy'])

model.fit(x_train_index, y_train,
batch_size=512,
epochs=200)

然后开始训练。得到评估结果。

86%的准确度。