u++の備忘録

Machine Learning Casual Talks #8 に参加しました&全発表まとめ

はじめに

本日開催された「Machine Learning Casual Talks #8」に参加しました。今回は「Blog枠」での参加です。

機械学習を用いたシステムを実運用している話を中心に、実践的な機械学習に関して気軽に話せる会を開催します。 実際に運用していく上での工夫や、知見を共有していきましょう!

mlct.connpass.com

以前から行きたいとは思っていたのですが上手く日程が合わず、今回が初参加です。登壇者の方は以前にもお会いしたことがある方が多く、興味津々で臨みました。

Youtube配信は、こちらです。

youtu.be

Machine Learning Casual Talksをはじめた理由

@chezou さん

発表資料

docs.google.com

「エムスリーにおける機械学習活用事例と開発の効率化」

@m_nishiba さん、エムスリー株式会社 機械学習エンジニア

発表資料


概要

  • 小規模な「AI・機械学習チーム」で、機械学習プロジェクトを推進していくやり方について、エムスリーの事例やTipsを紹介
  • 機械学習を生業にする人としてのキャリア論も展開
    • 「出世したいなら上司を出世させる」というメッセージが印象的だった
    • ビジネス貢献・組織貢献・技術貢献のどこに焦点を合わせていくか

所感

  • 発表では割愛された部分も多いほど、情報量が多い素晴らしい資料
  • 機械学習チームにも利益目標を設定」辺りの部分は、ちょうど昨日に書評を書きながら色々考えた話だったので、特に興味深く話を聞いた

upura.hatenablog.com

「BEDOREにおける対話エンジンの開発と運用」

@naohachi89 さん、株式会社BEDORE 機械学習エンジニア

概要

  • BtoB領域で提供している機械学習プロダクトを提供していく中で遭遇した課題と解決策を紹介
  • 特に自然言語処理に関する案件を担当(対話エージェントなど)
  • 課題1: モデルの予測性能がある日突然急激に低下
  • 対策1: スナップショットの保存、分析用のログ
  • 課題2: 互換性の欠如によるエラーの発生
  • 対策2: バージョン管理、CIによる後方互換性の担保
  • 課題3: デプロイに時間がかかる
  • 対策3: 学習済モデルをS3からではなく、Flastic File Systemから読み込んでしまう
    • 現在調整中で、確率的にマウントに失敗する問題がある
  • 課題4: 学習に異常に時間がかかる
  • 対策4: コストに対してパフォーマンスの見合わない施策を削除する
    • BtoBでは削除に労力を要するので、事前の検討が大事

所感

  • 機械学習ドリブンなプロダクト」(機械学習モデルを必須としたプロダクト)を展開しているBEDOREならではの、実用的な知見が詰まった発表だった

LT「bfloat16について」

@roishi2j2 さん

発表資料

docs.google.com

概要

  • bfloat16とは?
  • TensorFlow(に限らず深層学習)で使うデータ型
    • GPUが扱える (高速化のために必須)
    • サイズが小さい (高速化のために必須)
    • 精度は十分

cloud.google.com

所感

  • 存在自体を知らなかった
  • この辺りきちんと勉強していきたい

LT「強化学習を可視化する。chainerrl-visualizerを動かしてみた」

@mogamin さん

概要

  • 強化学習のつらい所
    • 報酬設計が難しい
    • マルチワーカーが苦手
    • シミュレータの開発コストが高い
    • 方策設計はどうあるべきか
    • アルゴリズム部、Deep Q-Network部の試行錯誤
  • chainerrl-visualizer
    • STEP時系列なOUTPUT層や、各STEPでの取るべきアクションなどを可視化してくれる
    • git cloneしてexampleを手軽に実行できる

所感

  • 一度手元で動かしてみたい

LT「分散深層学習のチューニングや辛い話」

@vaaaaanquish さん

発表資料


