子供が急に海外で暮らすということ

やっぱり、インターネットは成功者の話ばかりではなくて厳しい現実の話もあった方がいいと思うので、我が家のことをもう少し書きます。

我が家は僕の海外赴任に家族全員がついてくる形でカリフォルニアに来ています。長女は渡米時4歳、次女は10ヶ月でした。僕がアメリカに来たかった気持ちを100とすると、妻は19、長女は2ぐらいです。パパは来たくて来てるのでつらいことはありません。妻は大変苦しみましたが、素晴らしい友人に恵まれてどうにか折り合いをつけてくれました。次女は元々バブーなのでまだ自我はない。問題は長女です。

長女は日本語の習得が早く、2歳ぐらいで流暢にしゃべりはじめ渡米時にはほとんど大人顔負けの語彙でした。これは大変喜ばしかったのですが、換言すればこの時点で日本語で確たる自我を確立したのだと思います。このせいかどうか分かりませんが、この年齢にしては意外なほどに新しい環境や言葉の違いに対する拒否反応が大きく、順応は困難を極めました。

娘はたまたまTK*1に該当したので渡米後ほどなくして現地校に入れました。これは賛否あると思うんですが、日本人がいっぱいいると却ってその子たちとつるんでしまって言葉を覚えないかもしれないなと思って割と躊躇なくそうしました。ところが娘の場合はこれは完全に失敗でした。娘は誰とも話せない孤独からずっとふさぎ込むようになりました。夜は毎日しくしく泣きました。これは一時的なものだろう、誰でも最初はこうだと思いましたし、実際に同級生の保護者にもそう慰められました。ところが娘は3ヶ月経っても半年たっても良くなることはありませんでした。今なお、土曜の夜からもう月曜日のことを考えて泣くのです。日曜日にすらなっていないのに。時々パニックになってどうしようもない時もあります。

親の都合で自分の意志とは関係なく人生を歪められるというのは親として慚愧に堪えません。娘はまだ耐え難い状況にNO!を突きつけ独りで飛び出す力を持たないのです。こんな残酷なことはない。

あるいは、ある日突然に峠を超えるのかもしれない。その分水嶺を越えたらあれだけ苦しんだ日々は過去のものとなり、この美しく自由な大地を受け入れるのかもしれない。でも"こちら側"にいる我々にはそれがいつなのか分からない。それは永遠に続くかもわからないのだ。

大丈夫!僕は、我が家は大丈夫だった!その言葉に悪意はないのはよくわかっている。だけどその度に、あなたはうちの娘が僕に泣きながら取りすがってどれだけ学校が孤独で苦痛に満ちている場所か訴えてくる姿を知らないだろうと肩が震えるのをどうすることもできない。誰の言葉もわからない、誰にも頼れない。言葉がわからないから休み時間は独りでフラフラ歩いて過ごしてるんだよ〜と言われたときの胸がつぶれる音が聞こえるんですかと。自分が無実の娘を罰しており、そのことで僕が罰されているような行き場のない絶望感です。

このエントリは誰かを呪うために書いたわけではなく、海外に出るというのはやはりそれ相応の覚悟と代償を伴うものだということを書き残しておきたかった。やはりうまくいくばかりではない。我が家は、本当に素敵な友人に恵まれ、会社の上司の心遣いに恵まれ、なんとかこれ以上の悪化がないように努めています。TKは所詮義務教育ではないので、心を病むぐらいならば日本語学校なりホームチューターなりを選ぼうと思います。

このエントリを読んで、やっぱり日本人が多い街に住んだほうがいいだとか、最初は日本語学校に入れようとか、そのいずれが言いたいわけでもないです。だってこれは100%人によるのだから。だからこそ、それは事前にシミュレーションゲームのように完全に対策可能なものでもなければ後から簡単に修正可能なものではない、現実はかくも度し難いものなのだということだということぐらいが教訓だと思います。

胸くそ悪いものを読ませてすみませんでした。僕はこの世で一番美しい娘を抱きしめて眠ります。

*1:保育園の年中に相当する年次だが小学校に通えるプログラム。Pre-Kとも。

年末のご挨拶と近況、あるいは機械学習のはじめ方

あ、退職エントリとかではないです。雑多な駄文をお許しください。

2018年を振り返る

3月にカリフォルニア州パロアルトのラボに異動になって早くも3/4年が経ちました。 自分としてはまだ1年経ってないのかという感じです。もっと長く居るような気もするが、来たばかりのような感じもある。 生活には完全に慣れました。結局の所、生きていくだけなら "You need a bag?" に "No" だけ言えれば何とかなります。

家族のこと、子供のこと

家族が適応に苦しんでいます。特に子供は、8月からTKという公立小学校の下部組織に通っていますが、予想を遥かに超えて心を閉ざしたままです。 娘はとてもシャイで、思ったことが口にできません。非常に端的に言うと、生きる力が強くありません。象徴的なできごとが幾つかありました。

ある日、先生が陪審員の義務で代わりの先生が来た日、普段とは違う教室に預けられた。 お昼になってみんなは弁当を取りに行ったが娘は「誰も教えてくれなかったから」部屋に残り、そのまま食事も摂らずに途方に暮れて過ごした。

またある日はバッグにフォークが入ってなかった。実際には底の方に入っていて気付かなかった。 娘は「手で食べるのはお姉さんじゃないから」そのまま諦めてお弁当を食べなかった。

自分だけ食事をとっていけないはずがない。隣の部屋に歩いていけばよいのだ。 フォークがなければ手で食べればよいのだ。どのみちおにぎりとタコさんウインナーとかしか入っていない。 食器がなければ "fork!" と言えばいい。"Help me!" でもいい。言葉なぞ重要ではないのだ。毎日毎晩教えた。それでもできない。

それでも娘の不安と孤独と理不尽を思うと怒る気にはなれません。こんな目に遭わせてすまなかったねと抱き合って泣くしかできません。 向いてない、向いてなかったんだ。この地には勝者しかいない。敗者は去ったのだ。我々は路傍の石にすぎない。

