u++の備忘録

「サブスクリプションミートアップ vol.1」に参加しました(全発表まとめ)

本日開催された「サブスクリプションミートアップ vol.1」に参加しました。サブスクリプション事業における知見共有を目的とした会で、中でもKPIの運用・計測などアナリスト的観点の話が中心でした。

subscription-meetup.connpass.com

開催趣旨は、当日も説明に使われた次のスライドにまとまっています。


残存率の推移シミュレーションによるLTV予測と費用対効果の可視化手法

五島 陽さん / 株式会社ドワンゴ

やりたいこと=LTVの予測

  • 投資判断基準の明確化のため
  • 意識している点
    • 回収期間を決める
    • LTVは一つではない(組み合わせの切り口次第)
  • 算出方法

費用対効果の可視化手法

  • 「期待売上」「ROAS(期待売上/費用)」の散布図
    • logを取ったり、偏差値に変換したり

所感

  • 可視化時に対数を取るのはよくやるが、偏差値を取るのはあまり発想になかった
  • 確かに一般に理解しやすい気がするので、機会があれば試してみたい

カスタマーサクセスの効果を可視化・定量化・KPI化した話

鎌田 真太郎さん / freee株式会社

やりたいこと/やったこと

  • (LTV = 新規獲得MRR × Lifetime)は指標として有名だが、施策のKPIとして「遠い」
    • 効果測定の一貫性が薄い
    • 1つの施策が全体に与える影響は多くない
  • 追いやすい中間指標を作った
    • (詳しくは内緒らしい)「よくないActivity Log / 経理労務などの作業アウトプット数」みたいな指標

反省とこれから

  • 失敗
    • 中間指標の中間指標が大量発生してしまった
    • SaaS Matricsとの距離感が微妙
  • 反省
    • 複雑にしすぎた
    • 「良くないものを減らす」は人間の感情的に良くないのかもしれない
      • 増やす方向の指標の方が楽しい

所感

  • 「KPIを新規作成する場合、減らすより増やす方向の指標の方が良さそう」という視点は、新鮮で面白かった

初心者アナリストのくせに継続率を爆上げした話

椋林 淳一さん / Retty株式会社

前提条件

  • Rettyのミッション:レストラン情報で、個別最適
  • サブスクリプションしてもらうのは、お店側
    • 顧客には無料で使ってもらう

やったこと

  • 継続率が上がっていない課題を発見
    • サービスへの満足
      • どれだけ送客できるか
      • お店が来店実感しているかどうか
  • 顧客との関係性強化
    • 介在価値(営業部門への関連)
  • セグメンテーションして、とにかく仮説を出す
    • A群:初回更新タイミングで更新している店舗
    • B群:初回更新タイミングで解約している店舗
  • プロダクトを触って、仮説を出す
  • とにかく仮説を洗い出して、良い結果が出るまで仮説検証

結論

  • "User Happy"なお店が良い
  • 駄目な例(どんなお店か分からない)
    • 写真がない
    • 推しポイントが分からない など

送客、継続インパクトが大きかったもの

  • ネット予約
    • 送客が増えるだけでなく、来店実感もしてもらえる
  • 来店したくなるコース
    • 魅力的なコース情報を充実させる

結果

  • 継続率成長率が160%に
  • プロダクト側とビジネス側の両方の課題を解決
    • プロダクト側:店舗情報が不十分だった
    • ビジネス側:継続率向上のための施策が不明瞭だった

所感

  • Rettyは次のブログでデータ分析組織の存在を認知していたが、より具体的な事例が聞けて良かった

engineer.retty.me

サブスクリプションはなぜ失敗するのか

重村 裕紀さん / 株式会社 FiNC Technologies

やったこと

  • FiNCプレミアムの退会率改善のグロースハック
  • ABテスト、分析ダッシュボード作りを繰り返す→3カ月後、全く解約率下がらず・・・
  • 社内・社外のPMに相談
  • 頂いたメッセージ「コア機能にフルスイングしろ」
    • いろいろな機能がそこそこ使われていたが、当時継続して利用されるコア機能が存在しなかった
    • FiNCオリジナル体組成計を無料で使えるようにし、コア機能として押し出し
  • トライアル期間の退会率が15%改善

