【QLBD】LSTM情感分析实验复现(三)one embedding

注1:参考学习  科学空间论坛   苏剑林 的博文

注2:记录实验复现细节,并根据版本更新对代码作出修正。

注3:python3.5;keras2.0.9

【QLBD】LSTM情感分析实验复现(一)one-hot encoding

【QLBD】LSTM情感分析实验复现(二)分词&one-hot

【QLBD】LSTM情感分析实验复现(三)one embedding

【QLBD】LSTM情感分析实验复现(四)word embdding

上一篇实验一使用one-hot的方法表示 单个字,本篇将采用字向量的方法表示字,也就是原博所说的one embdding。Word2vec 的思想是用训练得到的高维词向量来表示一个英文单词,并且单词之间的某些关系在词向量的运算上可以体现出来。但是英文与中文又有不同,英文的每一个单词单独成词,而中文的单字成词的情况并不多。one-hot的方法缺陷在与字与字之间的关系完全相同,事实上并非如此。如果以字为基本单元训练词向量,则可以捕捉到字之间的某些联系。

这里说一下原博在博文中提到的“只有one hot和word embedding才是理论上说得过去的,而one embedding则看上去变得不伦不类了,因为字似乎不能说具有比较确定的意思。”我觉得这里还要说到词向量的发展上,词向量首先是以英文作为研究对象的,英文的最小单位是24个英文字母,这些字母是不带任何含义的(不包括部分冠词),而再上一级的是各个英文单词,每一个都单独成词,且都包含意思,显然以每个字母为最小语义单位生成词向量并没有任何意义,但是以每个单词为单位生成词向量是有意义的。而对应到中文,中文最小的语义单元是字,很多字都有具体的含义,一种或者多种含义;字与字组合成词,其表述的意义相对于单个字的含义会减少,但是含义更准确。一个英文单词可能对应一个词,也可能对应一个字,这样以字为最小语义单位和以词为语义单位都是有意义的。其实我们可以将one embdding 看做是解决one-hot无法衡量两个字之间联系问题的一种方法,当然字向量挖掘出来的字与字之间的关系是否靠谱有待验证商榷。以上仅为个人理解,不当之处欢迎指出讨论

字向量的训练将直接使用keras中的embdding层。

本次实验测试one embdding 的方法:以字为单位,不分词,将每个句子截断为200字(不够则补空字符串),然后将句子以字向量矩阵的形式输入到LSTM模型中进行学习分类。

一、程序模块调用

import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout, Embedding
from keras.layers import LSTM

二、语料

pos = pd.read_excel('E:/Coding/yuliao/pos.xls',header=None,index=None)#10677篇
pos['label'] = 1
neg = pd.read_excel('E:/Coding/yuliao/neg.xls',header=None,index=None)#10428篇
neg['label'] = 0
all_ = pos.append(neg, ignore_index=True)
三、部分数值设置

maxlen = 200 #截断字数
min_count = 20 #出现次数少于该值的字扔掉。这是最简单的降维方法
四、语料预处理一

content = ''.join(all_[0]) #所有语料拼接成一个字符串
abc = pd.Series(list(content)).value_counts() #统计每个字的频率
abc = abc[abc >= min_count] #去掉低频字,简单降维
abc[:] = range(1, len(abc)+1) #用0-2416间的整数对每个字按顺序重新赋值,一个整数代表一个字
abc[''] = 0 #添加空字符串用来补全
word_set = set(abc.index) #构建字典
五、语料预处理二

def doc2num(s, maxlen):  #构建将文本转化为数字向量的函数,maxlen=200
    s = [i for i in s if i in word_set]
    s = s[:maxlen] + ['']*max(0, maxlen-len(s)) #200字,多截少补空
    return list(abc[s])

all_['doc2num'] = all_[0].apply(lambda s: doc2num(s, maxlen)) #使用函数将文本转化为数字向量  
##all_中的文本向量表示形式:[8, 65, 795, 90, 152, 152, 289, 37, 22, 49, 12...  
六、打乱数据

idx = list(range(len(all_))) #生成实际的索引列表  
2.np.random.shuffle(idx) #根据索引打乱文本顺序  
3.all_ = all_.loc[idx] #重新生成表格  
七、按要求规范数据格式 