どうすべきか結論は出ておりませんが、会社が親身に相談に乗ってくれているのでもうちょっと色々考えてみようと思います。ありがたいことです。

仕事

僕のこの地でのミッションは、会社に新たな収益源の萌芽をもたらすことです。 サービスの詳細をまだ公開できないのが心苦しいのですが、前期はプロトタイプとして小さなウェブサービスをひと月ぐらいでひとりで開発しました。方法論だけ以下のエントリにまとめてあります。

fushiroyama.hatenablog.com

会社はこれを非常に高く評価してくれて、ボーナスをいっぱいくれました。本当に良い会社です。ありがたいことです。

その後一瞬モチベーションを失いかけたのですが、後述する機械学習のプロジェクトが始まってまたモリモリとやる気が戻ってきました。 いまは死ぬ前の山のフドウのような清らかな笑顔で仕事場に向かっております。

機械学習

実はこのエントリで一番書きたかったのがこれです。これも以前別エントリで書きましたが、僕はこの言い方は好きではないのだが、まあいわゆる文系出身でして、数学はIAIIBまでしかやっていません。それも大部分は忘却の彼方に置いてきました。その状態からとりあえず機械学習のプロジェクトを始めるまでの準備をここに残そうと思います。

以下、自分が現状認識している機械学習について持論を述べておりますが、これは大いに誤っている可能性がありますから、容赦なくご指摘ください。来春までのプロジェクトの糧にしたいです。

fushiroyama.hatenablog.com

まず、機械学習の目的なのですが、これが個人的にとても重要だと思っています。我々のプロジェクトの場合、機械学習を利用する目的は「よりユーザのコンテキストを理解した提案をするため」です。

昨今世間で言われる "AI" の大部分は、機械学習の「学習済みモデル」を指していると認識しております。 これは関数のようなもので、ある入力に対して直ちに結果を出力します。プログラミングアルゴリズムとの違いは、これが単純なif-elseによる分岐ではなく、膨大なデータを使った学習の結果できあがったある種のブラックボックスであるという点です。 この関数の出力が、人間が目で見て頭で考えて行うそれよりも高精度であることが機械学習を使う究極の目的のひとつです。

機械学習が得意なことは限られています。すなわち、

  1. 連続的な変化から次の結果を予測する。これが「回帰 (Regression)」
  2. いくつかある候補の中から、入力したものがどれに類するか判定する。これが「分類 (Classification)」
  3. 雑多なデータの集まりから(時には人間にもわからない)性質の類似を見つけ出し、自律的にまとまりをくくりだす。これが「クラスタリング (Clustering)」

これらが考えられます。

1と2は実際のデータと正解データを与えながら学習させ、最終的には未知なるデータを渡した時に正解を予測してもらうという使い方をします。これは教師あり学習と呼ばれます。 対して3は、人間にも正解が分からないがデータを繰り返し与えることで特徴の似通っている部分をもとに群を形成してもらい、未知なるあるデータがどの群に類するかを出力してもらって同じ群の商品をユーザにオススメするなどの使い方をします。これは教師なし学習と呼ばれます。

僕の会社は世界最大規模のニュースメディアカンパニーであり、最大の強みは膨大な量のニュース記事そのものです。 したがって僕らがやりたいことは、ユーザが読みたいであろう記事をいかにユーザにシームレスに提供するかというところに帰着します。 必然的に、3の応用系が僕のミッションとなりそうです。

機械学習の数学

ここからが本題なんですが、僕は機械学習のプロジェクトを始める前に理論寄りの勉強をしました。 これには理由がありまして、自分の頭で考えて試行錯誤するための自力が欲しかったからです。

もしやりたいことが「カリフォルニアに棲む全野鳥の詳細な画像認識」のようにハッキリと決まっており、そのための教師データ(野鳥の画像)もある場合は、理論などすっ飛ばしてとにかく機械学習のライブラリなりフレームワークなりの使い方のみ覚えた方が求める結果に早くたどり着けると思います。

で、理論を学ぶ場合はどうしても高校程度の数学を避けて通ることができません。 ただそれほど極端に恐れる必要はなく、ざっくりと「行列の内積」と「微分」がわかれば良さそうです。 ここでは自分が基礎知識を身につけるために使った教材を紹介したいです。

ゼロから作るDeep Learning

この本はマジで素晴らしいです。Deep Learning機械学習の手法のひとつで、敢えて誤解を恐れずに言うならば、教師データから最適な重みパラメータ(後述)を自動獲得することを目指した学習手法です。

この本の特徴は、特定の機械学習のライブラリやフレームワークの使い方の解説に主眼を置いておらず、NumPyという行列の計算を便利に行うためのライブラリとPyplotというグラフ描画用ライブラリを用いる以外はもっぱら愚直に機械学習で利用する関数を理論に基づいて実装していくというスタイルをとっていることです。

はじめはパーセプトロンという、複数の信号を受け取ってひとつの信号を出力する回路を実装します。 信号(たとえば電流を想像してください)は、重みと呼ばれる信号を増幅/減退させるための係数を掛けて、それが閾値を超えると1、それ以外は0を出力するというような極めてシンプルなものです。 これを使って、プログラマなら誰でも理解できるANDゲートやORゲート(論理回路)を実装します。 次に、シンプルな論理回路を組み合わせてXORゲートのような非線形回路を実装し、ニューラルネットにつなげます。

それからニューラルネットワークという、パーセプトロンの発展形を実装していきます。ニューラルネットワークでは信号の出力部分を司る関数(活性化関数といいます)にシグモイド関数などのなめらかな変化を表現する関数を用い、単純な01の信号から連続的な実数の変化を出力できるようになります。 ディープラーニングとはこのニューラルネットワークを多層に重ねて学習をさせることで精度を上げる手法です。

ここで必要となる数学の知識は、たかだか指数関数の値の変化だとかその総和であるとか総和に対する割合であるとか、いずれも四則演算であって何ら恐れる必要がありません。行列の内積も、結局の所データをベクトルとして表現して各ニューロンを通る際に次元を合わせなければならないという程度に過ぎず、かしこまって高校の教科書を取り出さなくても次のウェブサイトを参照するぐらいで十分です。

