Y's note

愛と夢と人生について書きます

高く飛ぼうぜ

www.amazon.co.jp

海外ドラマ シリコンバレーを再度見ている。シリコンバレーとは音楽の著作権を検索するために、データの圧縮アルゴリズムを開発し、仲間と共にPied Piperという会社を起業をする話し。何気ないドラマのシーンやセリフがWebプロダクト開発や起業に携わったことがある人は共感できること多数という感じ。

シリコンバレー (テレビドラマ) - Wikipedia

今日はシーズン1の第1話、実用最小限の製品の中のやり取りで、データ圧縮アルゴリズムを開発したプログラマーでありPied Piperを起業するリチャードとそれを支えるインキュベーターのアーリック会話に注目したい。実現したいことシーズン1の第1話ではリチャードには大手IT会社に数百万ドルで買収されるか自分たちで起業をするかを迷い、勇気のある後者を選択している。

買収されると自身や開発に携わったメンバー、更にはインキュベーターのアーリックも富を得る形になるが、リチャードが自分で起業をするという思いをアーリックや他のメンバーに告げる。その話しを聞いたときのアーリックの発言。直近の富を取るより、ものを創りそれを見届けたいという思いでリチャードの意思に賛同する発言。(ちなみにアーリックは過去にアビアトというプロダクトを売却し富を得ているが、こころのどこかで後悔をしている) その発言を下記に記載。

  • アーリック : 「売らなかったらとかんがえちまう、心残りだ」
  • アーリック : 「だから言うよ お前は俺に似ている」
  • アーリック : 「何かを創り 見届けたいんだ」
  • リチャード : 「即金の100万を蹴っても許す?」
  • アーリック : 「許すどころか ワクワクしている」
  • アーリック : 「高く飛ぼうぜ パートナー」

f:id:yutakikuchi:20201005041758p:plain:w400

起業を選択したリチャードの勇気のある決断、それを勇気づけてくれたアーリック。アーリック最高かよ...。「高く飛ぼうぜ パートナー」このシーンを見ると何度も心を震わせられるので、第1話を是非多くの人に見てほしい。

ユニットエコノミクスの定義

起業の科学  スタートアップサイエンス

起業の科学 スタートアップサイエンス

ユニットエコノミクスとは

Startupが事業やプロダクトの拡大前に一人もしくは一社獲得した場合に利益を出すことが可能かどうかを表す指標がユニットエコノミクスである。ユニットエコノミクスが利益を出している状態であり、Startupが戦略として事業とプロダクトを拡大するのであれば利益額もスケールアップする。しかし、仮にユニットエコノミクスが基準ラインを達成されていなく、拡大を試みると赤字額を膨らませてしまう。ユニットエコノミクスが健全で無い状態は提供価値の価格とコストが見合っていない証拠。よってこの場合は事業の収益構造を見直す必要がある。

ユニットエコノミクスの計算は次の変数が利用される

  • ①顧客生涯価値(LTV)
  • ②顧客一人、または一社獲得するためのコスト(CPAまたはCACと呼ばれる)
    • CPA : Cost Per Acquisition
    • CAC : Customer Acquisition Cost

ユニットエコノミクスの計算式は下記で示される。(ただし、LTVなどは展開したい事業によって意味合いが異なる場合がある)

  • Unit \hspace{1mm} Economics = \frac{LTV}{CPA(CAC)} : 顧客生涯価値を顧客獲得コストで割ったもの
  • LTV = \frac{MRR}{Customer \hspace{1mm} Churn \hspace{1mm} Rate} : 月額のリカーリング売上を月ごとのChurn Rateで割ったもの
  • CPA = \frac{Marketing \hspace{1mm} Cost}{Acquired \hspace{1mm} Customers} : 総合的なマーケティングコストを獲得した顧客数で割ったもの

LTV < CPA(CAC)となってしまうと、ユニットエコノミクスは1より小さい値となってしまい、この場合は拡大をしない判断をする。一般的にはユニットエコのミスクの値となるLTV/CPA(CAC)が3以上を出している場合が望ましいとされる。 3に達していない場合はChurn Rateを如何にさげるためのプロダクト改善やSuccessチームの体制構築、もしくはマーケ費用を投下した顧客獲得効率を上げる(CPC,CACを下げる)活動を必要とされる。

