見出し画像

word2vecを用いて久保建英選手の評価を分析してみる

久しぶりのnoteの投稿になります。コロナが大分続いているのもあり、しばらくは自宅でのリモートワークがここ数ヶ月続いている状況です。

最近は仕事でも自然言語処理を用いた機械学習やデーターサイエンス寄りのことをやる機会がちょくちょく出始めてきました。

しばらくはNuxtやvue.jsなどのフロントを書いたり、サーバーサイドエンジニアとしてDjangoのコードを書いたりという機会も少しずつ減って、機械学習寄りの仕事も増えていきそうです。

現在は全然わからないため日々勉強中のため、自分で色々とサンプルや簡単なものを作り始めていたりします。

今回やること

リーガやプレミア、ブンデスなどの欧州リーグやJリーグなども再開されました。特にマジョルカに所属する久保建英選手の活躍が素晴らしいものです。

現段階で4得点とチーム状況を考えれば十分活躍しており、来シーズンの去就が気になる所でもあります。

今回はword2vecの勉強も兼ねて趣味であるサッカーの分析をしてみようかと思います。

対象とするのサッカー記事です。yahooニュースの記事から久保建英選手の記事を40件近く取得し、それらを形態素解析で分かち書きの状態にしてからword2vecに学習させます。

word2vecとは

word2vecは文章中の単語の意味をベクトル化する技術であり、関連する単語を抽出したり、単語同士の類似度を測ったりすることもできます。

仕組みとしてはニューラルネットワークを用いており、CBOWとskip-gramというモデルが使用されています。

詳しくはこちらを参照してください。

使用するライブラリ

今回の分析では以下のライブラリ郡を使います。

requests
スクレイピングのためのHTTPリクエストを行うためのモジュール
https://2.python-requests.org/en/master/

以下で簡単に導入できます

$ pip install requests

BeautifulSoup
スクレイピングしたHTMLやXMLをパースしたりするためのモジュール

以下で簡単に導入できます 

$ pip install beautifulsoup4

mecab-python3
形態素解析にはmecabを使用します。mecabの環境構築はmacやwindowsなどのOSによって違うため、各自それぞれで環境構築をしてください。

macでのやり方はこちらを参照してください。

gensim

Word2vecのモデルを作成するためのモジュール

導入方法は以下になります

$ pip install --upgrade gensim

Yahooニュースから記事をスクレイピングする

それではまずYahooニュースから「久保建英」というキーワードでヒットする記事をスクレイピングしていきます


import requests
from bs4 import BeautifulSoup as bs


def request_bs_soup(url):
    # Responseオブジェクトの取得
    response = requests.get(url)
    soup = bs(response.text.encode(response.encoding), 'html.parser')
    return soup

def get_news_articles(keyword, count):
    url = 'https://news.yahoo.co.jp/search/'
    url += '?p={}&ei=utf-8&fr=news_sw'.format(keyword)

    # 記事リスト
    articles_links = []

    # ページ数
    page = int(count / 10)
    for i in range(1, page + 1):
        path = url + '&b=' + str(i)
        soup = request_bs_soup(path)
 
        # title部分の取得
        selected_class = soup.select("h2[class='t']")
        articles_links += [s.find('a').get('href') for s in selected_class]
 
    return articles_links
    
def get_article(path):

    # 記事に対してリクエスト
    soup = request_bs_soup(path)
    text = soup.find('p', class_='yjDirectSLinkTarget').text
    return text

if __name__ == "__main__":
    keyword = '久保建英'
    count = 40
    articles = get_news_articles(keyword, count)

    for path in articles:
        article = get_article(path)
        print(article)

まず最初にget_news_articlesという関数で引数keywordで検索した記事のリンクを引数countの枚数分取得します。

上記のコードでは「久保建英」というキーワードの記事を40記事分取得しています。これで記事のリンクがList型でarticlesという変数に格納されます。

get_articleという関数に記事のリンクを与えることで、実際に記事本文の文章を取得します。

上記を実行すると以下のように記事の本文が出力されるはずです。

 マジョルカに所属するMF久保建英が、9日に行われたリーガ・エスパニョーラ第35節レバンテ戦後、同試合についてコメントした。同日、マジョルカのクラブ公式サイトが伝え
た。

 マジョルカは40分、クチョ・エルナンデスのヘディング弾で先制に成功すると、84分にはゴール前で混戦から最後は久保が左足で押し込んで追加点を獲得。2-0で勝利した18>位マジョルカは、1試合未消化の17位アラべスとの勝ち点差を「3」に縮めた。

 リーグ再開後初、8試合ぶり今季4点目を挙げた久保は試合後、次のようにコメント。お辞儀パフォーマンスにも言及した上で、次節セビージャ戦に向けた意気込みも語った。