atarimae.biz

この本ではその後も、新しく出てくる知識はとくに外部情報に頼ることなく解説を読みながら進められる程度の難易度でずっと進んで行きます。 損失関数という、ニューラルネットワークが手掛かりにしながら適切なパラメータを探るための関数を自分で実装しながら二乗和誤差を使った最小二乗法(最近話題になりましたね)などを学びます。

さて、この辺りで微分が出てきます。 機械学習では学習の際に最適なパラメータを探索していくんですが、このときに各データがどういう風に変化していくかをその瞬間の変化量を元に求めます。 これこそが微分であり、それ以上でもそれ以下でもありません。

僕はどれだけ思い出しても高校時代にまともに微分をやった記憶がなく、正直イチからの勉強になりましたが、これについてもこの本の解説がとても丁寧なので特に難なく読み進められると思います。たとえば、次は本書で紹介される数値微分Pythonでの実装例ですが、

def numerical_diff(f, x):
    h = 1e-4  # 0.0001
    return (f(x + h) - f(x - h)) / 2 * h

これって、第一引数に関数を、第二引数に何か変数を取ってすごーーーーーく小さい値を加えつつ実行したときの差分を、その小さな値で割った(つまりその小さい値の範囲内での変化量。ただの比)だけですよね。プログラマだからこそ、教科書にある lim -> 0 みたいなのより分かりやすいと思うんです。

指数関数の微分公式とかも突然当たり前の知識として出てくるんですが、これも次のリンク等を見ておけば十分です。

atarimae.biz

あとは、勾配法、微分を効率的に行う誤差伝播法、畳み込みニューラルネットワークと発展していきます。 この本では主にMNISTという手描き数字のデータセットを使った画像認識を扱っていますが、続刊では自然言語処理も扱っているようです。 とにかく、最初に読む一冊として強くおすすめします。

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「超」入門 微分積分

先述のとおり、個人的には「ゼロから作るDeep Learning」を手を動かしながら読んでいけば、機械学習をやるための数学を別途やる必要はそれほどないように思っていますが、夜に補完的に読んでいる本を紹介します。

「超」入門 微分積分 (ブルーバックス)

「超」入門 微分積分 (ブルーバックス)

これは中々面白い本で、微分積分を小中学校で学ぶような図形の面積の求め方といったイメージしやすいアプローチで解説してくれる本です。

特徴的なのは、まずは積分から解説している点です。筆者の主張によると、積分法の方がずっと古代から成立したものであり、それが身近なものの面積や体積の近似を簡単に求めるための実用的な用途であり、図形から視覚的に理解しやすいだろうという考えのようです。 実際に三角形や長方形を使って半球やトーラス体などの面積や体積を求めますが、小中学校の知識から極限に関する洞察などをわかりやすく与えてくれます。

後半では期待する微分の解説ですが、これも可能な範囲で極力二次元三次元図形を使った視覚的にイメージしやすい方法で変化量を解説してあり、微分の各種公式がどうしてそういう形なのかも暗記ではなく理解から導出できるような作りになっています。 これを読む前と後で機械学習の勉強に対する貢献があったか否かはちょっと判断がむずかしいですが、増減表とかは僕は知らなかったので今後高校数学をやる上ではとても良かったです。それに、何よりの貢献はやはり微分に対する恐怖が減ったことかな。

やさしく学ぶ 機械学習を理解するための数学のきほん

それから、これは紹介するかちょっと迷ったんですが一応読んだので紹介しておきます。 機械学習で使われる用語や理論を分かりやすく解説してあるという点で、前出の「ゼロから作るDeep Learning」に小さい重みを、「超入門 微分積分」に大きい重みを掛けてシグモイド関数に通したような本ですが、女の子二人の会話形式で進んでいく体裁が好みの分かれるところです。ハマる人にはハマると思うので書店で見かけたら試し読みしてみてください。

今後

このエントリで書いた内容はあくまで機械学習プロジェクトの準備体操であって、恐らく現実世界の課題を解決するために想像を超える艱難辛苦が待ち受けていることは想像に難くありません。年明けからは外部の協力会社さんにエキスパートアドバイザーとして入ってもらいながら、いまやってるプロジェクトの最適なレコメンデーションエンジンを実装予定です。

重ね重ね、会社にはこのAIの時代に自分に1Qを投資して機械学習のプロジェクトをやらせてもらえることを感謝します。 よい結果を出すことが会社への最大の恩返しだと思うので、気合を入れて頑張ろうと思います。

それでは少し早いですが、よいお年を。白山、タイキック。

ヨセミテ国立公園探訪記

妻子がThanksgiving Holidaysで一時帰国したので、同僚とヨセミテ国立公園に行ってきました。

f:id:fushiroyama:20181119062946j:plain
Taft Point

いわゆる「ヨセミテ国立公園」は広範な土地を指しており、今回我々が拠点にしたのはYosemite Valleyと呼ばれるアクセスもよく見どころが程よく集まっている辺りです。

f:id:fushiroyama:20181119064033p:plain

僕が住むカリフォルニア州BurlingameからはSan Mateo Bridgeを通ってEast Bayを抜け、ほんの4時間足らずで行くことができます。

f:id:fushiroyama:20181119064246p:plain

道中サービスエリアのようなものはなく、ランチやトイレ、給油などは適宜高速をおりつつ摂る必要があります*1

f:id:fushiroyama:20181119064611j:plain

f:id:fushiroyama:20181119064637j:plain

今回の最大の懸案事項は雪とタイヤです。年によっては11月の後半は雪が降っていることもあるらしいのですが、妻帰国のタイミングでSUBARU Outbackを修理に出すことが既に決まっていたので、HONDA Insightという小さいハイブリッド車を使うことを余儀なくされます。

