2022-10-10AI00
请注意,本文编写于 53 天前,最后修改于 53 天前,其中某些信息可能已经过时。

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

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

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

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

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

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

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

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

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)

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

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

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

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函数进行输出。

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。

model.compile(loss='binary_crossentropy', 
              optimizer='adam',
              metrics=['accuracy'])

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

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

屏幕截图 2022-01-08 153810.png

86%的准确度。

本文作者:Ch1nfo

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!