Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

【ML Tech RPT. 】第5回 不均衡データ学習 (Learning from Imbalanced Data) を学ぶ (2)

f:id:ssatsuki040508:20181210005017p:plain
Sansan DSOC 研究員の吉村です. 気づけば入社から一年が経ち, 社会人二年目に突入しました. 今年もコツコツとブログを継続していこうと思っております.

さて, 本題に入っていきましょう.
今回は不均衡データ学習のアプローチの一つである Cost-Sensitive Learning についての話をします. 本記事は前回の記事の続編という立ち位置なので, 前回の記事をご覧になっていない方は, 是非下記の記事から読んでみてください.
buildersbox.corp-sansan.com

Notation

説明をする前に, 今回の記事で用いる変数についてこちらでまとめておきます.

 x_i  i番目の事例. データ点.
 y_i  i番目の事例に対応する真のラベル. 二値分類の場合は,  y_i \in \{-1, 1\}. 多クラス分類の場合は,  y\in\{1, 2, \cdots, M\}
 \hat{y}  i番目の事例のラベルの予測値.
 f 判別関数.  f \in \mathbb{R}
 L, \phi 損失関数.
 L_i  i番目の事例に対応する損失関数.
 C Cost Matrix.
 S 事例に対応するスコアベクトル.  S=(S_1, S_2, \cdots, S_M)\top.  S_kはラベル kに対応するスコア

Cost-Sensitive Learningの概要

Cost-Sensitive Learning (コスト考慮型学習) とは, 不均衡データ学習に対処するためのアプローチの一つで, 許容し難い誤りには大きなコストを, 許容できる誤りには小さなコストを課す損失関数でラベル予測をする方法です.

例えば, 製造した自動車の部品が不良品か良品かを機械学習を用いて判別するとき, 普通の分類問題としてモデルを学習する場合には, 実際は不良品のものを良品と識別することも, 実際は良品のものを不良品と識別することも同等の影響があるとみなします. つまり, このときの損失関数 L L= \sum_i \mathbb{I}[y_i \neq \hat{y}_i]のように表すことができます. ただし,  \mathbb{I}は指示関数を,  y_iは製品 iの真の品質(良品なら +1, 不良品なら -1), \hat{y}_iは製品 iの識別結果を表します. しかし, この問題は人によっては次のように考える人もいるかもしれません.

「製造しているのが自動車の部品であることから, もし不良品が出荷されると死亡事故につながる恐れがあるため絶対に不良品を良品とモデルで識別することは避けたい. しかし, 良品を不良品と識別することはその不良品を一つ製造するコストが無駄になるだけなのである程度は許容できる. 」

仮にこれを「不良品を良品と識別することは良品を不良品と識別することよりも n倍の影響がある」と判断したとすれば, このときの損失関数 L L=\sum_i (n \mathbb{I} [(y_i=-1) \land (\hat{y}_i=+1) ] + \mathbb{I}[(y_i=+1) \land (\hat{y}_i=-1)])と書くことができます.

このように各種誤りの影響度をコストとして与えることで考慮するのがCost-Sensitive Learningです. 各種誤りに対するコストを損失関数に反映させるための方法の一つとしては, Cost Matrix(コスト行列)を用いる方法があります.

続きを読む

try!Swiftで次の世代の開発者やプロダクトのことを考えた - Diversity in Developer -

こんにちは。Eight事業部でiOSエンジニアをしています アマゾネス です。

東京で開催された try!Swift というカンファレンスに参加して、すごく大事な話を聞いたので共有します。

f:id:zankief:20190325134930j:plain

try!Swiftとは?

try! SwiftはSwift言語での開発における最新の応用事例について集まる国際コミュニティです。このイベントは世界中から人々が集まり、Swiftのスキルを向上させるための、高度な知識やテクニックを共有し、協力しあうことを目的としています。

TOKYO - try! Swift Conference から引用

Swift は、2014年に apple から発表されたプログラミング言語で、主に iOS、MacOS のアプリケーションを作ることに使用されています。私はiOS版の Eight を実装するために、Swift を書いています。 


カンファレンスには、世界各国から900人を超えるエンジニアが集まりました。私は初めてこのような大規模なカンファレンスに参加したのですが、日本以外からの参加者の多さにとても驚きました。


登壇内容はどれも素晴らしく、知らないことだらけで、とても勉強になりました。登壇で使用された資料をオンラインで公開されている方が多いので、興味がある方は是非ググったり、Twitter等で検索してみてください。

Female YouTuber Mayuko Inoue