チェーンを買うかどうか検討しましたが、Instagram等で軽く検索しても雪の形跡は見られないのでそのまま行くことにしました。最悪、道中のコンビニ的な場所でも手に入るようです*2

Yosemite Valley

途中山中のワインディングロードをグネグネと走る以外はひたすらまっすぐ広大なアメリカの大地を4時間弱走りつづけます。ポプテピピックのPoppy Pappy Dayを爆音で鳴らしながら爆走すると気持ち良いでしょうね!

言い忘れたけどYosemite Valleyに入ると手持ちのT-Mobile回線は半分以上の場所で電波を拾えなくなるので、事前にGoogle Mapでサンフランシスコ半島〜ヨセミテ国立公園全体のデータをダウンロードしておくことをおすすめします*3。仮に車にカーナビが内蔵されている場合でも、トレッキング時に紙の地図と現在位置を照らし合わせるのに大変重宝するので強くおすすめします。

ヨセミテ国立公園に入る際に、入山料のようなものを$35支払います。その後どんどん進むと最初のビューポイント「Tunnel View」にたどり着きます*4

f:id:fushiroyama:20181119072827j:plain
Tunnel View

さらに進むとMacの壁紙で有名なEl Capitanが眼の前に近づいてきます。想像以上にMacですね!

f:id:fushiroyama:20181119073844j:plain
El Capitan

Lodge

我々の宿はYosemite Valley Lodgeという小さなロッジが集まった村のようなところです。2泊の2 Bed Roomで$500ほど。 ロッジからはYosemiteの山々をすぐ近くに見上げることができ、また敷地内では鹿に遭遇するなどYosemiteならではの体験ができます。

f:id:fushiroyama:20181119074511j:plain

f:id:fushiroyama:20181119074550j:plain

f:id:fushiroyama:20181119074619j:plain

ロッジにはレストランも2, 3個併設されており、部屋には清潔なシャワーもトイレも付いていて子連れにも安心な感じでした。 もっと裕福な人達は近くにMajestic Yosemite Hotelという高級ホテルがあり、また、もっと自然を楽しみたい人はキャンプができるような場所もたくさんあります。

Glacier Point

Yosemite Valleyの見どころのひとつ「Glacier Point」
その名のとおり、氷河の雪解け水が山を削ってできた壮大な景色が堪能できます。

ここで重要な決断が。Yosemite ValleyからGlacier Pointに行くには、

  1. Lodgeから車で10分ほどで麓まで行ってFour Mile Trailheadから高低差1000mを一気に登る
  2. Lodgeから車で60分ほど大きく迂回して下から直接Glacier Pointに至る

の2つがあります。1000mを4マイルで一気に駆け登るのは初心者向きではないとのことで、後者にしました。

f:id:fushiroyama:20181119075956j:plain

このルートだと子供連れでも車でそのままGlacier Pointの展望台近くまで行くことができます。

f:id:fushiroyama:20181119081935j:plain
Glacier Point

f:id:fushiroyama:20181119081819j:plain

f:id:fushiroyama:20181119081847j:plain

Sentinel Dome Trailhead

このルートを選んだ最大の理由が、Glacier Pointから南にすぐの「Sentinel Dome Trailhead」のトレッキングです。これが素晴らしかった。

ここはSentinel Domeの名のとおり「見張り台」のようになった小高い丘というか岩山に登ってこの辺りを360℃見渡すことができ、そのままTaft Pointというこれまた絶景を拝むことができるトレッキングルートです。大人の足で2, 3時間ほど。小学生以下の子供はやや厳しい。

f:id:fushiroyama:20181119082523p:plain

f:id:fushiroyama:20181119082947j:plain
コーヒーを沸かしてくれる同僚

f:id:fushiroyama:20181119083052j:plain

f:id:fushiroyama:20181119083102j:plain
Sentinel Dome

f:id:fushiroyama:20181119083150j:plain

f:id:fushiroyama:20181119083202j:plain

f:id:fushiroyama:20181119083242j:plain

f:id:fushiroyama:20181119083330j:plain
Taft Point

f:id:fushiroyama:20181119083350j:plain

Taft Pointの絶景は筆舌に尽くしがたいです。本当に美しいものはカメラにすら収まりきらない。自然の力に涙が出てきます。

Mariposa Grove

Glacier Pointを下から回ったので、そのまま戻らずに「Mariposa Grove」というセコイアの巨木群を見ることのできる森を目指します。

f:id:fushiroyama:20181119083902p:plain

Sentinel Dome Trailheadには食事ができるところが何もなかったので、道中Wawona Visitor Center横のホテルのレストランに立ち寄る。

f:id:fushiroyama:20181119084119j:plain

Mariposa Groveは駐車場のキャパの関係で入り口を封鎖されており中に入ることができなかったので、そこから15分おきに出ている無料シャトルバスを利用します。シャトルバスは5pmで営業終了なので注意。着くとそこはシシ神の森のような巨木の世界。

f:id:fushiroyama:20181119084700j:plain

f:id:fushiroyama:20181119084656j:plain

f:id:fushiroyama:20181119084719j:plain

f:id:fushiroyama:20181119084748j:plain

f:id:fushiroyama:20181119084817j:plain

f:id:fushiroyama:20181119084850j:plain

f:id:fushiroyama:20181119084913j:plain

f:id:fushiroyama:20181119084934j:plain

太い木というよりは、電車とかビルとかそういうレベルのバカでかいセコイアの木々は圧巻でした。 トレッキングルート自体は至って穏やかで、子供連れでも楽しめます。

Mist Trail

最後はYosemite Valleyでもっともメジャーな観光スポットのひとつ*5「Mist Trail」

ここにはよい駐車場がないのでYosemite Valleyを巡回している無料シャトルバスに駅番号8番Lodgeから乗り、16番のMist Trailで降りることでいくことができます。

このトレッキングルートには滝が2つあり、Mistの名が指すとおりその滝の水しぶきで体が濡れるようなルートとのこと。残念ながら乾季の最後であるこの時期は体が濡れるようなことはなかったが、滝に水は残っており大満足。

