考虑一个客户服务满意度的场景, 客户和客服人员进行会话, 会话结束后, 可以通过系统智能的将会话过程进行分类.
这是一个监督学习文本分类问题, 需要提前进行数据标注. 通过将客服人员和客户之间的聊天内容打上满意度标签进行训练数据的准备. 最满意是 5 颗星, 最不满意是 0 颗星.
在训练之前要准备词向量模型, 非常遗憾我大中国, 没有什么可以使用的开放的中文的语料 (可悲, 可叹, 国家的钱都白花了), 我们使用了中文 wiki.
把对话内容进行分词, 然后通过加载词向量模型找到每个词的向量, 如果找不到就用一个默认向量替代 (惨), 然后每个会话就构成了一个二维矩阵, 每行是一个词向量, 多少行就是这个会话中有多少词, 就是所谓的时间序列, 因为话是从前往后说.(你见到倒着说话的吗, 可能也有, 要不然怎么叫做倒背如流)
这里使用 GRU, 相当于简化的 LSTM 吧, 广义上都是 RNN(模型就不深说了, 只讲怎么用, 别用错了); 一个 RNN 网络实际上就是能够把时间序列数据一个个输入, 这里就是一个个词向量进行输入 (再加上上下文数据), 然后 RNN 网络输出两个值, 一个是正常的输出, 一个是上下文输出. 对于第一个词向量是没有上一个词向量的上下文输出, 往往会指定一个随机值. 然后第二次网络训练就是利用第二个词向量和第一个词向量通过网络计算出来的上下文输出作为 RNN 的网络输入, 如此下去, 直到把一个对话的词向量都用光; 这时候取得最后一个网络输出 (注意不是上下文输出), 用这个输出作为标签值. 与真正的标签值进行对比, 一般用交叉熵, 当然前提是设计好网络结构. 通过利用交叉熵的损失计算, 来进行反向传播, 优化网络参数. 再重新利用数据训练.
值得注意的是 RNN 网络的两个输出, 一个是个上下文输出, 用于序列的输入; 一个是网络输出, 整个序列中只有最后一个有用处, 如果一个会话中有 N 个分词, 那么 1 到 N-1 的序列产生的网络输出都直接扔掉. 只有第 N 个用来做损失计算, 反向传播计算, 调整参数. 同样也可以看出来, N 次使用 RNN 才反向一次.
这个训练过程有点类似把序列数据进行高密度压缩了, 像压缩编码一样.
下面看如何通过 Pytorch 实现:
Pytorch 有做好的 GRU 模型, 这个模型提供的便利是, 支持给模型一次提供多个时间序列, 就是给 RNN 网络提供的输入 是三个维度, BATCH,TIME SEQ,DATA,BATCH 就是在我们讨论的情景里面就是多少个会话, TIME SEQ 就是一个会话中多少个词向量, DATA 就是词向量. 维度的位置可以在初始化的时候指定, BATCH_FIRST 标识第一个维度是 BATCH, 如果不指定这个, 那么第一个维度是 TIME SEQ, 第二个维度是 BATCH.
Pytorch 的样例中使用了内置的词嵌入, 通过翻阅很多 Pytorch 的 GITHUB 项目, 我发现它深深迷惑了众生. Pytorch 样例本意是帮助使用者能够冷启动, 不依赖词嵌入模型, 但是实际应用中没有词嵌入模型是可能是玩不了的, 于是对于使用者来说不知道怎么把样例的 embedding 过程去掉. 其实非常简单, 只要概念清楚, 很容易应用自己的词向量模型, 很多人建议 copy_词向量模型, 这可真不是一个好办法, 貌似重用了原来的代码, 但是更容易混淆概念.
Pytorch 需要特别重视的就是模型的 INPUT 和 OUTPUT 的维度意义, BATCH TIME SEQ 和 DATA; 还有损失函数的使用, 因为 Pytorch 的交叉熵需要的不是 ONE HOT 编码, 它需要一个标量值, 自动转换成 ONE HOT. 所以训练数据的标签, 应该就是 0-5,0 表示没有星, 5 表示 5 颗星.
来源: http://www.jianshu.com/p/46d9dec06199