Sansan Tech Blog

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

チームに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 といった新しい技術も、登場から程なくしてクラウドによって誰でも簡単に利用できるようになりました。

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

言語処理学会第25回年次大会に参加し発表しました

こんにちは。 DSOC R&D グループの高橋寛治です。

2019年3月12日から15日にわたり開催された言語処理学会第25回年次大会に参加し、ポスター発表を行いました。

本記事は、参加報告および発表内容の紹介となります。

言語処理学会第25回年次大会

言語処理学会が開催している年次大会で、国内の言語処理の研究者が一堂に会する年に一度のイベントだと言っても過言ではありません。 毎年3月の2〜3週目におおよそ1週間にわたって発表や講演があり、また懇親会や若手の会など、勉強だけでなく研究者間の交流も深められる非常に有意義な会です。

私は学生の頃から参加させていただいており、今回は6回目の参加となります。 昨年度の奥田による参加報告はこちらをご覧下さい。

今年度も昨年に引き続きプラチナスポンサーとして、ブース出展やスポンサーイブニングなどでお話しする機会をいただきました。

f:id:kanjirz50:20190322135221j:plain

NLPは交流が盛んな学会で、今年ひときわ印象に残ったのは研究者間でのNLP Splatoonでした。 学会発表はもちろんのことですが、来年もNLP Splatoonには参加したいと思います。 (Splatoonについても皆さん研究熱心でした 笑)

f:id:kanjirz50:20190322140804p:plain

発表内容

「部署役職テキストの自動分割」というタイトルで発表いたしました。

研究の概要は、部署と役職に関しては構造化されておらず連続した文字列(以降、部署役職テキストと呼ぶ)となっているため、これを分割するというものです。 特別新しい手法の提案ではなく、部署役職テキストに対して分割を行った結果の報告です。

f:id:kanjirz50:20190322135635j:plain

よくある手法である系列ラベリングを用いて、部署役職テキスト中のトークンが部署か役職かをタグ付けします。 実際にはIOB2タグ(Inside-outside-beginning)と呼ばれるタグの推定を行います。 例えば、 B-部署 といったようなタグです。

結果、部署と役職が正確に分割されるのは 78% でした。

たくさんの人にお越しいただき、ポスター内容について議論することができ非常に良い機会でした。 特別な手法を使っているわけではないため、同様の手法で別タスクを解いたときの知見を聞けたり、CRFを使うのではなくルールベースでどこまでできるかなど、今後を考える上での様々な課題が浮かび上がりました。 現在の取り組みを共有することで様々な意見をいただけるのは、本当に嬉しく、学会に参加できてよかったと感じます。

f:id:kanjirz50:20190322135401j:plain

学会で発表するときっといい

学会でたびたびお会いすることのあるGunosyの関さんに「学会で企業研究をアピールしていこう!」と言われたことを非常によく覚えています。 今回の発表では、企業側が持つ課題を学術側にアピールできたのではないかと思います。

今後もこのような活動を続けて、産業界の課題とその向き合い方について発表していきたいと思います。

執筆者プロフィール

高橋寛治 Sansan株式会社 DSOC (Data Strategy & Operation Center) R&Dグループ研究員

阿南工業高等専門学校卒業後に、長岡技術科学大学に編入学。同大学大学院電気電子情報工学専攻修了。在学中は、自然言語処理の研究に取り組み、解析ツールの開発や機械翻訳に関連する研究を行う。大学院を卒業後、2017年にSansan株式会社に入社。現在はキーワード抽出など自然言語処理を生かした研究に取り組んでいる。

Webアプリケーションにおける正しいキャッシュ戦略

こんにちは。プロダクト開発部のサーバサイドエンジニアの荒川です。普段はSansanのスマホアプリのAPIの開発をしています。

今回扱うテーマは皆さん大好きキャッシュ(Cache) です。

Webアプリケーションを開発するエンジニアである以上、キャッシュの存在からは逃れられないでしょう。 例えばパフォーマンスを向上させる手段として、キャッシュを仕込むことは往々にしてあるかと思います。

キャッシュを使えばパフォーマンスが向上しそう、というイメージも強いため安易に選択する戦略になりがちですが、正しく扱うことは本質的に難しいです。 しかしキャッシュを上手に使えば、ユーザ体験を圧倒的に向上させることができます。

そんな諸刃の剣キャッシュ💰について考慮するべきこと、その戦略を改めてまとめてみました。

今回の対象

今回の対象は、アプリケーションレベルでのキャッシュ戦略を取り扱います。 いわゆるキャッシュメモリ、ブラウザのキャッシュ、HTTPにおけるキャッシュ、CDNにおけるキャッシュなどとは文脈上異なるので注意してください。

さて整理すべきは、クライアント(Client)、サーバ(Server)、キャッシュ(Cache)、データベース(Database)の4つです。 本エントリでは以下のアーキテクチャをベースとしていきます。

f:id:ad-sho-loko:20190322023513p:plain
アーキテクチャ図

システム構成はシンプルなクライアントサーバのWebシステムです。 説明の都合上、各種サーバの分散化や冗長化などは割愛しています。

その他の用語を軽く説明します。

クライアント(Client)は、Webアプリケーションにおいてはブラウザに該当します。

サーバ(Server)は、アプリケーションサーバです。キャッシュサーバやデータベースと通信をするためにアプリケーションコードが動いています。

キャッシュ(Cache)は、キャッシュサーバです。当たり前ですが、I/Oについてはデータベースよりも高速でなければいけません。

データベース(Database)は、基本的にRDBMSと理解してもらって問題ありません。またS3などのストレージを追加で置いてもらってもOKです。

続きを読む

© Sansan, Inc.