f:id:fushiroyama:20181119085645j:plain

f:id:fushiroyama:20181119085905j:plain

f:id:fushiroyama:20181119085746j:plain

f:id:fushiroyama:20181119085914j:plain
Vernal Falls

f:id:fushiroyama:20181119090228j:plain

f:id:fushiroyama:20181119090248j:plain

f:id:fushiroyama:20181119090237j:plain

f:id:fushiroyama:20181119090247j:plain
Nevada Fall

f:id:fushiroyama:20181119090455j:plain

所感

Yosemite Valleyは車で有名なスポットを回って写真を撮るだけでは「Macの壁紙で見たことある」以上の感想を持つのは難しいかもしれませんが、トレッキングをすると本当に心から素晴らしいです。50万円のカメラを持っていっても、あの大きさと、高さと、奥行きと、匂いと、空気とをすべて収めることは無理です。なので、お子さん連れの方もぜひやさしいルートでもいいのでトレッキングに挑戦してみて欲しいです。

費用はガソリン代$45, 橋代$5, 入山料$35, 宿泊が$500を2で割って$300いかないぐらいでした。ここに食費ですね。思ったよりずっとリーズナブル!

雪のリスクがある11月の後半、それも平日に行ったということもあり、どこに行っても非常に空いていて快適でした。サンフランシスコ・ベイエリアはいま未曾有の空気汚染に苦しんでいるので、つかの間の癒しとなりました。

ヨセミテ国立公園は信じられないほど広く、今回の旅だけで到底回りきれるものではありません。カリフォルニアに少しでもいられるうちに定期的に開拓していきたいと思います。

*1:フリーウェイが終わりYosemite Valleyに近づいてくると道端にもチラホラある。ただしValley内に入ると給油場所がないので注意する必要がある。

*2:実際に途中で水を買うために道路脇の店に寄ったが、キャンプ用品やカーメンテナンス系も充実していた。

*3:Google Map ダウンロード」とかでググってください。

*4:正確には往路を少し逸れる必要があるので完全な道中ではない。

*5:シャトルバスの運転手のおじいちゃんのアナウンスより。

神様も知らないおしりの秘密

せなかポリポリ

さいころ寝るときによく母親が背中を掻いてくれた。これは背中がかゆいから掻いてもらうのではなく、いわばスキンシップの一環だったのだと思う。

妻と新婚時代、寝転がっているときに背中をポリポリしたら「なにこれー!きもちいい〜☺️」と言うのを聞いてむしろ驚いた。親愛の証としての背中ポリポリは普遍的行為ではなかったのだ。

僕は妻にそうしたように、娘にもこの毛づくろい行為としての背中ポリポリを施した。僕が母にそうされたように、娘は背中にたっぷりと父のポリポリを受けて育った。

おしりポリポリ

そのうち娘が「パパー、『おしりポリポリ』して〜」と要求するようになった。おしりポリポリとはなんだろうか、おしり探偵なら知っているかもしれない。 うつ伏せに寝る娘のズボンに手を突っ込んで背中を掻く要領でポリポリしてやると「きもちいい〜☺️」とのこと。正解だった…

以来、娘は夜寝るときに「背中ポリポリ」「おしりポリポリ」「足の指ポリポリもみもみ」の3点セットを必ず要求するようになった。特に母親は「おしりなんて掻かないよっ!」とにべもないので、娘は必ず僕と寝るようにすらなった。

行き過ぎたおしりポリポリ

そのうち娘はリラックスすると公共の場でもいきなり寝転がっておしりをぺろーんと出して「パパおしりポリポリ〜😆」などと言うようになった。これにはさすがにひっくり返りそうになった。4歳児には家の中と外の区別がいつもつくとは限らないのだ。

ひとまず、おしりを人前で出すことは恥ずかしいからやめようということになった。そしたら、「ここはパパしかいないから大丈夫だよね?」とこう来る。困った。少々苦しいが「神様が見ている」ということにした。神様が見ているからお尻を出してはいけない。

ただし例外を設けよう。お布団の中に入っているときだけは神様にも見えない。夜、本を読んで、電気を消して、布団をしっかりかぶって、そのときだけおしりをぺろーんと出してポリポリしてもOKにしよう。それが妥協点だった。娘はおしりにたっぷりと父のポリポリを受けて育つことなった。

神様が見てるーっ!

高熱が出て学校をお休みした火曜日の昼間。娘はパパの書斎のゲストベッドで電気を消して昔話の音声を聞きながら静かに過ごしている。 まだ熱があるので部屋を暖めて布団をかぶらずに寝転がっている。

「…パパー。寂しい。こっちきて」
「…パパー。おしりポリポリして」

僕は笑いながら添い寝してやっておしりをぺろーんと出してポリポリしてやる。

「パパーッ!だめーっ!急いで布団をかけて!神様が見てるーっ!」

そうだったそうだった。でも多分大丈夫だよ、今日も山火事の煙で空は曇ってるからたぶん神様にも見えてない。 ゆっくりやすみなさい。

子供を現地病院に連れて行った

TL;DR

はじめに、このエントリは情報提供のために書いたものではなく単なる日記です。 知りたい肝心の内容が抜けていても怒らないでください🙏 それから医療用語は僕がテキトーに訳している部分があるのでご指摘ください。

サンフランシスコ・ベイエリアで日本語の通じる病院

この地で暮らしていると、いかに僕が健康であろうとも*1子供が学校に行くための予防接種(Immunizaion)等で病院に行く必要があります。
カリフォルニア州は公立学校に通うためのImmunizationに厳格で、基本的に個人の信条や宗教上の理由による予防接種の免除(Exemption)を認めていません*2
これは子を持つ親として大変心強く、日本もこうすればよいのにと思うのですが、日本できっちり予防接種を受けていてもまだいくつか足りないぐらい接種が厳格です。うちの子も入学前に駆け込みで5本も追加接種する必要がありました。

