PaddlePaddle文本卷积实现情感分类和微博女友情绪监控AI
本期文章我們將使用文本卷積和StackLSTM層來實現一個情感分類網絡,這樣你就可以擁有一個屬于自己的情感監控AI啦,甚至通過微博的接口來監控你女朋友的情緒。而要實現這一切,你不需要別的什么東西,你只需要一個微博認證開發者賬號,以及你女朋友的微博ID,當然還有我們牛逼的PaddlePaddle深度學習開發神器。噢,對了,我差點忘記了,首先你要有個女朋友。。。什么?你沒有?去淘寶買個二手充氣的吧。。。
開個玩笑,其實沒有女朋友也沒有關系啦,你可以用這個AI來監控任何你想監控的任何一個人。長久以來,人們都希望自己有一個人工智能,我是說,真正的人工智能,可以自動判別人類情感,并且將判別結果告知主人,這樣我們就可以從繁瑣的刷微博、看朋友圈等浪費時間的卻又有時候不得不做的事情中解放出來。設想有一個人工智能可以監控你喜歡的人的微博,甚至監控你的朋友圈,當TA發一些比較消極的消息時,能被我們的智能AI探測到,然后AI會通過郵箱或者短信等手段通知你,你收到之后便可能第一時間給予TA一個深情的安慰…繼而發展出一段曠世戀情…聽起來非常不錯吧?而這個東西就是我們本文要實現的東西。
0. 準備工作
啊,在開始之前,我們來捋一捋我們的思路,這將是一個不大卻略顯復雜的項目。
- 我們將需要一個微博賬號,這個微博賬號要能申請到微博接口,并且訪問它;
- 我們需要一個超強的情感分類數據集,畢竟我們是在搞AI,脫離數據都是扯淡;
- 我們需要用到PaddlePaddle,這在之前我們提到過,它在構建情感分類上有著得天獨厚的優勢,如果你還沒有入門,那么請看看我之前寫的入門博客,將PaddlePaddle當做一個輕量級框架來用確實是個不錯的主意。
哇,咋一看需要的東西真多,別急讓我們一步一步來,我們將會把一些準備工作簡單的介紹,但是主要的工作還是在,構建一個情感分類AI。首先當然我們既然要做一個微博情感監控,那么我們肯定要讓AI能夠access到微博的數據。所以說你要有個微博賬號啦,沒有的話申請一個。如果你想進一步看到我每天更新的博客文章,也可以來關注關注我啊,強行收粉,傳送門.
1. 微博開發者認證及應用創建
好吧,有句話怎么說來著,不會后臺開發的深度學習工程師不是好設計師….既然我們踏上了一條通往人工智能的不歸路那就得下定決心踩著坑了。說起微博開發者認證,你可能需要準備,你的身份證照片等信息。從這里進入開始開發者認證開發者認證連接. 在本文中就不詳細說明如何去認證了。一旦認證完了微博開發者,接下來就來創建一個移動應用。當然我們這里創建的移動應用并不是真正的移動應用,而是一個獲取API接口的機會。從微博開放平臺進入微連接,接入移動應用,這個時候會需要創建一個全新的應用。應用名稱就隨便填一個吧,應用平臺選擇其他,因為我們只要做一個Python后臺程序,所以也不需要用到什么Android或者iOS SDK,當然啦,你要創建一個應用需要這么一些前提:
- 你要有一個應用,其實為此你不必要搭建一個網站,可以直接用我的應用網址:www.luoli-luoli.com;
- 你需要給你的應用取一個名字,這個就隨便取啦,只要和已有的不重復即可,然后你可能還需要為你的應用設置一個logo,這個就看個人PS水平了。
這是我申請時候用的app:
如果你好奇為什么叫蘿莉蘿莉這個名字,是因為我的APP名字叫做蘿莉蘿莉…
好啦,相信你已經開始操作,操作完之后,你可能需要把這篇文章放入readlist,一天之后再來繼續看吧…因為微博應用審核需要一天。
2. 開始構建情感分類深度學習模型
我們是一個數據工作者,數據工作者的事情時候需要去自己尋找數據。我們已經有了一個大膽的 設想,做一個人工智能來監控女朋友的反常情感。那么我們肯定需要一些標注的數據集來訓練我們的模型,以此來實現一個可以判別情感的AI。在開始搜尋數據之前,讓我們來大膽的設想一下,加入我們把情感值分為兩類,Positive 和 Negative,可能情況會變得簡單一些,這個時候,情感分類的任務就變成了一個二分類問題,我們有時候只需要知道女朋友是開心還是傷心,或者說是中性,而對于其他的情感,我們目前可能不是非常關心。因此,我們將目標鎖定在中文情感分類數據集,我們暫且不去考慮分級過多的情感數據集,先從最簡單的開始。
讓我們來分析一下,要構建一個情感分類模型,初步來想有兩種方法:
- 第一種構建一個正面詞匯詞庫,和一個反面詞匯詞庫,這樣每次來一個新的句子的時候我們可以判斷是正面詞匯多呢,還是反面詞匯多,從而來決定一個句子是反面還是正面,盡管這種方法簡單易行,但是在遇到比如這樣的句子時:?你他媽今天還真的把我當紙老虎了是不??那么這句話中,他媽實際上是一個消極詞匯,但是卻不能歸到消極詞匯中,因為它也可能是一個中性詞匯;
- 第二種當然是使用深度學習的方法啦,深度學習構建出來的復雜模型,不僅僅可以根據標簽來判定哪些詞匯是正面的,哪些詞匯是負面的,同時也能夠學習到不同詞語在不同語境下所表現出來的消極以及正面性。
在本篇文章中,我們將使用一個stacked LSTM模型和一個文本卷積模型來實現情感的分類,為了簡化操作,我們將使用一個英文的電影評價數據集,用這個來做一個簡易的英文情感分類器,我們將使用PaddlePaddle訓練一個模型來對句子進行精準的分類。當然啦,如果大家希望把這個轉移到中文上,我在這里也提供一些中文方面的語料給大家,中國計算機中文信息技術會議的一個微博情感分類標注數據集,該數據集包含了20個話題,其中每個話題有正負兩種情感的評論總共約2000余條,下載地址?(百度網盤)在此。這個數據集中,每個話題的評論在一個xml文件中,xml中包含句子和詞性標注。
而Imdb數據集非常小巧,我們將在PaddlePaddle的代碼中直接實現下載,無需手動下載。
3. PaddlePaddle構建情感分類器 - 文本處理
如果之前對PaddlePaddle沒有什么了解,那么可以參照我之前寫的文章,與其他框架的對比,傳送門,簡而言之,我們之所以使用PaddlePaddle來構建這么一個應用是基于它的這么一些優點:
- 快速實現和部署,為什么我說快速,PaddlePaddle有著其他國外框架無法比擬的優勢,那就是健全的中文文檔,包括像我寫的這么一些非官方的文檔,對于新手搭建網絡來說非常的輕而易舉;
- 輕量級,我認為PaddlePaddle相對于其他的框架來說,輕量是它的一個非常好的優點,它沒有TensorFlow那么笨重,除此之外我甚至認為MXNet在輕量級上沒有它好。原因很簡單,從安裝到構建網絡到訓練我可以在一個腳本文件中完成整個Pipeline。
好啦,閑話不多說,讓我們先用PaddlePaddle來玩一些Playground的東西。在PaddlePaddle里面其實是內置了一些數據的,當然啦,這些數據會自動通過網絡下載,但是我們可以直接導入它,從而可以知道PaddlePaddle喂入數據的格式到底是什么,閑話不多說,直接上代碼:
| from __future__ import print_functionimport sysimport paddle.v2 as paddlefrom __future__ import print_functionimport sysimport paddle.v2 as paddleimport sysimport osimport jsonimport nltkif __name__ == '__main__':# initpaddle.init(use_gpu=False) print('load dictionary...')word_dict = paddle.dataset.imdb.word_dict() print(word_dict) |
簡單吧,一切就是如此的簡潔,大家可以看到打印出來的word dict其實就是一個詞袋,后面的數字表示的是這個詞的id,為什么要這么處理?這就是涉及到文本處理領域基本的東西了-word bag,詞袋法。我們知道一個神經網絡模型,不管它多復雜,它的輸入其實都是數字向量,那么文本怎么變成數字向量輸入到網絡里面去呢?我們知道圖片輸入到網絡好理解,因為圖片本身就是一個個的像素點啊。那文本要輸入網絡其實也非常簡單,只需要把文字映射成為一個int ID就可以了。至于怎么映射,直接對所有詞匯取一個詞袋,給它一個ID即可。
毫無疑問,如果大家要構建中文的情感分類器,那原理也是一樣的,只不過是對中文語料的進行一個詞袋和ID映射的處理。
4. PaddlePaddle構建情感分類器 - 網絡構建
其實情感分類也是一個分類任務,和圖片分類是一樣,而且情感分類是一個非常簡單的二分類問題。大家如果有想法的話可以發散為三分類四分類問題,那么對應的就是不同的情感等級。我們做一個簡易教程,當然無法做到非常深入,但是萬變不離其宗,非常期待大家繼續跟我一起關注PaddlePaddle的后續發展動態,我會在PaddlePaddle更新API之后不斷地維護這些代碼以及創造更多的教程來教大家怎么把這個框架用起來。閑話不多說,我們首先思考一下兩個問題:
- 圖片分類網絡是怎么構建的?
- 圖片分類的網絡可以用來分類文本嗎?
首先我們知道圖片分類用CNN分,那么CNN其實它的要求是一個二維的矩陣,文本也和圖片是一樣的,但是文本通過ID轉換之后得到的實際上是一個一維的向量,因為只有一句話。所以在這里有一個東西不得不傳授給大家,那就是embedding,這個embedding你可以理解為嵌入,為什么要嵌入,什么是嵌入?這個其實不難理解,意思就是你事先有一個矩陣,這個矩陣的每一個元素是一個隨機分布里面取的值,然后你在一個句子中的每一個ID,都映射到這個矩陣當中來,從而得到一個二維的矩陣,達到次嵌入的目的,一般情況下,詞嵌入是一個比較復雜的東西,如果把這個東西加入到網絡一起訓練的話,你甚至可以做你的word2vec模型了,好在PaddlePaddle已經幫我們處理好了這些問題,我們可以直接調用PaddlePaddle里面的embed層來把一維的句子,轉成CNN需要的二維。
在轉換之前,我們需要看一下Imdb的數據是怎么讀取的:
| def reader_creator(pos_pattern, neg_pattern, word_idx, buffer_size): # this unk is a tokenUNK = word_idx['<unk>'] # start a quen to using multi-processqs = [Queue.Queue(maxsize=buffer_size), Queue.Queue(maxsize=buffer_size)] def load(pattern, queue): for doc in tokenize(pattern):queue.put(doc)queue.put(None) def reader(): # Creates two threads that loads positive and negative samples # into qs.t0 = threading.Thread(target=load, args=(pos_pattern,qs[0], ))t0.daemon = Truet0.start()t1 = threading.Thread(target=load, args=(neg_pattern,qs[1], ))t1.daemon = Truet1.start() # Read alternatively from qs[0] and qs[1].i = 0doc = qs[i].get() while doc != None: yield [word_idx.get(w, UNK) for w in doc], i % 2i += 1doc = qs[i % 2].get() # If any queue is empty, reads from the other queue.i += 1doc = qs[i % 2].get() while doc != None: yield [word_idx.get(w, UNK) for w in doc], i % 2doc = qs[i % 2].get() return reader() |
這個方法其實已經內置在Paddle中,我們不需要寫它,但是為了讓大家能夠理解,我把它單獨拿出來講解一下,這個函數執行的操作其實非常簡單,那就是根據上面所得到的word dict,把文本的每一個句子轉換成一維的數字向量。由于Imdb里面是一句正情緒,一句負情緒,所以或有一個 %2的操作。
好了,接下來重點來了,我們要用PaddlePaddle構建我們的模型了,我之前提到了這個embed層,我們直接embed之后,接一個CNN來構建一個簡單的文本卷積分類網絡:
| def convolution_net(input_dim, class_dim=2, emb_dim=128, hid_dim=128): # we are starting with a embed layer data = paddle.layer.data("word",paddle.data_type.integer_value_sequence(input_dim)) emb = paddle.layer.embedding(input=data, size=emb_dim) # this convolution is a sequence convolution conv_3 = paddle.networks.sequence_conv_pool( input=emb, context_len=3, hidden_size=hid_dim) conv_4 = paddle.networks.sequence_conv_pool( input=emb, context_len=4, hidden_size=hid_dim) output = paddle.layer.fc( input=[conv_3, conv_4], size=class_dim, act=paddle.activation.Softmax()) lbl = paddle.layer.data("label", paddle.data_type.integer_value(2)) cost = paddle.layer.classification_cost(input=output, label=lbl)return cost, output |
可以說,這個網絡簡直到令人想哭,但是它并不是“簡單”,這里面有一個詞嵌入操作,緊接著是兩個卷積層,注意這里的卷基層并非是圖片卷積,而是文本序列卷積,這個應該是PaddlePaddle中特有的一個特殊層,百度在文本序列和語音序列處理上還是有一套,等一下大家會看到,這么一個簡單的模型可以在僅僅6個epoch就達到99.99%的精確度。embed的size是128,隱藏層神經元個數是128。大家其實完全不用關系這些網絡是怎么連接的,我們把訓練的代碼寫貼上來:
| from __future__ import print_functionimport sysimport paddle.v2 as paddleimport sysimport osimport jsonimport nltkdef convolution_net(input_dim, class_dim=2, emb_dim=128, hid_dim=128): data = paddle.layer.data("word",paddle.data_type.integer_value_sequence(input_dim)) emb = paddle.layer.embedding(input=data, size=emb_dim) conv_3 = paddle.networks.sequence_conv_pool( input=emb, context_len=3, hidden_size=hid_dim) conv_4 = paddle.networks.sequence_conv_pool( input=emb, context_len=4, hidden_size=hid_dim) output = paddle.layer.fc( input=[conv_3, conv_4], size=class_dim, act=paddle.activation.Softmax()) lbl = paddle.layer.data("label", paddle.data_type.integer_value(2)) cost = paddle.layer.classification_cost(input=output, label=lbl)return cost, output if __name__ == '__main__': # initpaddle.init(use_gpu=False) # those lines are get the codeprint('load dictionary...') word_dict = paddle.dataset.imdb.word_dict()print(word_dict) dict_dim = len(word_dict) class_dim = 2 train_reader = paddle.batch(paddle.reader.shuffle(lambda: paddle.dataset.imdb.train(word_dict), buf_size=1000), batch_size=100) test_reader = paddle.batch(lambda: paddle.dataset.imdb.test(word_dict), batch_size=100) feeding = {'word': 0, 'label': 1} # get the output of the model[cost, output] = convolution_net(dict_dim, class_dim=class_dim) parameters = paddle.parameters.create(cost) adam_optimizer = paddle.optimizer.Adam( learning_rate=2e-3, regularization=paddle.optimizer.L2Regularization(rate=8e-4), model_average=paddle.optimizer.ModelAverage(average_window=0.5)) trainer = paddle.trainer.SGD( cost=cost, parameters=parameters, update_equation=adam_optimizer)def event_handler(event): if isinstance(event, paddle.event.EndIteration): if event.batch_id % 100 == 0:print("\nPass %d, Batch %d, Cost %f, %s" % (event.pass_id, event.batch_id, event.cost, event.metrics)) else:sys.stdout.write('.')sys.stdout.flush() if isinstance(event, paddle.event.EndPass): with open('./params_pass_%d.tar' % event.pass_id, 'w') as f:trainer.save_parameter_to_tar(f) result = trainer.test(reader=test_reader, feeding=feeding)print("\nTest with Pass %d, %s" % (event.pass_id, result.metrics)) inference_topology = paddle.topology.Topology(layers=output) with open("./inference_topology.pkl", 'wb') as f:inference_topology.serialize_for_inference(f)trainer.train( reader=train_reader, event_handler=event_handler, feeding=feeding, num_passes=20) |
我們直接來跑起來看一下:
簡直amazing,2個epoch之后就達到了90%的準確度!!! 非常非常的impressive!!
5. PaddlePaddle構建情感分類模型 - 模型部署
好了,到了最最重要的時刻來了,我們辛辛苦苦訓練了兩天兩夜的模型,是時候看看它的威力了。此時此刻,你的項目文件夾的目錄最少要跟我一樣:
我現在只有一個main.py,這里面就是我們訓練的腳本。我們有一個inference_topology.pkl,這個是我們的網絡模型保存的二進制文件。大家注意了,這是我見過的最清晰的網絡保存和權重保存方式!!沒有之一!!PaddlePaddle的網絡模型保存在了pkl,權重是一個tar的壓縮文件!!!。這個比TensorFlow或者MXNet要人性化很多!!MXNet jb的根本不知道保存到哪里去了, TensorFlow還得手動寫一個腳本來frozen一個模型,PaddlePaddle一步到位,非常牛逼!!
為了讓大家體驗一下預測的快感,我直接把代碼貼出來了:
| # -*- coding: utf-8 -*-# file: predict.py# author: JinTian# time: 16/11/2017 8:17 PM# Copyright 2017 JinTian. All Rights Reserved.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# ------------------------------------------------------------------------import numpy as npimport sysimport paddle.v2 as paddlefrom __future__ import print_functionimport sysimport osimport jsonimport nltkdef convolution_net(input_dim, class_dim=2, emb_dim=128, hid_dim=128, is_predict=False): data = paddle.layer.data("word",paddle.data_type.integer_value_sequence(input_dim)) emb = paddle.layer.embedding(input=data, size=emb_dim) conv_3 = paddle.networks.sequence_conv_pool( input=emb, context_len=3, hidden_size=hid_dim) conv_4 = paddle.networks.sequence_conv_pool( input=emb, context_len=4, hidden_size=hid_dim) output = paddle.layer.fc(input=[conv_3, conv_4], size=class_dim, act=paddle.activation.Softmax()) if not is_predict: lbl = paddle.layer.data("label", paddle.data_type.integer_value(2)) cost = paddle.layer.classification_cost(input=output, label=lbl)return cost else:return outputif __name__ == '__main__': # Movie Reviews, from imdb testpaddle.init(use_gpu=False) word_dict = paddle.dataset.imdb.word_dict() dict_dim = len(word_dict) class_dim = 2 reviews = ['Read the book, forget the movie!','This is a great movie.']print(reviews) reviews = [c.split() for c in reviews] UNK = word_dict['<unk>'] input = []for c in reviews:input.append([[word_dict.get(words, UNK) for words in c]]) # 0 stands for positive sample, 1 stands for negative sample label = {0: 'pos', 1: 'neg'} # Use the network used by trainer out = convolution_net(dict_dim, class_dim=class_dim, is_predict=True) parameters = paddle.parameters.create(out)print(parameters) # out = stacked_lstm_net(dict_dim, class_dim=class_dim, stacked_num=3, is_predict=True) probs = paddle.infer(output_layer=out, parameters=parameters, input=input)print('probs:', probs) labs = np.argsort(-probs)print(labs)for idx, lab in enumerate(labs):print(idx, "predicting probability is", probs[idx], "label is", label[lab[0]]) |
讓我們來看一下預測的結果:
6. 后記
我們已經可以用PaddlePaddle構建自己的深度學習應用了!!!這次是情感分類!!這是一個非常不錯的開端,我們已經有了自己的預測腳本,接下來只需要把我們的預測腳本和你的微博應用程序結合起來,當檢測到女友的情緒不穩定時,就通過郵件通知你。當然這部分工作一直到現在我都沒有通過微博的審核。。。。(無力吐槽微博ing)。不管怎么用,我們不得不再次贊一下百度PaddlePaddle的開發團隊,用PaddlePaddel構建模型沒有太多的abstraction,構建出來的模型非常簡單便捷,如果要在深度學習和人工智能領域定義一個敏捷開發的代表,那么PaddlePaddle非它莫屬啦~~。
7. 擴展
實現情感分類其實只是PaddlePaddle應用的冰山一角,我們可以通過這個基礎的應用來實現無數的創意深度學習應用,就像Android操作系統一樣,雖然底層的API都是一樣但是卻可以在這個基礎之上建造微信,支付寶,直播APP這樣的功能多樣的應用程序。本文雖然給大家展示的是一個微博情感監控,大家也可以把這個東西應用在自己的APP當中,比如,根據用戶發的評論來回復的情感極性來回復相應的話語,比如用在自己的聊天機器人中,使得它更加具有情感性,都是不錯的應用。如果大家有什么好的想法和創意,也可以在原始博客下面評論與我互動,我會把更好的idea更新在我后面的博客中,期待你的創意!
本期列車到此結束,如果大家對本文由任何疑問,歡迎通過微信找到我,也歡迎大家訂閱本文的首發地址也是永久更新維護地址:?https://jinfagang.github.io
總結
以上是生活随笔為你收集整理的PaddlePaddle文本卷积实现情感分类和微博女友情绪监控AI的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android中的人脸检测入门
- 下一篇: [读书笔记] 深入探索Android热修