また、ユニットエコノミクスだけではなく、拡大時に顧客獲得に払ったSales & Marketing費用の回収期間(PayBack Period)も見定めなければならない。回収期間は短ければ短いほどよいが、一般的には6〜12ヶ月であれば望ましい状態とされている。 CPA(CAC)が小さく、MRRが大きくなれば回収期間の値も短くなる。

  • Payback \hspace{1mm} Period = \frac{CPA(CAC)}{MRR)} : 顧客獲得に払ったSales & Marketing費用の回収期間

良いイシューの3つの条件

「イシューからはじめよ」 を再度読み直ししている。仕事においてプロセスを見直したい人、生産性を上げたいと思っている人にはオススメ。

生産性を 生産性 = アウトプット / インプット = 成果 / 投下した労力・時間 と定義した場合、バリューのある仕事にフォーカスすべきであり、バリューのある仕事とはイシュー度と解の質の2軸から成り立つ。この本の中で書かれている重要なポイントとしては、ひたすら課題を解いて解の質を上げることをするのではなく、まずはイシュー度(課題の質)を上げること。最初にすべきこととして課題を解くことではなく、課題をちゃんと見極めることの重要性が書かれている。僕個人の経験としてもビジネスにおいては課題解決を如何に効率よく行うか、また解決手段をいち早く提示・構築できるかにバリューとしての焦点が当たりがちだが、本当に解くべき課題なのか、その課題設定が間違っていたら最初に立ち返ってやり直すような習慣が必要だと感じることが多い。

本の中に書かれていた良いイシューの3つの条件として下記が定義されている。

  • 本質的な選択肢である
    • その答えが出ると今後の検討方向性に大きく影響を与える
  • 深い仮説がある
    • 一般的に信じられている信念や前提を突き崩せないかを常に考える
    • 共通性、関係性、グルーピング、ルールの発見により検討対象を新しい構造として説明すること
  • 答えを出せる
    • 既存のやりかた・技術でも答えがだせるもの

またこういった良いイシューを特定するための行動として、何のフィルターも通っていない生の情報である一次情報を集めるために現場で起こっていることを聞くこと。そしてその情報をもとにスキャン(調べる)活動を行うこととされている。

全ての内容を読まなくても、序章・第一章の合計70ページでも非常に価値が高い一冊である。

経営とエンジニアリング

経営とエンジニアリング

会社経営とエンジニアリングは似ているという話。

ITエンジニアの多くの方なら、解きたい課題解決のために製品開発をする、開発のためにロードマップを書き、実現するためのスケジュールを引く。開発項目を更に細かいissue化し優先度を決め、それを解くためのアルゴリズムを構築する。この手順はおそらく誰もが経験しているだろう。この手順、それはまさしく経営も一緒。

経営は会社が実現するVisionを定義、Visionを実現するために必要なモノ・人・金を管理した事業構築を目指す。事業の中身は常に仮説検証の連続であり、検証と実行のために必要な短期・長期のロードマップを書き、スケジュールを引く。経営として解決しなければならない特にモノ・人・金に関する項目をissue化し優先度を決め、それを解くための手段を構築する。

何よりも経営として自社のビジネスモデルを構築するのはエンジニアがアルゴリズムを書く作業と非常によく似ている。エンジニアリングの観点でも目的を達成するためには最小限のソースコードで実現するために、OSSを活用したり、同じstepのコードを書かないようライブラリにして効率化、また外部サービスのWebAPIを利用したりなど、最小限の労力で最大限の効果を得られるよう考慮されている。

経営も同様。最小限の労力で最大限の効果が得られるよう経営アルゴリズムを書いている。自社の事業が最大限に成長するために、自社で作るモノを明確化、それに必要な人を採用、予算をかける。解きたい課題に対して再現性を生み出すために製品化したり、製品営業をパッケージ化して効率化を図る。実現したい事業の全てを自社で作れない場合は外部の企業との連携をするなど。

ね、会社経営とエンジニアリングは似ているでしょ。似ていない部分も多いはずだけど、エンジニアの方でも経営に携わるというキャリアパスは大いに有り得ると思います。

AIのモデルをオンライン環境で評価する方法

はじめに

昨年末にABEJA Advent Calenderで「AIをシステムに実装する方法」というタイトルでシステムエンジニアの方向けにQiitaで書いたところそれなりに反響があったので、今回はAIのモデルをオンライン環境で評価する方法の部分についてより詳しい内容を書きたいと思います。ここで言うオンライン環境とは製品のProduction環境、特にオンラインで実行できる環境という形で読んでいただけると良いと思います。内容をBlogからQiita側に移しましたので、下記リンクを参照ください。