概要

  • Yahoo!スパコンKukaiを利用したEコマースの偽物検知モデル
  • 課題
    • Eコマースは最終的には人間がBANするが、人手には限界があるので精度が重要
    • 違反者が少ない不均衡データ(Samplerが重要)
    • 常に新しい手法が出続ける(Overfitに注意、Samplerが重要)
    • サービスの文化や変動の課題(Samplerが重要)
  • 深層学習に対する工夫
    • 学習の進み具合に応じて正例:負例の割合を調整(最初は1:1で徐々に実情に合わせていく)
    • 機械学習向けにカテゴリを再編
    • カテゴリ情報をEmbeddingレイヤーで表現
  • 分散深層学習に対する工夫
    • 特定ノードでCVを出し、学習途中で精度の低いカテゴリを特定しSamplerを調整
    • 画像同士のphashを計算し同一画像をなるべく使わないようにする
    • 複数のGPUを利用したパラメータ探索
  • 「分散深層学習は機械学習モデリングとソフトウェアエンジニアリングの総合格闘技

所感

  • 分散深層学習を用いたプロダクトの細かな知見が詰め込まれた発表だった

パネルディスカッション

@m_nishiba さん、@naohachi89 さん、@hurutoriya さんの3人によるパネルディスカッションでした。

ここでは質問内容だけ列挙しておきます。気になる質問があれば、Youtube配信をご覧ください。

  • プロジェクトの取捨選択をできる人はどれだけいますか?
  • 売上を目標に設定すると、KPIの奪い合いが起きるのでは?
  • FAQ分類タスクはマルチクラスだと大変なのでは二値分類を重ねるほうが良い?(@naohachi89 さん)
  • 精度とスピード(計算量)のトレードオフはどう対応している?
  • AWS/GCP/オンプレなどの選択は?
  • データ分析はどれくらいやっている?(@m_nishiba さん)
  • 精度の閾値はどうやって決めている?
  • 機械学習を使わない解決策はチームの評価に含まれるのか?
  • 機械学習未経験でも採用される可能性があるか?
  • 全てのMLフレームワークに精通しているか?
  • 機械学習チームのデータエンジニアやアプリケーションエンジニアは、常に仕事がある状態になっているのか?
  • イノベーションのジレンマに陥ることは?
  • スマホ監視をどう実現したか?(ゲスト:@vaaaaanquish さん)

おわりに

本記事では、ブログ枠で参加した「Machine Learning Casual Talks #8」について、全発表の概要や感想などをまとめました。所感などは、帰宅後に追記したいと思います。

おまけ

エムスリーの@m_nishiba さんからノベルティを頂きました。今回のイベントで初配布とのことです。ありがとうございます!

AtCoder「全国統一プログラミング王決定戦予選」をPythonで解く

atcoder.jp

C問題まで普通に解ける問題だったのに、妙な力みから些細なミスでWAを連発しました。猛省せねばです・・・。

f:id:upura:20190127230034p:plain

A - Subscribers(100点)

  • 最大値は、片方が片方を包含した場合なので、AとBの小さい方
  • 最小値は、両者を完全に分離した場合なので0か、Nの制約でできない場合は(A + B - N)
N, A, B = list(map(int, input().split()))

ans1 = min(A, B)
ans2 = max((A + B - N), 0)

print(str(ans1) + ' ' + str(ans2))

[追記]

[追記終わり]

B - Touitsu(200点)

  • 左から1文字ずつ、A・B・Cを比較していく
  • 変える必要がある文字数は
    • 現状がA・B・Cで全て違う場合は2
    • 何か一つだけ違う場合は1
    • 全て同じ場合は0
  • 綺麗に書ける気もしたが、クソみたいな条件分岐を書いた

[追記]

setで重複していない数を数えれば条件分岐は不要だった

[追記終わり]

N = int(input())
A = input()
B = input()
C = input()

ans = 0

for i in range(N):
    if A[i] == B[i]:
        if B[i] == C[i]:
            ans += 0
        else:
            ans += 1
    else:
        if B[i] == C[i]:
            ans += 1
        else:
            if C[i] != A[i]:
                ans += 2
            else:
                ans += 1

print(ans)

C - Different Strokes(400点)

  •  A_i + B_iがi番目の皿の価値
  • それぞれ価値が高い順に取っていく
import numpy as np
N = int(input())

