言語処理学会の論文を自然言語処理してみた

# 概要
恥ずかしながら、[言語処理学会](https://www.anlp.jp/)なるものが日本にあることを全く知らず、とある会合でその存在をたまたま知りました。
ならば、この言語処理学会の論文を自然言語処理すれば、日本の現在の自然言語処理界隈の動向がわかるのではないかと浅はかにも考えました。
というわけで、やった事はとても簡単です。

- [言語処理学会第28回年次大会(NLP2022)](https://www.anlp.jp/proceedings/annual_meeting/2022/index.html)をスクレイピング
- 便利な[自然言語可視化ライブラリnlplot](https://speakerdeck.com/takapy/zi-ran-yan-yu-ke-shi-hua-raiburari-nlplot-nogoshao-jie?slide=11)で自然言語処理

これだけです。

# 論文PDFをスクレイピングダウンロード
おなじみBeautifulsoupでスクレイピング
```
from bs4 import BeautifulSoup
import urllib.request as req
import urllib
from urllib.parse import urljoin

url = "https://www.anlp.jp/proceedings/annual_meeting/2022/"
res = req.urlopen(url)
soup = BeautifulSoup(res, "html.parser")
result = soup.select("a[href]")

# 実際にPDFが存在する
base = "https://www.anlp.jp/proceedings/annual_meeting/2022/"

li = []
for link in result:
    href = link.get('href')
    url_1 = urljoin(base, href)
    li.append(url_1)

a = [s for s in li if s.endswith('pdf')]
```
PDFをダウンロード、`pdf_dir`ディレクトリに保存します。
```
for i in range(len(a)):
    file_name=a[i].split('/')[-1]
    urllib.request.urlretrieve(a[i],'pdf_dir/'+file_name )
```

# 前処理＆nlplotで可視化
ここで全てのPDFファイルをテキスト化して自然言語処理（NLP）してもいいのですが、一度やってみたところ収拾がつかなくなってしまいました。
そこで、論文のテーマごとに分けてNLPかけると、なんとか収まりが良くなります。
もう一度、[言語処理学会第28回年次大会(NLP2022)](https://www.anlp.jp/proceedings/annual_meeting/2022/index.html)に戻って各論文のテーマを確認してみましょう。
![言語処理学会タイムスケジュール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/3f743b65-ea22-3cf3-eafe-dad484fa39db.png)
各テーマのPDFファイルは以下のようになっているようです。
![言語処理学会PDF.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/1bef0862-5c88-c76f-3b56-b50dff0ba50f.png)
テーマが「機械学習」なら、そのPDFは`A1-*.pdf`、`A2-*.pdf`、`A3-*.pdf`となりそうです。
まずダウンロードしたPDFを読み込み、テキスト化します。テキストは変数textに入れていきます。
```
import glob
import os
import fitz

text = ''

# テーマ「機械学習」の論文
pdfs = glob.glob(os.path.join('./pdf_dir/', 'A[1-3]-*.pdf'))
for pdf in pdfs:
    pdf_file = fitz.open(pdf)
    num_pages = pdf_file.page_count
    for page in range(num_pages):
        t = pdf_file.get_page_text(page)
        text += t
    pdf_file.close()
```
読み込んだtextを文章ごとにpandasのDataFrameにしていきます。改行'\n'で分割していきます。
ついでにテキストから数字を削除してしまいます。
```
import pandas as pd

textlist=text.splitlines()
# データフレームに変換
text_df = pd.DataFrame({'textlist':textlist})
text_df['textlist']=text_df['textlist'].str.replace('\d','') #数字削除
text_df

# 	textlist
# 0	日本語 GPT を用いたトークナイザの影響の調査
# 1	井上 誠一 Nguyen Tung 中町 礼文 李 聖哲 佐藤 敏紀
# 2	LINE 株式会社 東京都立大学
# 3	{seiichi.inoue, tung.nguyen, akifumi.nakamachi,
# 4	shengzhe.li, toshinori.sato}@linecorp.co.jp
# ...	...
# 7375	Diederik P. Kingma and Jimmy Ba. Adam: A metho...
# 7376	stochastic optimization, .
# 7377	― ―
# 7378	This work is licensed by the author(s) under C...
# 7379	(https://creativecommons.org/licenses/by/./).
# 7380 rows × 1 columns
```
形態素解析器はMeCabです。
「名詞」と「固有名詞」だけ抽出。
```
import MeCab

def mecab_text(text):
    
    #MeCabのインスタンスを作成（辞書はmecab-ipadic-neologdを使用）
    mecab = MeCab.Tagger('-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')
    
    #形態素解析
    node = mecab.parseToNode(text)
    
    #形態素解析した結果を格納するリスト
    wordlist = []
    
    while node:
        #名詞のみリストに格納する
        if node.feature.split(',')[0] == '名詞':
            wordlist.append(node.surface)
        #他の品詞を取得したければ、elifで追加する
        elif node.feature.split(',')[0] == '固有名詞':
            wordlist.append(node.surface)
        node = node.next
    return wordlist

#形態素結果をリスト化し、データフレームtext_dfに結果を列追加する
text_df['words'] = text_df['textlist'].apply(mecab_text)
```
次にnlplotの出番。
uni-gramを睨みながら、ゴミワードをストップワード（stopwords）指定。
```
import nlplot

npt = nlplot.NLPlot(text_df, target_col='words')

# top_nで頻出上位単語, min_freqで頻出下位単語を指定できる
stopwords = ['こと','文','的','ため','表','提案','結果','よう','数','図','語','例','性','ら','もの','度','値','化','中',
    'a','b','c','are','on','the','and','of','by','in','is','for','s','this','under','author','https','pp','to','%','()','we',
            'as','m']

npt.bar_ngram(
    title='uni-gram',
    xaxis_label='word_count',
    yaxis_label='word',
    ngram=1,
    top_n=50,
    stopwords=stopwords,
)
```

![newplot-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/ca0b21a5-8091-e5f8-4acc-40fd60d77741.png)

bi-gram

```
npt.bar_ngram(
    title='bi-gram',
    xaxis_label='word_count',
    yaxis_label='word',
    ngram=2,
    top_n=50,
    stopwords=stopwords,
)
```

![newplot-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/93daf1ee-f0f6-ebfd-b65d-ef542fdc0c80.png)

ワードクラウド

```
import matplotlib.pyplot as plt
%matplotlib inline

fig_wc = npt.wordcloud(
    width=1000,
    height=600,
    max_words=300,
    max_font_size=100,
    colormap='tab20_r',
    stopwords=stopwords,
    mask_file=None,
    save=False
)
plt.figure(figsize=(15, 25))
plt.imshow(fig_wc, interpolation="bilinear")
plt.axis("off")
plt.show()
```
![ワードクラウド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/1c311ff3-8f41-a219-79c8-f7819d218a26.png)

共起ネットワーク
```
from plotly.offline import iplot

# ビルド（データ件数によっては処理に時間を要します）
npt.build_graph(stopwords=stopwords, min_edge_frequency=25)

# ビルド後にノードとエッジの数が表示される。ノードの数が100前後になるようにするとネットワークが綺麗に描画できる
# node_size:63, edge_size:63

fig_co_network = npt.co_network(
    title='Co-occurrence network',
    sizing=100,
    node_size='adjacency_frequency',
    color_palette='hls',
    width=1100,
    height=700,
    save=False
)
iplot(fig_co_network)
```
![供起ネットワーク.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/2d4adbb1-a402-46e9-db5e-c25f3ab4645f.png)

サンバーストチャート

```
fig_sunburst = npt.sunburst(
    title='sunburst chart',
    colorscale=True,
    color_continuous_scale='Oryel',
    width=1000,
    height=800,
    save=False
)
fig_sunburst.show()
```

![サンバースト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/c5442263-8a6c-5e18-1ee1-c11f9faa0aac.png)

# トピック分析追加、その他
## トピック分析の追加

nlplotでは、以前この先のトピック分析までできたのですが、連携が難しいようで、現在はpyLDAvisを使ったトピック分析可視化はサポートされていません。
ですので、pyLDAvisを使ったトピック分析の可視化は独自でやるしかありません。
参考までに、コードと結果を追記します。尚、再度ストップワード除去が必要であることに注意してください。

ストップワード除去
```
def del_stopwords(w):
    w = [x for x in w if x not in stopwords]
    return w
    
text_df['words']=text_df['words'].apply(del_stopwords)
```
以下の処理がとりあえず必要です。
```
text_df['words_lda']=text_df['words'].apply(lambda x:' '.join(x))
```

LDAモデル作成と学習

```
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation

# テキストデータをBOW形式に変換する
tf_vectorizer = CountVectorizer(
    #token_pattern='(?u)\\b\\w+\\b',
    max_df=0.90,
    min_df=10,
)
tf = tf_vectorizer.fit_transform(text_df["words_lda"])

# LDAのモデル作成と学習
lda = LatentDirichletAllocation(n_components=20)
lda.fit(tf)
```
可視化

```
import pyLDAvis
import pyLDAvis.sklearn

# jupyter notebookで結果を表示するために必要
pyLDAvis.enable_notebook()

pyLDAvis.sklearn.prepare(
    lda, # LDAのモデル (LatentDirichletAllocation のインスタンス)
    tf, # BOWデータ (sparse matrix)
    tf_vectorizer, # CountVectorizer もしくは TfIdfVectorizer のインスタンス
)
```

![トピック分析可視化.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483137/1f77f47e-f89d-12ce-fcd9-a15a2dcce6fb.png)

## その他
今回は、学会の論文のテーマを「機械学習」だけに絞りましたが、他のテーマも正規表現を利用してファイル名を取得することで同様の処理ができます。
例えば、テーマを「機械翻訳」としたい時は、PDFファイルの読み込みで以下のように設定してください。以降は同じ処理でできます。

```
# テーマ「機械翻訳」の論文
pdfs = glob.glob(os.path.join('./pdf_dir/', 'C[5-6]-*.pdf'))
```

# まとめ
私は特に自然言語処理にそれほど精通しているわけではないので、本分析へのコメントは控えます。それなりに知見のある人が見れば有用なのかもしれませんし、ほとんどどうでもいい結果なのかもしれません。
学んだことが一つあるとすれば、CCというのがメールのCC(Carbon Copy)ではなく、おそらくはCreateive Commonsの略であろう、ということくらいです。

これ、言語処理とどーゆー関係があるのですか？

以上です。

# 参考

[クリエイティブ・コモンズ・ジャパン](https://www.anlp.jp/proceedings/annual_meeting/2022/index.html)