【Doc2Vec】(β) アンサイクロペディア記事検索機【自然言語処理】

[クソアプリ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/kuso-app) 16日目の記事です。

# はじめに
今年もこの時期がやってまいりました
前回は、[Discordの通知をすべてDiscordで通知するBOT](https://qiita.com/Zect/items/4dbc0754588528156b57)という、discord.pyを使ったくそBOTを作りました。
今回は、少し趣向を変え、自然言語処理を使ったくそアプリを作りました！

その名も...
**(β)アンサイクロペディア記事検索機**

<iframe width="560" height="315" src="https://www.youtube.com/embed/0S6RSq3HIyw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

`※この映像は開発段階のものです。実際のアプリと異なる場合があります`

https://kuso2022.streamlit.app/

# 対象読者

- Pythonのコードが読める人
- AIに興味がある人
- 少し古くても文句を言わない人

# このアプリのくそなところ
このアプリは、後でも紹介しますが、`streamlit`というホスティングサービスを使用しています。
これは、githubリポジトリからコードを持ってくることでホストができます。
そして、githubの最大アップロード量は100MB...

モデルの容量が大きすぎたので、今回実装ができたのは、
類義語検索のところまで・・・でした。。

ちなみに、doc2vecでの実装を無理やりword2vecに切り替えたため、アプリ自体もバグってます。

- 一文字での検索のみ対応
- 類似してるかわからない文字が並ぶ
- 読めない

どうぞ、お楽しみください。

# 使用技術の紹介
## Gensim
このアプリでは、Gensimというライブラリを使って実装しています。
詳しい話は、最近話題のChatGPTに聞いてみましょう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974385/c2b7ee94-bf10-43e5-ca02-73e5dcf2df72.png)

少し誤解が生まれそうな回答ですが、大方このような意味ですし、この記事を理解する上では問題ないので ﾖｼｯ
もっと知りたい方は、Gensimの公式ドキュメントを読んでみてください。

https://radimrehurek.com/gensim/intro.html

## Word2VecとDoc2Vec

このアプリでは、Word2VecとDoc2Vecという、Gensimの中のメソッドを使っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974385/bcc37c88-56db-43dd-70bf-b11168459025.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974385/b68628ec-7947-0ed2-3cd7-609bca74d032.png)

二つの違いも聞いておきましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974385/5c8c8adc-5ab8-f79e-b17e-46192126021f.png)

まとめると、

- Word2Vec：単語検索ができる
- Doc2Vec：文章検索ができる

ということになります。

## Streamlit

https://streamlit.io/

今回のアプリのホストには、データを扱う系のアプリに特化したstreamlitというサービスを使用しました。

超簡単に使えました。
詳しくは公式ドキュメント参照

ChatGPTによるコードの生成も、基礎的なものであればうまく行くので、ChatGPTに聞くのもありです。

# アンサイクロペディアの記事を取得
アンサイクロペディアはAPIからの記事取得方法もあるそうでしたが、よくわからなかったので、スクレイピングで行いました。

1記事1.5秒、計31,998記事...

めっちゃ時間かかりました。

コードの実装には、以下の記事を参考にしました。

https://qiita.com/toshiyuki_tsutsui/items/f143946944a428ed105b

コードはここに置きました。

[kusoapp_scraping-py](https://gist.github.com/Zect3279/86ba153de84ea3d62a442c7511dd7ce9#file-kusoapp_scraping-py)

アンサイクロペディアには、特別ページというものが存在します。
その一つにページ一覧がおいてあるページがあります。

https://ansaikuropedia.org/index.php?title=特別:ページ一覧

これと
```py
wiki_articles = Wiki_Reader()
wiki_articles.df.to_csv('wiki_article.csv')
print('\nSuccessfully downloaded')
```
このコードの二つでアンサイクロペディアの記事の取得ができます。


30行目以降の`get_article_pages()`はそこからページのリンクをリスト化しています。

72行目移行の`get_article_data()`で記事のIDや本文を取得しています。

あまりにも処理が長く、途中で処理を中断しなければならないことが多々あったので、50記事毎に書き出す処理を付けました。
74-76行目です。

17,20,58-61行目のコメントアウトをうまく使うことで、途中から処理を再開させることができます。

# doc2vecを使った学習

[kusoapp_train-py](https://gist.github.com/Zect3279/86ba153de84ea3d62a442c7511dd7ce9#file-kusoapp_train-py)

コードはここにあります。

doc2vecに関しては、ここで特別言うことはありません。
ただ公式docsに書いてあるコードをそのまま実行した感じです。

## cached_property

```py
@cached_property
```

コードの一部にこのデコレーターが見られると思います。
これは、その関数の返り値をキャッシュすることができます。

これにより、毎回呼び出すたびに処理をする、ということを防ぐことができます。


# アプリの実装編

コードはここです。

[kusoapp_streamlit-py](https://gist.github.com/Zect3279/86ba153de84ea3d62a442c7511dd7ce9#file-kusoapp_streamlit-py)

## gensim周り

```py
from gensim.models import doc2vec
```

まず、gensimからdoc2vecを読み込みます。
これは、gensim.modelsに定義されています。

gensim.modelsには、他にも面白い機能を持ったものがあるので、ぜひ調べてみてください。


```py
similar_words1 = model.wv.most_similar(word)
```

このコードで、入力した単語と似た使われ方をされている単語を探します。


```py
vector = model.infer_vector([word])
similar_words2 = model.dv.most_similar([vector])
```

後半部分のコードですが、
これは、単語をベクトルに変換しています。
そのあと、近しいベクトルを持った記事を検索しています。
今回は、単語:文章間の検索のいい方法が思いつかなかったので、単純にベクトルの比較を行いました。

いい方法知ってる方、教えてください（ﾎﾞｿｯ



## streamlit周り

```py
col1, col2 = st.columns(2)
with col1:
  ...

with col2:
  ...

```

これは、二つ以上の要素を横に並べる時に使えます。
詳しくは公式docsを参照

https://docs.streamlit.io/library/api-reference/layout/st.columns


```py
word = st.text_input("Search word:")
```

これで、単語の入力を行います。


```py
st.title("類義語検索")
```
タイトルの記述に使います。

```py
st.write("^ 好きな単語を入力してください")
```
通常文章を記述するときに使用します。


```py
st.markdown(f"[{str(df.title.values)[2:-2].replace('u3000', '')}](https://ansaikuropedia.org/index.php?curid={str(df.id.values)[1:-1]})")
```
st.markdownは、要素をmarkdown形式で記述することができます。
streamlitでのハイパーテキストの記述は、これで行うそうです。

# 最後に

いかがでしたか?

次は、アンサイクロペディアの記事を要約する予定です。
お楽しみに

では ﾉｼ

# 反省
さて、
この記事で紹介したwebサイトまでならよかったのですが、
実際にコードを実行してみた皆様は、
「このアプリのどこがくそアプリなの？」
「普通に精度iijan」
「発展性アルヨネ？」
などと、今にも僕のTwitterを炎上させたい気持ちでいっぱいだと思われます。

言い訳をさせてください。

当初予定では、類義語検索を行い、
「いや、うん、そうなんだけど...」
という言葉を引き出す検索結果を表示する予定でした。

しかし、イレギュラーだらけのアンサイクロペディアを読み込ませても、うまく行ってしまいました。

原因はいくつか考えられますが、一番大きいのは、
__終わってる記事は一部だけで、記事のほとんどがまとも__
だったことだと思います。

来年度は、この反省を生かし、前回よりも面白く、終わっているくそアプリを作りたいと思います！


では、また会いましょう ﾉｼ