Python
自然言語処理
データ分析
word2vec
doc2vec

AIが見つけた、埋もれたQiita良記事100選

背景

Qiita殿堂入り記事、と、7つの「驚愕」

良い記事なんだけどなかなかいいねがつかなかった記事を、
AIによって発掘したいという試み
または、機械によって「いいね」かどうか判断させたり、
何いいねになる記事なのか、予測することは可能なのか?という問題を考えてみたい。

以下の2つの記事の中でも少し予告していた、
一番難しそうな内容に挑戦してみる。

Qiitaの記事データの取得と、全体感について記載。
既にいいねを多数集めている「良記事」を「殿堂入り」として月ごとにまとめ。

直近の記事データから、傾向分析を実施し、
様々な知見や気づきを得ることが出来た。

上の記事を読んでいない人へのまとめ。前回までのあらすじ

  • Qiitaのデータを分析したよ。
    • 2016年4月の記事は、2018年5月に比べて3倍いいねが貰えたよ(仕様変更影響大きいね)
    • ⇒分析や比較は、出来るだけ投稿日の近い記事同士が望ましいね。
  • 月ごとに、「いいね」が高い記事を殿堂入りとしてランキング化したよ。
    • TOP30記事=「0.38%」で、その月の投稿に対するいいねの「43.7%」 も占めるよ。
    • どの記事を読んでも面白いから見てね。
  • データを分析した結果、いろいろ面白いことが分かったよ。一部を紹介するね。
    • 「いいね」の平均値は8.02。半分以上は0~2。めっちゃ偏っているね。
    • 人気のタグが「いいね」が多いわけではなくて、「機械学習」とかが多いんだなあ。
    • 「タグ」と「いいね」の関係表や、タグ同士の関連を図示するグラフを作ったよ。

今回やりたいこと

最初に記載した考察の結果、「いいね」が多い⇒良記事、と仮定した。
殿堂入り」に集めた結果はどれも良記事で、仮定は正しかった。

しかし、「いいね」が少ない⇒良記事ではない、は成り立たない
7つの「驚愕」の結果を見ても明らかなように、「いいね」はトレンド次第で大きく偏る。
そのため、人の目にあまり触れられていなかった、不遇な良記事が多数眠っているのではないか?

自然言語処理/機械学習を駆使して、海の底に眠る良記事を発掘出来ないだろうか!?

  • 試した結果こんなの出ました、で終わりにしない。
  • 確かに良記事っぽい、ところまで、チューニングを行う。
  • チューニングには、極力、人間(私)の主観的チューニングを入れない。
    • (「初心者」タグは面白い、とか決め打ちではなく、数値がN以上を取る、などで調整)

本投稿の内容

  • 結論/成果 = AIによって「良記事」と判断された100記事を公開
    • 「良記事」レベルを判定するプログラムが出来た。
    • 「文章的な記事」に偏ってしまった感はあるものの、
    • 良記事なのにいいねが少ないものを抽出できたような気がする。
      • 良記事かどうか?は個々人の判断によるところが大きいため、
      • 成果と手法を公開し、フィードバックを募ることにする。
  • 試行錯誤の過程や、処理のコード/ノウハウを公開
  • 試行錯誤① Doc2Vec
    • テキストをベクトル化/数値に変換する技術。
    • 良記事との距離が近い記事=良記事、という仮定は成り立つのか?
  • 試行錯誤② クラスタリング
    • 良記事と、良記事ではない(失礼)は、クラスタリングによって分類されるか?
  • 試行錯誤③ 最終的には上記組み合わせ(オリジナルロジック)
    • 結果に至るまでの最終考察と手法。
  • コードの実行環境は全て、Windows10 + Python3 +JupyterNotebook を前提。

当初仮説

良記事の類似記事は良記事?という仮説(Doc2Vec活用)

Doc2Vecを使うと、文章をベクトル化して表現することが出来る。
(Word2Vecを応用して、文章に適用出来るようにしたようなもの)
詳細は、ググれば出てくるので省略する。

この技術を使えば、ある記事に対して、「最も似ている記事はどれ?」
といった形で検索が出来る。
(ただし「文章」を扱うので、精度についてはお察しレベル)

埋もれている良記事=複数の良記事が近くに居る、と仮定すると、
ある記事に対して、類似記事のTOP10などを出して、
類似記事の平均いいね値が高い記事は、埋もれた良記事なのではないか?という仮説を立てた。

各種前提

実装の話に行く前に、いくつか今回の前提を記載する。

今回使用するデータは全て、2018年6月末ごろに取得した、
2018年05月に投稿された記事 ~ 2017年06月に投稿された記事
のデータ=79072記事について扱っており、
「いいね」の値についても、データ取得時点の値である。

Doc2Vecのモデルを作る方法

Doc2Vecを作るための前加工処理

Doc2Vecのモデルを作るためには、学習用のデータとして、
「文章の名前 (タイトルやタグ): 文章のテキストを単語リスト化したもの」
の形(TaggedDocument形式)に記事を加工する必要がある。

形態素解析はmecab&mecabの辞書(ipadic-neologd)を使用する。

以下のようにして加工する。

TaggedDocumentリストの作成
#品詞を限定した、タグドキュメントリストの生成
from gensim.models.doc2vec import Doc2Vec
from gensim.models.doc2vec import TaggedDocument

import MeCab

#Mecabによる解析の実験:ipadic-neologdの辞書を指定している。
text = '日本語の自然言語処理は難しいよね。'
tagger = MeCab.Tagger(r"-Ochasen -d .\mecab-ipadic-neologd")        
result = tagger.parse(text)
print(result)

#タグドキュメントのリストを作る
TaggedDocument_list=[]

#投稿記事を、タグドキュメント化して、そのリストを作っていく。
#全記事対象にした。
for item_info in item_info_list:
    #投稿記事のURLと本文(body)を取り出す。
    url_str=item_info[3]
    body=item_info[9]
    # ★ノーマイライズを入れる
    normalized_body=normalize_neologd(body)

    lines = tagger.parse(normalized_body).splitlines()
    words = []
    for line in lines:
        chunks = line.split('\t')
        # ★特定の品詞に限定して利用する。
        if len(chunks) > 3 and (chunks[3].startswith('動詞') or chunks[3].startswith('形容詞') or (chunks[3].startswith('名詞') and not chunks[3].startswith('名詞-数'))):
            words.append(chunks[0])
    #TaggedDocumentは、以下のような形式で生成する。モデル利用時には、tagsの値でこの文書を指定する。
    #TaggedDocument_pair = TaggedDocument(words=['名詞', '動詞', '形容詞', '日本語', '処理', '難しい'], tags=['d1'])
    #タグは、URLにする。
    TaggedDocument_pair = TaggedDocument(words=words, tags=[url_str] )
    TaggedDocument_list.append(TaggedDocument_pair)

