masato-ka's diary

日々思ったこととか、やったことの備忘録。

Javaライブラリ、KuromojiとKumoで日本語 Word cloudを作成する。

この記事について

この記事ではJavaでWordCloudを作るためのライブラリKumoの使い方を紹介しています。Word cloudは文章中の単語を並べ、各単語の出現頻度にしたがって文字の大きさを変化させる画像です。これにより、直感的に文章の内容や特徴を捉えることができます。

Kumoの概要と日本語形態素解析の検討

JavaのWord cloudライブラリを探したところKumoというライブラリを発見しました。このライブラリはJavaのみで完結しています。

github.com

また、ラテン系文字だけでなく、中国語の形態素解析もできるようです。しかし、日本語については形態素解析器が含まれていません。そこで、日本語解析はJava形態素解析器としてKuromojiを利用します。

www.atilika.com

使い方

それでは早速使い方を見ていきましょう。

Javaプロジェクトの依存関係へ追加

前述の通り、日本語の形態素解析にはKuromojiを追加します。まずはMavenまたはGradleの依存関係へKumoとKuromojiを追加しましょう。Kumoはkumo-coreを依存関係に設定すると形態素解析部分を除いたWord cloud作成ライブラリのみをインストールすることができます。Kuromojiについて今回はipadic版を利用します。たの辞書を使う場合もこの後の利用方法に変更はありません。

<dependency>
    <groupId>com.kennycason</groupId>
    <artifactId>kumo-core</artifactId>
    <version>1.17</version>
</dependency>
<dependency>
    <groupId>com.atilika.kuromoji</groupId>
    <artifactId>kuromoji-ipadic</artifactId>
    <version>0.9.0</version>
</dependency>

Kuromojiの形態素解析の実行

Kuromojiの形態素解析後Map<String,Long>という型でデータをまとめます。Keyは抽出した単語、Valueは文章中での出現回数です。 inputTextは文章などが入っている想定です。また、以下の例では固有名詞のみを抜き出しています。

public Map<String,Long> tokenize(String inputText){
    List<Token> tokens = tokenizer.tokenize(inputText);
    Map<String,Long> dataSet = tokens.stream()
          .filter(t -> t.getPartOfSpeechLevel2().equals("固有名詞"))
          .map(t -> t.getSurface())
          .collect(Collectors.groupingBy(e->e,Collectors.counting()));
}

実際にWord cloudを作成するためにはkumo-coreライブラリで提供されているWordFrequencyオブジェクトのListを後述するWordCloudオブジェクトへ渡す必要があります。そのために以下のように変換を行います。

List<WordFrequency> wordFrequencies = dataSet.entrySet()
                .stream()
                .map(data -> new WordFrequency(data.getKey(), data.getValue().intValue()))
                .collect(Collectors.toList());

KumoでWord cloudの作成

実際にWord cloud を作成するにはkumo-coreで提供されるWordCloudクラスを使います。

final Dimension dimension = new Dimension(300,300);//  (1)
final WordCloud wordCloud = new WordCloud(dimension, CollisionMode.PIXEL_PERFECT);// (2)
wordCloud.setPadding(2);
wordCloud.setBackground(new CircleBackground(200)); // (3)
wordCloud.setBackgroundColor(Color.white); // (4)
wordCloud.setColorPalette(new LinearGradientColorPalette(Color.RED, Color.BLUE, Color.GREEN,
         30, 30)); // (5)
wordCloud.setFontScalar(new LinearFontScalar(10, 40)); // (6)
wordCloud.build(this.wordFrequencies); // (7)
BufferedImage resultImage = wordCloud.getBufferedImage(); // (8)
  • (1) Dimensionオブジェクトで作成する画像のサイズを決めるここでは300 X 300
  • (2) コンストラクタへ(1)のDimensionオブジェクトを渡してコンストラクタを作成する。
  • (3) 半径200の円状にWord cloudを作成する。四角や指定したテンプレート画像に沿った形状にすることもできる。
  • (4) 作成画像の背景を白に変更
  • (5) 文字のグラデーションを設定
  • (6) フォントサイズの設定
  • (7) 作成元のデータが入っているListオブジェクトを渡しビルドする。
  • (8) 作成された画像をBufferedImageとして 取得する。

最終的に出力されたBufferedImageオブジェクトをファイルに保存します。画像の拡張子はデフォルトでpngとなっていますが、設定で変更できるようです。

作成してみたWord cloudの画像

試しに、このブログに対して処理をかけてみたのが以下の結果です。見事に横文字単語だけで構成されています。あんまり日本語の形態素解析をしたありがたみがないです。

f:id:masato-ka:20190312230457p:plain
WordCloudをこのブログにかけてみた

ニュース記事なんかで試してみるといいかもしれませんね。

まとめ

自分のブログにWord Cloudを実行すると今までにどんなブログを書いていたのかわかりやすくなります。今後のブログ内容や自分の興味関心などをアバウトに把握できていい感じです。

if-up 2019に参加した話。

この記事について

この記事ではソラコムさん主催のif-up 2019へ参加した感想について記載しています。if-upは2017年に開催されたソラコムさん主催のイベントです。その後同名のイベントはなかったのですが、今回パワーアップした形で帰ってきました。

if-up 2019の概要について

前述の通り、if-upは2017年に同名イベントが開催されましたが、今回は少し毛色が違ったイベントになったと感じました。詳細は以下のイベントページにを確認いただければと思います。

if-up2019.soracom.jp

