For Your ISHIO Blog

データ分析や機械学習やスクラムや組織とか、色々つぶやくブログです。

自然言語処理ライブラリspaCyを試してみた。

お恥ずかしながら、spaCyを最近知りましたので、試してみました。 spaCyは、pythonで動かす自然言語処理ライブラリです。

spacy.io

目次

spaCyとは

spaCyは、pythonで動かす自然言語処理ライブラリです。特徴は、 事前に訓練された統計モデルと単語ベクトルが付属している点です。現在33言語をサポート、8言語に対する13個の統計モデルを利用できる。TensorFlow、PyTorch、scikit-learn、Gensim、その他のPythonのAIエコシステムとシームレスに相互運用可能。現在のバージョンはv2.x。

環境

  • Python3系
  • モデルがライブラリとしてインストール可能。pipでインストール。
pip install -U spacy

対応言語・モデル

複数の言語に対応していますが、今回は英語を扱います。英語には、4つほど学習済みの畳み込みニューラルネットワークモデル(CNN)があるようです。

モデルのインポート

en_core_web_smモデルが推奨されているようなので、デフォルトのまま利用します。

import spacy

# 英語のtokenizer、tagger、parser、NER、word vectorsをインポート
nlp = spacy.load('en_core_web_sm')

テキストのインポートとトークン化

# サンプルテキストに対する固有表現とエンティティタイプの抽出
text = u'My name is Ishio from Japan. Today, talk football!!'
doc = nlp(text)
token = doc[3] # Ishio
print([d for d in doc])
print(token)

出力結果。

[My, name, is, Ishio, from, Japan, ., Today, ,, love, football, !, !]
Ishio

品詞タグ付け

text = u'My name is Ishio from Japan. Today, talk football!!'
doc = nlp(text)
for d in doc:
    print((d.text, d.pos_, d.dep_))

出力結果。固有名詞はPROPNとなっています。

('My', 'ADJ', 'poss')
('name', 'NOUN', 'nsubj')
('is', 'VERB', 'ROOT')
('Ishio', 'PROPN', 'attr')
('from', 'ADP', 'prep')
('Japan', 'PROPN', 'pobj')
('.', 'PUNCT', 'punct')
('Today', 'NOUN', 'npadvmod')
(',', 'PUNCT', 'punct')
('love', 'NOUN', 'ROOT')
('football', 'NOUN', 'dobj')
('!', 'PUNCT', 'punct')
('!', 'PUNCT', 'punct')

固有表現抽出

spaCyの学習済みモデルを利用して、文章内から、固有表現を抽出します

print([(d.text, d.label_, spacy.explain(d.label_)) for d in doc.ents])

出力結果。

[('Ishio', 'ORG', 'Companies, agencies, institutions, etc.'),
 ('Japan', 'GPE', 'Countries, cities, states'),
 ('Today', 'DATE', 'Absolute or relative dates or periods')]

3つの単語が固有表現とそのエンティティタイプが抽出されました。ちなみに、spacy.explain()は、ラベルに対する説明を返してくれます。例えばORGは「Companies, agencies, institutions, etc.」という意味になります。Todayも固有名詞として認識されているが、DATEとして認識されています。IshioPERSONとして抽出されてほしかったですが、ORG(企業、組織等)として認識されました。(My Name is…としているのに。。。)。試しに、Ishioの部分をJohnに変更してみた結果、PERSONとして認識されました。

tokenに対する様々なメソッド

様々なNLPのためのメソッドが用意されていますので、いくつか試してみます。

text = u'My name is Ishio from Japan. I was born in Saitama!!'
token = doc[3] # Ishio
# トークン出力。
print(token)  # Ishio 
# 再掲:品詞
print(token.pos_)  # PROPN 
# 再掲:より細かめの品詞
print(token.tag_)  # PROPN 
# 言語
print(token.lang_)  # en 
# テキストの出力。末尾に空白文字があれば一緒に出力。
print(token.text_with_ws) # 'Ishio '
# 末尾に空白文字があれば出力。
print(token.whitespace_)
# エンティティのタイプ
print(token.ent_type_) # 'ORG'
# 小文字化
print(token.lower_) # ishio
# 接頭辞(デフォルト1文字)、接尾辞(デフォルト3文字)
print(token.prefix_, token.suffix_ ) #('I', 'hio')