print(len(TaggedDocument_list))

加工の時のポイントは2点ある。
当初はただの分かち書きで実施していて、生成したモデルの精度が少し悪かったために、
この2点の処理を入れたところ、Doc2Vecの精度が向上した。

  • 特定の品詞に絞って利用
  • ノーマライズ

特定の品詞に絞って利用するのは、
意味の薄い単語をあらかじめ消しておき、
文章中の重要な用語だけ扱うようにすることで精度を上げるため。

ノーマライズについては、以下の例がイメージしやすい。
全角のアルファベットの変換や全角スペースを削除or半角化したりして、
同じ意味の単語が、同じ表記になるように、揃えること。

ノーマライズの実行例
normalize_neologd("南アルプスの 天然水 Sparking Lemon レモン一絞り")
# >  '南アルプスの天然水Sparking Lemonレモン一絞り'

この関数は、mecab-ipadic-neologdのプロジェクトから
パクってきたコピーしてきただけのもので、コードは以下の通り。

ノーマライズ
#https://github.com/neologd/mecab-ipadic-neologd/wiki/Regexp.ja
#ノーマライズ:詳細は↑のURLを参照

# encoding: utf8
from __future__ import unicode_literals
import re
import unicodedata

def unicode_normalize(cls, s):
    pt = re.compile('([{}]+)'.format(cls))

    def norm(c):
        return unicodedata.normalize('NFKC', c) if pt.match(c) else c

    s = ''.join(norm(x) for x in re.split(pt, s))
    s = re.sub('-', '-', s)
    return s

def remove_extra_spaces(s):
    s = re.sub('[  ]+', ' ', s)
    blocks = ''.join(('\u4E00-\u9FFF',  # CJK UNIFIED IDEOGRAPHS
                      '\u3040-\u309F',  # HIRAGANA
                      '\u30A0-\u30FF',  # KATAKANA
                      '\u3000-\u303F',  # CJK SYMBOLS AND PUNCTUATION
                      '\uFF00-\uFFEF'   # HALFWIDTH AND FULLWIDTH FORMS
                      ))
    basic_latin = '\u0000-\u007F'

    def remove_space_between(cls1, cls2, s):
        p = re.compile('([{}]) ([{}])'.format(cls1, cls2))
        while p.search(s):
            s = p.sub(r'\1\2', s)
        return s

    s = remove_space_between(blocks, blocks, s)
    s = remove_space_between(blocks, basic_latin, s)
    s = remove_space_between(basic_latin, blocks, s)
    return s

def normalize_neologd(s):
    s = s.strip()
    s = unicode_normalize('0-9A-Za-z。-゚', s)

    def maketrans(f, t):
        return {ord(x): ord(y) for x, y in zip(f, t)}

    s = re.sub('[˗֊‐‑‒–⁃⁻₋−]+', '-', s)  # normalize hyphens
    s = re.sub('[﹣-ー—―─━ー]+', 'ー', s)  # normalize choonpus
    s = re.sub('[~∼∾〜〰~]', '', s)  # remove tildes
    s = s.translate(
        maketrans('!"#$%&\'()*+,-./:;<=>?@[¥]^_`{|}~。、・「」',
              '!”#$%&’()*+,-./:;<=>?@[¥]^_`{|}〜。、・「」'))

    s = remove_extra_spaces(s)
    s = unicode_normalize('!”#$%&’()*+,-./:;<>?@[¥]^_`{|}〜', s)  # keep =,・,「,」
    s = re.sub('[’]', '\'', s)
    s = re.sub('[”]', '"', s)
    return s    

Doc2Vecのモデルの生成

準備したTaggedDocument_listの学習を実行する。
オプションの指定の仕方によってかなり精度は変わる。

モデルの生成
%%time
# 学習実行(パラメータを調整可能)
# documents:学習データ(TaggedDocumentのリスト)
# min_count=1:最低1回出現した単語を学習に使用する
# dm=0:学習モデル=DBOW(デフォルトはdm=1:学習モデル=DM)
#model = Doc2Vec(documents=TaggedDocument_list, min_count=10, dm=0)
#iter:トレーニング反復回数 デフォルトは5?
Doc2VecModel = Doc2Vec(documents=TaggedDocument_list, size=150, alpha=0.0015, sample=1e-4, min_count=10, workers=4, iter=30)

# 学習したモデルを保存
Doc2VecModel.save('simple30_Doc2VecModel_150.model')

今回のポイントは、「iter」の指定。
トレーニング反復回数が少ないと、精度が出なかった。

実行回数については、もう少し複雑な計算をさせて、
動的に決めているコード例見受けられたが、
あまり凝ったことをやらなくても、何度かやってみて、
良さそうな値にすれば良いと思う。数十分程度で終わる。
当初、この設定をせずに実施していて精度が出なかった。
次に、凝ったコードを実施していたが、
最終的にiterの数を増やすだけで、凝ったコードと同等以上の結果になった。

ここで、「精度」と言っているのは、Doc2Vecの精度ではなく、
Word2Vecの結果の納得感のこと。
Doc2Vecモデルを上記の方法で作ると、合わせてWord2Vecモデルも生成される。
文章の類似度、を人の目で判断することは難しいが、
単語の類似度、であれば、納得感があるかどうか、で何となく分かる。
具体的には、次の、モデルの内容確認のコマンド結果を見て、
パラメータのチューニングを検討しているというわけ。

作成したモデルの内容確認

作成したDoc2Vecモデルで、どんな成果が得れるのか遊んでみる確認してみる。
以下のコードによって、
ある記事に似た記事を探したり
ある単語に似た単語を探したり(Word2Vec機能)が実施できる。

作成したモデルで遊んでみる
model = Doc2VecModel
#ファイルからロードする場合は以下:
#model = Doc2Vec.load('simple30_Doc2VecModel_150.model')

# ベクトル'd1'を表示(型はnumpy.ndarray)
# print(model.docvecs['d1'])
# 各文書と、最も類似度が高い文書を表示(デフォルト値:10個)
print(model.docvecs.most_similar('https://qiita.com/youwht/items/f21325ff62603e8664e6', topn=3))

print(model.most_similar(positive=u"python", topn=10))
print(model.most_similar(positive=u"プログラム", topn=10))
print(model.most_similar(positive=u"開発", topn=10))
print(model.most_similar(positive=u"アプリ", topn=10))
print(model.most_similar(positive=u"機械学習", topn=10))