技術系のお話がほとんどかと思ったのですが、「Developerとして」というような技術から少し離れたお話もいくつかありました。
中でも、Mayuko Inoueさんがお話されていた「次へつなごう— Extending a hand to the next generation of Apple developers」がとても心に残ったので、その内容についてお話をしたいと思います。


まず、Mayukoさんについて。

iOSエンジニア📱 Youtubeでテック業界で働くエンジニアとしての生活について動画も作ってます。 🇺🇸生まれ、🇺🇸育ちの二世の関西弁訛りの日本人です。

helloMayuko_jp (@helloMayuko_jp) | Twitter より

現在は Netflix でiOSエンジニアとして働かれているそうです。
美人、語学堪能、おまけにプログラミングも一流なんて...とにかく凄すぎる。交流会で少しだけお話する機会があったのですが、とても気さくで、本当に素晴らしい方でした。

彼女自身の1日の様子を収めた動画↓がとても有名です。

www.youtube.com

開発者の多様性について

彼女は過去、エンジニアへの応募に人種的な多様性がないことを疑問に思い、自身の Twitter でモバイルエンジニア、特にiOSのエンジニアが少ないのは何故かと言うことを問いかけたそうです。

開発するためのPCが高い、モバイル端末が高い、など金銭面に関わることや、どうやってなればいいのかわからない、などのレスポンスがあったそうです。


私はラッキーな形でプログラミングの世界に入ったと思います。
1人プログラムを書いている友達がいたので、その人に教えてもらえたし、開発するためのMacを買えました。でも、世の中には様々な問題から、エンジニアの世界に入ることができない人がいるのです。


このような状況を鑑みると、今はまだ「開発者の世界は多様性に富んでいる!」とは言うことはできなさそうです。

次の世代の開発者のために今の開発者ができること

Diversity (多様性) が謳われる現代において、特定の人々で作るプロダクトよりも、様々な人で作った方がより良いプロダクトになるはずである、そのような状態を実現するために、Mayukoさんは以下の二つの提案をされていました。

- イメージを作る

自分のしたいこと、自分にしかできないこと、など自分自身が持つ多様性と向き合い、そこからどのようなことができるかイメージを作りましょう、ということかと、私は思います。具体的な例として「プログラミングを教える」「自分の知識を共有する」などがあげられていました。イメージをつくることによって、次の世代に繋げるための、行動が生まれるのではないでしょうか。

- あなたのことをしってもらおう

文字通り、開発者である自分のことを発信することによって、色んな人に開発者ってこんな感じなんだ! ということを知ってもらおうということ。知る、ということは、興味を持つきっかけにもなるし、開発者になりたい人への参考にもなりますので、間口が広がるでしょう。 

私が考えたストーリーと感じた事

既に開発者である私たちが行動する  → 色んな人に開発者になるチャンスが生まれる  →  開発者になる  →  様々なプロダクトが生まれる  → 沢山の人が喜ぶ!


開発者になる入り口を広げることにより、今、そして、次の世代では多様性豊かな開発者が沢山生まれ、その結果として、新しくそして素敵なプロダクトが世に出る。
これは一人一人が違う社会の中で、大事なことだと思います。


Mayukoさんのスピーチを聞いて、開発者として技術を高めるだけでなく、他にもやるべき事があることに気づかされました。弊社のエンジニアにもメンターとしてプログラミングを教えたりしている者がいますが、私はとても尊敬します。


また、これは開発者だけでなく、他の業種の人にも言えることなのではないかな、と思います。プロフェッショナルになることはもちろん大事なことですが、それだけでは良い製品は生まれないのかもしれません。


今回は、現在開発者である自分にできることの一つとして、この内容を寄稿させていただきました。


2019年のtry!Swiftに参加できて本当に良かったです。

f:id:zankief:20190325135001j:plain

補足:

Mayukoさんの登壇の様子は後日 try Swift! の YouTubeチャンネルでシェアされるそうです! また、登壇の内容を文章にしてくださっている方がいらっしゃいました! 本当に感謝です。シェアさせていただきます。

次へつなごう— Extending a hand to the next generation of Apple developers | try! Swift Tokyo 2019 2-16 - niwatakoのはてなブログ

 

チームにE2Eテストの文化を広めた話

こんにちは。Sansan 事業部プロダクト開発部の奥野です。

私は中途採用で昨年11月に入社して4ヶ月ほどになる、関西支店で勤務するエンジニアです。MAIDO という開発チーム*1 に所属しています。

今回の記事では、1月に私が主催した Selenium のチーム内勉強会を起点にして始まった、E2E テスト導入に向けた動きについてレポートします。なお、E2E テスト(End to End テスト、詳細は後述)の技術的側面については深掘りせず、新しい文化の導入に主題を置きます。