token = doc[9] # born
# 見出し語(lemma)
print(token.lemma_) # bear
# 正規化(normalization)
print(token.norm_) # born

booleanを返してくれるメソッドも多数用意されています。多用しそうなものを列挙します

メソッド 意味
is_alpha  アルファベット
is_ascii  ASCII文字
is_digit  数字列
is_lower  小文字
is_upper  大文字
is_title  先頭大文字
is_punct  記号
is_left_punct  左に記号があるか。# e.g. (?
is_right_punct 左に記号があるか。# e.g. ]?
is_space 空白文字
is_quote クエスチョンマーク
is_currency 通貨記号
like_url URLっぽいtokenを判定
like_num 数字っぽいtokenを判定
like_email メールアドレスっぽいtokenを判定
is_stop ストップリストに含まれるか判定

「文」に分割

センテンス(文)に分割します。

text = (u"I am in Tokyo. I was born in Saitama.")
doc = nlp(text)
sents = list(doc.sents)
print(sents)

出力結果。

[I am in Tokyo., I was born in Saitama.]

Semantic Similarity(意味的類似性)

apples, _, oranges = nlp(u'apples and oranges')
a_o_similarity = apples.similarity(oranges)
o_a_similarity = oranges.similarity(apples)
print(a_o_similarity, o_a_similarity)

出力結果。0-1の値を返します。

0.60679483 0.60679483

text間の類似性も計算可能です。

doc1 = nlp(u"my name is John")
doc2 = nlp(u"my name is Ishio")
similarity = doc1.similarity(doc2)
print(similarity)

出力結果

0.9182574986865027

SpaCyとNLTKの比較

f:id:ishitonton:20181123150621p:plain

Processing pipelineの部分はまた今度勉強します。

【備忘録】Kernel:A Data Science Framework for Quoraを読んだ

ここ数カ月、チームビルディングやソフトウェアエンジニアリングに駆り出され、業務でデータ分析ができていない。KaggleでQuora(自然言語処理のコンペ)が開始したので、奮起してとりあえずKernelを読んで勉強中。

色々忘れまくっていて、Couseraで身に付けた知識も消失しているので、メモします。

導入として読んだKernel

A Data Science Framework for Quora | Kaggle

データサイエンスのワークフロー

f:id:ishitonton:20181121213155p:plain

データ前処理項目

  1. ターゲット列の削除
  2. サンプリング
  3. 均衡 vs 不均衡(アンダーサンプリングやSMOTE)
  4. 欠損値処理(平均値による置き換えなど)
  5. ノイズフィルタリング
  6. 標準化と正規化
  7. PCA
  8. 特徴量選択

均衡 vs 不均衡

1つのクラスで90%、他方で10%の場合、標準的な最適化基準またはパフォーマンス指標が効果的ではない。不均衡なデータセットのパフォーマンス測定でaccuracyを利用すべきではない。

記号の扱い

stringライブラリを利用すると、記号一覧を取得できる。EDAにおけるノイズ除去に有用。

import string

$ string.punctuation
-> '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

ストップワード

NLTKライブラリを利用すると、英語のstopwordを取得できます。なおNLTKはNatural Language Toolkitの略。179文字のストップワードを取得できる。

from nltk.corpus import stopwords

