NLPCC2013中文微博細粒度情感識別(二)
偷懶若干天后迴歸。。在上一篇中我們得到了NLPCC2013的中文微博資料,將其按照8:1:1的比例分成了訓練集,驗證集和測試集。下一步就是對資料進行預處理以及embedding。這是第一次嘗試一邊寫部落格一邊把自己的想法記錄下來,希望有所幫助。
分析:按照體量的大小可以將文字分為字級別->詞級別->實體級別->句子級別->段落級別->文章級別。由於我們的任務主要是情感識別,通常情況下可以代表情感的往往只是某些字或詞,所以對於資料的預處理最多進行到分詞,不許要做NER。只要基於當前資料做出正確的分詞並且embedding,即可作為文章的輸入。分詞工具在這裡選擇開源並且遵守MIT(個人理解為可以商用)的jieba分詞。先看一下資料的分詞難點:
#回覆@王羊羊的微博:是,要的就是安靜的生存環境,而且,到北京五環25分#鍾車程,938離我住處只有300米,飲食一條街更是北京飲食街的翻版,應有盡有。 #//@王羊羊的微博:廊坊是個好地方,緊鄰京津,有點鬧中取靜的意思。 #[抓狂]我竟然被那個考了車牌幾個月但一直沒有開過車的老媽笑話我笨! #這家位於檳城George Town,Tunes Hotel斜對面的餅家,家庭式作業,她#的鹹蛋酥,年糕椰蓉酥,叉燒酥都很不錯。[good] #做人原則:⑴能不罵人,就不罵! #聯絡方式:13810938511 QQ:122095021
以上幾條微博可以反映出這個資料存在的某些問題:存在回覆標籤,存在表情符號,中英混用,全半形符號混用,數字問題。之前也做過推特的情感分類,發現同為社交媒體,中文社交媒體的資料處理要比英文複雜的多。對於這種情況,我首先觀察了一下資料特點,然後直接根據每種不同情況作了相應處理,在這裡不再贅述,想看的朋友們可以直接看最後放出的程式碼,寫的還算比較清楚,每個功能都寫成了一個小函式。
本來想分詞之後直接訓練embedding的,但是考慮到一共才4w多個樣本,而且很多都不符合一般的語言規範,想了想還是用了別人的embedding。連結在此:https://github.com/facebookresearch/fastText/blob/master/pretrained-vectors.md 但是考慮到用了別人的embedding正常情況下就可以開始搭模型了。在這裡多做了一步工作就是對資料進行了一個視覺化。程式碼同樣在下面。
視覺化分為了兩個部分:構建詞雲和詞向量的視覺化。前者基本可以理解為找到文字中的若干關鍵詞並可視化,後者即為將embedding降維後得到的結果。
先看一下詞雲:
可以看到由於資料來源是網路文字,即使去掉停詞後還是有大量的口語/無意義詞彙,但是好在從其中一些小字也可以找到一些微博早期大家關注的熱點,如果沿時間橫向對比的話,應該是可以得到一些有意思的結論的。最直觀的一個結論可以看到詞雲中出現的兩個國家美國和日本,代表了網民門討論的熱點國家,還是比較符合實際的。下面一張圖做了一些美化,本質上還是這個東西。
做完了詞雲,再來看一下詞向量降維得到的結果。經過統計資料裡面實際上有兩萬多個可以和詞向量對應的詞語,全部顯示基本是不可能了,差點沒把我這破air累死。最終選取了其中500個詞,儘管這樣還是需要截圖才能看到一個大概的趨勢。其實這樣一個統計就類似於聚類的結果統計,只不過使用cosine距離作為度量,具體實現是用的sklearn的tsne模組。看一看哪些和哪些是比較相似的。也檢查一下embedding的情況怎麼樣,是否能反映語義資訊。整個圖裡面的點大概聚成了即堆,挑有代表性的討論討論。
可以看到一些平時相關的詞語確實距離非常近
這裡是第二堆詞,其實和第一堆並不能發現什麼太大的差別,導致出現兩堆的情況很有可能是因為這兩堆都是高維空間投射到2維空間的影子。實際上兩堆各自內部的詞應該還與另一堆有關聯。
這一堆基本都是單個字或者標點符號,沒有吧標點符號全部去掉的原意主要就是因為像!?...這樣的符號其實是很大程度上可以反映情緒的,所以除了一些確實不必要的其他都保留了下來。但是可以看到也有一些小瑕疵,比如/這種符號還是沒有去幹淨。
其實以上這些視覺化不僅可以用來做情感分析,還可以做一些輿情分析,本質上是一樣的。下面Po出所有原始碼,寫的不太好還請各路大神多指教~我只是個小白哈哈~
import jieba import re import gensim import collections import matplotlib.pyplot as plt from wordcloud import WordCloud from wordcloud import STOPWORDS import numpy as np from PIL import Image from gensim.models import KeyedVectors from sklearn.manifold import TSNE import tqdm import matplotlib from matplotlib import font_manager #matplotlib.use('qt4agg') input_file = open("data_all.txt") counter = 0 corpora = [] real_vocab = [] """ 第一部分,預處理和分詞 """ def chn_tokenize(sentence): # 使用jieba分詞實現完整的中文資料集分詞 line_list = jieba.lcut(sentence, HMM=True) return line_list #print(line_list) def del_reply_mark(sentence): output = re.sub('@.*?:', '', sentence).strip("回覆").strip("//") output = re.sub('@.*? ', '', output) if output == "": output = "***" #print(output) return output def rep_chn_punc(sentence): table = {ord(f): ord(t) for f, t in zip( u',。!?【】()%#@&1234567890①②③④⑤、·:[]():;', u',.!?....%#@&123456789012345,........')} output = sentence.translate(table).replace("...", "…").replace("《", "").replace("》", "").replace("℃", "度")\ .replace("——", "").replace("..", "…").replace("「", "").replace("」", "").replace("....", "…")\ .replace(".....", "…").replace(".", "").replace(",", "").replace("-", "").replace("T T", "TT")\ .replace("T_T", "TT") return output def clean_pun(sentence): strip_special_chars = re.compile("[^A-Za-z0-9]+") output = re.sub(strip_special_chars, "", sentence) return output for i in tqdm.tqdm(input_file.readlines()): sen = rep_chn_punc(i) sen = del_reply_mark(sen).strip("\n").strip("\t").strip(".") counter += 1 #print(sen) word_list = chn_tokenize(sen) try: while word_list.index(" "): del word_list[word_list.index(" ")] except ValueError: #print(word_list) corpora.append(word_list) for j in range(len(word_list)): if word_list[j] not in real_vocab: real_vocab.append(word_list[j]) print(counter) """ 第二部分, """ def word_freq(text): corpora_in_line = [] for line in range(len(text)): corpora_in_line.extend(text[line]) frequency = collections.Counter(corpora_in_line) print(frequency) def word_cloud(text, path): stopwords = set(STOPWORDS) weibo = np.array(Image.open(path)) stopwords.add("一個") corpora_in_str = "" for line in range(len(text)): str_text = " ".join(text[line]) #print(str_text) corpora_in_str += str_text my_wordcloud = WordCloud(font_path='SimHei.ttf', stopwords=STOPWORDS , width=1500, height=750, background_color="black").generate(corpora_in_str) plt.imshow(my_wordcloud) plt.axis("off") plt.show() def plot_with_labels(low_dim_embs, labels, filename='tsne.pdf'): #from matplotlib import rcParams myfont = font_manager.FontProperties(fname='SimHei.ttf') #matplotlib.rcParams['font.family'] = 'sans-serif' matplotlib.rcParams['font.family'] = ['SimHei'] #matplotlib.rcParams['axes.unicode_minus'] = False assert low_dim_embs.shape[0] >= len(labels), "More labels than embeddings" plt.figure(figsize=(18, 18))# in inches print("flag2") for i, label in enumerate(labels): x, y = low_dim_embs[i, :] plt.scatter(x, y) plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom') plt.savefig(filename) #wiki.zh.vec def embedding(model_path, vocab): zh_model = KeyedVectors.load_word2vec_format(model_path) embedding = np.array([]) show_vocab = [] for m in range(len(vocab)): if vocab[m] in STOPWORDS: continue if len(show_vocab) > 499: break if vocab[m] in zh_model.vocab: show_vocab.append(vocab[m]) embedding = np.append(embedding, zh_model[vocab[m]]) print(len(show_vocab)) embedding = embedding.reshape(len(show_vocab), 300) tsne = TSNE() print("flag0") low_dim_embedding = tsne.fit_transform(embedding) print("flag1") plot_with_labels(low_dim_embedding, show_vocab) #word_freq(corpora) #word_cloud(corpora, "weibologo.jpg") embedding("wiki.zh.vec", real_vocab)
如果你看到了這裡並且喜歡這篇部落格,希望你可以給EST凌晨4點還在寫部落格的博主一點支援~鼓勵我寫出更多更好的部落格~謝謝啦~