サブスクリプション事業において重要なこと

  • 売上=会員数 × 月額料金
    • 月額料金は変えづらいので、会員数を増やすことが基本になる
  • 会員を3分類
    • ヘビー(ここを増やすのが重要)
    • ライト
    • 休眠
  • 新規開拓に頼らない戦略が取れると、マーケティング経費を抑えて投資に回すことができ、良いサイクルに突入していく
  • チャネルごとに「初回利用率」を上げることでヘビーユーザーを作ろうとしている

所感

  • 「コア機能を作り、ヘビーユーザーを積み上げ続ける」という端的なメッセージでマーケティングを推進しているのは分かりやすくて良かった

f:id:upura:20190515192730p:plain

「Machine learning graph pitch #1」に参加しました(全発表まとめ)

本日開催された「Machine learning graph pitch #1」に参加しました。機械学習の中でも、特にグラフ関連の技術を実務で使っている5人の方々のLTをお聞きできました。

machine-learning-pitch.connpass.com

Improving "People You May Know" on Directed Social Graph(Graph Embedding を用いた双方向つながり予測)

agata (@agatan_) さん


やりたいこと

  • Wantedly Peopleでのつながりを増やしたい
  • 「もしかして知り合いかも?」の表示
  • Link prediction: 繋がる可能性の予測
  • 初期は「同じ会社」「共通のつながり人数」などルールベースでやってきた
  • ルールで拾いきれないユーザにも推薦を出したい

Graph Embeddingを用いる利点

  • 複雑な関係を表現できる
    • エッジが2, 3億あるWantedlyのデータを1次元ベクトルとして扱えたら嬉しい
  • 推薦候補の検索が高速に表現できる
    • 全探索するとしてもない積を取るだけ
    • embeddingの近傍情報を使って推薦候補を絞る
  • (他のタスクへの応用)
    • 別タスクの入力として使う

技術

  • シンプルなdeep walkで試した
  • 双方向ではないエッジを捨てているので、次の情報が落ちる
    • つながりリクエストが拒否された情報
    • 「単方向のつながり」を経由して弱く繋がっているという情報
  • つながりの種類を表現することで、上記の情報も活用
    • "Complex Embedding" を参考に実現

結果

  • 「Top 50以内に繋がるユーザを推薦できる」ユーザ数が10.3%増加
  • もともと推薦を出せていたユーザに対する推薦の質はほぼ同等
  • 実運用上は、ルールベースは残しつつ、拾えないユーザにGraph Embeddingを用いた情報を出しているらしい

GANを用いたリンク予測におけるネガティブサンプルの生成

inuzuka (@studio_graph3) さん

  • クックパッドのリサーチエンジニア。アルバイトを経て、2019年に新卒入社。


技術

  • リンク予測
  • スコアリング関数の定め方が大事(資料 p. 17)
  • 2種類の方法
    • 類似度に基づいた手法
    • 類似度に基づかない手法
      • 分類モデルでリンクの存在を直接予測
      • 行列の因子分解を利用

やりたいこと

  • 知識グラフにおけるリンク予測
  • 訓練データに登場しないペアについてもリンクを予測する
  • 知識グラフを連続ベクトル特徴空間に
    • 知識グラフを計算可能に
    • 意味的な類似性を内包させたい

技術的難しさ

  • データセットには正例しか保持していない
  • 訓練するとき
    • 1: 訓練データから作られた正例
    • 0: 人手で作られた負例
  • マージンに基づいたランキングのlossを最小化
  • 負例は人手で作っているので、決定境界から遠い負例を生成しがち
    • 訓練に有効ではない
    • モデル収束の邪魔になる
  • → GANを基に、ネガティブサンプリング

提案手法の課題

  • モデルが大きくなる
    • GANを用いることで収束がやや困難に
    • 精度は出るが時間がかかる
  • 既存のネガティブサンプリング手法から得られた知見
    • 全エンティティと関係の組についてスコアを計算
  • (画像ではなく離散値を出力するので、Mode collapseの問題は検知しやすい)

DAGの埋め込み手法とdisk embedding

nunuki (@nunuki_) さん

  • LAPRAS (旧 scouty)のMLエンジニア。