モデルの内容確認結果:

コード中のURLが示すのは、対義語の記事。
「赤の他人」の対義語は「白い恋人」 これを自動生成したい物語
の記事に似ていると判断された、とってもとっても不名誉な記事TOP3は・・・
(※2018年5月~2017年6月の全79072記事中から選ばれたTOP3)

意外と似ている気がしないでもない・・・(失礼)。

また、Word2Vecモデルも合わせて生成されるので、
それぞれ似ている上位10単語を示してみる。

順位 python プログラム 開発 アプリ 機械学習
1位 py コード 構築 アプリケーション ディープラーニング
2位 Python スクリプト 開発中 画面 DeepLearning
3位 pip ソースコード アプリ開発 端末 自然言語処理
4位 numpy サンプル 整備 ブラウザ 深層
5位 pyhon ソース 導入 プロジェクト 強化学習
6位 bash インタプリタ 整える app データサイエンス
7位 conda 実行 開発者 サービス 人工知能
8位 jupyter notebook 実行ファイル 運用 デプロイ Deep Learning
9位 pytest 動作 プロダクション ログイン 教材
10位 venv 動く 環境 アイコン モデリング

pythonの5位の「pyhon」は、衝撃的なことに、
タグとしても利用されていて、2018年7月時点で、10投稿されている。
3回くらい見直してしまった

記事数をもっと増やせば、Word2Vecとしての精度はさらに上げることが出来る印象。
ある程度納得感のある類似検索結果であるため、
このモデルならば、文章の類似をやらせても上手くやってくれるだろう、と判断。

なお、コードブロックを考慮せずに実行しているのだが、
コードブロックは固まりになっているために、
自動的に上手く分かれてくれると思っていた通りの結果になった。
(例えば「if」の類義語は、else, return, continue, elseif・・・と続いた)

最初から良い結果を提示しているが、
上記で何点か上げたポイントを実行する前の結果は以下の通り。全然違う。ダメダメ。
こういったWord2Vecの結果を見ながら、どの納得のいく感じになるまで調整するのだ。

順位 python プログラム 開発 アプリ 機械学習
1位 py まず 向け アカウント 基礎
2位 ruby 検証 ツール 管理 学ぶ
3位 pip テスト ブラウザ プログラミング
4位 numpy 導入 整備 連携 ディープラーニング
5位 go 通り 構築 アプリケーション 初心者
6位 pwn 拡張 フレームワーク サービス 技術
7位 #!/ 行い 開発者 プロジェクト 知識
8位 sh とおり 無料 Slack 向け
9位 keras 実際 連携 ソース AI
10位 pandas 操作 チュートリアル 関連

ここまでの結果得られたモデルを使ってもいろいろ遊べるが、
今回の目的に戻って、
「良記事の類似記事は良記事」が成り立つのか考えてみよう。

良記事の類似記事のいいね数を調べる

既に認識されている良記事(今回は300いいね以上の記事と置く)に対して、
類似記事の平均いいね値を調べてみる。

良記事の類似記事の平均いいね数を見る
#URLをキーにした辞書型にしておく。
url_key_dict={}
for item_info in item_info_list:
    url_key = item_info[3]
    url_key_dict[url_key] = item_info
print(len(url_key_dict))

topnNO=30
#全部の記事URL(key)に対して
ruiji_iine_list=[]
for kiji_url in url_key_dict.keys():
    moto_kiji_iine = url_key_dict[kiji_url][0]
    #300いいね以上の記事に対しては、類似記事を取得する。
    if moto_kiji_iine >299 :
        sim_kiji_list = model.docvecs.most_similar(kiji_url,topn=topnNO)
        iinesuu_list=[]
        iinesuu_list.append(moto_kiji_iine)
        for sim_kiji in sim_kiji_list:
            sim_kiji_url=sim_kiji[0]
            sim_kiji_iine=url_key_dict[sim_kiji_url][0]
            iinesuu_list.append(sim_kiji_iine)
        ruiji_iine_list.append(iinesuu_list)

print(len(ruiji_iine_list))

total_val=0
moto_total_val=0
for iinesuu_list in ruiji_iine_list:
    moto_total_val+=iinesuu_list[0]
    for val in range(topnNO):
        total_val+=iinesuu_list[val+1]

print(total_val/topnNO/len(ruiji_iine_list))
print(moto_total_val/len(ruiji_iine_list))

結果:
300いいね以上の記事(平均いいね値648.6)の、
各類似記事TOP30の、
平均いいね数は、「35.7」になった。

ポイントは、
直近1年のQiita記事分析で分かった7つの「驚愕」
の内容で見た通り、全記事の平均いいね値は「8」であるため、
「35.7」はかなり高い数字である。

しかも、上記のプログラムの「似ている」の範囲(TOPいくつまでか)を広げるほど、
平均値は下がっていくことも判明した。
良記事に似ている度が高いほど、平均値が高いということ。
さらに、平均300以上の記事ではなく、平均100以上の記事を対象にしても、
同様の結果になる。(平均値は少し下がる)
* 「300以上」「TOP10」⇒「42.9」
* 「300以上」「TOP30」⇒「35.7」
* 「300以上」「TOP50」⇒「33.9」
* 「100以上」「TOP10」⇒「36.2」
* 「100以上」「TOP30」⇒「30.1」
* 「100以上」「TOP50」⇒「28.1」

なお、いいね数の指数的な分布状況を考えると、本来は「平均」ではなく、
対数化した平均などを見たほうが良いと思われるのだが、
大きいか小さいかというレベルでは影響は少ないので、単純平均で話を進める。

つまり、今回作ったモデルを使うと、
いいね数の高い記事に似ている記事は、いいね数が高い
という関係が成立している。

みんながいいねをする記事には、特徴がある、ということであり、
今回作成したDoc2Vecモデルが、その特徴の一部を捉えている、ということになる。

ここまで明確な傾向が出ていると大きな希望だ。
あとは、逆に、
類似記事の平均いいね値が高いのに、自分自身のいいね値が少ない記事
を見つければいいのだ!

埋もれた良記事の発掘⇒失敗

ここまで順調にいったかに見えたが、
最後の砦が待っていて、この方法では上手くいかなかった

上記で良記事だけを対象にしていた類似検索&平均計算処理を、
全記事を対象にして実施し、
類似記事の平均いいね値が高いのに、自分自身のいいね値が少ない記事
をフィルタリングして出した。