接種の記録(Immunization Record)は、なんだか黄色い所定の紙に州の認定医によるサイン付きで記録してもらい、各学区に提出する必要があります。 うちはSan Mateo市内にある日本ベイクリニック(Nihon Bay Clinic)という、お医者さまやスタッフのみなさん含めて完全に日本語の通じる病院にお世話になっています。
小児科の矢野先生と柏先生はご両名とも素晴らしい方で、非常に信頼しています。院長の紀平先生もホームドクターとして急を要さない日常のトラブルを全般的に診てくださいます。

はじめての現地病院

で、ここからが本題なんですが、次女の1歳の検診で心雑音(Heart Murmur)が疑われるので専門医に診てもらうように言われました。
これはどうしたって大ごとです。普段日常生活の英語にも事欠いているのにいわんや医学の専門用語をや。しかし娘の健康に関わるので必死です。紹介状を持ってMichael Griffin, MDを訪ねました。

カリフォルニア州はどうも、法律で通訳をつけてもらう権利が保障されているようです。

受付で通訳をつけてもらうことは可能かお願いしたのですが、僕の保険ネットワーク(Aetna)*3では通訳をやったことがないからまずは保険会社に電話して、専用の電話番号をもらったらそれを伝えてくれたら対応すると言われました。

出社前に病院に来ていたので急いでいたのと、まあ僕はそれでも単語がわからなくても聞き直しながらやりとりする自信があったのでそのまま通訳なしで診察に挑んだんですが、これは受付の対応が微妙っぽいので妻と一緒に行くときはちょっとしっかり準備して保険会社にも連絡した上で臨もうと思いました。
ちなみにここは受付でクレジットカードをSecurity PIN込みですべて紙に書いて提出するように言われたので、職業上ちょっと抵抗があるとごねたらPINだけは書かなくていいよ!とか言われたのでそれで妥協するという一幕がありました。Dr. Michael Griffinは素晴らしい先生だっただけに残念。

Innocent Heart Murmur

次女は検査の結果、Innocent Heart Murmur(良性の心雑音)とのことで、現状深刻なものではないそうです。

f:id:fushiroyama:20181109051735j:plain

今回は幸い、「心雑音」についてある程度知っていたのと、説明されたときの語彙も日常的なものだったので特に通訳なしで完全に意思疎通ができました。
具体的には、右心房・右心室から肺動脈に向かう部分が狭く、"Velocity*4が足りない" が、これは赤ん坊の個体差の範疇だし、肋骨もまだ小さいからだろう。2年後にまた経過観察しよう。先天的な心臓の疾患を疑う所見はない、とのこと。

うーん、この日記は特にオチはないんですが、次女が無事で良かったなあというのが1点。
もう1点が、英語には聞き逃して良いものとダメなものがあるということですかね。これは仕事でもそう、というか日本語でもそうだと思うんですが、やっぱ病名とかその状況は理解できないままやり過ごすわけにはいかなくて、すごい集中して聞く必要があるなぁと。そして聞き取れなくてもしつこく食い下がってハッキリさせる必要がありますね。
僕がこの地に来て一番感心したのが、カリフォルニア州のサンフランシスコ・ベイエリア周辺は特にそうだという認識なんですが、とにかく移民のもとに成り立っているからみんなちゃんとした英語しゃべれないことに寛容で、こっちがどれだけヘタクソでもちゃんとコミュニケーションを取ろうと歩み寄ってくれるんですよね。それからどんなにアホみたいな質問をしても誰も呆れたりしない。カンファレンスに行くと、マジで

「なにか質問は?」
「素晴らしい発表をありがとう!君の発表によると『バナナはおやつに含まれる』という理解なんだけと合ってるかな」
「いい質問だね!そのとおり!バナナはおやつに含まれる」

みたいなそれもう質問というか挨拶だよねみたいなやりとりを何度も目にする。これは架空の会話だけどこの感じめっちゃ伝わると思う。

最後ちょっと無茶苦茶になったけどいい経験になりました。次回、通訳をうまくつけられたら追記します。じゃあの。

*1:ただし痔を除く

*2:SB-277. 2016年11月1日発効。

*3:アメリカの保険にはネットワークというものがあって、保険会社が加入しているのと同じネットワークの病院で診察を受けないと保険が下りないか非常に高額という仕組みになっている

*4:単位時間あたりに流れる速さ。実は音楽用語でもこのvelocityって使うんです

よりぬき「Androidテスト全書」さん

TL;DR

かねてより執筆中であった「Androidテスト全書」をついに出版しました。

Androidテスト全書

Androidテスト全書

  • 著者: 白山 文彦,外山 純生,平田 敏之,菊池 紘,堀江 亮介,
  • 製本版,電子版
  • PEAKSで購入する

我が国のAndroid史に残るほど良い本に仕上がったと思います。Androidのテストのみにフォーカスした本は日本では類を見ないと思いますが、2018年時点でのUIテストとCI/CDの実践的な知見まで含めると、世界でもここまでまとまった本はないんじゃないでしょうか1。 まだテストがなくこれから増やして行きたい現場や、新人教育に時間を割けない会社など、ぜひこの本を買ってAndroidのテストにチャレンジしてみてください!

それから、これはとても重要なことなのですが、出資者のみなさまで内容にご満足いただけた方はぜひ紹介コードを使ってお知り合いに薦めてあげてください。キャッシュバックがあります2。 これはまだプロジェクトが成立するかどうかも分からない段階から我々を支援してくださったみなさまだけの権利です。みなさまのご支援なしに我々の執筆はありえませんでした。心から感謝します。

さて、このエントリでは自分の担当した章からいくつか見どころを紹介したり、ボツネタを供養したり、思い出話を焚き上げたりしようと思います。お付き合いください。

Overview

購入を検討してくださっている方のためにざっと全体を俯瞰します。 すべてを紹介したいのですが、エントリが長くなりすぎるため自分の担当章のみに留めます。その他の章はそれぞれの著者が解説エントリをかいてくれるでしょう。

第1章「テスト入門」