ai-lab.lapras.com

技術

  • 自分でICML2019に通した論文を解説
  • Directed Acyclic Graph (DAGs) の Embedding
  • グラフの推移的な構造を保ったまま埋め込む手法
  • Upper Cone と Lower Cone という構造で、包含関係扱える
  • これまでは上位概念ほど小さくなっていくTreeのような構造を仮定してしまっていた

提案手法のContribution

  • DAGの既存の埋め込み手法を統一する "Disk Embedding" というフレームワークを導入
  • 全く新しいモデルである "Hyperbolic Disk Embedding" を提案

応用範囲

  • 例えば論文などは、親も子も豊富に繋がりがあるので DAGs が使えそう
  • Future Work になっている

vaaaaanquish (@vaaaaanquish) さん

  • エムスリー株式会社のMLエンジニア。MLエンジンの開発が主務。


やりたいこと

  • 医療関係者は時間がないので論文レコメンドしてあげたい
  • プロジェクトの制約
    • 興味を前提に医師が最も学べる論文を推薦したい
      • いろいろな問題を扱いたいが、エンジニア的には統一的な枠組みでできると嬉しい
    • 利用可能なデータとして「論文クリック」のような直接的な教師データがほぼない

Graph Embeddingを用いる利点

  • さまざまなタスクがあり、いろいろな評価関数があるので、多くの問題に適用できそう
  • Link Predictionタスクは、半教師あり学習の形でも解くことができる
  • (userとcontentsはpage viewで繋いでいるが、tag経由でも繋がっているのでcold start問題にも対応できる?)

おわりに

申込時に「最近読んだ面白かった機械学習関連の論文と理由」を聞いているだけあって、質疑応答の議論も多くかつ質が高く、よい勉強会でした。なお、申込時のアンケート結果は、事後のアンケート答えてくれた人には共有してくださるそうです。事前アンケートで参加者をフィルタリングして、事後アンケートに答えるモチベーションも作る良い方法だと感じました。

f:id:upura:20190513190548j:plain

「Sports Analyst Meetup #2」を開催しました #spoana

「Sports Analyst Meetup #2」を開催しました。実務でスポーツのデータ分析をしている方のロングトーク2本に加え、LTも10本と盛りだくさんの内容でした。

spoana.connpass.com

2月末に開催した初回に続く、第2回の開催です。

upura.hatenablog.com

発表資料はこちらにまとまっています(今後、資料が公開され次第追加していきます)。

spoana.connpass.com

Twitterの #spoana タグの付いた投稿は、下記にまとめています。

togetter.com

ロングトーク①「遺伝的アルゴリズムを用いたエンデュランス競技の最適ペース探索と、アスリートへの提示方法」

