言語処理100本ノック 2020「53. 予測」
問題文
問題の概要
学習を終えたモデルは、予測値が未知の特徴量(X_test)を与えて予測させることができます。
import pandas as pd from sklearn.linear_model import LogisticRegression X_train = pd.read_table('ch06/train.feature.txt', header=None) y_train = pd.read_table('ch06/train.txt', header=None)[1] clf = LogisticRegression(penalty='l2', solver='sag', random_state=0) clf.fit(X_train, y_train) y_train = clf.predict(X_train)
言語処理100本ノック 2020「52. 学習」
問題文
問題の概要
用意した特徴量と予測の対象のペアから、機械学習アルゴリズムを用いて予測器を学習させましょう。
import pandas as pd import joblib from sklearn.linear_model import LogisticRegression X_train = pd.read_table('ch06/train.feature.txt', header=None) y_train = pd.read_table('ch06/train.txt', header=None)[1] clf = LogisticRegression(penalty='l2', solver='sag', random_state=0) clf.fit(X_train, y_train) joblib.dump(clf, 'ch06/model.joblib')
言語処理100本ノック 2020「51. 特徴量抽出」
問題文
問題の概要
カテゴリ分類に有用そうな特徴量を抽出します。ここでは、問題文の指示通りの最低限の特徴量を作ります。sklearnに用意されている「CountVectorizer()」が利用可能です。
記事の見出しを単語列に変換したものが最低限のベースラインとなるであろう.
データセット内に「TMP」という一時的なカラムを作成し、X_train・X_valid・X_testを結合しておくことで、特徴量抽出の処理を一度で済ましています。
import joblib import pandas as pd from sklearn.feature_extraction.text import CountVectorizer X_train = pd.read_table('ch06/train.txt', header=None) X_valid = pd.read_table('ch06/valid.txt', header=None) X_test = pd.read_table('ch06/test.txt', header=None) use_cols = ['TITLE', 'CATEGORY'] X_train.columns = use_cols X_valid.columns = use_cols X_test.columns = use_cols X_train['TMP'] = 'train' X_valid['TMP'] = 'valid' X_test['TMP'] = 'test' data = pd.concat([X_train, X_valid, X_test]).reset_index(drop=True) vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b') bag = vectorizer.fit_transform(data['TITLE']) data = pd.concat([data, pd.DataFrame(bag.toarray())], axis=1) joblib.dump(vectorizer.vocabulary_, 'ch06/vocabulary_.joblib') X_train = data.query('TMP=="train"').drop(use_cols + ['TMP'], axis=1) X_valid = data.query('TMP=="valid"').drop(use_cols + ['TMP'], axis=1) X_test = data.query('TMP=="test"').drop(use_cols + ['TMP'], axis=1) X_train.to_csv('ch06/train.feature.txt', sep='\t', index=False, header=None) X_valid.to_csv('ch06/valid.feature.txt', sep='\t', index=False, header=None) X_test.to_csv('ch06/test.feature.txt', sep='\t', index=False, header=None)
言語処理100本ノック 2020「50. データの入手・整形」
問題文
問題の概要
本章では、ニュース記事の見出しからカテゴリを分類する機械学習モデルを構築します。最初に指示に従ってデータセットを整形します。次の4段階で処理しました。
- ファイルのデータ形式の確認
- 情報源(publisher)が”Reuters”, “Huffington Post”, “Businessweek”, “Contactmusic.com”, “Daily Mail”の事例(記事)のみを抽出
- 抽出された事例をランダムに並び替え
- 抽出された事例の80%を学習データ,残りの10%ずつを検証データと評価データに分割し,それぞれtrain.txt,valid.txt,test.txtというファイル名で保存
1について、提供されているデータセットにはheaderがありません。そのため読み込み時には「header=None」を指定し、後に「pandas.DataFrame.columns」でカラム名を定義しています。
2については「pandas.Series.isin()」を利用しました。同時に「pandas.Series.sample(frac=1)」で値をランダムに並び替えています。「pandas.Series.sample()」はデータセットから指定した割合を抽出する処理で、抽出率を100%にすることでランダム並び替えと同等の処理になります。
後に機械学習アルゴリズムに投入するための前処理として、目的変数(予測の対象)のカラムの値は「map」を用いて数値に変換しています。
最後に4について「train_test_split()」を用いてデータセットを分割しました。「stratify」で目的変数の値を指定することで、分割後の目的変数の割合が等しくなるように設定しています。この分割方法は、機械学習アルゴリズムを検証する上で一般的です*1。
import pandas as pd from sklearn.model_selection import train_test_split newsCorpora = pd.read_table('ch06/NewsAggregatorDataset/newsCorpora.csv', header=None) newsCorpora.columns = ['ID', 'TITLE', 'URL', 'PUBLISHER', 'CATEGORY', 'STORY', 'HOSTNAME', 'TIMESTAMP'] newsCorpora = newsCorpora[newsCorpora['PUBLISHER'].isin( ['Reuters', 'Huffington Post', 'Businessweek', 'Contactmusic.com', 'Daily Mail'])].sample(frac=1, random_state=0) X = newsCorpora[['TITLE', 'CATEGORY']].copy() X['CATEGORY'] = X['CATEGORY'].map({'b': 0, 'e': 1, 't': 2, 'm': 3}) y = newsCorpora['CATEGORY'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=0) X_valid, X_test, y_valid, y_test = train_test_split(X_test, y_test, test_size=0.5, stratify=y_test, random_state=0) X_train.to_csv('ch06/train.txt', sep='\t', index=False, header=None) X_valid.to_csv('ch06/valid.txt', sep='\t', index=False, header=None) X_test.to_csv('ch06/test.txt', sep='\t', index=False, header=None)
「Sports Analyst Meetup #8」をオンラインで開催しました #spoana
「Sports Analyst Meetup #8」*1を、7月18日に開催しました。昨今の情勢を受け、7回目に引き続きのオンライン開催でした。
資料
togetter
発表内容
今回は10名の方にLTをしていただきました。いずれも素敵な内容で、多くの方が発表メモをブログに書いてくださっています。
アーカイブ
オンライン開催の利点を活かして、発表者の許諾が得られた内容については、YouTubeにアーカイブを掲載していく予定です。
また前回に引き続き、今回もログミーに取材して頂きました。前回の記事の第1弾は既に公開されていますので、ぜひご覧ください。
おわりに
今回も多くの方にご参加いただき、誠にありがとうございました。今回も素敵な発表をしてくださった方々に、お礼申し上げます。
東西分割開催のJリーグ各チーム移動距離を可視化
新型コロナウイルス感染症の拡大防止のため第1節を終えた段階で中断していたJ1リーグは、7月4日に一斉再開しました。7月中は移動による感染リスクを避けるため近隣クラブが対戦する方式を採用しており、具体的には全18チームを東西に2分して各グループ内で対戦相手を決めています。
次の記事で言及されている通り、序盤戦の移動距離が他チームに比べて少なく、スタートダッシュに有利な状況のチームが存在する可能性もあります。この記事では具体的な数字が出ていなかったので、本ブログで実際にデータに基づく可視化に取り組みます。
結果
縦軸の距離は、ヒュベニの公式に基づき座標から計算しました。名古屋グランパスなど複数のホームスタジアムを有している場合は、主に使われている方を採用しています。また本拠地と別の場所でキャンプを張っているなどの個別対応は考慮していません。
当然ですが、札幌や九州のチームと戦うか否かの影響が大きくなりますね。
おわりに
本記事では、東西分割開催のJリーグ各チーム移動距離を可視化しました。今後の課題としては、本来予定されていた2-7節のカードの移動距離と比較すると、差分が分かりやすくなりそうです。一連のコードは、下記で公開しています。
ProbSpace「YouTube動画視聴回数予測」コンペ参加録
ProbSpaceで開催されていた「YouTube動画視聴回数予測」コンペに参加しました。Lain.さんとチームを組み、public 4位・private 6位でした。
コンペ概要
YouTube APIで取得できるメタデータを入力として、動画の視聴回数を予測するタスクでした。具体的には、下記のデータが利用できました。
項目名 | 説明 |
---|---|
video_id | 動画ごとに割り振られる一意なid |
title | 動画のタイトル |
publishedAt | 動画の投稿時間 |
channelId | 動画を投稿したチャンネルのid |
channelTitle | チャンネルのタイトル |
categoryId | 動画カテゴリのid |
collection_date | データレコードの収集日 |
tags | 動画に割り当てられたタグ` |
likes | 高評価の数 |
dislikes | 低評価の数 |
comment_count | コメント数 |
thumbnail_link | 動画のサムネへのリンク |
comments_disabled | コメントが許可されない動画であるか? Trueの場合にはcomment_countは0となる |
ratings_disabled | 高評価と低評価が許可されない動画であるか? Trueの場合にはlikesとdislikesは0となる |
description | 動画の説明文 |
video_id
, thumbnail_link
からはそれぞれ動画・サムネイル画像が取得でき、title
, description
はテキストです。いわゆるマルチモーダルなデータが利用できるコンペでした。
取り組み
YouTubeという題材の身近さとマルチモーダルの技術的な面白さに興味をそそられ、終了10日前とギリギリでしたが参加を決めました。
チャンネル登録よろしくお願いしますhttps://t.co/IXKuLZ0tpM pic.twitter.com/7SCC6v4a9T
— u++ (@upura0) June 19, 2020
その後にLain.さんとチームマージし、最終的にpublic 4位・private 6位となりました。ベストモデルは、私1モデルとLain.さん3モデルの合計4モデルの平均でした。
私の担当部分のソースコード一式はGitHubで公開しました。以下、概要を述べます。
特徴量
テーブル + テキストのtfidf*1&count*2 で特徴量を作りました。
テキストからはBERT*3、画像からはEfficientNet*4で特徴抽出しましたが、性能に寄与しませんでした。
モデル
画像を用いたEfficientNetのfine tuningも試しましたがCVが悪かったので、ニューラルネットワークは諦め勾配ブースティング系のモデル(LightGBM*5, CatBoost*6)を使いました。Pseudo Labeling でCV & public lbスコアが伸びました。
CV戦略
生の特徴量のみを用いたLightGBMモデルのfeature importanceで上位に来ていたratings_disabled
を対象にしたStratifiedKFold(n_splits=5, shuffle=True, random_state=7)
に変更して、CV & public lbスコアが伸びました。
おわりに
まずはチームを組んでくださったLain.さんにお礼申し上げます。
個人的な反省点は、マルチモーダルを扱うニューラルネットワークで全然lossが落ちなかったことです。結局は特徴抽出してLightGBMに突っ込む定番の解法になってしまいました。とはいえEfficientNetなどを実践で試せた経験を生かして、次回以降のコンペの糧としていきます。