$ eng_stopwords = set(stopwords.words("english"))
$ print(set(eng_stopwords))
->{"you've", 'an', 'at', 'through', 'over', 'am', "she's", 'any', 'theirs', 'now', 'we', 'it', 'being', 'for', 'doesn', 'if', 'there', "shouldn't", 'don', 'ma', 'my', 'her', 'this', 'that', 'won', 'then', 'more', 'such', 'had', 'most', "haven't", 'were', 'mightn', "mightn't", 'how', 're', 'yours', 'shan', 'you', 'isn', 'hers', "don't", 'a', 'just', 'below', 'down', "that'll", 'herself', 'than', 'only', 'few', 'does', 'out', "you'll", 'before', 'himself', 's', 'hasn', 'these', "hasn't", 'its', 'myself', 'against', 'but', 'off', 'whom', 'is', "you'd", 'some', 'ourselves', 'shouldn', "aren't", 'she', "should've", 'on', 'both', 'very', 'in', 'the', 'have', 'did', 'because', "couldn't", 'no', 'not', 'while', 'm', 'didn', 'to', 'under', "it's", 'once', 'needn', "didn't", 'other', "doesn't", 'are', "mustn't", 'all', 'been', 'who', 'as', 'during', 'above', 'and', 'why', 'do', 'with', 'until', 'from', 'again', 'between', 'after', 'which', 'your', 'will', 'wouldn', 'up', 'into', 'by', "wasn't", 'too', 'same', 'nor', "won't", 'about', "you're", 'here', 'themselves', 'has', 'i', 'each', 'd', 'further', 'was', 'those', 'o', 'couldn', "hadn't", 'them', 'aren', 'ain', 'yourselves', 'or', 've', 'what', 'mustn', 'should', 'our', "needn't", 'be', 'wasn', 'they', 'own', 'itself', 'so', 'doing', 'him', 'their', 'of', 't', 'hadn', 'where', "shan't", 'having', 'll', 'yourself', "wouldn't", 'when', 'weren', 'y', "isn't", 'ours', 'haven', 'his', 'me', "weren't", 'he', 'can'}

EDAの観点

自然言語処理でのEDAの観点をいくつかピックアップ

  • テキスト内の単語数
  • テキスト内のユニークな単語数
  • テキスト内の文字数
  • ストップワード
  • 句読点の数
  • 大文字の単語数
  • 先頭のみ大文字の単語数
  • 単語の平均長

大文字、小文字判定

isupper()islower()istitle()を利用する。

$ test = ['ISHIO', 'Ishio', 'ishio']
$ for t in test:
      print(t.isupper(), t.islower(), t.istitle())
->
True False False
False False True
False True False

【特別講義メモ】Deep LearningとGPUコンピューティング - Deep Learning基礎講座

東京大学公開講座として開講している「Deep Learning基礎講座」(私は参加していない!)。 本日は、NVIDIA社の2名(NVIDIA JAPAN 丹愛彦 氏、山崎和博 氏)をお招きした特別講義があり、講座受講者以外でも参加ができたので行ってまいりました。

実は、自身でGPUをほとんど触ったことがないのですが、今後触れ合うことが既に決定済みのため、まずは基礎知識を入手したいという気持ちで参加しました。今回は基本的な概要部をまとめています。

GPUコンピュータの歴史

NVIDIAは、大きく4つの事業でビジネスを行っている。「Gaming」「Enterprise Graphics」「HPCとクラウド (スーパーコンピューティング等)」「Inteligence Machine(ロボットや自動運転等)」。もともとはグラフィックスの会社だったが、GPUコンピューティングへと変遷してきた。2006年は、統合シェーダとCUDAが揃ったことから「GPU元年」と位置づけている。また2010年も、初のHPC仕様GPUであるFermiが登場した大きな節目の1つ。

統合シェーダーの登場で何が変わったのか

統合シェーダー登場前の3D処理の仕組みでは、図のように各段階の処理を、それぞれ別のハードウェアで行っていた。これにより、ハードウェアのリソースを最大限活かすことができていなかった。例えば頂点処理をする場合、Vertexシェーダーに負荷がかかるが、Pixcelシェーダーの負荷は低い。ここで遊休リソースが発生していた。

統合シェーダの登場によって、1つのシェーダーコアで処理が行えるようになった。頂点処理用にハードウェアリソースを割り当てる等、ハードウェアのリソースを割り当てをフレキシブルに可能となった。結果として性能が向上した。

f:id:ishitonton:20181107222439p:plain

GPUの中身はどうなっているのか

現在のメインのアーキテクチャは、Volta GPU。その1つ、「TESLA V100」は、データセンター向けのハイエンドなGPUであり、CUDA Coreが5000個以上も詰め込まれている。また、V100は80個のストリーミングマルチプロセッサを搭載。その1個のストリーミングマルチプロセッサには、64個のFP32演算用のCore、64個のint32演算用のCore、32個のFP64演算用のCore、8個のTensor Cor・・・うわぁぁっぁぁ。

とにかく、非常にたくさんの演算coreが搭載されている。

なぜこんな搭載してるのか。それは、ムーアの法則の終焉が見えているから。