qiita.com

AIをシステムに実装する方法

ABEJA Advent Calendar 2019

ABEJA Advent Calendar 2019の最終日の投稿をQiitaに書きました。こちらのBlogからリンクだけ張っておきます。僕の投稿は AIを活用したいが、何から始めたら良いか困っているシステムエンジニアの方 向けの内容になっています。ABEJA Advent Calendarも非常に内容が濃いものになっていますので、是非御覧ください。

Raspberrypi zero WとEdge TPUを活用した侵入者通知アプリを作る

やること

yut.hatenablog.com

f:id:yutakikuchi:20190921051804p:plain:w500

以前のBlog PostでRaspberrypi zeroとABEJA Platform(Cloud環境)を活用した侵入者通知アプリを作りました。解決したい課題としては、1階のマンションに住んでいる場合、庭・バルコニーへの不審者の立ち入りが気になります。そこで簡易的なRaspberrypiとLINE Botを利用し、簡易的な写真付きの侵入者通知アプリを作りたいと思いました。

前回はRaspberrypi側にcronで5秒間隔にて画像を撮影し、撮影された画像ををそのままABEJA PlatformのAPIへpostし、SSDにて人物検出を行いました。ただし、これには5秒以内に人がカメラの中に収まることを前提にしたものだったので、侵入者を見落とす可能性がありました。今回は人物検出をEdge側にてSSDで行い、リアルタイムで不審者を検出します。これはTPUがあるからこそ為せる技です。不審者を検出した場合のみLINE Botに通知し、常にAPIのcallのために外部通信を発生させる事無く、より効率的な構成にしようと思います。人物の検出はraspiのcamera moduleで行うため、事前に設定がonになっていること(vcgencmd get_camera コマンド)で確認をします。

RaspberrypiとEdge TPUの接続

f:id:yutakikuchi:20190923151412j:plain:w300

今回使用しているのはRaspberrypi zero WとEdge TPUのUSB Acceleratorとなります。

  • Raspberrypi zero Wはスターターキット : 4104円
  • USB Accelerator : 9450円
  • Raspiのcamera module : 1400円

だったので今回のハードの設定での 合計15000円 で構築している環境になります。上の写真のようにmicroB-USBの変換を用いて、USBのAcceleratorをRaspiと接続をしています。またraspberrypiのケースの中には既にcamera moduleが接続されており、ケースの中央の円からcameraが外向きに出ている状態です。

Raspberry Pi Zero W スターターキット--在庫限り - スイッチサイエンス Raspberry Pi Shop by KSY https://www.amazon.co.jp/raspberry-カメラモジュール-Raspberry-とケース500W画素-感光チップOV5647センサー/dp/B07NSS1QRW

実装する上で必要なもの

Raspberrypi側の環境設定

Raspberrypi zero Wにpyenvでpython 3.5.7を入れています。Defaultのsystemでのpythonは2.7.13になります。

pi@raspberrypi:~$ uname -a
Linux raspberrypi 4.19.66+ #1253 Thu Aug 15 11:37:30 BST 2019 armv6l GNU/Linux

pi@raspberrypi:~$ pyenv versions
  system
* 3.5.7 (set by /home/pi/.python-version)
  3.6.8

pi@raspberrypi:~$ echo $SHELL
/usr/bin/zsh

pi@raspberrypi:~$ vcgencmd get_camera
supported=1 detected=1

Raspi側のpyenv設定とnumpy等のinstall

Raspiのdefaultのpythonは2.7.13なので、これを3.5.7に設定します。またnumpy, pillow, picamera, opencv-python, opencv-contrib-pythonm, opencv-contrib-python-headlessを必要とするのでpipでinstallしておきます。また最新のRaspiのOSディストリビューションなどを最新のものにします。これを怠るとedgetpu側でerrorを起こす場合があります。opencv関係のpackageを入れないと後ほどerrorが出るので、先に入れておきます。

pi@raspberrypi:~$ sudo apt-get update
pi@raspberrypi:~$ sudo apt-get upgrade
pi@raspberrypi:~$ sudo apt-get dist-upgrade
pi@raspberrypi:~$ sudo apt install libhdf5-100 libharfbuzz0b libwebp6 libjasper1 libilmbase12 libopenexr22 libgstreamer1.0-0 libavcodec-extra57 libavformat57 libswscale4 libgtk-3 libgtk-3-0 libqtgui4 libqt4-test
pi@raspberrypi:~$ reboot