確かに、いくつかは、いいねが少ないのに面白い記事が見つかったのだが、
面白くない記事や、書き方的にイマイチな記事もかなりヒットしてしまった。
私の主観的にはダメというだけであり、他の人が見れば面白いかもしれない、
というのは多々ありつつも、少し考察をすると・・・

「良記事」でいいね数がかなり高い記事でも、Doc2Vecモデル上では、
面白くない記事に近いと分類されてしまうこともままあるハズ。
全体的な平均を取れば、面白い側に分類されることが多いのだろうが、
個別事例が100%の精度になっているわけでは無い。
そのため逆側から見ると、そうした記事と類似している面白くない記事は、
「類似記事の平均いいね値」が高くなってしまう。
そのため、面白くない記事も結構出てきてしまうのではないか?
平均で見ればDco2Vecモデルは「いいね判断」が出来ていても、
個別事例で使おうとすると、マギレ/誤検知が多い
ということ。

この件に関しては、この記事がイマイチだった、みたいな考察は大変失礼なので、
コードと結果記載は差し控えることにする。

一応、上記のコードに加えて、
「自分自身のいいね値が平均値8以上である」
みたいな条件を入れればかなり精度は良くなる(面白い記事が見つかる)のだが、
自分自身のいいね値を参照するようなロジックでは、
「埋もれた良記事」発掘として相応しくないし、
それで発掘出来てもプログラムの力と言いにくいため、あくまでも、
「ある記事の文章だけを機械が見て、他の良記事のサンプルから類推した結果」
というロジックだけで発掘出来る方向を目指す。
(単純に良記事を見つけたいだけならば、自身のいいね値での
 フィルタを入れた方が使いやすいが、今回は、機械が見つけられるか?
 の方をテーマにしたいので、それはやらないよ、ということ)

じゃあどうするの?

正直、この時点では結構困っており、
今回の試みは失敗 or 面白くないかもしれないと思っていた。

思いついたアイデアは、
1件1件の記事を個別に確認していくのではなく、
まずざっくりと「スジの良さそうな記事」「スジの悪そうな記事」
くらいに分類することは出来ないだろうか?
というもの。
「スジの良さそうな記事」に絞れば、
それは「自分自身のいいね値が平均値8以上である」というフィルタと
同じようなフィルタとして使えるだろう、ということ。

第二仮説:良記事のざっくり分類

Doc2Vecモデル自体は、(個別に見ると誤差が多いものの)
ある程度の方向性を示すものとしては機能していると思われる。
そのため、「クラスタリング」によって、
記事を分類するということを試してみる。

クラスタリングは、「タグ」(python、rubyなど)の
自動分類などのために用いられることが多い気がする。

しかし、今回は既に、Doc2Vecのモデルにおける「類似度」を見て、
「同じタグならば類似度が近い」という状態ではなく
(※そうかもしれないが、確認はしていない)、
「いいね数が近い場合は類似度が近い」という傾向が多少はある、
ということは確認済みであるため、
このまま強引にk-meansを適用して、
いいね数が高い記事群、低い記事群、に分かれることを期待する。

クラスタリング処理の実装

今回は、k-meansで、30個のクラスタに分けてみて、
各クラスタの個数や、平均いいね数などを確認してみる。
まずは、クラスタリングそのものの処理を行う。

各記事をベクトル化して、クラスタリング
from collections import defaultdict
from gensim.models.keyedvectors import KeyedVectors
from sklearn.cluster import KMeans
import pickle

key_list = range(len(model.docvecs))
print(len(key_list))

#ベクトル表現だけひたすら取り出してvectorに詰め込む。
vectors = [vector for vector in model.docvecs]
print(len(vectors))
#print(vectors[5])

#クラスタ数は、先に指定する。ここがクラスタリング処理
n_clusters = 30
kmeans_model = KMeans(n_clusters=n_clusters, verbose=1, random_state=42, n_jobs=-1)
kmeans_model.fit(vectors)

#pickleで出来たモデルは保存しておく。
with open(r'doc2vec_30_kmeans_model_30_150.dump', mode='wb') as f:
    pickle.dump(kmeans_model, f)

#クラスタリングのラベルを作る
key_list=[]
for item_info in item_info_list:
    iine=item_info[0]
    key_list.append(item_info[3])

print(len(key_list))
#print(key_list[5])

cluster_labels = kmeans_model.labels_
cluster_to_words = defaultdict(list)
for cluster_id, word in zip(cluster_labels, key_list):
    cluster_to_words[cluster_id].append(word)

#クラスタした結果を表示(各クラスタごとに3URLだけサンプル表示)
for words in cluster_to_words.values():
    print(words[:3])

この状態で、Doc2Vecモデルの距離が近い記事同士で、
30個に分類されたということになる。

各クラスタの情報を確認

各クラスタに対して、それぞれ下記を算出する。
* そのクラスタに含まれる記事の個数
* いいねの合計値
* いいねの平均値
* いいねの中央値
* いいねの「log2」を取った値の平均値

特に、いいねの「log2」を取った平均値が、
一定値以上のクラスタは、良記事集団の可能性がある。

今までは、単純平均で見てきたが、母数が減った集団になると、
単純平均は適切ではない。例えば、
「2」「2」「2」「2」「1024」⇒平均206
「16」「16」「16」「32」「64」⇒平均29
この場合、下の集団のほうが欲しい。(良記事集団は下の集団)
log2をとった平均で見ると、
「2」「2」「2」「2」「1024」⇒平均 2.8
「16」「16」「16」「32」「64」⇒平均 4.6
この例のように、 誤差的に入ってしまった一つの高いいね記事により、
単純平均は破壊されてしまうため、log2側で見るのだ。

各クラスタごとに、いいね平均値の確認
import statistics
import math
import pandas as pd

super_osusume_list=[]

for url_strs in cluster_to_words.values():
    iine_list=[]
    iine_log_list=[]
    #print(len(url_strs))

    #いいね数やログいいね数のリストを生成。
    for url_str in url_strs:
        iine=url_key_dict[url_str][0]
        iine_list.append(iine)
        log_2_iine=math.log(iine+1, 2)
        iine_log_list.append(log_2_iine)

    kosuu = len(iine_list)
    totalsum = sum(iine_list) 
    mean = statistics.mean(iine_list)
    median = statistics.median(iine_list)
    log_mean = statistics.mean(iine_log_list)
    outputlist=[kosuu,totalsum,mean,median,log_mean]

    print(outputlist)

    if log_mean>3.0:
        print(u"↑これが、そのクラスタ内の平均が高い")
        for url_str in url_strs:
            super_osusume_list.append(url_str)

print(len(super_osusume_list))