#按keras的输入要求来生成数据
x = np.array(list(all_['doc2num']))
y = np.array(list(all_['label']))
y = y.reshape((-1,1)) #调整标签形状
# x:array([[250, 245,  41, ...,   0,   0,   0],
       [ 15,  50,  62, ...,   0,   0,   0],
       [323,  15,  38, ...,   0,   0,   0],
       ..., 
       [246,  25, 277, ...,   0,   0,   0],
       [  4,  22,  17, ...,   0,   0,   0],
       [ 30, 645,   1, ...,   0,   0,   0]], dtype=int64)
# y:array([[0],
       [1],
       [0],
       ..., 
       [0],
       [1],
       [1]], dtype=int64)
八、建模训练

model = Sequential()
model.add(Embedding(len(abc), 256, input_length=maxlen)) # embdding层,生成字向量
model.add(LSTM(128))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

batch_size = 128 #批尺寸
train_num = 15000#训练集数量
model.fit(x[:train_num], y[:train_num], batch_size = batch_size, epochs=30)#不使用生成器,直接训练模型,30轮。
九、测试模型

model.evaluate(x[train_num:], y[train_num:], batch_size = batch_size)
十、结果

实验使用GPU型号为Geforce 940MX(对,还是它),每轮耗时在62s左右,训练了30轮。

训练集准确率为:0.9765,测试集准确率为:0.89238329277382245。

相比于one-hot 的方法其测试准确率是有所提升的。

过程如下:

训练过程:
Epoch 1/30
15000/15000 [==============================] - 64s 4ms/step - loss: 0.6918 - acc: 0.5192
Epoch 2/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.6777 - acc: 0.5961
Epoch 3/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.6660 - acc: 0.6141
Epoch 4/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.6632 - acc: 0.6038
Epoch 5/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.6609 - acc: 0.5890
Epoch 6/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.5941 - acc: 0.6595
Epoch 7/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.5567 - acc: 0.6989
Epoch 8/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.4639 - acc: 0.7933
Epoch 9/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.4576 - acc: 0.8027
Epoch 10/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.3546 - acc: 0.8655
Epoch 11/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.3315 - acc: 0.8663
Epoch 12/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.2799 - acc: 0.8967
Epoch 13/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.2474 - acc: 0.9125
Epoch 14/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.2196 - acc: 0.9263
Epoch 15/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.2066 - acc: 0.9318
Epoch 16/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1961 - acc: 0.9354
Epoch 17/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1807 - acc: 0.9440
Epoch 18/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1607 - acc: 0.9526
Epoch 19/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1870 - acc: 0.9365
Epoch 20/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1525 - acc: 0.9548
Epoch 21/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1323 - acc: 0.9641
Epoch 22/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1829 - acc: 0.9389
Epoch 23/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1474 - acc: 0.9559
Epoch 24/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1282 - acc: 0.9645
Epoch 25/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1155 - acc: 0.9694
Epoch 26/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1033 - acc: 0.9747
Epoch 27/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1066 - acc: 0.9731
Epoch 28/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1011 - acc: 0.9747
Epoch 29/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.1013 - acc: 0.9733
Epoch 30/30
15000/15000 [==============================] - 62s 4ms/step - loss: 0.0934 - acc: 0.9765
#测试:[0.37036194634867142, 0.89238329277382245]#[loss,acc]
十一、预测单个句子

def predict_one(s): #单个句子的预测函数
    s = np.array(doc2num(s, maxlen))
    s = s.reshape((1, s.shape[0]))
    return model.predict_classes(s, verbose=0)[0][0]
comment = pd.read_excel('E:/Coding/comment/sum.xls')
comment = comment[comment['rateContent'].notnull()]
comment['text'] = comment['rateContent']
#取一百篇用模型预测
aaaa = pd.DataFrame(comment['text'][500:600])
aaaa['result']=aaaa.text.apply(lambda s: predict_one(s))
结果如图:
注:预测单个句子的情感这里只展示了部分,虽有明显错误(某种角度上来说不能算是错误,很明显855没有明显的正、负倾向,861有正向倾向则是误判),但总体上来说one embdding 的效果略好于one-hot的方法。
【QLBD】LSTM情感分析实验复现(三)one embedding_第1张图片
(实验三  完)

你可能感兴趣的:(深度学习笔记)