毛色が違った大きな点としてはこれまでのイベントではIoTにおける課題解決をソラコムサービスでどうやって解決するかという内容が主体となっていたのですが、今回のイベントではその部分は少しトーンダウンしています。日本のものづくり、サービス開発は今どうなっているのか、みんな何を課題に取り組んでいるのか?そんなトーンのイベントとなっていました。 イベント冒頭のキーノートセッションでは「プロダクトの裏にはパッションをもった人とチームがいる!」、「プロダクトづくりの前提が変わった」、「日本からもっと良いプロダクトが出てもいいはず」という問いかけから始まり、大変興味深い内容のイベントとなりました。イベント全体を通して特に印象に残ったポイントについて紹介していきます。

if-upで印象に残ったポイント

世の中にないものを生み出すということ

 午前のキーノートでの主題的なテーマだと感じたことです。新しいプロダクトを作る際に、そのコンセプトが果たして正しいのか、それは便利なのかは誰にも分かり得ないことです。良いプロダクトを作るためにまた、作る側のパッションを保つためにもこういった不安を払拭していく必要があると思います。  そんな中でGroovXさんとソラコムさんのプロダクト開発で共通しているなと思ったことが、MVPを作り、ユーザへの提示を素早く行っていくことが一つの会になるのだなと思いました。Agile的な取り組みでは当たり前の解決策ではなるのですが、論より証拠というのはパワポ100ページよりも説得力がある非常にためになるお話でした。  GroovXさんのLOVOTの開発に関するお話の中でLOVOTはロボットではあるけども「便利ではなく心のケアを中心にプロダクトを作った」けれどもまだ形も無いもので投資家の理解も得られなければ、身内からも不安になる。そこで、MVPを作って仮説検証を繰り返し自分たちの目指す方向を正しく見定めたとのことでした。またソラコムさんのお話でもBeamからFunnelそしてHarvestとプロダクトが進化した経緯をベースに同様のことをおっしゃられていたと思います。

日本のプロダクトが売れないのはなぜか?

日本のプロダクトが売れないのは良いプロダクトが作れないのではなく、良いプロダクトの定義が変わったという話が興味深かったです。つまり日本のプロダクトは昔から同じもので過去の基準からすればいいもののはずだけど、そもそも世の中が考える良いプロダクトの基準が変わったため売れなくなってしまったという話です。じゃあ、そもそも「新しい良いプロダクトとはなんなのか?」について明確な定義は出ていなかったと思いますが、パネラーの方達から語られたのは

  • 良いプロダクトの定義が変わったよりはトレンドに振り回されず芯を通したプロダクト開発が必要である。
  • 自分たちが何をやっているのかしっかりをパッションを持つ。
  • 自分たちが何者なのかをはっきりさせる。

ということでした。プロダクトの機能を提供するのではなく、その機能が与える価値をはっきりとさせる、自分たちがユーザへ与える価値か解決する課題をしっかりと定義すること。これが良いプロダクトを作る秘訣なのかなと思ったり。少し前に流行ったコトづくりと似通ったところはありますが、プロダクトの価値としては本質的なんだろうなと思いました。

まとめ

 良いプロダクトをいかに作り出していくのかというのは非常に難しいテーマだと常日頃思います。今日のif-up2019参加してまだまだ振り返りきれていないところもあるのですが、やはり自分たちがテクノロジーを使い、いったい何に取り組むのかを明確化し定義する、それを素早く形にしていき検証を行っていくのが必要なのかなとも思います。それは大変難しいことでもあるのですが。。。

grpc-spring-boot-starterでSpringBootへgRPCを導入する。

# この記事について この記事ではgrpc-spring-boot-starterを使ってSpringBootでgRPCを利用する方法について記載します。主に自分向けの備忘録的記事となります。

プロジェクトの設定

ビルドツールはMavenを利用します。SpringBootのライブラリ作成後、pomのdependenciesに以下を追加して依存関係にgrpc-spring-boot-starterを追加します。 grpc-spring-boot-starterはgRPCをSpringBootで利用できるようにするAutoConfigurationを実行します。

<dependencies>
        <dependency>
            <groupId>io.github.lognet</groupId>
            <artifactId>grpc-spring-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
 </dependencies>

ビルドを実行するプラットフォームを特定するためにプラグイン拡張を追加します。プロトコルバッファをビルドするためのprotocコマンドダウンロードに利用します。

<build>
...
<extensions>
      <extension>
           <groupId>kr.motd.maven</groupId>
           <artifactId>os-maven-plugin</artifactId>
           <version>1.5.0.Final</version>
      </extension>
</extensions>
...
</build>

さらにpurotobuf-maven-pluginを追加することで、Mavenのビルドプロセスの中でプロトコルバッファをコンパイルできるようにします。

<build>
...
<plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.6.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.18.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
...
</plugins>
</build>

上記でgRPC導入の設定は完了です。

gRPCのインタフェース定義

gRPCではprotoファイルと呼ばれるファイルでRPCの定義を記述します。記述されたprotoファイルからprotobuf-manve-plugin(実際にはprotocコマンド)がgRPCのサーバ側の抽象クラスとクライアントコードを自動生成します。

protoファイルの作成

以下のようなprotoファイルで定義します。 先頭の「option java_package」で生成後のコードのパッケージを定義します。プロジェクトのパッケージに合わせて適宜変更します。

syntax = "proto3";

option java_multiple_files = true;
option java_package = "ka.masato.grpc.demo.grpcmavendemo";
option java_outer_classname = "GreetClass";

package greet;