CPUは、クロック周波数年率1.25倍の性能向上を果たしてきたが、2005年くらいから限界が見えてきた。CPUはマルチコア化により性能向上を維持してきたが、それでも最近は限界がみえていて、年率1.1%の性能向上と鈍化している。つまり、これからの性能向上は、別の方法が必要になっており、その別の方法の1つが多スレットの並列化GPU元から並列計算用に設計され、最適化されているので、今現在も年率1.5倍の性能向上を維持できている。

CPU vs GPU

「CPU vs GPU」の関係は、「レイテンシー vs スループット」の関係と一緒である。例として、空港までバイクとバスで移動することを考える。バイクは、1人あたりのデリバリーは速いが、30人の場合30往復する必要がある。一方、バスはバイクよりスピードが遅いが30人同時に運ぶことができる。

f:id:ishitonton:20181107231051p:plain

GPU⇔CPU間のデータ転送がボトルネックになる

GPU単体で計算できないので、CPUと組み合わせて利用する必要がある。GPUとCPUのメモリは物理的に分かれていて、IOバスで接続されている。GPUを利用する場合には、CPU側からGPU側にデータを転送する必要がある。

ここで、各コンポーネント間のデータ転送速度を見てみる。どこがボトルネックになるかがよくわかる。

  • GPUのメモリとGPUの演算コアの転送速度:900GB/秒
  • CPUとGPUの間の転送速度:32GB/秒。

GPUでパフォーマンスを出すためには、CPU⇔GPU間のデータ転送を必要最低限にする。また、この転送中に別の処理をオーバーラップさせる。こういったハードウェアをより効率的に利用するためにCUDAが準備されている。CUDAは、並列コンピューティングのための統合開発環境であり、これによりGPUとCPUを組み合わせたGPUコンピューティングを可能とする。

CUDAがあるととにかく嬉しい。コードを書く量が圧倒的に少なくて済む。

以上です。つかってみまーす。

【書評】データサイエンティスト養成読本-ビジネス活用編

『データサイエンティスト養成読本-ビジネス活用編-』を購入し5章まで拝読しました。データ分析界隈の今をときめく著者方が、ビジネス活用のための組織づくりや人材について語っている一冊です。自身にとって、とても実りある一冊でした。また各著者は結構同じ問題意識や志向をお持ちであることもわかりました。

いくつか自分の解釈や経験も踏まえて、いくつか重要な要素を絞って整理しました。

  1. ビジネス価値を定義する
  2. 解釈可能性を考慮する
  3. ビジネス成果までコミットする
  4. 生命線のビジネス業務理解
  5. データドリブンな組織づくり
  6. 機械学習の期待値調整

f:id:ishitonton:20181104131302p:plain

ビジネス価値を定義する

データ分析の効果を、何かしら「定量的な指標」で示すことが非常に重要である。理由は以下。

  • データ分析でビジネス成果を出しているのに、その成果をうまく表現し伝えることに失敗しているケースが多く存在する。
  • データ分析者は、ついOutcome(ビジネス成果)への意識が途切れることがある。

特に、金額換算する意識を持つとよい。金額換算の意識は、データ分析者のビジネス成果への意識を飛躍的に高めることに繋がる。また、「金額」や「収益に近い指標」の方が当然インパクトが大きい。

解釈可能性を考慮する

「凄い分析」ではなく、「活用イメージの湧く分析」を目指そう。新しい分析手法やアルゴリズムの分析結果に関して、現場に対してうまく説明できなければならなし、正しく理解してもらわねばならない。ビジネス成果は『現場』で生まれるので、彼らが納得しないと成果は生まれない。

これは例を挙げると、多少精度の高いDeep Learningを使うか、多少精度は落ちるが解釈可能性が高いロジスティック回帰モデルを利用するか。恐らくこの判断は、解釈可能性と精度を上げると売上がどれくらい上がるのか、等の事業内容や相関性に起因する。解釈可能性と精度は時としてトレードオフな関係にあるはず。

では、新しい分析手法やアルゴリズム試さなくて良いのか?というとそんなことはない。書籍には「余力があれば挑戦するぐらいが望ましい」と記載してあったが、個人的には5%~10%くらいは新たな取り組みに挑戦する時間を業務内に取り入れるべきと考える。