pi@raspberrypi:~$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

// 下記を追加
pi@raspberrypi:~$ vim .zshrc

export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"

pi@raspberrypi:~$ source .zshrc
pi@raspberrypi:~$ pyenv install 3.5.7
pi@raspberrypi:~$ pyenv local 3.5.7
pi@raspberrypi:~$ pyenv global 3.5.7

// pipで必要なmoduleを追加
pi@raspberrypi:~$ pip install numpy pillow picamera opencv-python opencv-contrib-python opencv-contrib-python-headless requests
pi@raspberrypi:~$ pip freeze
certifi==2019.9.11
chardet==3.0.4
edgetpu==1.9.2
gi==1.2
idna==2.8
numpy==1.17.2
opencv-contrib-python==3.4.4.19
opencv-contrib-python-headless==3.4.4.19
opencv-python==3.4.4.19
picamera==1.13
Pillow==6.1.0
pygame==1.9.6
pyparsing==2.4.2
requests==2.22.0
scipy==1.3.1
svgwrite==1.3.1
urllib3==1.25.5

RaspiへのTPU Moduleのinstall

GitHub - google-coral/edgetpu-platforms: EdgeTPU support for other platforms, specifically Raspberry Pi Zero

Edge TPUのModuleを上記リンクの内容に従ってinstallします。僕のRaspiはzero Wというタイプなので、EdgeTPU 1.9.2というModuleをinstallします。またinstall先が /usr/local/lib/python3.5/dist-packages/ 配下になるため、こちらをpythonpathに追加します。

pi@raspberrypi:~$ wget https://github.com/google-coral/edgetpu-platforms/releases/download/v1.9.2/edgetpu_api_1.9.2.tar.gz
pi@raspberrypi:~$ tar xzf edgetpu_api_1.9.2.tar.gz
pi@raspberrypi:~$ cd edgetpu_api 
pi@raspberrypi:~$ bash ./install.sh

pi@raspberrypi:~$ vi ~/.zshrc
//追記
export PYTHONPATH='$PYTHONPATH:/usr/local/lib/python3.5/dist-packages'
pi@raspberrypi:~$ source ~/.zshrc

Google Colraのgitにあるexampeを実行してみる

github.com 上記githubに存在するexamples-cameraを実行してみます。git clone後にmodelファイルをdownloadする必要があるので、download_models.shを実行します。download後にclassify_capture.pyを実行するとraspiのcamera moduleで撮影されたstreaming動画をclassificationを行いますが、一定時間の動画を解析した後に Deadline exceeded のerrorで処理が途中で止まってしまいます。おそらく原因はUSBの転送処理の限界、もしくはTPUの電源の限界かと思います。(まだ正しい解決策が分かっていません)

pi@raspberrypi:~$ git clone https://github.com/google-coral/examples-camera.git
pi@raspberrypi:~$ cd examples-camera
pi@raspberrypi:~$ bash ./download_models.sh

// 出力結果
./mobilenet_v1_1.0_224_quant_embedding_extractor.tflite
./mobilenet_v2_1.0_224_inat_plant_quant_edgetpu.tflite

pi@raspberrypi:~$ cd raspicam
pi@raspberrypi:~$ python classify_capture.py

// 出力結果
Inference: 19.25ms FPS: 12.2
61% jersey, T-shirt, tee shirt
5% sweatshirt
3% abaya
Inference: 20.39ms FPS: 12.0
52% jersey, T-shirt, tee shirt
7% abaya
3% sweatshirt
F /home/pi/edgetpu-ml-cpp-release-rpi0-1.9.2/darwinn/third_party/darwinn/driver/usb/usb_driver.cc:834] transfer on tag 1 failed. Abort. Deadline exceeded: USB
 transfer error 2 [LibUsbDataOutCallback]

不審者を検出し、写真をLINEに通知する

SSD modelファイルの指定

f:id:yutakikuchi:20190926092938p:plain:w400 f:id:yutakikuchi:20190825121911j:plain:w400

今回も簡易的なテスト通知を行う仕組みとします。上の画像のように家の中から外に向けてraspberrypiを固定し、常に監視している状態を作ります。