小山 浩之さん(合同会社アヘッドスポーツエンジニアリング

  • 自転車競技では長いレース中に常に全力を出しているわけではなく、ペース配分をしている
  • 「どこで休むか」が鍵となるが、全力で戦っている中で「休む」のは実は難しい
  • データドリブンで戦略的に休むための方策を考える
  • 現実世界でシミュレーションを実行しようとすると、可能性の組み合わせが発散する→遺伝的アルゴリズムを採用
  • とはいえ、現実世界を正確に表現できるとは限らないので「データにとらわれず」というメッセージが印象的だった

ロングトーク②「ブラインドサッカー日本代表チームのデータ分析サポートについて」

加藤 健太さん(データスタジアム株式会社)

  • 冒頭はブラインドサッカーの紹介
  • どう分析サポートしているか
    • スポーツの特性の把握
    • スモールスタート、適切な課題設定(監督と打ち合わせ)
    • 収集するデータの決定
    • データ収集方法の検討
  • ブラインドサッカーの場合、1日1試合というタイトなスケジュールでレポートを出している
  • 感覚的に大切なことは分かっている点について、実際に数字を取って分析KPIにしているのは日本くらいではないか?
  • 「Q. ブラインドサッカーにおける良い選手とは?」「A. 頭の中にコートのイメージを描けている選手」
  • 「Q. どうやって選手に分析結果を伝えている?」「A. 選手の背中に絵を書いて分析のイメージを伝えることも」

LT

実際の発表順にタイトル・競技名・発表者名を列挙させていただきます。
ご興味あるものがあれば、発表資料やtogetterにまとめたtwitterの投稿などをご参照ください。

「戦略シミュレーション分析」(競技:バドミントン)

funain さん

「大相撲優勝決定巴戦に見る不合理な分布」(競技:相撲)

tomi_ さん

「高校セーリング部のための「データ活用ツール」制作秘話」(競技:セーリング

fujit33 さん

「1年目のTリーグ」(競技:卓球)

e-toppo さん

富士登山競争を定量的に評価する」(競技:登山)

icebee__ さん

「サポーターの熱気を可視化する」(競技:サッカー)

Akira-Yama さん

「ボール保持力・奪取力マップから見るロシアW杯2018」(競技:サッカー)

saeeeeru さん

「ポジショナルプレーの研究がしたいです」(競技:サッカー)

gshirato さん

「バスケットボール, ハンドボール, ホッケー, バレーボール, 水球 リオオリンピックの球技の結果予測:統一予測モデルの構築について」

konakalab さん

「部活とSports Analysis」

tetsuroito さん

おわりに

本イベントを支えてくださった参加者・発表者・運営メンバーの皆さま、会場をご提供いただいたデータスタジアム株式会社の皆さまに、改めてお礼申し上げます。本当にありがとうございました。次回開催も検討しておりますので、ご参加やご発表など、どうぞよろしくお願いいたします。

f:id:upura:20190512222044p:plain

「diverta 2019 Programming Contest」をPythonで解く

A〜Dまで解きました。

atcoder.jp

A - Consecutive Integers(100点)

  • サンプルなど、実際に具体的な数字で考えると良い
N, K = list(map(int, input().split()))
print(N - K + 1)

B - RGB Boxes(200点)

  • r, gを固定したときにbが答えとなり得るか(割り切れる数字か)を考える
  • pythonでも時間的に危ないが通った(提出
R, G, B, N = list(map(int, input().split()))

cnt = 0
for r in range(N//R + 1):
    for g in range((N - r*R)//G + 1):
        if (N-r*R-g*G) % B == 0:
            cnt += 1

print(cnt)

f:id:upura:20190512015421p:plain

C - AB Substrings(400点)

  • まずは結合前の各文字列内の'AB'の数を数えておく
  • あとは結合でいくつ増やせるか
  • 解説の場合分けを考えれば良い
    • 'BCCCCA'が2つあっても、増やせる'AB'は2個ではなく1個
N = int(input())
S = [input() for i in range(N)]

cnt_ab_from_S = sum([s.count('AB') for s in S])
start_from_b_and_end_at_a = sum([(s[0] == 'B') and (s[-1] == 'A') for s in S])
not_start_from_b_and_end_at_a = sum([(s[0] != 'B') and (s[-1] == 'A') for s in S])
start_from_b_and_not_end_at_a = sum([(s[0] == 'B') and (s[-1] != 'A') for s in S])

ans = cnt_ab_from_S
if (start_from_b_and_end_at_a == 0):
    ans += min(not_start_from_b_and_end_at_a, start_from_b_and_not_end_at_a)
elif (not_start_from_b_and_end_at_a + start_from_b_and_not_end_at_a == 0):
    ans += (start_from_b_and_end_at_a - 1)
else:
    ans += (start_from_b_and_end_at_a + min(not_start_from_b_and_end_at_a, start_from_b_and_not_end_at_a))
print(ans)

D - DivRem Number(500点)

問題文の条件を数式で表すと、次のようになる。

(N - N%m)/m = N%m

ここで k = N%m と置いて整理すると、次の式を得る。

N = k(m + 1)

つまり、(m + 1) は N の約数である必要がある。ただしあくまで必要条件でしかないので、あとは候補の十分条件をそれぞれ確認する。

約数の候補を得るのは、次の実装などが高速。

qiita.com

N = int(input())


def make_divisors(n):
    divisors = []
    for i in range(1, int(n**0.5)+1):
        if n % i == 0:
            divisors.append(i)
            if i != n // i:
                divisors.append(n//i)

    # divisors.sort()
    return divisors


ans = 0
divisors = make_divisors(N)

for d in divisors:
    m = (d - 1)
    if m and (N//m == N % m):
        ans += m

print(ans)

Kaggle Kernel (Jupyter Notebook) でログを別ファイルに出力する

本記事では、Kaggle Kernel (Jupyter Notebook) でログを別ファイルに出力する方法を紹介します。

「Jupyter Notebook形式での出力が大量になると見づらいので、別ファイルに吐き出したい」などの需要を想定しています。

方法

loggerでファイルに書き込んでいく方法を採用します。loggerの流儀はいろいろあるかと思いますが、ここでは次の記事に倣います。

icebee.hatenablog.com

参考実装

Kaggle Kernelを例として作成しました。序盤でloggerを定義して getLogger() でファイルに書き込んでいきます。

www.kaggle.com

f:id:upura:20190505055042p:plain

書き込んだ情報は、Outputのタブで 001.log として保存されます。内容が短い場合はブラウザ上のプレビューで中身が確認できますし、長い場合はファイルをダウンロードして全文を確認することも可能です。

おわりに

本記事では、Kaggle Kernel (Jupyter Notebook) でログを別ファイルに出力する方法を紹介しました。

なお、本内容は「kaggler-ja slack」での発言内容を転記する形で作成しました。

yutori-datascience.hatenablog.com

コロケーションの指標「C-value」のPython実装

概要

  • コロケーション("Read a newspaper", "Write a blog" など、慣習的に用いられる英単語の組み合わせ)を抽出したい
  • 以下のブログで紹介されていたコロケーションの指標「C-value」をPythonで計算した

abicky.net

論文

Katerina T. Franzi and Sophia Ananiadou: Extracting Nested Collocations, In Proceedings of the 16th International Conference on Computational Linguistics (COLING ‘96), pp. 41-46, 1996.
https://www.aclweb.org/anthology/C96-1009

要旨

  • 「コロケーション度合い」の計算は、単純に単語列の出現頻度だけを見ても上手くいかない
  • 「C-value」という「コロケーション度合い」を尤もらしく計算する指標を提案
  • 具体例含めて、上述のブログで日本語で解説されている
  • ※ 1996年の論文

C-valueの算出アルゴリズム

f:id:upura:20190503153841p:plain

ここで revise は以下のように計算されます。

  • revise t(b): t(b) += (n(a) - t(a))
  • revise c(b): c(b) += 1

計算の具体例

与えられた文章群から、以下のような「コロケーションの候補」が得られたとします。コロケーションの候補は、単純にn-gramでの出現頻度の高いものを抽出します。次の表はたとえば、"Staff Reporter of The Wall Street Journal"という文字列が19回、"Wall Street Journal"が26回登場したことを意味します。

f:id:upura:20190503154330p:plain

ここで論文内のアルゴリズムに沿ってC-valueを算出すると、以下のようになりました。これは論文内で登場した結果とも一致しています。

f:id:upura:20190503154843p:plain

上位には、"Wall Street Journal", "The Wall Street Journal"など感覚的にコロケーションと判定できそうな文字列が来ています。一方で "of The Wall Street" など中途半端な単語で始まる文字列がC-valueは0(コロケーションではない)と判定されています。

実装

実装はNotebook形式でGitHub上に公開しています。

github.com

遺伝的アルゴリズムで平成から令和に変えてみる

遺伝的アルゴリズムを用いて 'HEISEI' という文字列を 'REIWA' に変えてみました。


開始時

f:id:upura:20190430210939p:plain

終了時

f:id:upura:20190430210938p:plain

ルール

  • 'HEISEI' という文字列から開始
  • 置き換えられる文字は、英大文字と半角スペースの27種類
  • 'REIWA '(末尾は半角スペース)になったら終了

遺伝的アルゴリズムの説明

次のブログの枠組みを流用しています。

upura.hatenablog.com

結果

今回の実験の場合、35世代目で 'REIWA ' を出力できました。1世代当たりのエージェント数を1000に設定しているので、合計で35000回の施行をしたことになります。

>>> 27*27*27*27*27*27
387420489

ナイーブに27通りの6文字を試すと4億通り弱のパターンがあるので、遺伝的アルゴリズムを用いることである程度効率よく探索できていると分かります。

おわりに

実装はGitHubで公開しています。

github.com