ジョブレコメンデーションについてのリサーチまとめ

9月のイベントでジョブレコメンデーションについて調べて発表(実務と論文で学ぶ ジョブレコメンデーション最前線2022)しましたが、ブログの方が情報量が多いのと、最近のSlideShareが非常にみづらいものになっているのに加え、アップデートもしやすいのでこちらに随時残せる記事を残しておこうと思います。今日はクリスマスなので、誰かにとってはクリスマスプレゼントとなりうるでしょうか。

業界にいるものとしての思い

私は大学時代に経済学部に通っていたときに、労働市場の流動性に関して興味を持っていました。
スキルさえあればすぐに労働者が転職をするという競争的な労働市場について思い巡らすことがありました。

しかしながら、就職活動を通じて、取ってつけたような志望動機で大して勉強をしてこなかったであろう人々と同列に扱われ、「労働市場の効率性に関して、現実は不確実な情報のなかでエイヤで決めるしかないのかな」という印象を感じました。

就職活動では求人メディア系の会社に入社しました。スキルある人が高い給与を享受しやすい仕組み作りをデータ分析の力で実現できたらいいなと思って入りました。

ジョブマッチング界隈の課題

この業界の課題については推薦システム実践入門 ―仕事で使える導入ガイドの8章5節のドメインに応じた特徴と課題で、仕事の推薦について書かれているのも参考になりますので、そちらも合わせてお読みください。

  • 双方の情報の充実化
    • 情報の非対称性を払拭できるだけの情報がない中で企業も候補者も面接に臨んでいる
  • 求人票の充実化
    • 何かしらのエージェントを使う際に自分が本当に行きたい、行くべき職場がないかもしれない
  • 求人票の質の向上
    • 候補者が企業に直接応募するにしても、求人票に記されたレベル感が曖昧なため

ジョブマッチング界隈のデータの課題

  • スキルや職歴の構造化データ化
    • 正確なスキル情報の記載
    • 所属した企業に関しても法人番号などを付与できるといい
  • 求人情報の記載項目の誤り是正、更新性アップ
    • 今は求めていない要件があるとか、扱う技術に変化があったとかを反映
  • 希望する仕事内容や職場の要望のデータ化
    • 希望する仕事について構造化データになっていない

ジョブマッチング系の論文色々

・Job Recommender Systems: A Review(2021)
・Domain Adaptation for Resume Classification Using Convolutional Neural Networks(2017)
・Implicit Skills Extraction Using Document Embedding and Its Use in Job Recommendation(2020)
・Machine Learned Resume-Job Matching Solution(2016)
・SKILL: A System for Skill Identification and Normalization(2015)
・Effectiveness of job title based embeddings on résumé to job ad recommendation(2021)
・conSultantBERT: Fine-tuned Siamese Sentence-BERT for Matching Jobs and Job Seekers(2021)

Job Recommender Systems: A Review(2021)

  • 論文の概要
    • Job Recommender System(JRS)研究のサーベイ論文(2011〜2021)[計133本]
    • 近年は求人などの大量のテキストを用いたジョブレコメンデーションが多い。深層学習も使われている。BERTもCNNも使われている。ハイブリッドモデルが多い傾向。
    • 近年ではアルゴリズムの公平性(年齢/性別での差別)に関心が持たれており、特徴量として差別的なものがなくても学習してしまう可能性が指摘されている。
    • 求職者とリクルーターのインタラクティブなデータ(click/skip)の利活用が重要となっている。研究者はRecsysなどのコンペのデータに依存しており、企業がデータセットの提供をするのが限定的であるのが問題としてある。JRSの発展のためにはKaggleやRecsysのコンペが重要とされている。
  • 参考になるところ
    • 求職者と採用担当者のインタラクティブなデータはこのコミュニティでデータとして貴重であること
    • 最近はTransformer系のアプローチが使われ始めていること

Domain Adaptation for Resume Classification Using Convolutional Neural Networks(2017)

  • 論文の概要
    • 27の職種のラベルが付与された求人票のデータ(テキスト:短文)をもとにCNNで分類器を作成
    • ラベルがない職務経歴データに関しては求人票で学習したモデルで推論
    • fastTextでの分類よりも性能が向上した

    • 求人票の数 > 越えれない壁 > 職務経歴データの数
    • CNNで学習する際の特徴量は学習済みのWord2Vecによる単語の分散表現で、同じ長さのものを使っている。
    • 最大値プーリングでの重要な特徴抽出、ドロップアウトの正則化など画像分類タスクと同様になされ、soft-max関数でマルチクラスの確率を出力している。
    • 苦手な職種でのファインチューニングがfuture work
  • 参考になるところ
    • 職務経歴書と違って収集が容易な求人票のデータをもとに学習し、職務経歴に関するラベルを推論をするスタイル
    • テキストデータに対してCNNを使っている