Seleniumとは

まず、勉強会の題材とした Selenium について簡単に紹介します。

Selenium とは、ブラウザを使用する E2E テストを自動化するテストフレームワークです。その歴史は2004年から始まっているため*2、ご存知の方も多いかと思います。

E2E テストは、システム全体を一通り動作させて行うテストであり、機能テストや結合テストといったテストフェーズで行われます。 Web アプリケーションの場合、ユーザがブラウザ操作を起点にして、クライアント処理・サーバ処理を通して画面再描画に至る過程を最初から最後までテストします。

E2E テストはユニットテストに比べると、ビジネスや要件の観点からのテストが可能であり効果的です。
一方で、E2E テストはシステム全体を動作させるため、テストの事前条件としてDBデータを揃えておく必要があるなどのセットアップが大変、テストに時間がかかるというデメリットがあります。

なぜ E2E テストをチームに導入しようと思ったか

Sansan はリリースしてから10年以上経過しているプロダクトであり、いわゆるレガシーシステムです。

仕様や設計が複雑化しており、仕様を把握するのも、テストをするのも大変です。少なからず技術的負債も蓄積しており、どのように向き合っていくかは常に問題になっています。また、大規模なリファクタリング、新しいアーキテクチャへの移行についても議論が盛んです。

一方で、MAIDO チームで開発している管理者向け機能では、テストコードによる保護、テスト自動化が積極的に行われていませんでした。個々のビジネスロジックに対するユニットテストはテストコード化されていましたが、機能テストレベルのテストコードは存在していませんでした。つまり、UI制御やDBアクセスを含めた機能仕様観点からテスト仕様は過去のテスト設計書から掘り起こさなければならず、テスト自動化されていませんでした。

そのような中で入社数ヶ月の身としては、プロダクト知識が不足しており、ソースコードを変更した際にそれがデグレを起こしていないか、不安で仕方がありませんでした。リグレッションテストをしようにも、何をテストしていいのかすら分からない始末でした。

「ここにテストコードがあれば、ある程度のリグレッションテストを行うことができるのに…」

私は前職で、Selenium を活用した E2E テストの自動化に取り組んでいました。それを主たる業務としていたわけではないですが、それなりに思い入れがあり、その導入効果も把握していたので、E2E テスト自動化の導入に向けて声を上げようと思った次第です。

*1:私が所属する関西支店の開発チーム MAIDO については、チームメンバーの加藤が紹介している記事がありますのでそちらをご覧ください。

*2:https://www.seleniumhq.org/docs/01_introducing_selenium.jsp#selenium-history

続きを読む

単語埋め込みを単語埋め込みに埋め込む -後編-

こんにちは、DSOC R&Dグループ インターン生の荒居です。 この記事は、単語埋め込みに単語埋め込みを埋め込む-前編-の続きの記事です。

前編では、ベースとなる 単語埋め込み になかった単語のベクトル表現を別の単語埋め込みから輸入することを実験的に行い定性的に評価をしました。 今回は同じ手法でベースとなる単語埋め込みを拡張し、文書分類のタスクを解かせてみることでその有効性の検証を行います。

振り返り

前回の記事では、2つの単語埋め込みの両方に含まれる単語のベクトル表現の対応関係を線形変換として表現し、その線形変換を片方の単語埋め込みにしか存在しない単語ベクトル表現にも適用することで、単語埋め込みの拡張を行いました。

f:id:koukyo1213:20190319152325p:plain
2つの単語埋め込みの積集合を用いて単語ベクトル空間の間の変換を取得する
f:id:koukyo1213:20190326110130p:plain
単語ベクトル表現を別の単語埋め込みから「輸入」する

続きを読む

単語埋め込みを単語埋め込みに埋め込む -前編-

こんにちは、DSOC R&Dグループ インターン生の荒居と申します。 今年の1月から自然言語処理のインターン生としてお世話になっています。

インターンでは文書分類のタスクを扱っていたのですが、単語埋め込み を用いるようなディープラーニングベースの手法において、しばしば単語埋め込みのボキャブラリに、扱う文書中の単語が含まれていないという問題(out of vocabulary, OOV)に行き当たりました。

本稿ではOOVとなる単語を減らすために複数の単語埋め込みを用いて単語埋め込みを拡張するという手法を考え、実験してみた結果を紹介させていただきます。

検証に用いたコードなどはGitHubにて公開しております。

github.com

OOVについて

OOVとなるような単語にはどのようなものがあるでしょうか?

