text.html 10.4 KB
Newer Older
ToTensor's avatar
ToTensor 已提交
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
 
<p class="content">理论介绍完了,下面回到鉴定评论文本的情感属性这个案例。先把这些文本向量化,然后用Keras中最简单的循环网络神经结构—Simple RNN层,构建循环神经网络,鉴定一下哪些客户的留言是好评,哪些是差评。</p> 
<h3 class="thirdTitle" id="bw204"><a >7.4.1 用Tokenizer给文本分词</a></h3> 
<p class="content">同学们可以在Kaggle网站通过关键字Product Comments搜索该数据集,然后基于该数据集新建Notebook。</p> 
<p class="content">读入这个评论文本数据集:</p> 
<div class="content_106"> 
 <p class="content_105">import pandas as pd # 导入Pandas</p> 
 <p class="content_105">import numpy as np # 导入Num Py</p> 
 <p class="content_105">dir = '../input/product-comments/'</p> 
 <p class="content_105">dir_train = dir+'Clothing Reviews.csv'</p> 
 <p class="content_105">df_train = pd.read_csv(dir_train) # 读入训练集</p> 
 <p class="content_105">df_train.head() # 输出部分数据</p> 
</div> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0251-0347.jpg"> 
 <p class="imgtitle">训练集中的前五条数据</p> 
</div> 
<p class="content">然后对数据集进行分词工作。词典的大小设定为2万。</p> 
<div class="content_106"> 
 <p class="content_105">from keras.preprocessing.text import Tokenizer # 导入分词工具</p> 
 <p class="content_105">X_train_lst = df_train["Review Text"].values # 将评论读入张量(训练集)</p> 
 <p class="content_105">y_train = df_train["Rating"].values # 构建标签集</p> 
 <p class="content_105">dictionary_size = 20000 # 设定词典的大小</p> 
 <p class="content_105">tokenizer = Tokenizer(num_words=dictionary_size) # 初始化词典</p> 
 <p class="content_105">tokenizer.fit_on_texts( X_train_lst ) # 使用训练集创建词典索引</p> 
 <p class="content_105"># 为所有的单词分配索引值,完成分词工作</p> 
</div> 
<p class="content">X_train_tokenized_lst = tokenizer.texts_to_sequences(X_train_lst)</p> 
<p class="content">分词之后,如果随机显示X_train_tokenized_lst的几个数据,会看到完成了以下两个目标。</p> 
<p class="content">■评论句子已经被分解为单词。</p> 
<p class="content">■每个单词已经被分配一个唯一的词典索引。</p> 
<p class="content">X_train_tokenized_lst目前是列表类型的数据。</p> 
<div class="content_113"> 
 <p class="content_109">[[665, 75, 1, 135, 118, 178, 28, 560, 4639, 12576, 1226, 82, 324, 52, 2339, 18256,</p> 
 <p class="content_109">51, 7266, 15, 63, 4997, 146, 6, 3858, 34, 121, 1262, 9902, 2843, 4, 49, 61, 267, 1,</p> 
 <p class="content_109">403, 33, 1, 39, 27, 142, 71, 4093, 89, 3185, 3859, 2208, 1068],</p> 
 <p class="content_109">[18257, 50, 2209, 13, 771, 6469, 71, 3485, 2562, 20, 93, 39, 952, 3186, 1194, 607,</p> 
 <p class="content_109">5886, 184],</p> 
 <p class="content_109">… … … …</p> 
 <p class="content_109">[5, 1607, 19, 28, 2844, 53, 1030, 5, 637, 40, 27, 201, 15]]</p> 
</div> 
<p class="content">还可以随机显示目前标签集的一个数据,目前y_train是形状为(22 641,)的张量:</p> 
<p class="content_112">[4]</p> 
<p class="content">下面将通过直方图显示各条评论中单词个数的分布情况,这个步骤是为词嵌入做准备:</p> 
<div class="content_106"> 
 <p class="content_105">import matplotlib.pyplot as plt # 导入matplotlib</p> 
 <p class="content_105">word_per_comment = [len(comment) for comment in X_train_tokenized_lst]</p> 
 <p class="content_105">plt.hist(word_per_comment, bins = np.arange(0,500,10)) # 显示评论长度分布</p> 
 <p class="content_105">plt.show()</p> 
</div> 
<p class="content">评论长度分布直方图如下图所示。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0252-0348.jpg"> 
 <p class="imgtitle">评论长度分布</p> 
</div> 
<p class="content">上图中的评论长度分布情况表明多数评论的词数在120以内,所以我们只需要处理前120个词,就能够判定绝大多数评论的类型。如果这个数目太大,那么将来构造出的词嵌入张量就达不到密集矩阵的效果。而且,词数太长的序列,Simple RNN处理起来效果也不好。</p> 
<p class="content">下面的pad_sequences方法会把数据截取成相同的长度。如果长度大于120,将被截断;如果长度小于120,将填充无意义的0值。</p> 
<div class="content_106"> 
 <p class="content_105">from keras.preprocessing.sequence import pad_sequences</p> 
 <p class="content_105">max_comment_length = 120 # 设定评论输入长度为120,并填充默认值(如字数少于120)</p> 
 <p class="content_105">X_train = pad_sequences(X_train_tokenized_lst, maxlen=max_comment_length)</p> 