上述の ./download_models.sh を実行したことにより様々な学習済みmodelファイルがdownloadされています。今回人物検出に使うモデルはSSDを指定します。raspberrypi側のcamera module + SSDで検知した不審者の写真を都度LINEに通知し、証拠写真としても記録する事を試みます。 学習済みmodelのファイルは mobilenet_ssd_v1_coco_quant_postprocess_edgetpu.tflite、検出ラベルのファイルとして coco_labels.txt を利用します。 coco_labelsにはpersonも含まれているので、今回はこれを利用します。

pi@raspberrypi:~$ ls ../all_models | grep ssd
mobilenet_ssd_v1_coco_quant_postprocess.tflite*
mobilenet_ssd_v1_coco_quant_postprocess_edgetpu.tflite*
mobilenet_ssd_v2_coco_quant_postprocess.tflite*
mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite*
mobilenet_ssd_v2_face_quant_postprocess.tflite*
mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite*

pi@raspberrypi:~$ ls ../all_models/coco_labels.txt
../all_models/coco_labels.txt

pi@raspberrypi:~$ grep "person" ../all_models/coco_labels.txt
0  person

classify_capture.pyの修正と処理手順

https://github.com/google-coral/examples-camera.git こちらの examples-camera ディレクトリにある classify_capture.py を下記のように修正し、LINEへの通知を行います。※下記はテストコードであり、一度人物を検出した場合はexitさせています。

ちなみに処理手順は以下のものです。

- raspberrypiのcamera moduleでstreamingから画像をcapture。その際にはpythonのpicamera moduleを使用。
- Edge TPU上でSSDを実行し、人物判定確率が60%以上のものに関しては画像をアップロード。
- LINE Botに通知するためには参照可能な画像URLを必要とするため、ABEJA Platform Datalakeに人物判定された画像をアップロード。URLを取得。
- URLの短縮化。bitlyのAPIから取得。
- LINE Messaging APIに画像の短縮URLを含めたJsonのRequest BodyをPOST。
- LINEに不審者をリアルタイムで検出し、証拠画像付きの通知が流れてくる。

ABEJA Platform Datalakeへの画像upload

LINE Botに通知する画像は参照可能なURLである表現される必要があります。今回はABEJA Platform Datalakeを使用することにします。ABEJA Platform Datalakeに画像をpostするために、SDKをinstallします。Datalakeに画像をuploadするとdownload_urlを取得する事ができるので、LINEへの画像URL postのためにdownload_urlという属性を利用します。ソースコード上にABEJAのcredential情報を入力すれば下記の内容で画像がupload、download_urlが取得可能です。

pi@raspberrypi:~$ curl -s https://packagecloud.io/install/repositories/abeja/platform-public/script.python.sh | bash
pi@raspberrypi:~$ pip install abeja-sdk
pi@raspberrypi:~$ pip freeze
abeja-sdk==0.2.12
...
def upload_image_datalake(image_path):
  abeja_credential = {
    'user_id': abeja_user_id,
    'personal_access_token': abeja_personal_access_token
  }
  datalake_client = DatalakeClient(organization_id=abeja_organization_id, credential=abeja_credential)
  channel = datalake_client.get_channel(abeja_channel_id)
  res = channel.upload_file(image_path)
  datalake_file = channel.get_file(file_id=res.file_id)
  content = datalake_file.get_file_info()
  return content['download_url']

Datalake.download_urlの短縮化

LINE Botに通知する上で、LINE Messaging APIを利用する必要がありますが、Request Bodyに必要な originalContentUrl という参照可能な画像URLを指定するURLの制限として最大文字数があるため、bitlyを用いて短縮化を図っています。

def get_bitly_url(download_url):
  headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer {}'.format(bitly_auth_key)
  }
  body = {
    'long_url': download_url
  }
  res = requests.post(bitly_post_url,
          json.dumps(body),
          headers=headers
        )
  return 'https://{}'.format(res.json()['id'])

LINEへの通知結果

f:id:yutakikuchi:20190927081234p:plain:w250

上のように人が検出された場合のみLINEにpush通知が来るようになりました。(注)smileのアイコンは自動的に付与されません。SSDで検出されたbounding boxも表示されてpush通知されています。侵入者が庭にいたという証拠も写真として押させることができた、また前回課題になっていた5秒間隔でCloudにある人物検出modelをAPIでcallすることなく、Edge側でその判定を持つことで見落とし無くリアルタイムに検知する仕組みを構築できました。次回は本当の住人を顔判定から見極め、そうではない不審者のみを通知することに挑戦したいと思います。