「得点できたことをとても喜んでいます。試合結果もチームが求めていたものでしたし、試合後はチーム全体で喜びました。チームは思い通りの試合をプレーし、どんなときも>(勝利を)信じています。僕たちは今後もこの戦い方を続けていかなければなりません」

「(GKコーチのフェルナンド・)マエストロには『次のゴールはあなたのための1点だ』と言っていたんですが、彼はどうやら覚えていなかったみたいです」

「次は日曜日(12日)のセビージャ戦です。僕たちの最善を尽くします。自陣に閉じこもり、彼らを追いかける展開にならないよう、挑戦しなければならない。うまくいけば『>ラモン・サンチェス・ピスフアン』から3ポイントを持ち帰れるはずです」
 海外サッカー、スペイン1部マジョルカは現地時間9日、リーガ・エスパニョーラ第35節でレバンテと対戦し、2-0で勝利した。日本代表MF久保建英は11試合連続で先発出場。1>点リードの後半39分に今季4ゴール目となる追加点を奪った。リーガ公式SNSは実際のゴールシーンを動画付きで公開。海外ファンからも賛辞を浴びている。

....

記事の本文を分かち書きする

次にスクレイピングで取得した記事の本文を分かち書きと言って形態素解析にかけることで品詞部分を抽出します。

import MeCab


# MeCab による単語への分割関数 (名詞のみ残す)
def split_text_only_noun(text):

    option = '-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd'
    tagger = MeCab.Tagger("-Ochasen " + option)
    tagger.parse('')

    # Execute class analysis
    node = tagger.parseToNode(text)

    words = []
    while node:
        word = node.surface
 
        class_feature = node.feature.split(',')[0]
        sub_class_feature = node.feature.split(',')[1]
 
        if class_feature in ['名詞', '動詞', '形容詞', '記号']:
            if sub_class_feature not in ['空白', '*']:
                words.append(word)
        node = node.next
    return words

今回は「久保建英」という文字列があっても「久保」「建英」に区切られてしまったり、「レアル・マドリード」も「レアル」「・」「マドリード」に分けられてしまうことも考えられたため、Mecabに新語辞書を適用しました。('-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd'の部分がそうです)

新語辞書を使うと以下のように「久保建英」が中途半端に区切られずにすみます。

$ mecab
久保建英
久保	名詞,固有名詞,人名,姓,*,*,久保,クボ,クボ
建	名詞,固有名詞,人名,名,*,*,建,ケン,ケン
英	名詞,固有名詞,地域,国,*,*,英,エイ,エイ
EOS

$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd
久保建英
久保建英	名詞,固有名詞,人名,一般,*,*,久保建英,クボタケフサ,クボタケフサ
EOS

split_text_only_noun関数からは以下のような品詞で区切られたリストが返ってきます。

['久保建英', '現地時間', '9日', '開催', 'さ', 'れ', 'ラ・リーガ', '35', '節', '、', 'マジョルカ', 'vs', '.', 'レバンテ', '11試合', '連続', '先発', '出場', 'し', '、', '後半39分', 'リーグ', '再開', '後', '初ゴール', 'なる', '今季', '4点', '目', '決める', '、', '2', '-', '0', '勝利', '大きく', '貢献', 'する', '活躍', '見せ', '。', '【', '秘蔵写真', '】', 'かわいい', '12歳', 'バルサ', '久保', '。', 'ロン', '毛', '長谷部', '&', '本田', '話す', '俊輔', '&', '中田英寿', '、', 'ギラ', 'つく', 'カズ', '川口', 'ヤンチャ', 'そう', '現役時代', '松木', '…', '日本', 'サッカー', '伝説', '50人', '。', '右', 'サイドハーフ', 'キックオフ', '笛', '聞い', '久保', '、', '立ち上がり', '2分', '鋭い', 'クロス', '送っ', '味方', 'ヘディングシュート', '繋げ', '、', '30分', '得意', 'カットイン', '痛烈', 'ミドルシュート', '放ち', '、', '相手', 'ゴール', '脅かし', '。', '1部', '残留', '向け', '勝ち点3', '獲得', '必須', 'マジョルカ', '前半40分', '、', 'クチョ・エルナンデス', 'ゴール', '先手', '取る', '、', '後半', 'レバンテ', '何', '度', '決定機', '許す', '、', '無失点', '切り抜ける', '。', '後半39分', '、', '久保', 'ハーフウェー', 'ライン', '付近', 'ドリブル', 'シュート', '持ち込む', '。', 'これ', '相手GK', '弾か', 'れ', '、', 'サポート', 'し', '味方', '二次', '攻撃', 'つなぎ', '、', 'ボール', 'ゴール前', 'こぼれ', 'ところ', '久保', '詰め', '左足', '押し込ん', '。', '久保', '27', '節', 'エイバル', '戦', '以来', 'ゴール', '。', 'ビセンテ', '・', 'モレノ', '監督', 'ら', 'チームスタッフ', '笑顔', '出迎える', '中', '、', '久保', '“', 'お辞儀', 'ポーズ', '”', '喜び', '表現', 'し', '。', '地元紙', '「', 'AS', '」', '試合後', '評価', 'チーム', '最高', '点', 'なる', '3点', '満点', 'つけ', '、', '「', 'マルカ', '」', '紙', '「', '(', '久保', ')', 'チーム', '2点', '目', 'ゴール', '反応', '、', '降格', '危機', 'ある', 'チーム', 'どれ', '重要', '勝利', '強調', 'し', '」', '久保', '追加点', 'チーム', '非常', '大きかっ', '描写', 'し', 'いる', '。', '「', 'Number', 'Web', '」', '連載', '中', '解説者', 'リーグ', '再開', '後', '久保', '「', '自分', 'いく', '、', '味方', '使う', '、', '判断', '閃き', '久保', '魅力', '。', 'フィジカル', '逞し', 'さ', '増し', 'い', '、', '期待', 'し', 'いい', '」(', '水沼貴史', '氏', ')、「', '久保', 'チャンス', 'メイク', 'できる', '選手', 'いれ', '、', 'シュート', '打てる', '機会', '増える', '思い', '」(', '中西哲生', '氏', ')', '話し', '、', 'ゴール', '直結', 'する', '活躍', '予見', 'し', 'い', '。', '4得点', '絡ん', '33', '節', 'セルタ', '戦', '含め', '、', 'ラ・リーガ', '今季', '終盤戦', '力', '発揮', 'し', 'いる', '言える', '。']