service Greet{
    rpc greeting (GreetRequest) returns (GreetResponse);
}


message GreetRequest{
    string value = 1;
}

message GreetResponse{
    string value = 1;
}

protoファイルの文法については以下を参照ください。

developers.google.com

protoファイルのビルド

protoファイルのビルドは前述のようにMavenのビルドプロセスの中で実行れます。以下のようにビルドを実行します。

$ mvnw clean package install

実行後、target/generated-souces/protobuf配下に生成されたコードが格納されています。IntelliJ IDEAで実行した際にはclassパスにも追加されているはずのなので、そのまま、srcフォルダ配下のパッケージから参照することができます。

gRPCの実装

生成されたコードをベースにサーバとクライアントを実装します。

サーバ側の実装

生成されたコードに含まれるGreetGrpc.GreetImplBaseを継承したクラスを作成します。この抽象クラスにはprotoファイルで定義したサービスがメソッドとして定義されており、これをオーバライドすることでAPIを実装します。また、継承クラスにはgrpc-spring-boot-starterで定義されている@GRpcServiceアノテーションをつけることで、gRPCのサーバとして登録されます。

@GRpcService
public class endpoint extends GreetGrpc.GreetImplBase{

    @Override
    public void greeting(GreetRequest request, StreamObserver<GreetResponse> responseObserver) {

        String value = request.getValue();
        GreetResponse greetResponse = GreetResponse.newBuilder().setValue("Greeting!"+ value).build();
         responseObserver.onNext(greetResponse);
         responseObserver.onCompleted();

    }
}

クライアントの実装

クライアント側ではスタブを作成し、スタブ経由でサービス側のメソッドを呼び出します。正確かはわかりませんがManagedChannelはgrpc-spring-boot-starterでbean定義されていると思います。

public class GreetClient {

    private final GreetGrpc.GreetBlockingStub stub;


    public GreetClient(ManagedChannel managedChannel) {
        this.stub = GreetGrpc.newBlockingStub(managedChannel);
    }

    public String getHello(){
        GreetRequest greetRequest = GreetRequest.newBuilder().setValue("Hello").build();
        GreetResponse greetResponse = this.stub.greeting(greetRequest);
        return greetResponse.getValue();

    }

}

まとめ

grpc-spring-boot-starterを使うとSpringBootで簡単にgRPCを実装することができました。また、proto-buf-mavenプラグインを利用することで、Mavenのビルドプロセスの中でプロトコルバッファのファイルからgRPCのコードを自動生成することができました。gRPCはprotoファイルで定義した内容からサービスの実装インタフェースと対応するクライアントコードを自動生成でき、サービス間通信のインターフェースを実装と定義を紐付ける良い方法だと思います。実際の開発にはprotoファイルの管理やなど実装以外に工夫が必要そうです。

SORACOM Lagoonと AndroidThingsでIoT X AIをやってみた。

この記事について

この記事はSORACOM Adventcalendar 2018 18日目の記事です。SORACOM Adventcalendar 2018もいよいよ終盤に差し掛かってきました。今回は2018 年11月24日に開催されたIoTつくるよで展示した SORACOM LagoonとAndroidThingsを使った物体温度計測システムについて紹介します。当日のLTの資料は以下の通りです。

speakerdeck.com

物体温度計測とは?

温度センサなどで部屋の温度や外気温を計測し可視化・モニタリングすることはIoTのユースケースとして一般的に紹介される例です。こう言った場合、部屋の住環境のモニタリングや商品が保管してある倉庫などの環境モニタリングとして実施されることが大半でしょう。 しかし、温度計測が必要であるが、前述の例のような部屋や空間といった面としてのセンシングではなく、ピンポイントで温度を計測したい場合が出てきます。例えば機械設備の保守では設備のある特定の部分の温度上昇を検知したい場合です。また、ショウケースに入った食品などの品質監視を行う場合、ショウケース内の温度を1箇所測っただけでは実際の食品の温度を知ることはできません。 こういった場合、測りたい物に直接センサを貼り付けて温度を監視したくなりますが、以下の理由から難しいと考えます。

  1. 計測対象にセンサの設置が困難
  2. 計測する個数があらかじめ特定できない
  3. 計測箇所があらかじめ特定できない。

この課題を非接触温度センサとDeep Learningによる物体検知を行うことで解決します。また取得したデータをSORACOM Lagoonでダッシュボードとして提供します。

作成したシステムの仕組みについて

全体概要

全体構成は以下のようになっています。

f:id:masato-ka:20181217221236j:plain

詳細については以下で順に説明します。

Android ThingsとSORACOMの接続について

今回デバイス構築に利用したのはGoogleが提供しているIoTデバイス向けのOSであるAndroidThingsを利用します。Android Thingsについての紹介記事は以下の過去記事を参照ください。

masato-ka.hatenablog.com

Android ThingsはSIMを利用する環境が整っていないため、SORACOMに接続する際はSORACOM Inventoryからデバイス登録を行い、デバイスのシークレットを取得します。取得したシークレットを元にSORACOM HarvestのAPIへインターネット経由でデータを送信します。具体的な手順は以下ソラコムさんのサイトを参照してください。

dev.soracom.io

AndroidThingsからAPIを叩く際はokhttpを使い。以下のコードスニペットを使い、データの送信を行なっています。実験用のため、デバイスIDやシークレットをハードコートしていますが、実際にはAndroidのKey storeなどに保存しておくと良いと思います。

