二日間でアイドル同士の掛け算を実装したお話

どうも、ミリオンライブのオタクです。
りあんりあんゆり、しずしほよりしほしず派です。

先日行われた高専カンファ北海道にて登壇し話した内容について話しきれなかったこと含めて軽くまとめたいと思います。

高専カンファ北海道についてはこちらを

kab-nan.hatenablog.com

元々10分枠で登壇予定だったケヴィンくんが風邪で喉をやられてしまったため、発表難しいから資料なしで2,3分だけ話させてくれとの事だったので残りの7分を僕が話すことになりました。これがカンファ二日前のお話。

元々考えてたことはあったので、気合で二日間で実装&スライド作るぞ!となりました。そのため色々適当だったりガバガバな部分がございますがご了承ください。

そもそも何を作ったのか

こちら僕の登壇資料になります。

簡単に言うとミリオンライブのアイドル52人のカップリングがお似合いかどうかを値で出してみよう!!って感じです。

そもそもこれを思いついたのが先日行われたIM@S Engineer Talk2019でみかみんさんが自然言語処理のお話をしていたのがきっかけだったりします。

t.co

パクリではございません。リスペクトです

卒研でも自然言語処理っぽいことやったりしているので頑張れば出来るかなーって思い今回やってみました。

スライドにも書いてますがざっくり流れとしましては

pixiv百科事典の各アイドルのページから情報収集
        ↓
集めた情報を教師データにして各アイドルのベクトル値を求める
        ↓
求めたアイドルのベクトル値を掛け算
        ↓
いい感じの値が出てくる!!!!!

といった感じです。

本当は行列の掛け算でやるのが理想でしたが時間との兼ね合いで断念。
行列でやってたら例えば一行一列目はそのアイドルにおいての「女の子」の要素を値にしたもの、二行一列目は「男の子」の要素を値にしたもの~~~みたいな感じかなと

実装について

何度も言いますが急遽作ったものなのでソースコードが汚かったり他サイトからの引用があったりしますが見逃してください。

pixiv百科事典からのデータ収集

とにかくデータを大量に集めたかったためアイドル個人だけでなくカップリングやユニットの百科事典も利用しました。

corps = []

link = "https://dic.pixiv.net/a/"
keyword = ["望月杏奈", "七尾百合子", "天海春香", "春日未来", "如月千早", "木下ひなた", "四条貴音",
           "ジュリア", "高山紗代子", "田中琴葉", "天空橋朋花", "箱崎星梨花", "松田亜利沙", "三浦あずさ",
           "水瀬伊織", "最上静香", "矢吹可奈", "エミリー・スチュアート", "大神環", "我那覇響", "菊地真",
           "北上麗花", "高坂海美", "佐竹美奈子", "島原エレナ", "高槻やよい", "永吉昴", "野々原茜", "馬場このみ",
           "福田のり子", "舞浜歩", "真壁瑞希", "百瀬莉緒", "横山奈緒", "秋月律子", "伊吹翼", "北沢志保",
           "篠宮可憐", "周防桃子", "徳川まつり", "所恵美", "豊川風花", "中谷育", "二階堂千鶴", "萩原雪歩",
           "双海亜美", "双海真美", "星井美希", "宮尾美也", "伴田路子", "白石紬", "桜守歌織", "はるみら", "あんゆり",
           "みななお", "いくもも", "かおつむ", "ことエレ", "ことめぐ", "しずしほ", "みきつば", "みらしず", "かなしほ",
           "ちはしず", "みずもも", "シアターデイズ", "ミリオンライブユニット",
           "レジェンドデイズ", "乙女ストーム!", "クレシェンドブルー", "エターナルハーモニー",
           "灼熱少女", "ミックスナッツ", "ARRIVE", "フェアリースターズ", "エンジェルスターズ", "プリンセススターズ", "Cleasky",
           "トゥインクルリズム", "EScape", "4Luxury", "閃光☆HANABI団",
           "りるきゃん", "Charlotte・Charlotte", "ピコピコプラネッツ", "ミリラジ組", "トライスタービジョン", "ミリマス15歳組"]

