引言
Quora是一個流行的知識分享平臺,我常常在Quora上分享我的想法。這個平臺是基于問答的形式,它因其簡易的設(shè)計和平滑的用戶體驗而出名。
當(dāng)新的問題被添加到Quora時,這些問題由機器人自動基于問題的上下文進(jìn)行標(biāo)記并稍后由用戶進(jìn)行編輯。這些標(biāo)簽反映了問題被歸入的話題類別。如下是一個問題的基本概貌。
最近我在尋找合適的數(shù)據(jù)集,然后我偶然看到了Quora里的一個這個頁面:Programming Challenges。我選擇了這個叫做Answered的二分類問題挑戰(zhàn)。其中,包括了近10000個問題(訓(xùn)練集和測試集總計)。每個問題和其話題標(biāo)簽以及其他的一些信息被以JSON格式儲存。下圖是一個JSON的示例。
示例問題JSON
動手干吧
第一個任務(wù)就是要從JSON文件中讀取數(shù)據(jù)。訓(xùn)練集總共大約有9000個問題,而測試集總共約1000個問題。
import json
f=open("answered_data_10k.in").read().split("n")
train_set=f[1:9001]
test_set=f[9002:-1]
train=[json.loads(i)for i in train_set]
test=[json.loads(i)for i in test_set]
questions=train+test
接下來就要提取出所有數(shù)據(jù)集中的主題標(biāo)簽。在JSON文件中,主題存儲在鍵"key"中。不同的問題有不同數(shù)量的主題標(biāo)簽。單個問題所允許存在的最大標(biāo)簽數(shù)為26。同時也存在沒有關(guān)聯(lián)主題標(biāo)簽的問題。
#Create the list of topics
topic_list=[]
for question in questions:
if len(question["topics"])>0:
for topic in question["topics"]:
topic_list=topic_list+[topic["name"]]
topic_list=list(set(topic_list))
print(len(topic_list))
在這個挑戰(zhàn)所提供的數(shù)據(jù)中,一共有8762個主題標(biāo)簽。
在提取出主題標(biāo)簽之后,我們需要將具有相同標(biāo)簽的問題聚類。在動手之前,我們先對數(shù)據(jù)進(jìn)行分析,因為如果直接對8762個進(jìn)行聚類將會很困難而且聚類的質(zhì)量也難以保證。
因此我們限定了每一個主題下的最小問題數(shù)來解決這個問題。擁有多于1個問題的主題有3275個。擁有5個問題的主題恰好有900個,這個數(shù)量相對更適合進(jìn)行聚類。
最終,我們決定將主題下的最小問題數(shù)規(guī)定為5個,這主要有兩個原因。首先是為了更好地用向量來表示主題,其次因為具有較少問題的主題大多數(shù)情況下是和無關(guān)的問題所關(guān)聯(lián)的。
#Assigning question to topics.
question_list=[]
final_topic_list=[]
for topic in topic_list:
temp=[]
for question in questions:
context=[i["name"]for i in question["topics"]]
if topic in context:
temp.append(question['question_text'])
if len(temp)>=5:
question_list.append(temp)
final_topic_list.append(topic)
topic_list=final_topic_list
接下來,我們寫一個函數(shù),通過轉(zhuǎn)換為小寫、去除標(biāo)點符號和停用詞來正則化每個段落。每個話題下有五到多個問題。我們把每個話題下的問題的集合當(dāng)做一個文檔。
這樣,我們先遍歷話題標(biāo)簽,然后把問題聚集成段落,再把段落正則化化。然后我們把段落和段落的話題標(biāo)簽喂給Gensim的TaggedDocument函數(shù),進(jìn)行進(jìn)一步的正則化。
from nltk import word_tokenize
from nltk.corpus import stopwords
from gensim import models
from gensim.models.doc2vec import TaggedDocument
#Function for normalizing paragraphs.
def normalize(string):
lst=word_tokenize(string)
lst=[word.lower()for word in lst if word.isalpha()]
lst=[w for w in lst if not w in stopwords.words('english')]
return(lst)
#Aggregate questions under each topic tag as a paragraph.
#Normalize the paragraph
#Feed the normalized paragraph along with the topic tag into Gensim's Tagged Document function.
#Append the return value to docs.
docs=[]
for index,item in enumerate(topic_list):
question="".join(question_list[index])
question=normalize(question)
docs.append(TaggedDocument(words=question,tags=[item]))
為Gensim的DocVec準(zhǔn)備數(shù)據(jù)
接下來我們訓(xùn)練Doc2Vec模型。
應(yīng)該調(diào)整vector_size和window,直到結(jié)果是最優(yōu)的。
import gensim
model=gensim.models.Doc2Vec(vector_size=200,window=3,min_count=0,workers=4,epochs=40)
model.build_vocab(docs)
model.train(docs,total_examples=model.corpus_count,epochs=model.iter)
Doc2Vec Training
Doc2Vec模型訓(xùn)練好后,我們就用KMeans算法聚類了文檔向量。簇的數(shù)量從100到50之間進(jìn)行了檢查。接近100的簇數(shù)會導(dǎo)致大簇被切分成小簇,而簇數(shù)等于50時會使得沒有相關(guān)性的簇被組合成大簇。在仔細(xì)評估聚簇結(jié)果后,最后選擇60作為簇數(shù)。
from sklearn.cluster import KMeans
from sklearn import metrics
import pylab as pl
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
kmeans_model=KMeans(n_clusters=60,init='k-means++',max_iter=100)
X=kmeans_model.fit(model.docvecs.doctag_syn0)
labels=kmeans_model.labels_.tolist()
l=kmeans_model.fit_predict(model.docvecs.doctag_syn0)
#map each centroid to its topic tag
word_centroid_map=dict(zip(model.docvecs.offset2doctag,l))
#Print Cluster List
for cluster in range(0,100):
print("nCluster%d"%cluster)
words=[]
for i in range(0,len(word_centroid_map.values())):
if(list(word_centroid_map.values())<i>==cluster):
words.append(list(word_centroid_map.keys())<i>)
print(words)
擬合KMeans模型并取回簇的列表
下面是一些簇的例子。
第一個簇里有相當(dāng)多的“設(shè)計、UI/UX設(shè)計、軟件開發(fā)、web開發(fā)和網(wǎng)站”。
第二個簇里有不少“在線廣告、營銷工具”
第三個簇里有許多像“生活、自我提升”這樣的話題。
看到這個簇你就能明白數(shù)據(jù)庫里大部分跟性有關(guān)的話題都是關(guān)于培養(yǎng)孩子和性-教育的方面。其他的熱門關(guān)鍵詞都沒有出現(xiàn)。
這個簇是一個政-治和經(jīng)濟話題的大雜燴。同時,這也揭示了經(jīng)濟與政-治之間的內(nèi)在聯(lián)系,以及一個地區(qū)的經(jīng)濟因素是如何成為該地區(qū)發(fā)展的主要評價標(biāo)準(zhǔn)的。
影視、美術(shù)、書籍、音樂以及其他形式的創(chuàng)造性藝術(shù)。
這個簇里為啥有國際象棋(Chess)?
這個非常有趣,主要聚焦在宗-教-信-仰(Religion)上。有三件事我希望你們注意:假設(shè)問題(Hypothesis Questions)在這個簇里。倫-理(Ethics)在這個簇里?;贚GBTQ的問題跟宗-教-信-仰最相關(guān),而(Homosexuality)和親子(Parenting and Children)在一個簇里。
很清晰的是,簇形成的方式反映出了關(guān)于Quora上的話題之間互相關(guān)聯(lián)的方式,以及問題被詢問的方式。調(diào)查問題(Servey Questions)由于它的獨特性,自己形成了一個簇,而政-治(Politics)有非常多的相關(guān)話題。
這是60個簇中我覺得值得一提的一些。你也可以提一下自己發(fā)現(xiàn)的有趣的簇。這個項目的代碼可以在Github找到。