private static final String HARVEST_ENDPOINT = "https://api.soracom.io/v1/devices/";
private static final String DEVICE_ID = "<デバイスID>";
private static final String DEVICE_SECRET_KEY = "<デバイスシークレット>";

private OkHttpClient mOkHttpClient;
private ObjectMapper mObjectMapper;


String payload = mObjectMapper.writeValueAsString(harvestSummaryPayload);

MediaType MINE_TYPE = MediaType.parse("application/json; charset=utf-8");
//"x-device-secret: <シークレットキー>"
Request request = new Request.Builder()
          .url(HARVEST_ENDPOINT+DEVICE_ID+"/publish")
           .addHeader("x-device-secret", DEVICE_SECRET_KEY)
          .addHeader("Content-Type", "application/json")
          .post(RequestBody.create(MediaType.parse("application/json;charset=utf-8"), payload))
           .build();
Response response = mOkHttpClient.newCall(request).execute();
response.body().string();

物体検出について

今回の物体検出は学習済みのTensorflowモデルをTensorflow liteモデルへ変換し、AndroidThings場で動作させました。動作させたモデルはSSD Mobilenet v1 cocoと呼ばれるモデルです。AndroidThingsでの動作については以下の過去記事を参照ください。

masato-ka.hatenablog.com

接触温度センサについて

接触温度センサはPanasonic社のAMG8833赤外線アレイセンサを利用します。このセンサは8X8の格子状に非接触で温度データを取得することができます。データの取得インタフェースはI2C です。エアコンに内蔵され、人発見などの利用されるセンサーです。

masato-ka.hatenablog.com

実行結果

 デバイス側でおの物体検知と温度計測について

AndroidThings側での実行結果は以下の動画のようになっています。物体検出でマグカップを認識しています。その後、マグカップにお湯を注ぐことで発生する温度上昇をセンサーで捉えています。このようにAIだけ、センサーだけではできない事象を多次元的に捉えることがAI X IoTの醍醐味でしょう。

youtu.be

SORACOM Lagoonでの可視化について

SORACOm Lagoonでの可視化は以下のようになっています。このダッシュボードでは缶コーヒーの温度と個数を計測しています。これらを1つのデバイスで実現出来つところがみそです。

f:id:masato-ka:20181217220642p:plain

まとめ

今回はIoT X AIということでAndroidThingsで取得したセンサ情報と画像認識の結果をSORACOM Lagoonで可視化させてみました。ソラコムのサービス自体は昨年まではセルラー前提といった印象が強かったですが今年のDiscoveryイベントを境にセルラーだけでなくより幅の広いIoT プラットフォームとしての進化を遂げています。そんなソラコムサービスだからこそ、IoT X AIなサービスでも活躍できるのではないのでしょうか。来年もソラコムさんからますます目が離せません!

SORACOM LTE-M Buttonで家庭内PoCをやってみた話

1. この記事について

この記事はSORACOM LTE-M Button powerd by AWS Advent Calendar 2018 8日目の記事になる。前回このブログではSORACOM LTE-M ButtonがIoTデバイスとしてとて素晴らしいお手本となっていることを紹介した。

masato-ka.hatenablog.com

この意見は今も1mmとして変わってない。その上で、どんな素晴らしいデバイスを使ったとしてもアプリケーションとユーザとの関係を考えユースケースを実装することが価値になるということをお伝えしたい。そうでなければせっかくの素晴らしいデバイスを台無しにしてしまう。今回はおうちハック的にSORACOM LTE-M Buttonを使ってみたお話となる。

2. 検討したユースケース

今回SORACOM LTE-M Buttonを利用して実装したアプリケーションのユースケースは日用品の在庫管理ユースケースである。

これはSORACOM LTE-M Buttonをクリックするたびに日用品の在庫の数をカウントする機能を持っている。あらかじめ備蓄品の数を設定しておき、備蓄品を利用したらボタンをワンクリックする。そうすると、在庫数を減算していく仕組みである。管理している在庫数を可視化させることでリアルタイムに備蓄品の数を確認することができる。家庭内でよく使う備蓄品としてトイレットペーパやティッシュといった日用品から卵などの食品まで色々なものを割り当てることができる。今回は冷蔵庫の卵の数をカウントするEgg Counterを実装した。

2.1 ユースケースにおける課題感

ユースケースに対する課題として考えたことは、「帰宅途中に買い物をする際に冷蔵庫の中身を覚えておらず買い物に苦慮する場面」であった。 例えば夕飯の支度に卵を使いたいが、卵の残数やその賞味期限などを出先で確認することが難しい。我が家は共働きの2人暮らしである。そののため、特にこういった食品の残数について、「お互いがいつ使ったかわからない」ことから残数の把握は困難になる。

2.2 在庫の減り具合を見ることの意義

在庫状況を可視化することで上記の課題に対して、出先にて最新の在庫状況を確認することが可能となる。そのため、無駄な買い物を抑止することができる。また、可視化状況を共有することでコミュニケーションにより多重購入という悲劇を未然に防ぐ手立てともなる。

2.3 本家ダッシュボタンとの違い

本家ダッシュボタンでは似たような課題に対して、無くなる、無くなりそうなタイミングですぐに注文できるという価値を提供している。一方で現在の家の中の在庫数については管理することができない。この場合、上記に挙げた課題感に対しては対応することができない。また時系列で在庫の消費量を記録することで将来の消費予測やカイゼン活動につなげることができる。

3. Egg Counterのアーキテクチャ

ここで実際に作成したSORACOM IoT Buttonを利用した在庫管理アプリケーション、Egg Counterについて紹介する。