分かち書きしたデータをテキストファイルに保存

上記で記事本文を品詞に分かち書きする所まで出来ました。次にそれらをテキストファイルに保存する所まで行います。

# 分かち書きしたデータをファイルに保存
def save_wakati_file(wakati_list, save_path='wakati.txt', add_flag=False):

    # 新規保存か追加保存かの選択
    mode = 'w'
    if add_flag:
        mode = 'a'

    # 分かち書きしたデータをファイルに保存
    with open('./' + save_path, mode=mode, encoding='utf-8') as f:
        f.write(' '.join(wakati_list))


if __name__ == "__main__":

    list_text = []
    for path in articles:
        article = get_article(path)
        list_text += split_text_only_noun(article)

    save_wakati_file(list_text)

' '.join(wakati_list)とあるように品詞を半角スペース毎に区切ってテキストファイルに保存しています。

save_wakati_fileという関数の処理が完了するとwakati.txtというテキストファイルが作成されるはずです。

分かち書きしたテキストファイルをword2vecに学習させる

それでは先ほど保存したテキストファイルをword2vecに学習させてみます。これにより新たなコーパスを作成します。

from gensim.models import word2vec


def save_word2vec_model(load_path, save_path):
    # 分かち書きしたテキストデータからコーパスを作成
    sentences = word2vec.LineSentence(load_path)
 
    # ベクトル化
    model = word2vec.Word2Vec(
        sentences,
        sg=1,
        size=100,
        min_count=2,
        window=30,
        hs=1
    )
 
    model.save(save_path)
    # 作成したモデルをファイルに保存


if __name__ == "__main__":
    load_path = 'wakati.txt'
    save_model_path = 'save.model'
 
    save_word2vec_model(load_path, save_model_path)

wakati.txtを読み込ませて学習させたモデルを作成します。モデルはsave.modelという名前で保存されます。

これでyahooニュースの記事の内容のモデルの作成が完了しました。

プレー評価の類似単語を分析してみる

それでは実際に学習したモデルを読みこんでみます。以下のコードで実行できます。

from gensim.models import word2vec

load_model_path = 'save.model'
model = word2vec.Word2Vec.load(load_model_path)

results = model.wv.most_similar(positive=['ドリブル'])
print(results)

positiveの引数である「ドリブル」に距離が近い単語を一覧で出力します。「ドリブル」の単語に類似度が近い単語が上位10が表示されます。

「ドリブル」

('メッシ', 0.7708497047424316),
('強烈', 0.7691226005554199),
('数値', 0.7690764665603638),
('成功率', 0.768637478351593),
('作りだし', 0.7673574686050415),
('叩き', 0.7668083906173706),
('10試合', 0.7653787732124329),
('ミドルシュート', 0.7612514495849609),
('直近', 0.7611082792282104),
('中5', 0.7605136036872864)