log2の平均値が一定のクラスタがどこか見えやすくした。
ひどいコードだ

記事の個数、いいね合計値、平均値、中央値、「log2」を取った値の平均値
のならびで表示している。

結果
[20279, 86803, 4.280437891414764, 1, 1.4025844646740377]
[732, 2150, 2.9371584699453552, 1.0, 0.9005997411336497]
[1984, 27188, 13.703629032258064, 4.0, 2.556772511011302]
[1724, 18076, 10.48491879350348, 4.0, 2.41081308764506]
[24312, 49699, 2.0442168476472524, 1.0, 0.9730674856456951]
[8187, 72085, 8.804812507634054, 2, 1.8539635079080152]
[3025, 20874, 6.900495867768595, 2, 1.8217969402674867]
[718, 11084, 15.437325905292479, 4.0, 2.594425110439478]
[1805, 30528, 16.913019390581717, 5, 2.646739678414474]
[2828, 20459, 7.234441301272985, 2.0, 1.925711294559684]
[493, 5045, 10.233265720081135, 3, 2.1077944304651517]
[492, 3747, 7.615853658536586, 2.0, 1.8214605320444002]
[426, 4190, 9.835680751173708, 2.0, 1.8648483554057411]
[2471, 18732, 7.580736543909349, 3, 1.9783958575503753]
[136, 3358, 24.691176470588236, 7.0, 3.2308068296130306]
↑これが、そのクラスタ内の平均が高い
[826, 15354, 18.588377723970943, 6.0, 2.849973539814431]
[1580, 41598, 26.327848101265822, 6.0, 3.0348010258987252]
↑これが、そのクラスタ内の平均が高い
[2307, 80924, 35.07758994364976, 5, 2.924070196663596]
[2115, 29439, 13.919148936170213, 3, 2.2782404704806263]
[633, 12045, 19.028436018957347, 5, 2.6515975462456294]
[88, 6357, 72.23863636363636, 9.0, 3.6168170882385287]
↑これが、そのクラスタ内の平均が高い
[434, 4606, 10.612903225806452, 4.0, 2.437344518113521]
[382, 42430, 111.07329842931937, 10.0, 3.9578317001193537]
↑これが、そのクラスタ内の平均が高い
[58, 177, 3.0517241379310347, 1.0, 1.4149235500639377]
[327, 11766, 35.981651376146786, 8, 3.3305086025506463]
↑これが、そのクラスタ内の平均が高い
[227, 2475, 10.90308370044053, 4, 2.439790206051181]
[32, 177, 5.53125, 2.0, 1.7983720785655]
[298, 7369, 24.72818791946309, 6.0, 3.0091577507563905]
↑これが、そのクラスタ内の平均が高い
[116, 5454, 47.01724137931034, 6.0, 3.13154297919538]
↑これが、そのクラスタ内の平均が高い
[37, 304, 8.216216216216216, 4, 2.1762433527742653]

2927

7個の「良記事クラスタ」が存在しており、
その「良記事クラスタ」に属する記事は、2927個あるということ。
いいねの単純平均値で見ても、結構高い。

クラスタによって、記事の個数がかなり違いが出ている点に注目したい。
ほとんどの記事は「いいね」が非常に少ない状況であり、
また、個数が多いクラスタは平均もあまり高くないため、そうした
「ふつーの記事」に分類されてしまったものが集められているのかもしれない。

クラスタ間の平均値が結構差が出ているために、
良記事のざっくり判断、はある程度成功したと考えられる。

79,072記事中、2,927記事を、良記事的な分類と判断した。

最終実装

さあ、またも超長い戦いになっているが、
いよいよ最後の実装。
これまでのノウハウを組み合わせて、
本当に機械が判断した良記事を抽出する。

クラスタリングで判断された良記事群に対して、
それぞれ類似記事の平均値の計算を実施し、
その数値の高いものは、AIが判断する良記事である

これまでの処理の組み合わせ判断によって、
精度を向上させるというワケ。

自身のいいねが低いものが出ていれば、
それは人がいいねをつけなかった、「埋もれた良記事」ということ。

埋もれたものだけ表示していると、
「どの記事に似ているからいいね判断されているんだよ!?」
ってことが分からなくなってしまうため、
既に多数のいいねを集めている記事も含めて、
BEST100を抽出してみよう!

AIが判断する良記事BEST100の抽出処理

  • マギレを減らすため、類似TOP300と少し範囲を広める。
  • どんなタグが付いているのか興味があるため、タグも表示する
BEST100の抽出処理
topNo=300
url_iine_ruiji_log_list=[]
for url_str in super_osusume_list:
    sim_kiji_list = model.docvecs.most_similar(url_str, topn=topNo)
    total_ruiji_iinesuu=0
    total_ruiji_iinesuu_log=0

    for sim_kiji in sim_kiji_list:
        sim_kiji_url = sim_kiji[0]
        sim_kiji_iine = url_key_dict[sim_kiji_url][0]
        total_ruiji_iinesuu += sim_kiji_iine

        #0のログを取らないようにする対応
        sim_kiji_iine+=1
        total_ruiji_iinesuu_log += math.log(sim_kiji_iine,2)

    #通常の平均値、ログを取った平均値を計算
    ruiji_iine_ave = total_ruiji_iinesuu/topNo
    ruiji_iine_log_ave = total_ruiji_iinesuu_log/topNo    
    moto_iine = url_key_dict[url_str][0]
    title = url_key_dict[url_str][1]

    tag_name_list=[]
    for tag_data in url_key_dict[url_str][2]:
        tag_name_list.append(tag_data["name"])

    url_iine_ruiji_log=[url_str,title,moto_iine,ruiji_iine_ave,ruiji_iine_log_ave,tag_name_list]
    url_iine_ruiji_log_list.append(url_iine_ruiji_log)

url_iine_ruiji_log = pd.DataFrame(url_iine_ruiji_log_list,
                  columns=['url_str', 'title', 'moto_iine', 'ruiji_iine_ave','ruiji_iine_log_ave','tag_name_list'])

url_iine_ruiji_log = url_iine_ruiji_log.sort_values(by='ruiji_iine_log_ave', ascending=False)

#カラム内の文字数の設定変更(URLが途中で表現されることになる。デフォルトは50から変更する。
pd.set_option("display.max_colwidth", 120)
#行数の設置絵変更
pd.set_option("display.max_rows", 101)

url_iine_ruiji_log.head(100)

上記の結果は、dataframeで表示しているが、
別途加工して、結果発表に進むことにする。
ドキドキ、ワクワク

結果発表

※埋もれていない良記事も含んでいます。