3.1 アーキテクチャ方針

Egg Counterのアーキテクチャを決定する上で以下の二点をポイントとしてて検討を行った。

  1. 簡便に在庫の管理を実現する機能
  2. 簡易PoCに的を絞り簡易なつくりとすること。

1についてはセンサーなどは使わずにSORACOM Buttonを、使うことで簡単に在庫の記録を行えるようにした。 2.については既存サービスを活用することで構築の手間を省いた。また利用するサービス数を絞れるように各サービスの使い方を工夫した。

3.2 構成

上記アーキテクチャ方針にしたがって以下のような構成とした。

f:id:masato-ka:20181208110531j:plain
Egg Counter アーキテクチャ

以下のように冷蔵庫から卵を取り出すごとにSORACOM LTE-M Buttonを押すことで在庫数をカウントしていく。カウントされた在庫数は Lagoon上で可視化されるようにした。データの永続化自身はSORACOM Harvestに任せ、AWS Lambdaからアクセスするようにしている。

  • ボタンに割り当てられたイベントは以下のようになっている。
ボタンイベント 動作 意味
シングル 在庫減算 卵を使った時に卵の在庫を減らす。
ダブル 在庫加算 卵の補充や誤押の際の在庫修正に利用する。
ロング 在庫のリセット 卵を新規に補充した際に設定された値に在庫を更新する。

3.3 AWS Lambda のコード

Lambdaのコードは以下のリポジトリに掲載している。

github.com

CloudFormationを使いデプロイすることができる。今回は先日のAWS reInventで公開されたIntteliJ IDEAWS Toolkits pluginを使って開発を進めた。

Lamda ファンクション本体は以下のようにクリックに応じて各イベント処理にディスパッチするコードのみになっている。

def lambda_handler(event, context):

    click_type = event['deviceEvent']['buttonClicked']['clickType']
    if click_type == "SINGLE":
        do_single_click_event()
        logger.info("Called single click event.")
    if click_type == "DOUBLE":
        do_double_click_event()
        logger.info("Called double click event.")
    if click_type == "LONG":
        do_long_click_event()
        logger.info("Called Long press event.")
    return {"result":"success"}

SINGLEクリックイベントのメソッドではSORACOM APIから現在の在庫数を取得するget_latest_remainingメソッドを実行し、在庫の減算を行なっている。減算した結果をpersistent_remainingHarvestのAPIで送っているようにしている。

def do_single_click_event():
    latest_remaining = get_latest_remaining()
    remaining = latest_remaining - 1 if latest_remaining > 0 else 0
    persistent_remaining(remaining)

ダブルクリックやロングについても加算、リセットのみが違うだけで動きは同じである。ソラコムのAPIについてもオーソドックスにアクセスすれば問題ない。この部分は今年ソラコム側でセルラー回線以外へサービスの対応を進めているため、実現できるようになった。

参考2では記載されていないが、参考1で入れたデータを取得するためのエンドポイントはhttps://api.soracom.io/v1/data/Device/{DEVICE ID}?sort=desc&limit=1となる。詳細は上述のGitHubのコードをご覧いただきたい。

4. 改善ポイント

4.1 AWS Lambdaに渡すキーの多さ

SORACOMHarvestの WEB APIとSORACOM APIのDATA APIを使うことで、AWS Lambdaは以下の4つの情報を持つことになる。 これらはAWS Lambdaに環境変数として設定している。SORACOM Harvestへデータを送信するためにはAWS Lambdaをデバイスと登録してキーを払い出す必要がある。SORACOM Buttonが出てきたタイミングでボタンの情報をHarvestに連携するために従来のSORACOM APIとHarvestのAPIを一緒にしてもいいと思う。AWS側にDB持たせればいいというのはごもっとも。

キー名 説明 用途
バイスID SORACOM Inventoryから払い出されたデバイスID SORACOM Harvestへデータを送る際に利用
バイスシークレット SORACOM Inventoryから払い出されたデバイスのシークレットキー SORACOM Harvestへデータを送る際に利用
Authキー SAMユーザのAuthキー SORACOM Harvestからデータを取得する際に利用
AuthキーID SAMユーザのAuthキーID SORACOM Harvestからデータを取得する際に利用

4.2 AWS Lambdaの環境変数

環境変数の値として記号を含む文字列を設定するとエスケープされてしまう。また、環境変数から文字列として取り出した場合、エスケープされた文字列が取り出されるため、エスケープを除去する必要がある。今回の場合、SORACOM Inventoryで払い出したデバイスシークレットキーに「=」が含まれており、これが「\」でエスケープされていた。そのため、以下のようなコードでエスケープ文字を除去した。

KEY_NAME = os.environ['key name'].replace('/','')

5. ユーザレビュー

仕組み自体の構築は1〜2時間で完了し、いざ家庭内 PoCとして投入してみた。

5.1 ユーザの感想

意気揚々と仕組みの説明と使い方、意義を説明した結果ユーザから「めんどくせぇ」という感想をいただいた。UXへの敗北である。「ボタンを押す煩わしさ>卵の個数がわかるメリット」の図式が出来上がったために発生した問題だ。前述したように、仕組みやデバイスが良くてもそれを活用するアプリケーションの価値に問題があった。

6. まとめ