</div> 
<p class="content">至此,分词工作就完成了。此时尚未做词嵌入的工作,因为词嵌入是要和神经网络的训练过程中一并进行的。</p> 
<h3 class="thirdTitle" id="bw205"><a >7.4.2 构建包含词嵌入的SimpleRNN </a></h3> 
<p class="content">现在通过Keras来构建一个含有词嵌入的Simple RNN:</p> 
<div class="content_106"> 
 <p class="content_105">from keras.models import Sequential # 导入序贯模型</p> 
 <p class="content_105">from keras.layers.embeddings import Embedding #导入词嵌入层</p> 
 <p class="content_105">from keras.layers import Dense #导入全连接层</p> 
 <p class="content_105">from keras.layers import Simple RNN #导入Simple RNN层</p> 
 <p class="content_105">embedding_vecor_length = 60 # 设定词嵌入向量长度为60</p> 
 <p class="content_105">rnn = Sequential() #序贯模型</p> 
 <p class="content_105">rnn.add(Embedding(dictionary_size, embedding_vecor_length,</p> 
 <p class="content_111">input_length=max_comment_length)) # 加入词嵌入层</p> 
 <p class="content_105">rnn.add(Simple RNN(100)) # 加入Simple RNN层</p> 
 <p class="content_105">rnn.add(Dense(10, activation='relu')) # 加入全连接层</p> 
 <p class="content_105">rnn.add(Dense(6, activation='softmax')) # 加入分类输出层</p> 
 <p class="content_105">rnn.compile(loss='sparse_categorical_crossentropy', #损失函数</p> 
 <p class="content_111">optimizer='adam', # 优化器</p> 
 <p class="content_111">metrics=['acc']) # 评估指标</p> 
 <p class="content_105">print(rnn.summary()) #输出网络模型</p> 
</div> 
<p class="content">神经网络的构建我们已经相当熟悉了,并不需要太多的解释,这里的流程如下。</p> 
<p class="content">■先通过Embedding层进行词嵌入的工作,词嵌入之后学到的向量长度为60(密集矩阵),其维度远远小于词典的大小20 000(稀疏矩阵)。</p> 
<p class="content">■加一个含有100个神经元的Simple RNN层。</p> 
<p class="content">■再加一个含有10个神经元的全连接层。</p> 
<p class="content">■最后一个全连接层负责输出分类结果。使用Softmax函数激活的原因是我们试图实现的是一个从0到5的多元分类。</p> 
<p class="content">■编译网络时,损失函数选择的是sparse_categorical_crossentropy,我们是第一次使用这个损失函数,因为这个训练集的标签,是1,2,3,4,5这样的整数,而不是one-hot编码。优化器的选择是adam,评估指标还是选择acc。</p> 
<p class="content">网络结构如下:</p> 
<div class="content_113"> 
 <p class="content_109">_________________________________________________________________</p> 
 <p class="content_109">Layer (type)         Output Shape        Param #</p> 
 <p class="content_109">=================================================================</p> 
 <p class="content_109">embedding_1 (Embedding)   (None, 300, 60)      1200000</p> 
 <p class="content_109">_________________________________________________________________</p> 
 <p class="content_109">simple_rnn_1 (Simple RNN)   (None, 100)        16100</p> 
 <p class="content_109">_________________________________________________________________</p> 
 <p class="content_109">dense_1 (Dense)        (None, 10)         1010</p> 
 <p class="content_109">_________________________________________________________________</p> 
 <p class="content_109">dense_2 (Dense)        (None, 6)         66</p> 
 <p class="content_109">=================================================================</p> 
 <p class="content_109">Total params: 1,217,176</p> 
 <p class="content_109">Trainable params: 1,217,176</p> 
 <p class="content_109">Non-trainable params: 0</p> 
 <p class="content_109">_________________________________________________________________</p> 
</div> 
<h3 class="thirdTitle" id="bw206"><a >7.4.3 训练网络并查看验证准确率</a></h3> 
<p class="content">网络构建完成后,开始训练网络:</p> 
<div class="content_106"> 
 <p class="content_105">history = rnn.fit(X_train, y_train,</p> 
 <p class="content_119">validation_split = 0.3,</p> 
 <p class="content_119">epochs=10,</p> 
 <p class="content_119">batch_size=64)</p> 
</div> 
<p class="content">这里在训练网络的同时把原始训练集临时拆分成训练集和验证集,不使用测试集。而且理论上Kaggle竞赛根本不提供验证集的标签,因此也无法用验证集进行验证。</p> 
<p class="content">训练结果显示,10轮之后的验证准确率为0.5606:</p> 
<div class="content_113"> 
 <p class="content_109">Train on 7000 samples, validate on 3000 samples</p> 
 <p class="content_109">Epoch 1/10</p> 
 <p class="content_109">15848/15848 [============================] - 24s 1ms/step - loss: 1.2480 - acc:</p> 
 <p class="content_109">0.5503 - val_loss: 1.2242 - val_acc: 0.5429</p> 
 <p class="content_109">Epoch 2/10</p> 
 <p class="content_109">15848/15848 [============================] - 35s 2ms/step - loss: 1.1596 - acc:</p> 
 <p class="content_109">0.5622 - val_loss: 1.1692 - val_acc: 0.5520</p> 
 <p class="content_109">… …</p> 
 <p class="content_109">Epoch 10/10</p> 
 <p class="content_109">15848/15848 [==============================] - 24s 2ms/step - loss: 0.8630 - acc:</p> 
 <p class="content_109">0.6456 - val_loss: 1.1032 - val_acc: 0.5606</p> 
</div> 
<p class="content">如果采用其他类型的前馈神经网络,其效率和RNN的成绩会相距甚远。如何进一步提高验证集准确率呢?我们下面会继续寻找方法。</p>