「ドリブル」に最も近い類似語としてはやっぱり「メッシ」が該当しました。「成功率」も上位として出ている通り、久保建英とメッシがドリブル成功数が1位と2位とランクインされているのもあることから、お互いのプレーが比較されていることがここからもわかります。

ここから別な単語もいくつか試してみます。

「パス」

('成功', 0.9849017858505249),
('数', 0.9443048238754272),
('18本', 0.897209644317627),
('15本', 0.8949441313743591),
('激減', 0.8936759233474731), 
('ボールタッチ', 0.890598714351654),
('程度', 0.8896241784095764),
('本数', 0.8875795602798462),
('39回', 0.8870886564254761),
('半分', 0.8859546780586243)

ほとんどのパスが「成功」しているということでしょうか。「激減」というのがなんなのか気になります。

「シュート」

('右足', 0.8274426460266113),
('防が', 0.8194200992584229),
('敵', 0.808085024356842),
('味方', 0.8034147024154663),
('持ち込ん', 0.7923069000244141),
('こぼれ球', 0.7906830310821533),
('突き刺し', 0.7896105051040649),
('はじい', 0.7876080870628357),
('遭う', 0.7851463556289673),
('ダイレクト', 0.7825111746788025)

右足が一番の上位に来ました。今季の2得点は利き足とは別の「右足」であることからでしょうか。4得点目は「こぼれ球」と「ダイレクト」でもありました。

年も近くて連携もお互い良いと言われている「クチョ・エルナンデス」を入れてみます。

「クチョ・エルナンデス」

('ヘディング', 0.9377632141113281),
('右サイド', 0.9270282983779907),
('頭', 0.895275354385376),
('枠', 0.89146888256073),
('29分', 0.8838574886322021),
('飛ん', 0.8794739246368408),
('弾', 0.8774632811546326),
('40分', 0.8752747774124146),
('枠内', 0.8737980127334595),
('アイトール・フェルナンデス', 0.8673501014709473)

ヘディングが一番の上位に出てきました。クチョ・エルナンデスってヘディングのプレー得意だったかなと思いましたが、直近の試合がヘディングで決めていることから一番の類似単語として出たみたいです。

右サイドというのも上位に出ている事から右サイド=久保建英との連携面も類似度が高いことも表しているのかもしれません。

単語同士の引き算をしてみる

from gensim.models import word2vec

load_model_path = 'save.model'
model = word2vec.Word2Vec.load(load_model_path)

results = model.wv.most_similar(positive=['マジョルカ'], negative=['勝利'])
print(results)

次にword2vecの機能でもある単語の同士の引き算をやってみます。positiveには関連度の高い単語、negativeには関連度の低い単語を入れることで、positive - negativeという計算をすることができます。

「マジョルカ」から「勝利」を抜いてみます。

「マジョルカ - 勝利」

('攻撃', 0.37490764260292053),
('9', 0.37009772658348083),
('0', 0.3674681484699249),
('スペイン', 0.35304975509643555),
('カウンター', 0.35262393951416016),
('3', 0.32466739416122437),
('」', 0.32443761825561523),
('陣', 0.3211783766746521),
('部', 0.32022857666015625),
('中央', 0.3182632029056549)

類似度の数値が0.3と低いですが、「攻撃」「カウンター」と出ている通り、「攻撃面」が課題ということでしょうか。「中央」の攻撃面にも問題があるということかもしれません。

次に「マジョルカ」から「ゴール」を引いてみたらどうなるでしょうか。

「マジョルカ - ゴール」

('決めて', 0.4731130301952362),
('争う', 0.4691792130470276),
('導い', 0.45282360911369324),
('サンタンデール', 0.43266013264656067),
('導く', 0.43252235651016235),
('繋ぐ', 0.42018401622772217),
('奪取', 0.41551899909973145),
('今季', 0.4144895076751709),
('久保建英', 0.4022209644317627),
('日', 0.4009222388267517)

「決めて」と出てきたとのが少し面白い所です。久保の絶妙なパスやクロスを味方が見事に外して正直ゴールを決めきれないシーンが何度かあったことも気になります。

「久保建英」というのも出ている通り、ゴールに結びつくプレーに久保が多く関わっているのも納得できます。

WordCloudを生成してみる

最後にwakati.txtを使って記事本文の内容でwordcloudの画像を生成してみます。

画像1

「入山」というのが出てきました。選手で誰かいたかと思い調べてみた所、こちらの記事の内容が反映されてしまったそうです。


今回作成したソースコードは一応こちらに載せています。


参考資料



この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
Pythonのバックエンドエンジニア、たまにインフラなどもやる。最近はブロックチェーン系技術にハマっており、機械学習とGo言語なども勉強中。LaravelでWEBサービスを友人と個人開発しています。 https://qiita.com/shimakaze_soft