SORACOM LTE-M Buttonは非常に優れたデバイスである。しかし、それを活かすユースケースをしっかりと作り込むべきだろう。今回は卵を取り出すという行為に無理やりボタンを押すという行為を繋げてしまい、ユーザへの手間を増やす結果となった。SORACOM LTE-M Buttonを活かすためには。ボタンを押すという行為がユースケースで無理なく出てくることが重要じゃないかと思う。良いデバイスなだけに見落としやすい。

SORACOM LTE-M Button powerd by AWSがIoTアーキテクチャの優れたリファレンスモデルな件

この記事について

この記事ではSORACOM LTE-M Button powerd by AWSについてのファーストインプレッションを紹介する。7月の発表から発売まで首を長くして待ち、ようやく手元に届いたこの製品。IoTを実現するシステム、デバイスのお手本と言ってもいいくらい綺麗な作りになっていた。この記事ではSORACOM LTE-M Button powerd by AWSがなぜIoTのお手本と言えるのか考察していく。 以下のツイートをしっかりと文章にした内容だ。

SORACOM LTE-M Buttonについて

SORACOM LTE-M Buttonは7月に行われたSORACOM Discoveryイベントで発表されたソラコム初の自社デバイスだ。中身はAWS IoT ボタンAmazon dashボタンのベース)で、AWS IoT 1Clickサービスを使って利用することができる。 このデバイスができることはAmazon dashボタンのように、ボタンをクリックすること。ボタンのクリックは3パターンあり、それぞれシングルクリック、ダブルクリック、長押しである。ボタンをクリックするとAWS IoT 1Clickへイベント情報を送信する。ユーザはAWS IoT 1Clickに送られたイベントを処理することで任意のアプリケーションを実装することができる。処理はAWS Lambdaで記述する。  このようにデバイス自体の機能は本家AWS IoTボタンと一緒である。しかし、SORACOM LTE-M Button powerd by AWSKDDI社の LTE-Mと呼ばれる、LTEの通信網を利用したLPWA規格を採用している。この回線を利用することでWiFiを必要とせず、デバイス単体でAWSへのコネクティビティを確保している。

blog.soracom.jp

何がIoTアーキテクチャのリファレンスとして優れているのか?

ここからはSORACOM LTE-M Button powerd by AWSがIoTのお手本たる理由について語っていきたい。

1. シームレスなデバイスアクティベート

SORACOM LTE-M Button powerd by AWSはパッケージ開封後、すぐにAWSに接続することができる。すぐという言葉は曖昧だがおそらく最短30秒もあれば十分な時間だろう。本体の裏にあるデバイスコードをAWS IoT 1Clickのデバイス登録画面に入力し、画面の案内に従い、デバイスのボタンを一回クリックすれば完了する。  もちろんAWS IoT 1Clickの仕組みがシンプルという見方もできる。しかし、最も大きい理由はLTE-Mを使うことで、WiFi接続をすっ飛ばすことができるという点だ。 従来この手のデバイスWiFiを利用していた。そのためにデバイスを一度設定モードで起動(WiFiアクセスポイントになったりする)しスマートフォンから専用アプリかWebブラウザで接続し、WiFiのパスワードや酷い場合は専用クラウドサービスのパスワード入力を必要というお世辞にもスマートとは言えない解決方法を提供してくれていた。またWiFi接続を利用することで実質的に屋内での利用に限定されていた。この問題をLTE-Mとソラコムの基盤でものの見事に解決してくれたのだ。  当然利用するユーザにとって接続の利便性を提供するだけでなく、利用するシーンを限定しないという利点がある。ユーザがデバイスを利用するために何を提供すれば良いのか、非常に参考になる。

  • 実際の設定方法がみたい方は以下公式ドキュメント参考のこと。

SORACOM LTE-M Button powered by AWS をクリックしてSlackに通知する | Getting Started with SORACOM LTE-M Button | SORACOM Developers

2. 標準で提供されるデバイス管理機能

SORACOM LTE-M Button powerd by AWSAWSへの登録以外にソラコムの対応サービスへ登録を行う必要がある。登録しなくても利用はできるのだが、通信料の更新などができない。欲を言えばこの部分はAWS登録と連動してほしかった。が、この一点に目を瞑れば、この登録もユーザにはメリットがある。なぜならば登録することでデバイスの管理がソラコムのユーザコンソール画面から行えるからだ。実際の管理画面は以下のように見える。

f:id:masato-ka:20181104145439p:plain

残りのクリック回数や残電池量を見ることができる。また、最後に接続した時間やその際のイベントのペイロードとしてJSONデータを確認することができる。デバイスや障害調査だけでなく、ユーザの利用状況調査などIoTサービスの本質的な情報の収集という点で非常にありがたい情報だ。デバイス管理を行う上で何の情報に気をつけて取得すれば良いのか参考になる。

3. 責務の境界とセキュリティ

 最後にユーザの 目に触れないが、無意識のうちに重要な部分だ。実は前項で紹介したシームレスなデバイスアクティベートも標準で提供されるデバイス管理機能も、ソラコムサービスとAWSの役割がしっかりしているため、結果として実現されいる機能だと考えている。  現状知りうる範囲から推測できるSORACOM LTE-M Button powerd by AWSアーキテクチャを起こしてみた。