A = []
B = []
val = []

for i in range(N):
    a, b = list(map(int, input().split()))
    A.append(a)
    B.append(b)
    val.append(a + b)

A = np.array(A)
B = np.array(B)
val = np.array(val)

sort_index = val.argsort()

A = (A[sort_index][::-1])
B = (B[sort_index][::-1])

taka = (A[0::2])
aoki = (B[1::2])

print(sum(taka) - sum(aoki))

参考

今回のコンテストは、下記の高橋社長のブログで言及されています。

chokudai.hatenablog.com

【書籍メモ】「ビジネスでインパクトが出せるデータサイエンティストになるためには」

ビジネスでインパクトが出せるデータサイエンティストになるためには

  • 岩永二郎, Retty株式会社
  • 公益社団法人日本経営工学会『経営システム』Vol.28, No.2 (2019), pp.127-132

はじめに

「ビジネスでインパクトが出せるデータサイエンティストになるためには」という解説文を読んだので、感想をつらつらと述べます。


感想

文章構成は、以下の通りです。以下、必要に応じて文章を引用し、章ごとに感想をまとめます。

  1. はじめに
  2. データサイエンティストが力を発揮する場
  3. 課題設定
  4. 解決方法の設計
  5. 検証
  6. 育成
  7. まとめ

はじめに

冒頭から本稿の立ち位置を明示しており、非常に読みやすい文章でした。

「データサイエンティスト」は一種の「バズワード」であると言及し、その上で本稿で議論する内容を次のように明示しています。

本稿ではビジネス上でサイエンスを実践し,ビジネスにインパクトを与える方法について議論する.特にデータサイエンティストの一連の業務を通してサイエンスの素養が必要であることに焦点をあてる.

データサイエンティストが力を発揮する場

データサイエンティストが活躍するための前提条件として、次のような内容に触れています。

  • 事業ドメインやサービスの規模
  • データの質と量

これらは私が現職(事業会社のデータアナリスト)で働いている理由とも適合しており、全面的に同意でした。

前者について言えば、下記のTJOさんのブログでも言及されている通り、私はデータサイエンスが生み出せる効果はあくまで既存事業の掛け算に過ぎないと考えています。その率は大きくてもせいぜい数%が現実的という認識です。

tjo.hatenablog.com

掛けられる率がある程度固定ならば、元々の事業ドメインやサービス規模の大きさ自体が重要になってきます。

難しいのは、事業ドメインやサービス規模が大きい会社は一般に「古臭い」会社だということです。データサイエンティストが活躍する土壌はあるものの、風土が整っていない場合も、現状少なくありません。

しかし逆に言えばデータサイエンスへの理解などの風土さえ整っていれば、事業ドメインやサービス規模が大きい会社の方が、データサイエンスがビジネス貢献できるインパクトが大きいと常々考えています。

後者について、データの質と量は言わずもがなの話だと思います。この部分が整っていないと、どれだけ優秀な人材でも成果は発揮できないでしょう。

本稿の著者が「日本最大級の実名型グルメサービスRetty」に勤めているということで、データの質の文脈で「実名/匿名」などの要素に言及している点は興味深かったです。

課題設定

本章の冒頭の文にも、大きく同意しました。

データサイエンティストの仕事の肝となるのは適切な課題を設定することである

先日私がとあるイベントで発表したスライドを引用しますが、ビジネスのデータ分析の文脈では「解決すべき課題を特定し仮説を立てる」ことに価値があると考えています。

必要最低限の要素に注力する点や、大学でのアカデミックな研究の経験との連関など、自身の経験と照らしわせて腑に落ちる話が多かったです。

解決方法の設計

本章では「様々な知識と技術を駆使して適切に課題を解決する」という至極まっとうな主張が述べられていますが、個人的に最も印象的だったのは次の文です。

もう一つ付け加えるならば解決方法にちょっとした色気をもたせることである

自身の経験から言うと「様々な知識と技術を駆使して適切に課題を解決する」を徹底すると、データサイエンス専門企業でも無い限り、単純なクロス集計レベルの分析で十分な場合も多いです。