本書はLocal Unit Test3からUIテストを含むInstrumented Test4まで、単に「テスト」と言っても広大な範囲をカバーしています。 したがって1章ではまず本書における用語の定義と整理をしています。

それからテストを書くことが品質を上げるのみならず、結果的に開発スピードまで上がってしまう話をしています。 スタートアップなんかでありがちなんですが、「スピード優先で」テストを書かない現場もあると思うんですけど、これは個人的には疑問に感じています。 なのでボスが上記のようなことを言い出したらAndroidテスト全書にはテストを書いたほうが早くできるって書いてましたけど」とか言ってやってください。

第2章「ユニットテスト実践入門」

生まれて始めてAndroidユニットテストを書く人のために、JUnit 4とHamcrestを使ってテストを書く方法をごくごく簡潔に解説しました。 それから、AssertJを使ったアサーションも簡単に解説しています。なぜTruthやAssertK、Expektじゃないんだ?というような話にも触れています。

2章の目玉は「テストダブル」です。次のようなテスト対象クラスを用意して、「スタブ」「モック」「スパイ」を自作しながらそれぞれの使い分けを解説しています。

class WeatherForecast {
  val satellite = Satellite()
  fun shouldBringUmbrella(): Boolean {
    val weather = satellite.getWeather()
    return when (weather) {
      Weather.SUNNY, Weather.CLOUDY -> false
      Weather.RAINY -> true
    }
  }
}

自作したテストダブルは結局Mockitoで置き換えるのですが、どうしてこれをわざわざ自作してまでみっちり解説したかというと、テストダブルの正しい定義と使い方を身につけていれば仮にMockitoが廃れた世界線でもこの知識は活かせると信じたからです。

本章の草案ではテストダブルの説明そのものも意図的にMockitoの実装に寄せていたのですが、アーリーアクセスのKazuCocoa氏のご指摘を受けてxUnit Patterns.comのTest Doubleに準拠するように書き直し、サンプルコードも全面的に改めました。とても良い章になったと思います。

第3章「ユニットテスト応用編」

前章が意図的にAndroidフレームワークに依存しないモジュールのユニットテストだったのに対し、本章ではまずAndroidフレームワークのコードを利用したモジュールのテスト方法を解説しました。それから、これまでの内容を総合して「現場で使えるテクニック」をいくつか厳選して収録しました。 普段開発していて自分の中に溜め込んだAndroidのテストの知見というのはそれこそ大小さまざまに数えきれないほどあり、すべてを解説するのは無理というものです。したがって自分でうんうんうなったり、Twitterでアンケートを実施したりしてテーマを絞りました。そのうち2つを紹介します。

ひとつは非同期処理のテストです。ExecutorService を使った次のようなモジュールのユニットテストの方法を解説しました。

class StringFetcher {
  fun fetch(): String {
    Thread.sleep(1000L)
    return "foo"
  }
}

class AsyncStringFetcher(val fetcher: StringFetcher) {
  val executor: ExecutorService = Executors.newCachedThreadPool()
  fun fetchAsync(onSuccess: (value: String) -> Unit,
                             onFailure: (error: Throwable) -> Unit) {
    executor.submit {
      try {
        val value = fetcher.fetch()
        onSuccess(value)
      } catch (error: Throwable) {
        onFailure(error)
      }
    }
  }
}

ここでは CountDownLatch を使った方法に留まらず、もう一歩先に進めたアプローチも紹介しています。 非同期処理ライブラリも栄枯盛衰が激しく、何かひとつに絞って解説しても廃れたらどうしようもないというのは明白です。従ってここでも「非同期処理はどうしてテストがしづらくて、どういう考え方を身につけていればこの先新しい非同期処理ライブラリが出てきても工夫してテストが書けるのか?」ということを意識しながら書きました。詳しくは本編で!

もうひとつは「テストのないプロジェクトにテストを導入する」と題して、テストコードがない現場でいかにも目にするような悲しみに包まれたJavaのコードを題材にして、少しずつ改良しながらテストを書いていく節です。次のようなクラスを少しずつ変えながらテストを書いていきます。

public class LegacyCode {
    private LocalDataFetcher localDataFetcher = new LocalDataFetcher();
    private RemoteDataFetcher remoteDataFetcher = new RemoteDataFetcher();
    void loadData(String param, Context context, Callback<OldData> callback) {
        OldData result;
        if (NetworkUtils.isOnline(context)) {
            result = remoteDataFetcher.fetch(param);
        } else {
            result = localDataFetcher.fetch(param);
        }
        callback.onSuccess(result);
    }
}

どうでしょうか、ワクワク(げっそり)しませんか?こちらもぜひ本編を読んでいただきたいです。

その他の章は他の著者の方に譲るとして、本書は特に日本語でまとまった知見の少ないUIテストのノウハウががっつり3章に渡って収録されているかなり貴重な書籍です。 ぜひお求めいただけると嬉しいです。

この本に書かなかったこと

先程も書きましたが、この本を書くにあたって収録しきれなかったネタは山程あります。 アサーションにしてもモックにしても応用編にしても、構想段階や執筆段階で倍ぐらいあったものを削りに削っていまの形になりました。著者としてはコンパクトな文量ですべてを収められなかった悔しさというのはどうしてもあります。

例えば、 AsyncTaskAsyncTaskLoader のテスト方法は敢えて収録しませんでした。理由は本当にいくつもあるのですが、これらのモジュールがかつて程は用いられてないことや、 Activity と密結合していてどの章のどの節で解説すべきなのか、それも Robolectric を使って Local Unit Test で無理やり解説すべきなのか、 IdlingResource を駆使して Instrumented Teset として解説すべきなのか…ね、悩ましいでしょう。それよりかは、非同期処理の本質に迫るような解説にするに留めました。

それから非同期処理でもちゃんとエラーを通知できるJUnit Rulesなんていうのも最初書いてたんですが、これもカットしました。ここで供養しましょうか。

class MultiThreadFail : TestRule {
  val errorRef = AtomicReference<AssertionError>()

  fun fail(message: String) {
    errorRef.set(AssertionError(message))
  }