注記:元記事のいいね数は、2018年6月末時点の値。
もし、そこから数字が大きく伸びていたとしたら、
本投稿によって発掘された成果かもしれません!?

順位 類似記事log平均 元記事いいね数 記事タイトル/リンク タグ
1 3.64 1232 優秀な技術者を追い出してしまう方法 [Git, Redmine, 開発プロセス, アンチパターン, ソフトウェア開発]
2 3.57 7 プログラミング講師が考える、プログラミング学習に必要な資質と講師側の規範、また教育というものについて [日記, 教育, 新卒教育, メンタリング, 健康]
3 3.54 20 社会人10年目になったので振り返る(一部上場コンテンツプロバイダー会社) [ポエム, 転職, キャリア, 新人プログラマ応援]
4 3.54 22 改めてRiot.jsをその他のFW・ライブラリと比較して客観的に評価した [ポエム, フロントエンド, riot, riot.js]
5 3.53 20 【ポエム】コミュ力低いプログラマがサラリーマンで生きていくために工夫すべきと思うこと [ポエム]
6 3.53 6 エンジニアがオウンドメディアを書くということ [ポエム, オウンドメディア]
7 3.52 41 モグラたたき開発になる理由 [C++, デバッグ, ソフトウェア開発, モグラたたき開発]
8 3.52 16 【インターン】実際に依頼した仕事と感想を列挙するよ(だいたい技術要素ベース) [Python, JavaScript, TypeScript, インターン]
9 3.51 34 ReduxとMobXの選定観点 [reactjs, redux, mobx]
10 3.51 6 Frontrend Vol.11 - 2017年度フロントエンド大反省会まとめ #frontrend [SPA, フロントエンド, AtomicDesign, ssr, nuxt.js]
11 3.5 22 RailsとReactでオンライン予約表QRnoteを作って公開した話 [Rails, React, Rails5]
12 3.5 8 NICじゃない、チームに必要なチーミング [チームマネジメント]
13 3.49 21 『アドラーの心理学』をエンジニアの目線で解釈すると? [ポエム, 教育, 心理学, コミュニケーション, 人間関係]
14 3.49 30 新卒3年目のWebエンジニアが1・2年目にやってきた7つのこと [ポエム, エンジニア, 新卒エンジニア]
15 3.49 5 Component Driven Development with Atomic Components [JavaScript, angular, フロントエンド]
16 3.49 881 JavaScriptフレームワーク選定の議論 [JavaScript]
17 3.48 4 ロンリーQAによるJaSST '18 Tokyo レポート(2日目) [CI, スクラム, QA, Jasst]
18 3.48 11 Rails4環境でカジュアルにVue.jsを導入してからのこの1年を3つのステップで振り返る [vue.js]
19 3.48 17 Python覚えて3ヶ月の素人がCoursera Deep Learning Specializationを1週間で完走した話 [Python, DeepLearning, ポエム, ディープラーニング, coursera]
20 3.48 1017 未経験者が認識してなさそうなTIPS [tips, 初心者, 備忘録, コミュニケーション, 新人プログラマ応援]
21 3.47 32 Tech Night @ Shiodome#4 #テクシオ に参加してきたのでざっくりレポートする [勉強会, devops, 自動化]
22 3.46 2 瞑想するエンジニア ( GoogleやFacebookもやっている「マインドフルネス」のススメ ) [仕事]
23 3.46 49 真に Universal な ReactComponent を書く [reactjs, react-native]
24 3.46 7 DevOpsを知ったかぶりしていた私が、チームの人とつまづきながらカイゼン・ジャーニーしている話。 [devops, ポエム]
25 3.46 9 PHPカンファレンス関西2017行ってきたよ [PHP, phpkansai, phpカンファレンス関西]
26 3.44 2371 開設後3週間で収益10万円を得た個人開発サイトでやったことの全部を公開する [Ruby, JavaScript, Heroku, AWS, 個人開発]
27 3.44 235 Angular + Firebaseで作ったWEBサービスをリリースしたので、たまった知見を書き出してみる [JavaScript, angular, SEO, SPA, Firebase]
28 3.44 246 ムダな資格集めとはもうサヨナラ 「自分の教育マップ」の作り方 [教育, マネジメント, 資格, 新人教育, 新人プログラマ応援]
29 3.43 2 SEに求められるコミュニケーション力とは? ヒューマンスキルとして必要なこと [コミュニケーション, 新卒応援, 人間関係]
30 3.43 1 【増席しました!!40→60】PWA Beginners 勉強会 #3 に参加しました。2018/04/16(月) [AMP, PWA]
31 3.43 6 ゼロベースで考える今の(古いタイプの)システム開発現場、デキるエンジニアになろう [仕事, ビジネス, エンジニア, 新人プログラマ応援, 人間関係]
32 3.42 10 プロジェクトマネージャーになるための勉強方法 [マネジメント, プログラムマネジメント]
33 3.42 16 チームでの趣味ソフト開発ノウハウ -マイクロブログホスティングサービスUZOMUZOの開発を通して- [Rails, Twitter, チーム開発, Slack, mastodon]
34 3.42 34 エンジニアの次のステップの勉強法2 - 技術の別の道への活かし方 [アジャイル, マネジメント, 組織, エンジニア, プロダクトマネージャー]
35 3.41 992 「中年の危機」ど真ん中のオッサンがWEBサービス作ってみた。 [C#, bootstrap, オープンデータ, Webサービス, ASP.NET_Core]
36 3.4 13 新卒2年目が終わったWebプログラマが転職活動をしている話 [PHP, 転職, 転職活動, 新卒エンジニア]
37 3.4 606 半年で40kg痩せた!ダイエットでわかるリーンなプロジェクトマネジメント手法 [アジャイル, ダイエット, リーン, プロジェクトマネジメント]
38 3.4 8 マネジャーは自分の強みを何におくのか? [Redmine, プログラミング教育, ソフトウェア開発]
39 3.4 1097 11ヶ月間でTOEICスコアを300点から835点に上げた英語学習法 [英語, English, toeic, 英語上達完全マップ]
40 3.4 972 就活のためにWEBサービスを作ったら、転職できた以上の価値をもたらしてくれた話 [PHP, laravel, Webサービス, webサービス開発]
41 3.39 332 [React Native入門完全版]「いきなりデート」のアプリをReact Nativeで開発した知見をまとめます。 [JavaScript, Rails, es6, React, reactnative]
42 3.39 3 10年以上勤めた大手通信事業者を退職してスタートアップ企業へ転職した話 [ポエム, 研究, 転職, 退職]
43 3.39 40 応用情報技術者試験に合格する勉強方法・対策 [まとめ, 資格, 応用情報技術者試験, 勉強法]
44 3.39 42 AngularにWebComponentsがやって来るヤァ!ヤァ!ヤァ!〜@angular/elementsのファーストインプレッション〜 [JavaScript, angular, WebComponents]
45 3.38 0 🔰初めての、Developers Summit(デブサミ) 2018 1日目 行ってきた。 [メモ, 初心者, デブサミ, 感想文, DevelopersSummit]
46 3.38 327 有志の社内勉強会を1年で50回くらいやった結果 伝えたいこと [勉強会, ポエム, 社内勉強会, 運営]
47 3.38 194 講義録『エンジニアとしてこの先生きのこるために』(@t_wada先生) [プログラミング, 初心者, キャリア, 新人プログラマ応援, 学習法]
48 3.38 795 会社勤めのエンジニアが開発したサービスを買い取って独立した話 [キャリア]
49 3.37 196 【便乗記事】エンジニアがデスマーチを生き抜くための知見をまとめてみた [プロジェクト管理, マネジメント, エンジニア, 生存戦略, デスマーチ]
50 3.36 4 (レポート)RSGT2018参加報告 ~ Gathering ~ [スクラム, LT, ワークショップ]
51 3.36 1476 プロジェクトの残業を50%削減したタスク管理手法を惜しみなく公開する [todo, チーム開発, プロジェクト管理, タスク管理, プロジェクトマネジメント]
52 3.35 1002 ディープラーニングに入門するためのリソース集と学習法(2018年版) [Python, ディープラーニング, 深層学習]
53 3.35 111 相性抜群なVueとSVGでオンラインマインドマップ [JavaScript, SVG, vue.js, フロントエンド, Firebase]
54 3.35 80 UnityエンジニアがTypeScript+WebGLで3Dダンジョンゲーム作ってみた結果 [HTML5, WebGL, Unity, three.js, TypeScript]
55 3.35 46 今更ながらRedmineでチームのタスク管理した時の話(+これからのタスク管理) [Redmine, プロジェクト管理, タスク管理, プロジェクトマネジメント]
56 3.35 16 スタートアップで、あえて最先端の技術を使うメリットとデメリット [JavaScript, ポエム, angular, Firebase, スタートアップ]
57 3.35 109 プロダクト開発チームで今年やってみてよかったこと10選 [マネジメント, pm, プロダクトマネジメント, プロダクトマネージャー]
58 3.35 4 フリーランスを目指すなら知っておけ! ~社会保険のお話~ [フリーランス, 社会保険]
59 3.34 186 React 案件での面談時の質問事項 [JavaScript, React, 面談対策]
60 3.34 2 ClojureScript + re-frame 入門 [ClojureScript, re-frame]
61 3.34 1 「サーバント リーダーシップ」 のまとめ [リーダーシップ]
62 3.34 6 続 テストガールRINA(指教編) [テスト, adventcalendar2017]
63 3.34 0 ストレングス基礎コース [ストレングスファインダー]
64 3.33 4 実質3日間の勉強で基本情報技術者試験に合格した(と思う)ので、勉強法を公開します!【初心者|忙しい人向け】 [基礎, 勉強, 基本情報処理技術者試験, 基本情報技術者]
65 3.33 43 新人に向けてるわけでもない特に言っておきたいほどでもない纏まってすらいない話 [ポエム, 新人プログラマ応援]
66 3.33 896 「お前らのフリーランスになるメリットは間違っている」というお話 [プロジェクト管理, 法律, 法務, 新人プログラマ応援, フリーランス]
67 3.32 17 それでもやっぱり redux は面倒くさい [JavaScript, flux, ポエム, redux]
68 3.32 141 自立自走型チームの構築と心理的安全性を高める施策 [アジャイル, ポエム, チーム開発, マネジメント]
69 3.32 391 5000人に聞いた、2018年最先端のフロントエンドツールはこれだ [CSS, JavaScript, フロントエンド, エンジニア, 戸田奈津子訳]
70 3.32 6 「成長頭打ち」と感じたエンジニアは筋トレに励め。 [筋トレ, エンジニア, データサイエンティスト, スクラムマスター]
71 3.31 1 3人のgit初心者 - The git beginners of three - [Git, bitkeeper]
72 3.31 4 Flutterウィークリー #18 [Android, iOS, Dart, Flutter, FlutterWeekly]
73 3.31 14 Next.jsライクなReact Routerを作ってみた [JavaScript, OSS, React, react-router, next.js]
74 3.31 39 React Nativeでの高速プロトタイピング・MVP開発のためのTips7つ (firebase / gas / google cloud function) [reactnative, react-native, Firestore]
75 3.31 972 【保存版・初心者向け】独学でAIエンジニアになりたい人向けのオススメの勉強方法 [Python, 機械学習, DeepLearning, AI, AIAcademy]
76 3.3 15 要約 プログラマが知るべき97のこと+10 [プログラミング, 英語, プログラマ, プログラミング作法, 英語学習]
77 3.3 233 エンジニア組織がない会社でエンジニア組織を立ち上げるためにやった3つのこと [新人教育, 組織, エンジニア, プロダクトマネージャー, 新人プログラマ応援]
78 3.3 9 Chrome Dev Summit 2017まとめ [JavaScript, Chrome, カンファレンス, PWA, ProgressiveWebApps]
79 3.3 39 もし軍曹が携帯電話をいま開発したら [C++, デバッグ, ソフトウェア開発, モグラたたき開発, デスマーチ]
80 3.3 0 (レポート)Developers Summit 2018(2日目) [アジャイル, hipchat, スクラム, VR, モブプログラミング]
81 3.3 4 (レポート)Navitime × NRI勉強会 [スクラム, ワークショップ, ふりかえり, 講演]
82 3.29 39 Microsoftの自然言語分類技術を本気で検証してみた。〈Microsoft Cognitive ToolkitでのChatBot実装〉 [C#, 自然言語処理, 機械学習, DeepLearning, bot]
83 3.29 6 人工芸術家//自分の代わりに機械に作品を作ってもらいたい人の話 [人工知能, 芸術, 人工創造性]
84 3.29 3 九州(福岡)で9年間プログラマやったらこんな感じだった [ses, 転職, キャリア, フリーランス, 経歴]
85 3.29 415 Nuxt.jsとFirebaseを組み合わせて爆速でWebアプリケーションを構築する [JavaScript, vue.js, Firebase, nuxt.js]
86 3.29 389 週末在宅ワークの生産性を上げる5つの視点と32の工夫 [アジャイル, 効率化, マネジメント, エンジニア, 働き方改革]
87 3.29 53 【サポーターズCoLab勉強会】20代エンジニアのキャリア論 に行ってきました [キャリア, エンジニア, 新人プログラマ応援]
88 3.28 9 明日の開発カンファレンス 2018 に行ってきた [Security, devops, スクラム]
89 3.28 360 2018 年 React と Redux のエコシステム総まとめ [JavaScript, reactjs, React, redux]
90 3.28 114 最強のリンク集の最強のリンク集 [JavaScript, GitHub, awesome]
91 3.27 12 ロンリーQAによるJaSST '18 Tokyo レポート(1日目) [CI, スクラム, QA, Jasst]
92 3.27 620 ペアプロ懐疑派だった僕が、実務でペアプロ導入して180度考えが変わった話 [アジャイル, チーム開発, 開発プロセス, スクラム, ペアプロ]
93 3.27 7 初めてのReactで初めてのSPAを作りました [JavaScript, SPA, reactjs, React]
94 3.27 2 (レポート)プロダクトオーナー祭り2018 ~世界を創るのは俺たちだ!~ [スクラム, set, ふりかえり]
95 3.27 15 【和田卓人氏特別講演】若手エンジニアに送る"心構え"と"キャリア観" / 2017年9月23日 [初心者, 和田卓人, キャリア, エンジニア, 若手]
96 3.27 562 CTOのやるべきことは何なのか?(翻訳と考察) [経営, CTO, スタートアップ, テクノロジー・ビジネス]
97 3.27 8 プログラミングをあまりしたことない人がReactNativeを始めるときに知っておきたいことや参考資料のまとめ [JavaScript, reactnative]
98 3.26 24 自己組織化された開発チームへの道のり2017 [マネジメント, 自己組織化, 組織, プロダクトマネージャー, エンジニアリングマネージャー]
99 3.26 7 「ITエンジニア採用に欠かせない原則とは」を読んで [採用]
100 3.26 1 1年間の「ふりかえり」をふりかえる [スクラム, ふりかえり]

結果考察

元々いいねが高い記事ももちろん抽出されているし、
良い記事に見えるのに、「いいね」が少ないものも、
かなり抽出されていると感じた。
(詳細はもう少し時間をかけて見ないと・・・)

なお、「殿堂入り記事」レベルは、全体の0.4%程度であり、
抽出処理では、自分自身=元記事いいね数は参照していないのだが、
(正確にはクラスタの平均いいね計算で、自身のいいね数も含めているものの、
 その参照の仕方は、多数の中の平均として使っているに過ぎない)
予想以上に、殿堂入りレベルの記事が入っている割合が高い

上述の100記事の平均いいね数は、なんと206

「コード系」より「文章系」の方が多く抽出されているような気がする。
どういう分類だよ

つけられている「タグ」について、
比較的マイナーで、TOP60位に入っていないものが多い、
ような気はする。

Doc2Vec、クラスタリング、各種精度チューニングや工夫、によって、
自身のいいね数、タグ、を考慮に入れなくても、
これくらいの精度で「良記事の抽出」を実現することが出来た

実際どの程度の精度なのか、
詳細はぜひ皆さんの感覚で記事を読んで確認していただきたい
また、この結果、全く知らなかった埋もれていた世界が
少しでも広がることがあれば、大変うれしく思う

相変わらず難しい点は、今回抽出された記事が、
果たして本当に良記事なのか、その精度をはかる方法だ。
良記事かどうかは「主観」によって異なるためだ。

しかし、情報が過多である現在、
機械にある程度の判別をさせたり、
機械にオススメ記事を探してもらったり、という
「リコメンド」は多く求められている。

また、今回のモデル、クラスタを使うと、
記事を書いた瞬間から、その記事が何いいねくらいつきそうか、
分類&予測が出来る

(※この記事自体を入れて予測させても面白いが、
  ハズすと悲しいし、既に超長くなりすぎているので今回はパス)

あとがき

表のあとがき

以下の2点の投稿は、この投稿のための助走でもあった。
しかし、「いいねが少ない良記事」の発掘は、
ふつーに考えても超難しい
かなり分かりにくくなってしまったかもしれない。
100回くらい読み直してみると良いかもしれない

繰り返し強調しておきたい。
Qiitaには、いいね数だけで測れない良記事が沢山眠っている。

トレンドで、現在表にあがってくる記事だけでなく、
「殿堂入り」のように過去のトレンドの保存や、
今回の投稿のように、隠された良記事を発掘しやすくする
何らかの仕掛けを望む人は多いのではないだろうか?

あと、この記事自体が埋められてしまうと発掘者が埋まってしまうため、
それだけは避けなければならない。つまり・・・

裏のあとがき

Qiita分析に関する一連の投稿は、実は以下の2点と合わせた3部構成の目的がある。

言語をベクトル化する技術(Word2Vecの系統)が面白いと感じた。
しかし、その実装例や紹介例として、
「王」ー「男」+「女」=「女王」 をそのまま動かすだけのような情報が多く、
(それはそれで重要で、チュートリアル的にもありがたい情報なのだが)
真の力を引き出せていないように感じた。

単語、文字、文章で、それぞれもっと強力な応用例を提示し、
世の中の可能性を刺激したい、という一連のテーマとして設定した。

Word2Vec⇒「赤の他人」、
Char2Vec⇒「平成の次の元号」、
そして、
Doc2Vec⇒「Qiita分析系の一連の記事」。

無事、一巡することが出来て、まずはホッとしている。
大変だった・・・。(次は二巡目!?)

追記:次回のテーマは、二巡目ではなく「しりとり」にしてみた。以下記事。
「しりとり」徹底分析!最強キャラ(文字)解説&5つの「驚愕」&最長の増殖しりとりへ挑戦
(日本人なら絶対に驚くしりとりの意外なデータ分析結果が明らかに!?後半の表が必見)

全記事どれも、昨今の投稿の中でも特に無駄を極めた内容に、
多くの応援をいただき、大変感謝しています!

人類の進化は「遊び」からはじまる。
こんな「遊び」が出来るならば、というアイデアに触発される人がでて、
生活にも役に立つような「発明」が生まれるのだ。
          ~  Char Fuitter (1847~1912 オランダ) ~


この物語はフィクションです。
登場する人物・団体・名称等は架空であり、
実在のものとは関係ありません。
Char Fuitter (チャー・フイター)は架空の人物です。
また、このようなおかしな内容の記事からリンクしてしまった
リンク先の皆様、ご迷惑かけて申し訳ありません。