理由としては、メンタルモデルが拡大しないこと、メンタルモデルが拡大しないと事業の成長はストップする、ステイすることは停滞することを意味するため。自動化できる分類作業等をマンパワーでやりまくっている組織は結構多いと思う。これは単純に自動化できることを知らないケースがかなりあると思う。

ビジネス成果までコミットする

分析結果を「現場に丸投げ」せずに、名指しする勢いで具体的に誰が何をやるべきか、得られるビジネス成果を提示しよう。理由は以下。

  • ほったらかすと、いつまでもデータ分析の結果が実務で活かされない。
  • ビジネス成果が出る可能性を他者に委ねることになる。

ついつい分析者は、分析結果で満足してしまう難がある。私自身もよくある!後は良しなに的な気持ちもなることもあるが、ビジネス成果が出ないと、その分析はコストでしかない。データ分析者が積極的に現場をリードするくらいやろう。

また、特にデータ分析経験の少ない現場であれば、二人三脚でビジネス成果まで積極的に関与しよう。もし、それが機械学習のプロジェクトであれば、そもそも意識的に協力体制を作らないと疎結合になってしまう。特徴量の中身もよくわからんでおわる。

生命線のビジネス業務理解

データアナリストは、ビジネス成果が出る『現場』の業務を理解しよう。その理由は以下。

  • 現場を知らない人が積極的に現場の思い込みを語ることはできない。
  • 現場を知らない人の言葉に現場は耳を傾けない。
  • 現場の業務を知ると、データ分析結果に具体性が宿る。
  • データ分析結果に具体性が宿ると、現場は動きやすくなり、データ分析によるビジネス成果を得る可能性が高くなる。
  • データは、実際に起こった事象のほんの一部に過ぎない。多くの事象はデータとして記録されていない。

ビジネスの多くは人と人のコミュニケーション。お互いのことを知らずして信頼関係は築けない。現場に自分を知ってもらい信頼関係を築くために、常日頃努力する必要がある。積極的に知ろうとする姿勢も大事。

一番善き方法は、実際の一定期間業務経験をすること。これはビジネス側が協力的になる副次効果もある。

データドリブンな組織づくり

数字に基づいたPDCA、意思決定を行える「データドリブンな組織」を作っていくことは非常に重要である。その理由は、この不確実性が極めて高い世の中において、少しでもその不確実を低下させて事業を推進していくためには、データを中心に意思決定ができる組織が重要と考えるからである。

データドリブンな組織を作っていくためには、社内のメンバーのデータリテラシーを高めていくことが非常に重要です。そのために各社は色々トライしています。

  • ダッシュボードで、分析結果が誰からでも見れるようする。
  • ダッシュボードで、分析結果をすぐに見れるようにする。
  • みんなが見て、使ってもらえる「愛されダッシュボード」を作ることを意識する。
  • Slackで、分析結果を誰でもすぐに議論できるようにする。
  • Slackで、重要なKPIをプル配信し、データ意識を浸透させる。
  • Slackで、データ分析に興味あるメンバーが議論や質問できるチャネルを作成する。
  • 各部署で、簡易的な分析を実行できるデータ民主化を進める。例えば、Githubへのクエリ知見蓄積など。
  • ときに、営業サポートになってしまう組織が多々ある。それを回避するためBI環境を整備する。

上に挙げた「ビジネス成果の定量化」の観点で言えば、このデータ意識の醸成などは、Slackのチャネル参加人数数や議論の数、コメントをくれているユニークユーザ数などで、どの程度社内に浸透できているかを評価できる気もするなと思います。

また、組織として長期的にデータドリブンに成長していくためには、1人のスーパーマンに依存せずに、分業しスケールできるチーム体制も重要です。ビジネススキルを補うチーム編成、すなわち各分野に精通した人材と協同して動ける体制が、組織の成長をドライブするはず。

機械学習の期待値調整