OOVが発生するのは単語埋め込みを学習する際に、学習に用いたコーパス中に入っていなかった、あるいは出現頻度が低かった単語が存在するからです。したがって珍しい単語、一般的ではない単語がOOVを発生させやすいと言えます。また、学習させるコーパスが違うならばOOVとなる単語も変わるということもあり得るわけです。

具体例を考えてみましょう。ここでは、Wikipediaコーパスで学習したfastText(300d) と 求人データで学習したword2vec(200d) を学習済み単語埋め込みとして用いて、 Livedoorニュースコーパスを例に説明させていただきます。素晴らしいデータをありがとうございます。

Livedoorニュースコーパスにはユニークな単語は何語くらい入っているのでしょうか?

NElogd で分ち書きに直してカウントしてみたところ、およそ9万語のユニークな単語が含まれていることがわかりました。

# NEologdを使う
tagger = MeCab.Tagger(
    "-Ochasen -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/")

def nn_tokenizer(text):
    # 全角を半角へ
    text = mojimoji.zen_to_han(text.replace("\n", ""), kana=False)

    # URL除去
    text = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
    parsed = tagger.parse(text).split("\n")
    parsed = [t.split("\t") for t in parsed]

    # 空文字とEOSを除く
    parsed = list(filter(lambda x: x[0] != "" and x[0] != "EOS", parsed))
    parsed = [p[2] for p in parsed]
    return parsed


loader = DataLoader("../input/text/")  # リポジトリを参照
loader.tokenize(nn_tokenizer)

data = loader.data
tk = keras.preprocessing.text.Tokenizer(lower=True, filters="")
tk.fit_on_texts(data.tokenized.map(lambda x: " ".join(x)))

word_idx = tk.word_index
print(len(word_idx))  # => 90866

続いて、Livedoorニュースコーパスに含まれる単語の中で、2つの学習済み単語埋め込みに含まれていない単語がないか調べてみましょう。

def load_embedding(path):
    binary = False
    if "bin" in path:
        binary = True
    emb = KeyedVectors.load_word2vec_format(path, binary=binary)
    return emb


stanby = load_embedding(
    "/path/to/where/you/put/stanby-jobs-200d-word2vector.bin")  # Bizreachの200d word2vec
wiki = load_embedding(
    "/path/to/where/you/put/model.vec")  # WikiコーパスのfastText

stanby_words = set(stanby.vocab.keys())
wiki_words = set(wiki.vocab.keys())
livedoor_words = set(word_idx.keys())

oov_stanby = livedoor_words - stanby_words
oov_wiki = livedoor_words - wiki_words

print(len(oov_stanby))  # => 48371
print(len(oov_wiki))  # => 30477

かなりの単語がOOVとなっていることがわかります。実際にはどのような単語が抜けているのでしょうか? 少し単語を眺めてみましょう。

print(oov_stanby)

# => {
#    'オリバー・ストーン', 'ナショナルスタジアム', '小川純', 
#    '19cm', '証人', '決意表明', '武家', '数え上げる', '富士通グループ', 
#    'ブロードバンドルータ', 'powert', '商用電源', '聖光学院中学校・高等学校',
#    ...}

print(oov_wiki)

# => {
#    ...
#    '倉敷市芸文館', '不倫は文化', '3杯目', '首根っこ', 'エコポンパ', 
#    'happiness!!!', 'tanita', 'ベイベ', 
#    'appbundle', 'viewカード', 'ヴォルフスブルグ', '狡辛い',
#    ...}

なかなか特徴的な単語が多いですね。fastText の方には入っていて word2vec には入っていない単語や、その逆の単語はどの程度あるのでしょうか?

in_stanby = oov_wiki - oov_stanby
in_wiki = oov_stanby - oov_wiki

print(len(in_stanby))  # => 4279
print(len(in_wiki))  # => 22173

今操作してきた単語集合を図で表すとこのようになります。

f:id:koukyo1213:20190319144350p:plain
単語集合の関係図

続きを読む

Dawn of Innovation :SIP2019登壇レポート

f:id:sansan_nissy:20190325174321j:plain
こんにちは!DSOCの西田です。
最近、Cから始まる大好きなブランドと心中すると心に決め、Yから始まるブランドの服をたくさん売りました。今のところ、悔いはありません。


さて、今回はそんな強い信念を持ちながら挑んだイベントSIP2019の登壇レポートをお届けします!
SIP2019で私が携わったものは大きく4つあります!

  1. [Day1 Session] Think Different とは何か?
  2. [Day2 Closing Keynote Session] 知を融合せよ ~イノベーションを生む組織の法則~
  3. [Artwork] Dawn of Innovation
  4. [Booth] DSOCブース