Implicit Skills Extraction Using Document Embedding and Its Use in Job Recommendation(2020)

  • 論文の概要
    • 職務経歴書や求人からNLPでスキル表現を抽出
    • 暗黙的なスキル表現も抽出
    • 候補者と求人のマッチングのスコア(Affinity Score)を定義
    • 暗黙的スキル情報を使うとマッチングの性能がアップすることがわかった
    • テキストに対して3つの観点でスキル抽出
      ・NER(Named Entity Recognition(固有表現抽出))を適用→文書内のスキルの位置を見つける、カテゴライズを行う
      ・PoS(Part of Speech(品詞の付与))を適用→スキル表現かどうかは専門家によるアノテーションを実施
      ・色々集めた辞書にあるか計算→Wikipedia、Onet/Hopeというサイトなどで自動での拡張型スキル辞書用意
    • 暗黙的なスキル表現も抽出
      暗黙スキル(Web DevelopmentやHard Workingなどの表現)の考慮→分散表現の平均値で表現
    • スキルとの関連度スコアの定義
      職務経歴書や求人からのスキル抽出のためにスコアを用いる
    • 表現の除外
      関連度スコアに閾値を設けて下回ったらその表現を除外するなどを行う。
    • 関連度スコア(x)
      ・NERで抽出した表現の信頼度(S1)
      ・文法が、事前に定義したルールにどれだけ合っているか(S2)
      ・そのスキルがNERで得たスキルっぽいもの、ポテンシャルスキル、辞書スキルの3つの辞書にどれだけ合致したかの割合(S3)
      ・その単語とスキル辞書との分散表現における最大のcos類似度の値(S4)
      ・パラメータは経験則的に決める
      ・関連スコアは0~1の間をとる
    • アフィニティスコア
      ・候補者と求人のマッチの適切さの指標
      ・エッジウェイトの平均としている
    • エッジウェイト(Y)
      ・スキル間のcos類似度(E1)
      ・文書を通じたスキルの出現頻度の割合(E2)
      ・明示的なスキルがあるかどうか(E3)→暗黙だと減点
      ・ウェイト(ω)はそのマッチングが成功したかどうかで重み付ける。
      ・関連スコアは0~1の間をとる
  • 参考になるところ
    • 暗黙的なスキルに関して着目したところ
    • スコアリングのモデルを定め、手元にあるデータとWikipediaなどの集合知も使ってかなり慎重にスキル抽出を行っている
  • Machine Learned Resume-Job Matching Solution(2016)

    • 論文の概要
      • 深層学習(CNN)を用いて、リッチな特徴量を用意した上で、職務経歴書と求人のマッチング最適化を行っている
      • 職務経歴書の数は4.7万枚
      • KerasやScikit-learnで書かれており軽量
      • 浅いモデルとしてXGBoostを、深いモデルとしてCNNの両方を、最終的に多数決によるアンサンブルにすることで性能が向上
      • アンサンブル手法としてはバギングを行っている
      • リッチな特徴量
        ・Manual Feature(年齢とか性別とか)
        ・Cluster Feasture(クラスタリングしたもの、トピック(LDA))
        ・Semantic Feature(意味的な類似度)
      • モデルはGPUでもCPUでも動く
      • ソースコードはGitHubで公開されてる

    • 参考になるところ
      • CNNながらGPUなしでも計算できるようなモデルのアーキテクチャになっている
      • 特徴量はマニュアルで作成したものと自動で抽出したものの両方をうまく使う

    SKILL: A System for Skill Identification and Normalization(2015)

    • 論文の概要
      • 職務経歴書や求人に記されているスキルの抽出に関するNER(Named Entity Recognition:固有表現抽出)の研究
      • 老舗の求人サイトであるCareerBuilderの
        社員による研究
      • Wikipediaベースのカテゴリ分類、Google検索による集合知を利用してスキル表現の抽出をしている
      • Word2Vecを用いて、あるスキルと関連しているスキルをもとにタグ付をするアプローチが紹介されている
      • データ
        大量の職務経歴書と求人票
      • アプローチ
        ・スキル分類→wikipediaベースのAPI、国の定めた定義など
        ・スキル判定→単一語(そのまま利用)、複数語(Google検索結果から判断(集合知))
        ・スキルタグ付け→Word2Vecをユニグラムで学習し、関連度を計算しスキルのタグ付け
    • 参考になるところ
      • Google検索を使って得た集合知をスキルの分類に活かしている
      • Word2vecで計算した類似度をもとにスキルのタグ付けをしている

    Effectiveness of job title based embeddings on résumé to job ad recommendation(2021)

    • 論文の概要
      • 求人票の表現に対して、分散表現を用いた求人レコメンドの研究
      • 求人票全体を使うよりも、タイトル部分を使ったほうがレコメンドの性能が高くなった
        ・分散表現はWord2VecやDoc2Vecの学習済みや学習したものを色々試している
        ・Cold-Start問題に対しても分散表現は有効な手段であるとしている
    • 参考になるところ
      • 汚い本文のテキストを使うよりもタイトルを使ったほうが良いと言う、岡目八目というか、労せずに成果を出す作法を知れたという気持ち
      • 分散表現がCold-Start問題に対する有効な手段という可能性

    conSultantBERT: Fine-tuned Siamese Sentence-BERT for Matching Jobs and Job Seekers(2021)

    • 論文の概要
      • データは27万件で正例が12.6万件、負例が10.9万件で職務経歴書(ユニークだと15万件)と求人票(2.3万件)からなる。ラベルはキャリアアドバイザーが付けたもの。
      • 各センテンスに対してBERTを適用し、文書単位で平均を取るなどしている。文書は最初の512トークンを使うのがよいとしている。
      • BERTを分類問題・回帰問題についてそれぞれSiamese network(シャムネットワーク)を用いてファインチューニング
        ・出力層は求職者のレジュメと求人票のCOS類似度をマッチングスコアとしている
        ・埋め込み層のデータで教師あり学習をランダムフォレストで行い、COS類似度ではなくランダムフォレストの吐いたスコアを使って推論するバージョンも用意している
      • 回帰タスクでのファインチューニングが一番よかった
    • 参考になるところ
      • BERTをセンテンスに対して使って、平均値をとりそれを特徴量にしている
      • 全文を使わないほうが分類性能が良い可能性
      • 埋め込み層のデータで教師あり学習をして、それのスコアを用いるというアプローチ

    BLSTMを用いた文書分類でデザイナーズマンション予測に再挑戦

    はじめに

    仕事で深層学習を使うことはないのですが、扱える技術の幅を広げていきたいなと思い、BLSTMを用いた文書分類についてkerasでの簡単なサンプルコードをやってみようと思います。データは以前集めた、某不動産紹介サイトの賃貸マンションの設備に関する文書とそれがデザイナーズマンションかどうかのラベルを使います。そして文書内の単語からその文書がデザイナーズマンションかどうかを予測します。前回はAUCで83%だったので、それを超えれると良いですね。

    目次

    単純なRNNとは

    • モチベーション
      • フィードフォワード型のニューラルネットワークではうまく扱うことができない時系列データをうまく扱えるようにすること。
    •  特徴
      • 入力が互いに関係している(多層パーセプトロンの際は置かれていない仮定)
        • 直訳すると循環するニューラルネットワークとなる。
      • 最初の文の単語が2つ目、3つ目の単語に影響を与える可能性を考慮。
      具体的な関数の形としては、 $$ h_t = \tanh (W h_{t-1} + U x_t) \\\ y_t = softmax(V h_t) $$ で与えられる。
      \( h_t \)は隠れ層の状態を、\( x_t \)は入力変数を、\( y_t \)は出力ベクトルを表している。(他のアルファベットは重み行列)

    LSTMとは

    • モチベーション
      • 単純なRNNの勾配消失問題を解決するために提案された手法。
    • 特徴
      • 単純なRNNに置き換えることで性能が大幅に向上することも珍しくない。
        • 時系列データ、長い文章、録音データからなる長期的なパターンを取り出すことを得意としている手法。
      • 勾配消失問題に強い
      • Long Short-Term Memory:長短期記憶
        • 長期依存性を学習できるRNNの亜種。
        • 入力ゲート、忘却ゲート、出力ゲート、内部隠れ層という4つの層が相互に関わり合う。重要な入力を認識し、それを長期状態に格納すること、必要な限りで記憶を保持すること、必要なときに記憶を取り出すことを学習する。
          • 入力ゲート:内部隠れ層のどの部分を長期状態に加えるかを決める。
          • 忘却ゲート:長期状態のどの部分を消去するか決める。
          • 出力ゲート:各タイムステップで、長期状態のどの部分を読み出し、出力するかを決める。
    $$
    i = \sigma (W_i h_{t-1} + U_i x_t) \\
    f = \sigma (W_f h_{t-1} + U_f x_t) \\
    o = \sigma (W_o h_{t-1} + U_ox_t ) \\
    g = \tanh (W_g h_{t-1} + U_g x_t) \\
    c_t = (c_{t-1} \otimes f ) \otimes ( g \otimes i) \\
    h_t = \tanh (c_t) \otimes o
    $$ i:入力ゲート(input)
    f:忘却ゲート(forget)
    o:出力ゲート(output)
    \( \sigma \):シグモイド関数
    g:内部隠れ層状態
    \(c_t \):時刻tにおけるセル状態
    \(h_t \):時刻tにおける隠れ状態

    Bidirectional LSTMとは

    • モチベーション
      •  従来のRNNの制約を緩和するために導入。
    • 特徴
      • ある特定の時点で過去と将来の利用可能な入力情報を用いて訓練するネットワーク
        • 従来のRNNのニューロンをフォワード(未来)なものとバックワード(過去)なものとの2つに分ける。
          • 2つのLSTMを訓練している。
        • 全体のコンテキストを利用して推定することができるのが良いらしい。
          • 文章で言うと、文章の前(過去)と後(未来)を考慮して分類などを行うイメージ。
        • 人間もコンテキストを先読みしながら解釈することもある。
      • BLSTMは全てのシークエンスの予測問題において有効ではないが、適切に扱えるドメインで良い結果を残す。
      • 1997年とかなり歴史があるものらしい
    出典:Deep Dive into Bidirectional LSTM

    Bidirectional LSTMで文書分類

    今回は、BLSTMを使って、マンションの設備に関するテキスト情報から、そのマンションがデザイナーズマンションかどうかを予測します。全体のソースコードはGoogle Colabを御覧ください。

    データ

    データを確認してみると、
     
    今回のデータがテキストとラベルからなっていることがわかる。なお、データ数は1864件あり、そのうち16%がデザイナーズマンションです。

    前処理

    Google Colab上で形態素解析を行うために、MeCabをインストールする。
     
    これでMaCab NeologdをGoogle ColabのPythonで実行できます。
    テキストデータの前処理を行うために名詞だけを抽出してリスト形式で返す関数を定義します。
     
    ここで、語彙に関するデータを作成します。
     
    続いて、単語とそのインデックスからなるdict形式のデータを作成します。
     
    最後に、これまでに作成した語彙とそのインデックスのデータを用いて、実際に学習に使う入力データと出力データを作成します。
     

    実行

    先程作成したデータを訓練データとテストデータに分けます。
    ここで、AUCを計算するための関数を定義します。(まるまる拝借しました)
    続いて、kerasを用いて、ネットワークを構築し、コンパイルを行います。
      テストデータでの予測精度を確認します。
    実際のラベルとテキストデータを見比べてみます。
    LSTMの場合は、BLSTMに関する記述(1行)をネットワークから除外すれば良いので簡単に試すことができます。 せっかくなので比較してみたところ、AUCは0.841でした。BLSTMは0.844だったので、若干上回る程度のようです。 以前扱った記事ではデザイナーズマンション分類はAUCが0.83程度だったので、LSTMを使ったほうが精度が少しだけ高いようです。

    追記

    TensorBoardの検証のloglossを見てみます。 エポックに対してloglossが非常に荒れています。学習曲線としてはセオリー的にアウトなようです。一方で、検証のAUCはエポックに従って高まるようです。 AUCは良くなっているけどloglossが増え続けている。loglossが下がらないと過学習している可能性が高いので、これは過学習しているだけなのだろう。

    参考文献

     

    [R]ボージョレ・ヌーボーのコメントに対してLDATSパッケージを使って時系列トピックモデルを扱う

    はじめに

    先日、某勉強会でLTをしました。その際に10秒だけ紹介したRのパッケージについて記事を書いてみようと思います。

    LDATSパッケージについて

    時系列でのトピックモデルを推定することができるパッケージです。
    やっていることとしてはLDAでトピックを推定して次元を減らし、そのトピックの多変量時系列に関してベイズ手法による変化点検知のためのパラメータ推定を行っているようです。GitHubの該当しそうなソースコードに多変量のデータに対するsoftmax関数での回帰をやっているとの記述がある。(multinomial Bayesian Time Series analysis

    元となっている論文を見る限り、BoW(Bag of Words)を想定して作っておらず、20~30程度のグループからなるデータに対して適用するのがちょうど良いです。アクセスログのページカテゴリや、マーケティングの顧客セグメントであればそんなに数は多くないので扱いやすいと思います。

    データ

    Webサイトから集めてきたボージョレ・ヌーボーのキャッチコピー14年分を今回は扱います。実は販売店側のキャッチコピーとワイン委員会が決めた評価が存在します。私の知っている世界は販売店側のキャッチコピーだけでした。

    試してみた

    今回はとにかく動くことだけを考えて、汚いコードとなっております。やっていることとしては、キャッチコピーを販売側とワイン委員会側のものを一つにつないで、数字を正規表現で「数字」に変換し、RMeCabで形態素解析をし、LDATS向けの形式のデータを作成していきます。
    途中で、日本語の文字化け問題を回避するためにGoogle翻訳を使って単語名を置き換えています。
    1時系列につき1文書となるようにデータを作っていく必要があるのですが、今回はボージョレ・ヌーボーのキャッチコピーなので最初から1時系列につき1文書となっているため都合が良いです。
    データとソースコードはこちら

    こちらは論文の図と同じものだとドキュメントの説明にあったので、論文の説明を見る限り、表すものとしては以下のようです。

    • 一番上の積み上げグラフはトピックごとの単語の割合を表しています。
    • 二番目の折れ線グラフはLDAによって推定されたトピックの時系列推移です。
    • 三番目のヒストグラムは二番目の時系列における変化点を集計したものです。
    • 四番目の折れ線グラフはモデルが推定したトピック割合の変化点の前後での推移です。

    今回の図では文字が潰れていて見にくいですが、

    • トピック1はボキャブラリーが比較的リッチなコメント(「フルーティー」「フレグランス」「複雑」)
    • トピック2は数字を用いたコメント(「何年に一度の!」みたいな)
    • トピック3はボキャブラリーが貧相なコメント(「すごい!」みたいな)

    のようです。
    二番目の折れ線グラフを見る限り、周期的に数字を用いたコメントが現れているように思われます。四番目の折れ線グラフの変化点を見る限り、近年は数字を用いたコメントが相対的に減ってきて、リッチなボキャブラリーになってきているようです。

    おわりに

    時系列トピックモデルをカジュアルに試せる面白そうなパッケージだなと思い、LDATSパッケージを触ってみましたが、そもそもBoWなどを想定して作られているパッケージではないので、単語数が多いような分析ではそもそも可視化ができず使いにくいだろうなと思いました。マーケティングなどでユーザーのセグメントの推移を分析したい場合などにちょうど良いのだろうと思われます。

    参考情報

    [1] Long‐term community change through multiple rapid transitions in a desert rodent community
    [2] Latent Dirichlet Allocation coupled with Bayesian Time Series analyses
    [3] Package ‘LDATS’

    R advent calendar 2019 RSelenium、jpmesh、sfパッケージで東京23区の事故物件を分析してみよう!

    はじめに

    今回で3回目となるR advent Calendarですが、前回は「一発屋芸人の検索トレンドのデータ」を扱い、前々回は「ポケモンのデータ」を扱いました。今回は人の命に関わるようなデータを扱ってみたいと思い、某サイトから東京都の23区内における事故物件の住所と詳細を集めてきました。どのようなエリアが事故が起きやすいのかの分析を行います。(以下では、事故物件をAP(Accident Property)と呼びます。)

    分析工程

    ・データの収集
    ・データの整形
    ・可視化
    ・分析

    データの収集

    APに関する情報を某サイトより集める必要があります。そこで必要なライブラリとしては、RSeleniumやtwitteRがあげられます。
    twitteRが必要な理由は、APに関するサイトにAPの一覧ページがなく、公式アカウントがAPに関するページのリンクを投稿しているところにあります。ただ、私が以前使っていた頃とはTwitterAPIの仕様が変わり、3ヶ月よりも前の情報にアクセスできなくなっていました。そのため、今後のデータに関してはTwitterAPIでいいのですが、過去のものに関しては別アプローチが必要となります。
    また、APに関するサイトはJavaScriptで地図が表示されているだけなので、RSeleniumを使って地図をクリックさせ、表示された情報をスクレイピングするなどの処理が必要となります。
    当初の想定ではTwitterのデータ収集、リンク先の情報をRSeleniumでスクレイピングするだけの簡単な仕事だと思っていたのですが、過去のデータにアクセスできないので、地図上で一つ一つ見つけていくためにRSeleniumだけで頑張ることにしました。(私の過去のアドカレ史上、一番面倒なデータとなりました。)

    誰もやらないと思うのですが、一応手順を記しておきます。

    RSeleniumだけでMapからAPの情報を抽出するための手順
    1.都内の住所一覧を収集
    2.検索窓に住所を入力
    3.検索結果一覧の上位5件をクリック
    4.一度地図を引くことでAPを広い範囲で捉えれるようにする
    5.APのマークの要素を取得し、1件ずつクリックし、表示されたAPの情報をデータフレームに格納する

    こちらが取得できたデータです。

    RSeleniumでAPの
    ・住所
    ・発生時期(フリーテキスト)
    ・AP詳細
    ・投稿日
    を集めることができるので、その住所データに対して、Yahoo!のジオコードに関するAPIを利用します。(利用申請が必要なはずです。4年前くらいに申請していたのでそのまま使えました。)
    Yahoo!のAPIを使えば、住所から緯度経度の情報を取得することができます。

    APの緯度経度がわかれば、jpmeshパッケージを用いて1kmメッシュやら10kmメッシュやらのメッシュデータに変換することができます。
    jpmeshを用いてメッシュデータに変換し、メッシュ単位でAPの発生件数を集計します。

    データ収集用のソースコードは思いのほか長くなってしまったので、GitHubにあげておきました。
    https://github.com/KamonohashiPerry/r_advent_calendar_2019

    ここで再度、手順を整理しておきましょう。

    Twitterに出てきたものだけを取得(直近3ヶ月〜)する場合、
    run_tweet_collect.RでTweetを収集

    run_selenium.RでAPの情報をスクレイピング

    run_map_api.Rで住所から緯度経度の取得

    making_mesh_data_and_download_other_data.Rで緯度経度からメッシュデータへの変換、その他の人口データや地価データと接続をします。

    直近3ヶ月以前のものを取得する場合、
    run_selenium_from_map.Rで地図上から直接APの情報を取得する

    making_mesh_data_and_download_other_data.Rで緯度経度からメッシュデータへの変換、その他の人口データや地価データと接続をします。

    データの整形

    APの1kmメッシュデータを手に入れたら、kokudosuuchiパッケージを使って国土地理院の収集したデータをつなぎこみます。手順としては、以下のとおりです。

    まずは推計人口というそのエリアの人口の予測値です。今回は2010年のものを抽出しました。こちらは1kmメッシュのデータなので、変換することなく使えて都合が良いです。

    続いて、2015年の公示地価を抽出しました。

    こちらはメッシュデータではないので、緯度経度の情報から1kmメッシュのデータに変換する必要があります。後で行います。

    単純集計・可視化

    今回のデータセットのデータ数は3919件です。本当は7000件以上はあると思われますが、マップから取ってくるという勝手上、なかなか全てを取り切ることができませんでした。

    まずは、1kmメッシュごとのAP発生件数のヒストグラムです。

    1kmメッシュにおける人口のヒストグラムです。

    公示地価のヒストグラムです。

    1kmメッシュにおける人口あたりのAP件数のヒストグラムです。

    分析

    ここでは、色々な軸でAPのデータに向き合ってみようと思います。

    APの発生件数の集計

    人口が多いところがAPの発生件数が多いところだと思われますが、とりあえず確認します。

    世田谷区は最も人口が多いことから、AP発生件数では一番となっています。続いて、歌舞伎町などがある新宿が来ています。しかしながら、人口に占めるAP発生件数で言うと、港区がかなり高く出ているのがわかります。

    人口あたりのAP件数

    ここでは、メッシュデータをsfパッケージ用のオブジェクトに変換して、1kmにおける人口あたりのAP発生割合を可視化しています。

    こちらのmapviewパッケージで作ったマップはインタラクティブにいじることができます。ぜひ関心のあるエリアでいじってみてください。

    1kmメッシュ人口あたりのAP発生件数(×100)の可視化

    1kmメッシュでのAP発生件数の可視化

    比率ベースで、色の明るいメッシュのところを見ると、港区、中央区、新宿区、渋谷区などがAPが発生しやすいようです。件数ベースで言うと新宿が一番多いですね。
    一番色が明るい港区はてっきり六本木ではないかと思ったのですが、新橋から日比谷にかけたエリアでした。会社員による自○が多いようです。恐ろしいものです。

    APの名前の集計

    APの名前を集計してみます。これは別にこの名前だからAPになりやすいというわけではなく、単純に数が多いだけの可能性がありますし、実際にそうだろうと思われます。AP発生率を知るには、APではないものも含めた全物件名に占めるAP発生件名を手に入れないといけませんが、全物件名を収集するのが難しいことから単純に頻度の集計となります。今回は、wordcloud2パッケージを使って、ワードクラウドにしてみます。文字が大きいと頻度が高いものとなります。

    ハイツ、荘、コーポ、マンション、号棟、アパート、ハウスなどが多く出現しているようです。ただ、物件の名前としても頻度が高いとも考えられますね。

    地価と人口あたりのAP発生件数の関係

    ここでは地価のデータとAP発生の関係性について見てみます。

    地価の階級値(10個のパーセンタイルに分割)を横軸に、縦軸に人口あたりAP発生数をおくと、地価が上がるに従い人口あたりAP発生数が高まる傾向があります。これは、人口密度が高く地価の高いところではAPが発生しやすいということを示しているのではないでしょうか。人口密度が高いと地価があがる、人口密度が高いと治安が悪くなるという可能性が考えられます。

    APの詳細の集計

    ここではAPになってしまった詳細の内容について先ほどと同様に形態素解析を行いワードクラウドにしてみます。

    どうやら孤独死が多いようです。高齢者の人口構成比が関係しているのだろうと思われます。

    APの発生時期に関するテキストマイニング

    ここでは、発生時期に含まれる四桁の数字を集計して、何年くらいのAPが多いのかをざっくりと掴みます。

    どうやら昔のデータはあまり登録されていないようです。記憶が確かではないかもしれませんし、古すぎるものは消されているのかもしれませんね。あのサイトはユーザー生成コンテンツ(UGC)なので、投稿する人はそこまで昔のことをわざわざ投稿しないのかもしれないですね。

    APの詳細に関するテキストマイニング

    ここではトピック数10として、topicmodelsパッケージを使いLDAを行います。

    なかなか恐ろしいキーワードが多いですが、なんとなくですがうまく分類されているのではないかと思われます。

    トピック1は男性の不幸
    トピック2は不動産屋に言われた告知事項
    トピック3は孤独死
    トピック4は病死
    トピック5は火災・転落・事故
    トピック6は事故のあった建物に関する記載
    トピック7は腐乱した事例
    トピック8は建物に関して不明であることの記載
    トピック9は心理的瑕疵あり
    トピック10は自○

    となっているように思われます。まさかこのようなデータにトピックモデルを使うことになるとは。

    おわりに

    今回はR言語のみを用いて、APに関するデータを収集し、地図にプロットしたり他のメッシュデータとつなぎ合わせて分析をするなどしました。APが発生しやすいエリア、APと地価との関係、APのテキストマイニングなど興味深い結果が得られたと思います。
    一つ残念なのは、時系列情報がフリーテキストなので、APがどのエリアでどの頻度で発生していくのかの分析のコストが高く、今回は時系列情報を用いた分析にチャレンジできませんでした。
    今後はタクシーの需要推定の分析で行われているように、メッシュ単位でのAP発生確率の推定などを機械学習で行えると面白いなと思います。どなたか一緒にアノテーションしましょう!

    それでは、どうか良い年末をお過ごし下さい!
    メリークリスマス!

    参考情報

    数多くの方々の記事を見てどうにか仕上げることができました。感謝します。

    [1]【追記あり】sfパッケージでシェープファイルを読み込んでmapviewパッケージで可視化するまで
    [2]How to use mesh cord in R
    [3]Rを使ってワードクラウドを作ってみました
    [4]国土数値情報ダウンロードサービスWeb APIからデータを取得するためのRパッケージです
    [5]東京の地価公示データを眺める
    [6]Chapter 1 Introduction to spatial data in R
    [7][翻訳] RSelenium vignette: RSeleniumの基本
    [8]RからYahoo!のジオコーディングを利用する方法
    [9]EMBEDDING A LEAFLET MAP ON WORDPRESS
    [10]mapview advanced controls
    [11]RSeleniumでChromeからファイルをダウンロードするディレクトリを指定する方法
    [12]Selenium Serverが立ち上がらないときはportが被っているかも!?
    [13]brew install selenium-server-standalone
    [14]ナウでヤングなRの環境変数管理方法
    [15]タクシードライバー向け需要予測について
    [16]LDA with topicmodels package for R, how do I get the topic probability for each term?
    [17]dplyr — 高速data.frame処理

    [Python]機械学習などでテキストデータを特徴量にする際のソースコード集

    テキストデータの特徴量化について

    仕事ではテキストデータを多用するので、機械学習などで扱うためにテキストデータを特徴量にするためのアプローチを色々と整理してソースコードを残しておきたいと思います。今回はあくまでも私の知っているものだけなので、網羅性はないかもしれませんが悪しからず。
    (2019/08/18 追記)Stackingをカジュアルに行えるvecstackというモジュールを用いた予測も試してみました。下の方の追記をご覧ください。

    アプローチ

    テキストデータを特徴量にする際のアプローチとしては、以下の3つが良く使っているものとなります。
    ・単語ベース
    ・クラスタ、トピック、分散表現ベース
    ・文書間の類似度ベース

    今回扱うデータ

    ひょんなことから、昨年10月くらいに取りためたマンションの施設情報のテキストです。

    緑色が印象的な某不動産紹介サイトをクローリングしました。全部で1864件ほどの文書数となります。

    加えて、デザイナーズマンションかどうかのフラグを作成しました(17%くらいがデザイナーズマンションの割合)。これでもって、マンションの施設情報からデザイナーズマンションかどうかを分類できるかチャレンジしたいと思います。
    ここにデータを置いていますので、興味のある方はご利用ください。

    今回扱うモデル

    ランダムフォレストです。10foldsクロスバリデーションによるAUCの結果を各手法のスコアとして扱います。

    こちらは、任意の手法に関して10foldsクロスバリデーションで実行し、AUCのグラフを生成してくれるソースコードです。主にscikit-learnのサイトに載っているものです。引数のclassifierをsklearnの任意のモデルのインスタンスで渡せば動きます。

    単語ベース

    シンプルに単語をそのまま特徴量にするというものですが、文書によっては単語数が多すぎて収集がつかないと思います。そこで単語を簡単に選択できるDocumentFeatureSelectionというパッケージを利用します。

    このパッケージでは
    ・TF-IDFベースの特徴量選択
    ・PMI(Pointwise Mutual Information)ベースの特徴量選択
    ・SOA(Strength of association)ベースの特徴量選択
    ・BNS(Bi-Normal Separation)ベースの特徴量選択
    を行うことができます。

    まずは今回のベースラインとして、単語のカウントベースでの特徴量を扱いたいと思います。
    その前に、GitHubに上がっているデータに対して以下のように簡単な前処理をしておきます。

    ようやくベースラインの予測となります。以下のコードを実行すると、ROCが描かれた図がJupyter上で表示されます。

    AUC82%というのはベースラインとしてはなかなか強敵なのではないでしょうか。

    さて、本題の特徴量選択パッケージの適用をするためのソースコードを以下に記します。

    以上のソースコードを実行すれば、tf_idf_scored_df、pmi_scored_df、soa_scored_df、bns_scored_dfにスコアを付与された単語のリストが手に入ります。

    ここでは各スコアに関してアドホックに閾値を設けて、特徴量として利用することにします。

    TF-IDFベースの特徴量選択

    PMIベースの特徴量選択

    SOAベースの特徴量選択

    BNSベースの特徴量選択

    クラスタ、トピック、分散表現ベース

    続いて、k-meansやLDAやword2vecを用いて特徴量を作成する方法です。今回はk-means、ミニバッチk-means、LDA、FastTextによる分散表現を扱います。

    k-means、ミニバッチk-means

    LDA

    こちらはgensimでLDAを推定し、推定したトピックの割合をデータフレームで返すコードです。

    トピック数をとりあえず30個くらいに指定して推定したトピックの割合を特徴量として文書分類を行います。そのため、特徴量の数は30個になります。

    FastTextによる分散表現

    今回はデータ数が少ないことから、学習済みの分散表現を用います。日本語のコーパスに対して、FastTextで推定された分散表現となります。学習済み分散表現はこちらから拝借しました。

    分散表現は単語に対して計算されるので、単語に対して分散表現を足し合わせたものを特徴量として扱います。ここでは分散表現の合計値、平均値、TF-IDFで重みを付けた平均値の3つのパターンを試します。

    合計値ベース

    平均値ベース

    TF-IDFで単語を重みづけた平均値ベース

    文書間の類似度ベース

    今回は、デザイナーズマンションの定義文に似ているかどうかという観点で類似度ベースの特徴量を作ってみたいと思います。

    今回は変数が一つだけなので、機械学習はせず、デザイナーズマンション割合との関係を図示するにとどめておきます。横軸がデザイナーズマンションの定義と施設情報の類似度で、縦軸がデザイナーズマンション割合です。

    どうやら、途中でデザイナーズマンション割合がピークを迎えるようです。

    おわりに

    最先端の手法は調べれていないですが、テキストデータを特徴量に落とし込む手段を備忘録として残しておきました。今回あげた中では、SOAベースの特徴量選択のAUCが83%と一番高かったですが、ベースラインが82%と僅差でした。そして、分散表現形のものは80%に届いた程度です。余力があれば新しい特徴量の作り方が分かり次第アップデートしようと思います。

    追記

    “Automate Stacking In Python How to Boost Your Performance While Saving Time”という記事を見つけたので、紹介されているvecstackモジュールを使って今回のモデルに関して簡単にstackingしてみようと思います。
    コードに関しては、こちらのGitHubに上げています。試してみた所、AUCは88%になりました。結構上がりましたね。しかもコードはめちゃ短いので楽です。

    参考文献

    [1]Julian Avila et al(2019), 『Python機械学習ライブラリ scikit-learn活用レシピ80+』, impress top gear
    [2]Receiver Operating Characteristic (ROC) with cross validation
    [3]@Kensuke-Mitsuzawa(2016), “テキストデータで特徴量選択をする”, Qiita
    [4]JapaneseTokenizer 1.6
    [5]DocumentFeatureSelection 1.5
    [6]自然言語処理における自己相互情報量 (Pointwise Mutual Information, PMI)
    [7]【Techの道も一歩から】第3回「第11回テキストアナリティクス・シンポジウム」
    [8]文書分類タスクでよく利用されるfeature selection

    学習済み分散表現を用いた文書分類に挑戦(一部再学習も)

    はじめに

    2018年9月のテキストアナリティクスシンポジウムに行った際に、学習済みの分散表現で事前学習したモデルを使って分類してうまくいく事例が紹介されていました。
    全てのタスクにおいてうまくいくとは思えませんが、試すコストはあまりかからないので試してみます。

    2017年のテキストアナリティクスシンポジウムにおいても、メルカリやGunosyでは分散表現を用いた手法が一番精度が高いと言われていましたし、今年の会ではNLP系の学会でも分散表現はデファクトスタンダードになっているという話も伺いました。
    2013~14年はLDAを使った研究が多かった気がしますが、徐々にシフトしていっているんですね。

    これまで(Word2Vecを用いて蒙古タンメン中本の口コミ評価を予測してみる)は4000件程度の蒙古タンメン中本の口コミの情報を元に分散表現を手に入れていましたが、学習済みの分散表現を用いたアプローチも有効かもしれないと思い、試してみようと思います。

    分類タスク

    某グルメ口コミサイトの蒙古タンメン中本の口コミのテキストから、3.5点以上の評価かどうかを予測するタスクを扱います。
    本当は、ポケモン図鑑の説明文から水やら炎やらのタイプを予測するとかをしたいのですが、あいにく手元にデータがないので、以前集めた蒙古タンメン中本の口コミを使います。(実は後日、ポケモン図鑑のデータを集めたのですが、平仮名にまみれたデータな上に、データ数も800件しかなかったので、どのみち厳しかったです。)

    学習済み分散表現

    Word2Vecなどで大量の文書をもとに学習させた分散表現のことを指します。
    大規模コーパスで分散表現を手に入れる際は、数十GBにも相当するテキストデータを数時間かけて推定するので、学習済みのモデルは非常にありがたいです。(4年前に会社のPCで計算した際は、12時間くらいかかったこともありました。)

    無料で提供してくださっている分散表現については、すでにこちらのブログで紹介されています。そこで紹介されているものに少し付け足すと、日本語の分散表現に関しては以下のようなものがあります。

    • 白ヤギコーポレーションのモデル:Gensim
    • 東北大学 乾・岡崎研究室のモデル:Gensim
    • Facebookの学習済みFastTextモデル:Gensim
    • NWJC から取得した単語の分散表現データ (nwjc2vec):Gensim
    • NNLM embedding trained on Google News:TensorFlow

    そこで、今回は各種学習済み分散表現と蒙古タンメン中本コーパスで求めた分散表現の文書分類の性能バトルをしてみたいと思います。
    ただ、分散表現ではなく、単語の頻度をもとに特徴量を作ったものが一番精度が高いのですが、分散表現同士の比較でもってどの学習済み分散表現が中本の口コミ分類に役に立ちそうなのかを明らかにしようと思います。(本来は分析という観点から即でボツですが、見苦しくも比較していきます。)

    前処理

    前処理は以下の通りで、テキストデータを分かち書きして、数値や低頻度・高頻度語を除外しています。

    処理を施すとこのようなデータになります。

    特徴量は、scikit-learnのCountVectorizerやTfidfVectorizer、分散表現の合計・平均・TF-IDFを求めたものを用意します。

    蒙古タンメン中本の口コミ4000件から作成した分散表現:Gensim

    まず、以前のブログで紹介した蒙古タンメン中本の分散表現ですが、以下のように推定しています。

    Pipelineを用いてExtraTreesClassifierによる学習をします。特徴量は先程あげた、テキストベースのCountVectorizerやTfidfVectorizer、分散表現の合計・平均・TF-IDFで、評価指標はAUCのクロスバリデーションスコアとします。

    汗に関してコンテキストの似ている単語を抽出しています。

    結果は、以下の通りで、分散表現を使わない方がAUCが高いです。ただ、w2v_tfidf(分散表現のTF-IDFを特徴量にしたもの)が分散表現の中でAUCが高いようです。今回はこの60.5%をベースラインに比較していこうと思います。

    白ヤギコーポレーションのモデル:Gensim

    こちらのリンク、「word2vecの学習済み日本語モデルを公開します」から、ダウンロードしてそのまま以下のコードでモデルを扱えます。

    汗の関連語を抽出していますが、中国の歴史の何かですか?可汗とかいう単語は聞いたことあるかも。

    まずは白ヤギさんの分散表現をそのまま使って予測してみます。(コードは先程のものとほぼ重複するので省略しています。)
    残念ながら、ベースラインの60.5%には至りませんでした。

    hogehoge.modelというフルモデル形式の場合は、再学習が可能です。詳しくはこちら(models.word2vec – Word2vec embeddings model)に書かれています。

    今回は、白ヤギさんの分散表現に対して、追加で蒙古タンメン中本のテキストを食わせて再学習させます。

    ベースラインの60.5%よりも下回り、さきほどの白ヤギさんのもともとの分散表現よりも下回りました。

    再学習してもかえって精度が下がったりすることから、簡単に精度が出るわけではなさそうです。まぁ、理想はその適用領域での大量のテキストデータがあることで、Wikipediaを元に作成した分散表現に強く依存しても駄目なのだろうと思われます。

    東北大学 乾・岡崎研究室のモデル:Gensim

    日本語 Wikipedia エンティティベクトルからダウンロードした学習済み分散表現を用います。ダウンロード後は普通に「gzip -d file.txt.gz」みたいにターミナル上で解凍します。以下のコードを実行すればすぐに使うことができます。
    ただし、KeyedVectors形式のものは白ヤギさんのように再学習ができません。(Why use KeyedVectors instead of a full model?

    汗の類似語に関しては、難しい単語が高めに出ているようです。

    残念ながら、ベースラインの60.5%には至りませんでした。