機械学習は、ときにビジネスサイドやクライアントから、現実とはかけ離れた期待値を投げかけられることがあります。そもそも、機械学習の特性として、最初から精度の保証ができないため、正しい期待値を持ちにくいという特性があります。以下を意識して取り組もう。

  • 機械学習自体がボトルネックになるサービスかを調査しよう。
  • POCを通じてフィージビリティを確認した上でプロジェクトを進めよう。
  • ビジネス価値を定義して、機械学習を導入することへの期待値が整理しよう。これは、リリース後の成功を定量的に評価できるようにもなる。
  • 期待値は「現実ライン」「最低ライン」「理想ライン」で提示しよう。
  • 機械学習を使わないという選択肢も持とう。
  • 仮にSI事業の場合、「一定の精度を超えたら検収」は双方にとってリスク。精度による追加ボーナスやレベニューシェア等、契約方法は考えよう。
  • 保守管理のことを恐らく考えてない場合が多い。精度劣化は回避できないし、勝手に壊れていく、継続的な学習が必要なことも理解してもらおう。

以上です。

Elastic Cloudの無料トライアルで5分でデプロイしてKibana最新版のグラフ機能を利用してみた

検索エンジンElasticSearchや可視化ツールKibanaをクラウド上で容易にデプロイして利用できるSaaS製品「Elastic Cloud」がリリースされています(結構前に)。現在、14日間の無料トライアルが可能であり、最新版のElasticSearchやKibanaをAWSGCP上で利用できます。今回は、Kibanaの最新版にしか入っていないグラフ機能を試したく、デプロイしてみました。

Elasticの製品は、非構造化データを蓄積・検索・可視化する上での有力な選択肢の1つと考えます。サンプルデータもついてますし、めちゃくちゃ簡単にお試し利用できるので、触ったことがない方は一度試してみることをお勧めします。

Elasticsearchの パワーを利用した SaaS製品群 | Elastic

デプロイは5分で終わる

Elasticsearchの パワーを利用した SaaS製品群 | Elastic」のページ左のElasticsearch Serviceにメールアドレスを入力し「今すぐトライ」します。受信メール上のリンクをクリックすると、デプロイの設定が開始します。(※2018年10月30日時点)

f:id:ishitonton:20181030232106p:plain

デプロイまでの設定は以下の通りです。

  1. deployment nameを決める。(何でもよい)

  2. AWSGCPを利用するかを決める。どちらでも良いと思うのですが、AWSにはregionに「Asia Pacific (Tokyo)」があったので、私はAWSを選択しました。

  3. regionの設定。上記の通り。

  4. set up your deployment。何も変更せず。

  5. デプロイメントの最適化。いくつか最適化のプランがありますが、Elastic側っで推奨している「I/O Optimized」を選択しました。

最後に「Create deployment」をクリックしてデプロイがスタートです。

f:id:ishitonton:20181030233456p:plain f:id:ishitonton:20181030233602p:plain

デプロイはおそらく5分くらいで完了します。終了後のマイページの画面は下記になります。デプロイしたElasticc Cloudのdeploymentが表示されており、KibanaとElasticSearchのロゴが確認できます。これらをクリックしてみましょう。 f:id:ishitonton:20181030234558p:plain

AWS上にデプロイした詳細情報が表示されています(一部黒塗り)。インスタンスはElasticSearchがmasterとSlaveで計3台、Kibanaが1台立っています。シャーディング2でElasticSearchは負荷分散を行っているものと考えられます。また、赤枠部分にElasticSearchとKibanaのエンドポイントのリンクが記載されています。Kibanaの部分をクリックすれば、Kibanaの画面に移ります。

f:id:ishitonton:20181030235122p:plain

サンプルデータをインポートする。

さっそくKibanaにログインしてみます。最初の状態は、ElasticSearchにデータが何も入っていない状態です。ただ、このトライアルでは、Add Data to Kibanaと記載してあるとおり、サンプルのデータが用意してあるので問題ありません。log分析やMetrics監視、セキュリティ解析等、いくつかデータを用意してくれていますが、今回はフライトデータを利用したいと思います。画像赤枠の「Sample DataLoad a data set and a Kibana dashboard」をクリックします。Sample flight dataがありますので、Addしていただくと、データのインポートは完了します。

f:id:ishitonton:20181031000641p:plain

続きを読む

Mercari Tech Meetupに参加してきたメモ - 後半

機械学習技術についてトークするMercari Tech Meetupに参加しましたので、その振り返り後半部を記載します。前半部は以下の記事を参照ください。

ishitonton.hatenablog.com