  override fun apply(base: Statement?, description: Description?): Statement {
    return object : Statement() {
      @Throws(Throwable::class)
      override fun evaluate() {
        errorRef.set(null)
        base?.evaluate()
        errorRef.get()?.let { throw it }
      }
    }
  }
}

class TestClass {
  val onSuccess: (value: String) -> Unit = { _ -> rule.fail("ERROR") }
  val onFailue: (error: Throwable) -> Unit = { _ -> rule.fail("ERROR") }

  @get:Rule
  val rule = MultiThreadFail()

  /* ... */
}

こんな風にしておけば非同期処理のコールバックの中からでもアサーションを失敗させられます。 ただ、テストケースからルールにアクセスするのはキモイとか色々査読コメントをいただき、収録しませんでした。

他にも、実際に解説したライブラリひとつ取っても、便利ではあるんだけどマニアックすぎるメソッドは解説を断念したものも多数あります。 Parcelable のテストも ThreeTenABP のテストも SharedPreferences のテストもみんなみーんな書きたかった。本を書くというのは本当に難しい。

思い出話

さて、この本は元々僕が書きたい!と言って書くことになりました。 なので他に僕よりも優れた著者陣が多数参加してくださったにも関わらず、筆頭著者として本当に色々好き勝手やらせていただきました。 たとえば、ある著者とのやり取りでは「めっちゃ内容は良いんですが前戯が長いのでヌける部分を手っ取り早くください!」みたいなクソ失礼な注文を付けたりしたのですが、最終的に素晴らしい玉稿5をあげてくださいました。

優秀な著者のみなさま、編集長のひつじさんとPEAKSの永野さんには感謝してもしきれません。 特にひつじさんの編集は本当にびっくりしました。空間認識能力が違いすぎるとでも言うのでしょうか、他人の書いた膨大な長さの原稿を一瞬で全体像を把握して適切な長さに圧縮する力は脱帽としか言いようがありません。凄いものを見ました。本当にありがとうございます。何もかも、とても良い思い出になりました。

そして何よりも、プロジェクトをサポートしてくださったみなさんに厚く御礼申し上げます! このエントリを読んで興味を持ってくださった方、きっと損はさせません!買ってください!宜しくお願いいたします🙏

最後に、やっぱり妻と子供にはお礼を言わないわけにはいきません。どれだけの休日を犠牲にしたかわかりません。 異国の地に引っ越してきたばかりで孤独と不安だったことでしょう。とても申し訳ない。

4歳の長女とはいつも一緒に寝ていたのだが、僕が執筆のために毎夜書斎にこもるようになってからは、書斎にあるゲストベッドに毎晩長女も泊まりに来るようになりました。毎晩しばらく一緒にベッドに入ってやって、おしゃべりして、背中をポリポリして、足の指をポリポリもみもみ6してやって、おでこにキスをしてプーさんを抱っこさせ、そのままパパは原稿に向かうというスタイルが確立されました。太陽のように美しい娘。しばらく余裕ができるので一緒に寝てやろうと思います。

なお、家族会議の結果「バンドマンの彼女が歌詞にされるみたいで嫌だ」ということで本のあとがきに「妻の○○○に感謝します」みたいな文言は入れないことが閣議決定されました。代わりにここに記すものであります。ありがとう。

結びに

DroidKaigi 2019にはプロポーザルが採択されてもしなくても日本に一時帰国して参加予定です! 「著者です」Tシャツを着て参加しているので声を掛けてください!

suzuri.jp

じゃあの!


  1. 冗談抜きでこの本は翻訳すると世界に通用すると思います。

  2. 紹介コードは購入ページから確認できます

  3. 実機を使わずに開発マシンやCIサーバだけで実行するユニットテスト

  4. 実機やエミュレータを使ったテスト。

  5. 「玉稿」って言い方、あるプロの編集さんとお仕事をした時に先方が使っててその字面の凄さに気に入ってしまいました。

  6. 「ポリポリもみもみ」は長女の命名。足の指をマッサージしてやったところ気に入って「足の指ポリポリもみもみして〜」と毎晩言ってきます。

Firebase Functionsでデプロイしているバージョンを表示したい

おっす!オラFirebase好き彦!

いまやってる新規サービスはFirebase Functionsを使って開発してて毎日のようにデプロイしてるんだけど、妙な挙動に遭遇したときにそれがどのバージョン*1のFunctionで起こったことなのか特定する必要が出てきた。Functionが実行されるときにそのバージョンがわかればいいな〜と思ったんだけど、環境変数等でパッと取得できそうになかったのでデプロイされているバージョンのコミットハッシュをファイルに書き出して読み込むようにしてみたぞ!もっと良い方法があったら教えてくれよな!

1) コミットハッシュを生成するnpm taskを追加する

一番安直にgitコマンドでコミットハッシュを取ることにした。

<!-- functions/package.json -->

"scripts": {
  "commit_hash": "echo `git rev-parse HEAD` > COMMIT_HASH"
}

npm run commit_hash で動作確認する。 COMMIT_HASHecho 'COMMIT_HASH' >> ../.gitignore とでもしておこう。

2) デプロイ時にcommit_hashを実行する

predeploycommit_hash を追記する。

<!-- firebase.json -->

"functions": {
  "source": "functions",
  "predeploy": [
    "npm --prefix \"$RESOURCE_DIR\" run build",
    "npm --prefix \"$RESOURCE_DIR\" run commit_hash"
  ]
}

--prefix \"$RESOURCE_DIR\" がないとプロジェクトルートの package.json からタスクを探そうとするので注意。

3) Functionから読み込む

あとはFunctionから読み出してログに書くなりレスポンスに含めるなり好きにできる。

import { readFileSync } from 'fs'

function readCommitHash(): string {
    const commitHash: Buffer = readFileSync('COMMIT_HASH')
    return commitHash.toString('utf8')
}

4) deploy

$ firebase deploy

いや〜Firebaseっていいですねっ!

*1:Function自体のバージョンではなく自分のアプリケーションのバージョンね