しかし、データサイエンティスト(になりたい人)の一般的な習性として「新しい手法を使いたい」「技術的な課題に挑みたい」というものがあり、ビジネスでデータサイエンスを活用する上でのミスマッチの一要因だと思っています。

「解決方法の設計」と題された章で、このような部分に言及があるのは、一定規模以上の実務経験の賜物だと感じました。

検証

まず、「データサイエンティストの一連の業務」として「検証」が一つの章立てで存在しているのが素晴らしいです。

文中で次のように述べられている通り、検証はビジネス上の「短期的な」利益を考慮すると、優先順位が低くなりがちな業務です。

事業会社でスピードを重視していると施策の検証を手薄にすることがちらほら見受けられる

とはいえ、検証は「長期的な」視点で、今後の施策の成功率や質を上げていく重要な価値のある行為だと考えています。

昨日私は、とあるデータ分析のコンテスト*1に出場しました。

このコンテストでは序盤・中盤と、他者を突き放す精度を出すモデルを実装しました。何度も「これで優勝できるかな」と考えていたのですが、最後に別の参加者に抜かれ、惜しくも2位という結果でした。

本章にも著者の経験で似たような事例が書かれているのですが、データサイエンティストは常に謙虚に自分を見つめ直す必要があるなと改めて実感しています。

育成

著者はここまで一貫して「サイエンスの素養が必要」と主張しており、本章でも次のような記述があります。

大学で十分に研究能力を身につけていればよいが,そのようなデータサイエンティストはビジネスの現場では一握りである.是非とも大学でサイエンスの素養を身につける教育を施していただきたい.

私の経験談としても、「課題設定」の章で述べた「解決すべき課題を特定し仮説を立てる」能力は、大学時代の研究室で鍛えられたと思っています。

本章の最後では、レビューの大切さについても触れられています。

私が定期的にブログを書く理由の一つも、いろんな方から感想や意見をもらい新たな知見が得られるからです。ビジネスロジックや顧客情報として問題ない範囲については、積極的にオープンな場で公開していくのが良いと思っています。

まとめ

本章では、本稿の内容を簡潔にまとめています。

本稿は、「はじめに」「まとめ」で論旨が明瞭に示されています。著者自身にアカデミック・ライティングの素養が備わっていると分かる文章で、本稿の説得力をより一層高める内容になっていました。

おわりに

本記事では、解説文「ビジネスでインパクトが出せるデータサイエンティストになるためには」について章ごとに感想をまとめました。全面的に私の思考と重なる部分が明文化されており、同意ばかりの感想になってしまいました。

本記事を執筆する中で、自分の中での思考を整理するきっかけにもなりました。ご恵贈いただいた岩永さんに、改めてお礼申し上げます。

「カンマ区切りで複数要素が入った列」をpandasで集計

本記事では、「カンマ区切りで複数要素が入った列」をpandasで集計する方法を紹介します。

「カンマ区切りで複数要素が入った列」とは、下記のような状況です。例えばアンケートの複数選択可の回答などで、行ごとに含まれる要素数も異なる場合が考えられます。

import pandas as pd
df = pd.DataFrame({'s': ['X,Y,Z', 'X', 'X,Y', 'X,Y,Z']},
                  index=['a', 'b', 'c', 'd'])
df

f:id:upura:20190123233331p:plain

文字列のまま部分一致で処理する方法もありますが、集計が目的の場合は、各行の要素をまとめて一つのリストに格納してしまうのが手っ取り早いと思います。

まずは各行の要素をリストに変換します。

df['l'] = df['s'].str.split(',')
df

f:id:upura:20190123233938p:plain

そして、sum()関数で足し合わせます。

ans = sum(df['l'], [])
ans
['X', 'Y', 'Z', 'X', 'X', 'Y', 'X', 'Y', 'Z']

あまり馴染みがないかもしれませんが、sum()の第2引数は、足し合わせの初期値です。指定がない場合は0なので、普段はあまり意識しない引数です。

今回の場合は、初期値の空のリストに、各要素のリストを次々と足し合わせる処理となります。最終的には、全ての要素が格納された一つのリストが得られるといった具合です。