for word in keyword:
    with request.urlopen(link + parser.quote_plus(word)) as response:
        #responseはhtmlのformatになっている
        html = response.read().decode('utf-8')
        soup = BeautifulSoup(html, "lxml")
        #<p>タグを取得
        p_tags = soup.find_all('p')
        # html形式の文字列から<p></p>で囲まれている部分を取り出し形態素解析→corpsに追加
        for p in p_tags:
            corps.append(split_text_noun(p.text))

 

split_text_nounという関数で形態素解析を行っています。

形態素解析mecab-ipadic-neologdという辞書を利用しました。

普通の辞書では最近の言葉に対応していないため最新の単語まで網羅しているneologdを使いました。多分普通のmecabではアイドル名をフルネームで認識してくれないと思います。。。

def split_text_noun(text):
    tagger = MeCab.Tagger("-Ochasen -d C:\mecab-ipadic-neologd\\build\mecab-ipadic-2.7.0-20070801-neologd-20190808")

    tagger.parse("")  # エラー回避のために空文字をパース
    node = tagger.parseToNode(text)  # 最初のnodeを取得

    words = []

    while node:
        word = node.surface  # surfaceには単語が入っている
        hinsi = node.feature.split(",")[0]  # featureには品詞や品詞細分類,活用形,読み方など様々な情報が入っている
        if hinsi == "名詞":
            if re.search('[0-9]+', word):
                pass
            else:
                words.append(word)
        node = node.next  # 次のnodeに移る

    word = ' '.join(words)

    return word

学習部分

word2vecを使ってモデルを作成。

100回ほど学習させています。

””百合””という単語のベクトルを1.00とした場合、アイドルがその言葉にどれだけ近いかを各アイドルのベクトルとして判断。

seme変数とuke変数を用意しここにアイドルの名前をぶち込みます。

seme × uke ≠ uke × seme
を成り立たせるため兼攻めの値の方が大きい方が燃えるよなって思ったためsemeアイドルのベクトルを10倍しました。

結果としてはこれでいい感じになったので良かったです。

# モデル作成
model = word2vec.Word2Vec(corps, size=30, min_count=13, window=8, iter=100)
# モデル保存
model.save("pixiv_wiki.model")
# モデル読みこみ
model = word2vec.Word2Vec.load("pixiv_wiki.model")

seme = sys.argv[1]
uke = sys.argv[2]
key = "百合"
#単語のベクトルを見る
word_vector = model.wv[key]
words = model.most_similar([word_vector], [], 15000)

for name, vector in words:
    if(seme == name):
        seme_vec = abs(vector * 10)
    elif(uke == name):
        uke_vec = abs(vector)

result = seme_vec * uke_vec

print(result)

実行例

望月杏奈と七尾百合子の場合

f:id:kab_nan:20190827023808j:plain

左:七尾百合子 右:望月杏奈

望月杏奈 × 七尾百合子 = 0.521208516171705
七尾百合子 × 望月杏奈 = 0.383473814941553

正解!!!!!

最上静香と北沢志保の場合

f:id:kab_nan:20190827024841j:plain

左:最上静香 右:北沢志保

最上静香 × 北沢志保 = 0.8480868357467131

北沢志保 × 最上静香 = 0.7599406408698961

解釈違い

 

最後に

これその都度ベクトル求めるために学習させてるから常に同じ値が出るわけではないんですよね。なので正直精度はガバガバです。
こんなガバガバ精度でも良ければ、誰々のカップリングの値が見たいと言っていただければ試してみます。

ただまあ好きなものを自己流で実現できるってことはやってて楽しかったですね。
こうやって就職した後も仕事で磨いたスキルを好きなことに活かせたらなぁと思います。

来年就職したら関東に住むから絶対Im@stady行くぞ!!!!

何か気になる点ございましたらtwitterの方にご連絡ください。

ご覧いただきありがとうございました。