前半部は、メルカリが簡単な売買を実現するための機械学習の活用を中心に記載しました。後半は安全な売買の実現のための違反検知について記載します。また、運用面やインフラ面についても少しだけ記載しています。 なお、conpassに一部発表資料も公開されました↓↓

Mercari ML Meetup

メルカリ出品数は10億点

これは2018年7月末の数字です。1日に数10万出品がされる計算になります。ビッグデータですね。この中から違反出品を探し出すことが求められます。CtoCビジネスに登場するアイテムは本当に未知らしく、海岸の砂と甲子園の砂も売れるかもと思って出品するユーザがいるようです。

違反検知に機械学習を活用

1万円札がメルカリ上で1万円以上の値段で出品されていたニュースは記憶に新しいところ。出品されるものに対して「OKかNGか」を判断しルールにしていくことが求められます。安全にお客様に売買してもらうために、機械学習を利用しています。

大量多カテゴリの不均衡データから違反出品を探し出すために、当初は多値分類に挑戦していたが、課題が見えてきたそう。その1つとして、商品ごとの検知容易さや検知後の対応が異なることが挙げられます。例えば、画像で検知しやすい出品もあれば、文章で検知しやすい出品もあります。これは商品によって異なります。

また、対応の緊急度が異なれば、対応内容も異なることも挙げられます。仮に1万円札が出品されていたら表示されないようにするが、他の商品はアラートを出力するだけのものもある。

このように、商品によって違反検知への考え方が異なる中で、特定のカテゴリに特化したバイナリ分類に分解することで解決を行ったそうです。ここで問題になるのは、1つのモデルだったら、それだけ運用すればよいが、モデルによって異なる運用があり、目的があり、デプロイ方法があるということ。

モデルの運用をプラットフォームで解決する

大量のモデルに対して、評価・ReTrainingを自動化できる業務設計やシステム設計に取組んでいるのがメルカリです。例えば、katibを利用してハイパーパラメータのチューニングを行っています。Katibはスケーリングにも優れるし、k8sとの相性が良いとのこと。 また、モデルのパフォーマンス維持には正しい教師データの蓄積が必要。これはモデルはやってくれないので、自分たちでこの出品は「違反である」とpushする。実際に一万円札をNGかどうか判断して違反のデータテーブルを作り、再学習に回していくライフサイクルを作ることが非常に大事のようです。聞いた感触では、違反かどうかを判断するプロの目利きであるスタッフがいるようですので、人間も一部介したシステム・業務設計により、うまく鮮度の高い学習データを作っている印象を受けました。

その他のお話

その他にも運用面でためになる話をたくさん聞けました。

  • 全モデルがVersion管理されイメージ化されている。全ての実行時のイメージをコンテナレジストリで管理、いつでも再実行できるようになっている。
  • モデルはメモリを使う。数GB使うのは当たり前。プロセス毎にモデルをロードする必要があるとつらい。Copy on Write (Cow)を活かす工夫が必要。
  • ヒトの行動を予測するようなモデルが多い。基準や傾向はすぐに変わってしまうので、そこに追従するためにハイパーパラメータをReTrainingすることが大事。そして、モデル評価とReTrainingの自動化が重要。
  • 多くのモデルをビジネス側から求められている。とても間に合わないので、「民主化」となる仕組みを用意してMLエンジニア以外でも簡単にできるように準備を進めている。

簡単ですが、以上です。

Jリーグの移籍情報をスクレイピングしてチーム間の関係性を可視化する

湘南ベルマーレ、ルバン杯優勝おめでとうございます。 私は他チームのサポータですが、湘南を応援していました。理由は、梅崎選手が好きなのと、自分が応援するチームと湘南間での移籍が多いからです。

Jリーグの移籍情報で、チーム間のコネクションの強さがわかるかと思い、移籍情報をスクレイピングして可視化してみました。 コードは汚いですがご了承ください。

スクレイピング先は下記ページです。

www.jsgoal.jp

ページはこんな感じです。2015~2017年の3年間におけるJ1、J2チームの移籍を対象にしました。 f:id:ishitonton:20181028124731p:plain

コードは下記になります。BeautifulSoupを利用しています。 スクレイピングした情報をpandasを利用して、データフレーム形式に構造化しました。

import urllib
from bs4 import BeautifulSoup
from collections import defaultdict
import pandas as pd