2つのセッションと展示会場でのデータビジュアライゼーション「Dawn of Innovation」の展示とDSOCブースの準備です。
今回はセッション2つとデータビジュアライゼーションの作品について当日の様子をご紹介します。

続きを読む

テクノロジのゆくえ

CTO の 藤倉 です。

3 月 14 日、15 日に渡り、弊社主催のカンファレンスである Sansan Innovation Project 2019(以下 SIP)が開催されました。SIP は 2016 年から毎年開催されていますが、4 回目となる今年は初の 2 日間ということで、規模も内容もこれまでを超える盛大なものになりました。SIP では様々な分野の第一線で活躍されている方々をスピーカーに迎えています。今年もテクノロジに関するセッションが多く用意され、その中のいくつかを担当させてもらいました。

jp.sansan.com

2F-1 『これからの技術の進化』

二日目のランチセッションに、アマゾンウェブサービスジャパンの技術統括責任者をされている岡嵜さんとの対談をさせていただきました。このセッションでは、過去、現在、未来と話題を時系列で大きく三つに分けて、それぞれのタイミングにおける技術について話をしました。さすが、あの AWS の技術統括責任者をされている方だけあって、岡嵜さんからは非常に示唆に富むお話を聞くことができました。登壇者ではありましたが、私自身も大変勉強になりました。

f:id:s_yuka:20190327091953j:plain

この記事では、このセッションでの内容やそこからの気づきを書きたいと思います。

クラウドを振り返る

まずは過去。AWS が創業したのが 2006 年です。そしてちょうど今から 10 年前である 2009 年は、EC2 や S3、ELB、RDS など、Web アプリケーションを作るための主要なサービスが出揃っていました。そして待望の東京リージョンが開設されたのが 2011 年です。一方、Sansan は 2007 年に創業しました。当時はサービスのローンチにクラウドを利用するという発想がなく、当たり前のようにデータセンターを借りてオンプレミスでサービスを運用していました。

当時を思い起こすと、世の中では、そもそもクラウドを利用することの是非が議論されていたように思います。主な論点は経済的な問題とセキュリティでした。従量課金制ではコストが事前に決定できない、オンプレミスに対して運用コストが高い、セキュリティに不安がある、などなど。クラウドがまだ新しい技術であったこともあり、議論は活発でした。

現在これだけクラウドサービスが使われていることを考えると、上記のような議論は尽くされ、あとはユーザ企業のポリシーによって利用可否が判断できるようになっているということでしょうか。

現在

今日のクラウドサービスは、そのサービス数と内容が驚異的に進化しています。特に、先日リポートした AWS re:Invent を見ても、その規模には圧倒されるばかりです。

buildersbox.corp-sansan.com

re:Invent 2018 の会期中に発表された AWS の新サービスの数は 100 を超えており、中の方ですら追いつくのが大変だとのこと。私が特に注目したのは Ground Station や RoboMaker でした。

Ground Station は人工衛星からデータを取得するための地上局をフルマネージドサービスとして利用することができ、ハードウェアの運用を不要にするためのサービスです。また、RoboMaker は、ロボットのためのソフトウェア開発環境を提供し、開発からテスト、デプロイをするためのサービスです。

元々は Web サービスを構築するために提供されていた AWS ですが、もはや Web テクノロジだけに留まりません。昨今は Web テクノロジとアナログな世界とを繋げるサービスにも注目されていますが、AWS を使えばあらゆるサービス開発の手間が省かれていきます。

これからのエンジニアリング

クラウドサービスの発展によって、我々開発者はアプリケーション開発に集中することができるようになりました。これは一体何を意味するのでしょう。

物理レイヤーから OS、フレームワークまでもがクラウドで用意され、ブラックボックス化されていきます。そのおかげで、開発者はユーザにとって価値ある機能を効率よく開発することができます。これはつまり、多くの開発者は直接的にユーザ価値になるものを作らなければ存在が認められない世の中になってきているとも言えます。かつてはサーバのアーキテクチャを知っていることに価値がありましたが、今ではその知識が活きることは稀です。

先進的なテクノロジについても変化しています。ガートナーのハイプサイクル等でも表現されている通り、新しい技術には黎明期や最盛期、減衰期があります。このサイクルがとても短くなっているように思います。ディープラーニングや IoT、AR といった新しい技術も、登場から程なくしてクラウドによって誰でも簡単に利用できるようになりました。

クラウドの進化に伴って、開発者も進化していかなければならないのです。

© Sansan, Inc.