f:id:masato-ka:20181104160255p:plain

 SORACOM LTE-M Button powerd by AWSKDDI社のLTE-M網を経由してソラコムの基盤につながる。ここは他のソラコムサービスと同じだと考えられる。そこからSORACOM Beamを使ってAWS IoT 1clickへ接続されているようだ。そのため、公衆インターネット回線へ出ることなくデータをセキュアにAWSへ転送できる。 また、デバイス管理機能はソラコムのサービス内で完結している。もちろんAWS側で同等の機能を組むこともできるだろうが、基本的にはAWSはアプリケーションの実現に集中することができる。  つまり、アプリケーションを実現するAWSサービスとデバイスの安全なコネクティビティと管理を実現するソラコムサービスというサービス間の責務の分解が施されている。IoTサービスで様々なサービスが似たような機能を重複して提供している。その中でソラコムのサービスの位置付けとしてIoTアーキテクチャの下回りをしっかりと提供し、他者クラウドサービスはアプリケーション実現のみに利用できるという一種のレイヤー構造を提示しているのではないだろうか。今回のSORACOM LTE-M Button powerd by AWSでは難しいかもしれないが、こういったレイヤー構造を組んでおけば、アプリケーション層をオンプレミスな環境として組むことや、費用やサービスのスケールに伴い、利用するクラウドを変えいてくことをユーザやデバイスへの負担なく実現できることだろう。IoTの全体アーキテクチャを考える際に非常に参考にしたい点だ。

まとめ

このようにソラコムの基盤とAWSのサービスをうまく繋ぎこみ、デバイスの提供と運用を実現している。ソラコム自社で提供する基盤だけあってソラコムのポテンシャルが十分に発揮できていると思った。もちろんエンドユーザ向けサービスとしてのユースケースやビジネスモデルは存在しない。これはこのデバイスを使うユーザが作り上げる必要がある。しかし、どうやってデバイスをユーザに届けるか、どうやって自分たちのビジネスモデルを実現するアプリケーションを作るか、どうやってサービスを運用していくか、こういったIoTサービスを実現する上考えなければならない問題についてその解の示唆を与えてくれている。SORACOM LTE-M Button powerd by AWSはまさしくIoTアーキテクチャの優れたリファレンスモデルとなっている。きっと僕たちがいつまでも実現しないからソラコムが自分たちで作ってしまったのだ!

ちなみに現在(2018年11月4日)キャンペーン価格中で3980円で購入することができる。キャンペーンの終了時期は未定だが、この機会にぜひこのデバイスを体験していただきたい。Amazon dashボタンをハックして喜んだり、温度センサの値をWEBブラウザでグラフにしてIoTとか言って喜んでいる場合ではない。

soracom.jp

Android ThingsとRaspberry Pi 3Bでお手軽物体検出

この記事について

この記事ではRaspberry Pi 3B にAndroidThings 1.0.3をインストールした環境で、TensorFlow Liteを使った物体検出を実行してみた。AndroidThingsを問わずTensorFlown Liteを使った物体認識ではClassificationのモデルを使った方法がサンプルとして公開されている。Classificationは画像そのものをラベリングするもので、例えば猫の画像や犬の画像といった具合に画像自体をクラス分けしていく。これに対して物体検出(object detection)とは一枚の画像中に写っている物体を検出する手法となっている。今回は'''coco-ssd-mobilenet_v1'''と呼ばれるモデルをTensorFlow liteを使って動かしてみた。動かすだけならディープラーニングの知識は必要なく、学習済みのモデルとそのモデルに沿った入力と出力に合わせたデータを渡してあげれば良い。

f:id:masato-ka:20180826202603j:plain

物体検出周りについて

coco-ssd-mobilenet_v1について

技術的な詳細は専門ではないため割愛する。Mobilenetは計算量を削減するように工夫された物体識別用のDNN(Deep Nural Network)のネットワーク構造を指している。また、SSDは'''Single Shot multibox Detector'''の略で、1枚の画像から複数の物体を切り出すDNNのネットワーク構造を指す。cocoは物体検出タスク用の学習セットの名前だ。それぞれの詳細について詳しく知りたい方は専門的な書籍や論文を参照し欲しい。

TensorFlow Lite

今回はAndroidThings上でTensorFlow Liteと言うはDNN用の数値計算ライブラリを使った。TensorFlow LiteはTensorFlowをモバイル環境(AndroidiOS)でも動くようにカスタマイズしたものだ。普通のTensorFlowの学習済みモデルを量子化という形で圧縮して使用する。量子化とはモデルのパラメータを浮動小数点から整数に置き換え計算負荷を抑えたものらしい。専門的なところは微妙に違うかもしれないが理解はそんな感じだ。

物体検出の実行

実行結果

以下はRaspberry Pi 3B上で動かした結果だ。10FPSくらいで動いている。工夫すればもう少し早くなるかもしれない。カメラはRaspberry Pi CAMERA V2を使用している。SONY製センサが搭載されているモデルを購入した。一度640X480で画像を撮影して、300X300にリサイズしている。リサイズ後の画像をディスプレイに表示させているので表示が潰れたようになっている。

youtu.be

使用したモデルファイル

モデルファイルはTensorFlowで学習したものが公開されている。このモデルを元にTensorFlow Liteで利用できるように量子化されたモデルも公開されている。TensorFlow LiteのAndroidサンプルでも利用されているモデルだ。ツールを使い自分でモデルを圧縮する方法もあるが今回は実施していない。オリジナルのモデルを試す場合は必須となる。

https://storage.googleapis.com/download.tensorflow.org/models/tflite/coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip

ソースコードのポイント

ソースコードは以下のGitHubに掲載している。これをgradeleでビルドすればAndroidThings上で実行できるはずだ。ところどころフォルダがないと言われるかもしれないが、その場合は自分で当該のフォルダを作成すれば問題ない。

github.com