こうなってしまえば、あとはollections.Counter()を使って簡単に集計が可能です。

import collections
c = collections.Counter(ans)
c
Counter({'X': 4, 'Y': 3, 'Z': 2})

短いですが、本記事では「カンマ区切りで複数要素が入った列」をpandasで集計する方法を紹介しました。

実装はGitHubで公開しました。

github.com

多クラス分類の不均衡データのdownsampling

問題設計

例えば4クラス分類で、比率が[0.5, 0.25, 0.15, 0.10]のような問題を考えます。

from sklearn.datasets import make_classification

args = {
    'n_samples': 100000,
    'n_features': 10,
    'n_informative': 3,
    'n_redundant': 0,
    'n_repeated': 0,
    'n_classes': 4,
    'n_clusters_per_class': 1,
    'weights': [0.5, 0.25, 0.15, 0.10],
    'random_state': 42,
}

X, y = make_classification(**args)

c = collections.Counter(list(y))
print(c)
Counter({0: 49727, 1: 25021, 2: 15094, 3: 10158})

RandomUnderSampler

このような多クラス分類の不均衡データをdownsamplingする場合、下記の記事で2値分類のdownsamplingに使った「imblearn.under_samplingのRandomUnderSampler」が、同様に利用できます。

upura.hatenablog.com

sampler = RandomUnderSampler(random_state=42)
# downsampling
X_resampled, y_resampled = sampler.fit_resample(X, y)
collections.Counter(list(y_resampled))
Counter({0: 10158, 1: 10158, 2: 10158, 3: 10158})

数が最も少ないラベル3の10158に揃えて、その他のラベルのデータがdownsamplingされています。

make_imbalance

より細かい設定でdownsamplingしたい場合は「imblearn.datasetsのmake_imbalance」が利用できます。

imbalanced-learn.readthedocs.io

downsamplingの方針を辞書型で定義することで、方針通りにデータを抽出できます。意図的に不均衡にも抽出可能です。

from imblearn.datasets import make_imbalance

key = [0, 1, 2, 3]
val = [500, 1000, 1500, 2000]
strategy = dict(zip(key, val))


X_r, y_r = make_imbalance(X, y,
                      sampling_strategy=strategy,
                      random_state=123)

collections.Counter(list(y_r))
Counter({0: 500, 1: 1000, 2: 1500, 3: 2000})

おわりに

本記事では、多クラス分類の不均衡データを題材に、pythonでのdownsamplingの方法を2つ紹介しました。

実装はGitHubで公開しました。

github.com

Data Driven Developer Meetup #4 で登壇しました&全発表まとめ

はじめに

Data Driven Developer Meetup #4 のメインセッションで登壇しました。

d3m.connpass.com

スポンサーセッション

機械学習を使ったサポート問い合わせ予測』

by freee 株式会社 Asaki Iwata 様 (@AsakiIwata)

  • なぜ問い合わせの予測が必要か
    • サポートの人員計画を立てたい
  • どうやって?
    • 予測の許容範囲は10%程度
    • 法人と個人でトレンドが異なる
    • 個人は確定申告の時期に集中する
    • 法人は繁忙時期がバラバラ
      • 今回は法人に絞る
    • 月次予測→日次予測→サポートチームに数字を報告
    • Gradient Boosting, XGBoost
    • MSEで評価
  • これからどうするのか
    • 上振れと下振れで別の重み付けのロジック

メインセッション

機械学習を⽤いた⽇経電⼦版Proのユーザ分析』


『医療用語に注目した文書の類似度計算』

by エムスリー 株式会社 nishiba 様 (@m_nishiba)

  • 資料が公開済だったのでメモは取りませんでした


LT

『Bokehではじめるデータビジュアライゼーション』

by YukiyoshiSato 様 (@YukiyoshiSato)

github.com

『SlackへのKPI通知botを作ったらいろいろ捗った話』

by Yagi 様 (@yaginuuun)

  • 資料が公開済だったのでメモは取りませんでした

qiita.com

おわりに

