Topic Extraction on Medium.com Documents using NMF and LDA in Python使用Python NMF和LDA对Medium文章主题提取

最近发现一个满是资源和干货的博客网站Medium.com,正好也在倒腾自然语言处理,决定写个网络爬虫做点主题提取分析。Medium.com可以按topic添加自己感兴趣的文章,比如下图的人工智能topic。Medium.com不会一次性加载所有的文章列表,而是通过用户下拉经由JavaScript动态加载。简单起见,我写的爬虫也就刚好爬取首次加载的文章。
Screen Shot 2017-07-31 at 14.57.42

# Define a Web Scraper which will get documents from Medium.com for a given topic
def mediumScraper(topic):
    links = []
    content = []
    
    response = requests.get('https://medium.com/topic/'+topic)
    soup = BeautifulSoup(response.text,'lxml')
    
    for post in soup.findAll("a", { "data-action" : "open-post" }):
        links.append(post['href'].split('?')[0])
    
    for link in links:
        response2 = requests.get(link)
        soup2 = BeautifulSoup(response2.text,'lxml')
        paragraphs = ''
        for paragraph in soup2.findAll("p", { "class" : "graf graf--p graf-after--p" }):
            paragraphs += paragraph.text

        content.append(paragraphs)
        print('Found one document on topic %s' %(topic))
        
    return pd.DataFrame({'topic':topic,'links':links,'content':content},columns=['topic','links','content'])

# Scrape 8 topics and return as a data frame
topics = ['data-science','politics','technology','sexuality','artificial-intelligence','culture','software-engineering','equality']
df = mediumScraper(topics[0])
for topic in topics[1:]:
    df = pd.concat([df, mediumScraper(topic)],ignore_index=True)

由于有些文章只针对会员开放,所以content内容为空。删除2篇空文章后,最终得到的data frame共200行,既每个topic有25篇文章。

接下来就是NLP的tokenization标准流程了。得到的corpus只包含去除非字符和stopwords并取字根的转小写单词。

corpus = []
for i in range(0,200):
    # Tokenization
    # Remove any character except letters
    # Convert into lower case
    content = re.sub('[^a-zA-Z]',' ',df['content'][i]).lower()
    # Convert into stem word
    ps = PorterStemmer()
    content = ' '.join([ps.stem(word) for word in content.split(' ') if not word in set(stopwords.words('english'))])
    corpus.append(content)

紧接着就可以对corpus做数值化矩阵转化。简单来说就是词频转化。

# 简单的词频转化;适用LDA
cv = CountVectorizer(max_features=2000)
X = cv.fit_transform(corpus)
# TF-IDF转化;适用NMF
tf = TfidfVectorizer(max_features=2000)
X_2 = tf.fit_transform(corpus)

说实话,Latent Dirichlet Allocation要透彻理解真不是件容易的事。简单来说,LDA是一个三层贝叶斯概率模型,包含词、主题和文档三层结构。

Non-Negative Matrix Factorisation非负矩阵分解则要直观许多。如下图,不过这里需要做下调整,V就是X或者X_2,一行是一篇文档,而一行包含的列就是文档包含的词;W就是一会作用LDA或者NMF(transform方法)得到的矩阵,它把列从词转变成主题,显然,文档可以被划归为所在行主题权值最大的那个;H就是作用LDA或者NMF得到的矩阵componets_,我们可以取每行词权值最大的几个作为该主题的一个概要。
Screen Shot 2017-07-31 at 14.14.30

下面我们可以比较下LDA和NMF的结果。

  • 上面8组是LDA得到的主题包含的top 50词的wordcloud。下面8组是NMF得到的主题包含的top 50词的wordcloud。看起来,NMF的分组更能贴近Medium.com本身的topic [‘data-science’,‘politics’,‘technology’,‘sexuality’,‘artificial-intelligence’,‘culture’,‘software-engineering’,‘equality’]。

 

  • 下面左图是LDA,右图是NMF。图片是8行25列,既每个像素代表一个文档,且每行的25列来源于df的同一个topic。像素的颜色则代表LDA或者NMF给出的topic。若能同行每列颜色一致,两行之间颜色异同,则越能接近Medium.com自身的topic给定。显然,NMF的表现优于LDA。

     

  • 另外,NMF的计算复杂度也明显小于LDA。
    LDA done in 1.403s.
    NMF done in 0.022s.

最后,小结一下,以SVD,NMF为代表的矩阵讲解运算在机器学习,图像识别,文字挖掘等诸多领域都有广泛应用。单就文本主题提取而言,NMF在这里由于LDA。不过实际使用中,不知道应该怎么取舍。哈,我会持续关注和提高的。

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s