プロジェクトはモジュールに分かれている。

  • アプリケーション部のappモジュール
  • カメラ制御のcameraモジュール
  • 物体検出部のobjectdetectionモジュール

解説というほどでもないが物体検出処理のポイントについて記載する。TensorFlow Liteで実装する場合はモデルにあった入力を与え、モデルが吐き出す出力に合わせて結果を受け取る必要がある。しかし、それだけ合わせればどんなモデルでも利用するだけなら簡単に実装できそうである。今回の場合、入力は300X300pixelのカラー画像となる。つまり270000次元のデータ(300300RGB)となる。またそれぞれは1byteのデータになる。つまり270000要素の1次元のバイト配列を与えることになる。(配列のデータ構造の次元と与えるデータの次元が違うことに注意)

TensorFlowLiteのライブラリはbuild.gradleファイルに以下を設定するだけでいい。その他の環境設定は不要。

dependencies {
   implementation 'org.tensorflow:tensorflow-lite:1.9.0'
    compileOnly 'com.google.android.things:androidthings:+'
}

入力データは300X300にリサイズした画像(Bitmapオブジェクト)を1次元のByteByfferに以下のように書き出す。

private static int TF_INTPU_IMAGE_WIDTH = 300;
private static int TF_INTPU_IMAGE_HEIGHT = 300;
private static int DIM_BATCH_SIZE = 1;
private static int DIM_PIXELE_SIZE = 3;

private ByteBuffer convertBitmapToByteBuffer(Bitmap bitmap) {

        int[] intValues = new int[TF_INPUT_IMAGE_WIDTH * TF_INPUT_IMAGE_HEIGHT];
        ByteBuffer imgData = ByteBuffer.allocateDirect(DIM_BATCH_SIZE * TF_INPUT_IMAGE_WIDTH
                * TF_INPUT_IMAGE_HEIGHT * DIM_PIXELE_SIZE);
        imgData.order(ByteOrder.LITTLE_ENDIAN);
        TensorFlowHelper.convertBitmapToByteBuffer(bitmap, intValues, imgData);
        return imgData;
}

出力データ用のアウトプットとして以下のようなデータ構造を作成する。numDetectionは検出させたい物体数の最大値を定義する。

Map<Integer,Object> outputData = new HashMap<>();
float outputLocations = new float[1][numDetection][4];
float outputClasses = new float[1][numDetection];
float outputScores = new float[1][numDetection];
float numDetections = new float[1];
outputData.put(0, outputLocations);
outputData.put(1, outputClasses);
outputData.put(2, outputScores);
outputData.put(3, numDetections);

あらかじめモデルを読み込ませたmTensorFlowLiteのInterpreter#runForMultipleInputsOutputsを実行して画像検出処理を実行する。 認識結果はoutputDataに格納されている。

Object[] inputArray = {imgData};
mTensorFlowLite.runForMultipleInputsOutputs(inputArray, outputData);

実行した結果を以下のように処理する。

private stattic final int LABEL_OFFSET = 1;

final ArrayList<Recognition> recognitions = new ArrayList<>(numDetection);
        for(int i=0; i < outputData.size(); i++){
            final RectF location = new RectF(
                    outputLocations[0][i][1] * TF_INPUT_IMAGE_WIDTH,
                    outputLocations[0][i][0] * TF_INPUT_IMAGE_HEIGHT,
                    outputLocations[0][i][3] * TF_INPUT_IMAGE_WIDTH,
                    outputLocations[0][i][2] * TF_INPUT_IMAGE_HEIGHT
            );
            Recognition recognition =
                    new Recognition(
                            "" + i,
                            label.get((int) outputClasses[0][i] + LABEL_OFFSET),
                            outputScores[0][i],
                            location);
            recognitions.add(recognition);
        }

        return recognitions;

認識した物体ごとに以下の結果が格納されている。

  • outputLocationsは画像上での物体の位置 3次配列で2番目の添字が物体、3番目の添字が物体の位置を表す2点の座標となっている。
  • outputClassesは認識した物体のクラスとなっている。値はラベルファイルの行数-1になっているので、ラベルファイルを行ごとにArrayListか何かに格納し、参照する。
  • outputScoresは信頼度Accuracyを表している。

ちなみに今回作ったRecognitionオブジェクトはこんなかんんじ

public class Recognition {

    private final String id;
    private final String title;
    private final Float confidence;
    private final RectF location;

    public Recognition(
            final String id, final String title, final float confidence, final RectF location) {
        this.id = id;
        this.title = title;
        this.confidence = confidence;
        this.location = location;
    }

    public String getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public Float getConfidence() {
        return confidence == null ? 0f : confidence;
    }

    public RectF getLocation(){return location;}

    @Override
    public String toString() {
        String resultString = "";
        if (id != null) {
            resultString += "[" + id + "] ";
        }

        if (title != null) {
            resultString += title + " ";
        }

        if (confidence != null) {
            resultString += String.format("(%.1f%%) ", confidence * 100.0f);
        }

        return resultString.trim();
    }

}

まとめ

AndroidThingsを使うことで、TensorFlowの環境構築をすっ飛ばしてコードを書くだけで物体検出を動かすことができる。今回作成したサンプルのうち、カメラ部分と物体検出部分をライブラリとしてまとめて、bintrayで公開している。自分のアプリケーションに使ってみたい場合はそれを使うのもあり。性能と品質は保証できないけど。TensorFlow Liteは自分で目的に合わせた認識モデルを作れる様になったらより活用の幅が広がると思う。しかし、そこが活用する上での大きな課題だったりも。