無事に終わって良かったです。質問も盛り上がって、良い勉強会だと思いました。改めて、運営さんと会場提供のfreee株式会社さんにお礼申し上げます。

ハッシュタグ #d3m の内容は、下記にまとめています。

togetter.com

Fizz Buzz in PyTorch

In this article, I utilize MLP(Multilayer perceptron) by PyTorch to solve Fizz Buzz problem.

This challenge is highly inspired by the following page, and I use the same situation.

joelgrus.com

You can see the Japanese version here:

upura.hatenablog.com

Train data & Test data

  • Train data: FizzBuzz data of N = 101~10000
  • Test data: FizzBuzz data of N = 1~100

Format of X, y

X are binary encoded N.

def binary_encode(i, num_digits):
    return np.array([i >> d & 1 for d in range(num_digits)])
for i in range(1, 10):
    print(binary_encode(i, 10))
[1 0 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0 0]
[1 1 0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0]
[1 0 1 0 0 0 0 0 0 0]
[0 1 1 0 0 0 0 0 0 0]
[1 1 1 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0 0]
[1 0 0 1 0 0 0 0 0 0]

y are {0, 1, 2, 3}. it means that this is a classification task for 4 values.

def fizz_buzz_encode(i):
    if   i % 15 == 0: return 3
    elif i % 5  == 0: return 2
    elif i % 3  == 0: return 1
    else: return 0

def fizz_buzz(i, prediction):
    return [str(i), "fizz", "buzz", "fizzbuzz"][prediction]

These X and y are used for training.

Prepare for Valid data

library

import torch
from torch import nn, optim
from torch.utils.data import (Dataset, 
                              DataLoader,
                              TensorDataset)
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'IPAPGothic'
%matplotlib inline

torch.manual_seed(1)    # reproducible
from fastprogress import master_bar, progress_bar

Depart Valid data

Train data are consist of 923 data, and first 100 are departed as Valid data.

NUM_DIGITS = 10
NUM_HIDDEN = 100
BATCH_SIZE = 128
X = np.array([binary_encode(i, NUM_DIGITS) for i in range(101, 2 ** NUM_DIGITS)])
y = np.array([fizz_buzz_encode(i) for i in range(101, 2 ** NUM_DIGITS)])

X_train = X[100:]
y_train = y[100:]
X_valid = X[:100]
y_valid = y[:100]

Convert to torch

X_train = torch.tensor(X_train, dtype=torch.float32)
X_valid = torch.tensor(X_valid, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = torch.tensor(y_valid, dtype=torch.int64)

Model definition

net = nn.Sequential(
    nn.Linear(10, 100),
    nn.ReLU(),
    nn.BatchNorm1d(100),
    nn.Linear(100, 4),
)

loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr = 0.05)

ds = TensorDataset(X_train, y_train)
loader = DataLoader(ds, batch_size=32, shuffle=True)

Training

train_losses = []
test_losses = []

for _ in progress_bar(range(600)):
    running_loss = 0.0

    net.train()
    
    for i, (xx, yy) in enumerate(loader):
        y_pred = net(xx)
        loss = loss_fn(y_pred, yy)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        running_loss += loss.item()

    train_losses.append(running_loss / i)
    
    net.eval()
    y_pred = net(X_valid)
    test_loss = loss_fn(y_pred, y_valid)
    test_losses.append(test_loss.item())

f:id:upura:20190121201207p:plain

Apply to Test data

numbers = np.arange(1, 101)
X_test = np.array([binary_encode(i, NUM_DIGITS) for i in range(1, 101)])
X_test = torch.tensor(X_test, dtype=torch.float32)

net.eval()
_, y_pred = torch.max(net(X_test), 1)

output = np.vectorize(fizz_buzz)(numbers, y_pred)
print(output)

f:id:upura:20190121201143p:plain

Now, we can get "successful" result. Accuracy is 95%.

from sklearn.metrics import accuracy_score

y_true = np.array([fizz_buzz_encode(i) for i in range(1, 101)])
print(accuracy_score(y_true, y_pred))
0.94999999999999996

The implementation can be seen in my GitHub:

github.com