urls = ["https://www.jsgoal.jp/transfer/2015/?division=J1",
        "https://www.jsgoal.jp/transfer/2016/?division=J1",
        "https://www.jsgoal.jp/transfer/2017/?division=J1",
        "https://www.jsgoal.jp/transfer/2015/?division=J2",
        "https://www.jsgoal.jp/transfer/2016/?division=J2",
        "https://www.jsgoal.jp/transfer/2017/?division=J2"]

d = defaultdict(list)
for url in urls:
    if "2015" in url:
        year = "2015"
    elif "2016" in url:
        year = "2016"
    else:
        year = "2017"
    res = urllib.request.urlopen(url)
    soup = BeautifulSoup(res, "html.parser")
    clubs = soup.find_all(class_="field")
    for club in clubs:
        club_name = club.h1.span.text
        in_out_info = [club.select("div > div")[0], club.select("div > div")[1]]
        for info in in_out_info:
            name = info.select("div > div > b > a")
            team = info.select("div > div > p")
            for a, t in zip(name, team):
                d['name'].append(a.text)
                d["transfer_team"].append(t.text)
                d['team'].append(club_name)
                d['year'].append(year)
df = pd.DataFrame(d)

df.head()をみると、こんな感じです。

f:id:ishitonton:20181028125121p:plain

前処理として、チームをJ1とJ2チームに絞り、不要な文字列の除去と揺らぎを修正します。 主にpandasstrエクセラを利用して文字列を修正しています。

dic=[("千葉","ジェフユナイテッド千葉"),
     ("清水", "清水エスパルス"),
     ("名古屋", "名古屋グランパス"),
     ("京都", "京都サンガF.C."),
     ("湘南", "湘南ベルマーレ"),
... 
    ("栃木", "栃木SC"),
    ("町田", "FC町田ゼルビア"),
    ("横浜FC", "横浜FC"),
    ("讃岐", "カマタマーレ讃岐"),
    ("金沢", "ツエーゲン金沢"),
    ("徳島", "徳島ヴォルティス")]
df = df[df["transfer_team"].str.contains(u'←')]
df["transfer_team"] = df["transfer_team"].str.replace(u'[ ←', '').str.replace(u' ]', '')
df["transfer_team"] = df["transfer_team"].str.split(u'/').str[0]
for d in dic:
    df['team'] = df['team'].str.replace(d[1].decode('utf-8'), d[0].decode('utf-8'))

もう少しきれいになりました。

f:id:ishitonton:20181028125533p:plain

最後に、移籍元チームをone-hotにして、チーム間での移籍件数を集計し、さらにJ1/J2のチームに絞り込みました。

df = pd.concat([df, pd.get_dummies(df.transfer_team)], axis=1)
df_sum = df.groupby('team').sum()
df = df[df.index.tolist()]

元データとしては、こんな感じです。

f:id:ishitonton:20181028130035p:plain

MDSとかクラスタリングとか、どのように可視化しようか考えたのですが、シンプルにヒートマップで可視化したいと思います。matplotlibseabornを利用します。

%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(10,10),dpi=200)
sns.heatmap(df5, annot=True, cbar=True, cmap='hot_r')
plt.show()

可視化結果はこんな感じ。縦軸横軸はそれぞれチームを示していて、交わっているところの点数が高いほど、チーム間の移籍が多いです。今回、チーム名伏せているのですが、virtualenvの参照先がおかしいのか日本語文字化けが直らんので、面倒なので省略しました。おそらく皆さんの環境であれば文字化けないので、気になる方は試してみてください。(後で画像は更新するかも) f:id:ishitonton:20181028130541p:plain

考察

移籍が多いところを見ると、以下のようなところがここ3年では、関係性が強いことがわかりました。

「広島から浦和に選手が移籍しまくる」話もありましたが、ここ最近はないですね。 もっといい感じにするには、下記を考慮すればよいかと思いました。

  • データが3年分しかなかったので、もう何年か分あればより充実した結果になる。
  • ユースからの移籍とかのデータを今回は省いたので、そういったデータも利用すれば「このチームは内製型」みたいな特徴が可視化・定量化できるはず。
  • 期限付き移籍や完全移籍などのステータスを省いているので、そこらへんを重み付けすると面白いかも。

簡単ですが、以上です。